From: MirceaKitsune Date: Sun, 17 Apr 2011 01:17:00 +0000 (+0300) Subject: fteqcc source X-Git-Url: https://de.git.xonotic.org/?p=voretournament%2Fvoretournament.git;a=commitdiff_plain;h=8a4e6636262245483ff68abf76bd6180661d20ce fteqcc source --- diff --git a/misc/mediasource/extra/fteqcc-src/LICENSE b/misc/mediasource/extra/fteqcc-src/LICENSE new file mode 100644 index 00000000..2f3289af --- /dev/null +++ b/misc/mediasource/extra/fteqcc-src/LICENSE @@ -0,0 +1,87 @@ +GNU GENERAL PUBLIC LICENSE +Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. +Preamble +The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. + +To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. + +We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. + +Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. + +Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. + +The precise terms and conditions for copying, distribution and modification follow. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION +0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. + +1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. + +You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: + + +a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. + +b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. + +c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) +These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. +Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. + +3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: + +a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, + +b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, + +c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) +The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. +If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. + +4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. + +5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. + +6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. + +7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. + +This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. + +8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. + +9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. + +10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. + +NO WARRANTY + +11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + + +END OF TERMS AND CONDITIONS diff --git a/misc/mediasource/extra/fteqcc-src/Makefile b/misc/mediasource/extra/fteqcc-src/Makefile new file mode 100644 index 00000000..40a721c6 --- /dev/null +++ b/misc/mediasource/extra/fteqcc-src/Makefile @@ -0,0 +1,89 @@ +COMMON_OBJS=comprout.o hash.o qcc_cmdlib.o qcd_main.o +QCC_OBJS=qccmain.o qcc_pr_comp.o qcc_pr_lex.o +VM_OBJS=pr_exec.o pr_edict.o pr_multi.o initlib.o qcdecomp.o +GTKGUI_OBJS=qcc_gtk.o qccguistuff.o +WIN32GUI_OBJS=qccgui.o qccguistuff.o +TUI_OBJS=qcctui.o +LIB_OBJS= + +CC=gcc -Wall + +all: qcc + +USEGUI_CFLAGS= +# set to -DUSEGUI when compiling the GUI +BASE_CFLAGS=-ggdb $(USEGUI_CFLAGS) + +BASE_LDFLAGS=-s +# set to "" for debugging + +DO_CC=$(CC) $(BASE_CFLAGS) -o $@ -c $< $(CFLAGS) + +lib: + +R_win_nocyg: $(QCC_OBJS) $(COMMON_OBJS) $(WIN32GUI_OBJS) + $(CC) $(BASE_CFLAGS) -o fteqcc.exe -O3 $(BASE_LDFLAGS) $(QCC_OBJS) $(COMMON_OBJS) $(WIN32GUI_OBJS) -mno-cygwin -mwindows -lcomctl32 +R_nocyg: $(QCC_OBJS) $(COMMON_OBJS) $(WIN32GUI_OBJS) + $(CC) $(BASE_CFLAGS) -o fteqcc.exe -O3 $(BASE_LDFLAGS) $(QCC_OBJS) $(COMMON_OBJS) $(WIN32GUI_OBJS) -mno-cygwin -lcomctl32 +R_win: $(QCC_OBJS) $(COMMON_OBJS) $(WIN32GUI_OBJS) + $(CC) $(BASE_CFLAGS) -o fteqcc.exe -O3 $(BASE_LDFLAGS) $(QCC_OBJS) $(COMMON_OBJS) $(WIN32GUI_OBJS) -mwindows -lcomctl32 + +win_nocyg: + $(MAKE) USEGUI_CFLAGS="-DUSEGUI -DQCCONLY" R_win_nocyg +nocyg: + $(MAKE) USEGUI_CFLAGS="-DUSEGUI -DQCCONLY" R_nocyg +win: + $(MAKE) USEGUI_CFLAGS="-DUSEGUI -DQCCONLY" R_win + +R_qcc: $(QCC_OBJS) $(COMMON_OBJS) $(TUI_OBJS) + $(CC) $(BASE_CFLAGS) -o fteqcc.bin -O3 $(BASE_LDFLAGS) $(QCC_OBJS) $(TUI_OBJS) $(COMMON_OBJS) +qcc: + $(MAKE) USEGUI_CFLAGS="" R_qcc + +qccmain.o: qccmain.c qcc.h + $(DO_CC) + +qcc_cmdlib.o: qcc_cmdlib.c qcc.h + $(DO_CC) + +qcc_pr_comp.o: qcc_pr_comp.c qcc.h + $(DO_CC) + +qcc_pr_lex.o: qcc_pr_lex.c qcc.h + $(DO_CC) + +comprout.o: comprout.c qcc.h + $(DO_CC) + +hash.o: hash.c qcc.h + $(DO_CC) + +qcd_main.o: qcd_main.c qcc.h + $(DO_CC) + +qccguistuff.o: qccguistuff.c qcc.h + $(DO_CC) + +qcc_gtk.o: qcc_gtk.c qcc.h + $(DO_CC) `pkg-config --cflags gtk+-2.0` + +R_gtkgui: $(QCC_OBJS) $(COMMON_OBJS) $(GTKGUI_OBJS) + $(CC) $(BASE_CFLAGS) $(USEGUI_CFLAGS) -o fteqccgui.bin -O3 $(GTKGUI_OBJS) $(QCC_OBJS) $(COMMON_OBJS) `pkg-config --libs gtk+-2.0` +gtkgui: + $(MAKE) USEGUI_CFLAGS="-DUSEGUI -DQCCONLY" R_gtkgui + +clean: + $(RM) fteqcc.bin fteqcc.exe $(QCC_OBJS) $(COMMON_OBJS) $(VM_OBJS) $(GTKGUI_OBJS) $(WIN32GUI_OBJS) $(TUI_OBJS) + +qcvm.so: $(QCC_OBJS) $(VM_OBJS) $(COMMON_OBJS) + $(CC) $(BASE_CFLAGS) -o $@ -O3 $(BASE_LDFLAGS) $(QCC_OBJS) $(VM_OBJS) $(COMMON_OBJS) -shared + + +test.o: test.c + $(DO_CC) + +testapp.bin: qcvm.so test.o + $(CC) $(BASE_CFLAGS) -o testapp.bin -O3 $(BASE_LDFLAGS) qcvm.so test.o + +regressiontest: testapp.bin + ./testapp.bin regression.dat -srcfile regression.src diff --git a/misc/mediasource/extra/fteqcc-src/cmdlib.h b/misc/mediasource/extra/fteqcc-src/cmdlib.h new file mode 100644 index 00000000..768d899a --- /dev/null +++ b/misc/mediasource/extra/fteqcc-src/cmdlib.h @@ -0,0 +1,93 @@ +// cmdlib.h + +#ifndef __CMDLIB__ +#define __CMDLIB__ + +#include "progsint.h" + +/*#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef NeXT +#include +#endif +*/ + +// the dec offsetof macro doesn't work very well... +#define myoffsetof(type,identifier) ((size_t)&((type *)NULL)->identifier) + + +// set these before calling CheckParm +extern int myargc; +extern char **myargv; + +//char *strupr (char *in); +//char *strlower (char *in); +int QCC_filelength (int handle); +int QCC_tell (int handle); + +int QC_strcasecmp (const char *s1, const char *s2); + +#ifdef _MSC_VER +#define QC_vsnprintf _vsnprintf +#else +#define QC_vsnprintf vsnprintf +#endif + +#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) + #ifndef LIKEPRINTF + #define LIKEPRINTF(x) __attribute__((format(printf,x,x+1))) + #endif +#endif +#ifndef LIKEPRINTF +#define LIKEPRINTF(x) +#endif + +double I_FloatTime (void); + +void VARGS QCC_Error (int errortype, const char *error, ...) LIKEPRINTF(2); +int CheckParm (char *check); + + +int SafeOpenWrite (char *filename, int maxsize); +int SafeOpenRead (char *filename); +void SafeRead (int handle, void *buffer, long count); +void SafeWrite (int handle, void *buffer, long count); +void SafeClose(int handle); +int SafeSeek(int hand, int ofs, int mode); +void *SafeMalloc (long size); + + +long QCC_LoadFile (char *filename, void **bufferptr); +void QCC_SaveFile (char *filename, void *buffer, long count); + +void DefaultExtension (char *path, char *extension); +void DefaultPath (char *path, char *basepath); +void StripFilename (char *path); +void StripExtension (char *path); + +void ExtractFilePath (char *path, char *dest); +void ExtractFileBase (char *path, char *dest); +void ExtractFileExtension (char *path, char *dest); + +long ParseNum (char *str); + + +char *QCC_COM_Parse (char *data); +char *QCC_COM_Parse2 (char *data); + +extern char qcc_token[1024]; +extern int qcc_eof; + + + +#endif diff --git a/misc/mediasource/extra/fteqcc-src/comprout.c b/misc/mediasource/extra/fteqcc-src/comprout.c new file mode 100644 index 00000000..a2a7a3bd --- /dev/null +++ b/misc/mediasource/extra/fteqcc-src/comprout.c @@ -0,0 +1,252 @@ +//compile routines + +#include "qcc.h" +#undef progfuncs + +char errorfile[128]; +int errorline; + +progfuncs_t *qccprogfuncs; + +#include + +extern int qcc_compileactive; +jmp_buf qcccompileerror; +char qcc_gamedir[128]; +void QCC_PR_ResetErrorScope(void); + + + +#ifdef MINIMAL + +#else + +int qccalloced; +int qcchunksize; +char *qcchunk; +void *qccHunkAlloc(size_t mem) +{ + mem = (mem + 7)&~7; + + qccalloced+=mem; + if (qccalloced > qcchunksize) + QCC_Error(ERR_INTERNAL, "Compile hunk was filled"); + + memset(qcchunk+qccalloced-mem, 0, mem); + return qcchunk+qccalloced-mem; +} +void qccClearHunk(void) +{ + if (qcchunk) + { + free(qcchunk); + qcchunk=NULL; + } +} +int qccpersisthunk; +void PostCompile(void) +{ + if (!qccpersisthunk) + qccClearHunk(); + + if (asmfile) + { + fclose(asmfile); + asmfile = NULL; + } +} +pbool PreCompile(void) +{ + QCC_PR_ResetErrorScope(); + + qccClearHunk(); + strcpy(qcc_gamedir, ""); + qcchunk = malloc(qcchunksize=128*1024*1024); + while(!qcchunk && qcchunksize > 8*1024*1024) + { + qcchunksize /= 2; + qcchunk = malloc(qcchunksize); + } + qccalloced=0; + + return !!qcchunk; +} + +void QCC_main (int argc, char **argv); +void QCC_ContinueCompile(void); +void QCC_FinishCompile(void); + +int comp_nump;char **comp_parms; +//void Editor(char *fname, int line, int numparms, char **compileparms); +pbool CompileParams(progfuncs_t *progfuncs, int doall, int nump, char **parms) +{ + comp_nump = nump; + comp_parms = parms; + *errorfile = '\0'; + qccprogfuncs = progfuncs; + if (setjmp(qcccompileerror)) + { + PostCompile(); + if (*errorfile) + { + if (!externs->useeditor) + printf("Error in %s on line %i\n", errorfile, errorline); + else + externs->useeditor(progfuncs, errorfile, errorline, nump, parms); + } + return false; + } + + if (!PreCompile()) + return false; + QCC_main(nump, parms); + + while(qcc_compileactive) + QCC_ContinueCompile(); + + PostCompile(); + + return true; +} +int Comp_Begin(progfuncs_t *progfuncs, int nump, char **parms) +{ + comp_nump = nump; + comp_parms = parms; + qccprogfuncs = progfuncs; + *errorfile = '\0'; + if (setjmp(qcccompileerror)) + { + PostCompile(); + if (*errorfile) + externs->useeditor(progfuncs, errorfile, errorline, nump, parms); + return false; + } + + if (!PreCompile()) + return false; + QCC_main(nump, parms); + + return true; +} +int Comp_Continue(progfuncs_t *progfuncs) +{ + qccprogfuncs = progfuncs; + if (setjmp(qcccompileerror)) + { + PostCompile(); + if (*errorfile && externs->useeditor) + externs->useeditor(progfuncs, errorfile, errorline, comp_nump, comp_parms); + return false; + } + + if (qcc_compileactive) + QCC_ContinueCompile(); + else + { + PostCompile(); + + if (*errorfile && externs->useeditor) + externs->useeditor(progfuncs, errorfile, errorline, comp_nump, comp_parms); + + return false; + } + + return true; +} +#endif +pbool CompileFile(progfuncs_t *progfuncs, char *filename) +{ +#ifdef MINIMAL + return false; +#else + char srcfile[32]; + char newname[32]; + static char *p[5]; + int parms; + char *s, *s2; + + p[0] = NULL; + parms = 1; + + strcpy(newname, filename); + s = newname; + if (strchr(s+1, '/')) + { + while(1) + { + s2 = strchr(s+1, '/'); + if (!s2) + { + *s = '\0'; + break; + } + s = s2; + } + p[parms] = "-src"; + p[parms+1] = newname; + parms+=2; + + strcpy(srcfile, s+1); + srcfile[strlen(srcfile)-4] = '\0'; + strcat(srcfile, ".src"); + + if (externs->FileSize(qcva("%s/%s", newname, srcfile))>0) + { + p[parms] = "-srcfile"; + p[parms+1] = srcfile; + parms+=2; + } + } + else + { + p[parms] = "-srcfile"; + p[parms+1] = newname; + newname[strlen(newname)-4] = '\0'; + strcat(newname, ".src"); + parms+=2; + } +// p[2][strlen(p[2])-4] = '\0'; +// strcat(p[2], "/"); + + while (!CompileParams(progfuncs, true, parms, p)) + { + return false; + } + return true; +#endif +} + +int QC_strncasecmp(const char *s1, const char *s2, int n) +{ + int c1, c2; + + while (1) + { + c1 = *s1++; + c2 = *s2++; + + if (!n--) + return 0; // strings are equal until end point + + if (c1 != c2) + { + if (c1 >= 'a' && c1 <= 'z') + c1 -= ('a' - 'A'); + if (c2 >= 'a' && c2 <= 'z') + c2 -= ('a' - 'A'); + if (c1 != c2) + return -1; // strings not equal + } + if (!c1) + return 0; // strings are equal + } + + return -1; +} + +void editbadfile(char *fname, int line) +{ + strcpy(errorfile, fname); + errorline = line; +} + diff --git a/misc/mediasource/extra/fteqcc-src/execloop.h b/misc/mediasource/extra/fteqcc-src/execloop.h new file mode 100644 index 00000000..365c93a4 --- /dev/null +++ b/misc/mediasource/extra/fteqcc-src/execloop.h @@ -0,0 +1,1141 @@ +//qc execution code. +//we have two conditions. +//one allows us to debug and trace through our code, the other doesn't. + +//hopefully, the compiler will do a great job at optimising this code for us, where required. +//if it dosn't, then bum. + +//the general overhead should be reduced significantly, and I would be supprised if it did run slower. + +//run away loops are checked for ONLY on gotos and function calls. This might give a poorer check, but it will run faster overall. + +//Appears to work fine. + +#if INTSIZE == 16 +#define cont cont16 +#define reeval reeval16 +#define st st16 +#define pr_statements pr_statements16 +#define fakeop fakeop16 +#define dstatement_t dstatement16_t +#define sofs signed short +#define uofs unsigned short +#elif INTSIZE == 32 +#define cont cont32 +#define reeval reeval32 +#define st st32 +#define pr_statements pr_statements32 +#define fakeop fakeop32 +#define dstatement_t dstatement32_t +#define sofs signed int +#define uofs unsigned int +#elif INTSIZE == 24 +#error INTSIZE should be set to 32. +#else +#error Bad cont size +#endif + +#define ENGINEPOINTER(p) ((char*)(p) - progfuncs->stringtable) +#define QCPOINTER(p) (eval_t *)(p->_int+progfuncs->stringtable) +#define QCPOINTERM(p) (eval_t *)((p)+progfuncs->stringtable) + +//rely upon just st +{ +#ifdef DEBUGABLE +cont: //last statement may have been a breakpoint + s = st-pr_statements; + s+=1; + s=ShowStep(progfuncs, s); + st = pr_statements + s; + +reeval: +#else + st++; +#endif + + switch (st->op) + { + case OP_ADD_F: + OPC->_float = OPA->_float + OPB->_float; + break; + case OP_ADD_V: + OPC->_vector[0] = OPA->_vector[0] + OPB->_vector[0]; + OPC->_vector[1] = OPA->_vector[1] + OPB->_vector[1]; + OPC->_vector[2] = OPA->_vector[2] + OPB->_vector[2]; + break; + + case OP_SUB_F: + OPC->_float = OPA->_float - OPB->_float; + break; + case OP_SUB_V: + OPC->_vector[0] = OPA->_vector[0] - OPB->_vector[0]; + OPC->_vector[1] = OPA->_vector[1] - OPB->_vector[1]; + OPC->_vector[2] = OPA->_vector[2] - OPB->_vector[2]; + break; + + case OP_MUL_F: + OPC->_float = OPA->_float * OPB->_float; + break; + case OP_MUL_V: + OPC->_float = OPA->_vector[0]*OPB->_vector[0] + + OPA->_vector[1]*OPB->_vector[1] + + OPA->_vector[2]*OPB->_vector[2]; + break; + case OP_MUL_FV: + OPC->_vector[0] = OPA->_float * OPB->_vector[0]; + OPC->_vector[1] = OPA->_float * OPB->_vector[1]; + OPC->_vector[2] = OPA->_float * OPB->_vector[2]; + break; + case OP_MUL_VF: + OPC->_vector[0] = OPB->_float * OPA->_vector[0]; + OPC->_vector[1] = OPB->_float * OPA->_vector[1]; + OPC->_vector[2] = OPB->_float * OPA->_vector[2]; + break; + + case OP_DIV_F: + OPC->_float = OPA->_float / OPB->_float; + break; + case OP_DIV_VF: + OPC->_vector[0] = OPB->_float / OPA->_vector[0]; + OPC->_vector[1] = OPB->_float / OPA->_vector[1]; + OPC->_vector[2] = OPB->_float / OPA->_vector[2]; + break; + + case OP_BITAND: + OPC->_float = (float)((int)OPA->_float & (int)OPB->_float); + break; + + case OP_BITOR: + OPC->_float = (float)((int)OPA->_float | (int)OPB->_float); + break; + + + case OP_GE: + OPC->_float = (float)(OPA->_float >= OPB->_float); + break; + case OP_GE_I: + OPC->_int = (int)(OPA->_int >= OPB->_int); + break; + case OP_GE_IF: + OPC->_float = (float)(OPA->_int >= OPB->_float); + break; + case OP_GE_FI: + OPC->_float = (float)(OPA->_float >= OPB->_int); + break; + + case OP_LE: + OPC->_float = (float)(OPA->_float <= OPB->_float); + break; + case OP_LE_I: + OPC->_int = (int)(OPA->_int <= OPB->_int); + break; + case OP_LE_IF: + OPC->_float = (float)(OPA->_int <= OPB->_float); + break; + case OP_LE_FI: + OPC->_float = (float)(OPA->_float <= OPB->_int); + break; + + case OP_GT: + OPC->_float = (float)(OPA->_float > OPB->_float); + break; + case OP_GT_I: + OPC->_int = (int)(OPA->_int > OPB->_int); + break; + case OP_GT_IF: + OPC->_float = (float)(OPA->_int > OPB->_float); + break; + case OP_GT_FI: + OPC->_float = (float)(OPA->_float > OPB->_int); + break; + + case OP_LT: + OPC->_float = (float)(OPA->_float < OPB->_float); + break; + case OP_LT_I: + OPC->_int = (int)(OPA->_int < OPB->_int); + break; + case OP_LT_IF: + OPC->_float = (float)(OPA->_int < OPB->_float); + break; + case OP_LT_FI: + OPC->_float = (float)(OPA->_float < OPB->_int); + break; + + case OP_AND: + OPC->_float = (float)(OPA->_float && OPB->_float); + break; + case OP_OR: + OPC->_float = (float)(OPA->_float || OPB->_float); + break; + + case OP_NOT_F: + OPC->_float = (float)(!OPA->_float); + break; + case OP_NOT_V: + OPC->_float = (float)(!OPA->_vector[0] && !OPA->_vector[1] && !OPA->_vector[2]); + break; + case OP_NOT_S: + OPC->_float = (float)(!(OPA->string) || !*PR_StringToNative(progfuncs, OPA->string)); + break; + case OP_NOT_FNC: + OPC->_float = (float)(!(OPA->function & ~0xff000000)); + break; + case OP_NOT_ENT: + OPC->_float = (float)(PROG_TO_EDICT(progfuncs, OPA->edict) == (edictrun_t *)sv_edicts); + break; + + case OP_EQ_F: + OPC->_float = (float)(OPA->_float == OPB->_float); + break; + case OP_EQ_IF: + OPC->_float = (float)(OPA->_int == OPB->_float); + break; + case OP_EQ_FI: + OPC->_float = (float)(OPA->_float == OPB->_int); + break; + + + case OP_EQ_V: + OPC->_float = (float)((OPA->_vector[0] == OPB->_vector[0]) && + (OPA->_vector[1] == OPB->_vector[1]) && + (OPA->_vector[2] == OPB->_vector[2])); + break; + case OP_EQ_S: + if (OPA->string==OPB->string) + OPC->_float = true; + else if (!OPA->string) + { + if (!OPB->string || !*PR_StringToNative(progfuncs, OPB->string)) + OPC->_float = true; + else + OPC->_float = false; + } + else if (!OPB->string) + { + if (!OPA->string || !*PR_StringToNative(progfuncs, OPA->string)) + OPC->_float = true; + else + OPC->_float = false; + } + else + OPC->_float = (float)(!strcmp(PR_StringToNative(progfuncs, OPA->string),PR_StringToNative(progfuncs, OPB->string))); + break; + case OP_EQ_E: + OPC->_float = (float)(OPA->_int == OPB->_int); + break; + case OP_EQ_FNC: + OPC->_float = (float)(OPA->function == OPB->function); + break; + + + case OP_NE_F: + OPC->_float = (float)(OPA->_float != OPB->_float); + break; + case OP_NE_V: + OPC->_float = (float)((OPA->_vector[0] != OPB->_vector[0]) || + (OPA->_vector[1] != OPB->_vector[1]) || + (OPA->_vector[2] != OPB->_vector[2])); + break; + case OP_NE_S: + if (OPA->string==OPB->string) + OPC->_float = false; + else if (!OPA->string) + { + if (!OPB->string || !*(PR_StringToNative(progfuncs, OPB->string))) + OPC->_float = false; + else + OPC->_float = true; + } + else if (!OPB->string) + { + if (!OPA->string || !*PR_StringToNative(progfuncs, OPA->string)) + OPC->_float = false; + else + OPC->_float = true; + } + else + OPC->_float = (float)(strcmp(PR_StringToNative(progfuncs, OPA->string),PR_StringToNative(progfuncs, OPB->string))); + break; + case OP_NE_E: + OPC->_float = (float)(OPA->_int != OPB->_int); + break; + case OP_NE_FNC: + OPC->_float = (float)(OPA->function != OPB->function); + break; + +//================== + case OP_STORE_IF: + OPB->_float = (float)OPA->_int; + break; + case OP_STORE_FI: + OPB->_int = (int)OPA->_float; + break; + + case OP_STORE_F: + case OP_STORE_ENT: + case OP_STORE_FLD: // integers + case OP_STORE_S: + case OP_STORE_I: + case OP_STORE_FNC: // pointers + case OP_STORE_P: + OPB->_int = OPA->_int; + break; + case OP_STORE_V: + OPB->_vector[0] = OPA->_vector[0]; + OPB->_vector[1] = OPA->_vector[1]; + OPB->_vector[2] = OPA->_vector[2]; + break; + + //store a value to a pointer + case OP_STOREP_IF: + if ((unsigned int)OPB->_int >= addressableused) + { + pr_xstatement = st-pr_statements; + PR_RunError (progfuncs, "bad pointer write in %s", progfuncs->stringtable + pr_xfunction->s_name); + } + ptr = QCPOINTER(OPB); + ptr->_float = (float)OPA->_int; + break; + case OP_STOREP_FI: + if ((unsigned int)OPB->_int >= addressableused) + { + pr_xstatement = st-pr_statements; + PR_RunError (progfuncs, "bad pointer write in %s", progfuncs->stringtable + pr_xfunction->s_name); + } + ptr = QCPOINTER(OPB); + ptr->_int = (int)OPA->_float; + break; + case OP_STOREP_I: + if ((unsigned int)OPB->_int >= addressableused) + { + pr_xstatement = st-pr_statements; + PR_RunError (progfuncs, "bad pointer write in %s", progfuncs->stringtable + pr_xfunction->s_name); + } + ptr = QCPOINTER(OPB); + ptr->_int = OPA->_int; + break; + case OP_STOREP_F: + case OP_STOREP_ENT: + case OP_STOREP_FLD: // integers + case OP_STOREP_S: + case OP_STOREP_FNC: // pointers + if ((unsigned int)OPB->_int >= addressableused) + { + pr_xstatement = st-pr_statements; + PR_RunError (progfuncs, "bad pointer write in %s", progfuncs->stringtable + pr_xfunction->s_name); + } + ptr = QCPOINTER(OPB); + ptr->_int = OPA->_int; + break; + case OP_STOREP_V: + if ((unsigned int)OPB->_int >= addressableused) + { + pr_xstatement = st-pr_statements; + PR_RunError (progfuncs, "bad pointer write in %s", progfuncs->stringtable + pr_xfunction->s_name); + } + ptr = QCPOINTER(OPB); + ptr->_vector[0] = OPA->_vector[0]; + ptr->_vector[1] = OPA->_vector[1]; + ptr->_vector[2] = OPA->_vector[2]; + break; + + case OP_STOREP_C: //store character in a string + if ((unsigned int)OPB->_int >= addressableused) + { + pr_xstatement = st-pr_statements; + PR_RunError (progfuncs, "bad pointer write in %s", progfuncs->stringtable + pr_xfunction->s_name); + } + ptr = QCPOINTER(OPB); + *(unsigned char *)ptr = (char)OPA->_float; + break; + + case OP_MULSTORE_F: // f *= f + OPB->_float *= OPA->_float; + break; + case OP_MULSTORE_V: // v *= f + OPB->_vector[0] *= OPA->_float; + OPB->_vector[1] *= OPA->_float; + OPB->_vector[2] *= OPA->_float; + break; + case OP_MULSTOREP_F: // e.f *= f + if ((unsigned int)OPB->_int >= addressableused) + { + pr_xstatement = st-pr_statements; + PR_RunError (progfuncs, "bad pointer write in %s", progfuncs->stringtable + pr_xfunction->s_name); + } + ptr = QCPOINTER(OPB); + OPC->_float = (ptr->_float *= OPA->_float); + break; + case OP_MULSTOREP_V: // e.v *= f + ptr = QCPOINTER(OPB); + OPC->_vector[0] = (ptr->_vector[0] *= OPA->_float); + OPC->_vector[0] = (ptr->_vector[1] *= OPA->_float); + OPC->_vector[0] = (ptr->_vector[2] *= OPA->_float); + break; + + case OP_DIVSTORE_F: // f /= f + OPB->_float /= OPA->_float; + break; + case OP_DIVSTOREP_F: // e.f /= f + ptr = QCPOINTER(OPB); + OPC->_float = (ptr->_float /= OPA->_float); + break; + + case OP_ADDSTORE_F: // f += f + OPB->_float += OPA->_float; + break; + case OP_ADDSTORE_V: // v += v + OPB->_vector[0] += OPA->_vector[0]; + OPB->_vector[1] += OPA->_vector[1]; + OPB->_vector[2] += OPA->_vector[2]; + break; + case OP_ADDSTOREP_F: // e.f += f + ptr = QCPOINTER(OPB); + OPC->_float = (ptr->_float += OPA->_float); + break; + case OP_ADDSTOREP_V: // e.v += v + ptr = QCPOINTER(OPB); + OPC->_vector[0] = (ptr->_vector[0] += OPA->_vector[0]); + OPC->_vector[1] = (ptr->_vector[1] += OPA->_vector[1]); + OPC->_vector[2] = (ptr->_vector[2] += OPA->_vector[2]); + break; + + case OP_SUBSTORE_F: // f -= f + OPB->_float -= OPA->_float; + break; + case OP_SUBSTORE_V: // v -= v + OPB->_vector[0] -= OPA->_vector[0]; + OPB->_vector[1] -= OPA->_vector[1]; + OPB->_vector[2] -= OPA->_vector[2]; + break; + case OP_SUBSTOREP_F: // e.f -= f + ptr = QCPOINTER(OPB); + OPC->_float = (ptr->_float -= OPA->_float); + break; + case OP_SUBSTOREP_V: // e.v -= v + ptr = QCPOINTER(OPB); + OPC->_vector[0] = (ptr->_vector[0] -= OPA->_vector[0]); + OPC->_vector[1] = (ptr->_vector[1] -= OPA->_vector[1]); + OPC->_vector[2] = (ptr->_vector[2] -= OPA->_vector[2]); + break; + + + //get a pointer to a field var + case OP_ADDRESS: + if ((unsigned)OPA->edict >= (unsigned)maxedicts) + { +#ifndef DEBUGABLE + pr_trace++; + printf("OP_ADDRESS references invalid entity in %s", progfuncs->stringtable + pr_xfunction->s_name); + st--; + goto cont; +#else + PR_RunError (progfuncs, "OP_ADDRESS references invalid entity in %s", PR_StringToNative(progfuncs, pr_xfunction->s_name)); +#endif + } + ed = PROG_TO_EDICT(progfuncs, OPA->edict); +#ifdef PARANOID + NUM_FOR_EDICT(ed); // make sure it's in range +#endif + if (!ed || ed->readonly) + { + pr_xstatement = st-pr_statements; +#ifndef DEBUGABLE + //boot it over to the debugger + pr_trace++; + printf("assignment to read-only entity in %s", progfuncs->stringtable + pr_xfunction->s_name); + st--; + goto cont; +#else + { + ddef16_t *d16; + fdef_t *f; + d16 = ED_GlobalAtOfs16(progfuncs, st->a); + f = ED_FieldAtOfs(progfuncs, OPB->_int + progfuncs->fieldadjust); + PR_RunError (progfuncs, "assignment to read-only entity in %s (%s.%s)", PR_StringToNative(progfuncs, pr_xfunction->s_name), PR_StringToNative(progfuncs, d16->s_name), f?f->name:NULL); + } +#endif + } + +//Whilst the next block would technically be correct, we don't use it as it breaks too many quake mods. +// if (ed->isfree) +// { +// pr_xstatement = st-pr_statements; +// PR_RunError (progfuncs, "assignment to free entitiy in %s", progfuncs->stringtable + pr_xfunction->s_name); +// } + OPC->_int = ENGINEPOINTER((((int *)edvars(ed)) + OPB->_int + progfuncs->fieldadjust)); + break; + + //load a field to a value + case OP_LOAD_I: + case OP_LOAD_F: + case OP_LOAD_FLD: + case OP_LOAD_ENT: + case OP_LOAD_S: + case OP_LOAD_FNC: + if ((unsigned)OPA->edict >= (unsigned)maxedicts) + PR_RunError (progfuncs, "OP_LOAD references invalid entity in %s", progfuncs->stringtable + pr_xfunction->s_name); + ed = PROG_TO_EDICT(progfuncs, OPA->edict); +#ifdef PARANOID + NUM_FOR_EDICT(ed); // make sure it's in range +#endif + ptr = (eval_t *)(((int *)edvars(ed)) + OPB->_int + progfuncs->fieldadjust); + OPC->_int = ptr->_int; + break; + + case OP_LOAD_V: + if ((unsigned)OPA->edict >= (unsigned)maxedicts) + PR_RunError (progfuncs, "OP_LOAD_V references invalid entity in %s", progfuncs->stringtable + pr_xfunction->s_name); + ed = PROG_TO_EDICT(progfuncs, OPA->edict); +#ifdef PARANOID + NUM_FOR_EDICT(ed); // make sure it's in range +#endif + ptr = (eval_t *)(((int *)edvars(ed)) + OPB->_int + progfuncs->fieldadjust); + OPC->_vector[0] = ptr->_vector[0]; + OPC->_vector[1] = ptr->_vector[1]; + OPC->_vector[2] = ptr->_vector[2]; + break; + +//================== + + case OP_IFNOT_S: + RUNAWAYCHECK(); + if (!OPA->string || !PR_StringToNative(progfuncs, OPA->string)) + st += (sofs)st->b - 1; // offset the s++ + break; + + case OP_IFNOT_F: + RUNAWAYCHECK(); + if (!OPA->_float) + st += (sofs)st->b - 1; // offset the s++ + break; + + case OP_IFNOT: + RUNAWAYCHECK(); + if (!OPA->_int) + st += (sofs)st->b - 1; // offset the s++ + break; + + case OP_IF_S: + RUNAWAYCHECK(); + if (OPA->string && PR_StringToNative(progfuncs, OPA->string)) + st += (sofs)st->b - 1; // offset the s++ + break; + + case OP_IF_F: + RUNAWAYCHECK(); + if (OPA->_int) + st += (sofs)st->b - 1; // offset the s++ + break; + + case OP_IF: + RUNAWAYCHECK(); + if (OPA->_int) + st += (sofs)st->b - 1; // offset the s++ + break; + + case OP_GOTO: + RUNAWAYCHECK(); + st += (sofs)st->a - 1; // offset the s++ + break; + + case OP_CALL8H: + case OP_CALL7H: + case OP_CALL6H: + case OP_CALL5H: + case OP_CALL4H: + case OP_CALL3H: + case OP_CALL2H: + G_VECTOR(OFS_PARM1)[0] = OPC->_vector[0]; + G_VECTOR(OFS_PARM1)[1] = OPC->_vector[1]; + G_VECTOR(OFS_PARM1)[2] = OPC->_vector[2]; + case OP_CALL1H: + G_VECTOR(OFS_PARM0)[0] = OPB->_vector[0]; + G_VECTOR(OFS_PARM0)[1] = OPB->_vector[1]; + G_VECTOR(OFS_PARM0)[2] = OPB->_vector[2]; + + case OP_CALL8: + case OP_CALL7: + case OP_CALL6: + case OP_CALL5: + case OP_CALL4: + case OP_CALL3: + case OP_CALL2: + case OP_CALL1: + case OP_CALL0: + RUNAWAYCHECK(); + pr_xstatement = st-pr_statements; + + + if (st->op > OP_CALL8) + pr_argc = st->op - (OP_CALL1H-1); + else + pr_argc = st->op - OP_CALL0; + fnum = OPA->function; + if ((fnum & ~0xff000000)==0) + { + pr_trace++; + printf("NULL function from qc (%s).\n", progfuncs->stringtable + pr_xfunction->s_name); +#ifndef DEBUGABLE + goto cont; +#endif + break; + } +/* +{ + static char buffer[1024*1024*8]; + int size = sizeof buffer; + progfuncs->save_ents(progfuncs, buffer, &size, 0); +}*/ + + + p=pr_typecurrent; +//about to switch. needs caching. + + //if it's an external call, switch now (before any function pointers are used) + PR_MoveParms(progfuncs, (fnum & 0xff000000)>>24, p); + PR_SwitchProgs(progfuncs, (fnum & 0xff000000)>>24); + + newf = &pr_functions[fnum & ~0xff000000]; + + if (newf->first_statement < 0) + { // negative statements are built in functions + +if (pr_typecurrent != 0) +{ + PR_MoveParms(progfuncs, 0, pr_typecurrent); + PR_SwitchProgs(progfuncs, 0); +} + i = -newf->first_statement; +// p = pr_typecurrent; + progfuncs->lastcalledbuiltinnumber = i; + if (i < externs->numglobalbuiltins) + { + prinst->numtempstringsstack = prinst->numtempstrings; + (*externs->globalbuiltins[i]) (progfuncs, (struct globalvars_s *)current_progstate->globals); + if (prinst->continuestatement!=-1) + { + st=&pr_statements[prinst->continuestatement]; + prinst->continuestatement=-1; + break; + } + } + else + { + i -= externs->numglobalbuiltins; + if (i >= current_progstate->numbuiltins) + { +// if (newf->first_statement == -0x7fffffff) +// ((builtin_t)newf->profile) (progfuncs, (struct globalvars_s *)current_progstate->globals); +// else + PR_RunError (progfuncs, "Bad builtin call number - %i", -newf->first_statement); + } + else + current_progstate->builtins [i] (progfuncs, (struct globalvars_s *)current_progstate->globals); + } + PR_MoveParms(progfuncs, p, pr_typecurrent); +// memcpy(&pr_progstate[p].globals[OFS_RETURN], ¤t_progstate->globals[OFS_RETURN], sizeof(vec3_t)); + PR_SwitchProgs(progfuncs, (progsnum_t)p); + +//#ifndef DEBUGABLE //decide weather non debugger wants to start debugging. + s = st-pr_statements; + goto restart; +//#endif +// break; + } +// PR_MoveParms((OPA->function & 0xff000000)>>24, pr_typecurrent); +// PR_SwitchProgs((OPA->function & 0xff000000)>>24); + s = PR_EnterFunction (progfuncs, newf, p); + st = &pr_statements[s]; + + goto restart; +// break; + + case OP_DONE: + case OP_RETURN: + + RUNAWAYCHECK(); + + pr_globals[OFS_RETURN] = pr_globals[st->a]; + pr_globals[OFS_RETURN+1] = pr_globals[st->a+1]; + pr_globals[OFS_RETURN+2] = pr_globals[st->a+2]; +/* +{ + static char buffer[1024*1024*8]; + int size = sizeof buffer; + progfuncs->save_ents(progfuncs, buffer, &size, 0); +} +*/ + s = PR_LeaveFunction (progfuncs); + st = &pr_statements[s]; + if (pr_depth == prinst->exitdepth) + { + return; // all done + } + goto restart; +// break; + + case OP_STATE: + externs->stateop(progfuncs, OPA->_float, OPB->function); + break; + + case OP_ADD_I: + OPC->_int = OPA->_int + OPB->_int; + break; + case OP_ADD_FI: + OPC->_float = OPA->_float + (float)OPB->_int; + break; + case OP_ADD_IF: + OPC->_float = (float)OPA->_int + OPB->_float; + break; + + case OP_SUB_I: + OPC->_int = OPA->_int - OPB->_int; + break; + case OP_SUB_FI: + OPC->_float = OPA->_float - (float)OPB->_int; + break; + case OP_SUB_IF: + OPC->_float = (float)OPA->_int - OPB->_float; + break; + + case OP_CONV_ITOF: + OPC->_float = (float)OPA->_int; + break; + case OP_CONV_FTOI: + OPC->_int = (int)OPA->_float; + break; + + case OP_CP_ITOF: + ptr = (eval_t *)(((qbyte *)sv_edicts) + OPA->_int); + OPC->_float = (float)ptr->_int; + break; + + case OP_CP_FTOI: + ptr = (eval_t *)(((qbyte *)sv_edicts) + OPA->_int); + OPC->_int = (int)ptr->_float; + break; + + case OP_BITAND_I: + OPC->_int = (OPA->_int & OPB->_int); + break; + + case OP_BITOR_I: + OPC->_int = (OPA->_int | OPB->_int); + break; + + case OP_MUL_I: + OPC->_int = OPA->_int * OPB->_int; + break; + case OP_DIV_I: + if (OPB->_int == 0) //no division by zero allowed... + OPC->_int = 0; + else + OPC->_int = OPA->_int / OPB->_int; + break; + case OP_EQ_I: + OPC->_int = (OPA->_int == OPB->_int); + break; + case OP_NE_I: + OPC->_int = (OPA->_int != OPB->_int); + break; + + + //array/structure reading/riting. + case OP_GLOBALADDRESS: + OPC->_int = ENGINEPOINTER(&OPA->_int + OPB->_int); + break; + case OP_POINTER_ADD: //pointer to 32 bit (remember to *3 for vectors) + OPC->_int = OPA->_int + OPB->_int*4; + break; + + case OP_LOADA_I: + case OP_LOADA_F: + case OP_LOADA_FLD: + case OP_LOADA_ENT: + case OP_LOADA_S: + case OP_LOADA_FNC: + ptr = (eval_t *)(&OPA->_int + OPB->_int); + OPC->_int = ptr->_int; + break; + + case OP_LOADA_V: + ptr = (eval_t *)(&OPA->_int + OPB->_int); + OPC->_vector[0] = ptr->_vector[0]; + OPC->_vector[1] = ptr->_vector[1]; + OPC->_vector[2] = ptr->_vector[2]; + break; + + + + case OP_ADD_SF: //(char*)c = (char*)a + (float)b + OPC->_int = OPA->_int + (int)OPB->_float; + break; + case OP_SUB_S: //(float)c = (char*)a - (char*)b + OPC->_int = OPA->_int - OPB->_int; + break; + case OP_LOADP_C: //load character from a string + ptr = QCPOINTERM(OPA->_int + (int)OPB->_float); + OPC->_float = *(unsigned char *)ptr; + break; + case OP_LOADP_I: + case OP_LOADP_F: + case OP_LOADP_FLD: + case OP_LOADP_ENT: + case OP_LOADP_S: + case OP_LOADP_FNC: + ptr = QCPOINTERM(OPA->_int + OPB->_int); + OPC->_int = ptr->_int; + break; + + case OP_LOADP_V: + ptr = QCPOINTERM(OPA->_int + OPB->_int); + OPC->_vector[0] = ptr->_vector[0]; + OPC->_vector[1] = ptr->_vector[1]; + OPC->_vector[2] = ptr->_vector[2]; + break; + + case OP_XOR_I: + OPC->_int = OPA->_int ^ OPB->_int; + break; + case OP_RSHIFT_I: + OPC->_int = OPA->_int >> OPB->_int; + break; + case OP_LSHIFT_I: + OPC->_int = OPA->_int << OPB->_int; + break; + + + case OP_FETCH_GBL_F: + case OP_FETCH_GBL_S: + case OP_FETCH_GBL_E: + case OP_FETCH_GBL_FNC: + i = (int)OPB->_float; + if(i < 0 || i > ((eval_t *)&glob[st->a-1])->_int) + { + PR_RunError(progfuncs, "array index out of bounds: %s[%d]", PR_GlobalStringNoContents(progfuncs, st->a), i); + } + t = (eval_t *)&pr_globals[(uofs)st->a + i]; + OPC->_int = t->_int; + break; + case OP_FETCH_GBL_V: + i = (int)OPB->_float; + if(i < 0 || i > ((eval_t *)&glob[st->a-1])->_int) + { + PR_RunError(progfuncs, "array index out of bounds: %s[%d]", PR_GlobalStringNoContents(progfuncs, st->a), i); + } + t = (eval_t *)&pr_globals[(uofs)st->a + +((int)OPB->_float)*3]; + OPC->_vector[0] = t->_vector[0]; + OPC->_vector[1] = t->_vector[1]; + OPC->_vector[2] = t->_vector[2]; + break; + + case OP_CSTATE: + externs->cstateop(progfuncs, OPA->_float, OPB->_float, fnum); + break; + + case OP_CWSTATE: + externs->cwstateop(progfuncs, OPA->_float, OPB->_float, fnum); + break; + + case OP_THINKTIME: + externs->thinktimeop(progfuncs, (struct edict_s *)PROG_TO_EDICT(progfuncs, OPA->edict), OPB->_float); + break; + + + case OP_BITSET: // b (+) a + OPB->_float = (float)((int)OPB->_float | (int)OPA->_float); + break; + case OP_BITSETP: // .b (+) a + ptr = QCPOINTER(OPB); + ptr->_float = (float)((int)ptr->_float | (int)OPA->_float); + break; + case OP_BITCLR: // b (-) a + OPB->_float = (float)((int)OPB->_float & ~((int)OPA->_float)); + break; + case OP_BITCLRP: // .b (-) a + ptr = QCPOINTER(OPB); + ptr->_float = (float)((int)ptr->_float & ~((int)OPA->_float)); + break; + + case OP_RAND0: + G_FLOAT(OFS_RETURN) = (rand()&0x7fff)/((float)0x7fff); + break; + case OP_RAND1: + G_FLOAT(OFS_RETURN) = (rand()&0x7fff)/((float)0x7fff)*OPA->_float; + break; + case OP_RAND2: + if(OPA->_float < OPB->_float) + { + G_FLOAT(OFS_RETURN) = OPA->_float+((rand()&0x7fff)/((float)0x7fff) + *(OPB->_float-OPA->_float)); + } + else + { + G_FLOAT(OFS_RETURN) = OPB->_float+((rand()&0x7fff)/((float)0x7fff) + *(OPA->_float-OPB->_float)); + } + break; + case OP_RANDV0: + G_FLOAT(OFS_RETURN+0) = (rand()&0x7fff)/((float)0x7fff); + G_FLOAT(OFS_RETURN+1) = (rand()&0x7fff)/((float)0x7fff); + G_FLOAT(OFS_RETURN+2) = (rand()&0x7fff)/((float)0x7fff); + break; + case OP_RANDV1: + G_FLOAT(OFS_RETURN+0) = (rand()&0x7fff)/((float)0x7fff)*OPA->_vector[0]; + G_FLOAT(OFS_RETURN+1) = (rand()&0x7fff)/((float)0x7fff)*OPA->_vector[1]; + G_FLOAT(OFS_RETURN+2) = (rand()&0x7fff)/((float)0x7fff)*OPA->_vector[2]; + break; + case OP_RANDV2: + for(i = 0; i < 3; i++) + { + if(OPA->_vector[i] < OPB->_vector[i]) + { + G_FLOAT(OFS_RETURN+i) = OPA->_vector[i]+((rand()&0x7fff)/((float)0x7fff) + *(OPB->_vector[i]-OPA->_vector[i])); + } + else + { + G_FLOAT(OFS_RETURN+i) = OPB->_vector[i]+(rand()*(1.0f/RAND_MAX) + *(OPA->_vector[i]-OPB->_vector[i])); + } + } + break; + + + case OP_SWITCH_F: + case OP_SWITCH_V: + case OP_SWITCH_S: + case OP_SWITCH_E: + case OP_SWITCH_FNC: + swtch = OPA; + swtchtype = st->op; + RUNAWAYCHECK(); + st += (sofs)st->b - 1; // offset the st++ + break; + case OP_CASE: + switch(swtchtype) + { + case OP_SWITCH_F: + if (swtch->_float == OPA->_float) + { + RUNAWAYCHECK(); + st += (sofs)st->b-1; // -1 to offset the s++ + } + break; + case OP_SWITCH_E: + case OP_SWITCH_FNC: + if (swtch->_int == OPA->_int) + { + RUNAWAYCHECK(); + st += (sofs)st->b-1; // -1 to offset the s++ + } + break; + case OP_SWITCH_S: + if (swtch->_int == OPA->_int) + { + RUNAWAYCHECK(); + st += (sofs)st->b-1; // -1 to offset the s++ + } + if ((!swtch->_int && PR_StringToNative(progfuncs, OPA->string)) || (!OPA->_int && PR_StringToNative(progfuncs, swtch->string))) //one is null (cannot be not both). + break; + if (!strcmp(PR_StringToNative(progfuncs, swtch->string), PR_StringToNative(progfuncs, OPA->string))) + { + RUNAWAYCHECK(); + st += (sofs)st->b-1; // -1 to offset the s++ + } + break; + case OP_SWITCH_V: + if (swtch->_vector[0] == OPA->_vector[0] && swtch->_vector[1] == OPA->_vector[1] && swtch->_vector[2] == OPA->_vector[2]) + { + RUNAWAYCHECK(); + st += (sofs)st->b-1; // -1 to offset the s++ + } + break; + default: + PR_RunError (progfuncs, "OP_CASE with bad/missing OP_SWITCH %i", swtchtype); + break; + } + break; + case OP_CASERANGE: + switch(swtchtype) + { + case OP_SWITCH_F: + if (swtch->_float >= OPA->_float && swtch->_float <= OPB->_float) + { + RUNAWAYCHECK(); + st += (sofs)st->c-1; // -1 to offset the s++ + } + break; + default: + PR_RunError (progfuncs, "OP_CASERANGE with bad/missing OP_SWITCH %i", swtchtype); + } + break; + + + + + + + + + + case OP_BITAND_IF: + OPC->_int = (OPA->_int & (int)OPB->_float); + break; + case OP_BITOR_IF: + OPC->_int = (OPA->_int | (int)OPB->_float); + break; + case OP_BITAND_FI: + OPC->_int = ((int)OPA->_float & OPB->_int); + break; + case OP_BITOR_FI: + OPC->_int = ((int)OPA->_float | OPB->_int); + break; + + case OP_MUL_IF: + OPC->_float = (OPA->_int * OPB->_float); + break; + case OP_MUL_FI: + OPC->_float = (OPA->_float * OPB->_int); + break; + + case OP_MUL_VI: + OPC->_vector[0] = OPA->_vector[0] * OPB->_int; + OPC->_vector[1] = OPA->_vector[0] * OPB->_int; + OPC->_vector[2] = OPA->_vector[0] * OPB->_int; + break; + case OP_MUL_IV: + OPC->_vector[0] = OPB->_int * OPA->_vector[0]; + OPC->_vector[1] = OPB->_int * OPA->_vector[1]; + OPC->_vector[2] = OPB->_int * OPA->_vector[2]; + break; + + case OP_DIV_IF: + OPC->_float = (OPA->_int / OPB->_float); + break; + case OP_DIV_FI: + OPC->_float = (OPA->_float / OPB->_int); + break; + + case OP_AND_I: + OPC->_int = (OPA->_int && OPB->_int); + break; + case OP_OR_I: + OPC->_int = (OPA->_int || OPB->_int); + break; + + case OP_AND_IF: + OPC->_int = (OPA->_int && OPB->_float); + break; + case OP_OR_IF: + OPC->_int = (OPA->_int || OPB->_float); + break; + + case OP_AND_FI: + OPC->_int = (OPA->_float && OPB->_int); + break; + case OP_OR_FI: + OPC->_int = (OPA->_float || OPB->_int); + break; + + case OP_NOT_I: + OPC->_int = !OPA->_int; + break; + + case OP_NE_IF: + OPC->_int = (OPA->_int != OPB->_float); + break; + case OP_NE_FI: + OPC->_int = (OPA->_float != OPB->_int); + break; + + case OP_GSTOREP_I: + case OP_GSTOREP_F: + case OP_GSTOREP_ENT: + case OP_GSTOREP_FLD: // integers + case OP_GSTOREP_S: + case OP_GSTOREP_FNC: // pointers + case OP_GSTOREP_V: + case OP_GADDRESS: + case OP_GLOAD_I: + case OP_GLOAD_F: + case OP_GLOAD_FLD: + case OP_GLOAD_ENT: + case OP_GLOAD_S: + case OP_GLOAD_FNC: + pr_xstatement = st-pr_statements; + PR_RunError(progfuncs, "Extra opcode not implemented\n"); + break; + + case OP_BOUNDCHECK: + if ((unsigned int)OPA->_int < (unsigned int)st->c || (unsigned int)OPA->_int >= (unsigned int)st->b) + { + pr_xstatement = st-pr_statements; + PR_RunError(progfuncs, "Progs boundcheck failed. Value is %i.", OPA->_int); + } + break; +/* case OP_PUSH: + OPC->_int = ENGINEPOINTER(&localstack[localstack_used+pr_spushed]); + pr_spushed += OPA->_int; + if (pr_spushed + localstack_used >= LOCALSTACK_SIZE) + { + pr_spushed = 0; + pr_xstatement = st-pr_statements; + PR_RunError(progfuncs, "Progs pushed too much"); + } + break; + case OP_POP: + pr_spushed -= OPA->_int; + if (pr_spushed < 0) + { + pr_spushed = 0; + pr_xstatement = st-pr_statements; + PR_RunError(progfuncs, "Progs poped more than it pushed"); + } + break; +*/ + default: + if (st->op & 0x8000) //break point! + { + pr_xstatement = s = st-pr_statements; + + printf("Break point hit in %s.\n", pr_xfunction->s_name+progfuncs->stringtable); + if (pr_trace<1) + pr_trace=1; //this is what it's for + + s = ShowStep(progfuncs, s); + st = &pr_statements[s]; //let the user move execution + pr_xstatement = s = st-pr_statements; + +#if 0 //fakeop stuff - not practical, the rest of the code is more optimised, st needs to point at the correct statement + memcpy(&fakeop, st, sizeof(dstatement_t)); //don't hit the new statement as a break point, cos it's probably the same one. + fakeop.op &= ~0x8000; + st = &fakeop; //a little remapping... +#else + st->op &= ~0x8000; //just remove the breakpoint and go around again, but this time in the debugger. +#endif + + goto reeval; //reexecute + } + pr_xstatement = st-pr_statements; + PR_RunError (progfuncs, "Bad opcode %i", st->op); + } +} + + +#undef cont +#undef reeval +#undef st +#undef pr_statements +#undef fakeop +#undef dstatement_t +#undef sofs +#undef uofs + +#undef ENGINEPOINTER +#undef QCPOINTER +#undef QCPOINTERM + diff --git a/misc/mediasource/extra/fteqcc-src/gui.h b/misc/mediasource/extra/fteqcc-src/gui.h new file mode 100644 index 00000000..66ad9c56 --- /dev/null +++ b/misc/mediasource/extra/fteqcc-src/gui.h @@ -0,0 +1,21 @@ +void GoToDefinition(char *name); +void EditFile(char *name, int line); + +void GUI_SetDefaultOpts(void); +int GUI_BuildParms(char *args, char **argv); + +unsigned char *QCC_ReadFile (char *fname, void *buffer, int len); +int QCC_FileSize (char *fname); +pbool QCC_WriteFile (char *name, void *data, int len); +void GUI_DialogPrint(char *title, char *text); + +extern char parameters[16384]; + +extern char progssrcname[256]; +extern char progssrcdir[256]; + +extern pbool fl_hexen2; +extern pbool fl_autohighlight; +extern pbool fl_compileonstart; +extern pbool fl_showall; +extern pbool fl_log; diff --git a/misc/mediasource/extra/fteqcc-src/hash.c b/misc/mediasource/extra/fteqcc-src/hash.c new file mode 100644 index 00000000..2b7b9120 --- /dev/null +++ b/misc/mediasource/extra/fteqcc-src/hash.c @@ -0,0 +1,272 @@ +#include "hash.h" +#include +#include + +#ifndef _WIN32 +#ifndef stricmp +#define stricmp strcasecmp +#endif +#endif + +// hash init assumes we get clean memory +void Hash_InitTable(hashtable_t *table, int numbucks, void *mem) +{ + table->numbuckets = numbucks; + table->bucket = (bucket_t **)mem; +} + +int Hash_Key(const char *name, int modulus) +{ //fixme: optimize. + unsigned int key; + for (key=0;*name; name++) + key += ((key<<3) + (key>>28) + *name); + + return (int)(key%modulus); +} +int Hash_KeyInsensative(const char *name, int modulus) +{ //fixme: optimize. + unsigned int key; + for (key=0;*name; name++) + { + if (*name >= 'A' && *name <= 'Z') + key += ((key<<3) + (key>>28) + (*name-'A'+'a')); + else + key += ((key<<3) + (key>>28) + *name); + } + + return (int)(key%modulus); +} + +void *Hash_Get(hashtable_t *table, const char *name) +{ + int bucknum = Hash_Key(name, table->numbuckets); + bucket_t *buck; + + buck = table->bucket[bucknum]; + + while(buck) + { + if (!STRCMP(name, buck->key.string)) + return buck->data; + + buck = buck->next; + } + return NULL; +} +void *Hash_GetInsensative(hashtable_t *table, const char *name) +{ + int bucknum = Hash_KeyInsensative(name, table->numbuckets); + bucket_t *buck; + + buck = table->bucket[bucknum]; + + while(buck) + { + if (!stricmp(name, buck->key.string)) + return buck->data; + + buck = buck->next; + } + return NULL; +} +void *Hash_GetKey(hashtable_t *table, int key) +{ + int bucknum = key%table->numbuckets; + bucket_t *buck; + + buck = table->bucket[bucknum]; + + while(buck) + { + if (buck->key.value == key) + return buck->data; + + buck = buck->next; + } + return NULL; +} +void *Hash_GetNext(hashtable_t *table, char *name, void *old) +{ + int bucknum = Hash_Key(name, table->numbuckets); + bucket_t *buck; + + buck = table->bucket[bucknum]; + + while(buck) + { + if (!STRCMP(name, buck->key.string)) + { + if (buck->data == old) //found the old one + break; + } + + buck = buck->next; + } + if (!buck) + return NULL; + + buck = buck->next;//don't return old + while(buck) + { + if (!STRCMP(name, buck->key.string)) + return buck->data; + + buck = buck->next; + } + return NULL; +} +void *Hash_GetNextInsensative(hashtable_t *table, char *name, void *old) +{ + int bucknum = Hash_KeyInsensative(name, table->numbuckets); + bucket_t *buck; + + buck = table->bucket[bucknum]; + + while(buck) + { + if (!STRCMP(name, buck->key.string)) + { + if (buck->data == old) //found the old one + break; + } + + buck = buck->next; + } + if (!buck) + return NULL; + + buck = buck->next;//don't return old + while(buck) + { + if (!STRCMP(name, buck->key.string)) + return buck->data; + + buck = buck->next; + } + return NULL; +} + + +void *Hash_Add(hashtable_t *table, char *name, void *data, bucket_t *buck) +{ + int bucknum = Hash_Key(name, table->numbuckets); + + buck->data = data; + buck->key.string = name; + buck->next = table->bucket[bucknum]; + table->bucket[bucknum] = buck; + + return buck; +} +void *Hash_AddInsensative(hashtable_t *table, char *name, void *data, bucket_t *buck) +{ + int bucknum = Hash_KeyInsensative(name, table->numbuckets); + + buck->data = data; + buck->key.string = name; + buck->next = table->bucket[bucknum]; + table->bucket[bucknum] = buck; + + return buck; +} +void *Hash_AddKey(hashtable_t *table, int key, void *data, bucket_t *buck) +{ + int bucknum = key%table->numbuckets; + + buck->data = data; + buck->key.value = key; + buck->next = table->bucket[bucknum]; + table->bucket[bucknum] = buck; + + return buck; +} + +void Hash_Remove(hashtable_t *table, char *name) +{ + int bucknum = Hash_Key(name, table->numbuckets); + bucket_t *buck; + + buck = table->bucket[bucknum]; + if(!buck) + return; + + if (!STRCMP(name, buck->key.string)) + { + table->bucket[bucknum] = buck->next; + return; + } + + + while(buck->next) + { + if (!STRCMP(name, buck->next->key.string)) + { + buck->next = buck->next->next; + return; + } + + buck = buck->next; + } + return; +} + +void Hash_RemoveData(hashtable_t *table, char *name, void *data) +{ + int bucknum = Hash_Key(name, table->numbuckets); + bucket_t *buck; + + buck = table->bucket[bucknum]; + if(!buck) + return; + + if (buck->data == data) + if (!STRCMP(name, buck->key.string)) + { + table->bucket[bucknum] = buck->next; + return; + } + + + while(buck->next) + { + if (buck->next->data == data) + if (!STRCMP(name, buck->next->key.string)) + { + buck->next = buck->next->next; + return; + } + + buck = buck->next; + } + return; +} + + +void Hash_RemoveKey(hashtable_t *table, int key) +{ + int bucknum = key%table->numbuckets; + bucket_t *buck; + + buck = table->bucket[bucknum]; + if(!buck) + return; + + if (buck->key.value == key) + { + table->bucket[bucknum] = buck->next; + return; + } + + + while(buck->next) + { + if (buck->next->key.value == key) + { + buck->next = buck->next->next; + return; + } + + buck = buck->next; + } + return; +} diff --git a/misc/mediasource/extra/fteqcc-src/hash.h b/misc/mediasource/extra/fteqcc-src/hash.h new file mode 100644 index 00000000..778e67a5 --- /dev/null +++ b/misc/mediasource/extra/fteqcc-src/hash.h @@ -0,0 +1,38 @@ +//============================= +//David's hash tables +//string based. + +#ifndef HASH_H__ +#define HASH_H__ + +#define Hash_BytesForBuckets(b) (sizeof(bucket_t)*b) + +#define STRCMP(s1,s2) (((*s1)!=(*s2)) || strcmp(s1+1,s2+1)) //saves about 2-6 out of 120 - expansion of idea from fastqcc +typedef struct bucket_s { + void *data; + union { + const char *string; + int value; + } key; + struct bucket_s *next; +} bucket_t; +typedef struct hashtable_s { + int numbuckets; + bucket_t **bucket; +} hashtable_t; + +void Hash_InitTable(hashtable_t *table, int numbucks, void *mem); //mem must be 0 filled. (memset(mem, 0, size)) +int Hash_Key(const char *name, int modulus); +void *Hash_Get(hashtable_t *table, const char *name); +void *Hash_GetInsensative(hashtable_t *table, const char *name); +void *Hash_GetKey(hashtable_t *table, int key); +void *Hash_GetNext(hashtable_t *table, char *name, void *old); +void *Hash_GetNextInsensative(hashtable_t *table, char *name, void *old); +void *Hash_Add(hashtable_t *table, char *name, void *data, bucket_t *buck); +void *Hash_AddInsensative(hashtable_t *table, char *name, void *data, bucket_t *buck); +void Hash_Remove(hashtable_t *table, char *name); +void Hash_RemoveData(hashtable_t *table, char *name, void *data); +void Hash_RemoveKey(hashtable_t *table, int key); +void *Hash_AddKey(hashtable_t *table, int key, void *data, bucket_t *buck); + +#endif diff --git a/misc/mediasource/extra/fteqcc-src/initlib.c b/misc/mediasource/extra/fteqcc-src/initlib.c new file mode 100644 index 00000000..1ca5728b --- /dev/null +++ b/misc/mediasource/extra/fteqcc-src/initlib.c @@ -0,0 +1,834 @@ +#define PROGSUSED +#include "progsint.h" +#include + +typedef struct prmemb_s { + struct prmemb_s *prev; + int level; +} prmemb_t; +void *PRHunkAlloc(progfuncs_t *progfuncs, int ammount) +{ + prmemb_t *mem; + ammount = sizeof(prmemb_t)+((ammount + 3)&~3); + mem = memalloc(ammount); + memset(mem, 0, ammount); + mem->prev = memb; + if (!memb) + mem->level = 1; + else + mem->level = ((prmemb_t *)memb)->level+1; + memb = mem; + + return ((char *)mem)+sizeof(prmemb_t); +} + +int PRHunkMark(progfuncs_t *progfuncs) +{ + return ((prmemb_t *)memb)->level; +} +void PRHunkFree(progfuncs_t *progfuncs, int mark) +{ + prmemb_t *omem; + while(memb) + { + if (memb->level <= mark) + return; + + omem = memb; + memb = memb->prev; + memfree(omem); + } + return; +} + +//for 64bit systems. :) +//addressable memory is memory available to the vm itself for writing. +//once allocated, it cannot be freed for the lifetime of the VM. +void *PRAddressableAlloc(progfuncs_t *progfuncs, int ammount) +{ + ammount = (ammount + 4)&~3; //round up to 4 + if (addressableused + ammount > addressablesize) + Sys_Error("Not enough addressable memory for progs VM"); + + addressableused += ammount; + +#ifdef _WIN32 + if (!VirtualAlloc (addressablehunk, addressableused, MEM_COMMIT, PAGE_READWRITE)) + Sys_Error("VirtualAlloc failed. Blame windows."); +#endif + + return &addressablehunk[addressableused-ammount]; +} + +void PRAddressableFlush(progfuncs_t *progfuncs, int totalammount) +{ + addressableused = 0; + if (totalammount < 0) //flush + { + totalammount = addressablesize; +// return; + } + + if (addressablehunk) +#ifdef _WIN32 + VirtualFree(addressablehunk, 0, MEM_RELEASE); //doesn't this look complicated? :p + addressablehunk = VirtualAlloc (NULL, totalammount, MEM_RESERVE, PAGE_NOACCESS); +#else + free(addressablehunk); + addressablehunk = malloc(totalammount); //linux will allocate-on-use anyway, which is handy. +// memset(addressablehunk, 0xff, totalammount); +#endif + if (!addressablehunk) + Sys_Error("Out of memory\n"); + addressablesize = totalammount; +} + +int PR_InitEnts(progfuncs_t *progfuncs, int max_ents) +{ + maxedicts = max_ents; + + sv_num_edicts = 0; + + max_fields_size = fields_size; + + prinst->edicttable = PRHunkAlloc(progfuncs, maxedicts*sizeof(struct edicts_s *)); + sv_edicts = PRHunkAlloc(progfuncs, externs->edictsize); + prinst->edicttable[0] = sv_edicts; + ((edictrun_t*)prinst->edicttable[0])->fields = PRAddressableAlloc(progfuncs, max_fields_size); + QC_ClearEdict(progfuncs, sv_edicts); + sv_num_edicts = 1; + + if (externs->entspawn) + externs->entspawn((struct edict_s *)sv_edicts, false); + + return max_fields_size; +} +edictrun_t tempedict; //used as a safty buffer +float tempedictfields[2048]; + +void PR_Configure (progfuncs_t *progfuncs, int addressable_size, int max_progs) //can be used to wipe all memory +{ + unsigned int i; + edictrun_t *e; + +// int a; +#ifdef QCJIT + prinst->usejit = true; +#endif + + max_fields_size=0; + fields_size = 0; + progfuncs->stringtable = 0; + QC_StartShares(progfuncs); + QC_InitShares(progfuncs); + + for ( i=1 ; iedicttable[i]); + prinst->edicttable[i] = NULL; +// e->entnum = i; + if (e) + memfree(e); + } + + PRHunkFree(progfuncs, 0); //clear mem - our hunk may not be a real hunk. + if (addressable_size<0) + addressable_size = 8*1024*1024; + PRAddressableFlush(progfuncs, addressable_size); + + pr_progstate = PRHunkAlloc(progfuncs, sizeof(progstate_t) * max_progs); + +/* for(a = 0; a < max_progs; a++) + { + pr_progstate[a].progs = NULL; + } +*/ + + maxprogs = max_progs; + pr_typecurrent=-1; + + prinst->reorganisefields = false; + + maxedicts = 1; + prinst->edicttable = &sv_edicts; + sv_num_edicts = 1; //set up a safty buffer so things won't go horribly wrong too often + sv_edicts=(struct edict_s *)&tempedict; + tempedict.readonly = true; + tempedict.fields = tempedictfields; + tempedict.isfree = false; +} + + + +struct globalvars_s *PR_globals (progfuncs_t *progfuncs, progsnum_t pnum) +{ + if (pnum < 0) + { + if (!current_progstate) + return NULL; //err.. you've not loaded one yet. + return (struct globalvars_s *)current_progstate->globals; + } + return (struct globalvars_s *)pr_progstate[pnum].globals; +} + +struct entvars_s *PR_entvars (progfuncs_t *progfuncs, struct edict_s *ed) +{ + if (((edictrun_t *)ed)->isfree) + return NULL; + + return (struct entvars_s *)edvars(ed); +} + +int PR_GetFuncArgCount(progfuncs_t *progfuncs, func_t func) +{ + unsigned int pnum; + unsigned int fnum; + dfunction_t *f; + + pnum = (func & 0xff000000)>>24; + fnum = (func & 0x00ffffff); + + if (pnum >= (unsigned)maxprogs || !pr_progstate[pnum].functions) + return -1; + else if (fnum >= pr_progstate[pnum].progs->numfunctions) + return -1; + else + { + f = pr_progstate[pnum].functions + fnum; + return f->numparms; + } +} + +func_t PR_FindFunc(progfuncs_t *progfuncs, char *funcname, progsnum_t pnum) +{ + dfunction_t *f=NULL; + if (pnum == PR_ANY) + { + for (pnum = 0; (unsigned)pnum < maxprogs; pnum++) + { + if (!pr_progstate[pnum].progs) + continue; + f = ED_FindFunction(progfuncs, funcname, &pnum, pnum); + if (f) + break; + } + } + else if (pnum == PR_ANYBACK) //run backwards + { + for (pnum = maxprogs-1; pnum >= 0; pnum--) + { + if (!pr_progstate[pnum].progs) + continue; + f = ED_FindFunction(progfuncs, funcname, &pnum, pnum); + if (f) + break; + } + } + else + f = ED_FindFunction(progfuncs, funcname, &pnum, pnum); + if (!f) + return 0; + + { + ddef16_t *var16; + ddef32_t *var32; + switch(pr_progstate[pnum].intsize) + { + case 24: + case 16: + var16 = ED_FindTypeGlobalFromProgs16(progfuncs, funcname, pnum, ev_function); //we must make sure we actually have a function def - 'light' is defined as a field before it is defined as a function. + if (!var16) + return (f - pr_progstate[pnum].functions) | (pnum << 24); + return *(int *)&pr_progstate[pnum].globals[var16->ofs]; + case 32: + var32 = ED_FindTypeGlobalFromProgs32(progfuncs, funcname, pnum, ev_function); //we must make sure we actually have a function def - 'light' is defined as a field before it is defined as a function. + if (!var32) + return (f - pr_progstate[pnum].functions) | (pnum << 24); + return *(int *)&pr_progstate[pnum].globals[var32->ofs]; + } + Sys_Error("Error with def size (PR_FindFunc)"); + } + return 0; +} + +eval_t *PR_FindGlobal(progfuncs_t *progfuncs, char *globname, progsnum_t pnum) +{ + unsigned int i; + ddef16_t *var16; + ddef32_t *var32; + if (pnum == PR_CURRENT) + pnum = pr_typecurrent; + if (pnum == PR_ANY) + { + eval_t *ev; + for (i = 0; i < maxprogs; i++) + { + if (!pr_progstate[i].progs) + continue; + ev = PR_FindGlobal(progfuncs, globname, i); + if (ev) + return ev; + } + return NULL; + } + if (pnum < 0 || (unsigned)pnum >= maxprogs || !pr_progstate[pnum].progs) + return NULL; + switch(pr_progstate[pnum].intsize) + { + case 16: + case 24: + if (!(var16 = ED_FindGlobalFromProgs16(progfuncs, globname, pnum))) + return NULL; + + return (eval_t *)&pr_progstate[pnum].globals[var16->ofs]; + case 32: + if (!(var32 = ED_FindGlobalFromProgs32(progfuncs, globname, pnum))) + return NULL; + + return (eval_t *)&pr_progstate[pnum].globals[var32->ofs]; + } + Sys_Error("Error with def size (PR_FindGlobal)"); + return NULL; +} + +void SetGlobalEdict(progfuncs_t *progfuncs, struct edict_s *ed, int ofs) +{ + ((int*)pr_globals)[ofs] = EDICT_TO_PROG(progfuncs, ed); +} + +char *PR_VarString (progfuncs_t *progfuncs, int first) +{ + int i; + static char out[1024]; + char *s; + + out[0] = 0; + for (i=first ; istringtable; + strcat (out, s); + +//#ifdef PARANOID + if (strlen(out)+1 >= sizeof(out)) + Sys_Error("VarString (builtin call ending with strings) exceeded maximum string length of %i chars", sizeof(out)); +//#endif + } + } + return out; +} + +int PR_QueryField (progfuncs_t *progfuncs, unsigned int fieldoffset, etype_t *type, char **name, evalc_t *fieldcache) +{ + fdef_t *var; + var = ED_FieldAtOfs(progfuncs, fieldoffset); + if (!var) + return false; + + if (type) + *type = var->type & ~(DEF_SAVEGLOBAL|DEF_SHARED); + if (name) + *name = var->name; + if (fieldcache) + { + fieldcache->ofs32 = var; + fieldcache->varname = var->name; + } + + return true; +} + +eval_t *GetEdictFieldValue(progfuncs_t *progfuncs, struct edict_s *ed, char *name, evalc_t *cache) +{ + fdef_t *var; + if (!cache) + { + var = ED_FindField(progfuncs, name); + if (!var) + return NULL; + return (eval_t *) &(((int*)(((edictrun_t*)ed)->fields))[var->ofs]); + } + if (!cache->varname) + { + cache->varname = name; + var = ED_FindField(progfuncs, name); + if (!var) + { + cache->ofs32 = NULL; + return NULL; + } + cache->ofs32 = var; + cache->varname = var->name; + if (!ed) + return (void*)~0; //something not null + return (eval_t *) &(((int*)(((edictrun_t*)ed)->fields))[var->ofs]); + } + if (cache->ofs32 == NULL) + return NULL; + return (eval_t *) &(((int*)(((edictrun_t*)ed)->fields))[cache->ofs32->ofs]); +} + +struct edict_s *ProgsToEdict (progfuncs_t *progfuncs, int progs) +{ + if ((unsigned)progs >= (unsigned)maxedicts) + { + printf("Bad entity index %i\n", progs); + progs = 0; + } + return (struct edict_s *)PROG_TO_EDICT(progfuncs, progs); +} +int EdictToProgs (progfuncs_t *progfuncs, struct edict_s *ed) +{ + return EDICT_TO_PROG(progfuncs, ed); +} + +string_t PR_StringToProgs (progfuncs_t *progfuncs, char *str) +{ + char **ntable; + int i, free=-1; + + if (!str) + return 0; + +// if (str-progfuncs->stringtable < progfuncs->stringtablesize) +// return str - progfuncs->stringtable; + + for (i = prinst->numallocedstrings-1; i >= 0; i--) + { + if (prinst->allocedstrings[i] == str) + return (string_t)((unsigned int)i | 0x80000000); + if (!prinst->allocedstrings[i]) + free = i; + } + + if (free != -1) + { + i = free; + prinst->allocedstrings[i] = str; + return (string_t)((unsigned int)i | 0x80000000); + } + + prinst->maxallocedstrings += 1024; + ntable = memalloc(sizeof(char*) * prinst->maxallocedstrings); + memcpy(ntable, prinst->allocedstrings, sizeof(char*) * prinst->numallocedstrings); + memset(ntable + prinst->numallocedstrings, 0, sizeof(char*) * (prinst->maxallocedstrings - prinst->numallocedstrings)); + prinst->numallocedstrings = prinst->maxallocedstrings; + if (prinst->allocedstrings) + memfree(prinst->allocedstrings); + prinst->allocedstrings = ntable; + + for (i = prinst->numallocedstrings-1; i >= 0; i--) + { + if (!prinst->allocedstrings[i]) + { + prinst->allocedstrings[i] = str; + return (string_t)((unsigned int)i | 0x80000000); + } + } + + return 0; +} + +char *PR_RemoveProgsString (progfuncs_t *progfuncs, string_t str) +{ + char *ret; + + //input string is expected to be an allocated string + //if its a temp, or a constant, just return NULL. + if ((unsigned int)str & 0xc0000000) + { + if ((unsigned int)str & 0x80000000) + { + int i = str & ~0x80000000; + if (i >= prinst->numallocedstrings) + { + pr_trace = 1; + return NULL; + } + if (prinst->allocedstrings[i]) + { + ret = prinst->allocedstrings[i]; + prinst->allocedstrings[i] = NULL; //remove it + return ret; + } + else + { + pr_trace = 1; + return NULL; //urm, was freed... + } + } + } + pr_trace = 1; + return NULL; +} + +char *PR_StringToNative (progfuncs_t *progfuncs, string_t str) +{ + if ((unsigned int)str & 0xc0000000) + { + if ((unsigned int)str & 0x80000000) + { + int i = str & ~0x80000000; + if (i >= prinst->numallocedstrings) + { + pr_trace = 1; + return ""; + } + if (prinst->allocedstrings[i]) + return prinst->allocedstrings[i]; + else + { + pr_trace = 1; + return ""; //urm, was freed... + } + } + if ((unsigned int)str & 0x40000000) + { + int i = str & ~0x40000000; + if (i >= prinst->numtempstrings) + { + pr_trace = 1; + return ""; + } + return prinst->tempstrings[i]; + } + } + + if (str >= progfuncs->stringtablesize) + { + pr_trace = 1; + return ""; + } + return progfuncs->stringtable + str; +} + + +string_t PR_AllocTempString (progfuncs_t *progfuncs, char *str) +{ + char **ntable; + int newmax; + int i; + + if (!str) + return 0; + + if (prinst->numtempstrings == prinst->maxtempstrings) + { + newmax = prinst->maxtempstrings += 1024; + prinst->maxtempstrings += 1024; + ntable = memalloc(sizeof(char*) * newmax); + memcpy(ntable, prinst->tempstrings, sizeof(char*) * prinst->numtempstrings); + prinst->maxtempstrings = newmax; + if (prinst->tempstrings) + memfree(prinst->tempstrings); + prinst->tempstrings = ntable; + } + + i = prinst->numtempstrings; + if (i == 0x10000000) + return 0; + + prinst->numtempstrings++; + + prinst->tempstrings[i] = memalloc(strlen(str)+1); + strcpy(prinst->tempstrings[i], str); + + return (string_t)((unsigned int)i | 0x40000000); +} + +void PR_FreeTemps (progfuncs_t *progfuncs, int depth) +{ + int i; + if (depth > prinst->numtempstrings) + { + Sys_Error("QC Temp stack inverted\n"); + return; + } + for (i = depth; i < prinst->numtempstrings; i++) + { + memfree(prinst->tempstrings[i]); + } + + prinst->numtempstrings = depth; +} + + +struct qcthread_s *PR_ForkStack (progfuncs_t *progfuncs); +void PR_ResumeThread (progfuncs_t *progfuncs, struct qcthread_s *thread); +void PR_AbortStack (progfuncs_t *progfuncs); + + +void RegisterBuiltin(progfuncs_t *progfncs, char *name, builtin_t func); + +progfuncs_t deffuncs = { + PROGSTRUCT_VERSION, + PR_Configure, + PR_LoadProgs, + PR_InitEnts, + PR_ExecuteProgram, + PR_SwitchProgs, + PR_globals, + PR_entvars, + PR_RunError, + ED_Print, + ED_Alloc, + ED_Free, + + EDICT_NUM, + NUM_FOR_EDICT, + + + SetGlobalEdict, + + PR_VarString, + + NULL, + PR_FindFunc, +#ifdef MINIMAL + NULL, + NULL, +#else + Comp_Begin, + Comp_Continue, +#endif + + filefromprogs, + NULL,//filefromnewprogs, + + SaveEnts, + LoadEnts, + + SaveEnt, + RestoreEnt, + + PR_FindGlobal, + ED_NewString, + (void*)PRHunkAlloc, + + GetEdictFieldValue, + ProgsToEdict, + EdictToProgs, + + EvaluateDebugString, + + NULL, + PR_StackTrace, + + PR_ToggleBreakpoint, + 0, + NULL, +#ifdef MINIMAL + NULL, +#else + Decompile, +#endif + NULL, + NULL, + RegisterBuiltin, + + 0, + 0, + + PR_ForkStack, + PR_ResumeThread, + PR_AbortStack, + + 0, + + QC_RegisterFieldVar, + + 0, + 0, + + PR_AllocTempString, + + PR_StringToProgs, + PR_StringToNative, + 0, + PR_QueryField, + QC_ClearEdict +}; +#undef printf + +//defs incase following structure is not passed. +struct edict_s *safesv_edicts; +int safesv_num_edicts; +double safetime=0; + +progexterns_t defexterns = { + PROGSTRUCT_VERSION, + + NULL, //char *(*ReadFile) (char *fname, void *buffer, int len); + NULL, //int (*FileSize) (char *fname); //-1 if file does not exist + NULL, //bool (*WriteFile) (char *name, void *data, int len); + printf, //void (*printf) (char *, ...); + (void*)exit, //void (*Sys_Error) (char *, ...); + NULL, //void (*Abort) (char *, ...); + sizeof(edictrun_t), //int edictsize; //size of edict_t + + NULL, //void (*entspawn) (struct edict_s *ent); //ent has been spawned, but may not have all the extra variables (that may need to be set) set + NULL, //bool (*entcanfree) (struct edict_s *ent); //return true to stop ent from being freed + NULL, //void (*stateop) (float var, func_t func); + NULL, + NULL, + NULL, + + //used when loading a game + NULL, //builtin_t *(*builtinsfor) (int num); //must return a pointer to the builtins that were used before the state was saved. + NULL, //void (*loadcompleate) (int edictsize); //notification to reset any pointers. + + (void*)malloc, //void *(*memalloc) (int size); //small string allocation malloced and freed randomly by the executor. (use memalloc if you want) + free, //void (*memfree) (void * mem); + + + NULL, //builtin_t *globalbuiltins; //these are available to all progs + 0, //int numglobalbuiltins; + + PR_NOCOMPILE, + + &safetime, //double *gametime; + + &safesv_edicts, //struct edict_s **sv_edicts; + &safesv_num_edicts, //int *sv_num_edicts; + + NULL, //int (*useeditor) (char *filename, int line, int nump, char **parms); +}; + +//progfuncs_t *progfuncs = NULL; +#undef memfree +#undef prinst +#undef extensionbuiltin +#undef field +#undef shares +#undef sv_num_edicts + + +#ifdef QCLIBDLL_EXPORTS +__declspec(dllexport) +#endif +void CloseProgs(progfuncs_t *inst) +{ +// extensionbuiltin_t *eb; + void (VARGS *f) (void *); + + unsigned int i; + edictrun_t *e; + + f = inst->parms->memfree; + + for ( i=1 ; imaxedicts; i++) + { + e = (edictrun_t *)(inst->prinst->edicttable[i]); + inst->prinst->edicttable[i] = NULL; + if (e) + { +// e->entnum = i; + f(e); + } + } + + PRHunkFree(inst, 0); + +#ifdef _WIN32 + VirtualFree(inst->addressablehunk, 0, MEM_RELEASE); //doesn't this look complicated? :p +#else + free(inst->addressablehunk); +#endif + +/* + while(inst->prinst->extensionbuiltin) + { + eb = inst->prinst->extensionbuiltin->prev; + f(inst->prinst->extensionbuiltin); + inst->prinst->extensionbuiltin = eb; + } +*/ + if (inst->prinst->field) + f(inst->prinst->field); + if (inst->prinst->shares) + f(inst->prinst->shares); //free memory + f(inst->prinst); + f(inst); +} + +void RegisterBuiltin(progfuncs_t *progfuncs, char *name, builtin_t func) +{ +/* + extensionbuiltin_t *eb; + eb = memalloc(sizeof(extensionbuiltin_t)); + eb->prev = progfuncs->prinst->extensionbuiltin; + progfuncs->prinst->extensionbuiltin = eb; + eb->name = name; + eb->func = func; +*/ +} + +#ifndef WIN32 +#define QCLIBINT //don't use dllspecifications +#endif + +#if defined(QCLIBDLL_EXPORTS) +__declspec(dllexport) +#endif +progfuncs_t * InitProgs(progexterns_t *ext) +{ + progfuncs_t *funcs; + + if (!ext) + ext = &defexterns; + else + { + int i; + if (ext->progsversion > PROGSTRUCT_VERSION) + return NULL; + + for (i=0;imemalloc(sizeof(progfuncs_t)); + memcpy(funcs, &deffuncs, sizeof(progfuncs_t)); + + funcs->prinst = ext->memalloc(sizeof(prinst_t)); + memset(funcs->prinst,0, sizeof(prinst_t)); + + funcs->pr_trace = &funcs->prinst->pr_trace; + funcs->progstate = &funcs->pr_progstate; + funcs->callargc = &funcs->pr_argc; + + funcs->parms = ext; + + SetEndian(); + + return funcs; +} + + + + + + + + + + + + + + + + +#ifdef QCC +void main (int argc, char **argv) +{ + progexterns_t ext; + + progfuncs_t *funcs; + funcs = InitProgs(&ext); + if (funcs->PR_StartCompile(argc, argv)) + while(funcs->PR_ContinueCompile()); +} +#endif diff --git a/misc/mediasource/extra/fteqcc-src/pr_comp.h b/misc/mediasource/extra/fteqcc-src/pr_comp.h new file mode 100644 index 00000000..13b93bce --- /dev/null +++ b/misc/mediasource/extra/fteqcc-src/pr_comp.h @@ -0,0 +1,540 @@ +// this file is shared by the execution and compiler + +/*i'm part way through making this work +I've given up now that I can't work out a way to load pointers. +Setting them should be fine. +*/ +#ifndef __PR_COMP_H__ +#define __PR_COMP_H__ + + +/*this distinction is made as the execution uses c pointers while compiler uses pointers from the start of the string table of the current progs*/ +#ifdef COMPILER +typedef int QCC_string_t; +#else +//typedef char *string_t; +#endif + +//typedef enum {ev_void, ev_string, ev_float, ev_vector, ev_entity, ev_field, ev_function, ev_pointer, ev_integer, ev_struct, ev_union} etype_t; +// 0 1 2 3 4 5 6 7 8 9 10 + +#define OFS_NULL 0 +#define OFS_RETURN 1 +#define OFS_PARM0 4 // leave 3 ofs for each parm to hold vectors +#define OFS_PARM1 7 +#define OFS_PARM2 10 +#define OFS_PARM3 13 +#define OFS_PARM4 16 +#define OFS_PARM5 19 +#define OFS_PARM6 22 +#define OFS_PARM7 25 +#define RESERVED_OFS 28 + + +enum { + OP_DONE, //0 + OP_MUL_F, + OP_MUL_V, + OP_MUL_FV, + OP_MUL_VF, + OP_DIV_F, + OP_ADD_F, + OP_ADD_V, + OP_SUB_F, + OP_SUB_V, + + OP_EQ_F, //10 + OP_EQ_V, + OP_EQ_S, + OP_EQ_E, + OP_EQ_FNC, + + OP_NE_F, + OP_NE_V, + OP_NE_S, + OP_NE_E, + OP_NE_FNC, + + OP_LE, //20 + OP_GE, + OP_LT, + OP_GT, + + OP_LOAD_F, + OP_LOAD_V, + OP_LOAD_S, + OP_LOAD_ENT, + OP_LOAD_FLD, + OP_LOAD_FNC, + + OP_ADDRESS, //30 + + OP_STORE_F, + OP_STORE_V, + OP_STORE_S, + OP_STORE_ENT, + OP_STORE_FLD, + OP_STORE_FNC, + + OP_STOREP_F, + OP_STOREP_V, + OP_STOREP_S, + OP_STOREP_ENT, //40 + OP_STOREP_FLD, + OP_STOREP_FNC, + + OP_RETURN, + OP_NOT_F, + OP_NOT_V, + OP_NOT_S, + OP_NOT_ENT, + OP_NOT_FNC, + OP_IF, + OP_IFNOT, //50 + OP_CALL0, //careful... hexen2 and q1 have different calling conventions + OP_CALL1, //remap hexen2 calls to OP_CALL2H + OP_CALL2, + OP_CALL3, + OP_CALL4, + OP_CALL5, + OP_CALL6, + OP_CALL7, + OP_CALL8, + OP_STATE, //60 + OP_GOTO, + OP_AND, + OP_OR, + + OP_BITAND, + OP_BITOR, + + + //these following ones are Hexen 2 constants. + + OP_MULSTORE_F, + OP_MULSTORE_V, + OP_MULSTOREP_F, + OP_MULSTOREP_V, + + OP_DIVSTORE_F, //70 + OP_DIVSTOREP_F, + + OP_ADDSTORE_F, + OP_ADDSTORE_V, + OP_ADDSTOREP_F, + OP_ADDSTOREP_V, + + OP_SUBSTORE_F, + OP_SUBSTORE_V, + OP_SUBSTOREP_F, + OP_SUBSTOREP_V, + + OP_FETCH_GBL_F, //80 + OP_FETCH_GBL_V, + OP_FETCH_GBL_S, + OP_FETCH_GBL_E, + OP_FETCH_GBL_FNC, + + OP_CSTATE, + OP_CWSTATE, + + OP_THINKTIME, + + OP_BITSET, + OP_BITSETP, + OP_BITCLR, //90 + OP_BITCLRP, + + OP_RAND0, + OP_RAND1, + OP_RAND2, + OP_RANDV0, + OP_RANDV1, + OP_RANDV2, + + OP_SWITCH_F, + OP_SWITCH_V, + OP_SWITCH_S, //100 + OP_SWITCH_E, + OP_SWITCH_FNC, + + OP_CASE, + OP_CASERANGE, + + + + + + //the rest are added + //mostly they are various different ways of adding two vars with conversions. + + OP_CALL1H, + OP_CALL2H, + OP_CALL3H, + OP_CALL4H, + OP_CALL5H, + OP_CALL6H, //110 + OP_CALL7H, + OP_CALL8H, + + + OP_STORE_I, + OP_STORE_IF, + OP_STORE_FI, + + OP_ADD_I, + OP_ADD_FI, + OP_ADD_IF, //110 + + OP_SUB_I, + OP_SUB_FI, + OP_SUB_IF, + + OP_CONV_ITOF, + OP_CONV_FTOI, + OP_CP_ITOF, + OP_CP_FTOI, + OP_LOAD_I, + OP_STOREP_I, + OP_STOREP_IF, //120 + OP_STOREP_FI, + + OP_BITAND_I, + OP_BITOR_I, + + OP_MUL_I, + OP_DIV_I, + OP_EQ_I, + OP_NE_I, + + OP_IFNOT_S, + OP_IF_S, + + OP_NOT_I, //130 + + OP_DIV_VF, + + OP_XOR_I, + OP_RSHIFT_I, + OP_LSHIFT_I, + + OP_GLOBALADDRESS, + OP_POINTER_ADD, //32 bit pointers + + OP_LOADA_F, + OP_LOADA_V, + OP_LOADA_S, + OP_LOADA_ENT, //140 + OP_LOADA_FLD, + OP_LOADA_FNC, + OP_LOADA_I, + + OP_STORE_P, //152... erm.. wait... + OP_LOAD_P, + + OP_LOADP_F, + OP_LOADP_V, + OP_LOADP_S, + OP_LOADP_ENT, + OP_LOADP_FLD, //150 + OP_LOADP_FNC, + OP_LOADP_I, + + OP_LE_I, + OP_GE_I, + OP_LT_I, + OP_GT_I, + + OP_LE_IF, + OP_GE_IF, + OP_LT_IF, + OP_GT_IF, //160 + + OP_LE_FI, + OP_GE_FI, + OP_LT_FI, + OP_GT_FI, + + OP_EQ_IF, + OP_EQ_FI, + + //------------------------------------- + //string manipulation. + OP_ADD_SF, //(char*)c = (char*)a + (float)b + OP_SUB_S, //(float)c = (char*)a - (char*)b + OP_STOREP_C,//(float)c = *(char*)b = (float)a + OP_LOADP_C, //(float)c = *(char*) //170 + //------------------------------------- + + + OP_MUL_IF, + OP_MUL_FI, + OP_MUL_VI, + OP_MUL_IV, + OP_DIV_IF, + OP_DIV_FI, + OP_BITAND_IF, + OP_BITOR_IF, + OP_BITAND_FI, + OP_BITOR_FI, //180 + OP_AND_I, + OP_OR_I, + OP_AND_IF, + OP_OR_IF, + OP_AND_FI, + OP_OR_FI, + OP_NE_IF, + OP_NE_FI, + +//erm... FTEQCC doesn't make use of these... These are for DP. + OP_GSTOREP_I, + OP_GSTOREP_F, //190 + OP_GSTOREP_ENT, + OP_GSTOREP_FLD, // integers + OP_GSTOREP_S, + OP_GSTOREP_FNC, // pointers + OP_GSTOREP_V, + OP_GADDRESS, + OP_GLOAD_I, + OP_GLOAD_F, + OP_GLOAD_FLD, + OP_GLOAD_ENT, //200 + OP_GLOAD_S, + OP_GLOAD_FNC, + OP_BOUNDCHECK, + +//back to ones that we do use. + OP_STOREP_P, + OP_PUSH, //push 4octets onto the local-stack (which is ALWAYS poped on function return). Returns a pointer. + OP_POP, //pop those ones that were pushed (don't over do it). Needs assembler. + + OP_SWITCH_I,//hmm. + OP_GLOAD_V, + + OP_IF_F, + OP_IFNOT_F, + + OP_NUMREALOPS, + + /* + These ops are emulated out, always, and are only present in the compiler. + */ + + OP_BITSET_I, + OP_BITSETP_I, + + OP_MULSTORE_I, + OP_DIVSTORE_I, + OP_ADDSTORE_I, + OP_SUBSTORE_I, + OP_MULSTOREP_I, + OP_DIVSTOREP_I, + OP_ADDSTOREP_I, + OP_SUBSTOREP_I, + + OP_MULSTORE_IF, + OP_MULSTOREP_IF, + OP_DIVSTORE_IF, + OP_DIVSTOREP_IF, + OP_ADDSTORE_IF, + OP_ADDSTOREP_IF, + OP_SUBSTORE_IF, + OP_SUBSTOREP_IF, + + OP_MULSTORE_FI, + OP_MULSTOREP_FI, + OP_DIVSTORE_FI, + OP_DIVSTOREP_FI, + OP_ADDSTORE_FI, + OP_ADDSTOREP_FI, + OP_SUBSTORE_FI, + OP_SUBSTOREP_FI, + + OP_NUMOPS +}; + + +#ifndef COMPILER +typedef struct statement16_s +{ + unsigned short op; + unsigned short a,b,c; +} dstatement16_t; +typedef struct statement32_s +{ + unsigned int op; + unsigned int a,b,c; +} dstatement32_t; +#else +typedef struct QCC_statement16_s +{ + unsigned short op; + unsigned short a,b,c; +} QCC_dstatement16_t; +typedef struct QCC_statement32_s +{ + unsigned int op; + unsigned int a,b,c; +} QCC_dstatement32_t; +#define QCC_dstatement_t QCC_dstatement32_t +#endif + +//these should be the same except the string type +#ifndef COMPILER +typedef struct ddef16_s +{ + unsigned short type; // if DEF_SAVEGLOBAL bit is set + // the variable needs to be saved in savegames + unsigned short ofs; + string_t s_name; +} ddef16_t; + +typedef struct ddef32_s +{ + unsigned int type; // if DEF_SAVEGLOBAL bit is set + // the variable needs to be saved in savegames + unsigned int ofs; + string_t s_name; +} ddef32_t; + +typedef struct fdef_s +{ + unsigned int type; // if DEF_SAVEGLOBAL bit is set + // the variable needs to be saved in savegames + unsigned int ofs; + unsigned int progsofs; //used at loading time, so maching field offsets (unions/members) are positioned at the same runtime offset. + char * name; +} fdef_t; + +typedef void *ddefXX_t; +#else +typedef struct QCC_ddef16_s +{ + unsigned short type; // if DEF_SAVEGLOBAL bit is set + // the variable needs to be saved in savegames + unsigned short ofs; + QCC_string_t s_name; +} QCC_ddef16_t; + +typedef struct QCC_ddef32_s +{ + unsigned int type; // if DEF_SAVEGLOBAL bit is set + // the variable needs to be saved in savegames + unsigned int ofs; + QCC_string_t s_name; +} QCC_ddef32_t; + +#define QCC_ddef_t QCC_ddef32_t +#endif + +#define DEF_SAVEGLOBAL (1<<15) +#define DEF_SHARED (1<<14) + +#define MAX_PARMS 8 + +#ifndef COMPILER +typedef struct +{ + int first_statement; // negative numbers are builtins + int parm_start; + int locals; // total ints of parms + locals + + int profile; // runtime + + string_t s_name; + string_t s_file; // source file defined in + + int numparms; + qbyte parm_size[MAX_PARMS]; +} dfunction_t; +#else +typedef struct +{ + unsigned int first_statement; // negative numbers are builtins + unsigned int parm_start; + int locals; // total ints of parms + locals + + int profile; // runtime + + QCC_string_t s_name; + QCC_string_t s_file; // source file defined in + + int numparms; + qbyte parm_size[MAX_PARMS]; +} QCC_dfunction_t; +#endif + + +#define PROG_VERSION 6 +#define PROG_KKQWSVVERSION 7 +#define PROG_EXTENDEDVERSION 7 +#define PROG_SECONDARYVERSION16 (*(int*)"1FTE" ^ *(int*)"PROG") //something unlikly and still meaningful (to me) +#define PROG_SECONDARYVERSION32 (*(int*)"1FTE" ^ *(int*)"32B ") //something unlikly and still meaningful (to me) +typedef struct +{ + int version; + int crc; // check of header file + + unsigned int ofs_statements; //comp 1 + unsigned int numstatements; // statement 0 is an error + + unsigned int ofs_globaldefs; //comp 2 + unsigned int numglobaldefs; + + unsigned int ofs_fielddefs; //comp 4 + unsigned int numfielddefs; + + unsigned int ofs_functions; //comp 8 + unsigned int numfunctions; // function 0 is an empty + + unsigned int ofs_strings; //comp 16 + unsigned int numstrings; // first string is a null string + + unsigned int ofs_globals; //comp 32 + unsigned int numglobals; + + unsigned int entityfields; + + //debug / version 7 extensions + unsigned int ofsfiles; //non list format. no comp + unsigned int ofslinenums; //numstatements big //comp 64 + unsigned int ofsbodylessfuncs; //no comp + unsigned int numbodylessfuncs; + + unsigned int ofs_types; //comp 128 + unsigned int numtypes; + unsigned int blockscompressed; + + int secondaryversion; //Constant - to say that any version 7 progs are actually ours, not someone else's alterations. +} dprograms_t; +#define standard_dprograms_t_size ((int)&((dprograms_t*)NULL)->ofsfiles) + +#endif + + + + + +typedef struct { + char filename[128]; + int size; + int compsize; + int compmethod; + int ofs; +} includeddatafile_t; + + + + +typedef struct typeinfo_s +{ + etype_t type; + + int next; + int aux_type; + int num_parms; + + int ofs; //inside a structure. + int size; + char *name; +} typeinfo_t; diff --git a/misc/mediasource/extra/fteqcc-src/pr_edict.c b/misc/mediasource/extra/fteqcc-src/pr_edict.c new file mode 100644 index 00000000..d6c21199 --- /dev/null +++ b/misc/mediasource/extra/fteqcc-src/pr_edict.c @@ -0,0 +1,3118 @@ + + +#define PROGSUSED +struct edict_s; +#include "progsint.h" +//#include "crc.h" + +/*int maxedicts; + +evalc_t spawnflagscache; +*/ + +#ifdef _WIN32 +//this is windows all files are written with this endian standard. we do this to try to get a little more speed. +#define NOENDIAN +#endif + + +vec3_t vec3_origin; + +//edictrun_t *sv_edicts; +//int sv_num_edicts; + +//int pr_edict_size; // in bytes +//int pr_max_edict_size; + +//unsigned short pr_crc; + +fdef_t *ED_FieldAtOfs (progfuncs_t *progfuncs, unsigned int ofs); +pbool ED_ParseEpair (progfuncs_t *progfuncs, void *base, ddefXX_t *key, char *s, int bits); + + + +/* +#define MAX_FIELD_LEN 64 +#define GEFV_CACHESIZE 5 + +typedef struct { + ddef_t *pcache; + char field[MAX_FIELD_LEN]; +} gefv_cache; + +static gefv_cache gefvCache[GEFV_CACHESIZE] = {{NULL, ""}, {NULL, ""}}; +*/ + +/* +================= +QC_ClearEdict + +Sets everything to NULL +================= +*/ +void QC_ClearEdict (progfuncs_t *progfuncs, struct edict_s *ed) +{ + edictrun_t *e = (edictrun_t *)ed; + int num = e->entnum; + memset (e->fields, 0, fields_size); + e->isfree = false; + e->entnum = num; +} + +edictrun_t *ED_AllocIntoTable (progfuncs_t *progfuncs, int num) +{ + edictrun_t *e; + + prinst->edicttable[num] = *(struct edict_s **)&e = (void*)memalloc(externs->edictsize); + memset(e, 0, externs->edictsize); + e->fields = PRAddressableAlloc(progfuncs, fields_size); + e->entnum = num; + QC_ClearEdict(progfuncs, (struct edict_s*)e); + + return e; +} + +/* +================= +ED_Alloc + +Either finds a free edict, or allocates a new one. +Try to avoid reusing an entity that was recently freed, because it +can cause the client to think the entity morphed into something else +instead of being removed and recreated, which can cause interpolated +angles and bad trails. +================= +*/ +struct edict_s *ED_Alloc (progfuncs_t *progfuncs) +{ + unsigned int i; + edictrun_t *e; + + for ( i=0 ; iisfree && ( e->freetime < 2 || *externs->gametime - e->freetime > 0.5 ) )) + { + if (!e) + e = ED_AllocIntoTable(progfuncs, i); + else + QC_ClearEdict (progfuncs, (struct edict_s*)e); + + if (externs->entspawn) + externs->entspawn((struct edict_s *) e, false); + return (struct edict_s *)e; + } + } + + if (i >= maxedicts-1) //try again, but use timed out ents. + { + for ( i=0 ; iisfree)) + { + if (!e) + e = ED_AllocIntoTable(progfuncs, i); + else + QC_ClearEdict (progfuncs, (struct edict_s*)e); + + if (externs->entspawn) + externs->entspawn((struct edict_s *) e, false); + return (struct edict_s *)e; + } + } + + if (i >= maxedicts-2) + { + printf("Running out of edicts\n"); + pr_trace = 1; //trip the debugger whilst it's still valid + } + if (i >= maxedicts-1) + { + int size; + char *buf; + buf = progfuncs->save_ents(progfuncs, NULL, &size, 0); + progfuncs->parms->WriteFile("edalloc.dump", buf, size); + Sys_Error ("ED_Alloc: no free edicts"); + } + } + + sv_num_edicts++; + e = (edictrun_t*)EDICT_NUM(progfuncs, i); + + if (!e) + e = ED_AllocIntoTable(progfuncs, i); + else + QC_ClearEdict (progfuncs, (struct edict_s*)e); + + if (externs->entspawn) + externs->entspawn((struct edict_s *) e, false); + + return (struct edict_s *)e; +} + +/* +================= +ED_Free + +Marks the edict as free +FIXME: walk all entities and NULL out references to this entity +================= +*/ +void ED_Free (progfuncs_t *progfuncs, struct edict_s *ed) +{ + edictrun_t *e = (edictrun_t *)ed; +// SV_UnlinkEdict (ed); // unlink from world bsp + + if (e->isfree) //this happens on start.bsp where an onlyregistered trigger killtargets itself (when all of this sort die after 1 trigger anyway). + { + if (pr_depth) + printf("Tried to free free entity within %s\n", pr_xfunction->s_name+progfuncs->stringtable); + else + printf("Engine tried to free free entity\n"); +// if (developer.value == 1) +// pr_trace = true; + return; + } + + if (externs->entcanfree) + if (!externs->entcanfree(ed)) //can stop an ent from being freed. + return; + + e->isfree = true; + e->freetime = (float)*externs->gametime; + +/* + ed->v.model = 0; + ed->v.takedamage = 0; + ed->v.modelindex = 0; + ed->v.colormap = 0; + ed->v.skin = 0; + ed->v.frame = 0; + VectorCopy (vec3_origin, ed->v.origin); + VectorCopy (vec3_origin, ed->v.angles); + ed->v.nextthink = -1; + ed->v.solid = 0; +*/ +} + +//=========================================================================== + +/* +============ +ED_GlobalAtOfs +============ +*/ +ddef16_t *ED_GlobalAtOfs16 (progfuncs_t *progfuncs, int ofs) +{ + ddef16_t *def; + unsigned int i; + + for (i=0 ; inumglobaldefs ; i++) + { + def = &pr_globaldefs16[i]; + if (def->ofs == ofs) + return def; + } + return NULL; +} +ddef32_t *ED_GlobalAtOfs32 (progfuncs_t *progfuncs, unsigned int ofs) +{ + ddef32_t *def; + unsigned int i; + + for (i=0 ; inumglobaldefs ; i++) + { + def = &pr_globaldefs32[i]; + if (def->ofs == ofs) + return def; + } + return NULL; +} + +/* +============ +ED_FieldAtOfs +============ +*/ +fdef_t *ED_FieldAtOfs (progfuncs_t *progfuncs, unsigned int ofs) +{ +// ddef_t *def; + unsigned int i; + + for (i=0 ; inumglobaldefs ; i++) + { + def = &pr_globaldefs16[i]; + if (!strcmp(def->s_name+progfuncs->stringtable,name) ) + return def; + } + return NULL; +} +ddef32_t *ED_FindGlobal32 (progfuncs_t *progfuncs, char *name) +{ + ddef32_t *def; + unsigned int i; + + for (i=1 ; inumglobaldefs ; i++) + { + def = &pr_globaldefs32[i]; + if (!strcmp(def->s_name+progfuncs->stringtable,name) ) + return def; + } + return NULL; +} + +unsigned int ED_FindGlobalOfs (progfuncs_t *progfuncs, char *name) +{ + ddef16_t *d16; + ddef32_t *d32; + switch(current_progstate->intsize) + { + case 24: + case 16: + d16 = ED_FindGlobal16(progfuncs, name); + return d16?d16->ofs:0; + case 32: + d32 = ED_FindGlobal32(progfuncs, name); + return d32?d32->ofs:0; + } + Sys_Error("ED_FindGlobalOfs - bad intsize"); + return 0; +} + +ddef16_t *ED_FindGlobalFromProgs16 (progfuncs_t *progfuncs, char *name, progsnum_t prnum) +{ + ddef16_t *def; + unsigned int i; + + for (i=1 ; inumglobaldefs ; i++) + { + def = &pr_progstate[prnum].globaldefs16[i]; + if (!strcmp(def->s_name+progfuncs->stringtable,name) ) + return def; + } + return NULL; +} +ddef32_t *ED_FindGlobalFromProgs32 (progfuncs_t *progfuncs, char *name, progsnum_t prnum) +{ + ddef32_t *def; + unsigned int i; + + for (i=1 ; inumglobaldefs ; i++) + { + def = &pr_progstate[prnum].globaldefs32[i]; + if (!strcmp(def->s_name+progfuncs->stringtable,name) ) + return def; + } + return NULL; +} + +ddef16_t *ED_FindTypeGlobalFromProgs16 (progfuncs_t *progfuncs, char *name, progsnum_t prnum, int type) +{ + ddef16_t *def; + unsigned int i; + + for (i=1 ; inumglobaldefs ; i++) + { + def = &pr_progstate[prnum].globaldefs16[i]; + if (!strcmp(def->s_name+progfuncs->stringtable,name) ) + { + if (pr_progstate[prnum].types) + { + if (pr_progstate[prnum].types[def->type&~DEF_SAVEGLOBAL].type != type) + continue; + } + else if ((def->type&(~DEF_SAVEGLOBAL)) != type) + continue; + return def; + } + } + return NULL; +} + + +ddef32_t *ED_FindTypeGlobalFromProgs32 (progfuncs_t *progfuncs, char *name, progsnum_t prnum, int type) +{ + ddef32_t *def; + unsigned int i; + + for (i=1 ; inumglobaldefs ; i++) + { + def = &pr_progstate[prnum].globaldefs32[i]; + if (!strcmp(def->s_name+progfuncs->stringtable,name) ) + { + if (pr_progstate[prnum].types) + { + if (pr_progstate[prnum].types[def->type&~DEF_SAVEGLOBAL].type != type) + continue; + } + else if ((def->type&(~DEF_SAVEGLOBAL)) != (unsigned)type) + continue; + return def; + } + } + return NULL; +} + +unsigned int *ED_FindGlobalOfsFromProgs (progfuncs_t *progfuncs, char *name, progsnum_t prnum, int type) +{ + ddef16_t *def16; + ddef32_t *def32; + static unsigned int pos; + switch(pr_progstate[prnum].intsize) + { + case 16: + case 24: + def16 = ED_FindTypeGlobalFromProgs16(progfuncs, name, prnum, type); + if (!def16) + return NULL; + pos = def16->ofs; + return &pos; + case 32: + def32 = ED_FindTypeGlobalFromProgs32(progfuncs, name, prnum, type); + if (!def32) + return NULL; + return &def32->ofs; + } + Sys_Error("ED_FindGlobalOfsFromProgs - bad intsize"); + return 0; +} + +/* +============ +ED_FindFunction +============ +*/ +dfunction_t *ED_FindFunction (progfuncs_t *progfuncs, char *name, progsnum_t *prnum, progsnum_t fromprogs) +{ + dfunction_t *func; + unsigned int i; + char *sep; + + progsnum_t pnum; + + if (prnum) + { + sep = strchr(name, ':'); + if (sep) + { + pnum = atoi(name); + name = sep+1; + } + else + { + if (fromprogs>=0) + pnum = fromprogs; + else + pnum = pr_typecurrent; + } + *prnum = pnum; + } + else + pnum = pr_typecurrent; + + if ((unsigned)pnum > (unsigned)maxprogs) + { + printf("Progsnum %i out of bounds\n", pnum); + return NULL; + } + + if (!pr_progstate[pnum].progs) + return NULL; + + for (i=1 ; inumfunctions ; i++) + { + func = &pr_progstate[pnum].functions[i]; + if (!strcmp(func->s_name+progfuncs->stringtable,name) ) + return func; + } + return NULL; +} + +/* +============ +PR_ValueString + +Returns a string describing *data in a type specific manner +============= +*/ +char *PR_ValueString (progfuncs_t *progfuncs, etype_t type, eval_t *val) +{ + static char line[256]; + fdef_t *fielddef; + dfunction_t *f; + +#ifdef DEF_SAVEGLOBAL + type &= ~DEF_SAVEGLOBAL; +#endif + + if (pr_types) + type = pr_types[type].type; + + switch (type) + { + case ev_struct: + sprintf (line, "struct"); + break; + case ev_union: + sprintf (line, "union"); + break; + case ev_string: + sprintf (line, "%s", PR_StringToNative(progfuncs, val->string)); + break; + case ev_entity: + sprintf (line, "entity %i", NUM_FOR_EDICT(progfuncs, (struct edict_s *)PROG_TO_EDICT(progfuncs, val->edict)) ); + break; + case ev_function: + if (!val->function) + sprintf (line, "NULL function"); + else + { + if ((val->function & 0xff000000)>>24 >= (unsigned)maxprogs || !pr_progstate[(val->function & 0xff000000)>>24].functions) + sprintf (line, "Bad function"); + else + { + f = pr_progstate[(val->function & 0xff000000)>>24].functions + (val->function & ~0xff000000); + sprintf (line, "%i:%s()", (val->function & 0xff000000)>>24, f->s_name+progfuncs->stringtable); + } + } + break; + case ev_field: + fielddef = ED_FieldAtOfs (progfuncs, val->_int ); + if (!fielddef) + sprintf (line, ".??? (%i)", val->_int); + else + sprintf (line, ".%s (%i)", fielddef->name, val->_int); + break; + case ev_void: + sprintf (line, "void type"); + break; + case ev_float: + sprintf (line, "%5.1f", val->_float); + break; + case ev_integer: + sprintf (line, "%i", val->_int); + break; + case ev_vector: + sprintf (line, "'%5.1f %5.1f %5.1f'", val->_vector[0], val->_vector[1], val->_vector[2]); + break; + case ev_pointer: + sprintf (line, "pointer"); + { +// int entnum; +// int valofs; + if (val->_int == 0) + { + sprintf (line, "NULL pointer"); + break; + } + //FIXME: :/ + sprintf(line, "UNKNOWN"); +// entnum = ((qbyte *)val->edict - (qbyte *)sv_edicts) / pr_edict_size; +// valofs = (int *)val->edict - (int *)edvars(EDICT_NUM(progfuncs, entnum)); +// fielddef = ED_FieldAtOfs (progfuncs, valofs ); +// if (!fielddef) +// sprintf(line, "ent%i.%s", entnum, "UNKNOWN"); +// else +// sprintf(line, "ent%i.%s", entnum, fielddef->s_name); + } + break; + default: + sprintf (line, "bad type %i", type); + break; + } + + return line; +} + +/* +============ +PR_UglyValueString + +Returns a string describing *data in a type specific manner +Easier to parse than PR_ValueString +============= +*/ +char *PR_UglyValueString (progfuncs_t *progfuncs, etype_t type, eval_t *val) +{ + static char line[256]; + fdef_t *fielddef; + dfunction_t *f; + int i, j; + +#ifdef DEF_SAVEGLOBAL + type &= ~DEF_SAVEGLOBAL; +#endif + + if (pr_types) + type = pr_types[type].type; + + switch (type) + { + case ev_struct: + sprintf (line, "structures cannot yet be saved"); + break; + case ev_union: + sprintf (line, "unions cannot yet be saved"); + break; + case ev_string: + { + char *outs = line; + int outb = sizeof(line)-2; + char *ins = PR_StringToNative(progfuncs, val->string); + //markup the output string. + while(*ins && outb > 0) + { + switch(*ins) + { + case '\n': + *outs++ = '\\'; + *outs++ = 'n'; + ins++; + outb-=2; + break; + case '\"': + *outs++ = '\\'; + *outs++ = '"'; + ins++; + outb-=2; + break; + case '\\': + *outs++ = '\\'; + *outs++ = '\\'; + ins++; + outb-=2; + break; + default: + *outs++ = *ins++; + outb--; + break; + } + } + *outs = 0; + } + break; + case ev_entity: + sprintf (line, "%i", val->_int); + break; + case ev_function: + i = (val->function & 0xff000000)>>24; //progs number + if ((unsigned)i >= maxprogs || !pr_progstate[(unsigned)i].progs) + sprintf (line, "BAD FUNCTION INDEX: %i", val->function); + else + { + j = (val->function & ~0xff000000); //function number + if ((unsigned)j >= pr_progstate[(unsigned)i].progs->numfunctions) + sprintf(line, "%i:%s", i, "CORRUPT FUNCTION POINTER"); + else + { + f = pr_progstate[(unsigned)i].functions + j; + sprintf (line, "%i:%s", i, f->s_name+progfuncs->stringtable); + } + } + break; + case ev_field: + fielddef = ED_FieldAtOfs (progfuncs, val->_int ); + sprintf (line, "%s", fielddef->name); + break; + case ev_void: + sprintf (line, "void"); + break; + case ev_float: + if (val->_float == (int)val->_float) + sprintf (line, "%i", (int)val->_float); //an attempt to cut down on the number of .000000 vars.. + else + sprintf (line, "%f", val->_float); + break; + case ev_integer: + sprintf (line, "%i", val->_int); + break; + case ev_vector: + if (val->_vector[0] == (int)val->_vector[0] && val->_vector[1] == (int)val->_vector[1] && val->_vector[2] == (int)val->_vector[2]) + sprintf (line, "%i %i %i", (int)val->_vector[0], (int)val->_vector[1], (int)val->_vector[2]); + else + sprintf (line, "%f %f %f", val->_vector[0], val->_vector[1], val->_vector[2]); + break; + default: + sprintf (line, "bad type %i", type); + break; + } + + return line; +} + +//compatible with Q1 (for savegames) +char *PR_UglyOldValueString (progfuncs_t *progfuncs, etype_t type, eval_t *val) +{ + static char line[256]; + fdef_t *fielddef; + dfunction_t *f; + +#ifdef DEF_SAVEGLOBAL + type &= ~DEF_SAVEGLOBAL; +#endif + + if (pr_types) + type = pr_types[type].type; + + switch (type) + { + case ev_struct: + sprintf (line, "structures cannot yet be saved"); + break; + case ev_union: + sprintf (line, "unions cannot yet be saved"); + break; + case ev_string: + sprintf (line, "%s", PR_StringToNative(progfuncs, val->string)); + break; + case ev_entity: + sprintf (line, "%i", NUM_FOR_EDICT(progfuncs, (struct edict_s *)PROG_TO_EDICT(progfuncs, val->edict))); + break; + case ev_function: + f = pr_progstate[(val->function & 0xff000000)>>24].functions + (val->function & ~0xff000000); + sprintf (line, "%s", f->s_name+progfuncs->stringtable); + break; + case ev_field: + fielddef = ED_FieldAtOfs (progfuncs, val->_int ); + sprintf (line, "%s", fielddef->name); + break; + case ev_void: + sprintf (line, "void"); + break; + case ev_float: + if (val->_float == (int)val->_float) + sprintf (line, "%i", (int)val->_float); //an attempt to cut down on the number of .000000 vars.. + else + sprintf (line, "%f", val->_float); + break; + case ev_integer: + sprintf (line, "%i", val->_int); + break; + case ev_vector: + if (val->_vector[0] == (int)val->_vector[0] && val->_vector[1] == (int)val->_vector[1] && val->_vector[2] == (int)val->_vector[2]) + sprintf (line, "%i %i %i", (int)val->_vector[0], (int)val->_vector[1], (int)val->_vector[2]); + else + sprintf (line, "%f %f %f", val->_vector[0], val->_vector[1], val->_vector[2]); + break; + break; + default: + sprintf (line, "bad type %i", type); + break; + } + + return line; +} + +char *PR_TypeString(progfuncs_t *progfuncs, etype_t type) +{ +#ifdef DEF_SAVEGLOBAL + type &= ~DEF_SAVEGLOBAL; +#endif + + if (pr_types) + type = pr_types[type].type; + + switch (type) + { + case ev_struct: + return "struct"; + case ev_union: + return "union"; + case ev_string: + return "string"; + case ev_entity: + return "entity"; + case ev_function: + return "function"; + case ev_field: + return "field"; + case ev_void: + return "void"; + case ev_float: + return "float"; + case ev_vector: + return "vector"; + case ev_integer: + return "integer"; + default: + return "BAD TYPE"; + } +} + +/* +============ +PR_GlobalString + +Returns a string with a description and the contents of a global, +padded to 20 field width +============ +*/ +char *PR_GlobalString (progfuncs_t *progfuncs, int ofs) +{ + char *s; + int i; + ddef16_t *def16; + ddef32_t *def32; + void *val; + static char line[128]; + + switch (current_progstate->intsize) + { + case 16: + case 24: + val = (void *)&pr_globals[ofs]; + def16 = ED_GlobalAtOfs16(progfuncs, ofs); + if (!def16) + sprintf (line,"%i(?""?""?)", ofs); + else + { + s = PR_ValueString (progfuncs, def16->type, val); + sprintf (line,"%i(%s)%s", ofs, def16->s_name+progfuncs->stringtable, s); + } + + i = strlen(line); + for ( ; i<20 ; i++) + strcat (line," "); + strcat (line," "); + return line; + case 32: + val = (void *)&pr_globals[ofs]; + def32 = ED_GlobalAtOfs32(progfuncs, ofs); + if (!def32) + sprintf (line,"%i(?""?""?)", ofs); + else + { + s = PR_ValueString (progfuncs, def32->type, val); + sprintf (line,"%i(%s)%s", ofs, def32->s_name+progfuncs->stringtable, s); + } + + i = strlen(line); + for ( ; i<20 ; i++) + strcat (line," "); + strcat (line," "); + return line; + } + Sys_Error("Bad offset size in PR_GlobalString"); + return ""; +} + +char *PR_GlobalStringNoContents (progfuncs_t *progfuncs, int ofs) +{ + int i; + ddef16_t *def16; + ddef32_t *def32; + static char line[128]; + + switch (current_progstate->intsize) + { + case 16: + case 24: + def16 = ED_GlobalAtOfs16(progfuncs, ofs); + if (!def16) + sprintf (line,"%i(?""?""?)", ofs); + else + sprintf (line,"%i(%s)", ofs, def16->s_name+progfuncs->stringtable); + break; + case 32: + def32 = ED_GlobalAtOfs32(progfuncs, ofs); + if (!def32) + sprintf (line,"%i(?""?""?)", ofs); + else + sprintf (line,"%i(%s)", ofs, def32->s_name+progfuncs->stringtable); + break; + default: + Sys_Error("Bad offset size in PR_GlobalStringNoContents"); + } + + i = strlen(line); + for ( ; i<20 ; i++) + strcat (line," "); + strcat (line," "); + + return line; +} + + +/* +============= +ED_Print + +For debugging +============= +*/ +void ED_Print (progfuncs_t *progfuncs, struct edict_s *ed) +{ + int l; + fdef_t *d; + int *v; + unsigned int i;unsigned int j; + char *name; + int type; + + if (((edictrun_t *)ed)->isfree) + { + printf ("FREE\n"); + return; + } + + printf("\nEDICT %i:\n", NUM_FOR_EDICT(progfuncs, (struct edict_s *)ed)); + for (i=1 ; iname; + l = strlen(name); + if (l >= 2 && name[l-2] == '_') + continue; // skip _x, _y, _z vars + + v = (int *)((char *)edvars(ed) + d->ofs*4); + + // if the value is still all 0, skip the field +#ifdef DEF_SAVEGLOBAL + type = d->type & ~DEF_SAVEGLOBAL; +#else + type = d->type; +#endif + + for (j=0 ; jtype, (eval_t *)v)); + } +} + +void ED_PrintNum (progfuncs_t *progfuncs, int ent) +{ + ED_Print (progfuncs, EDICT_NUM(progfuncs, ent)); +} + +/* +============= +ED_PrintEdicts + +For debugging, prints all the entities in the current server +============= +*/ +void ED_PrintEdicts (progfuncs_t *progfuncs) +{ + unsigned int i; + + printf ("%i entities\n", sv_num_edicts); + for (i=0 ; iisfree) + continue; + active++; +// if (ent->v.solid) +// solid++; +// if (ent->v.model) +// models++; +// if (ent->v.movetype == MOVETYPE_STEP) +// step++; + } + + printf ("num_edicts:%3i\n", sv_num_edicts); + printf ("active :%3i\n", active); +// Con_Printf ("view :%3i\n", models); +// Con_Printf ("touch :%3i\n", solid); +// Con_Printf ("step :%3i\n", step); + +} + + + +//============================================================================ + + +/* +============= +ED_NewString +============= +*/ +char *ED_NewString (progfuncs_t *progfuncs, char *string, int minlength) +{ + char *newc, *new_p; + int i,l; + + minlength++; + + l = strlen(string) + 1; + + newc = PRAddressableAlloc (progfuncs, lofs); + + if (pr_types) + type = pr_types[((ddef16_t*)key)->type & ~DEF_SAVEGLOBAL].type; + else + type = ((ddef16_t*)key)->type & ~DEF_SAVEGLOBAL; + break; + case 32: + d = (void *)((int *)base + ((ddef32_t*)key)->ofs); + + if (pr_types) + type = pr_types[((ddef32_t*)key)->type & ~DEF_SAVEGLOBAL].type; + else + type = ((ddef32_t*)key)->type & ~DEF_SAVEGLOBAL; + break; + default: + Sys_Error("Bad bits in ED_ParseEpair"); + d = 0; + } + + switch (type) + { + case ev_string: + st = PR_StringToProgs(progfuncs, ED_NewString (progfuncs, s, 0)); + *(string_t *)d = st; + break; + + case ev_float: + *(float *)d = (float)atof (s); + break; + + case ev_integer: + *(int *)d = atoi (s); + break; + + case ev_vector: + strcpy (string, s); + v = string; + w = string; + for (i=0 ; i<3 ; i++) + { + while (*v && *v != ' ') + v++; + *v = 0; + ((float *)d)[i] = (float)atof (w); + w = v = v+1; + } + break; + + case ev_entity: + *(int *)d = atoi (s); + break; + + case ev_field: + def = ED_FindField (progfuncs, s); + if (!def) + { + printf ("Can't find field %s\n", s); + return false; + } + *(int *)d = def->ofs; + break; + + case ev_function: + if (s[1]==':'&&s[2]=='\0') + { + *(func_t *)d = 0; + return true; + } + func = ED_FindFunction (progfuncs, s, &i, -1); + if (!func) + { + printf ("Can't find function %s\n", s); + return false; + } + *(func_t *)d = (func - pr_progstate[i].functions) | (i<<24); + break; + + default: + break; + } + return true; +} + +/* +==================== +ED_ParseEdict + +Parses an edict out of the given string, returning the new position +ed should be a properly initialized empty edict. +Used for initial level load and for savegames. +==================== +*/ +#if 1 +char *ED_ParseEdict (progfuncs_t *progfuncs, char *data, edictrun_t *ent) +{ + fdef_t *key; + pbool init; + char keyname[256]; + int n; + +// eval_t *val; + + init = false; + +// clear it +// if (ent != (edictrun_t *)sv_edicts) // hack +// memset (ent+1, 0, pr_edict_size - sizeof(edictrun_t)); + +// go through all the dictionary pairs + while (1) + { + // parse key + data = QCC_COM_Parse (data); + if (qcc_token[0] == '}') + break; + if (!data) + { + printf ("ED_ParseEntity: EOF without closing brace\n"); + return NULL; + } + + strncpy (keyname, qcc_token, sizeof(keyname)-1); + keyname[sizeof(keyname)-1] = 0; + + // another hack to fix heynames with trailing spaces + n = strlen(keyname); + while (n && keyname[n-1] == ' ') + { + keyname[n-1] = 0; + n--; + } + + // parse value + data = QCC_COM_Parse (data); + if (!data) + { + printf ("ED_ParseEntity: EOF without closing brace\n"); + return NULL; + } + + if (qcc_token[0] == '}') + { + printf ("ED_ParseEntity: closing brace without data\n"); + return NULL; + } + + init = true; + +// keynames with a leading underscore are used for utility comments, +// and are immediately discarded by quake + if (keyname[0] == '_') + continue; + + key = ED_FindField (progfuncs, keyname); + if (!key) + { + if (!strcmp(keyname, "angle")) //Quake anglehack - we've got to leave it in cos it doesn't work for quake otherwise, and this is a QuakeC lib! + { + if ((key = ED_FindField (progfuncs, "angles"))) + { + sprintf (qcc_token, "0 %f 0", atof(qcc_token)); //change it from yaw to 3d angle + goto cont; + } + } + if (!strcmp(keyname, "light")) //Quake lighthack - allows a field name and a classname to go by the same thing in the level editor + if ((key = ED_FindField (progfuncs, "light_lev"))) + goto cont; + printf ("'%s' is not a field\n", keyname); + continue; + } + +cont: + if (!ED_ParseEpair (progfuncs, ent->fields, (ddefXX_t*)key, qcc_token, 32)) + { + continue; +// Sys_Error ("ED_ParseEdict: parse error on entities"); + } + } + + if (!init) + ent->isfree = true; + + return data; +} +#endif + +/* +================ +ED_LoadFromFile + +The entities are directly placed in the array, rather than allocated with +ED_Alloc, because otherwise an error loading the map would have entity +number references out of order. + +Creates a server's entity / program execution context by +parsing textual entity definitions out of an ent file. + +Used for both fresh maps and savegame loads. A fresh map would also need +to call ED_CallSpawnFunctions () to let the objects initialize themselves. +================ +*/ + +char *ED_WriteGlobals(progfuncs_t *progfuncs, char *buffer) //switch first. +{ +#define AddS(str) strcpy(buffer, str);buffer+=strlen(str); + int *v; + ddef32_t *def32; + ddef16_t *def16; + unsigned int i; + unsigned int j; + char *name; + int type; + int curprogs = pr_typecurrent; + int len; + switch(current_progstate->intsize) + { + case 16: + case 24: + for (i=0 ; inumglobaldefs ; i++) + { + def16 = &pr_globaldefs16[i]; + name = def16->s_name + progfuncs->stringtable; + len = strlen(name); + if (!*name) + continue; + if (name[len-2] == '_' && (name[len-1] == 'x' || name[len-1] == 'y' || name[len-1] == 'z')) + continue; // skip _x, _y, _z vars (vector components, which are saved as one vector not 3 floats) + + type = def16->type; + +#ifdef DEF_SAVEGLOBAL + if ( !(def16->type & DEF_SAVEGLOBAL) ) + continue; + type &= ~DEF_SAVEGLOBAL; +#endif + if (current_progstate->types) + type = current_progstate->types[type].type; + if (type == ev_function) + { + v = (int *)¤t_progstate->globals[def16->ofs]; + if ((v[0]&0xff000000)>>24 == (unsigned)curprogs) //same progs + { + if (!progfuncs->stringtable[current_progstate->functions[v[0]&0x00ffffff].s_name]) + continue; + else if (!strcmp(current_progstate->functions[v[0]&0x00ffffff].s_name+ progfuncs->stringtable, name)) //names match. Assume function is at initial value. + continue; + } + + if (curprogs!=0) + if ((v[0]&0xff000000)>>24 == 0) + if (!ED_FindFunction(progfuncs, name, NULL, curprogs)) //defined as extern + { + if (!progfuncs->stringtable[pr_progstate[0].functions[v[0]&0x00ffffff].s_name]) + continue; + else if (!strcmp(pr_progstate[0].functions[v[0]&0x00ffffff].s_name + progfuncs->stringtable, name)) //same name. + continue; + } + + //else function has been redirected externally. + goto add16; + } + else if (type != ev_string //anything other than these is not saved + && type != ev_float + && type != ev_integer + && type != ev_entity + && type != ev_vector) + continue; + + v = (int *)¤t_progstate->globals[def16->ofs]; + + // make sure the value is not null, where there's no point in saving + for (j=0 ; jtype&~DEF_SAVEGLOBAL, (eval_t *)v))); + } + break; + case 32: + for (i=0 ; inumglobaldefs ; i++) + { + def32 = &pr_globaldefs32[i]; + name = def32->s_name + progfuncs->stringtable; + if (name[strlen(name)-2] == '_') + continue; // skip _x, _y, _z vars (vector components, which are saved as one vector not 3 floats) + + type = def32->type; + +#ifdef DEF_SAVEGLOBAL + if ( !(def32->type & DEF_SAVEGLOBAL) ) + continue; + type &= ~DEF_SAVEGLOBAL; +#endif + if (current_progstate->types) + type = current_progstate->types[type].type; + if (type == ev_function) + { + v = (int *)¤t_progstate->globals[def32->ofs]; + if ((v[0]&0xff000000)>>24 == (unsigned)curprogs) //same progs + if (!strcmp(current_progstate->functions[v[0]&0x00ffffff].s_name+ progfuncs->stringtable, name)) //names match. Assume function is at initial value. + continue; + + if (curprogs!=0) + if ((v[0]&0xff000000)>>24 == 0) + if (!ED_FindFunction(progfuncs, name, NULL, curprogs)) //defined as extern + if (!strcmp(pr_progstate[0].functions[v[0]&0x00ffffff].s_name+ progfuncs->stringtable, name)) //same name. + continue; + + //else function has been redirected externally. + goto add32; + } + else if (type != ev_string //anything other than these is not saved + && type != ev_float + && type != ev_integer + && type != ev_entity + && type != ev_vector) + continue; + + v = (int *)¤t_progstate->globals[def32->ofs]; + + // make sure the value is not null, where there's no point in saving + for (j=0 ; jtype&~DEF_SAVEGLOBAL, (eval_t *)v))); + } + break; + default: + Sys_Error("Bad number of bits in SaveEnts"); + } + + return buffer; +} + +char *ED_WriteEdict(progfuncs_t *progfuncs, edictrun_t *ed, char *buffer, pbool q1compatible) +{ + fdef_t *d; + + int *v; + unsigned int i;unsigned int j; + char *name; + int type; + int len; + + for (i=0 ; iname; + len = strlen(name); + if (len>4 && (name[len-2] == '_' && (name[len-1] == 'x' || name[len-1] == 'y' || name[len-1] == 'z'))) + continue; // skip _x, _y, _z vars + + v = (int *)((char*)ed->fields + d->ofs*4); + + // if the value is still all 0, skip the field +#ifdef DEF_SAVEGLOBAL + type = d->type & ~DEF_SAVEGLOBAL; +#else + type = d->type; +#endif + + for (j=0 ; jtype, (eval_t *)v))); + } + + return buffer; +#undef AddS +} + +char *SaveCallStack (progfuncs_t *progfuncs, char *s) +{ +#define AddS(str) strcpy(s, str);s+=strlen(str); + char buffer[8192]; + dfunction_t *f; + int i; + int progs; + + int arg; + int *globalbase; + + progs = -1; + + if (pr_depth == 0) + { + AddS ("\n"); + return s; + } + + globalbase = (int *)pr_globals + pr_xfunction->parm_start + pr_xfunction->locals; + + pr_stack[pr_depth].f = pr_xfunction; + for (i=pr_depth ; i>0 ; i--) + { + f = pr_stack[i].f; + + if (!f) + { + AddS ("\n"); + } + else + { + if (pr_stack[i].progsnum != progs) + { + progs = pr_stack[i].progsnum; + + sprintf(buffer, "//%i %s\n", progs, pr_progstate[progs].filename); + AddS (buffer); + } + if (!f->s_file) + sprintf(buffer, "\t\"%i:%s\"\n", progs, f->s_name+progfuncs->stringtable); + else + sprintf(buffer, "\t\"%i:%s\" //%s\n", progs, f->s_name+progfuncs->stringtable, f->s_file+progfuncs->stringtable); + AddS (buffer); + + AddS ("\t{\n"); + for (arg = 0; arg < f->locals; arg++) + { + ddef16_t *local; + local = ED_GlobalAtOfs16(progfuncs, f->parm_start+arg); + if (!local) + sprintf(buffer, "\t\tofs%i %i // %f\n", f->parm_start+arg, *(int *)(globalbase - f->locals+arg), *(float *)(globalbase - f->locals+arg) ); + else + { + if (local->type == ev_entity) + { + sprintf(buffer, "\t\t\"%s\" \"entity %i\"\n", local->s_name+progfuncs->stringtable, ((eval_t*)(globalbase - f->locals+arg))->edict); + } + else + sprintf(buffer, "\t\t\"%s\"\t\"%s\"\n", local->s_name+progfuncs->stringtable, PR_ValueString(progfuncs, local->type, (eval_t*)(globalbase - f->locals+arg))); + + if (local->type == ev_vector) + arg+=2; + } + AddS (buffer); + } + AddS ("\t}\n"); + + if (i == pr_depth) + globalbase = localstack + localstack_used - f->locals; + else + globalbase -= f->locals; + } + } + return s; +#undef AddS +} + +//there are two ways of saving everything. +//0 is to save just the entities. +//1 is to save the entites, and all the progs info so that all the variables are saved off, and it can be reloaded to exactly how it was (provided no files or data has been changed outside, like the progs.dat for example) +char *SaveEnts(progfuncs_t *progfuncs, char *mem, int *len, int alldata) +{ +#define AddS(str) strcpy(s, str);s+=strlen(str); + char *s, *os; + unsigned int a; + int oldprogs; + + if (mem) + { + os = s = mem; + } + else + os = s = memalloc(5*1024*1024); + + if (alldata == 2) + { //special Q1 savegame compatability mode. + //engine will need to store references to progs type and will need to preload the progs and inti the ents itself before loading. + + //Make sure there is only 1 progs loaded. + for (a = 1; a < maxprogs; a++) + { + if (pr_progstate[a].progs) + break; + } + if (!pr_progstate[0].progs || a != maxprogs) //the state of the progs wasn't Q1 compatible. + { + memfree(os); + return NULL; + } + + //write the globals + AddS ("{\n"); + + oldprogs = pr_typecurrent; + PR_SwitchProgs(progfuncs, 0); + + s = ED_WriteGlobals(progfuncs, s); + + PR_SwitchProgs(progfuncs, oldprogs); + + AddS ("}\n"); + + + //write the ents + for (a = 0; a < sv_num_edicts; a++) + { + edictrun_t *ed = (edictrun_t *)EDICT_NUM(progfuncs, a); + + AddS ("{\n"); + + if (!ed->isfree) + s = ED_WriteEdict(progfuncs, ed, s, true); + + AddS ("}\n"); + } + + *len = s - os; + return os; + } + + if (alldata) + { + AddS("general {\n"); + AddS(qcva("\"maxprogs\" \"%i\"\n", maxprogs)); +// AddS(qcva("\"maxentities\" \"%i\"\n", maxedicts)); +// AddS(qcva("\"mem\" \"%i\"\n", hunksize)); +// AddS(qcva("\"crc\" \"%i\"\n", header_crc)); + AddS(qcva("\"numentities\" \"%i\"\n", sv_num_edicts)); + AddS("}\n"); + + oldprogs = pr_typecurrent; + + for (a = 0; a < maxprogs; a++) + { + if (!pr_progstate[a].progs) + continue; + PR_SwitchProgs(progfuncs, a); + { + AddS (qcva("progs %i {\n", a)); + AddS (qcva("\"filename\" \"%s\"\n", pr_progstate[a].filename)); + AddS (qcva("\"crc\" \"%i\"\n", pr_progs->crc)); + AddS (qcva("\"numbuiltins\" \"%i\"\n", current_progstate->numbuiltins)); + AddS ("}\n"); + } + } + + if (alldata == 3) + { + //include callstack + AddS("stacktrace {\n"); + s = SaveCallStack(progfuncs, s); + AddS("}\n"); + } + + for (a = 0; a < maxprogs; a++) //I would mix, but external functions rely on other progs being loaded + { + if (!pr_progstate[a].progs) + continue; + + AddS (qcva("globals %i {\n", a)); + + PR_SwitchProgs(progfuncs, a); + + s = ED_WriteGlobals(progfuncs, s); + + AddS ("}\n"); + } + PR_SwitchProgs(progfuncs, oldprogs); + } + for (a = 0; a < sv_num_edicts; a++) + { + edictrun_t *ed = (edictrun_t *)EDICT_NUM(progfuncs, a); + + if (ed->isfree) + continue; + + AddS (qcva("entity %i{\n", a)); + + s = ED_WriteEdict(progfuncs, ed, s, false); + + AddS ("}\n"); + } + + *len = s - os; + return os; + +#undef AddS +} + +int header_crc; + +//if 'general' block is found, this is a compleate state, otherwise, we should spawn entities like +int LoadEnts(progfuncs_t *progfuncs, char *file, float killonspawnflags) +{ + eval_t *fulldata; //this is part of FTE_FULLSPAWNDATA + char *datastart; + + eval_t *var; + + char filename[128]; + int num; + int numbuiltins; + edictrun_t *ed=NULL; + ddef16_t *d16; + ddef32_t *d32; + func_t CheckSpawn=0; + + extern edictrun_t tempedict; + + int crc = 1; + int entsize = 0; + int numents = 0; + + pbool resethunk=0; + pbool isloadgame; + if (!strncmp(file, "loadgame", 8)) + { + isloadgame = true; + numents = -1; + file+=8; + fulldata = NULL; + } + else + { + isloadgame = false; + + if (pr_typecurrent>=0) + num = ED_FindGlobalOfs(progfuncs, "__fullspawndata"); + else + num = 0; + if (num) + fulldata = (eval_t *)((int *)pr_globals + num); + else + fulldata = NULL; + } + + while(1) + { + datastart = file; + file = QCC_COM_Parse(file); + if (file == NULL) + break; //finished reading file + else if (!strcmp(qcc_token, "entity")) + { + if (entsize == 0 && resethunk) //edicts have not yet been initialized, and this is a compleate load (memsize has been set) + { + entsize = PR_InitEnts(progfuncs, maxedicts); +// sv_num_edicts = numents; + + for (num = 0; num < numents; num++) + { + ed = (edictrun_t *)EDICT_NUM(progfuncs, num); + + if (!ed) + { + ed = ED_AllocIntoTable(progfuncs, num); + ed->isfree = true; + if (externs->entspawn) + externs->entspawn((struct edict_s *) ed, true); + } + } + } + + file = QCC_COM_Parse(file); + num = atoi(qcc_token); + file = QCC_COM_Parse(file); + if (qcc_token[0] != '{') + Sys_Error("Progs loading found %s, not '{'", qcc_token); + if (!resethunk) + ed = (edictrun_t *)ED_Alloc(progfuncs); + else + { + ed = (edictrun_t *)EDICT_NUM(progfuncs, num); + + if (!ed) + { + Sys_Error("Edict was not allocated\n"); + ed = ED_AllocIntoTable(progfuncs, num); + } + } + ed->isfree = false; + if (externs->entspawn) + externs->entspawn((struct edict_s *) ed, true); + file = ED_ParseEdict(progfuncs, file, ed); + + if (killonspawnflags) + { + var = GetEdictFieldValue (progfuncs, (struct edict_s *)&ed, "spawnflags", &spawnflagscache); + if (var) + { + if ((int)var->_float & (int)killonspawnflags) + { + ed->isfree = true; + continue; + } + } + } + + if (!resethunk) + { + dfunction_t *f; + if ((var = GetEdictFieldValue (progfuncs, (struct edict_s *)ed, "classname", NULL))) + { + f = ED_FindFunction(progfuncs, var->string + progfuncs->stringtable, NULL, -1); + if (f) + { + var = (eval_t *)((int *)pr_globals + ED_FindGlobalOfs(progfuncs, "self")); + var->edict = EDICT_TO_PROG(progfuncs, ed); + PR_ExecuteProgram(progfuncs, f-pr_functions); + } + } + } + } + else if (!strcmp(qcc_token, "progs")) + { + file = QCC_COM_Parse(file); + num = atoi(qcc_token); + file = QCC_COM_Parse(file); + if (qcc_token[0] != '{') + Sys_Error("Progs loading found %s, not '{'", qcc_token); + + + filename[0] = '\0'; + header_crc = 0; + numbuiltins = 0; + + while(1) + { + file = QCC_COM_Parse(file); //read the key + if (!file) + Sys_Error("EOF in progs block"); + + if (!strcmp("filename", qcc_token)) //check key get and save values + {file = QCC_COM_Parse(file); strcpy(filename, qcc_token);} + else if (!strcmp("crc", qcc_token)) + {file = QCC_COM_Parse(file); header_crc = atoi(qcc_token);} + else if (!strcmp("numbuiltins", qcc_token)) + {file = QCC_COM_Parse(file); numbuiltins = atoi(qcc_token);} + else if (qcc_token[0] == '}') //end of block + break; + else + Sys_Error("Bad key \"%s\" in progs block", qcc_token); + } + + PR_ReallyLoadProgs(progfuncs, filename, header_crc, &pr_progstate[num], true); + if (!externs->builtinsfor) + { + // Sys_Error("Couldn't reset the builtin functions"); + current_progstate->builtins = NULL; //these are specific, we assume the global ones were set via pr_configure + current_progstate->numbuiltins = 0; + } + else + { + current_progstate->builtins = externs->builtinsfor(num, header_crc); + current_progstate->numbuiltins = numbuiltins; + } + } + else if (!strcmp(qcc_token, "globals")) + { + if (entsize == 0 && resethunk) //by the time we parse some globals, we MUST have loaded all progs + { + entsize = PR_InitEnts(progfuncs, maxedicts); +// sv_num_edicts = numents; + + for (num = 0; num < numents; num++) + { + ed = (edictrun_t *)EDICT_NUM(progfuncs, num); + + if (!ed) + { + ed = ED_AllocIntoTable(progfuncs, num); + ed->isfree = true; + } + + if (externs->entspawn) + externs->entspawn((struct edict_s *) ed, true); + } + } + + file = QCC_COM_Parse(file); + num = atoi(qcc_token); + + file = QCC_COM_Parse(file); + if (qcc_token[0] != '{') + Sys_Error("Globals loading found \'%s\', not '{'", qcc_token); + + PR_SwitchProgs(progfuncs, num); + while (1) + { + file = QCC_COM_Parse(file); + if (qcc_token[0] == '}') + break; + else if (!qcc_token[0] || !file) + Sys_Error("EOF when parsing global values"); + + switch(current_progstate->intsize) + { + case 16: + case 24: + if (!(d16 = ED_FindGlobal16(progfuncs, qcc_token))) + { + file = QCC_COM_Parse(file); + printf("global value %s not found", qcc_token); + } + else + { + file = QCC_COM_Parse(file); + ED_ParseEpair(progfuncs, pr_globals, (ddefXX_t*)d16, qcc_token, 16); + } + break; + case 32: + if (!(d32 = ED_FindGlobal32(progfuncs, qcc_token))) + { + file = QCC_COM_Parse(file); + printf("global value %s not found", qcc_token); + } + else + { + file = QCC_COM_Parse(file); + ED_ParseEpair(progfuncs, pr_globals, (ddefXX_t*)d32, qcc_token, 32); + } + break; + default: + Sys_Error("Bad intsize in LoadEnts"); + } + } + +// file = QCC_COM_Parse(file); +// if (com_token[0] != '}') +// Sys_Error("Progs loading found %s, not '}'", qcc_token); + } + else if (!strcmp(qcc_token, "general")) + { + QC_StartShares(progfuncs); +// QC_InitShares(); //forget stuff +// pr_edict_size = 0; + max_fields_size=0; + + file = QCC_COM_Parse(file); + if (qcc_token[0] != '{') + Sys_Error("Progs loading found %s, not '{'", qcc_token); + + while(1) + { + file = QCC_COM_Parse(file); //read the key + if (!file) + Sys_Error("EOF in general block"); + + if (!strcmp("maxprogs", qcc_token)) //check key get and save values + {file = QCC_COM_Parse(file); maxprogs = atoi(qcc_token);} +// else if (!strcmp("maxentities", com_token)) +// {file = QCC_COM_Parse(file); maxedicts = atoi(qcc_token);} +// else if (!strcmp("mem", com_token)) +// {file = QCC_COM_Parse(file); memsize = atoi(qcc_token);} +// else if (!strcmp("crc", com_token)) +// {file = QCC_COM_Parse(file); crc = atoi(qcc_token);} + else if (!strcmp("numentities", qcc_token)) + {file = QCC_COM_Parse(file); numents = atoi(qcc_token);} + else if (qcc_token[0] == '}') //end of block + break; + else + Sys_Error("Bad key \"%s\" in general block", qcc_token); + } + + PRAddressableFlush(progfuncs, -1); + resethunk=true; + + pr_progstate = PRHunkAlloc(progfuncs, sizeof(progstate_t) * maxprogs); + pr_typecurrent=0; + + sv_num_edicts = 1; //set up a safty buffer so things won't go horribly wrong too often + sv_edicts=(struct edict_s *)&tempedict; + prinst->edicttable = &sv_edicts; + + + sv_num_edicts = numents; //should be fine + +// PR_Configure(crc, NULL, memsize, maxedicts, maxprogs); + } + else if (!strcmp(qcc_token, "{")) + { + if (isloadgame) + { + if (numents == -1) //globals + { + while (1) + { + file = QCC_COM_Parse(file); + if (qcc_token[0] == '}') + break; + else if (!qcc_token[0] || !file) + Sys_Error("EOF when parsing global values"); + + switch(current_progstate->intsize) + { + case 16: + case 24: + if (!(d16 = ED_FindGlobal16(progfuncs, qcc_token))) + { + file = QCC_COM_Parse(file); + printf("global value %s not found", qcc_token); + } + else + { + file = QCC_COM_Parse(file); + ED_ParseEpair(progfuncs, pr_globals, (ddefXX_t*)d16, qcc_token, 16); + } + break; + case 32: + if (!(d32 = ED_FindGlobal32(progfuncs, qcc_token))) + { + file = QCC_COM_Parse(file); + printf("global value %s not found", qcc_token); + } + else + { + file = QCC_COM_Parse(file); + ED_ParseEpair(progfuncs, pr_globals, (ddefXX_t*)d32, qcc_token, 32); + } + break; + default: + Sys_Error("Bad intsize in LoadEnts"); + } + } + } + else + { + ed = (edictrun_t *)EDICT_NUM(progfuncs, numents); + if (!ed) + ed = ED_AllocIntoTable(progfuncs, numents); + + if (externs->entspawn) + externs->entspawn((struct edict_s *) ed, true); + + sv_num_edicts = numents; + ed->isfree = false; + file = ED_ParseEdict (progfuncs, file, ed); + } + numents++; + continue; + } + + if (entsize == 0 && resethunk) //edicts have not yet been initialized, and this is a compleate load (memsize has been set) + { + entsize = PR_InitEnts(progfuncs, maxedicts); +// sv_num_edicts = numents; + + for (num = 0; num < numents; num++) + { + ed = (edictrun_t *)EDICT_NUM(progfuncs, num); + + if (!ed) + { + ed = ED_AllocIntoTable(progfuncs, num); + ed->isfree = true; + } + } + } + + if (!ed) //first entity + ed = (edictrun_t *)EDICT_NUM(progfuncs, 0); + else + ed = (edictrun_t *)ED_Alloc(progfuncs); + ed->isfree = false; + if (externs->entspawn) + externs->entspawn((struct edict_s *) ed, true); + file = ED_ParseEdict(progfuncs, file, ed); + + if (killonspawnflags) + { + var = GetEdictFieldValue (progfuncs, (struct edict_s *)ed, "spawnflags", &spawnflagscache); + if (var) + { + if ((int)var->_float & (int)killonspawnflags) + { + ed->isfree = true; + ed->freetime = 0; + continue; + } + } + } + + if (!resethunk) + { + char *eclassname; + func_t f; + if (!CheckSpawn) + CheckSpawn = PR_FindFunc(progfuncs, "CheckSpawn", -2); + + var = GetEdictFieldValue (progfuncs, (struct edict_s *)ed, "classname", NULL); + if (!var || !var->string || !*PR_StringToNative(progfuncs, var->string)) + { + printf("No classname\n"); + ED_Free(progfuncs, (struct edict_s *)ed); + } + else + { + eval_t *selfvar; + + //added by request of Mercury. + if (fulldata) //this is a vital part of HL map support!!! + { //essentually, it passes the ent's spawn info to the ent. + char *nl; //otherwise it sees only the named fields of + char *spawndata;//a standard quake ent. + spawndata = PRHunkAlloc(progfuncs, file - datastart +1); + strncpy(spawndata, datastart, file - datastart); + spawndata[file - datastart] = '\0'; + for (nl = spawndata; *nl; nl++) + if (*nl == '\n') + *nl = '\t'; + fulldata->string = PR_StringToProgs(progfuncs, spawndata); + } + + selfvar = (eval_t *)((int *)pr_globals + ED_FindGlobalOfs(progfuncs, "self")); + selfvar->edict = EDICT_TO_PROG(progfuncs, ed); + + //DP_SV_SPAWNFUNC_PREFIX support + eclassname = PR_StringToNative(progfuncs, var->string); +#ifdef _WIN32 + _snprintf(filename, sizeof(filename), "spawnfunc_%s", eclassname); + filename[sizeof(filename)-1] = 0; +#else + snprintf(filename, sizeof(filename), "spawnfunc_%s", eclassname); +#endif + f = PR_FindFunc(progfuncs, filename, PR_ANYBACK); + if (!f) + f = PR_FindFunc(progfuncs, eclassname, PR_ANYBACK); + if (f) + { + if (CheckSpawn) + { + G_INT(OFS_PARM0) = f; + PR_ExecuteProgram(progfuncs, CheckSpawn); + //call the spawn func or remove. + } + else + PR_ExecuteProgram(progfuncs, f); + } + else if (CheckSpawn) + { + G_INT(OFS_PARM0) = 0; + PR_ExecuteProgram(progfuncs, CheckSpawn); + //the mod is responsible for freeing unrecognised ents. + } + else + { + printf("Couldn't find spawn function %s\n", PR_StringToNative(progfuncs, var->string)); + ED_Free(progfuncs, (struct edict_s *)ed); + } + } + } + } + else + Sys_Error("Command %s not recognised", qcc_token); + } + if (resethunk) + { + header_crc = crc; + if (externs->loadcompleate) + externs->loadcompleate(entsize); + + sv_num_edicts = numents; + } + + if (resethunk) + { + return entsize; + } + else + return max_fields_size; +} + +#define AddS(str) strcpy(s, str);s+=strlen(str); + +char *SaveEnt (progfuncs_t *progfuncs, char *buf, int *size, struct edict_s *ed) +{ + fdef_t *d; + int *v; + unsigned int i;unsigned int j; + char *name; + int type; + + char *s = buf; + +// if (ed->free) +// continue; + + AddS ("{\n"); + + + for (i=0 ; iname; + len = strlen(name); // should we skip vars with no name? + if (len > 2 && name[len-2] == '_') + continue; // skip _x, _y, _z vars + + v = (int*)((edictrun_t*)ed)->fields + d->ofs; + + // if the value is still all 0, skip the field + type = d->type & ~DEF_SAVEGLOBAL; + for (j=0 ; jtype, (eval_t *)v))); + } + + AddS ("}\n"); + + *size = s - buf; + + return buf; +} +struct edict_s *RestoreEnt (progfuncs_t *progfuncs, char *buf, int *size, struct edict_s *ed) +{ + edictrun_t *ent; + char *start = buf; + + buf = QCC_COM_Parse(buf); //read the key + if (!buf || !*qcc_token) + return NULL; + + if (strcmp(qcc_token, "{")) + Sys_Error("Restore Ent with no opening brace"); + + if (!ed) + ent = (edictrun_t *)ED_Alloc(progfuncs); + else + ent = (edictrun_t *)ed; + ent->isfree = false; + + if (externs->entspawn) + externs->entspawn((struct edict_s *) ent, false); + + buf = ED_ParseEdict(progfuncs, buf, ent); + + *size = buf - start; + + return (struct edict_s *)ent; +} + +#define Host_Error Sys_Error + +//return true if pr_progs needs recompiling (source files have changed) +pbool PR_TestRecompile(progfuncs_t *progfuncs) +{ + int newsize; + int num, found=0, lost=0, changed=0; + includeddatafile_t *s; + if (!pr_progs->ofsfiles) + return false; + + num = *(int*)((char *)pr_progs + pr_progs->ofsfiles); + s = (includeddatafile_t *)((char *)pr_progs + pr_progs->ofsfiles+4); + while(num>0) + { + newsize = externs->FileSize(s->filename); + if (newsize == -1) //ignore now missing files. - the referencer must have changed... + lost++; + else if (s->size != newsize) //file + changed++; + else + found++; + + s++; + num--; + } + if (lost > found+changed) + return false; + if (changed) + return true; + return false; +} +/* +#ifdef _DEBUG +//this is for debugging. +//I'm using this to detect incorrect string types while converting 32bit string pointers with bias to bound indexes. +void PR_TestForWierdness(progfuncs_t *progfuncs) +{ + unsigned int i; + int e; + edictrun_t *ed; + for (i = 0; i < pr_progs->numglobaldefs; i++) + { + if ((pr_globaldefs16[i].type&~(DEF_SHARED|DEF_SAVEGLOBAL)) == ev_string) + { + if (G_INT(pr_globaldefs16[i].ofs) < 0 || G_INT(pr_globaldefs16[i].ofs) >= addressableused) + printf("String type irregularity on \"%s\" \"%s\"\n", pr_globaldefs16[i].s_name+progfuncs->stringtable, G_INT(pr_globaldefs16[i].ofs)+progfuncs->stringtable); + } + } + + for (i = 0; i < numfields; i++) + { + if ((field[i].type&~(DEF_SHARED|DEF_SAVEGLOBAL)) == ev_string) + { + for (e = 0; e < sv_num_edicts; e++) + { + ed = (edictrun_t*)EDICT_NUM(progfuncs, e); + if (ed->isfree) + continue; + if (((int *)ed->fields)[field[i].ofs] < 0 || ((int *)ed->fields)[field[i].ofs] >= addressableused) + printf("String type irregularity \"%s\" \"%s\"\n", field[i].name, ((int *)ed->fields)[field[i].ofs]+progfuncs->stringtable); + } + } + } +} +#endif +*/ +char *decode(int complen, int len, int method, char *info, char *buffer); +/* +=============== +PR_LoadProgs +=============== +*/ +int PR_ReallyLoadProgs (progfuncs_t *progfuncs, char *filename, int headercrc, progstate_t *progstate, pbool complain) +{ + unsigned int i, type; +// extensionbuiltin_t *eb; +// float fl; + int len; +// int num; +// dfunction_t *f, *f2; + ddef16_t *d16; + ddef32_t *d32; + int *d2; + eval_t *eval; + char *s; + int progstype; + int trysleft = 2; +// bool qfhack = false; + pbool isfriked = false; //all imediate values were stripped, which causes problems with strings. + pbool hexencalling = false; //hexen style calling convention. The opcodes themselves are used as part of passing the arguments; + ddef16_t *gd16, *fld16; + float *glob; + dfunction_t *fnc; + dstatement16_t *st16; + + int hmark=0xffffffff; + + int reorg = prinst->reorganisefields || numfields; + + int stringadjust; + + current_progstate = progstate; + + strcpy(current_progstate->filename, filename); + + +// flush the non-C variable lookup cache +// for (i=0 ; iautocompile == PR_COMPILEALWAYS) //always compile before loading + { + printf("Forcing compile of progs %s\n", filename); + if (!CompileFile(progfuncs, filename)) + return false; + } + +// CRC_Init (&pr_crc); + +retry: + if ((len=externs->FileSize(filename))<=0) + { + if (externs->autocompile == PR_COMPILENEXIST || externs->autocompile == PR_COMPILECHANGED) //compile if file is not found (if 2, we have already tried, so don't bother) + { + if (hmark==0xffffffff) //first try + { + printf("couldn't open progs %s. Attempting to compile.\n", filename); + CompileFile(progfuncs, filename); + } + if ((len=externs->FileSize(filename))<0) + { + printf("Couldn't find or compile file %s\n", filename); + return false; + } + } + else if (externs->autocompile == PR_COMPILEIGNORE) + return false; + else + { + printf("Couldn't find file %s\n", filename); + return false; + } + } + + hmark = PRHunkMark(progfuncs); + pr_progs = PRHunkAlloc(progfuncs, len+1); + if (!externs->ReadFile(filename, pr_progs, len+1)) + { + if (!complain) + return false; + printf("Failed to open %s", filename); + return false; + } + +// for (i=0 ; iversion == PROG_VERSION) + { +// printf("Opening standard progs file \"%s\"\n", filename); + current_progstate->intsize = 16; + } + else if (pr_progs->version == PROG_EXTENDEDVERSION) + { +#ifndef NOENDIAN + for (i = standard_dprograms_t_size/sizeof(int); i < sizeof(dprograms_t)/sizeof(int); i++) + ((int *)pr_progs)[i] = PRLittleLong ( ((int *)pr_progs)[i] ); +#endif + if (pr_progs->secondaryversion == PROG_SECONDARYVERSION16) + { +// printf("Opening 16bit fte progs file \"%s\"\n", filename); + current_progstate->intsize = 16; + } + else if (pr_progs->secondaryversion == PROG_SECONDARYVERSION32) + { +// printf("Opening 32bit fte progs file \"%s\"\n", filename); + current_progstate->intsize = 32; + } + else + { +// printf("Opening KK7 progs file \"%s\"\n", filename); + current_progstate->intsize = 24; //KK progs. Yuck. Disabling saving would be a VERY good idea. + pr_progs->version = PROG_VERSION; //not fte. + } +/* else + { + printf ("Progs extensions are not compatible\nTry recompiling with the FTE compiler\n"); + HunkFree(hmark); + pr_progs=NULL; + return false; + } +*/ } + else + { + printf ("%s has wrong version number (%i should be %i)\n", filename, pr_progs->version, PROG_VERSION); + PRHunkFree(progfuncs, hmark); + pr_progs=NULL; + return false; + } + +//progs contains enough info for use to recompile it. + if (trysleft && (externs->autocompile == PR_COMPILECHANGED || externs->autocompile == PR_COMPILEEXISTANDCHANGED) && pr_progs->version == PROG_EXTENDEDVERSION) + { + if (PR_TestRecompile(progfuncs)) + { + printf("Source file has changed\nRecompiling.\n"); + if (CompileFile(progfuncs, filename)) + { + PRHunkFree(progfuncs, hmark); + pr_progs=NULL; + + trysleft--; + goto retry; + } + } + } + if (!trysleft) //the progs exists, let's just be happy about it. + printf("Progs is out of date and uncompilable\n"); + + if (headercrc != -1 && headercrc != 0) + if (pr_progs->crc != headercrc && pr_progs->crc != 0) //This shouldn't affect us. However, it does adjust expectations and usage of builtins. + { +// printf ("%s system vars have been modified, progdefs.h is out of date\n", filename); + PRHunkFree(progfuncs, hmark); + pr_progs=NULL; + return false; + } + + if (pr_progs->version == PROG_EXTENDEDVERSION && pr_progs->blockscompressed && !QC_decodeMethodSupported(2)) + { + printf ("%s uses compression\n", filename); + PRHunkFree(progfuncs, hmark); + pr_progs=NULL; + return false; + } + + fnc = pr_functions = (dfunction_t *)((qbyte *)pr_progs + pr_progs->ofs_functions); + pr_strings = ((char *)pr_progs + pr_progs->ofs_strings); + current_progstate->globaldefs = *(void**)&gd16 = (void *)((qbyte *)pr_progs + pr_progs->ofs_globaldefs); + current_progstate->fielddefs = *(void**)&fld16 = (void *)((qbyte *)pr_progs + pr_progs->ofs_fielddefs); + current_progstate->statements = (void *)((qbyte *)pr_progs + pr_progs->ofs_statements); + + glob = pr_globals = (void *)((qbyte *)pr_progs + pr_progs->ofs_globals); + + pr_linenums=NULL; + pr_types=NULL; + if (pr_progs->version == PROG_EXTENDEDVERSION) + { + if (pr_progs->ofslinenums) + pr_linenums = (int *)((qbyte *)pr_progs + pr_progs->ofslinenums); + if (pr_progs->ofs_types) + pr_types = (typeinfo_t *)((qbyte *)pr_progs + pr_progs->ofs_types); + + //start decompressing stuff... + if (pr_progs->blockscompressed & 1) //statements + { + switch(current_progstate->intsize) + { + case 16: + len=sizeof(dstatement16_t)*pr_progs->numstatements; + break; + case 32: + len=sizeof(dstatement32_t)*pr_progs->numstatements; + break; + default: + Sys_Error("Bad intsize"); + } + s = PRHunkAlloc(progfuncs, len); + QC_decode(progfuncs, PRLittleLong(*(int *)pr_statements16), len, 2, (char *)(((int *)pr_statements16)+1), s); + + current_progstate->statements = (dstatement16_t *)s; + } + if (pr_progs->blockscompressed & 2) //global defs + { + switch(current_progstate->intsize) + { + case 16: + len=sizeof(ddef16_t)*pr_progs->numglobaldefs; + break; + case 32: + len=sizeof(ddef32_t)*pr_progs->numglobaldefs; + break; + default: + Sys_Error("Bad intsize"); + } + s = PRHunkAlloc(progfuncs, len); + QC_decode(progfuncs, PRLittleLong(*(int *)pr_globaldefs16), len, 2, (char *)(((int *)pr_globaldefs16)+1), s); + + gd16 = *(ddef16_t**)¤t_progstate->globaldefs = (ddef16_t *)s; + } + if (pr_progs->blockscompressed & 4) //fields + { + switch(current_progstate->intsize) + { + case 16: + len=sizeof(ddef16_t)*pr_progs->numglobaldefs; + break; + case 32: + len=sizeof(ddef32_t)*pr_progs->numglobaldefs; + break; + default: + Sys_Error("Bad intsize"); + } + s = PRHunkAlloc(progfuncs, len); + QC_decode(progfuncs, PRLittleLong(*(int *)pr_fielddefs16), len, 2, (char *)(((int *)pr_fielddefs16)+1), s); + + *(ddef16_t**)¤t_progstate->fielddefs = (ddef16_t *)s; + } + if (pr_progs->blockscompressed & 8) //functions + { + len=sizeof(dfunction_t)*pr_progs->numfunctions; + s = PRHunkAlloc(progfuncs, len); + QC_decode(progfuncs, PRLittleLong(*(int *)pr_functions), len, 2, (char *)(((int *)pr_functions)+1), s); + + fnc = pr_functions = (dfunction_t *)s; + } + if (pr_progs->blockscompressed & 16) //string table + { + len=sizeof(char)*pr_progs->numstrings; + s = PRHunkAlloc(progfuncs, len); + QC_decode(progfuncs, PRLittleLong(*(int *)pr_strings), len, 2, (char *)(((int *)pr_strings)+1), s); + + pr_strings = (char *)s; + } + if (pr_progs->blockscompressed & 32) //globals + { + len=sizeof(float)*pr_progs->numglobals; + s = PRHunkAlloc(progfuncs, len); + QC_decode(progfuncs, PRLittleLong(*(int *)pr_globals), len, 2, (char *)(((int *)pr_globals)+1), s); + + glob = pr_globals = (float *)s; + } + if (pr_linenums && pr_progs->blockscompressed & 64) //line numbers + { + len=sizeof(int)*pr_progs->numstatements; + s = PRHunkAlloc(progfuncs, len); + QC_decode(progfuncs, PRLittleLong(*(int *)pr_linenums), len, 2, (char *)(((int *)pr_linenums)+1), s); + + pr_linenums = (int *)s; + } + if (pr_types && pr_progs->blockscompressed & 128) //types + { + len=sizeof(typeinfo_t)*pr_progs->numtypes; + s = PRHunkAlloc(progfuncs, len); + QC_decode(progfuncs, PRLittleLong(*(int *)pr_types), len, 2, (char *)(((int *)pr_types)+1), s); + + pr_types = (typeinfo_t *)s; + } + } + + len=sizeof(char)*pr_progs->numstrings; + s = PRAddressableAlloc(progfuncs, len); + memcpy(s, pr_strings, len); + pr_strings = (char *)s; + + len=sizeof(float)*pr_progs->numglobals; + s = PRAddressableAlloc(progfuncs, len); + memcpy(s, pr_globals, len); + glob = pr_globals = (float *)s; + + if (progfuncs->stringtable) + stringadjust = pr_strings - progfuncs->stringtable; + else + stringadjust = 0; + + if (!pr_linenums) + { + unsigned int lnotype = *(unsigned int*)"LNOF"; + unsigned int version = 1; + int ohm; + unsigned int *file; + char lnoname[128]; + ohm = PRHunkMark(progfuncs); + + strcpy(lnoname, filename); + StripExtension(lnoname); + strcat(lnoname, ".lno"); + if ((len=externs->FileSize(lnoname))>0) + { + file = PRHunkAlloc(progfuncs, len+1); + if (externs->ReadFile(lnoname, file, len+1)) + { + if ( file[0] != lnotype + || file[1] != version + || file[2] != pr_progs->numglobaldefs + || file[3] != pr_progs->numglobals + || file[4] != pr_progs->numfielddefs + || file[5] != pr_progs->numstatements + ) + { + PRHunkFree(progfuncs, ohm); //whoops: old progs or incompatible + } + else + pr_linenums = file + 6; + } + } + } + + pr_functions = fnc; +// pr_strings = ((char *)pr_progs + pr_progs->ofs_strings); + gd16 = *(ddef16_t**)¤t_progstate->globaldefs = (ddef16_t *)((qbyte *)pr_progs + pr_progs->ofs_globaldefs); + fld16 = (ddef16_t *)((qbyte *)pr_progs + pr_progs->ofs_fielddefs); +// pr_statements16 = (dstatement16_t *)((qbyte *)pr_progs + pr_progs->ofs_statements); + pr_globals = glob; + st16 = pr_statements16; +#undef pr_globals +#undef pr_globaldefs16 +#undef pr_functions +#undef pr_statements16 +#undef pr_fielddefs16 + + + current_progstate->edict_size = pr_progs->entityfields * 4 + externs->edictsize; + +// byte swap the lumps + for (i=0 ; inumfunctions; i++) + { +#ifndef NOENDIAN + fnc[i].first_statement = PRLittleLong (fnc[i].first_statement); + fnc[i].parm_start = PRLittleLong (fnc[i].parm_start); + fnc[i].s_name = (string_t)PRLittleLong ((long)fnc[i].s_name); + fnc[i].s_file = (string_t)PRLittleLong ((long)fnc[i].s_file); + fnc[i].numparms = PRLittleLong (fnc[i].numparms); + fnc[i].locals = PRLittleLong (fnc[i].locals); +#endif +/* if (!strncmp(fnc[i].s_name+pr_strings, "ext_", 4)) + { + for (eb = extensionbuiltin; eb; eb = eb->prev) + { + if (*eb->name == '_') + { + if (!strncmp(fnc[i].s_name+pr_strings+4, eb->name+1, strlen(eb->name+1))) + { + fnc[i].first_statement = -0x7fffffff; + *(void**)&fnc[i].profile = (void*)eb->func; + break; + } + } + else if (!strcmp(fnc[i].s_name+4, eb->name)) + { + fnc[i].first_statement = -0x7fffffff; + *(void**)&fnc[i].profile = (void*)eb->func; + break; + } + } + } +*/ + fnc[i].s_name += stringadjust; + fnc[i].s_file += stringadjust; + } + + //actual global values +#ifndef NOENDIAN + for (i=0 ; inumglobals ; i++) + ((int *)glob)[i] = PRLittleLong (((int *)glob)[i]); +#endif + + if (pr_types) + { + for (i=0 ; inumtypes ; i++) + { +#ifndef NOENDIAN + pr_types[i].type = PRLittleLong(current_progstate->types[i].type); + pr_types[i].next = PRLittleLong(current_progstate->types[i].next); + pr_types[i].aux_type = PRLittleLong(current_progstate->types[i].aux_type); + pr_types[i].num_parms = PRLittleLong(current_progstate->types[i].num_parms); + pr_types[i].ofs = PRLittleLong(current_progstate->types[i].ofs); + pr_types[i].size = PRLittleLong(current_progstate->types[i].size); + pr_types[i].name = (char *)PRLittleLong((long)current_progstate->types[i].name); +#endif + pr_types[i].name += stringadjust; + } + } + + if (reorg) + reorg = (headercrc != -1); + + QC_FlushProgsOffsets(progfuncs); + switch(current_progstate->intsize) + { + case 24: + case 16: + //byteswap the globals and fix name offsets + for (i=0 ; inumglobaldefs ; i++) + { +#ifndef NOENDIAN + gd16[i].type = PRLittleShort (gd16[i].type); + gd16[i].ofs = PRLittleShort (gd16[i].ofs); + gd16[i].s_name = (string_t)PRLittleLong ((long)gd16[i].s_name); +#endif + gd16[i].s_name += stringadjust; + } + + //byteswap fields and fix name offets. Also register the fields (which will result in some offset adjustments in the globals segment). + for (i=0 ; inumfielddefs ; i++) + { +#ifndef NOENDIAN + fld16[i].type = PRLittleShort (fld16[i].type); + fld16[i].ofs = PRLittleShort (fld16[i].ofs); + fld16[i].s_name = (string_t)PRLittleLong ((long)fld16[i].s_name); +#endif + if (reorg) + { + if (pr_types) + type = pr_types[fld16[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL)].type; + else + type = fld16[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL); + + if (progfuncs->fieldadjust && !pr_typecurrent) //we need to make sure all fields appear in their original place. + QC_RegisterFieldVar(progfuncs, type, fld16[i].s_name+pr_strings, 4*(fld16[i].ofs+progfuncs->fieldadjust), -1); + else if (type == ev_vector) //emit vector vars early, so their fields cannot be alocated before the vector itself. (useful against scramblers) + { + QC_RegisterFieldVar(progfuncs, type, fld16[i].s_name+pr_strings, -1, fld16[i].ofs); + } + } + else + { + fdef_t *nf; + if (numfields+1>maxfields) + { + i = maxfields; + maxfields += 32; + nf = memalloc(sizeof(fdef_t) * maxfields); + memcpy(nf, field, sizeof(fdef_t) * i); + memfree(field); + field = nf; + } + nf = &field[numfields]; + nf->name = fld16[i].s_name+pr_strings; + nf->type = fld16[i].type; + nf->progsofs = fld16[i].ofs; + nf->ofs = fld16[i].ofs; + + if (fields_size < (nf->ofs+type_size[nf->type])*4) + fields_size = (nf->ofs+type_size[nf->type])*4; + + numfields++; + } + fld16[i].s_name += stringadjust; + } + if (reorg && !(progfuncs->fieldadjust && !pr_typecurrent)) + for (i=0 ; inumfielddefs ; i++) + { + if (pr_types) + type = pr_types[fld16[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL)].type; + else + type = fld16[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL); + if (type != ev_vector) + QC_RegisterFieldVar(progfuncs, type, fld16[i].s_name+pr_strings-stringadjust, -1, fld16[i].ofs); + } + + break; + case 32: + for (i=0 ; inumglobaldefs ; i++) + { +#ifndef NOENDIAN + pr_globaldefs32[i].type = PRLittleLong (pr_globaldefs32[i].type); + pr_globaldefs32[i].ofs = PRLittleLong (pr_globaldefs32[i].ofs); + pr_globaldefs32[i].s_name = (string_t)PRLittleLong ((long)pr_globaldefs32[i].s_name); +#endif + pr_globaldefs32[i].s_name += stringadjust; + } + + for (i=0 ; inumfielddefs ; i++) + { + #ifndef NOENDIAN + pr_fielddefs32[i].type = PRLittleLong (pr_fielddefs32[i].type); + pr_fielddefs32[i].ofs = PRLittleLong (pr_fielddefs32[i].ofs); + pr_fielddefs32[i].s_name = (string_t)PRLittleLong ((long)pr_fielddefs32[i].s_name); + #endif + + if (reorg) + { + if (pr_types) + type = pr_types[pr_fielddefs32[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL)].type; + else + type = pr_fielddefs32[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL); + if (progfuncs->fieldadjust && !pr_typecurrent) //we need to make sure all fields appear in their original place. + QC_RegisterFieldVar(progfuncs, type, fld16[i].s_name+pr_strings, 4*(fld16[i].ofs+progfuncs->fieldadjust), -1); + else if (type == ev_vector) + QC_RegisterFieldVar(progfuncs, type, pr_fielddefs32[i].s_name+pr_strings, -1, pr_fielddefs32[i].ofs); + } + pr_fielddefs32[i].s_name += stringadjust; + } + if (reorg && !(progfuncs->fieldadjust && !pr_typecurrent)) + for (i=0 ; inumfielddefs ; i++) + { + if (pr_types) + type = pr_types[pr_fielddefs32[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL)].type; + else + type = pr_fielddefs32[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL); + if (type != ev_vector) + QC_RegisterFieldVar(progfuncs, type, pr_fielddefs32[i].s_name+pr_strings-stringadjust, -1, pr_fielddefs32[i].ofs); + } + break; + default: + Sys_Error("Bad int size"); + } + +//ifstring fixes arn't performed anymore. +//the following switch just fixes endian and hexen2 calling conventions (by using different opcodes). + switch(current_progstate->intsize) + { + case 16: + for (i=0 ; inumstatements ; i++) + { +#ifndef NOENDIAN + st16[i].op = PRLittleShort(st16[i].op); + st16[i].a = PRLittleShort(st16[i].a); + st16[i].b = PRLittleShort(st16[i].b); + st16[i].c = PRLittleShort(st16[i].c); +#endif + if (st16[i].op >= OP_CALL1 && st16[i].op <= OP_CALL8) + { + if (st16[i].b) + hexencalling = true; + + } + } + if (hexencalling) + { + for (i=0 ; inumstatements ; i++) + { + if (st16[i].op >= OP_CALL1 && st16[i].op <= OP_CALL8) + st16[i].op += OP_CALL1H - OP_CALL1; + } + } + break; + + case 24: //24 sucks. Guess why. + for (i=0 ; inumstatements ; i++) + { +#ifndef NOENDIAN + pr_statements32[i].op = PRLittleLong(pr_statements32[i].op); + pr_statements32[i].a = PRLittleLong(pr_statements32[i].a); + pr_statements32[i].b = PRLittleLong(pr_statements32[i].b); + pr_statements32[i].c = PRLittleLong(pr_statements32[i].c); +#endif + if (pr_statements32[i].op >= OP_CALL1 && pr_statements32[i].op <= OP_CALL8) + { + if (pr_statements32[i].b) + hexencalling = true; + + } + } + if (hexencalling) + { + for (i=0 ; inumstatements ; i++) + { + if (pr_statements32[i].op >= OP_CALL1 && pr_statements32[i].op <= OP_CALL8) + pr_statements32[i].op += OP_CALL1H - OP_CALL1; + } + } + break; + case 32: + for (i=0 ; inumstatements ; i++) + { +#ifndef NOENDIAN + pr_statements32[i].op = PRLittleLong(pr_statements32[i].op); + pr_statements32[i].a = PRLittleLong(pr_statements32[i].a); + pr_statements32[i].b = PRLittleLong(pr_statements32[i].b); + pr_statements32[i].c = PRLittleLong(pr_statements32[i].c); +#endif + if (pr_statements32[i].op >= OP_CALL1 && pr_statements32[i].op <= OP_CALL8) + { + if (pr_statements32[i].b) + hexencalling = true; + + } + } + if (hexencalling) + { + for (i=0 ; inumstatements ; i++) + { + if (pr_statements32[i].op >= OP_CALL1 && pr_statements32[i].op <= OP_CALL8) + pr_statements32[i].op += OP_CALL1H - OP_CALL1; + } + } + break; + } + + + if (headercrc == -1) + { + isfriked = true; + if (current_progstate->intsize != 16) + Sys_Error("Decompiling a bigprogs"); + return true; + } + + progstype = current_progstate-pr_progstate; + +// QC_StartShares(progfuncs); + + isfriked = true; + if (!pr_typecurrent) //progs 0 always acts as string stripped. + isfriked = -1; //partly to avoid some bad progs. + +// len = 0; + switch(current_progstate->intsize) + { + case 24: + case 16: + for (i=0 ; inumglobaldefs ; i++) + { + if (pr_types) + type = pr_types[gd16[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL)].type; + else + type = gd16[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL); + + if (gd16[i].type & DEF_SHARED) + { + gd16[i].type &= ~DEF_SHARED; + if (pr_types) + QC_AddSharedVar(progfuncs, gd16[i].ofs, pr_types[gd16[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL)].size); + else + QC_AddSharedVar(progfuncs, gd16[i].ofs, type_size[type]); + } + switch(type) + { + case ev_field: + if (reorg) + QC_AddSharedFieldVar(progfuncs, i, pr_strings - stringadjust); + break; + case ev_string: + if (((unsigned int *)glob)[gd16[i].ofs]>=progstate->progs->numstrings) + printf("Insane value\n"); + else if (isfriked != -1) + { + if (pr_strings[((int *)glob)[gd16[i].ofs]]) //quakec uses string tables. 0 must remain null, or 'if (s)' can break. + { + ((int *)glob)[gd16[i].ofs] += stringadjust; + isfriked = false; + } + else + ((int *)glob)[gd16[i].ofs] = 0; + } + break; + case ev_function: + if (((int *)glob)[gd16[i].ofs]) //don't change null funcs + { +// if (fnc[((int *)glob)[gd16[i].ofs]].first_statement>=0) //this is a hack. Make all builtins switch to the main progs first. Allows builtin funcs to cache vars from just the main progs. + ((int *)glob)[gd16[i].ofs] |= progstype << 24; + } + break; + } + } + break; + case 32: + for (i=0 ; inumglobaldefs ; i++) + { + if (pr_types) + type = pr_types[pr_globaldefs32[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL)].type; + else + type = pr_globaldefs32[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL); + + if (pr_globaldefs32[i].type & DEF_SHARED) + { + pr_globaldefs32[i].type &= ~DEF_SHARED; + if (pr_types) + QC_AddSharedVar(progfuncs, pr_globaldefs32[i].ofs, pr_types[pr_globaldefs32[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL)].size); + else + QC_AddSharedVar(progfuncs, pr_globaldefs32[i].ofs, type_size[type]); + } + switch(type) + { + case ev_field: + QC_AddSharedFieldVar(progfuncs, i, pr_strings - stringadjust); + break; + case ev_string: + if (pr_strings[((int *)glob)[pr_globaldefs32[i].ofs]]) //quakec uses string tables. 0 must remain null, or 'if (s)' can break. + { + ((int *)glob)[pr_globaldefs32[i].ofs] += stringadjust; + isfriked = false; + } + break; + case ev_function: + if (((int *)glob)[pr_globaldefs32[i].ofs]) //don't change null funcs + ((int *)glob)[pr_globaldefs32[i].ofs] |= progstype << 24; + break; + } + } + + if (pr_progs->version == PROG_EXTENDEDVERSION && pr_progs->numbodylessfuncs) + { + s = &((char *)pr_progs)[pr_progs->ofsbodylessfuncs]; + for (i = 0; i < pr_progs->numbodylessfuncs; i++) + { + d32 = ED_FindGlobal32(progfuncs, s); + d2 = ED_FindGlobalOfsFromProgs(progfuncs, s, 0, ev_function); + if (!d2) + Sys_Error("Runtime-linked function %s was not found in existing progs", s); + if (!d32) + Sys_Error("Couldn't find def for \"%s\"", s); + ((int *)glob)[d32->ofs] = (*(func_t *)&pr_progstate[0].globals[*d2]); + + s+=strlen(s)+1; + } + } + break; + default: + Sys_Error("Bad int size"); + } + + if ((isfriked && pr_typecurrent)) //friked progs only allow one file. + { + printf("You are trying to load a string-stripped progs as an addon.\nThis behaviour is not supported. Try removing some optimizations."); + PRHunkFree(progfuncs, hmark); + pr_progs=NULL; + return false; + } + + pr_strings+=stringadjust; + if (!progfuncs->stringtable) + progfuncs->stringtable = pr_strings; + + if (progfuncs->stringtablesize + progfuncs->stringtable < pr_strings + pr_progs->numstrings) + progfuncs->stringtablesize = (pr_strings + pr_progs->numstrings) - progfuncs->stringtable; + + eval = PR_FindGlobal(progfuncs, "thisprogs", progstype); + if (eval) + eval->prog = progstype; + + switch(current_progstate->intsize) + { + case 16: + if (pr_progs->version == PROG_EXTENDEDVERSION && pr_progs->numbodylessfuncs) + { + s = &((char *)pr_progs)[pr_progs->ofsbodylessfuncs]; + for (i = 0; i < pr_progs->numbodylessfuncs; i++) + { + d16 = ED_FindGlobal16(progfuncs, s); + if (!d16) + { + printf("Progs requires \"%s\" the external function \"%s\", but the definition was stripped", filename, s); + PRHunkFree(progfuncs, hmark); + pr_progs=NULL; + return false; + } + + ((int *)glob)[d16->ofs] = PR_FindFunc(progfuncs, s, PR_ANY); + if (!((int *)glob)[d16->ofs]) + printf("Warning: Runtime-linked function %s was not found in primary progs (loading %s)", s, filename); + /* + d2 = ED_FindGlobalOfsFromProgs(progfuncs, s, 0, ev_function); + if (!d2) + Sys_Error("Runtime-linked function %s was not found in primary progs (loading %s)", s, filename); + ((int *)glob)[d16->ofs] = (*(func_t *)&pr_progstate[0].globals[*d2]); + */ + s+=strlen(s)+1; + } + } + break; + case 24: + break; //cannot happen anyway. + case 32: + if (pr_progs->version == PROG_EXTENDEDVERSION && pr_progs->numbodylessfuncs) + { + s = &((char *)pr_progs)[pr_progs->ofsbodylessfuncs]; + for (i = 0; i < pr_progs->numbodylessfuncs; i++) + { + d32 = ED_FindGlobal32(progfuncs, s); + d2 = ED_FindGlobalOfsFromProgs(progfuncs, s, 0, ev_function); + if (!d2) + printf("Warning: Runtime-linked function %s was not found in existing progs", s); + if (!d32) + { + printf("Couldn't find def for \"%s\"", s); + PRHunkFree(progfuncs, hmark); + pr_progs=NULL; + return false; + } + ((int *)glob)[d32->ofs] = (*(func_t *)&pr_progstate[0].globals[*d2]); + + s+=strlen(s)+1; + } + } + break; + } + + eval = PR_FindGlobal(progfuncs, "__ext__fasttrackarrays", PR_CURRENT); + if (eval) //we support these opcodes + eval->_float = true; + + return true; +} + + + +struct edict_s *EDICT_NUM(progfuncs_t *progfuncs, unsigned int n) +{ + if (n >= maxedicts) + Sys_Error ("QCLIB: EDICT_NUM: bad number %i", n); + + return prinst->edicttable[n]; +} + +unsigned int NUM_FOR_EDICT(progfuncs_t *progfuncs, struct edict_s *e) +{ + edictrun_t *er = (edictrun_t*)e; + if (er->entnum >= maxedicts) + Sys_Error ("QCLIB: NUM_FOR_EDICT: bad pointer (%p)", e); + return er->entnum; +} diff --git a/misc/mediasource/extra/fteqcc-src/pr_exec.c b/misc/mediasource/extra/fteqcc-src/pr_exec.c new file mode 100644 index 00000000..bdd2af95 --- /dev/null +++ b/misc/mediasource/extra/fteqcc-src/pr_exec.c @@ -0,0 +1,1199 @@ +#define PROGSUSED +#include "progsint.h" +//#include "editor.h" + +#define HunkAlloc BADGDFG sdfhhsf FHS + + +#define Host_Error Sys_Error + + +//============================================================================= + +/* +================= +PR_PrintStatement +================= +*/ +/* +void PR_PrintStatement (progfuncs_t *progfuncs, dstatement16_t *s) +{ + int i; +printf("PR_PrintStatement is unsupported\n"); +return; + if ( (unsigned)s->op < OP_NUMOPS) + { + printf ("%s ", pr_opcodes[s->op].name); + i = strlen(pr_opcodes[s->op].name); + for ( ; i<10 ; i++) + printf (" "); + } + + if (s->op == OP_IF || s->op == OP_IFNOT) + printf ("%sbranch %i",PR_GlobalString(progfuncs, s->a),s->b); + else if (s->op == OP_GOTO) + { + printf ("branch %i",s->a); + } + else if ( (unsigned)(s->op - OP_STORE_F) < 6) + { + printf ("%s",PR_GlobalString(progfuncs, s->a)); + printf ("%s", PR_GlobalStringNoContents(progfuncs, s->b)); + } + else + { + if (s->a) + printf ("%s",PR_GlobalString(progfuncs, s->a)); + if (s->b) + printf ("%s",PR_GlobalString(progfuncs, s->b)); + if (s->c) + printf ("%s", PR_GlobalStringNoContents(progfuncs, s->c)); + } + printf ("\n"); +} +*/ + +/* +============ +PR_StackTrace +============ +*/ +char *QC_ucase(char *str) +{ + static char s[1024]; + strcpy(s, str); + str = s; + + while(*str) + { + if (*str >= 'a' && *str <= 'z') + *str = *str - 'a' + 'A'; + str++; + } + return s; +} + +void PR_StackTrace (progfuncs_t *progfuncs) +{ + dfunction_t *f; + int i; + int progs; + +#ifdef STACKTRACE + int arg; + int *globalbase; +#endif + progs = -1; + + if (pr_depth == 0) + { + printf ("\n"); + return; + } + +#ifdef STACKTRACE + globalbase = (int *)pr_globals + pr_xfunction->parm_start - pr_xfunction->locals; +#endif + + pr_stack[pr_depth].f = pr_xfunction; + pr_stack[pr_depth].s = pr_xstatement; + for (i=pr_depth ; i>0 ; i--) + { + f = pr_stack[i].f; + + if (!f) + { + printf ("\n"); + } + else + { + if (pr_stack[i].progsnum != progs) + { + progs = pr_stack[i].progsnum; + + printf ("<%s>\n", pr_progstate[progs].filename); + } + if (!f->s_file) + printf ("stripped : %s\n", f->s_name+progfuncs->stringtable); + else + { + if (pr_progstate[progs].linenums) + printf ("%12s %i : %s\n", f->s_file+progfuncs->stringtable, pr_progstate[progs].linenums[pr_stack[i].s], f->s_name+progfuncs->stringtable); + else + printf ("%12s : %s\n", f->s_file+progfuncs->stringtable, f->s_name+progfuncs->stringtable); + } + +#ifdef STACKTRACE + + for (arg = 0; arg < f->locals; arg++) + { + ddef16_t *local; + local = ED_GlobalAtOfs16(progfuncs, f->parm_start+arg); + if (!local) + { + printf(" ofs %i: %f : %i\n", f->parm_start+arg, *(float *)(globalbase - f->locals+arg), *(int *)(globalbase - f->locals+arg) ); + } + else + { + printf(" %s: %s\n", local->s_name+progfuncs->stringtable, PR_ValueString(progfuncs, local->type, (eval_t*)(globalbase - f->locals+arg))); + if (local->type == ev_vector) + arg+=2; + } + } + + if (i == pr_depth) + globalbase = localstack + localstack_used; + else + globalbase -= f->locals; +#endif + } + } +} + +/* +============ +PR_Profile_f + +============ +*/ +/* +void PR_Profile_f (void) +{ + dfunction_t *f, *best; + int max; + int num; + unsigned int i; + + num = 0; + do + { + max = 0; + best = NULL; + for (i=0 ; inumfunctions ; i++) + { + f = &pr_functions[i]; + if (f->profile > max && f->first_statement >=0) + { + max = f->profile; + best = f; + } + } + if (best) + { + if (num < 10) + printf ("%7i %s\n", best->profile, best->s_name); + num++; + best->profile = 0; + } + } while (best); +} +*/ + + +/* +============ +PR_RunError + +Aborts the currently executing function +============ +*/ +void VARGS PR_RunError (progfuncs_t *progfuncs, char *error, ...) +{ + va_list argptr; + char string[1024]; + + va_start (argptr,error); + Q_vsnprintf (string,sizeof(string)-1, error,argptr); + va_end (argptr); + +// { +// void SV_EndRedirect (void); +// SV_EndRedirect(); +// } + +// PR_PrintStatement (pr_statements + pr_xstatement); + PR_StackTrace (progfuncs); + printf ("\n"); + +//editbadfile(pr_strings + pr_xfunction->s_file, -1); + +// pr_depth = 0; // dump the stack so host_error can shutdown functions +// prinst->exitdepth = 0; + + Abort ("%s", string); +} + +/* +============================================================================ +PR_ExecuteProgram + +The interpretation main loop +============================================================================ +*/ + +/* +==================== +PR_EnterFunction + +Returns the new program statement counter +==================== +*/ +void PR_AbortStack (progfuncs_t *progfuncs); +int PR_EnterFunction (progfuncs_t *progfuncs, dfunction_t *f, int progsnum) +{ + int i, j, c, o; + + pr_stack[pr_depth].s = pr_xstatement; + pr_stack[pr_depth].f = pr_xfunction; + pr_stack[pr_depth].progsnum = progsnum; + pr_stack[pr_depth].pushed = pr_spushed; + pr_depth++; + if (pr_depth == MAX_STACK_DEPTH) + { + pr_depth--; + PR_StackTrace (progfuncs); + + printf ("stack overflow on call to %s\n", progfuncs->stringtable+f->s_name); + + //comment this out if you want the progs to try to continue anyway (could cause infinate loops) + PR_AbortStack(progfuncs); + Abort("Stack Overflow in %s\n", progfuncs->stringtable+f->s_name); + return pr_xstatement; + } + + localstack_used += pr_spushed; //make sure the call doesn't hurt pushed pointers + +// save off any locals that the new function steps on (to a side place, fromwhere they are restored on exit) + c = f->locals; + if (localstack_used + c > LOCALSTACK_SIZE) + { + localstack_used -= pr_spushed; + pr_depth--; + PR_RunError (progfuncs, "PR_ExecuteProgram: locals stack overflow\n"); + } + + for (i=0 ; i < c ; i++) + localstack[localstack_used+i] = ((int *)pr_globals)[f->parm_start + i]; + localstack_used += c; + +// copy parameters (set initial values) + o = f->parm_start; + for (i=0 ; inumparms ; i++) + { + for (j=0 ; jparm_size[i] ; j++) + { + ((int *)pr_globals)[o] = ((int *)pr_globals)[OFS_PARM0+i*3+j]; + o++; + } + } + + pr_xfunction = f; + return f->first_statement - 1; // offset the s++ +} + +/* +==================== +PR_LeaveFunction +==================== +*/ +int PR_LeaveFunction (progfuncs_t *progfuncs) +{ + int i, c; + + if (pr_depth <= 0) + Sys_Error ("prog stack underflow"); + +// restore locals from the stack + c = pr_xfunction->locals; + localstack_used -= c; + if (localstack_used < 0) + PR_RunError (progfuncs, "PR_ExecuteProgram: locals stack underflow\n"); + + for (i=0 ; i < c ; i++) + ((int *)pr_globals)[pr_xfunction->parm_start + i] = localstack[localstack_used+i]; + +// up stack + pr_depth--; + PR_MoveParms(progfuncs, pr_stack[pr_depth].progsnum, pr_typecurrent); + PR_SwitchProgs(progfuncs, pr_stack[pr_depth].progsnum); + pr_xfunction = pr_stack[pr_depth].f; + pr_spushed = pr_stack[pr_depth].pushed; + + localstack_used -= pr_spushed; + return pr_stack[pr_depth].s; +} + +ddef32_t *ED_FindLocalOrGlobal(progfuncs_t *progfuncs, char *name, eval_t **val) +{ + static ddef32_t def; + ddef32_t *def32; + ddef16_t *def16; + int i; + + switch (pr_progstate[pr_typecurrent].intsize) + { + case 16: + case 24: + //this gets parms fine, but not locals + if (pr_xfunction) + for (i = 0; i < pr_xfunction->numparms; i++) + { + def16 = ED_GlobalAtOfs16(progfuncs, pr_xfunction->parm_start+i); + if (!def16) + continue; + if (!strcmp(def16->s_name+progfuncs->stringtable, name)) + { + *val = (eval_t *)&pr_progstate[pr_typecurrent].globals[pr_xfunction->parm_start+i]; + + //we need something like this for functions that are not the top layer + // *val = (eval_t *)&localstack[localstack_used-pr_xfunction->numparms*4]; + def.ofs = def16->ofs; + def.s_name = def16->s_name; + def.type = def16->type; + return &def; + } + } + def16 = ED_FindGlobal16(progfuncs, name); + if (!def16) + return NULL; + def.ofs = def16->ofs; + def.type = def16->type; + def.s_name = def16->s_name; + def32 = &def; + break; + case 32: + //this gets parms fine, but not locals + if (pr_xfunction) + for (i = 0; i < pr_xfunction->numparms; i++) + { + def32 = ED_GlobalAtOfs32(progfuncs, pr_xfunction->parm_start+i); + if (!def32) + continue; + if (!strcmp(def32->s_name+progfuncs->stringtable, name)) + { + *val = (eval_t *)&pr_progstate[pr_typecurrent].globals[pr_xfunction->parm_start+i]; + + //we need something like this for functions that are not the top layer + // *val = (eval_t *)&localstack[localstack_used-pr_xfunction->numparms*4]; + return def32; + } + } + def32 = ED_FindGlobal32(progfuncs, name); + if (!def32) + return NULL; + break; + default: + Sys_Error("Bad int size in ED_FindLocalOrGlobal"); + def32 = NULL; + } + + *val = (eval_t *)&pr_progstate[pr_typecurrent].globals[def32->ofs]; + return &def; +} + +char *COM_TrimString(char *str) +{ + int i; + static char buffer[256]; + while (*str <= ' ' && *str>'\0') + str++; + + for (i = 0; i < 255; i++) + { + if (*str <= ' ') + break; + buffer[i] = *str++; + } + buffer[i] = '\0'; + return buffer; +} + +char *EvaluateDebugString(progfuncs_t *progfuncs, char *key) +{ + static char buf[256]; + char *c, *c2; + ddef32_t *def; + fdef_t *fdef; + eval_t *val; + char *assignment; + int type; + ddef32_t fakedef; + eval_t fakeval; + + assignment = strchr(key, '='); + if (assignment) + *assignment = '\0'; + + c = strchr(key, '.'); + if (c) *c = '\0'; + def = ED_FindLocalOrGlobal(progfuncs, key, &val); + if (!def) + { + if (atoi(key)) + { + def = &fakedef; + def->ofs = 0; + def->type = ev_entity; + val = &fakeval; + val->edict = atoi(key); + } + } + if (c) *c = '.'; + if (!def) + { + return "(Bad string)"; + } + type = def->type; + + //go through ent vars + c = strchr(key, '.'); + while(c) + { + c2 = c+1; + c = strchr(c2, '.'); + type = type &~DEF_SAVEGLOBAL; + if (current_progstate->types) + type = current_progstate->types[type].type; + if (type != ev_entity) + return "'.' without entity"; + if (c)*c = '\0'; + fdef = ED_FindField(progfuncs, COM_TrimString(c2)); + if (c)*c = '.'; + if (!fdef) + return "(Bad string)"; + val = (eval_t *) (((char *)PROG_TO_EDICT(progfuncs, val->_int)->fields) + fdef->ofs*4); + type = fdef->type; + } + + if (assignment) + { + assignment++; + switch (type&~DEF_SAVEGLOBAL) + { + case ev_string: + *(string_t *)val = PR_StringToProgs(progfuncs, ED_NewString (progfuncs, assignment, 0)); + break; + + case ev_float: + *(float *)val = (float)atof (assignment); + break; + + case ev_integer: + *(int *)val = atoi (assignment); + break; + +/* case ev_vector: + strcpy (string, assignment); + v = string; + w = string; + for (i=0 ; i<3 ; i++) + { + while (*v && *v != ' ') + v++; + *v = 0; + ((float *)d)[i] = (float)atof (w); + w = v = v+1; + } + break; +*/ + case ev_entity: + *(int *)val = EDICT_TO_PROG(progfuncs, EDICT_NUM(progfuncs, atoi (assignment))); + break; + + case ev_field: + fdef = ED_FindField (progfuncs, assignment); + if (!fdef) + { + int l,nl = strlen(assignment); + strcpy(buf, "Can't find field "); + l = strlen(buf); + if (nl > sizeof(buf)-l-2) + nl = sizeof(buf)-l-2; + memcpy(buf+l, assignment, nl); + assignment[l+nl+0] = '\n'; + assignment[l+nl+1] = 0; + return buf; + } + *(int *)val = G_INT(fdef->ofs); + break; + + case ev_function: + { + dfunction_t *func; + int i; + int progsnum = -1; + char *s = assignment; + if (s[0] && s[1] == ':') + { + progsnum = atoi(s); + s+=2; + } + else if (s[0] && s[1] && s[2] == ':') + { + progsnum = atoi(s); + s+=3; + } + + func = ED_FindFunction (progfuncs, s, &i, progsnum); + if (!func) + { + int l,nl = strlen(s); + + assignment[-1] = '='; + + strcpy(buf, "Can't find field "); + l = strlen(buf); + if (nl > sizeof(buf)-l-2) + nl = sizeof(buf)-l-2; + memcpy(buf+l, assignment, nl); + assignment[l+nl+0] = '\n'; + assignment[l+nl+1] = 0; + return buf; + } + *(func_t *)val = (func - pr_progstate[i].functions) | (i<<24); + } + break; + + default: + break; + + } + assignment[-1] = '='; + } + strcpy(buf, PR_ValueString(progfuncs, type, val)); + + return buf; +} + +int debugstatement; +//int EditorHighlightLine(window_t *wnd, int line); +void SetExecutionToLine(progfuncs_t *progfuncs, int linenum) +{ + int pn = pr_typecurrent; + int snum; + dfunction_t *f = pr_xfunction; + + switch(current_progstate->intsize) + { + case 16: + for (snum = f->first_statement; pr_progstate[pn].linenums[snum] < linenum; snum++) + { + if (pr_statements16[snum].op == OP_DONE) + return; + } + break; + case 24: + case 32: + for (snum = f->first_statement; pr_progstate[pn].linenums[snum] < linenum; snum++) + { + if (pr_statements32[snum].op == OP_DONE) + return; + } + break; + default: + Sys_Error("Bad intsize"); + snum = 0; + } + debugstatement = snum; +// EditorHighlightLine(editwnd, pr_progstate[pn].linenums[snum]); +} + +//0 clear. 1 set, 2 toggle, 3 check +int PR_ToggleBreakpoint(progfuncs_t *progfuncs, char *filename, int linenum, int flag) //write alternate route to work by function name. +{ + int ret=0; + unsigned int fl; + unsigned int i; + int pn = pr_typecurrent; + dfunction_t *f; + int op; + + for (pn = 0; (unsigned)pn < maxprogs; pn++) + { + if (!pr_progstate || !pr_progstate[pn].progs) + continue; + + if (linenum) //linenum is set means to set the breakpoint on a file and line + { + if (!pr_progstate[pn].linenums) + continue; + + for (f = pr_progstate[pn].functions, fl = 0; fl < pr_progstate[pn].progs->numfunctions; f++, fl++) + { + if (!stricmp(f->s_file+progfuncs->stringtable, filename)) + { + for (i = f->first_statement; ; i++) + { + if (pr_progstate[pn].linenums[i] >= linenum) + { + fl = pr_progstate[pn].linenums[i]; + for (; ; i++) + { + if ((unsigned int)pr_progstate[pn].linenums[i] > fl) + break; + + switch(pr_progstate[pn].intsize) + { + case 16: + op = ((dstatement16_t*)pr_progstate[pn].statements + i)->op; + break; + case 24: + case 32: + op = ((dstatement32_t*)pr_progstate[pn].statements + i)->op; + break; + default: + Sys_Error("Bad intsize"); + op = 0; + } + switch (flag) + { + default: + if (op & 0x8000) + { + op &= ~0x8000; + ret = false; + flag = 0; + } + else + { + op |= 0x8000; + ret = true; + flag = 1; + } + break; + case 0: + op &= ~0x8000; + ret = false; + break; + case 1: + op |= 0x8000; + ret = true; + break; + case 3: + if (op & 0x8000) + return true; + } + switch(pr_progstate[pn].intsize) + { + case 16: + ((dstatement16_t*)pr_progstate[pn].statements + i)->op = op; + break; + case 24: + case 32: + ((dstatement32_t*)pr_progstate[pn].statements + i)->op = op; + break; + default: + Sys_Error("Bad intsize"); + op = 0; + } + } + goto cont; + } + } + } + } + } + else //set the breakpoint on the first statement of the function specified. + { + for (f = pr_progstate[pn].functions, fl = 0; fl < pr_progstate[pn].progs->numfunctions; f++, fl++) + { + if (!strcmp(f->s_name+progfuncs->stringtable, filename)) + { + i = f->first_statement; + switch(pr_progstate[pn].intsize) + { + case 16: + op = ((dstatement16_t*)pr_progstate[pn].statements + i)->op; + break; + case 24: + case 32: + op = ((dstatement32_t*)pr_progstate[pn].statements + i)->op; + break; + default: + Sys_Error("Bad intsize"); + } + switch (flag) + { + default: + if (op & 0x8000) + { + op &= ~0x8000; + ret = false; + flag = 0; + } + else + { + op |= 0x8000; + ret = true; + flag = 1; + } + break; + case 0: + op &= ~0x8000; + ret = false; + break; + case 1: + op |= 0x8000; + ret = true; + break; + case 3: + if (op & 0x8000) + return true; + } + switch(pr_progstate[pn].intsize) + { + case 16: + ((dstatement16_t*)pr_progstate[pn].statements + i)->op = op; + break; + case 24: + case 32: + ((dstatement32_t*)pr_progstate[pn].statements + i)->op = op; + break; + default: + Sys_Error("Bad intsize"); + } + break; + } + } + } +cont: + continue; + } + + return ret; +} + +int ShowStep(progfuncs_t *progfuncs, int statement) +{ +// return statement; +// texture realcursortex; +static int lastline = 0; +static char *lastfile = 0; + + int pn = pr_typecurrent; + int i; + dfunction_t *f = pr_xfunction; + + if (f && pr_progstate[pn].linenums && externs->useeditor) + { + if (lastline == pr_progstate[pn].linenums[statement] && lastfile == f->s_file+progfuncs->stringtable) + return statement; //no info/same line as last time + + lastline = pr_progstate[pn].linenums[statement]; + lastfile = f->s_file+progfuncs->stringtable; + + lastline = externs->useeditor(progfuncs, lastfile, lastline, 0, NULL); + + if (pr_progstate[pn].linenums[statement] != lastline) + { + for (i = f->first_statement; ; i++) + { + if (lastline == pr_progstate[pn].linenums[i]) + { + return i; + } + else if (lastline <= pr_progstate[pn].linenums[i]) + { + return statement; + } + } + } + } + else if (f) //annoying. + { + if (*(f->s_file+progfuncs->stringtable)) //if we can't get the filename, then it was stripped, and debugging it like this is useless + if (externs->useeditor) + externs->useeditor(progfuncs, f->s_file+progfuncs->stringtable, -1, 0, NULL); + return statement; + } + + + return statement; +} + +//DMW: all pointer functions are modified to be absoloute pointers from NULL not sv_edicts +/* +==================== +PR_ExecuteProgram +==================== +*/ +void PR_ExecuteCode (progfuncs_t *progfuncs, int s) +{ + eval_t *t, *swtch=NULL; + + int swtchtype; + dstatement16_t *st16; + dstatement32_t *st32; + dfunction_t *newf; + int runaway; + int i; + int p; + edictrun_t *ed; + eval_t *ptr; + + float *glob; + + int fnum; + + prinst->continuestatement = -1; +#ifdef QCJIT + if (prinst->usejit) + { + PR_EnterJIT(progfuncs, s); + return; + } +#endif + fnum = pr_xfunction - pr_functions; + + runaway = 100000000; + +#define PRBOUNDSCHECK +#define RUNAWAYCHECK() \ + if (!--runaway) \ + { \ + pr_xstatement = st-pr_statements; \ + PR_StackTrace(progfuncs); \ + printf ("runaway loop error"); \ + while(pr_depth > prinst->exitdepth) \ + PR_LeaveFunction(progfuncs); \ + pr_spushed = 0; \ + return; \ + } + +#define OPA ((eval_t *)&glob[st->a]) +#define OPB ((eval_t *)&glob[st->b]) +#define OPC ((eval_t *)&glob[st->c]) + +restart: //jumped to when the progs might have changed. + glob = pr_globals; + switch (current_progstate->intsize) + { + case 16: +#define INTSIZE 16 + st16 = &pr_statements16[s]; + while (pr_trace) + { + #define DEBUGABLE + #ifdef SEPARATEINCLUDES + #include "execloop16d.h" + #else + #include "execloop.h" + #endif + #undef DEBUGABLE + } + + while(1) + { + #include "execloop.h" + } +#undef INTSIZE + Sys_Error("PR_ExecuteProgram - should be unreachable"); + break; + case 24: + case 32: +#define INTSIZE 32 + st32 = &pr_statements32[s]; + while (pr_trace) + { + #define DEBUGABLE + #ifdef SEPARATEINCLUDES + #include "execloop32d.h" + #else + #include "execloop.h" + #endif + #undef DEBUGABLE + } + + while(1) + { + #ifdef SEPARATEINCLUDES + #include "execloop32.h" + #else + #include "execloop.h" + #endif + } +#undef INTSIZE + Sys_Error("PR_ExecuteProgram - should be unreachable"); + break; + default: + Sys_Error("PR_ExecuteProgram - bad intsize"); + } +} + + +void PR_ExecuteProgram (progfuncs_t *progfuncs, func_t fnum) +{ + dfunction_t *f; + int i; + unsigned int initial_progs; + int oldexitdepth; + + int s; + + int tempdepth; + + unsigned int newprogs = (fnum & 0xff000000)>>24; + + initial_progs = pr_typecurrent; + if (newprogs != initial_progs) + { + if (newprogs >= maxprogs || !&pr_progstate[newprogs].globals) //can happen with hexen2... + { + printf("PR_ExecuteProgram: tried branching into invalid progs\n"); + return; + } + PR_MoveParms(progfuncs, newprogs, pr_typecurrent); + PR_SwitchProgs(progfuncs, newprogs); + } + + if (!(fnum & ~0xff000000) || (signed)(fnum & ~0xff000000) >= pr_progs->numfunctions) + { +// if (pr_global_struct->self) +// ED_Print (PROG_TO_EDICT(pr_global_struct->self)); + printf("PR_ExecuteProgram: NULL function from exe\n"); +// Host_Error ("PR_ExecuteProgram: NULL function from exe"); + +// PR_MoveParms(0, pr_typecurrent); + PR_SwitchProgs(progfuncs, initial_progs); + return; + } + + oldexitdepth = prinst->exitdepth; + + f = &pr_functions[fnum & ~0xff000000]; + + if (f->first_statement < 0) + { // negative statements are built in functions + i = -f->first_statement; + + if (i < externs->numglobalbuiltins) + (*externs->globalbuiltins[i]) (progfuncs, (struct globalvars_s *)current_progstate->globals); + else + { + i -= externs->numglobalbuiltins; + if (i > current_progstate->numbuiltins) + { + printf ("Bad builtin call number %i (from exe)\n", -f->first_statement); + // PR_MoveParms(p, pr_typecurrent); + PR_SwitchProgs(progfuncs, initial_progs); + return; + } + current_progstate->builtins [i] (progfuncs, (struct globalvars_s *)current_progstate->globals); + } + PR_MoveParms(progfuncs, initial_progs, pr_typecurrent); + PR_SwitchProgs(progfuncs, initial_progs); + return; + } + + if (pr_trace) + pr_trace--; + +// make a stack frame + prinst->exitdepth = pr_depth; + + + s = PR_EnterFunction (progfuncs, f, initial_progs); + + tempdepth = prinst->numtempstringsstack; + PR_ExecuteCode(progfuncs, s); + + + PR_MoveParms(progfuncs, initial_progs, pr_typecurrent); + PR_SwitchProgs(progfuncs, initial_progs); + + PR_FreeTemps(progfuncs, tempdepth); + prinst->numtempstringsstack = tempdepth; + + prinst->exitdepth = oldexitdepth; +} + + + + + + +typedef struct { + int fnum; + int progsnum; + int statement; +} qcthreadstack_t; +typedef struct qcthread_s { + int fstackdepth; + qcthreadstack_t fstack[MAX_STACK_DEPTH]; + int lstackused; + int lstack[LOCALSTACK_SIZE]; + int xstatement; + int xfunction; + progsnum_t xprogs; +} qcthread_t; + +struct qcthread_s *PR_ForkStack(progfuncs_t *progfuncs) +{ //QC code can call builtins that call qc code. + //to get around the problems of restoring the builtins we simply don't save the thread over the builtin. + int i, l; + int ed = prinst->exitdepth; + int localsoffset, baselocalsoffset; + qcthread_t *thread = memalloc(sizeof(qcthread_t)); + dfunction_t *f; + + //copy out the functions stack. + for (i = 0,localsoffset=0; i < ed; i++) + { + if (i+1 == pr_depth) + f = pr_xfunction; + else + f = pr_stack[i+1].f; + localsoffset += f->locals; //this is where it crashes + } + baselocalsoffset = localsoffset; + for (i = ed; i < pr_depth; i++) + { + thread->fstack[i-ed].fnum = pr_stack[i].f - pr_progstate[pr_stack[i].progsnum].functions; + thread->fstack[i-ed].progsnum = pr_stack[i].progsnum; + thread->fstack[i-ed].statement = pr_stack[i].s; + + if (i+1 == pr_depth) + f = pr_xfunction; + else + f = pr_stack[i+1].f; + localsoffset += f->locals; + } + thread->fstackdepth = pr_depth - ed; + + for (i = pr_depth - 1; i >= ed ; i--) + { + if (i+1 == pr_depth) + f = pr_xfunction; + else + f = pr_stack[i+1].f; + localsoffset -= f->locals; + for (l = 0; l < f->locals; l++) + { + thread->lstack[localsoffset-baselocalsoffset + l ] = ((int *)pr_globals)[f->parm_start + l]; + ((int *)pr_globals)[f->parm_start + l] = localstack[localsoffset+l]; //copy the old value into the globals (so the older functions have the correct locals. + } + } + + for (i = ed; i < pr_depth ; i++) //we need to get the locals back to how they were. + { + if (i+1 == pr_depth) + f = pr_xfunction; + else + f = pr_stack[i+1].f; + + for (l = 0; l < f->locals; l++) + { + ((int *)pr_globals)[f->parm_start + l] = thread->lstack[localsoffset-baselocalsoffset + l]; + } + localsoffset += f->locals; + } + thread->lstackused = localsoffset - baselocalsoffset; + + thread->xstatement = pr_xstatement; + thread->xfunction = pr_xfunction - pr_progstate[pr_typecurrent].functions; + thread->xprogs = pr_typecurrent; + + return thread; +} + +void PR_ResumeThread (progfuncs_t *progfuncs, struct qcthread_s *thread) +{ + dfunction_t *f, *oldf; + int i,l,ls; + progsnum_t initial_progs; + int oldexitdepth; + + int s; + int tempdepth; + + progsnum_t prnum = thread->xprogs; + int fnum = thread->xfunction; + + if (localstack_used + thread->lstackused > LOCALSTACK_SIZE) + PR_RunError(progfuncs, "Too many locals on resumtion of QC thread\n"); + + if (pr_depth + thread->fstackdepth > MAX_STACK_DEPTH) + PR_RunError(progfuncs, "Too large stack on resumtion of QC thread\n"); + + + //do progs switching stuff as appropriate. (fteqw only) + initial_progs = pr_typecurrent; + PR_MoveParms(progfuncs, prnum, pr_typecurrent); + PR_SwitchProgs(progfuncs, prnum); + + + oldexitdepth = prinst->exitdepth; + prinst->exitdepth = pr_depth; + + ls = 0; + //add on the callstack. + for (i = 0; i < thread->fstackdepth; i++) + { + if (pr_depth == prinst->exitdepth) + { + pr_stack[pr_depth].f = pr_xfunction; + pr_stack[pr_depth].s = pr_xstatement; + pr_stack[pr_depth].progsnum = initial_progs; + } + else + { + pr_stack[pr_depth].progsnum = thread->fstack[i].progsnum; + pr_stack[pr_depth].f = pr_progstate[thread->fstack[i].progsnum].functions + thread->fstack[i].fnum; + pr_stack[pr_depth].s = thread->fstack[i].statement; + } + + if (i+1 == thread->fstackdepth) + f = &pr_functions[fnum]; + else + f = pr_progstate[thread->fstack[i+1].progsnum].functions + thread->fstack[i+1].fnum; + for (l = 0; l < f->locals; l++) + { + localstack[localstack_used++] = ((int *)pr_globals)[f->parm_start + l]; + ((int *)pr_globals)[f->parm_start + l] = thread->lstack[ls++]; + } + + pr_depth++; + } + + if (ls != thread->lstackused) + PR_RunError(progfuncs, "Thread stores incorrect locals count\n"); + + + f = &pr_functions[fnum]; + +// thread->lstackused -= f->locals; //the current function is the odd one out. + + //add on the locals stack + memcpy(localstack+localstack_used, thread->lstack, sizeof(int)*thread->lstackused); + localstack_used += thread->lstackused; + + //bung the locals of the current function on the stack. +// for (i=0 ; i < f->locals ; i++) +// ((int *)pr_globals)[f->parm_start + i] = 0xff00ff00;//thread->lstack[thread->lstackused+i]; + + +// PR_EnterFunction (progfuncs, f, initial_progs); + oldf = pr_xfunction; + pr_xfunction = f; + s = thread->xstatement; + + tempdepth = prinst->numtempstringsstack; + PR_ExecuteCode(progfuncs, s); + + + PR_MoveParms(progfuncs, initial_progs, pr_typecurrent); + PR_SwitchProgs(progfuncs, initial_progs); + PR_FreeTemps(progfuncs, tempdepth); + prinst->numtempstringsstack = tempdepth; + + prinst->exitdepth = oldexitdepth; + pr_xfunction = oldf; +} + +void PR_AbortStack (progfuncs_t *progfuncs) +{ + while(pr_depth > prinst->exitdepth+1) + PR_LeaveFunction(progfuncs); + prinst->continuestatement = 0; +} + diff --git a/misc/mediasource/extra/fteqcc-src/pr_multi.c b/misc/mediasource/extra/fteqcc-src/pr_multi.c new file mode 100644 index 00000000..5a1ade75 --- /dev/null +++ b/misc/mediasource/extra/fteqcc-src/pr_multi.c @@ -0,0 +1,427 @@ +#define PROGSUSED +#include "progsint.h" + +#define HunkAlloc BADGDFG sdfhhsf FHS + +void PR_SetBuiltins(int type); +/* +progstate_t *pr_progstate; +progsnum_t pr_typecurrent; +int maxprogs; + +progstate_t *current_progstate; +int numshares; + +sharedvar_t *shares; //shared globals, not including parms +int maxshares; +*/ + +pbool PR_SwitchProgs(progfuncs_t *progfuncs, progsnum_t type) +{ + if ((unsigned)type >= maxprogs) + PR_RunError(progfuncs, "QCLIB: Bad prog type - %i", type); +// Sys_Error("Bad prog type - %i", type); + + if (pr_progstate[(unsigned)type].progs == NULL) //we havn't loaded it yet, for some reason + return false; + + current_progstate = &pr_progstate[(unsigned)type]; + + pr_typecurrent = type; + + return true; +} + +void PR_MoveParms(progfuncs_t *progfuncs, progsnum_t progs1, progsnum_t progs2) //from 2 to 1 +{ + unsigned int a; + progstate_t *p1; + progstate_t *p2; + + if (progs1 == progs2) + return; //don't bother coping variables to themselves... + + p1 = &pr_progstate[(int)progs1]; + p2 = &pr_progstate[(int)progs2]; + + if ((unsigned)progs1 >= maxprogs || !p1->globals) + Sys_Error("QCLIB: Bad prog type - %i", progs1); + if ((unsigned)progs2 >= maxprogs || !p2->globals) + Sys_Error("QCLIB: Bad prog type - %i", progs2); + + //copy parms. + for (a = 0; a < MAX_PARMS;a++) + { + *(int *)&p1->globals[OFS_PARM0+3*a ] = *(int *)&p2->globals[OFS_PARM0+3*a ]; + *(int *)&p1->globals[OFS_PARM0+3*a+1] = *(int *)&p2->globals[OFS_PARM0+3*a+1]; + *(int *)&p1->globals[OFS_PARM0+3*a+2] = *(int *)&p2->globals[OFS_PARM0+3*a+2]; + } + p1->globals[OFS_RETURN] = p2->globals[OFS_RETURN]; + p1->globals[OFS_RETURN+1] = p2->globals[OFS_RETURN+1]; + p1->globals[OFS_RETURN+2] = p2->globals[OFS_RETURN+2]; + + //move the vars defined as shared. + for (a = 0; a < numshares; a++)//fixme: make offset per progs + { + memmove(&((int *)p1->globals)[shares[a].varofs], &((int *)p2->globals)[shares[a].varofs], shares[a].size*4); +/* ((int *)p1->globals)[shares[a].varofs] = ((int *)p2->globals)[shares[a].varofs]; + if (shares[a].size > 1) + { + ((int *)p1->globals)[shares[a].varofs+1] = ((int *)p2->globals)[shares[a].varofs+1]; + if (shares[a].size > 2) + ((int *)p1->globals)[shares[a].varofs+2] = ((int *)p2->globals)[shares[a].varofs+2]; + } +*/ + } +} + +progsnum_t PR_LoadProgs(progfuncs_t *progfuncs, char *s, int headercrc, builtin_t *builtins, int numbuiltins) +{ + unsigned int a; + progsnum_t oldtype; + oldtype = pr_typecurrent; + for (a = 0; a < maxprogs; a++) + { + if (pr_progstate[a].progs == NULL) + { + pr_typecurrent = a; + current_progstate = &pr_progstate[a]; + if (PR_ReallyLoadProgs(progfuncs, s, headercrc, &pr_progstate[a], false)) //try and load it + { + current_progstate->builtins = builtins; + current_progstate->numbuiltins = numbuiltins; + +#ifdef QCJIT + if (prinst->usejit) + prinst->usejit = PR_GenerateJit(progfuncs); +#endif + if (oldtype>=0) + PR_SwitchProgs(progfuncs, oldtype); + return a; //we could load it. Yay! + } + if (oldtype!=-1) + PR_SwitchProgs(progfuncs, oldtype); + return -1; // loading failed. + } + } + PR_SwitchProgs(progfuncs, oldtype); + return -1; +} + +void PR_ShiftParms(progfuncs_t *progfuncs, int amount) +{ + int a; + for (a = 0; a < MAX_PARMS - amount;a++) + *(int *)&pr_globals[OFS_PARM0+3*a] = *(int *)&pr_globals[OFS_PARM0+3*(amount+a)]; +} + +//forget a progs +void PR_Clear(progfuncs_t *progfuncs) +{ + unsigned int a; + for (a = 0; a < maxprogs; a++) + { + pr_progstate[a].progs = NULL; + } +} + + + +void QC_StartShares(progfuncs_t *progfuncs) +{ + numshares = 0; + maxshares = 32; + if (shares) + memfree(shares); + shares = memalloc(sizeof(sharedvar_t)*maxshares); +} +void QC_AddSharedVar(progfuncs_t *progfuncs, int start, int size) //fixme: make offset per progs and optional +{ + int ofs; + unsigned int a; + + if (numshares >= maxshares) + { + void *buf; + buf = shares; + maxshares += 16; + shares = memalloc(sizeof(sharedvar_t)*maxshares); + + memcpy(shares, buf, sizeof(sharedvar_t)*numshares); + + memfree(buf); + } + ofs = start; + for (a = 0; a < numshares; a++) + { + if (shares[a].varofs+shares[a].size == ofs) + { + shares[a].size += size; //expand size. + return; + } + if (shares[a].varofs == start) + return; + } + + + shares[numshares].varofs = start; + shares[numshares].size = size; + numshares++; +} + + +//void ShowWatch(void); + +void QC_InitShares(progfuncs_t *progfuncs) +{ +// ShowWatch(); + if (!field) //don't make it so we will just need to remalloc everything + { + maxfields = 64; + field = memalloc(sizeof(fdef_t) * maxfields); + } + + numfields = 0; + progfuncs->fieldadjust = 0; +} + +void QC_FlushProgsOffsets(progfuncs_t *progfuncs) +{ //sets the fields up for loading a new progs. + //fields are matched by name to other progs + //not by offset + unsigned int i; + for (i = 0; i < numfields; i++) + field[i].progsofs = -1; +} + + +//called if a global is defined as a field +//returns offset. + +//vectors must be added before any of their corresponding _x/y/z vars +//in this way, even screwed up progs work. + +//requestedpos is the offset the engine WILL put it at. +//origionaloffs is used to track matching field offsets. fields with the same progs offset overlap + +//note: we probably suffer from progs with renamed system globals. +int QC_RegisterFieldVar(progfuncs_t *progfuncs, unsigned int type, char *name, int engineofs, int progsofs) +{ +// progstate_t *p; +// int pnum; + unsigned int i; + int namelen; + int ofs; + + int fnum; + + if (!name) //engine can use this to offset all progs fields + { //which fixes constant field offsets (some ktpro arrays) + progfuncs->fieldadjust = fields_size/4; + return 0; + } + + + prinst->reorganisefields = true; + + //look for an existing match + for (i = 0; i < numfields; i++) + { + if (!strcmp(name, field[i].name)) + { + if (field[i].type != type) + { + printf("Field type mismatch on \"%s\"\n", name); + continue; + } + if (!progfuncs->fieldadjust && engineofs>=0) + if ((unsigned)engineofs/4 != field[i].ofs) + Sys_Error("Field %s at wrong offset", name); + + if (field[i].progsofs == -1) + field[i].progsofs = progsofs; +// printf("Dupfield %s %i -> %i\n", name, field[i].progsofs,field[i].ofs); + return field[i].ofs-progfuncs->fieldadjust; //got a match + } + } + + if (numfields+1>maxfields) + { + fdef_t *nf; + i = maxfields; + maxfields += 32; + nf = memalloc(sizeof(fdef_t) * maxfields); + memcpy(nf, field, sizeof(fdef_t) * i); + memfree(field); + field = nf; + } + + //try to add a new one + fnum = numfields; + numfields++; + field[fnum].name = name; + if (type == ev_vector) + { + char *n; + namelen = strlen(name)+5; + + n=PRHunkAlloc(progfuncs, namelen); + sprintf(n, "%s_x", name); + ofs = QC_RegisterFieldVar(progfuncs, ev_float, n, engineofs, progsofs); + field[fnum].ofs = ofs+progfuncs->fieldadjust; + + n=PRHunkAlloc(progfuncs, namelen); + sprintf(n, "%s_y", name); + QC_RegisterFieldVar(progfuncs, ev_float, n, (engineofs==-1)?-1:(engineofs+4), (progsofs==-1)?-1:progsofs+1); + + n=PRHunkAlloc(progfuncs, namelen); + sprintf(n, "%s_z", name); + QC_RegisterFieldVar(progfuncs, ev_float, n, (engineofs==-1)?-1:(engineofs+8), (progsofs==-1)?-1:progsofs+2); + } + else if (engineofs >= 0) + { //the engine is setting up a list of required field indexes. + + //paranoid checking of the offset. + /* for (i = 0; i < numfields-1; i++) + { + if (field[i].ofs == ((unsigned)engineofs)/4) + { + if (type == ev_float && field[i].type == ev_vector) //check names + { + if (strncmp(field[i].name, name, strlen(field[i].name))) + Sys_Error("Duplicated offset"); + } + else + Sys_Error("Duplicated offset"); + } + }*/ + if (engineofs&3) + Sys_Error("field %s is %i&3", name, engineofs); + field[fnum].ofs = ofs = engineofs/4; + } + else + { //we just found a new fieldname inside a progs + field[fnum].ofs = ofs = fields_size/4; //add on the end + + //if the progs field offset matches annother offset in the same progs, make it match up with the earlier one. + if (progsofs>=0) + { + for (i = 0; i < numfields-1; i++) + { + if (field[i].progsofs == (unsigned)progsofs) + { +// printf("found union field %s %i -> %i\n", field[i].name, field[i].progsofs, field[i].ofs); + field[fnum].ofs = ofs = field[i].ofs; + break; + } + } + } + } +// if (type != ev_vector) + if (fields_size < (ofs+type_size[type])*4) + fields_size = (ofs+type_size[type])*4; + + if (max_fields_size && fields_size > max_fields_size) + Sys_Error("Allocated too many additional fields after ents were inited."); + field[fnum].type = type; + + field[fnum].progsofs = progsofs; + +// printf("Field %s %i -> %i\n", name, field[fnum].progsofs,field[fnum].ofs); + + //we've finished setting the structure + return ofs - progfuncs->fieldadjust; +} + + +//called if a global is defined as a field +void QC_AddSharedFieldVar(progfuncs_t *progfuncs, int num, char *stringtable) +{ +// progstate_t *p; +// int pnum; + unsigned int i, o; + + char *s; + + //look for an existing match not needed, cos we look a little later too. + /* + for (i = 0; i < numfields; i++) + { + if (!strcmp(pr_globaldefs[num].s_name, field[i].s_name)) + { + //really we should look for a field def + + *(int *)&pr_globals[pr_globaldefs[num].ofs] = field[i].ofs; //got a match + + return; + } + } + */ + + switch(current_progstate->intsize) + { + case 24: + case 16: + for (i=1 ; inumfielddefs; i++) + { + if (!strcmp(pr_fielddefs16[i].s_name+stringtable, pr_globaldefs16[num].s_name+stringtable)) + { +// int old = *(int *)&pr_globals[pr_globaldefs16[num].ofs]; + *(int *)&pr_globals[pr_globaldefs16[num].ofs] = QC_RegisterFieldVar(progfuncs, pr_fielddefs16[i].type, pr_globaldefs16[num].s_name+stringtable, -1, *(int *)&pr_globals[pr_globaldefs16[num].ofs]); + +// printf("Field %s %i -> %i\n", pr_globaldefs16[num].s_name+stringtable, old, *(int *)&pr_globals[pr_globaldefs16[num].ofs]); + return; + } + } + + s = pr_globaldefs16[num].s_name+stringtable; + + for (i = 0; i < numfields; i++) + { + o = field[i].progsofs; + if (o == *(unsigned int *)&pr_globals[pr_globaldefs16[num].ofs]) + { +// int old = *(int *)&pr_globals[pr_globaldefs16[num].ofs]; + *(int *)&pr_globals[pr_globaldefs16[num].ofs] = field[i].ofs-progfuncs->fieldadjust; +// printf("Field %s %i -> %i\n", pr_globaldefs16[num].s_name+stringtable, old, *(int *)&pr_globals[pr_globaldefs16[num].ofs]); + return; + } + } + + //oh well, must be a parameter. +// if (*(int *)&pr_globals[pr_globaldefs16[num].ofs]) +// Sys_Error("QCLIB: Global field var with no matching field \"%s\", from offset %i", pr_globaldefs16[num].s_name+stringtable, *(int *)&pr_globals[pr_globaldefs16[num].ofs]); + return; + case 32: + for (i=1 ; inumfielddefs; i++) + { + if (!strcmp(pr_fielddefs32[i].s_name+stringtable, pr_globaldefs32[num].s_name+stringtable)) + { + *(int *)&pr_globals[pr_globaldefs32[num].ofs] = QC_RegisterFieldVar(progfuncs, pr_fielddefs32[i].type, pr_globaldefs32[num].s_name+stringtable, -1, *(int *)&pr_globals[pr_globaldefs32[num].ofs]); + return; + } + } + + s = pr_globaldefs32[num].s_name+stringtable; + + for (i = 0; i < numfields; i++) + { + o = field[i].progsofs; + if (o == *(unsigned int *)&pr_globals[pr_globaldefs32[num].ofs]) + { + *(int *)&pr_globals[pr_globaldefs32[num].ofs] = field[i].ofs-progfuncs->fieldadjust; + return; + } + } + + //oh well, must be a parameter. + if (*(int *)&pr_globals[pr_globaldefs32[num].ofs]) + Sys_Error("QCLIB: Global field var with no matching field \"%s\", from offset %i", pr_globaldefs32[num].s_name+stringtable, *(int *)&pr_globals[pr_globaldefs32[num].ofs]); + return; + default: + Sys_Error("Bad bits"); + break; + } + Sys_Error("Should be unreachable"); +} + diff --git a/misc/mediasource/extra/fteqcc-src/pr_x86.c b/misc/mediasource/extra/fteqcc-src/pr_x86.c new file mode 100644 index 00000000..4ebfdda2 --- /dev/null +++ b/misc/mediasource/extra/fteqcc-src/pr_x86.c @@ -0,0 +1,1087 @@ +/* +when I say JIT, I mean load time, not execution time. + +notes: + qc jump offsets are all constants. we have no variable offset jumps (other than function calls/returns) + field remapping... fields are in place, and cannot be adjusted. if a field is not set to 0, its assumed to be a constant. + +optimisations: + none at the moment... + instructions need to be chained. stuff that writes to C should be cacheable, etc. maybe we don't even need to do the write to C + it should also be possible to fold in eq+ifnot, so none of this silly storeing of floats in equality tests + + eax - tmp + ebx - prinst->edicttable + ecx - tmp + edx - tmp + esi - + edi - tmp (because its preserved by subfunctions + ebp - + + to use gas to provide binary opcodes: + vim -N blob.s && as blob.s && objdump.exe -d a.out +*/ + +#define PROGSUSED +#include "progsint.h" + +#ifdef QCJIT + +static float ta, tb, nullfloat=0; + +unsigned int *statementjumps; //[MAX_STATEMENTS*2] +unsigned char **statementoffsets; //[MAX_STATEMENTS] +unsigned int numjumps; +unsigned char *code; +unsigned int codesize; +unsigned int jitstatements; + +void EmitByte(unsigned char byte) +{ + code[codesize++] = byte; +} +void Emit4Byte(unsigned int value) +{ + code[codesize++] = (value>> 0)&0xff; + code[codesize++] = (value>> 8)&0xff; + code[codesize++] = (value>>16)&0xff; + code[codesize++] = (value>>24)&0xff; +} +void EmitAdr(void *value) +{ + Emit4Byte((unsigned int)value); +} +void EmitFloat(float value) +{ + union {float f; unsigned int i;} u; + u.f = value; + Emit4Byte(u.i); +} +void Emit2Byte(unsigned short value) +{ + code[codesize++] = (value>> 0)&0xff; + code[codesize++] = (value>> 8)&0xff; +} + +void EmitFOffset(void *func, int bias) +{ + union {void *f; unsigned int i;} u; + u.f = func; + u.i -= (unsigned int)&code[codesize+bias]; + Emit4Byte(u.i); +} + +void Emit4ByteJump(int statementnum, int offset) +{ + statementjumps[numjumps++] = codesize; + statementjumps[numjumps++] = statementnum; + statementjumps[numjumps++] = offset; + + //the offset is filled in later + codesize += 4; +} + +void FixupJumps(void) +{ + unsigned int j; + unsigned char *codesrc; + unsigned char *codedst; + unsigned int offset; + + unsigned int v; + + for (j = 0; j < numjumps;) + { + v = statementjumps[j++]; + codesrc = &code[v]; + + v = statementjumps[j++]; + codedst = statementoffsets[v]; + + v = statementjumps[j++]; + offset = (int)(codedst - (codesrc-v)); //3rd term because the jump is relative to the instruction start, not the instruction's offset + + codesrc[0] = (offset>> 0)&0xff; + codesrc[1] = (offset>> 8)&0xff; + codesrc[2] = (offset>>16)&0xff; + codesrc[3] = (offset>>24)&0xff; + } +} + +int PR_LeaveFunction (progfuncs_t *progfuncs); +int PR_EnterFunction (progfuncs_t *progfuncs, dfunction_t *f, int progsnum); + +pbool PR_GenerateJit(progfuncs_t *progfuncs) +{ + unsigned int i; + dstatement16_t *op = (dstatement16_t*)current_progstate->statements; + unsigned int numstatements = current_progstate->progs->numstatements; + int *glob = (int*)current_progstate->globals; + + if (current_progstate->numbuiltins) + return false; + + jitstatements = numstatements; + + statementjumps = malloc(numstatements*12); + statementoffsets = malloc(numstatements*4); + code = malloc(numstatements*500); + + numjumps = 0; + codesize = 0; + + + + for (i = 0; i < numstatements; i++) + { + statementoffsets[i] = &code[codesize]; + switch(op[i].op) + { + //jumps + case OP_IF: + //integer compare + //if a, goto b + + //cmpl $0,glob[A] + EmitByte(0x83);EmitByte(0x3d);EmitAdr(glob + op[i].a);EmitByte(0x0); + //jnz B + EmitByte(0x0f);EmitByte(0x85);Emit4ByteJump(i + (signed short)op[i].b, -4); + break; + + case OP_IFNOT: + //integer compare + //if !a, goto b + + //cmpl $0,glob[A] + EmitByte(0x83);EmitByte(0x3d);EmitAdr(glob + op[i].a);EmitByte(0x0); + //jz B + EmitByte(0x0f);EmitByte(0x84);Emit4ByteJump(i + (signed short)op[i].b, -4); + break; + + case OP_GOTO: + EmitByte(0xE9);Emit4ByteJump(i + (signed short)op[i].a, -4); + break; + + //function returns + case OP_DONE: + case OP_RETURN: + //done and return are the same + + //part 1: store A into OFS_RETURN + + if (!op[i].a) + { + //assumption: anything that returns address 0 is a void or zero return. + //thus clear eax and copy that to the return vector. + EmitByte(0x31);EmitByte(0xc0); + EmitByte(0xa3);EmitAdr(glob + OFS_RETURN+0); + EmitByte(0xa3);EmitAdr(glob + OFS_RETURN+1); + EmitByte(0xa3);EmitAdr(glob + OFS_RETURN+2); + } + else + { + //movl glob[A+0],eax + EmitByte(0xa1);EmitAdr(glob + op[i].a+0); + //movl glob[A+0],edx + EmitByte(0x8b);EmitByte(0x0d);EmitAdr(glob + op[i].a+1); + //movl glob[A+0],ecx + EmitByte(0x8b);EmitByte(0x15);EmitAdr(glob + op[i].a+2); + //movl eax, glob[OFS_RET+0] + EmitByte(0xa3);EmitAdr(glob + OFS_RETURN+0); + //movl edx, glob[OFS_RET+0] + EmitByte(0x89);EmitByte(0x15);EmitAdr(glob + OFS_RETURN+1); + //movl ecx, glob[OFS_RET+0] + EmitByte(0x89);EmitByte(0x15);EmitAdr(glob + OFS_RETURN+2); + } + + //call leavefunction to get the return address + +// pushl progfuncs + EmitByte(0x68);EmitAdr(progfuncs); +// call PR_LeaveFunction + EmitByte(0xe8);EmitFOffset(PR_LeaveFunction, 4); +// add $4,%esp + EmitByte(0x83);EmitByte(0xc4);EmitByte(0x04); +// movl pr_depth,%edx + EmitByte(0x8b);EmitByte(0x15);EmitAdr(&pr_depth); +// cmp prinst->exitdepth,%edx + EmitByte(0x3b);EmitByte(0x15);EmitAdr(&prinst->exitdepth); +// je returntoc + EmitByte(0x74);EmitByte(0x09); +// mov statementoffsets[%eax*4],%eax + EmitByte(0x8b);EmitByte(0x04);EmitByte(0x85);EmitAdr(statementoffsets+1); +// jmp eax + EmitByte(0xff);EmitByte(0xe0); +// returntoc: +// ret + EmitByte(0xc3); + break; + + //function calls + case OP_CALL0: + case OP_CALL1: + case OP_CALL2: + case OP_CALL3: + case OP_CALL4: + case OP_CALL5: + case OP_CALL6: + case OP_CALL7: + case OP_CALL8: + //save the state in place the rest of the engine can cope with + //movl $i, pr_xstatement + EmitByte(0xc7);EmitByte(0x05);EmitAdr(&pr_xstatement);Emit4Byte(i); + //movl $(op[i].op-OP_CALL0), pr_argc + EmitByte(0xc7);EmitByte(0x05);EmitAdr(&pr_argc);Emit4Byte(op[i].op-OP_CALL0); + + //figure out who we're calling, and what that involves + //%eax = glob[A] + EmitByte(0xa1); EmitAdr(glob + op[i].a); + //eax is now the func num + + //mov %eax,%ecx + EmitByte(0x89); EmitByte(0xc1); + //shr $24,%ecx + EmitByte(0xc1); EmitByte(0xe9); EmitByte(0x18); + //ecx is now the progs num for the new func + + //cmp %ecx,pr_typecurrent + EmitByte(0x39); EmitByte(0x0d); EmitAdr(&pr_typecurrent); + //je sameprogs + EmitByte(0x74); EmitByte(0x3); + { + //can't handle switching progs + + //FIXME: recurse though PR_ExecuteProgram + //push eax + //push progfuncs + //call PR_ExecuteProgram + //add $8,%esp + //remember to change the je above + + //err... exit depth? no idea + EmitByte(0xcd);EmitByte(op[i].op); //int $X + + + //ret + EmitByte(0xc3); + } + //sameprogs: + + //andl $0x00ffffff, %eax + EmitByte(0x25);Emit4Byte(0x00ffffff); + + //mov $sizeof(dfunction_t),%edx + EmitByte(0xba);Emit4Byte(sizeof(dfunction_t)); + //mul %edx + EmitByte(0xf7); EmitByte(0xe2); + //add pr_functions,%eax + EmitByte(0x05); EmitAdr(pr_functions); + + //eax is now the dfunction_t to be called + //edx is clobbered. + + //mov (%eax),%edx + EmitByte(0x8b);EmitByte(0x10); + //edx is now the first statement number + //cmp $0,%edx + EmitByte(0x83);EmitByte(0xfa);EmitByte(0x00); + //jl isabuiltin + EmitByte(0x7c);EmitByte(22); + + { + //push %ecx + EmitByte(0x51); + //push %eax + EmitByte(0x50); + //pushl progfuncs + EmitByte(0x68);EmitAdr(progfuncs); + //call PR_EnterFunction + EmitByte(0xe8);EmitFOffset(PR_EnterFunction, 4); + //sub $12,%esp + EmitByte(0x83);EmitByte(0xc4);EmitByte(0xc); + //eax is now the next statement number (first of the new function, usually equal to ecx, but not always) + + //jmp statementoffsets[%eax*4] + EmitByte(0xff);EmitByte(0x24);EmitByte(0x85);EmitAdr(statementoffsets+1); + } + //isabuiltin: + + + //push current_progstate->globals + EmitByte(0x68);EmitAdr(current_progstate->globals); + //push progfuncs + EmitByte(0x68);EmitAdr(progfuncs); + //neg %edx + EmitByte(0xf7);EmitByte(0xda); + //call externs->globalbuiltins[%edx,4] +//FIXME: make sure this dereferences + EmitByte(0xff);EmitByte(0x14);EmitByte(0x95);EmitAdr(externs->globalbuiltins); + //add $8,%esp + EmitByte(0x83);EmitByte(0xc4);EmitByte(0x8); + + //but that builtin might have been Abort() + + //mov prinst->continuestatement,%eax + EmitByte(0xa1);EmitAdr(&prinst->continuestatement); + //eax is now prinst->continuestatement + + //cmp $-1,%eax + EmitByte(0x83);EmitByte(0xf8);EmitByte(0xff); + //je donebuiltincall + EmitByte(0x74);EmitByte(10+8); + { +EmitByte(0xcc); + //jmp statementoffsets[%eax*4] + EmitByte(0xff);EmitByte(0x24);EmitByte(0x85);EmitAdr(statementoffsets+1); + + //mov $-1,prinst->continuestatement + EmitByte(0xc7);EmitByte(0x05);EmitAdr(&prinst->continuestatement+1);Emit4Byte((unsigned int)-1); + } + //donebuiltincall: + break; + + case OP_MUL_F: + //flds glob[A] + EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a); + //fmuls glob[B] + EmitByte(0xd8);EmitByte(0x0d);EmitAdr(glob + op[i].b); + //fstps glob[C] + EmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c); + break; + case OP_DIV_F: + //flds glob[A] + EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a); + //fdivs glob[B] + EmitByte(0xd8);EmitByte(0x35);EmitAdr(glob + op[i].b); + //fstps glob[C] + EmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c); + break; + case OP_ADD_F: + //flds glob[A] + EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a); + //fadds glob[B] + EmitByte(0xd8);EmitByte(0x05);EmitAdr(glob + op[i].b); + //fstps glob[C] + EmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c); + break; + case OP_SUB_F: + //flds glob[A] + EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a); + //fsubs glob[B] + EmitByte(0xd8);EmitByte(0x25);EmitAdr(glob + op[i].b); + //fstps glob[C] + EmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c); + break; + + case OP_NOT_F: + //flds glob[A] + EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a); + //fldz + EmitByte(0xd9);EmitByte(0xee); + //fnstsw %ax + EmitByte(0xdf);EmitByte(0xe0); + //testb 0x40,%ah + EmitByte(0xf6);EmitByte(0xc4);EmitByte(0x40); + //je noteq + EmitByte(0x74);EmitByte(0x0c); + //movl 1.0f,glob[C] + EmitByte(0xc7);EmitByte(0x05);EmitAdr(glob + op[i].c);EmitFloat(0.0f); + //jmp end + EmitByte(0xeb);EmitByte(0x0a); + //noteq: + //movl 0.0f,glob[C] + EmitByte(0xc7);EmitByte(0x05);EmitAdr(glob + op[i].c);EmitFloat(1.0f); + //end: + break; + + case OP_STORE_F: + case OP_STORE_S: + case OP_STORE_ENT: + case OP_STORE_FLD: + case OP_STORE_FNC: + //movl glob[A],eax + EmitByte(0xa1);EmitAdr(glob + op[i].a); + //movl eax,glob[B] + EmitByte(0xa3);EmitAdr(glob + op[i].b); + break; + + case OP_STORE_V: + //movl glob[A+0],eax + EmitByte(0xa1);EmitAdr(glob + op[i].a+0); + //movl glob[A+1],edx + EmitByte(0x8b);EmitByte(0x0d);EmitAdr(glob + op[i].a+1); + //movl glob[A+2],ecx + EmitByte(0x8b);EmitByte(0x15);EmitAdr(glob + op[i].a+2); + + //movl eax, glob[B+0] + EmitByte(0xa3);EmitAdr(glob + op[i].b+0); + //movl edx, glob[B+1] + EmitByte(0x89);EmitByte(0x15);EmitAdr(glob + op[i].b+1); + //movl ecx, glob[B+2] + EmitByte(0x89);EmitByte(0x15);EmitAdr(glob + op[i].b+2); + break; + + case OP_LOAD_F: + case OP_LOAD_S: + case OP_LOAD_ENT: + case OP_LOAD_FLD: + case OP_LOAD_FNC: + case OP_LOAD_V: + //a is the ent number, b is the field + //c is the dest + + //movl glob[A+0],eax + EmitByte(0xa1);EmitAdr(glob + op[i].a); + //mov glob[B],ecx + EmitByte(0x8b); EmitByte(0x0d);EmitAdr(glob + op[i].b); + //FIXME: bound eax (ent number) + //FIXME: bound ecx (field index) + //mov (ebx,eax,4).%eax + EmitByte(0x8b); EmitByte(0x04); EmitByte(0x83); + //eax is now an edictrun_t + //mov fields(,%eax,4),%edx + EmitByte(0x8b);EmitByte(0x50);EmitByte((int)&((edictrun_t*)NULL)->fields); + //edx is now the field array for that ent + + //mov fieldajust(%edx,%ecx,4),%eax //offset = progfuncs->fieldadjust + EmitByte(0x8b); EmitByte(0x84); EmitByte(0x8a); Emit4Byte(progfuncs->fieldadjust*4); + //mov edx,glob[C] + EmitByte(0xa3);EmitAdr(glob + op[i].c); + + if (op[i].op == OP_LOAD_V) + { + //mov fieldajust+4(%edx,%ecx,4),%eax //offset = progfuncs->fieldadjust + EmitByte(0x8b); EmitByte(0x84); EmitByte(0x8a); Emit4Byte(4+progfuncs->fieldadjust*4); + //mov edx,glob[C+1] + EmitByte(0xa3);EmitAdr(glob + op[i].c+1); + + //mov fieldajust+8(%edx,%ecx,4),%eax //offset = progfuncs->fieldadjust + EmitByte(0x8b); EmitByte(0x84); EmitByte(0x8a); Emit4Byte(4+progfuncs->fieldadjust*4); + //mov edx,glob[C+1] + EmitByte(0xa3);EmitAdr(glob + op[i].c+2); + } + break; + + case OP_ADDRESS: + //a is the ent number, b is the field + //c is the dest + + //movl glob[A+0],eax + EmitByte(0xa1);EmitAdr(glob + op[i].a); + //mov glob[B],ecx + EmitByte(0x8b); EmitByte(0x0d);EmitAdr(glob + op[i].b); + //FIXME: bound eax (ent number) + //FIXME: bound ecx (field index) + //mov (ebx,eax,4).%eax + EmitByte(0x8b); EmitByte(0x04); EmitByte(0x83); + //eax is now an edictrun_t + //mov fields(,%eax,4),%edx + EmitByte(0x8b);EmitByte(0x50);EmitByte((int)&((edictrun_t*)NULL)->fields); + //edx is now the field array for that ent + //mov fieldajust(%edx,%ecx,4),%eax //offset = progfuncs->fieldadjust + //EmitByte(0x8d); EmitByte(0x84); EmitByte(0x8a); EmitByte(progfuncs->fieldadjust*4); + EmitByte(0x8d); EmitByte(0x84); EmitByte(0x8a); Emit4Byte(progfuncs->fieldadjust*4); + //mov edx,glob[C] + EmitByte(0xa3);EmitAdr(glob + op[i].c); + break; + + case OP_STOREP_F: + case OP_STOREP_S: + case OP_STOREP_ENT: + case OP_STOREP_FLD: + case OP_STOREP_FNC: + //movl glob[A],eax + EmitByte(0xa1);EmitAdr(glob + op[i].a); + //mov glob[B],ecx + EmitByte(0x8b); EmitByte(0x0d);EmitAdr(glob + op[i].b); + //mov %eax,(%ecx) + EmitByte(0x89);EmitByte(0x01); + break; + + case OP_STOREP_V: + //mov glob[B],ecx + EmitByte(0x8b); EmitByte(0x0d);EmitAdr(glob + op[i].b); + //movl glob[A],eax + EmitByte(0xa1);EmitAdr(glob + op[i].a+0); + //mov %eax,0(%ecx) + EmitByte(0x89);EmitByte(0x01); + //movl glob[A],eax + EmitByte(0xa1);EmitAdr(glob + op[i].a+0); + //mov %eax,4(%ecx) + EmitByte(0x89);EmitByte(0x41);EmitByte(0x04); + //movl glob[A],eax + EmitByte(0xa1);EmitAdr(glob + op[i].a+0); + //mov %eax,8(%ecx) + EmitByte(0x89);EmitByte(0x41);EmitByte(0x08); + break; + + case OP_EQ_E: + case OP_EQ_FNC: + //integer equality + //movl glob[A],%eax + EmitByte(0xa1);EmitAdr(glob + op[i].a); + //cmp glob[B],%eax + EmitByte(0x3b); EmitByte(0x0f); EmitAdr(glob + op[i].b); + //je 12 + EmitByte(0x74);EmitByte(0x0c); + //mov 0.0f,glob[C] + EmitByte(0xc7);EmitByte(0x05); EmitAdr(glob + op[i].a);EmitFloat(0.0f); + //jmp 10 + EmitByte(0xeb);EmitByte(0x0a); + //mov 1.0f,glob[C] + EmitByte(0xc7);EmitByte(0x05); EmitAdr(glob + op[i].a);EmitFloat(1.0f); + break; + + case OP_NE_E: + case OP_NE_FNC: + //integer equality + //movl glob[A],%eax + EmitByte(0xa1);EmitAdr(glob + op[i].a); + //cmp glob[B],%eax + EmitByte(0x3b); EmitByte(0x0f); EmitAdr(glob + op[i].b); + //je 12 + EmitByte(0x74);EmitByte(0x0c); + //mov 0.0f,glob[C] + EmitByte(0xc7);EmitByte(0x05); EmitAdr(glob + op[i].a);EmitFloat(1.0f); + //jmp 10 + EmitByte(0xeb);EmitByte(0x0a); + //mov 1.0f,glob[C] + EmitByte(0xc7);EmitByte(0x05); EmitAdr(glob + op[i].a);EmitFloat(0.0f); + break; + + case OP_NOT_ENT: + case OP_NOT_FNC: + //cmp glob[B],%eax + EmitByte(0x8c); EmitByte(0x3d); EmitAdr(glob + op[i].a);EmitByte(0x00); + //je 12 + EmitByte(0x74);EmitByte(0x0c); + //mov 0.0f,glob[C] + EmitByte(0xc7);EmitByte(0x05); EmitAdr(glob + op[i].a);EmitFloat(0.0f); + //jmp 10 + EmitByte(0xeb);EmitByte(0x0a); + //mov 1.0f,glob[C] + EmitByte(0xc7);EmitByte(0x05); EmitAdr(glob + op[i].c);EmitFloat(1.0f); + break; + + case OP_BITOR: //floats... + //flds glob[A] + EmitByte(0xd9); EmitByte(0x05);EmitAdr(glob + op[i].a); + //flds glob[B] + EmitByte(0xd9); EmitByte(0x05);EmitAdr(glob + op[i].b); + //fistp tb + EmitByte(0xdf); EmitByte(0x1d);EmitAdr(&tb); + //fistp ta + EmitByte(0xdf); EmitByte(0x1d);EmitAdr(&ta); + //mov ta,%eax + EmitByte(0xa1); EmitAdr(&ta); + //and tb,%eax + EmitByte(0x09); EmitByte(0x05);EmitAdr(&tb); + //fild tb + EmitByte(0xdf); EmitByte(0x05);EmitAdr(&tb); + //fstps glob[C] + EmitByte(0xd9); EmitByte(0x1d);EmitAdr(glob + op[i].c); + break; + + case OP_BITAND: + //flds glob[A] + EmitByte(0xd9); EmitByte(0x05);EmitAdr(glob + op[i].a); + //flds glob[B] + EmitByte(0xd9); EmitByte(0x05);EmitAdr(glob + op[i].b); + //fistp tb + EmitByte(0xdf); EmitByte(0x1d);EmitAdr(&tb); + //fistp ta + EmitByte(0xdf); EmitByte(0x1d);EmitAdr(&ta); + //mov ta,%eax + EmitByte(0xa1); EmitAdr(&ta); + //and tb,%eax + EmitByte(0x21); EmitByte(0x05);EmitAdr(&tb); + //fild tb + EmitByte(0xdf); EmitByte(0x05);EmitAdr(&tb); + //fstps glob[C] + EmitByte(0xd9); EmitByte(0x1d);EmitAdr(glob + op[i].c); + break; + + case OP_AND: + //test floats properly, so we don't get confused with -0.0 + + //flds glob[A] + EmitByte(0xd9); EmitByte(0x05); EmitAdr(glob + op[i].a); + //fcomps nullfloat + EmitByte(0xd8); EmitByte(0x1d); EmitAdr(&nullfloat); + //fnstsw %ax + EmitByte(0xdf); EmitByte(0xe0); + //test $0x40,%ah + EmitByte(0xf6); EmitByte(0xc4);EmitByte(0x40); + //je onefalse + EmitByte(0x75); EmitByte(0x1f); + + //flds glob[B] + EmitByte(0xd9); EmitByte(0x05); EmitAdr(glob + op[i].b); + //fcomps nullfloat + EmitByte(0xd8); EmitByte(0x1d); EmitAdr(&nullfloat); + //fnstsw %ax + EmitByte(0xdf); EmitByte(0xe0); + //test $0x40,%ah + EmitByte(0xf6); EmitByte(0xc4);EmitByte(0x40); + //jne onefalse + EmitByte(0x75); EmitByte(0x0c); + + //mov float0,glob[C] + EmitByte(0xc7); EmitByte(0x05); EmitAdr(glob + op[i].c); EmitFloat(1.0f); + //jmp done + EmitByte(0xeb); EmitByte(0x0a); + + //onefalse: + //mov float1,glob[C] + EmitByte(0xc7); EmitByte(0x05); EmitAdr(glob + op[i].c); EmitFloat(0.0f); + //done: + break; + case OP_OR: + //test floats properly, so we don't get confused with -0.0 + + //flds glob[A] + EmitByte(0xd9); EmitByte(0x05); EmitAdr(glob + op[i].a); + //fcomps nullfloat + EmitByte(0xd8); EmitByte(0x1d); EmitAdr(&nullfloat); + //fnstsw %ax + EmitByte(0xdf); EmitByte(0xe0); + //test $0x40,%ah + EmitByte(0xf6); EmitByte(0xc4);EmitByte(0x40); + //je onetrue + EmitByte(0x74); EmitByte(0x1f); + + //flds glob[B] + EmitByte(0xd9); EmitByte(0x05); EmitAdr(glob + op[i].b); + //fcomps nullfloat + EmitByte(0xd8); EmitByte(0x1d); EmitAdr(&nullfloat); + //fnstsw %ax + EmitByte(0xdf); EmitByte(0xe0); + //test $0x40,%ah + EmitByte(0xf6); EmitByte(0xc4);EmitByte(0x40); + //je onetrue + EmitByte(0x74); EmitByte(0x0c); + + //mov float0,glob[C] + EmitByte(0xc7); EmitByte(0x05); EmitAdr(glob + op[i].c); EmitFloat(0.0f); + //jmp done + EmitByte(0xeb); EmitByte(0x0a); + + //onetrue: + //mov float1,glob[C] + EmitByte(0xc7); EmitByte(0x05); EmitAdr(glob + op[i].c); EmitFloat(1.0f); + //done: + break; + + case OP_EQ_S: + case OP_NE_S: + //put a in ecx + //put b in edi + //mov a,%ecx + EmitByte(0x8b); EmitByte(0x0d); EmitAdr(glob + op[i].a); + //mov b,%edi + EmitByte(0x8b); EmitByte(0x3d); EmitAdr(glob + op[i].b); + + //early out if they're equal + //cmp %ecx,%edi + EmitByte(0x39); EmitByte(0xd1); + //je _true + EmitByte(0x74); EmitByte(0x68); + + //if a is 0, check if b is "" + //jecxz ais0 + EmitByte(0xe3); EmitByte(0x1a); + + //if b is 0, check if a is "" + //cmp $0,%edi + EmitByte(0x83); EmitByte(0xff); EmitByte(0x00); + //jne bnot0 + EmitByte(0x75); EmitByte(0x2a); + { + //push a + EmitByte(0x51); + //push progfuncs + EmitByte(0x68); EmitAdr(progfuncs); + //call PR_StringToNative + EmitByte(0xe8); EmitFOffset(PR_StringToNative,4); + //add $8,%esp + EmitByte(0x83); EmitByte(0xc4); EmitByte(0x08); + //cmpb $0,(%eax) + EmitByte(0x80); EmitByte(0x38); EmitByte(0x00); + //je _true + EmitByte(0x74); EmitByte(0x4b); + //jmp _false + EmitByte(0xeb); EmitByte(0x3d); + + //ais0: + { + //push edi + EmitByte(0x57); + //push progfuncs + EmitByte(0x68); EmitAdr(progfuncs); + //call PR_StringToNative + EmitByte(0xe8); EmitFOffset(PR_StringToNative,4); + //add $8,%esp + EmitByte(0x83); EmitByte(0xc4); EmitByte(0x08); + //cmpb $0,(%eax) + EmitByte(0x80); EmitByte(0x38); EmitByte(0x00); + //je _true + EmitByte(0x74); EmitByte(0x36); + //jmp _false + EmitByte(0xeb); EmitByte(0x28); + } + } + //bnot0: + + //push ecx + EmitByte(0x51); + //push progfuncs + EmitByte(0x68); EmitAdr(progfuncs); + //call PR_StringToNative + EmitByte(0xe8); EmitFOffset(PR_StringToNative,4); + //push %eax + EmitByte(0x50); + + //push %edi + EmitByte(0x57); + //push progfuncs + EmitByte(0x68); EmitAdr(progfuncs); + //call PR_StringToNative + EmitByte(0xe8); EmitFOffset(PR_StringToNative,4); + //add $8,%esp + EmitByte(0x83); EmitByte(0xc4); EmitByte(0x08); + + + //push %eax + EmitByte(0x50); + //call strcmp + EmitByte(0xe8); EmitFOffset(strcmp,4); + //add $16,%esp + EmitByte(0x83); EmitByte(0xc4); EmitByte(0x10); + //cmp $0,%eax + EmitByte(0x83); EmitByte(0xf8); EmitByte(0x00); + //je _true + EmitByte(0x74); EmitByte(0x0c); +//_false: + //mov 0.0f,c + EmitByte(0xc7); EmitByte(0x05); EmitAdr(glob + op[i].c); EmitFloat((op[i].op == OP_NE_S)?1.0f:0.0f); + //jmp done + EmitByte(0xeb); EmitByte(0x0a); +//_true: + //mov 1.0f,c + EmitByte(0xc7); EmitByte(0x05); EmitAdr(glob + op[i].c); EmitFloat((op[i].op == OP_NE_S)?0.0f:1.0f); +//_done: + break; + + case OP_NOT_S: + //mov A,%eax + EmitByte(0xa1);EmitAdr(glob + op[i].a); + //cmp $0,%eax + EmitByte(0x83); EmitByte(0xf8); EmitByte(0x00); + //je _true + EmitByte(0x74); EmitByte(0x1f); + //push %eax + EmitByte(0x50); + //push progfuncs + EmitByte(0x68); EmitAdr(progfuncs); + //call PR_StringToNative + EmitByte(0xe8); EmitFOffset(PR_StringToNative,4); + //add $8,%esp + EmitByte(0x83); EmitByte(0xc4); EmitByte(0x08); + //cmpb $0,(%eax) + EmitByte(0x80); EmitByte(0x38); EmitByte(0x00); + //je _true + EmitByte(0x74); EmitByte(0x0c); +//_false: + //mov 0.0f,c + EmitByte(0xc7); EmitByte(0x05); EmitAdr(glob + op[i].c); EmitFloat(0.0f); + //jmp done + EmitByte(0xeb); EmitByte(0x0a); +//_true: + //mov 1.0f,c + EmitByte(0xc7); EmitByte(0x05); EmitAdr(glob + op[i].c); EmitFloat(1.0f); +//_done: + break; + + case OP_ADD_V: + //flds glob[A] + EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a+0); + //fadds glob[B] + EmitByte(0xd8);EmitByte(0x05);EmitAdr(glob + op[i].b+0); + //fstps glob[C] + EmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c+0); + + //flds glob[A] + EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a+1); + //fadds glob[B] + EmitByte(0xd8);EmitByte(0x05);EmitAdr(glob + op[i].b+1); + //fstps glob[C] + EmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c+1); + + //flds glob[A] + EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a+2); + //fadds glob[B] + EmitByte(0xd8);EmitByte(0x05);EmitAdr(glob + op[i].b+2); + //fstps glob[C] + EmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c+2); + break; + case OP_SUB_V: + //flds glob[A] + EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a+0); + //fsubs glob[B] + EmitByte(0xd8);EmitByte(0x25);EmitAdr(glob + op[i].b+0); + //fstps glob[C] + EmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c+0); + + //flds glob[A] + EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a+1); + //fsubs glob[B] + EmitByte(0xd8);EmitByte(0x25);EmitAdr(glob + op[i].b+1); + //fstps glob[C] + EmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c+1); + + //flds glob[A] + EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a+2); + //fsubs glob[B] + EmitByte(0xd8);EmitByte(0x25);EmitAdr(glob + op[i].b+2); + //fstps glob[C] + EmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c+2); + break; + + case OP_MUL_V: + //this is actually a dotproduct + //flds glob[A] + EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a+0); + //fmuls glob[B] + EmitByte(0xd8);EmitByte(0x0d);EmitAdr(glob + op[i].b+0); + + //flds glob[A] + EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a+1); + //fmuls glob[B] + EmitByte(0xd8);EmitByte(0x0d);EmitAdr(glob + op[i].b+1); + + //flds glob[A] + EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a+2); + //fmuls glob[B] + EmitByte(0xd8);EmitByte(0x0d);EmitAdr(glob + op[i].b+2); + + //faddp + EmitByte(0xde);EmitByte(0xc1); + //faddp + EmitByte(0xde);EmitByte(0xc1); + + //fstps glob[C] + EmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c); + break; + + case OP_EQ_F: + case OP_NE_F: + case OP_LE: + case OP_GE: + case OP_LT: + case OP_GT: + //flds glob[A] + EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a); + //flds glob[B] + EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].b); + //fcomip %st(1),%st + EmitByte(0xdf);EmitByte(0xe9); + //fstp %st(0) (aka: pop) + EmitByte(0xdd);EmitByte(0xd8); + + //jcc _true + if (op[i].op == OP_LE) + EmitByte(0x7e); //jle + else if (op[i].op == OP_GE) + EmitByte(0x7d); //jge + else if (op[i].op == OP_LT) + EmitByte(0x7c); //jl + else if (op[i].op == OP_GT) + EmitByte(0x7f); //jg + else if (op[i].op == OP_NE_F) + EmitByte(0x75); //jne + else + EmitByte(0x74); //je + EmitByte(0x0c); +//_false: + //mov 0.0f,c + EmitByte(0xc7); EmitByte(0x05); EmitAdr(glob + op[i].c); EmitFloat(0.0f); + //jmp done + EmitByte(0xeb); EmitByte(0x0a); +//_true: + //mov 1.0f,c + EmitByte(0xc7); EmitByte(0x05); EmitAdr(glob + op[i].c); EmitFloat(1.0f); +//_done: + break; + + case OP_MUL_FV: + case OP_MUL_VF: + // + { + int v; + int f; + if (op[i].op == OP_MUL_FV) + { + f = op[i].a; + v = op[i].b; + } + else + { + v = op[i].a; + f = op[i].b; + } + + //flds glob[F] + EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + f); + + //flds glob[V0] + EmitByte(0xd8);EmitByte(0x0d);EmitAdr(glob + v+0); + //fmul st(1) + EmitByte(0xd8);EmitByte(0xc9); + //fstps glob[C] + EmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c+0); + + //flds glob[V0] + EmitByte(0xd8);EmitByte(0x0d);EmitAdr(glob + v+1); + //fmul st(1) + EmitByte(0xd8);EmitByte(0xc9); + //fstps glob[C] + EmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c+1); + + //flds glob[V0] + EmitByte(0xd8);EmitByte(0x0d);EmitAdr(glob + v+2); + //fmul st(1) + EmitByte(0xd8);EmitByte(0xc9); + //fstps glob[C] + EmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c+2); + + //fstp %st(0) (aka: pop) + EmitByte(0xdd);EmitByte(0xd8); + } + break; + + case OP_STATE: + //externs->stateop(progfuncs, OPA->_float, OPB->function); + //push b + EmitByte(0xff);EmitByte(0x35);EmitAdr(glob + op[i].b); + //push a + EmitByte(0xff);EmitByte(0x35);EmitAdr(glob + op[i].a); + //push $progfuncs + EmitByte(0x68); EmitAdr(progfuncs); + //call externs->stateop + EmitByte(0xe8); EmitFOffset(externs->stateop, 4); + //add $12,%esp + EmitByte(0x83); EmitByte(0xc4); EmitByte(0x0c); + break; +#if 0 + case OP_NOT_V: + //flds 0 + //flds glob[A+0] + //fcomip %st(1),%st + //jne _true + //flds glob[A+1] + //fcomip %st(1),%st + //jne _true + //flds glob[A+1] + //fcomip %st(1),%st + //jne _true + //mov 1,C + //jmp done + //_true: + //mov 0,C + //done: + break; + + case OP_EQ_V: + //flds glob[A] + EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a+0); + //flds glob[B] + EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].b+0); + //fcomip %st(1),%st + EmitByte(0xdf);EmitByte(0xe9); + //fstp %st(0) (aka: pop) + EmitByte(0xdd);EmitByte(0xd8); + + //jncc _true + if (op[i].op == OP_NE_V) + EmitByte(0x74); //je + else + EmitByte(0x75); //jne + EmitByte(0x0c); +//_false0: + //mov 0.0f,c + EmitByte(0xc7); EmitByte(0x05); EmitAdr(glob + op[i].c); EmitFloat(1.0f); + //jmp done + EmitByte(0xeb); EmitByte(0x0a); + + +//_true: + //mov 1.0f,c + EmitByte(0xc7); EmitByte(0x05); EmitAdr(glob + op[i].c); EmitFloat(0.0f); +//_done: + break; + + + case OP_EQ_V: + EmitByte(0xcd);EmitByte(op[i].op); + printf("QCJIT: instruction %i is not implemented\n", op[i].op); + break; + + case OP_NE_V: + EmitByte(0xcd);EmitByte(op[i].op); + printf("QCJIT: instruction %i is not implemented\n", op[i].op); + break; + + case OP_NOT_V: + EmitByte(0xcd);EmitByte(op[i].op); + printf("QCJIT: instruction %i is not implemented\n", op[i].op); + break; +#endif + default: + printf("QCJIT: Extended instruction set %i is not supported, not using jit.\n", op[i].op); + + + free(statementjumps); //[MAX_STATEMENTS] + free(statementoffsets); //[MAX_STATEMENTS] + free(code); + statementoffsets = NULL; + return false; + } + } + + FixupJumps(); + +#ifdef _WIN32 + { + DWORD old; + + //this memory is on the heap. + //this means that we must maintain read/write protection, or libc will crash us + VirtualProtect(code, codesize, PAGE_EXECUTE_READWRITE, &old); + } +#endif + +// externs->WriteFile("jit.x86", code, codesize); + + return true; +} + +void PR_EnterJIT(progfuncs_t *progfuncs, int statement) +{ +#ifdef __GNUC__ + //call, it clobbers pretty much everything. + asm("call *%0" :: "r"(statementoffsets[statement+1]),"b"(prinst->edicttable):"cc","memory","eax","ecx","edx"); +#elif defined(_MSC_VER) + void *entry = statementoffsets[statement+1]; + void *edicttable = prinst->edicttable; + __asm { + pushad + mov eax,entry + mov ebx,edicttable + call eax + popad + } +#else + #error "Sorry, no idea how to enter assembler safely for your compiler" +#endif +} +#endif diff --git a/misc/mediasource/extra/fteqcc-src/progsint.h b/misc/mediasource/extra/fteqcc-src/progsint.h new file mode 100644 index 00000000..39c316e5 --- /dev/null +++ b/misc/mediasource/extra/fteqcc-src/progsint.h @@ -0,0 +1,477 @@ +#ifdef _WIN32 + + #ifndef AVAIL_ZLIB + #ifdef _MSC_VER + //#define AVAIL_ZLIB + #endif + #endif + + #include + + enum{false, true}; +#else + #include + #include + + #include + #include + #include + #include + + #ifndef __declspec + #define __declspec(mode) + #endif + + typedef enum{false, true} boolean; +//#define _inline inline +#endif +typedef unsigned char qbyte; +#include + +#if defined(_M_IX86) || defined(__i386__) +//#define QCJIT +#endif + +#define DLL_PROG +#ifndef PROGSUSED +#define PROGSUSED +#endif + +extern int maxedicts; +extern int maxprogs; +extern int hunksize; + +#include "progtype.h" +#include "progslib.h" + +//extern progfuncs_t *progfuncs; + +#define prinst progfuncs->prinst +#define externs progfuncs->parms + +#include "pr_comp.h" + +#include "qcd.h" + +typedef struct +{ + int targetflags; //weather we need to mark the progs as a newer version + char *name; + char *opname; + int priority; + enum {ASSOC_LEFT, ASSOC_RIGHT, ASSOC_RIGHT_RESULT} associative; + struct QCC_type_s **type_a, **type_b, **type_c; +} QCC_opcode_t; +extern QCC_opcode_t pr_opcodes[]; // sized by initialization + + + + +#ifdef _MSC_VER +#define Q_vsnprintf _vsnprintf +#else +#define Q_vsnprintf vsnprintf +#endif + + +#define sv_num_edicts (*externs->sv_num_edicts) +#define sv_edicts (*externs->sv_edicts) + +#define printf externs->printf +#define Sys_Error externs->Sys_Error +#define Abort externs->Abort + +#define memalloc externs->memalloc +#define memfree externs->memfree + +int PRHunkMark(progfuncs_t *progfuncs); +void PRHunkFree(progfuncs_t *progfuncs, int mark); +void *PRHunkAlloc(progfuncs_t *progfuncs, int size); +void *PRAddressableAlloc(progfuncs_t *progfuncs, int ammount); + +#ifdef printf +#undef LIKEPRINTF +#define LIKEPRINTF(x) +#endif + +//void *HunkAlloc (int size); +char *VARGS qcva (char *text, ...) LIKEPRINTF(1); +void QC_InitShares(progfuncs_t *progfuncs); +void QC_StartShares(progfuncs_t *progfuncs); +void QC_AddSharedVar(progfuncs_t *progfuncs, int num, int type); +void QC_AddSharedFieldVar(progfuncs_t *progfuncs, int num, char *stringtable); +int QC_RegisterFieldVar(progfuncs_t *progfuncs, unsigned int type, char *name, int requestedpos, int originalofs); +pbool Decompile(progfuncs_t *progfuncs, char *fname); +int PR_ToggleBreakpoint(progfuncs_t *progfuncs, char *filename, int linenum, int flag); +void StripExtension (char *path); + + +#define edvars(ed) (((edictrun_t*)ed)->fields) //pointer to the field vars, given an edict + + +void SetEndian(void); +extern short (*PRBigShort) (short l); +extern short (*PRLittleShort) (short l); +extern int (*PRBigLong) (int l); +extern int (*PRLittleLong) (int l); +extern float (*PRBigFloat) (float l); +extern float (*PRLittleFloat) (float l); + + + +/* +#ifndef COMPILER +typedef union eval_s +{ + string_t string; + float _float; + float vector[3]; + func_t function; + int _int; + int edict; + progsnum_t prog; //so it can easily be changed +} eval_t; +#endif +*/ + +typedef struct edictrun_s +{ + pbool isfree; + + float freetime; // realtime when the object was freed + unsigned int entnum; + pbool readonly; //causes error when QC tries writing to it. (quake's world entity) + void *fields; + +// other fields from progs come immediately after +} edictrun_t; + + +int Comp_Begin(progfuncs_t *progfuncs, int nump, char **parms); +int Comp_Continue(progfuncs_t *progfuncs); + +char *EvaluateDebugString(progfuncs_t *progfuncs, char *key); +char *SaveEnts(progfuncs_t *progfuncs, char *mem, int *size, int mode); +int LoadEnts(progfuncs_t *progfuncs, char *file, float killonspawnflags); +char *SaveEnt (progfuncs_t *progfuncs, char *buf, int *size, struct edict_s *ed); +struct edict_s *RestoreEnt (progfuncs_t *progfuncs, char *buf, int *size, struct edict_s *ed); +char *PF_VarString (int first); +void PR_StackTrace (progfuncs_t *progfuncs); + +extern int noextensions; + +#ifndef COMPILER +typedef struct progstate_s +{ + dprograms_t *progs; + dfunction_t *functions; + char *strings; + union { + ddefXX_t *globaldefs; + ddef16_t *globaldefs16; + ddef32_t *globaldefs32; + }; + union { + ddefXX_t *fielddefs; + ddef16_t *fielddefs16; + ddef32_t *fielddefs32; + }; + void *statements; +// void *global_struct; + float *globals; // same as pr_global_struct + + typeinfo_t *types; + + int edict_size; // in bytes + + char filename[128]; + + builtin_t *builtins; + int numbuiltins; + + int *linenums; //debug versions only + + int intsize; //16 for standard (more limiting) versions +} progstate_t; + +typedef struct extensionbuiltin_s { + char *name; + builtin_t func; + struct extensionbuiltin_s *prev; +} extensionbuiltin_t; + +//============================================================================ + + +#define pr_progs current_progstate->progs +#define pr_functions current_progstate->functions +#define pr_strings current_progstate->strings +#define pr_globaldefs16 ((ddef16_t*)current_progstate->globaldefs) +#define pr_globaldefs32 ((ddef32_t*)current_progstate->globaldefs) +#define pr_fielddefs16 ((ddef16_t*)current_progstate->fielddefs) +#define pr_fielddefs32 ((ddef32_t*)current_progstate->fielddefs) +#define pr_statements16 ((dstatement16_t*)current_progstate->statements) +#define pr_statements32 ((dstatement32_t*)current_progstate->statements) +//#define pr_global_struct current_progstate->global_struct +#define pr_globals current_progstate->globals +#define pr_linenums current_progstate->linenums +#define pr_types current_progstate->types + + + +//============================================================================ + +void PR_Init (void); + +void PR_ExecuteProgram (progfuncs_t *progfuncs, func_t fnum); +int PR_LoadProgs(progfuncs_t *progfncs, char *s, int headercrc, builtin_t *builtins, int numbuiltins); +int PR_ReallyLoadProgs (progfuncs_t *progfuncs, char *filename, int headercrc, progstate_t *progstate, pbool complain); + +void *PRHunkAlloc(progfuncs_t *progfuncs, int ammount); + +void PR_Profile_f (void); + +struct edict_s *ED_Alloc (progfuncs_t *progfuncs); +void ED_Free (progfuncs_t *progfuncs, struct edict_s *ed); + +char *ED_NewString (progfuncs_t *progfuncs, char *string, int minlength); +// returns a copy of the string allocated from the server's string heap + +void ED_Print (progfuncs_t *progfuncs, struct edict_s *ed); +//void ED_Write (FILE *f, edictrun_t *ed); +char *ED_ParseEdict (progfuncs_t *progfuncs, char *data, edictrun_t *ent); + +//void ED_WriteGlobals (FILE *f); +void ED_ParseGlobals (char *data); + +//void ED_LoadFromFile (char *data); + +//define EDICT_NUM(n) ((edict_t *)(sv.edicts+ (n)*pr_edict_size)) +//define NUM_FOR_EDICT(e) (((byte *)(e) - sv.edicts)/pr_edict_size) + +struct edict_s *EDICT_NUM(progfuncs_t *progfuncs, unsigned int n); +unsigned int NUM_FOR_EDICT(progfuncs_t *progfuncs, struct edict_s *e); + +//#define NEXT_EDICT(e) ((edictrun_t *)( (byte *)e + pr_edict_size)) + +#define EDICT_TO_PROG(pf, e) (((edictrun_t*)e)->entnum) +#define PROG_TO_EDICT(pf, e) ((struct edictrun_s *)prinst->edicttable[e]) + +//============================================================================ + +#define G_FLOAT(o) (pr_globals[o]) +#define G_FLOAT2(o) (pr_globals[OFS_PARM0 + o*3]) +#define G_INT(o) (*(int *)&pr_globals[o]) +#define G_EDICT(o) ((edict_t *)((qbyte *)sv_edicts+ *(int *)&pr_globals[o])) +#define G_EDICTNUM(o) NUM_FOR_EDICT(G_EDICT(o)) +#define G_VECTOR(o) (&pr_globals[o]) +#define G_STRING(o) (*(string_t *)&pr_globals[o]) +#define G_STRING2(o) ((char*)*(string_t *)&pr_globals[o]) +#define GQ_STRING(o) (*(QCC_string_t *)&pr_globals[o]) +#define GQ_STRING2(o) ((char*)*(QCC_string_t *)&pr_globals[o]) +#define G_FUNCTION(o) (*(func_t *)&pr_globals[o]) +#define G_PROG(o) (*(progsnum_t *)&pr_globals[o]) //simply so it's nice and easy to change... + +#define RETURN_EDICT(e) (((int *)pr_globals)[OFS_RETURN] = EDICT_TO_PROG(e)) + +#define E_FLOAT(e,o) (((float*)&e->v)[o]) +#define E_INT(e,o) (*(int *)&((float*)&e->v)[o]) +#define E_VECTOR(e,o) (&((float*)&e->v)[o]) +#define E_STRING(e,o) (*(string_t *)&((float*)(e+1))[o]) + +const extern unsigned int type_size[]; + + +extern unsigned short pr_crc; + +void VARGS PR_RunError (progfuncs_t *progfuncs, char *error, ...) LIKEPRINTF(2); + +void ED_PrintEdicts (progfuncs_t *progfuncs); +void ED_PrintNum (progfuncs_t *progfuncs, int ent); + + +pbool PR_SwitchProgs(progfuncs_t *progfuncs, progsnum_t type); +void PR_MoveParms(progfuncs_t *progfuncs, progsnum_t progs1, progsnum_t progs2); + + + + +eval_t *GetEdictFieldValue(progfuncs_t *progfuncs, struct edict_s *ed, char *name, evalc_t *cache); + +#endif + + + + +#ifndef COMPILER + +//this is windows - all files are written with this endian standard +//optimisation +//leave undefined if in doubt over os. +#ifdef _WIN32 +#define NOENDIAN +#endif + + +typedef struct { + int varofs; + int size; +} sharedvar_t; +typedef struct +{ + int s; + dfunction_t *f; + int progsnum; + int pushed; +} prstack_t; + + + +//pr_multi.c +void PR_SetBuiltins(int type); + +#define var(type, name) type name +#define vars(type, name, size) type name[size] + +typedef struct prinst_s { +#ifdef QCJIT + pbool usejit; +#endif + char **tempstrings; + int maxtempstrings; + int numtempstrings; + int numtempstringsstack; + + char **allocedstrings; + int maxallocedstrings; + int numallocedstrings; + +var(progstate_t *, pr_progstate); +#define pr_progstate prinst->pr_progstate + +var(progsnum_t, pr_typecurrent); +#define pr_typecurrent prinst->pr_typecurrent +var(unsigned int, maxprogs); +#define maxprogs prinst->maxprogs + +var(progstate_t *,current_progstate); +#define current_progstate prinst->current_progstate + +var(unsigned int, numshares); +#define numshares prinst->numshares +var(sharedvar_t *,shares); //shared globals, not including parms +#define shares prinst->shares +var(unsigned int, maxshares); +#define maxshares prinst->maxshares + +var(struct prmemb_s *, memblocks); +#define memb prinst->memblocks + +var(unsigned int, maxfields); +#define maxfields prinst->maxfields +var(unsigned int, numfields); +#define numfields prinst->numfields +var(fdef_t*, field); //biggest size +#define field prinst->field + +int reorganisefields; + + +//pr_exec.c +#define MAX_STACK_DEPTH 64 +vars(prstack_t, pr_stack, MAX_STACK_DEPTH); +#define pr_stack prinst->pr_stack +var(int, pr_depth); +#define pr_depth prinst->pr_depth +var(int, spushed); +#define pr_spushed prinst->spushed + +#define LOCALSTACK_SIZE 4096 +vars(int, localstack, LOCALSTACK_SIZE); +#define localstack prinst->localstack +var(int, localstack_used); +#define localstack_used prinst->localstack_used + +var(int, continuestatement); +var(int, exitdepth); + +var(int, pr_trace); +#define pr_trace prinst->pr_trace +var(dfunction_t *, pr_xfunction); +#define pr_xfunction prinst->pr_xfunction +var(int, pr_xstatement); +#define pr_xstatement prinst->pr_xstatement + +var(int, pr_argc); +#define pr_argc prinst->pr_argc + +//pr_edict.c + +var(unsigned int, maxedicts); +#define maxedicts prinst->maxedicts + +var(evalc_t, spawnflagscache); +#define spawnflagscache prinst->spawnflagscache + + + + +var(unsigned int, fields_size); // in bytes +#define fields_size prinst->fields_size +var(unsigned int, max_fields_size); +#define max_fields_size prinst->max_fields_size + + +//initlib.c +var(char *, addressablehunk); +#define addressablehunk prinst->addressablehunk +var(unsigned int, addressableused); +#define addressableused prinst->addressableused +var(unsigned int, addressablesize); +#define addressablesize prinst->addressablesize + + +//var(extensionbuiltin_t *, extensionbuiltin); +//#define extensionbuiltin prinst->extensionbuiltin + + struct edict_s **edicttable; +} prinst_t; +extern vec3_t vec3_origin; + +eval_t *PR_FindGlobal(progfuncs_t *prfuncs, char *globname, progsnum_t pnum); +ddef16_t *ED_FindTypeGlobalFromProgs16 (progfuncs_t *progfuncs, char *name, progsnum_t prnum, int type); +ddef32_t *ED_FindTypeGlobalFromProgs32 (progfuncs_t *progfuncs, char *name, progsnum_t prnum, int type); +ddef16_t *ED_FindGlobalFromProgs16 (progfuncs_t *progfuncs, char *name, progsnum_t prnum); +ddef32_t *ED_FindGlobalFromProgs32 (progfuncs_t *progfuncs, char *name, progsnum_t prnum); +fdef_t *ED_FindField (progfuncs_t *progfuncs, char *name); +fdef_t *ED_FieldAtOfs (progfuncs_t *progfuncs, unsigned int ofs); +dfunction_t *ED_FindFunction (progfuncs_t *progfuncs, char *name, progsnum_t *pnum, progsnum_t fromprogs); +func_t PR_FindFunc(progfuncs_t *progfncs, char *funcname, progsnum_t pnum); +void PR_Configure (progfuncs_t *progfncs, int addressable_size, int max_progs); +int PR_InitEnts(progfuncs_t *progfncs, int maxents); +char *PR_ValueString (progfuncs_t *progfuncs, etype_t type, eval_t *val); +void QC_ClearEdict (progfuncs_t *progfuncs, struct edict_s *ed); +void PRAddressableFlush(progfuncs_t *progfuncs, int totalammount); +void QC_FlushProgsOffsets(progfuncs_t *progfuncs); + +ddef16_t *ED_GlobalAtOfs16 (progfuncs_t *progfuncs, int ofs); +ddef16_t *ED_FindGlobal16 (progfuncs_t *progfuncs, char *name); +ddef32_t *ED_FindGlobal32 (progfuncs_t *progfuncs, char *name); +ddef32_t *ED_GlobalAtOfs32 (progfuncs_t *progfuncs, unsigned int ofs); + +string_t PR_StringToProgs (progfuncs_t *inst, char *str); +char *PR_StringToNative (progfuncs_t *inst, string_t str); + +void PR_FreeTemps (progfuncs_t *progfuncs, int depth); + +char *PR_GlobalString (progfuncs_t *progfuncs, int ofs); +char *PR_GlobalStringNoContents (progfuncs_t *progfuncs, int ofs); + +pbool CompileFile(progfuncs_t *progfuncs, char *filename); + +pbool PR_GenerateJit(progfuncs_t *progfuncs); +void PR_EnterJIT(progfuncs_t *progfuncs, int statement); + +char *QCC_COM_Parse (char *data); +extern char qcc_token[1024]; +#endif diff --git a/misc/mediasource/extra/fteqcc-src/progslib.h b/misc/mediasource/extra/fteqcc-src/progslib.h new file mode 100644 index 00000000..f3f4488a --- /dev/null +++ b/misc/mediasource/extra/fteqcc-src/progslib.h @@ -0,0 +1,321 @@ + +#ifndef PROGSLIB_H +#define PROGSLIB_H +/*#define true 1 +#define false 0 + +#define PITCH 0 +#define YAW 1 +#define ROLL 2 + +typedef char bool; +//typedef float vec3_t[3]; +typedef int progsnum_t; +typedef int func_t; +#ifndef COMPILER +typedef char *string_t; +#endif +//typedef struct globalvars_s globalvars_t; +//typedef struct edict_s edict_t; +#define globalvars_t void +#define edict_t void +*/ + +#ifdef _MSC_VER + #define VARGS __cdecl +#endif +#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) + #define LIKEPRINTF(x) __attribute__((format(printf,x,x+1))) +#endif +#ifndef LIKEPRINTF + #define LIKEPRINTF(x) +#endif +#ifndef VARGS + #define VARGS +#endif + + +struct edict_s; +struct entvars_s; +struct globalvars_s; +struct qcthread_s; +typedef struct progfuncs_s progfuncs_t; +typedef void (*builtin_t) (progfuncs_t *prinst, struct globalvars_s *gvars); + +//used by progs engine. All nulls is reset. +typedef struct { + char *varname; + struct fdef_s *ofs32; + + int spare[2]; +} evalc_t; +#define sizeofevalc sizeof(evalc_t) +typedef enum {ev_void, ev_string, ev_float, ev_vector, ev_entity, ev_field, ev_function, ev_pointer, ev_integer, ev_variant, ev_struct, ev_union} etype_t; + +//the number of pointers to variables (as opposed to functions - those are fine) in these structures is excessive. +//Many of the functions are also obsolete. +struct progfuncs_s { + int progsversion; //PROGSTRUCT_VERSION + + + void (*Configure) (progfuncs_t *prinst, int addressablesize, int max_progs); //configure buffers and memory. Used to reset and must be called first. Flushes a running VM. + progsnum_t (*LoadProgs) (progfuncs_t *prinst, char *s, int headercrc, builtin_t *builtins, int numbuiltins); //load a progs + int (*InitEnts) (progfuncs_t *prinst, int max_ents); //returns size of edicts for use with nextedict macro + void (*ExecuteProgram) (progfuncs_t *prinst, func_t fnum); //start execution + pbool (*SwitchProgs) (progfuncs_t *prinst, progsnum_t num); //switch to a different progs - this should be obsolete. + struct globalvars_s *(*globals) (progfuncs_t *prinst, progsnum_t num); //get the globals of a progs + struct entvars_s *(*entvars) (progfuncs_t *prinst, struct edict_s *ent); //return a pointer to the entvars of an ent. can be achieved via the edict_t structure instead, so obsolete. + + void (VARGS *RunError) (progfuncs_t *prinst, char *msg, ...) LIKEPRINTF(2); //builtins call this to say there was a problem + void (*PrintEdict) (progfuncs_t *prinst, struct edict_s *ed); //get a listing of all vars on an edict (sent back via 'print') + + struct edict_s *(*EntAlloc) (progfuncs_t *prinst); + void (*EntFree) (progfuncs_t *prinst, struct edict_s *ed); + + struct edict_s *(*EDICT_NUM) (progfuncs_t *prinst, unsigned int n); //get the nth edict + unsigned int (*NUM_FOR_EDICT) (progfuncs_t *prinst, struct edict_s *e); //so you can find out what that 'n' will be + + void (*SetGlobalEdict) (progfuncs_t *prinst, struct edict_s *ed, int ofs); //set a global to an edict (partially obsolete) + + char *(*VarString) (progfuncs_t *prinst, int first); //returns a string made up of multiple arguments + + struct progstate_s **progstate; //internal to the library. + + func_t (*FindFunction) (progfuncs_t *prinst, char *funcname, progsnum_t num); + + int (*StartCompile) (progfuncs_t *prinst, int argv, char **argc); //1 if can compile, 0 if failed to compile + int (*ContinueCompile) (progfuncs_t *prinst); //2 if finished, 1 if more to go, 0 if failed + + char *(*filefromprogs) (progfuncs_t *prinst, progsnum_t prnum, char *fname, int *size, char *buffer); //reveals encoded/added files from already loaded progs + char *(*filefromnewprogs) (progfuncs_t *prinst, char *prname, char *fname, int *size, char *buffer); //reveals encoded/added files from a progs on the disk somewhere + + char *(*save_ents) (progfuncs_t *prinst, char *buf, int *size, int mode); //dump the entire progs info into one big self allocated string + int (*load_ents) (progfuncs_t *prinst, char *s, float killonspawnflags); //restore the entire progs state (or just add some more ents) (returns edicts ize) + + char *(*saveent) (progfuncs_t *prinst, char *buf, int *size, struct edict_s *ed); //will save just one entities vars + struct edict_s *(*restoreent) (progfuncs_t *prinst, char *buf, int *size, struct edict_s *ed); //will restore the entity that had it's values saved (can use NULL for ed) + + union eval_s *(*FindGlobal) (progfuncs_t *prinst, char *name, progsnum_t num); //find a pointer to the globals value + char *(*AddString) (progfuncs_t *prinst, char *val, int minlength); //dump a string into the progs memory (for setting globals and whatnot) + void *(*Tempmem) (progfuncs_t *prinst, int ammount, char *whatfor); //grab some mem for as long as the progs stays loaded + + union eval_s *(*GetEdictFieldValue) (progfuncs_t *prinst, struct edict_s *ent, char *name, evalc_t *s); //get an entityvar (cache it) and return the possible values + struct edict_s *(*ProgsToEdict) (progfuncs_t *prinst, int progs); //edicts are stored as ints and need to be adjusted + int (*EdictToProgs) (progfuncs_t *prinst, struct edict_s *ed); //edicts are stored as ints and need to be adjusted + + char *(*EvaluateDebugString) (progfuncs_t *prinst, char *key); //evaluate a string and return it's value (according to current progs) (expands edict vars) + + int *pr_trace; //start calling the editor for each line executed + + void (*StackTrace) (progfuncs_t *prinst); + + int (*ToggleBreak) (progfuncs_t *prinst, char *filename, int linenum, int mode); + + int numprogs; + + struct progexterns_s *parms; //these are the initial parms, they may be changed + + pbool (*Decompile) (progfuncs_t *prinst, char *fname); + + + struct prinst_s *prinst; //internal variables. Leave alone. + + int *callargc; //number of args of built-in call + void (*RegisterBuiltin) (progfuncs_t *prinst, char *, builtin_t); + + char *stringtable; //qc strings are all relative. add to a qc string. this is required for support of frikqcc progs that strip string immediates. + int fieldadjust; //FrikQCC style arrays can cause problems due to field remapping. This causes us to leave gaps but offsets identical. + + struct qcthread_s *(*Fork) (progfuncs_t *prinst); //returns a pointer to a thread which can be resumed via RunThread. + void (*RunThread) (progfuncs_t *prinst, struct qcthread_s *thread); + void (*AbortStack) (progfuncs_t *prinst); //annigilates the current stack, positioning on a return statement. It is expected that this is only used via a builtin! + + int lastcalledbuiltinnumber; //useful with non-implemented opcodes. + + int (*RegisterFieldVar) (progfuncs_t *prinst, unsigned int type, char *name, int requestedpos, int originalofs); + + char *tempstringbase; //for engine's use. Store your base tempstring pointer here. + int tempstringnum; //for engine's use. + + string_t (*TempString) (progfuncs_t *prinst, char *str); + + string_t (*StringToProgs) (progfuncs_t *prinst, char *str); + char *(*StringToNative) (progfuncs_t *prinst, string_t str); + int stringtablesize; + + int (*QueryField) (progfuncs_t *prinst, unsigned int fieldoffset, etype_t *type, char **name, evalc_t *fieldcache); //find info on a field definition at an offset + + void (*EntClear) (progfuncs_t *progfuncs, struct edict_s *e); +}; + +typedef struct progexterns_s { + int progsversion; //PROGSTRUCT_VERSION + + unsigned char *(*ReadFile) (char *fname, void *buffer, int len); + int (*FileSize) (char *fname); //-1 if file does not exist + pbool (*WriteFile) (char *name, void *data, int len); + int (VARGS *printf) (const char *, ...) LIKEPRINTF(1); + void (VARGS *Sys_Error) (const char *, ...) LIKEPRINTF(1); + void (VARGS *Abort) (char *, ...) LIKEPRINTF(1); + int edictsize; //size of edict_t + + void (*entspawn) (struct edict_s *ent, int loading); //ent has been spawned, but may not have all the extra variables (that may need to be set) set + pbool (*entcanfree) (struct edict_s *ent); //return true to stop ent from being freed + void (*stateop) (progfuncs_t *prinst, float var, func_t func); //what to do on qc's state opcode. + void (*cstateop) (progfuncs_t *prinst, float vara, float varb, func_t currentfunc); //a hexen2 opcode. + void (*cwstateop) (progfuncs_t *prinst, float vara, float varb, func_t currentfunc); //a hexen2 opcode. + void (*thinktimeop) (progfuncs_t *prinst, struct edict_s *ent, float varb); //a hexen2 opcode. + + + //used when loading a game + builtin_t *(*builtinsfor) (int num, int headercrc); //must return a pointer to the builtins that were used before the state was saved. + void (*loadcompleate) (int edictsize); //notification to reset any pointers. + + void *(VARGS *memalloc) (int size); //small string allocation malloced and freed randomly by the executor. (use malloc if you want) + void (VARGS *memfree) (void * mem); + + + builtin_t *globalbuiltins; //these are available to all progs + int numglobalbuiltins; + + enum {PR_NOCOMPILE, PR_COMPILENEXIST, PR_COMPILEEXISTANDCHANGED, PR_COMPILECHANGED, PR_COMPILEALWAYS, PR_COMPILEIGNORE} autocompile; + + double *gametime; //used to prevent the vm from reusing an entity faster than 2 secs. + + struct edict_s **sv_edicts; //pointer to the engine's reference to world. + unsigned int *sv_num_edicts; //pointer to the engine's edict count. + + int (*useeditor) (progfuncs_t *prinst, char *filename, int line, int nump, char **parms); //called on syntax errors or step-by-step debugging. +} progparms_t, progexterns_t; + +//FIXMEs +void QC_AddSharedVar(progfuncs_t *progfuncs, int start, int size); +void QC_AddSharedFieldVar(progfuncs_t *progfuncs, int num, char *relstringtable); +void ED_Print(progfuncs_t *progfuncs, struct edict_s *ed); +char *PR_RemoveProgsString(progfuncs_t *progfuncs, string_t str); +int PR_GetFuncArgCount(progfuncs_t *progfuncs, func_t func); + +#if defined(QCLIBDLL_EXPORTS) +__declspec(dllexport) +#endif +progfuncs_t * InitProgs(progparms_t *ext); +#if defined(QCLIBDLL_EXPORTS) +__declspec(dllexport) +#endif +void CloseProgs(progfuncs_t *inst); + +#ifndef COMPILER +typedef union eval_s +{ + string_t string; + float _float; + float _vector[3]; + func_t function; + int _int; + int edict; + progsnum_t prog; //so it can easily be changed +} eval_t; +#endif + +#define PR_CURRENT -1 +#define PR_ANY -2 //not always valid. Use for finding funcs +#define PR_ANYBACK -3 +#define PROGSTRUCT_VERSION 2 + + +#ifndef DLL_PROG +#define PR_Configure(pf, memsize, max_progs) (*pf->Configure) (pf, memsize, max_progs) +#define PR_LoadProgs(pf, s, headercrc, builtins, numb) (*pf->LoadProgs) (pf, s, headercrc, builtins, numb) +#define PR_InitEnts(pf, maxents) (*pf->InitEnts) (pf, maxents) +#define PR_ExecuteProgram(pf, fnum) (*pf->ExecuteProgram) (pf, fnum) +#define PR_SwitchProgs(pf, num) (*pf->SwitchProgs) (pf, num) +#define PR_globals(pf, num) (*pf->globals) (pf, num) +#define PR_entvars(pf, ent) (*pf->entvars) (pf, ent) + +#define PR_RegisterFieldVar(pf,type,name,reqofs,qcofs) (*pf->RegisterFieldVar) (pf,type,name,reqofs,qcofs) + +#define ED_Alloc(pf) (*pf->EntAlloc) (pf) +#define ED_Free(pf, ed) (*pf->EntFree) (pf, ed) +#define ED_Clear(pf, ed) (*pf->EntClear) (pf, ed) + +#define PR_LoadEnts(pf, s, kf) (*pf->load_ents) (pf, s, kf) +#define PR_SaveEnts(pf, buf, size, mode) (*pf->save_ents) (pf, buf, size, mode) + +#define EDICT_NUM(pf, num) (*pf->EDICT_NUM) (pf, num) +#define NUM_FOR_EDICT(pf, e) (*pf->NUM_FOR_EDICT) (pf, e) +#define SetGlobalEdict(pf, ed, ofs) (*pf->SetGlobalEdict) (pf, ed, ofs) +#define PR_VarString(pf,first) (*pf->VarString) (pf,first) + +#define PR_StartCompile(pf,argc,argv) (*pf->StartCompile) (pf,argc,argv) +#define PR_ContinueCompile(pf) (*pf->ContinueCompile) (pf) + +#define PR_StackTrace(pf) (*pf->StackTrace) (pf) +#define PR_AbortStack(pf) (*pf->AbortStack) (pf) + +#define PR_RunError(pf,str) (*pf->RunError) (pf,str) + +#define PR_PrintEdict(pf,ed) (*pf->PrintEdict) (pf, ed) + +#define PR_FindFunction(pf, name, num) (*pf->FindFunction) (pf, name, num) +#define PR_FindGlobal(pf, name, progs) (*pf->FindGlobal) (pf, name, progs) +#define PR_AddString(pf, ed, len) (*pf->AddString) (pf, ed, len) +#define PR_Alloc(pf,size) (*pf->Tempmem) (pf, size) + +#define PROG_TO_EDICT(pf, ed) (*pf->ProgsToEdict) (pf, ed) +#define EDICT_TO_PROG(pf, ed) (*pf->EdictToProgs) (pf, (struct edict_s*)ed) + +#define PR_RegisterBuiltin(pf, name, func) (*pf->RegisterBuiltin) (pf, name, func) + +#define PR_GetString(pf,s) (*pf->StringToNative) (pf, s) +#define PR_GetStringOfs(pf,o) (*pf->StringToNative) (pf, G_INT(o)) +#define PR_SetString(pf, s) (*pf->StringToProgs) (pf, s) + +#define NEXT_EDICT(pf,o) EDICT_NUM(pf, NUM_FOR_EDICT(pf, o)+1) +#define RETURN_EDICT(pf, e) (((int *)pr_globals)[OFS_RETURN] = EDICT_TO_PROG(pf, e)) + + +//builtin funcs (which operate on globals) +//To use these outside of builtins, you will likly have to use the 'globals' method. +#define G_FLOAT(o) (((float *)pr_globals)[o]) +#define G_FLOAT2(o) (((float *)pr_globals)[OFS_PARM0 + o*3]) +#define G_INT(o) (((int *)pr_globals)[o]) +#define G_EDICT(pf, o) PROG_TO_EDICT(pf, G_INT(o)) //((edict_t *)((char *) sv.edicts+ *(int *)&((float *)pr_globals)[o])) +#define G_EDICTNUM(pf, o) NUM_FOR_EDICT(pf, G_EDICT(pf, o)) +#define G_VECTOR(o) (&((float *)pr_globals)[o]) +#define G_FUNCTION(o) (*(func_t *)&((float *)pr_globals)[o]) + +/* +#define PR_GetString(p,s) (s?s + p->stringtable:"") +#define PR_GetStringOfs(p,o) (G_INT(o)?G_INT(o) + p->stringtable:"") +#define PR_SetStringOfs(p,o,s) (G_INT(o) = s - p->stringtable) +*/ +//#define PR_SetString(p, s) ((s&&*s)?(s - p->stringtable):0) +#define PR_NewString(p, s, l) PR_SetString(p, PR_AddString(p, s, l)) +/**/ + +#define ev_prog ev_integer + +#define E_STRING(o) (char *)(((int *)((char *)ed) + progparms.edictsize)[o]) + +//#define pr_global_struct pr_globals + +#endif + + +#define OFS_NULL 0 +#define OFS_RETURN 1 +#define OFS_PARM0 4 // leave 3 ofs for each parm to hold vectors +#define OFS_PARM1 7 +#define OFS_PARM2 10 +#define OFS_PARM3 13 +#define OFS_PARM4 16 +#define OFS_PARM5 19 +#define OFS_PARM6 22 +#define OFS_PARM7 25 +#define RESERVED_OFS 28 + + +#undef edict_t +#undef globalvars_t + +#endif //PROGSLIB_H diff --git a/misc/mediasource/extra/fteqcc-src/progtype.h b/misc/mediasource/extra/fteqcc-src/progtype.h new file mode 100644 index 00000000..ab120154 --- /dev/null +++ b/misc/mediasource/extra/fteqcc-src/progtype.h @@ -0,0 +1,23 @@ +#ifndef QCLIB_PROGTYPE_H +#define QCLIB_PROGTYPE_H + +#ifndef DLL_PROG + +#else +typedef float vec_t; +typedef vec_t vec3_t[3]; +#endif + +#ifndef t_bool +#define t_bool +typedef int pbool; + +#else +#define t_bool +#endif +typedef int progsnum_t; +typedef int func_t; +typedef int string_t; + +#endif /* QCLIB_PROGTYPE_H */ + diff --git a/misc/mediasource/extra/fteqcc-src/qcc.dsp b/misc/mediasource/extra/fteqcc-src/qcc.dsp new file mode 100644 index 00000000..f5af89c8 --- /dev/null +++ b/misc/mediasource/extra/fteqcc-src/qcc.dsp @@ -0,0 +1,256 @@ +# Microsoft Developer Studio Project File - Name="qcc" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=qcc - Win32 GUIDebug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "qcc.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "qcc.mak" CFG="qcc - Win32 GUIDebug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "qcc - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "qcc - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE "qcc - Win32 GUIDebug" (based on "Win32 (x86) Console Application") +!MESSAGE "qcc - Win32 GUIRelease" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "qcc - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /D "QCCONLY" /YX /FD /c +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x809 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 ../libs/zlib.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"../../fteqcc.exe" + +!ELSEIF "$(CFG)" == "qcc - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "QCCONLY" /FR /YX /FD /GZ /c +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x809 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 ..\libs\zlib.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"../../fteqcc_dbg.exe" /pdbtype:sept + +!ELSEIF "$(CFG)" == "qcc - Win32 GUIDebug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "qcc___Win32_GUIDebug" +# PROP BASE Intermediate_Dir "qcc___Win32_GUIDebug" +# PROP BASE Ignore_Export_Lib 0 +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "qcc___Win32_GUIDebug" +# PROP Intermediate_Dir "qcc___Win32_GUIDebug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "QCCONLY" /FR /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "QCCONLY" /FR /YX /FD /GZ /c +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x809 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 ..\libs\zlib.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"../../../fteqcc.exe" /pdbtype:sept +# ADD LINK32 ..\libs\zlib.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib comctl32.lib comdlg32.lib /nologo /subsystem:windows /debug /machine:I386 /out:"..\..\fteqcc.exe" /pdbtype:sept +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "qcc - Win32 GUIRelease" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "qcc___Win32_GUIRelease0" +# PROP BASE Intermediate_Dir "qcc___Win32_GUIRelease0" +# PROP BASE Ignore_Export_Lib 0 +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "qcc___Win32_GUIRelease0" +# PROP Intermediate_Dir "qcc___Win32_GUIRelease0" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /D "QCCONLY" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /D "QCCONLY" /FR /YX /FD /c +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x809 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 ../libs/zlib.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"../../../fteqcc.exe" +# ADD LINK32 ../libs/zlib.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib comctl32.lib /nologo /subsystem:windows /machine:I386 /out:"../../fteqccgui.exe" +# SUBTRACT LINK32 /pdb:none + +!ENDIF + +# Begin Target + +# Name "qcc - Win32 Release" +# Name "qcc - Win32 Debug" +# Name "qcc - Win32 GUIDebug" +# Name "qcc - Win32 GUIRelease" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\Comprout.c +# End Source File +# Begin Source File + +SOURCE=.\hash.c +# End Source File +# Begin Source File + +SOURCE=.\qcc_cmdlib.c +# End Source File +# Begin Source File + +SOURCE=.\qcc_gtk.c +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\qcc_pr_comp.c +# End Source File +# Begin Source File + +SOURCE=.\qcc_pr_lex.c +# End Source File +# Begin Source File + +SOURCE=.\qccgui.c + +!IF "$(CFG)" == "qcc - Win32 Release" + +# PROP Exclude_From_Build 1 + +!ELSEIF "$(CFG)" == "qcc - Win32 Debug" + +# PROP Exclude_From_Build 1 + +!ELSEIF "$(CFG)" == "qcc - Win32 GUIDebug" + +!ELSEIF "$(CFG)" == "qcc - Win32 GUIRelease" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\qccguistuff.c + +!IF "$(CFG)" == "qcc - Win32 Release" + +# PROP Exclude_From_Build 1 + +!ELSEIF "$(CFG)" == "qcc - Win32 Debug" + +# PROP Exclude_From_Build 1 + +!ELSEIF "$(CFG)" == "qcc - Win32 GUIDebug" + +!ELSEIF "$(CFG)" == "qcc - Win32 GUIRelease" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\QccMain.c +# End Source File +# Begin Source File + +SOURCE=.\qcctui.c + +!IF "$(CFG)" == "qcc - Win32 Release" + +!ELSEIF "$(CFG)" == "qcc - Win32 Debug" + +!ELSEIF "$(CFG)" == "qcc - Win32 GUIDebug" + +# PROP Exclude_From_Build 1 + +!ELSEIF "$(CFG)" == "qcc - Win32 GUIRelease" + +# PROP Exclude_From_Build 1 + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\qcd_main.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\gui.h +# End Source File +# Begin Source File + +SOURCE=.\pr_comp.h +# End Source File +# Begin Source File + +SOURCE=.\qcc.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/misc/mediasource/extra/fteqcc-src/qcc.h b/misc/mediasource/extra/fteqcc-src/qcc.h new file mode 100644 index 00000000..e71ad165 --- /dev/null +++ b/misc/mediasource/extra/fteqcc-src/qcc.h @@ -0,0 +1,906 @@ +#define COMPILER +#define PROGSUSED + +//#define COMMONINLINES +//#define inline _inline + +#include "cmdlib.h" +#include +/* +#include +#include + + +#include "pr_comp.h" +*/ + +//this is for testing +#define WRITEASM + +#ifdef __MINGW32_VERSION +#define MINGW +#endif + +#define progfuncs qccprogfuncs +extern progfuncs_t *qccprogfuncs; + +#ifndef _WIN32 +#define stricmp strcasecmp +#define strnicmp strncasecmp +#endif + +void *qccHunkAlloc(size_t mem); +void qccClearHunk(void); + +extern short (*PRBigShort) (short l); +extern short (*PRLittleShort) (short l); +extern int (*PRBigLong) (int l); +extern int (*PRLittleLong) (int l); +extern float (*PRBigFloat) (float l); +extern float (*PRLittleFloat) (float l); + + +#define MAX_ERRORS 10 + +#define MAX_NAME 64 // chars long + +extern unsigned int MAX_REGS; + +extern int MAX_STRINGS; +extern int MAX_GLOBALS; +extern int MAX_FIELDS; +extern int MAX_STATEMENTS; +extern int MAX_FUNCTIONS; + +#define MAX_SOUNDS 1024 //convert to int? +#define MAX_TEXTURES 1024 //convert to int? +#define MAX_MODELS 1024 //convert to int? +#define MAX_FILES 1024 //convert to int? +#define MAX_DATA_PATH 64 + +extern int MAX_CONSTANTS; +#define MAXCONSTANTLENGTH 64 +#define MAXCONSTANTVALUELENGTH 1024 +#define MAXCONSTANTPARAMLENGTH 32 +#define MAXCONSTANTPARAMS 32 + +typedef enum {QCF_STANDARD, QCF_HEXEN2, QCF_DARKPLACES, QCF_FTE, QCF_FTEDEBUG, QCF_KK7} qcc_targetformat_t; +extern qcc_targetformat_t qcc_targetformat; + + +/* + +TODO: + +"stopped at 10 errors" + +other pointer types for models and clients? + +compact string heap? + +always initialize all variables to something safe + +the def->type->type arrangement is really silly. + +return type checking + +parm count type checking + +immediate overflow checking + +pass the first two parms in call->b and call->c + +*/ + +/* + +comments +-------- +// comments discard text until the end of line +/ * * / comments discard all enclosed text (spaced out on this line because this documentation is in a regular C comment block, and typing them in normally causes a parse error) + +code structure +-------------- +A definition is: + [ = ] {, [ = ] }; + + +types +----- +simple types: void, float, vector, string, or entity + float width, height; + string name; + entity self, other; + +vector types: + vector org; // also creates org_x, org_y, and org_z float defs + + +A function type is specified as: simpletype ( type name {,type name} ) +The names are ignored except when the function is initialized. + void() think; + entity() FindTarget; + void(vector destination, float speed, void() callback) SUB_CalcMove; + void(...) dprint; // variable argument builtin + +A field type is specified as: .type + .vector origin; + .string netname; + .void() think, touch, use; + + +names +----- +Names are a maximum of 64 characters, must begin with A-Z,a-z, or _, and can continue with those characters or 0-9. + +There are two levels of scoping: global, and function. The parameter list of a function and any vars declared inside a function with the "local" statement are only visible within that function, + + +immediates +---------- +Float immediates must begin with 0-9 or minus sign. .5 is illegal. + +A parsing ambiguity is present with negative constants. "a-5" will be parsed as "a", then "-5", causing an error. Seperate the - from the digits with a space "a - 5" to get the proper behavior. + 12 + 1.6 + 0.5 + -100 + +Vector immediates are three float immediates enclosed in single quotes. + '0 0 0' + '20.5 -10 0.00001' + +String immediates are characters enclosed in double quotes. The string cannot contain explicit newlines, but the escape character \n can embed one. The \" escape can be used to include a quote in the string. + "maps/jrwiz1.bsp" + "sound/nin/pain.wav" + "ouch!\n" + +Code immediates are statements enclosed in {} braces. +statement: + { } + ; + local [ = ] {, [ = ] }; + return ; + if ( ) [ else ]; + while ( ) ; + do while ( ); + ( ); + +expression: + combiations of names and these operators with standard C precedence: + "&&", "||", "<=", ">=","==", "!=", "!", "*", "/", "-", "+", "=", ".", "<", ">", "&", "|" + Parenthesis can be used to alter order of operation. + The & and | operations perform integral bit ops on floats + +A built in function immediate is a number sign followed by an integer. + #1 + #12 + + +compilation +----------- +Source files are processed sequentially without dumping any state, so if a defs file is the first one processed, the definitions will be available to all other files. + +The language is strongly typed and there are no casts. + +Anything that is initialized is assumed to be constant, and will have immediates folded into it. If you change the value, your program will malfunction. All uninitialized globals will be saved to savegame files. + +Functions cannot have more than eight parameters. + +Error recovery during compilation is minimal. It will skip to the next global definition, so you will never see more than one error at a time in a given function. All compilation aborts after ten error messages. + +Names can be defined multiple times until they are defined with an initialization, allowing functions to be prototyped before their definition. + +void() MyFunction; // the prototype + +void() MyFunction = // the initialization +{ + dprint ("we're here\n"); +}; + + +entities and fields +------------------- + + +execution +--------- +Code execution is initiated by C code in quake from two main places: the timed think routines for periodic control, and the touch function when two objects impact each other. + +There are three global variables that are set before beginning code execution: + entity world; // the server's world object, which holds all global + // state for the server, like the deathmatch flags + // and the body ques. + entity self; // the entity the function is executing for + entity other; // the other object in an impact, not used for thinks + float time; // the current game time. Note that because the + // entities in the world are simulated sequentially, + // time is NOT strictly increasing. An impact late + // in one entity's time slice may set time higher + // than the think function of the next entity. + // The difference is limited to 0.1 seconds. +Execution is also caused by a few uncommon events, like the addition of a new client to an existing server. + +There is a runnaway counter that stops a program if 100000 statements are executed, assuming it is in an infinite loop. + +It is acceptable to change the system set global variables. This is usually done to pose as another entity by changing self and calling a function. + +The interpretation is fairly efficient, but it is still over an order of magnitude slower than compiled C code. All time consuming operations should be made into built in functions. + +A profile counter is kept for each function, and incremented for each interpreted instruction inside that function. The "profile" console command in Quake will dump out the top 10 functions, then clear all the counters. The "profile all" command will dump sorted stats for every function that has been executed. + + +afunc ( 4, bfunc(1,2,3)); +will fail because there is a shared parameter marshaling area, which will cause the 1 from bfunc to overwrite the 4 already placed in parm0. When a function is called, it copies the parms from the globals into it's privately scoped variables, so there is no collision when calling another function. + +total = factorial(3) + factorial(4); +Will fail because the return value from functions is held in a single global area. If this really gets on your nerves, tell me and I can work around it at a slight performance and space penalty by allocating a new register for the function call and copying it out. + + +built in functions +------------------ +void(string text) dprint; +Prints the string to the server console. + +void(entity client, string text) cprint; +Prints a message to a specific client. + +void(string text) bprint; +Broadcast prints a message to all clients on the current server. + +entity() spawn; +Returns a totally empty entity. You can manually set everything up, or just set the origin and call one of the existing entity setup functions. + +entity(entity start, .string field, string match) find; +Searches the server entity list beginning at start, looking for an entity that has entity.field = match. To start at the beginning of the list, pass world. World is returned when the end of the list is reached. + + + + +gotchas +------- + +The && and || operators DO NOT EARLY OUT like C! + +Don't confuse single quoted vectors with double quoted strings + +The function declaration syntax takes a little getting used to. + +Don't forget the ; after the trailing brace of a function initialization. + +Don't forget the "local" before defining local variables. + +There are no ++ / -- operators, or operate/assign operators. + +*/ + + +#if 1 +#include "hash.h" +extern hashtable_t compconstantstable; +extern hashtable_t globalstable, localstable; +#endif + +#ifdef WRITEASM +FILE *asmfile; +#endif +//============================================================================= + +// offsets are always multiplied by 4 before using +typedef unsigned int gofs_t; // offset in global data block +typedef struct QCC_function_s QCC_function_t; + +#define MAX_PARMS 8 + +typedef struct QCC_type_s +{ + etype_t type; + + struct QCC_type_s *parentclass; //type_entity... + struct QCC_type_s *next; +// function types are more complex + struct QCC_type_s *aux_type; // return type or field type + struct QCC_type_s *param; + int num_parms; // -1 = variable args +// struct QCC_type_s *parm_types[MAX_PARMS]; // only [num_parms] allocated + + unsigned int ofs; //inside a structure. + unsigned int size; + char *name; +} QCC_type_t; +int typecmp(QCC_type_t *a, QCC_type_t *b); + +typedef struct temp_s { + gofs_t ofs; + struct QCC_def_s *scope; +#ifdef WRITEASM + struct QCC_def_s *lastfunc; +#endif + struct temp_s *next; + pbool used; + unsigned int size; +} temp_t; + +//not written +typedef struct QCC_def_s +{ + QCC_type_t *type; + char *name; + struct QCC_def_s *next; + struct QCC_def_s *nextlocal; //provides a chain of local variables for the opt_locals_marshalling optimisation. + gofs_t ofs; + struct QCC_def_s *scope; // function the var was defined in, or NULL + int initialized; // 1 when a declaration included "= immediate" + int constant; // 1 says we can use the value over and over again + + int references; + int timescalled; //part of the opt_stripfunctions optimisation. + + int s_file; + int s_line; + + int arraysize; + pbool shared; + pbool saved; + pbool isstatic; + + temp_t *temp; +} QCC_def_t; + +//============================================================================ + +// pr_loc.h -- program local defs + + +//============================================================================= +extern char QCC_copyright[1024]; +extern char QCC_Packname[5][128]; +extern int QCC_packid; + +typedef union QCC_eval_s +{ + QCC_string_t string; + float _float; + float vector[3]; + func_t function; + int _int; + union QCC_eval_s *ptr; +} QCC_eval_t; + +const extern unsigned int type_size[]; +//extern QCC_def_t *def_for_type[9]; + +extern QCC_type_t *type_void, *type_string, *type_float, *type_vector, *type_entity, *type_field, *type_function, *type_pointer, *type_integer, *type_variant, *type_floatfield; + +struct QCC_function_s +{ + int builtin; // if non 0, call an internal function + int code; // first statement + char *file; // source file with definition + int file_line; + struct QCC_def_s *def; + unsigned int parm_ofs[MAX_PARMS]; // always contiguous, right? +}; + + +// +// output generated by prog parsing +// +typedef struct +{ + char *memory; + int max_memory; + int current_memory; + QCC_type_t *types; + + QCC_def_t def_head; // unused head of linked list + QCC_def_t *def_tail; // add new defs after this and move it + QCC_def_t *localvars; // chain of variables which need to be pushed and stuff. + + int size_fields; +} QCC_pr_info_t; + +extern QCC_pr_info_t pr; + + +typedef struct +{ + char name[MAXCONSTANTLENGTH]; + char value[MAXCONSTANTVALUELENGTH]; + char params[MAXCONSTANTPARAMS][MAXCONSTANTPARAMLENGTH]; + int numparams; + pbool used; + pbool inside; + + int namelen; +} CompilerConstant_t; +extern CompilerConstant_t *CompilerConstant; + +//============================================================================ + +extern pbool pr_dumpasm; + +//extern QCC_def_t **pr_global_defs; // to find def for a global variable + +typedef enum { +tt_eof, // end of file reached +tt_name, // an alphanumeric name token +tt_punct, // code punctuation +tt_immediate, // string, float, vector +} token_type_t; + +extern char pr_token[8192]; +extern token_type_t pr_token_type; +extern QCC_type_t *pr_immediate_type; +extern QCC_eval_t pr_immediate; + +extern pbool keyword_asm; +extern pbool keyword_break; +extern pbool keyword_case; +extern pbool keyword_class; +extern pbool keyword_const; +extern pbool keyword_continue; +extern pbool keyword_default; +extern pbool keyword_do; +extern pbool keyword_entity; +extern pbool keyword_float; +extern pbool keyword_for; +extern pbool keyword_goto; +extern pbool keyword_int; +extern pbool keyword_integer; +extern pbool keyword_state; +extern pbool keyword_string; +extern pbool keyword_struct; +extern pbool keyword_switch; +extern pbool keyword_thinktime; +extern pbool keyword_var; +extern pbool keyword_vector; +extern pbool keyword_union; +extern pbool keyword_enum; //kinda like in c, but typedef not supported. +extern pbool keyword_enumflags; //like enum, but doubles instead of adds 1. +extern pbool keyword_typedef; //fixme +extern pbool keyword_extern; //function is external, don't error or warn if the body was not found +extern pbool keyword_shared; //mark global to be copied over when progs changes (part of FTE_MULTIPROGS) +extern pbool keyword_noref; //nowhere else references this, don't strip it. +extern pbool keyword_nosave; //don't write the def to the output. +extern pbool keyword_union; //you surly know what a union is! + + +extern pbool keywords_coexist; +extern pbool output_parms; +extern pbool autoprototype; +extern pbool pr_subscopedlocals; +extern pbool flag_ifstring; +extern pbool flag_iffloat; +extern pbool flag_acc; +extern pbool flag_caseinsensative; +extern pbool flag_laxcasts; +extern pbool flag_hashonly; +extern pbool flag_fasttrackarrays; +extern pbool flag_assume_integer; +extern pbool flag_msvcstyle; + +extern pbool opt_overlaptemps; +extern pbool opt_shortenifnots; +extern pbool opt_noduplicatestrings; +extern pbool opt_constantarithmatic; +extern pbool opt_nonvec_parms; +extern pbool opt_constant_names; +extern pbool opt_precache_file; +extern pbool opt_filenames; +extern pbool opt_assignments; +extern pbool opt_unreferenced; +extern pbool opt_function_names; +extern pbool opt_locals; +extern pbool opt_dupconstdefs; +extern pbool opt_constant_names_strings; +extern pbool opt_return_only; +extern pbool opt_compound_jumps; +//extern pbool opt_comexprremoval; +extern pbool opt_stripfunctions; +extern pbool opt_locals_marshalling; +extern pbool opt_logicops; +extern pbool opt_vectorcalls; + +extern int optres_shortenifnots; +extern int optres_overlaptemps; +extern int optres_noduplicatestrings; +extern int optres_constantarithmatic; +extern int optres_nonvec_parms; +extern int optres_constant_names; +extern int optres_precache_file; +extern int optres_filenames; +extern int optres_assignments; +extern int optres_unreferenced; +extern int optres_function_names; +extern int optres_locals; +extern int optres_dupconstdefs; +extern int optres_constant_names_strings; +extern int optres_return_only; +extern int optres_compound_jumps; +//extern int optres_comexprremoval; +extern int optres_stripfunctions; +extern int optres_locals_marshalling; +extern int optres_logicops; + +pbool CompileParams(progfuncs_t *progfuncs, int doall, int nump, char **parms); + +void QCC_PR_PrintStatement (QCC_dstatement_t *s); + +void QCC_PR_Lex (void); +// reads the next token into pr_token and classifies its type + +QCC_type_t *QCC_PR_NewType (char *name, int basictype); +QCC_type_t *QCC_PR_ParseType (int newtype); extern pbool type_inlinefunction; +QCC_type_t *QCC_TypeForName(char *name); +QCC_type_t *QCC_PR_ParseFunctionType (int newtype, QCC_type_t *returntype); +QCC_type_t *QCC_PR_ParseFunctionTypeReacc (int newtype, QCC_type_t *returntype); +char *QCC_PR_ParseName (void); +CompilerConstant_t *QCC_PR_DefineName(char *name); + +void QCC_RemapOffsets(unsigned int firststatement, unsigned int laststatement, unsigned int min, unsigned int max, unsigned int newmin); + +#ifndef COMMONINLINES +pbool QCC_PR_CheckToken (char *string); +pbool QCC_PR_CheckName (char *string); +void QCC_PR_Expect (char *string); +pbool QCC_PR_CheckKeyword(int keywordenabled, char *string); +#endif +void VARGS QCC_PR_ParseError (int errortype, char *error, ...); +void VARGS QCC_PR_ParseWarning (int warningtype, char *error, ...); +void VARGS QCC_PR_Warning (int type, char *file, int line, char *error, ...); +void QCC_PR_ParsePrintDef (int warningtype, QCC_def_t *def); +void VARGS QCC_PR_ParseErrorPrintDef (int errortype, QCC_def_t *def, char *error, ...); + +int QCC_WarningForName(char *name); + +//QccMain.c must be changed if this is changed. +enum { + WARN_DEBUGGING, + WARN_ERROR, + WARN_NOTREFERENCED, + WARN_NOTREFERENCEDCONST, + WARN_CONFLICTINGRETURNS, + WARN_TOOFEWPARAMS, + WARN_TOOMANYPARAMS, + WARN_UNEXPECTEDPUNCT, + WARN_ASSIGNMENTTOCONSTANT, + WARN_ASSIGNMENTTOCONSTANTFUNC, + WARN_MISSINGRETURNVALUE, + WARN_WRONGRETURNTYPE, + WARN_CORRECTEDRETURNTYPE, + WARN_POINTLESSSTATEMENT, + WARN_MISSINGRETURN, + WARN_DUPLICATEDEFINITION, + WARN_UNDEFNOTDEFINED, + WARN_PRECOMPILERMESSAGE, + WARN_TOOMANYPARAMETERSFORFUNC, + WARN_STRINGTOOLONG, + WARN_BADTARGET, + WARN_BADPRAGMA, + WARN_HANGINGSLASHR, + WARN_NOTDEFINED, + WARN_NOTCONSTANT, + WARN_SWITCHTYPEMISMATCH, + WARN_CONFLICTINGUNIONMEMBER, + WARN_KEYWORDDISABLED, + WARN_ENUMFLAGS_NOTINTEGER, + WARN_ENUMFLAGS_NOTBINARY, + WARN_CASEINSENSATIVEFRAMEMACRO, + WARN_DUPLICATELABEL, + WARN_DUPLICATEMACRO, + WARN_ASSIGNMENTINCONDITIONAL, + WARN_MACROINSTRING, + WARN_BADPARAMS, + WARN_IMPLICITCONVERSION, + WARN_FIXEDRETURNVALUECONFLICT, + WARN_EXTRAPRECACHE, + WARN_NOTPRECACHED, + WARN_DEADCODE, + WARN_UNREACHABLECODE, + WARN_NOTSTANDARDBEHAVIOUR, + WARN_INEFFICIENTPLUSPLUS, + WARN_DUPLICATEPRECOMPILER, + WARN_IDENTICALPRECOMPILER, + WARN_FTE_SPECIFIC, //extension that only FTEQCC will have a clue about. + WARN_EXTENSION_USED, //extension that frikqcc also understands + WARN_IFSTRING_USED, + WARN_LAXCAST, //some errors become this with a compiler flag + WARN_UNDESIRABLECONVENTION, + WARN_SAMENAMEASGLOBAL, + WARN_CONSTANTCOMPARISON, + WARN_UNSAFEFUNCTIONRETURNTYPE, + + ERR_PARSEERRORS, //caused by qcc_pr_parseerror being called. + + //these are definatly my fault... + ERR_INTERNAL, + ERR_TOOCOMPLEX, + ERR_BADOPCODE, + ERR_TOOMANYSTATEMENTS, + ERR_TOOMANYSTRINGS, + ERR_BADTARGETSWITCH, + ERR_TOOMANYTYPES, + ERR_TOOMANYPAKFILES, + ERR_PRECOMPILERCONSTANTTOOLONG, + ERR_MACROTOOMANYPARMS, + ERR_CONSTANTTOOLONG, + ERR_TOOMANYFRAMEMACROS, + + //limitations, some are imposed by compiler, some arn't. + ERR_TOOMANYGLOBALS, + ERR_TOOMANYGOTOS, + ERR_TOOMANYBREAKS, + ERR_TOOMANYCONTINUES, + ERR_TOOMANYCASES, + ERR_TOOMANYLABELS, + ERR_TOOMANYOPENFILES, + ERR_TOOMANYPARAMETERSVARARGS, + ERR_TOOMANYTOTALPARAMETERS, + + //these are probably yours, or qcc being fussy. + ERR_BADEXTENSION, + ERR_BADIMMEDIATETYPE, + ERR_NOOUTPUT, + ERR_NOTAFUNCTION, + ERR_FUNCTIONWITHVARGS, + ERR_BADHEX, + ERR_UNKNOWNPUCTUATION, + ERR_EXPECTED, + ERR_NOTANAME, + ERR_NAMETOOLONG, + ERR_NOFUNC, + ERR_COULDNTOPENFILE, + ERR_NOTFUNCTIONTYPE, + ERR_TOOFEWPARAMS, + ERR_TOOMANYPARAMS, + ERR_CONSTANTNOTDEFINED, + ERR_BADFRAMEMACRO, + ERR_TYPEMISMATCH, + ERR_TYPEMISMATCHREDEC, + ERR_TYPEMISMATCHPARM, + ERR_TYPEMISMATCHARRAYSIZE, + ERR_UNEXPECTEDPUNCTUATION, + ERR_NOTACONSTANT, + ERR_REDECLARATION, + ERR_INITIALISEDLOCALFUNCTION, + ERR_NOTDEFINED, + ERR_ARRAYNEEDSSIZE, + ERR_ARRAYNEEDSBRACES, + ERR_TOOMANYINITIALISERS, + ERR_TYPEINVALIDINSTRUCT, + ERR_NOSHAREDLOCALS, + ERR_TYPEWITHNONAME, + ERR_BADARRAYSIZE, + ERR_NONAME, + ERR_SHAREDINITIALISED, + ERR_UNKNOWNVALUE, + ERR_BADARRAYINDEXTYPE, + ERR_NOVALIDOPCODES, + ERR_MEMBERNOTVALID, + ERR_BADPLUSPLUSOPERATOR, + ERR_BADNOTTYPE, + ERR_BADTYPECAST, + ERR_MULTIPLEDEFAULTS, + ERR_CASENOTIMMEDIATE, + ERR_BADSWITCHTYPE, + ERR_BADLABELNAME, + ERR_NOLABEL, + ERR_THINKTIMETYPEMISMATCH, + ERR_STATETYPEMISMATCH, + ERR_BADBUILTINIMMEDIATE, + ERR_PARAMWITHNONAME, + ERR_BADPARAMORDER, + ERR_ILLEGALCONTINUES, + ERR_ILLEGALBREAKS, + ERR_ILLEGALCASES, + ERR_NOTANUMBER, + ERR_WRONGSUBTYPE, + ERR_EOF, + ERR_NOPRECOMPILERIF, + ERR_NOENDIF, + ERR_HASHERROR, + ERR_NOTATYPE, + ERR_TOOMANYPACKFILES, + ERR_INVALIDVECTORIMMEDIATE, + ERR_INVALIDSTRINGIMMEDIATE, + ERR_BADCHARACTERCODE, + ERR_BADPARMS, + ERR_WERROR, + + WARN_MAX +}; + +#define FLAG_KILLSDEBUGGERS 1 +#define FLAG_ASDEFAULT 2 +#define FLAG_SETINGUI 4 +#define FLAG_HIDDENINGUI 8 +#define FLAG_MIDCOMPILE 16 //option can be changed mid-compile with the special pragma +typedef struct { + pbool *enabled; + char *abbrev; + int optimisationlevel; + int flags; //1: kills debuggers. 2: applied as default. + char *fullname; + char *description; + void *guiinfo; +} optimisations_t; +extern optimisations_t optimisations[]; + +typedef struct { + pbool *enabled; + int flags; //2 applied as default + char *abbrev; + char *fullname; + char *description; + void *guiinfo; +} compiler_flag_t; +extern compiler_flag_t compiler_flag[]; + +extern pbool qccwarningdisabled[WARN_MAX]; + +extern jmp_buf pr_parse_abort; // longjump with this on parse error +extern int pr_source_line; +extern char *pr_file_p; + +void *QCC_PR_Malloc (int size); + + +#define OFS_NULL 0 +#define OFS_RETURN 1 +#define OFS_PARM0 4 // leave 3 ofs for each parm to hold vectors +#define OFS_PARM1 7 +#define OFS_PARM2 10 +#define OFS_PARM3 13 +#define OFS_PARM4 16 +#define RESERVED_OFS 28 + + +extern QCC_def_t *pr_scope; +extern int pr_error_count, pr_warning_count; + +void QCC_PR_NewLine (pbool incomment); +QCC_def_t *QCC_PR_GetDef (QCC_type_t *type, char *name, QCC_def_t *scope, pbool allocate, int arraysize, pbool saved); + +void QCC_PR_PrintDefs (void); + +void QCC_PR_SkipToSemicolon (void); + +#define MAX_EXTRA_PARMS 128 +#ifdef MAX_EXTRA_PARMS +extern char pr_parm_names[MAX_PARMS+MAX_EXTRA_PARMS][MAX_NAME]; +extern QCC_def_t *extra_parms[MAX_EXTRA_PARMS]; +#else +extern char pr_parm_names[MAX_PARMS][MAX_NAME]; +#endif +extern pbool pr_trace; + +#define G_FLOAT(o) (qcc_pr_globals[o]) +#define G_INT(o) (*(int *)&qcc_pr_globals[o]) +#define G_VECTOR(o) (&qcc_pr_globals[o]) +#define G_STRING(o) (strings + *(QCC_string_t *)&qcc_pr_globals[o]) +#define G_FUNCTION(o) (*(func_t *)&qcc_pr_globals[o]) + +char *QCC_PR_ValueString (etype_t type, void *val); + +void QCC_PR_ClearGrabMacros (void); + +pbool QCC_PR_CompileFile (char *string, char *filename); +void QCC_PR_ResetErrorScope(void); + +extern pbool pr_dumpasm; + +extern QCC_string_t s_file; // filename for function definition + +extern QCC_def_t def_ret, def_parms[MAX_PARMS]; + +void QCC_PR_EmitArrayGetFunction(QCC_def_t *scope, char *arrayname); +void QCC_PR_EmitArraySetFunction(QCC_def_t *scope, char *arrayname); +void QCC_PR_EmitClassFromFunction(QCC_def_t *scope, char *tname); + +//============================================================================= + +extern char pr_immediate_string[8192]; + +extern float *qcc_pr_globals; +extern unsigned int numpr_globals; + +extern char *strings; +extern int strofs; + +extern QCC_dstatement_t *statements; +extern int numstatements; +extern int *statement_linenums; + +extern QCC_dfunction_t *functions; +extern int numfunctions; + +extern QCC_ddef_t *qcc_globals; +extern int numglobaldefs; + +extern QCC_def_t *activetemps; + +extern QCC_ddef_t *fields; +extern int numfielddefs; + +extern QCC_type_t *qcc_typeinfo; +extern int numtypeinfos; +extern int maxtypeinfos; + +extern int ForcedCRC; +extern pbool defaultstatic; + +extern int *qcc_tempofs; +extern int max_temps; +//extern int qcc_functioncalled; //unuse temps if this is true - don't want to reuse the same space. + +extern int tempsstart; +extern int numtemps; + +typedef char PATHSTRING[MAX_DATA_PATH]; + +PATHSTRING *precache_sounds; +int *precache_sounds_block; +int *precache_sounds_used; +int numsounds; + +PATHSTRING *precache_textures; +int *precache_textures_block; +int numtextures; + +PATHSTRING *precache_models; +int *precache_models_block; +int *precache_models_used; +int nummodels; + +PATHSTRING *precache_files; +int *precache_files_block; +int numfiles; + +int QCC_CopyString (char *str); + + + + +typedef struct qcc_cachedsourcefile_s { + char filename[128]; + int size; + char *file; + enum{FT_CODE, FT_DATA} type; //quakec source file or not. + struct qcc_cachedsourcefile_s *next; +} qcc_cachedsourcefile_t; +extern qcc_cachedsourcefile_t *qcc_sourcefile; + + + + + +#ifdef COMMONINLINES +static bool inline QCC_PR_CheckToken (char *string) +{ + if (pr_token_type != tt_punct) + return false; + + if (STRCMP (string, pr_token)) + return false; + + QCC_PR_Lex (); + return true; +} + +static void inline QCC_PR_Expect (char *string) +{ + if (strcmp (string, pr_token)) + QCC_PR_ParseError ("expected %s, found %s",string, pr_token); + QCC_PR_Lex (); +} +#endif + +void editbadfile(char *fname, int line); +char *TypeName(QCC_type_t *type); +void QCC_PR_IncludeChunk (char *data, pbool duplicate, char *filename); +void QCC_PR_IncludeChunkEx(char *data, pbool duplicate, char *filename, CompilerConstant_t *cnst); +pbool QCC_PR_UnInclude(void); +extern void *(*pHash_Get)(hashtable_t *table, char *name); +extern void *(*pHash_GetNext)(hashtable_t *table, char *name, void *old); +extern void *(*pHash_Add)(hashtable_t *table, char *name, void *data, bucket_t *); diff --git a/misc/mediasource/extra/fteqcc-src/qcc_cmdlib.c b/misc/mediasource/extra/fteqcc-src/qcc_cmdlib.c new file mode 100644 index 00000000..8d0f874a --- /dev/null +++ b/misc/mediasource/extra/fteqcc-src/qcc_cmdlib.c @@ -0,0 +1,918 @@ +// cmdlib.c + +#include "qcc.h" +#include +//#include + +#define PATHSEPERATOR '/' + +#ifndef QCC +extern jmp_buf qcccompileerror; +#endif + +// set these before calling CheckParm +int myargc; +char **myargv; + +char qcc_token[1024]; +int qcc_eof; + +const unsigned int type_size[12] = {1, //void + sizeof(string_t)/4, //string + 1, //float + 3, //vector + 1, //entity + 1, //field + sizeof(func_t)/4,//function + 1, //pointer (its an int index) + 1, //integer + 1, //fixme: how big should a variant be? + 0, //ev_struct. variable sized. + 0 //ev_union. variable sized. + }; + +/* +============================================================================ + + BYTE ORDER FUNCTIONS + +============================================================================ +*/ +short (*PRBigShort) (short l); +short (*PRLittleShort) (short l); +int (*PRBigLong) (int l); +int (*PRLittleLong) (int l); +float (*PRBigFloat) (float l); +float (*PRLittleFloat) (float l); + + +short QCC_SwapShort (short l) +{ + qbyte b1,b2; + + b1 = l&255; + b2 = (l>>8)&255; + + return (b1<<8) + b2; +} + +short QCC_Short (short l) +{ + return l; +} + + +int QCC_SwapLong (int l) +{ + qbyte b1,b2,b3,b4; + + b1 = (qbyte)l; + b2 = (qbyte)(l>>8); + b3 = (qbyte)(l>>16); + b4 = (qbyte)(l>>24); + + return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4; +} + +int QCC_Long (int l) +{ + return l; +} + + +float QCC_SwapFloat (float l) +{ + union {qbyte b[4]; float f;} in, out; + + in.f = l; + out.b[0] = in.b[3]; + out.b[1] = in.b[2]; + out.b[2] = in.b[1]; + out.b[3] = in.b[0]; + + return out.f; +} + +float QCC_Float (float l) +{ + return l; +} + +void SetEndian(void) +{ + union {qbyte b[2]; unsigned short s;} ed; + ed.s = 255; + if (ed.b[0] == 255) + { + PRBigShort = QCC_SwapShort; + PRLittleShort = QCC_Short; + PRBigLong = QCC_SwapLong; + PRLittleLong = QCC_Long; + PRBigFloat = QCC_SwapFloat; + PRLittleFloat = QCC_Float; + } + else + { + PRBigShort = QCC_Short; + PRLittleShort = QCC_SwapShort; + PRBigLong = QCC_Long; + PRLittleLong = QCC_SwapLong; + PRBigFloat = QCC_Float; + PRLittleFloat = QCC_SwapFloat; + } +} + + + +#ifndef MINIMAL +/* +================ +I_FloatTime +================ +*/ +/* +double I_FloatTime (void) +{ + struct timeval tp; + struct timezone tzp; + static int secbase; + + gettimeofday(&tp, &tzp); + + if (!secbase) + { + secbase = tp.tv_sec; + return tp.tv_usec/1000000.0; + } + + return (tp.tv_sec - secbase) + tp.tv_usec/1000000.0; +} + + */ + + +#ifdef QCC +int QC_strncasecmp (const char *s1, const char *s2, int n) +{ + int c1, c2; + + while (1) + { + c1 = *s1++; + c2 = *s2++; + + if (!n--) + return 0; // strings are equal until end point + + if (c1 != c2) + { + if (c1 >= 'a' && c1 <= 'z') + c1 -= ('a' - 'A'); + if (c2 >= 'a' && c2 <= 'z') + c2 -= ('a' - 'A'); + if (c1 != c2) + return -1; // strings not equal + } + if (!c1) + return 0; // strings are equal +// s1++; +// s2++; + } + + return -1; +} + +int QC_strcasecmp (const char *s1, const char *s2) +{ + return QC_strncasecmp(s1, s2, 0x7fffffff); +} + +#else +int QC_strncasecmp(const char *s1, const char *s2, int n); +int QC_strcasecmp (const char *s1, const char *s2) +{ + return QC_strncasecmp(s1, s2, 0x7fffffff); +} + +#endif + + + +#endif //minimal +/* +============== +COM_Parse + +Parse a token out of a string +============== +*/ +char *QCC_COM_Parse (char *data) +{ + int c; + int len; + + len = 0; + qcc_token[0] = 0; + + if (!data) + return NULL; + +// skip whitespace +skipwhite: + while ( (c = *data) <= ' ') + { + if (c == 0) + { + qcc_eof = true; + return NULL; // end of file; + } + data++; + } + +// skip // comments + if (c=='/' && data[1] == '/') + { + while (*data && *data != '\n') + data++; + goto skipwhite; + } + + // skip /* comments + if (c=='/' && data[1] == '*') + { + while (data[1] && (data[0] != '*' || data[1] != '/')) + data++; + data+=2; + goto skipwhite; + } + + +// handle quoted strings specially + if (c == '\"') + { + data++; + do + { + c = *data++; + if (c=='\\' && *data == '\"') + c = *data++; //allow C-style string escapes + else if (c=='\\' && *data == '\\') + c = *data++; // \ is now a special character so it needs to be marked up using itself + else if (c=='\\' && *data == 'n') + { // and do new lines while we're at it. + c = '\n'; + data++; + } + else if (c=='\"') + { + qcc_token[len] = 0; + return data; + } + else if (c=='\0'||c=='\n') + { + qcc_token[len] = 0; + return data; + } + qcc_token[len] = c; + len++; + } while (1); + } + +// parse single characters + if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':' || c==',') + { + qcc_token[len] = c; + len++; + qcc_token[len] = 0; + return data+1; + } + +// parse a regular word + do + { + qcc_token[len] = c; + data++; + len++; + c = *data; + if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':' || c=='\"' || c==',') + break; + } while (c>32); + + qcc_token[len] = 0; + return data; +} + +//more C tokens... +char *QCC_COM_Parse2 (char *data) +{ + int c; + int len; + + len = 0; + qcc_token[0] = 0; + + if (!data) + return NULL; + +// skip whitespace +skipwhite: + while ( (c = *data) <= ' ') + { + if (c == 0) + { + qcc_eof = true; + return NULL; // end of file; + } + data++; + } + +// skip // comments + if (c=='/' && data[1] == '/') + { + while (*data && *data != '\n') + data++; + goto skipwhite; + } + + +// handle quoted strings specially + if (c == '\"') + { + data++; + do + { + c = *data++; + if (c=='\\' && *data == '\"') + c = *data++; //allow C-style string escapes + else if (c=='\\' && *data == '\\') + c = *data++; // \ is now a special character so it needs to be marked up using itself + else if (c=='\\' && *data == 'n') + { // and do new lines while we're at it. + c = '\n'; + data++; + } + else if (c=='\"'||c=='\0') + qcc_token[len] = c; + len++; + } while (1); + } + +// parse numbers + if (c >= '0' && c <= '9') + { + if (c == '0' && data[1] == 'x') + { //parse hex + qcc_token[0] = '0'; + c='x'; + len=1; + data++; + for(;;) + { //parse regular number + qcc_token[len] = c; + data++; + len++; + c = *data; + if ((c<'0'|| c>'9') && (c<'a'||c>'f') && (c<'A'||c>'F') && c != '.') + break; + } + + } + else + { + for(;;) + { //parse regular number + qcc_token[len] = c; + data++; + len++; + c = *data; + if ((c<'0'|| c>'9') && c != '.') + break; + } + } + + qcc_token[len] = 0; + return data; + } +// parse words + else if ((c>= 'a' && c <= 'z') || (c>= 'A' && c <= 'Z') || c == '_') + { + do + { + qcc_token[len] = c; + data++; + len++; + c = *data; + } while ((c>= 'a' && c <= 'z') || (c>= 'A' && c <= 'Z') || c == '_'); + + qcc_token[len] = 0; + return data; + } + else + { + qcc_token[len] = c; + len++; + qcc_token[len] = 0; + return data+1; + } +} + +char *VARGS qcva (char *text, ...) +{ + va_list argptr; + static char msg[2048]; + + va_start (argptr,text); + QC_vsnprintf (msg,sizeof(msg)-1, text,argptr); + va_end (argptr); + + return msg; +} + + +#ifndef MINIMAL + +char *QC_strupr (char *start) +{ + char *in; + in = start; + while (*in) + { + *in = toupper(*in); + in++; + } + return start; +} + +char *QC_strlower (char *start) +{ + char *in; + in = start; + while (*in) + { + *in = tolower(*in); + in++; + } + return start; +} + + +/* +============================================================================= + + MISC FUNCTIONS + +============================================================================= +*/ + +/* +================= +Error + +For abnormal program terminations +================= +*/ +void VARGS QCC_Error (int errortype, const char *error, ...) +{ + extern int numsourcefiles; + va_list argptr; + char msg[2048]; + + va_start (argptr,error); + QC_vsnprintf (msg,sizeof(msg)-1, error,argptr); + va_end (argptr); + + printf ("\n************ ERROR ************\n%s\n", msg); + + + editbadfile(strings+s_file, pr_source_line); + + numsourcefiles = 0; + +#ifndef QCC + longjmp(qcccompileerror, 1); +#else + print ("Press any key\n"); + getch(); +#endif + exit (1); +} + + +/* +================= +CheckParm + +Checks for the given parameter in the program's command line arguments +Returns the argument number (1 to argc-1) or 0 if not present +================= +*/ +int QCC_CheckParm (char *check) +{ + int i; + + for (i = 1;i 0 && path[length] != PATHSEPERATOR) + length--; + path[length] = 0; +} + +/* +==================== +Extract file parts +==================== +*/ +void ExtractFilePath (char *path, char *dest) +{ + char *src; + + src = path + strlen(path) - 1; + +// +// back up until a \ or the start +// + while (src != path && *(src-1) != PATHSEPERATOR) + src--; + + memcpy (dest, path, src-path); + dest[src-path] = 0; +} + +void ExtractFileBase (char *path, char *dest) +{ + char *src; + + src = path + strlen(path) - 1; + +// +// back up until a \ or the start +// + while (src != path && *(src-1) != PATHSEPERATOR) + src--; + + while (*src && *src != '.') + { + *dest++ = *src++; + } + *dest = 0; +} + +void ExtractFileExtension (char *path, char *dest) +{ + char *src; + + src = path + strlen(path) - 1; + +// +// back up until a . or the start +// + while (src != path && *(src-1) != '.') + src--; + if (src == path) + { + *dest = 0; // no extension + return; + } + + strcpy (dest,src); +} + + +/* +============== +ParseNum / ParseHex +============== +*/ +long ParseHex (char *hex) +{ + char *str; + long num; + + num = 0; + str = hex; + + while (*str) + { + num <<= 4; + if (*str >= '0' && *str <= '9') + num += *str-'0'; + else if (*str >= 'a' && *str <= 'f') + num += 10 + *str-'a'; + else if (*str >= 'A' && *str <= 'F') + num += 10 + *str-'A'; + else + QCC_Error (ERR_BADHEX, "Bad hex number: %s",hex); + str++; + } + + return num; +} + + +long ParseNum (char *str) +{ + if (str[0] == '$') + return ParseHex (str+1); + if (str[0] == '0' && str[1] == 'x') + return ParseHex (str+2); + return atol (str); +} + + + + + + + + +//buffer size and max size are different. buffer is bigger. + +#define MAXQCCFILES 3 +struct { + char name[64]; + char *buff; +// int buffismalloc; + int buffsize; + int ofs; + int maxofs; +} qccfile[MAXQCCFILES]; +int SafeOpenWrite (char *filename, int maxsize) +{ + int i; + for (i = 0; i < MAXQCCFILES; i++) + { + if (!qccfile[i].buff) + { + strcpy(qccfile[i].name, filename); + qccfile[i].buffsize = maxsize; + qccfile[i].maxofs = 0; + qccfile[i].ofs = 0; +// if (maxsize > 8192) +// qccfile[i].buffismalloc = 1; +// else +// qccfile[i].buffismalloc = 0; +// if (qccfile[i].buffismalloc) + qccfile[i].buff = malloc(qccfile[i].buffsize); +// else +// qccfile[i].buff = memalloc(qccfile[i].buffsize); + return i; + } + } + QCC_Error(ERR_TOOMANYOPENFILES, "Too many open files on file %s", filename); + return -1; +} + +void ResizeBuf(int hand, int newsize) +{ +// int wasmal = qccfile[hand].buffismalloc; + char *nb; + + if (qccfile[hand].buffsize >= newsize) + return; //already big enough + +// if (newsize > 8192) +// { +// qccfile[hand].buffismalloc = true; + nb = malloc(newsize); +// } +// else +// { +// qccfile[hand].buffismalloc = false; +// nb = memalloc(newsize); +// } + + memcpy(nb, qccfile[hand].buff, qccfile[hand].maxofs); +// if (wasmal) + free(qccfile[hand].buff); +// else +// memfree(qccfile[hand].buff); + qccfile[hand].buff = nb; + qccfile[hand].buffsize = newsize; +} +void SafeWrite(int hand, void *buf, long count) +{ + if (qccfile[hand].ofs +count >= qccfile[hand].buffsize) + ResizeBuf(hand, qccfile[hand].ofs + count+(64*1024)); + + memcpy(&qccfile[hand].buff[qccfile[hand].ofs], buf, count); + qccfile[hand].ofs+=count; + if (qccfile[hand].ofs > qccfile[hand].maxofs) + qccfile[hand].maxofs = qccfile[hand].ofs; +} +int SafeSeek(int hand, int ofs, int mode) +{ + if (mode == SEEK_CUR) + return qccfile[hand].ofs; + else + { + ResizeBuf(hand, ofs+1024); + qccfile[hand].ofs = ofs; + if (qccfile[hand].ofs > qccfile[hand].maxofs) + qccfile[hand].maxofs = qccfile[hand].ofs; + return 0; + } +} +void SafeClose(int hand) +{ + externs->WriteFile(qccfile[hand].name, qccfile[hand].buff, qccfile[hand].maxofs); +// if (qccfile[hand].buffismalloc) + free(qccfile[hand].buff); +// else +// memfree(qccfile[hand].buff); + qccfile[hand].buff = NULL; +} + +qcc_cachedsourcefile_t *qcc_sourcefile; +long QCC_LoadFile (char *filename, void **bufferptr) +{ + char *mem; + int len; + len = externs->FileSize(filename); + if (len < 0) + { + QCC_Error(ERR_COULDNTOPENFILE, "Couldn't open file %s", filename); +// if (!Abort) + return -1; +// Abort("failed to find file %s", filename); + } + mem = qccHunkAlloc(sizeof(qcc_cachedsourcefile_t) + len+2); + + ((qcc_cachedsourcefile_t*)mem)->next = qcc_sourcefile; + qcc_sourcefile = (qcc_cachedsourcefile_t*)mem; + qcc_sourcefile->size = len; + mem += sizeof(qcc_cachedsourcefile_t); + strcpy(qcc_sourcefile->filename, filename); + qcc_sourcefile->file = mem; + qcc_sourcefile->type = FT_CODE; + + externs->ReadFile(filename, mem, len+2); + mem[len] = '\n'; + mem[len+1] = '\0'; + *bufferptr=mem; + + return len; +} +void QCC_AddFile (char *filename) +{ + char *mem; + int len; + len = externs->FileSize(filename); + if (len < 0) + Abort("failed to find file %s", filename); + mem = qccHunkAlloc(sizeof(qcc_cachedsourcefile_t) + len+1); + + ((qcc_cachedsourcefile_t*)mem)->next = qcc_sourcefile; + qcc_sourcefile = (qcc_cachedsourcefile_t*)mem; + qcc_sourcefile->size = len; + mem += sizeof(qcc_cachedsourcefile_t); + strcpy(qcc_sourcefile->filename, filename); + qcc_sourcefile->file = mem; + qcc_sourcefile->type = FT_DATA; + + externs->ReadFile(filename, mem, len+1); + mem[len] = '\0'; +} +void *FS_ReadToMem(char *filename, void *mem, int *len) +{ + if (!mem) + { + *len = externs->FileSize(filename); + mem = memalloc(*len); + } + return externs->ReadFile(filename, mem, *len); +} + +void FS_CloseFromMem(void *mem) +{ + memfree(mem); +} + + +#endif + +void StripExtension (char *path) +{ + int length; + + length = strlen(path)-1; + while (length > 0 && path[length] != '.') + { + length--; + if (path[length] == '/') + return; // no extension + } + if (length) + path[length] = 0; +} diff --git a/misc/mediasource/extra/fteqcc-src/qcc_pr_comp.c b/misc/mediasource/extra/fteqcc-src/qcc_pr_comp.c new file mode 100644 index 00000000..e37d4ff2 --- /dev/null +++ b/misc/mediasource/extra/fteqcc-src/qcc_pr_comp.c @@ -0,0 +1,9829 @@ +#ifndef MINIMAL + +#include "qcc.h" +void QCC_PR_ParseAsm(void); + +#define MEMBERFIELDNAME "__m%s" + +#define STRCMP(s1,s2) (((*s1)!=(*s2)) || strcmp(s1+1,s2+1)) //saves about 2-6 out of 120 - expansion of idea from fastqcc +#define STRNCMP(s1,s2,l) (((*s1)!=(*s2)) || strncmp(s1+1,s2+1,l)) //pathetic saving here. + +extern char *compilingfile; + +int conditional; + +extern int dotranslate; +extern int dotranslate_count; + +//standard qcc keywords +#define keyword_do 1 +#define keyword_return 1 +#define keyword_if 1 +#define keyword_else 1 +#define keyword_local 1 +#define keyword_while 1 + +//extended keywords. +pbool keyword_asm; +pbool keyword_break; +pbool keyword_case; +pbool keyword_class; +pbool keyword_const; //fixme +pbool keyword_continue; +pbool keyword_default; +pbool keyword_entity; //for skipping the local +pbool keyword_float; //for skipping the local +pbool keyword_for; +pbool keyword_goto; +pbool keyword_int; //for skipping the local +pbool keyword_integer; //for skipping the local +pbool keyword_state; +pbool keyword_string; //for skipping the local +pbool keyword_struct; +pbool keyword_switch; +pbool keyword_thinktime; +pbool keyword_var; //allow it to be initialised and set around the place. +pbool keyword_vector; //for skipping the local + + +pbool keyword_enum; //kinda like in c, but typedef not supported. +pbool keyword_enumflags; //like enum, but doubles instead of adds 1. +pbool keyword_typedef; //fixme +#define keyword_codesys flag_acc //reacc needs this (forces the resultant crc) +#define keyword_function flag_acc //reacc needs this (reacc has this on all functions, wierd eh?) +#define keyword_objdata flag_acc //reacc needs this (following defs are fields rather than globals, use var to disable) +#define keyword_object flag_acc //reacc needs this (an entity) +#define keyword_pfunc flag_acc //reacc needs this (pointer to function) +#define keyword_system flag_acc //reacc needs this (potatos) +#define keyword_real flag_acc //reacc needs this (a float) +#define keyword_exit flag_acc //emits an OP_DONE opcode. +#define keyword_external flag_acc //reacc needs this (a builtin) +pbool keyword_extern; //function is external, don't error or warn if the body was not found +pbool keyword_shared; //mark global to be copied over when progs changes (part of FTE_MULTIPROGS) +pbool keyword_noref; //nowhere else references this, don't strip it. +pbool keyword_nosave; //don't write the def to the output. +pbool keyword_union; //you surly know what a union is! + +#define keyword_not 1 //hexenc support needs this, and fteqcc can optimise without it, but it adds an extra token after the if, so it can cause no namespace conflicts + +pbool keywords_coexist; //don't disable a keyword simply because a var was made with the same name. +pbool output_parms; //emit some PARMX fields. confuses decompilers. +pbool autoprototype; //take two passes over the source code. First time round doesn't enter and functions or initialise variables. +pbool pr_subscopedlocals; //causes locals to be valid ONLY within their statement block. (they simply can't be referenced by name outside of it) +pbool flag_ifstring; //makes if (blah) equivelent to if (blah != "") which resolves some issues in multiprogs situations. +pbool flag_iffloat; //use an op_if_f instruction instead of op_if so if(-0) evaluates to false. +pbool flag_acc; //reacc like behaviour of src files (finds *.qc in start dir and compiles all in alphabetical order) +pbool flag_caseinsensative; //symbols will be matched to an insensative case if the specified case doesn't exist. This should b usable for any mod +pbool flag_laxcasts; //Allow lax casting. This'll produce loadsa warnings of course. But allows compilation of certain dodgy code. +pbool flag_hashonly; //Allows use of only #constant for precompiler constants, allows certain preqcc using mods to compile +pbool flag_fasttrackarrays; //Faster arrays, dynamically detected, activated only in supporting engines. +pbool flag_msvcstyle; //MSVC style warnings, so msvc's ide works properly +pbool flag_assume_integer; //5 - is that an integer or a float? qcc says float. but we support int too, so maybe we want that instead? + +pbool opt_overlaptemps; //reduce numpr_globals by reuse of temps. When they are not needed they are freed for reuse. The way this is implemented is better than frikqcc's. (This is the single most important optimisation) +pbool opt_assignments; //STORE_F isn't used if an operation wrote to a temp. +pbool opt_shortenifnots; //if(!var) is made an IF rather than NOT IFNOT +pbool opt_noduplicatestrings; //brute force string check. time consuming but more effective than the equivelent in frikqcc. +pbool opt_constantarithmatic; //3*5 appears as 15 instead of the extra statement. +pbool opt_nonvec_parms; //store_f instead of store_v on function calls, where possible. +pbool opt_constant_names; //take out the defs and name strings of constants. +pbool opt_constant_names_strings;//removes the defs of strings too. plays havok with multiprogs. +pbool opt_precache_file; //remove the call, the parameters, everything. +pbool opt_filenames; //strip filenames. hinders older decompilers. +pbool opt_unreferenced; //strip defs that are not referenced. +pbool opt_function_names; //strip out the names of builtin functions. +pbool opt_locals; //strip out the names of locals and immediates. +pbool opt_dupconstdefs; //float X = 5; and float Y = 5; occupy the same global with this. +pbool opt_return_only; //RETURN; DONE; at the end of a function strips out the done statement if there is no way to get to it. +pbool opt_compound_jumps; //jumps to jump statements jump to the final point. +pbool opt_stripfunctions; //if a functions is only ever called directly or by exe, don't emit the def. +pbool opt_locals_marshalling; //make the local vars of all functions occupy the same globals. +pbool opt_logicops; //don't make conditions enter functions if the return value will be discarded due to a previous value. (C style if statements) +pbool opt_vectorcalls; //vectors can be packed into 3 floats, which can yield lower numpr_globals, but cost two more statements per call (only works for q1 calling conventions). +pbool opt_simplifiedifs; //if (f != 0) -> if (f). if (f == 0) -> ifnot (f) +//bool opt_comexprremoval; + +//these are the results of the opt_. The values are printed out when compilation is compleate, showing effectivness. +int optres_shortenifnots; +int optres_assignments; +int optres_overlaptemps; +int optres_noduplicatestrings; +int optres_constantarithmatic; +int optres_nonvec_parms; +int optres_constant_names; +int optres_constant_names_strings; +int optres_precache_file; +int optres_filenames; +int optres_unreferenced; +int optres_function_names; +int optres_locals; +int optres_dupconstdefs; +int optres_return_only; +int optres_compound_jumps; +//int optres_comexprremoval; +int optres_stripfunctions; +int optres_locals_marshalling; +int optres_logicops; + +int optres_test1; +int optres_test2; + +void *(*pHash_Get)(hashtable_t *table, char *name); +void *(*pHash_GetNext)(hashtable_t *table, char *name, void *old); +void *(*pHash_Add)(hashtable_t *table, char *name, void *data, bucket_t *); + +QCC_def_t *QCC_PR_DummyDef(QCC_type_t *type, char *name, QCC_def_t *scope, int arraysize, unsigned int ofs, int referable, pbool saved); +QCC_type_t *QCC_PR_NewType (char *name, int basictype); +QCC_type_t *QCC_PR_FindType (QCC_type_t *type); +QCC_type_t *QCC_PR_PointerType (QCC_type_t *pointsto); +QCC_type_t *QCC_PR_FieldType (QCC_type_t *pointsto); + +void QCC_PR_ParseState (void); +pbool simplestore; +pbool expandedemptymacro; + +QCC_pr_info_t pr; +//QCC_def_t **pr_global_defs/*[MAX_REGS]*/; // to find def for a global variable + +//keeps track of how many funcs are called while parsing a statement +//int qcc_functioncalled; + +//======================================== + +QCC_def_t *pr_scope; // the function being parsed, or NULL +QCC_type_t *pr_classtype; +pbool pr_dumpasm; +QCC_string_t s_file, s_file2; // filename for function definition + +unsigned int locals_start; // for tracking local variables vs temps +unsigned int locals_end; // for tracking local variables vs temps + +jmp_buf pr_parse_abort; // longjump with this on parse error + +void QCC_PR_ParseDefs (char *classname); + +pbool qcc_usefulstatement; + +int max_breaks; +int max_continues; +int max_cases; +int num_continues; +int num_breaks; +int num_cases; +int *pr_breaks; +int *pr_continues; +int *pr_cases; +QCC_def_t **pr_casesdef; +QCC_def_t **pr_casesdef2; + +typedef struct { + int statementno; + int lineno; + char name[256]; +} gotooperator_t; + +int max_labels; +int max_gotos; +gotooperator_t *pr_labels; +gotooperator_t *pr_gotos; +int num_gotos; +int num_labels; + +QCC_def_t *extra_parms[MAX_EXTRA_PARMS]; + +#define ASSOC_RIGHT_RESULT ASSOC_RIGHT + +//======================================== + +//FIXME: modifiy list so most common GROUPS are first +//use look up table for value of first char and sort by first char and most common...? + +//if true, effectivly {b=a; return a;} +QCC_opcode_t pr_opcodes[] = +{ + {6, "", "DONE", -1, ASSOC_LEFT, &type_void, &type_void, &type_void}, + + {6, "*", "MUL_F", 3, ASSOC_LEFT, &type_float, &type_float, &type_float}, + {6, "*", "MUL_V", 3, ASSOC_LEFT, &type_vector, &type_vector, &type_float}, + {6, "*", "MUL_FV", 3, ASSOC_LEFT, &type_float, &type_vector, &type_vector}, + {6, "*", "MUL_VF", 3, ASSOC_LEFT, &type_vector, &type_float, &type_vector}, + + {6, "/", "DIV_F", 3, ASSOC_LEFT, &type_float, &type_float, &type_float}, + + {6, "+", "ADD_F", 4, ASSOC_LEFT, &type_float, &type_float, &type_float}, + {6, "+", "ADD_V", 4, ASSOC_LEFT, &type_vector, &type_vector, &type_vector}, + + {6, "-", "SUB_F", 4, ASSOC_LEFT, &type_float, &type_float, &type_float}, + {6, "-", "SUB_V", 4, ASSOC_LEFT, &type_vector, &type_vector, &type_vector}, + + {6, "==", "EQ_F", 5, ASSOC_LEFT, &type_float, &type_float, &type_float}, + {6, "==", "EQ_V", 5, ASSOC_LEFT, &type_vector, &type_vector, &type_float}, + {6, "==", "EQ_S", 5, ASSOC_LEFT, &type_string, &type_string, &type_float}, + {6, "==", "EQ_E", 5, ASSOC_LEFT, &type_entity, &type_entity, &type_float}, + {6, "==", "EQ_FNC", 5, ASSOC_LEFT, &type_function, &type_function, &type_float}, + + {6, "!=", "NE_F", 5, ASSOC_LEFT, &type_float, &type_float, &type_float}, + {6, "!=", "NE_V", 5, ASSOC_LEFT, &type_vector, &type_vector, &type_float}, + {6, "!=", "NE_S", 5, ASSOC_LEFT, &type_string, &type_string, &type_float}, + {6, "!=", "NE_E", 5, ASSOC_LEFT, &type_entity, &type_entity, &type_float}, + {6, "!=", "NE_FNC", 5, ASSOC_LEFT, &type_function, &type_function, &type_float}, + + {6, "<=", "LE", 5, ASSOC_LEFT, &type_float, &type_float, &type_float}, + {6, ">=", "GE", 5, ASSOC_LEFT, &type_float, &type_float, &type_float}, + {6, "<", "LT", 5, ASSOC_LEFT, &type_float, &type_float, &type_float}, + {6, ">", "GT", 5, ASSOC_LEFT, &type_float, &type_float, &type_float}, + + {6, ".", "INDIRECT_F", 1, ASSOC_LEFT, &type_entity, &type_field, &type_float}, + {6, ".", "INDIRECT_V", 1, ASSOC_LEFT, &type_entity, &type_field, &type_vector}, + {6, ".", "INDIRECT_S", 1, ASSOC_LEFT, &type_entity, &type_field, &type_string}, + {6, ".", "INDIRECT_E", 1, ASSOC_LEFT, &type_entity, &type_field, &type_entity}, + {6, ".", "INDIRECT_FI", 1, ASSOC_LEFT, &type_entity, &type_field, &type_field}, + {6, ".", "INDIRECT_FU", 1, ASSOC_LEFT, &type_entity, &type_field, &type_function}, + + {6, ".", "ADDRESS", 1, ASSOC_LEFT, &type_entity, &type_field, &type_pointer}, + + {6, "=", "STORE_F", 6, ASSOC_RIGHT, &type_float, &type_float, &type_float}, + {6, "=", "STORE_V", 6, ASSOC_RIGHT, &type_vector, &type_vector, &type_vector}, + {6, "=", "STORE_S", 6, ASSOC_RIGHT, &type_string, &type_string, &type_string}, + {6, "=", "STORE_ENT", 6, ASSOC_RIGHT, &type_entity, &type_entity, &type_entity}, + {6, "=", "STORE_FLD", 6, ASSOC_RIGHT, &type_field, &type_field, &type_field}, + {6, "=", "STORE_FNC", 6, ASSOC_RIGHT, &type_function, &type_function, &type_function}, + + {6, "=", "STOREP_F", 6, ASSOC_RIGHT, &type_pointer, &type_float, &type_float}, + {6, "=", "STOREP_V", 6, ASSOC_RIGHT, &type_pointer, &type_vector, &type_vector}, + {6, "=", "STOREP_S", 6, ASSOC_RIGHT, &type_pointer, &type_string, &type_string}, + {6, "=", "STOREP_ENT", 6, ASSOC_RIGHT, &type_pointer, &type_entity, &type_entity}, + {6, "=", "STOREP_FLD", 6, ASSOC_RIGHT, &type_pointer, &type_field, &type_field}, + {6, "=", "STOREP_FNC", 6, ASSOC_RIGHT, &type_pointer, &type_function, &type_function}, + + {6, "", "RETURN", -1, ASSOC_LEFT, &type_float, &type_void, &type_void}, + + {6, "!", "NOT_F", -1, ASSOC_LEFT, &type_float, &type_void, &type_float}, + {6, "!", "NOT_V", -1, ASSOC_LEFT, &type_vector, &type_void, &type_float}, + {6, "!", "NOT_S", -1, ASSOC_LEFT, &type_vector, &type_void, &type_float}, + {6, "!", "NOT_ENT", -1, ASSOC_LEFT, &type_entity, &type_void, &type_float}, + {6, "!", "NOT_FNC", -1, ASSOC_LEFT, &type_function, &type_void, &type_float}, + + {6, "", "IF", -1, ASSOC_RIGHT, &type_float, NULL, &type_void}, + {6, "", "IFNOT", -1, ASSOC_RIGHT, &type_float, NULL, &type_void}, + +// calls returns REG_RETURN + {6, "", "CALL0", -1, ASSOC_LEFT, &type_function, &type_void, &type_void}, + {6, "", "CALL1", -1, ASSOC_LEFT, &type_function, &type_void, &type_void}, + {6, "", "CALL2", -1, ASSOC_LEFT, &type_function, &type_void, &type_void}, + {6, "", "CALL3", -1, ASSOC_LEFT, &type_function, &type_void, &type_void}, + {6, "", "CALL4", -1, ASSOC_LEFT, &type_function, &type_void, &type_void}, + {6, "", "CALL5", -1, ASSOC_LEFT, &type_function, &type_void, &type_void}, + {6, "", "CALL6", -1, ASSOC_LEFT, &type_function, &type_void, &type_void}, + {6, "", "CALL7", -1, ASSOC_LEFT, &type_function, &type_void, &type_void}, + {6, "", "CALL8", -1, ASSOC_LEFT, &type_function, &type_void, &type_void}, + + {6, "", "STATE", -1, ASSOC_LEFT, &type_float, &type_float, &type_void}, + + {6, "", "GOTO", -1, ASSOC_RIGHT, NULL, &type_void, &type_void}, + + {6, "&&", "AND", 7, ASSOC_LEFT, &type_float, &type_float, &type_float}, + {6, "||", "OR", 7, ASSOC_LEFT, &type_float, &type_float, &type_float}, + + {6, "&", "BITAND", 3, ASSOC_LEFT, &type_float, &type_float, &type_float}, + {6, "|", "BITOR", 3, ASSOC_LEFT, &type_float, &type_float, &type_float}, + + //version 6 are in normal progs. + + + +//these are hexen2 + {7, "*=", "MULSTORE_F", 6, ASSOC_RIGHT_RESULT, &type_float, &type_float, &type_float}, + {7, "*=", "MULSTORE_V", 6, ASSOC_RIGHT_RESULT, &type_vector, &type_float, &type_vector}, + {7, "*=", "MULSTOREP_F", 6, ASSOC_RIGHT_RESULT, &type_pointer, &type_float, &type_float}, + {7, "*=", "MULSTOREP_V", 6, ASSOC_RIGHT_RESULT, &type_pointer, &type_float, &type_vector}, + + {7, "/=", "DIVSTORE_F", 6, ASSOC_RIGHT_RESULT, &type_float, &type_float, &type_float}, + {7, "/=", "DIVSTOREP_F", 6, ASSOC_RIGHT_RESULT, &type_pointer, &type_float, &type_float}, + + {7, "+=", "ADDSTORE_F", 6, ASSOC_RIGHT_RESULT, &type_float, &type_float, &type_float}, + {7, "+=", "ADDSTORE_V", 6, ASSOC_RIGHT_RESULT, &type_vector, &type_vector, &type_vector}, + {7, "+=", "ADDSTOREP_F", 6, ASSOC_RIGHT_RESULT, &type_pointer, &type_float, &type_float}, + {7, "+=", "ADDSTOREP_V", 6, ASSOC_RIGHT_RESULT, &type_pointer, &type_vector, &type_vector}, + + {7, "-=", "SUBSTORE_F", 6, ASSOC_RIGHT_RESULT, &type_float, &type_float, &type_float}, + {7, "-=", "SUBSTORE_V", 6, ASSOC_RIGHT_RESULT, &type_vector, &type_vector, &type_vector}, + {7, "-=", "SUBSTOREP_F", 6, ASSOC_RIGHT_RESULT, &type_pointer, &type_float, &type_float}, + {7, "-=", "SUBSTOREP_V", 6, ASSOC_RIGHT_RESULT, &type_pointer, &type_vector, &type_vector}, + + {7, "", "FETCH_GBL_F", -1, ASSOC_LEFT, &type_float, &type_float, &type_float}, + {7, "", "FETCH_GBL_V", -1, ASSOC_LEFT, &type_vector, &type_float, &type_vector}, + {7, "", "FETCH_GBL_S", -1, ASSOC_LEFT, &type_string, &type_float, &type_string}, + {7, "", "FETCH_GBL_E", -1, ASSOC_LEFT, &type_entity, &type_float, &type_entity}, + {7, "", "FETCH_GBL_FNC", -1, ASSOC_LEFT, &type_function, &type_float, &type_function}, + + {7, "", "CSTATE", -1, ASSOC_LEFT, &type_float, &type_float, &type_void}, + + {7, "", "CWSTATE", -1, ASSOC_LEFT, &type_float, &type_float, &type_void}, + + {7, "", "THINKTIME", -1, ASSOC_LEFT, &type_entity, &type_float, &type_void}, + + {7, "|=", "BITSET_F", 6, ASSOC_RIGHT, &type_float, &type_float, &type_float}, + {7, "|=", "BITSETP_F", 6, ASSOC_RIGHT, &type_pointer, &type_float, &type_float}, + {7, "&~=", "BITCLR_F", 6, ASSOC_RIGHT, &type_float, &type_float, &type_float}, + {7, "&~=", "BITCLRP_F", 6, ASSOC_RIGHT, &type_pointer, &type_float, &type_float}, + + {7, "", "RAND0", -1, ASSOC_LEFT, &type_void, &type_void, &type_float}, + {7, "", "RAND1", -1, ASSOC_LEFT, &type_float, &type_void, &type_float}, + {7, "", "RAND2", -1, ASSOC_LEFT, &type_float, &type_float, &type_float}, + {7, "", "RANDV0", -1, ASSOC_LEFT, &type_void, &type_void, &type_vector}, + {7, "", "RANDV1", -1, ASSOC_LEFT, &type_vector, &type_void, &type_vector}, + {7, "", "RANDV2", -1, ASSOC_LEFT, &type_vector, &type_vector, &type_vector}, + + {7, "", "SWITCH_F", -1, ASSOC_LEFT, &type_void, NULL, &type_void}, + {7, "", "SWITCH_V", -1, ASSOC_LEFT, &type_void, NULL, &type_void}, + {7, "", "SWITCH_S", -1, ASSOC_LEFT, &type_void, NULL, &type_void}, + {7, "", "SWITCH_E", -1, ASSOC_LEFT, &type_void, NULL, &type_void}, + {7, "", "SWITCH_FNC", -1, ASSOC_LEFT, &type_void, NULL, &type_void}, + + {7, "", "CASE", -1, ASSOC_LEFT, &type_void, NULL, &type_void}, + {7, "", "CASERANGE", -1, ASSOC_LEFT, &type_void, &type_void, NULL}, + + +//Later are additions by DMW. + + {7, "", "CALL1H", -1, ASSOC_LEFT, &type_function, &type_vector, &type_void}, + {7, "", "CALL2H", -1, ASSOC_LEFT, &type_function, &type_vector, &type_vector}, + {7, "", "CALL3H", -1, ASSOC_LEFT, &type_function, &type_vector, &type_vector}, + {7, "", "CALL4H", -1, ASSOC_LEFT, &type_function, &type_vector, &type_vector}, + {7, "", "CALL5H", -1, ASSOC_LEFT, &type_function, &type_vector, &type_vector}, + {7, "", "CALL6H", -1, ASSOC_LEFT, &type_function, &type_vector, &type_vector}, + {7, "", "CALL7H", -1, ASSOC_LEFT, &type_function, &type_vector, &type_vector}, + {7, "", "CALL8H", -1, ASSOC_LEFT, &type_function, &type_vector, &type_vector}, + + {7, "=", "STORE_I", 6, ASSOC_RIGHT, &type_integer, &type_integer, &type_integer}, + {7, "=", "STORE_IF", 6, ASSOC_RIGHT, &type_integer, &type_float, &type_integer}, + {7, "=", "STORE_FI", 6, ASSOC_RIGHT, &type_float, &type_integer, &type_float}, + + {7, "+", "ADD_I", 4, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, + {7, "+", "ADD_FI", 4, ASSOC_LEFT, &type_float, &type_integer, &type_float}, + {7, "+", "ADD_IF", 4, ASSOC_LEFT, &type_integer, &type_float, &type_float}, + + {7, "-", "SUB_I", 4, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, + {7, "-", "SUB_FI", 4, ASSOC_LEFT, &type_float, &type_integer, &type_float}, + {7, "-", "SUB_IF", 4, ASSOC_LEFT, &type_integer, &type_float, &type_float}, + + {7, "", "C_ITOF", -1, ASSOC_LEFT, &type_integer, &type_void, &type_float}, + {7, "", "C_FTOI", -1, ASSOC_LEFT, &type_float, &type_void, &type_integer}, + {7, "", "CP_ITOF", -1, ASSOC_LEFT, &type_pointer, &type_integer, &type_float}, + {7, "", "CP_FTOI", -1, ASSOC_LEFT, &type_pointer, &type_float, &type_integer}, + + {7, ".", "INDIRECT", 1, ASSOC_LEFT, &type_entity, &type_field, &type_integer}, + {7, "=", "STOREP_I", 6, ASSOC_RIGHT, &type_pointer, &type_integer, &type_integer}, + {7, "=", "STOREP_IF", 6, ASSOC_RIGHT, &type_pointer, &type_float, &type_integer}, + {7, "=", "STOREP_FI", 6, ASSOC_RIGHT, &type_pointer, &type_integer, &type_float}, + + {7, "&", "BITAND_I", 3, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, + {7, "|", "BITOR_I", 3, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, + + {7, "*", "MUL_I", 3, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, + {7, "/", "DIV_I", 3, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, + {7, "==", "EQ_I", 5, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, + {7, "!=", "NE_I", 5, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, + + {7, "", "IFNOTS", -1, ASSOC_RIGHT, &type_string, NULL, &type_void}, + {7, "", "IFS", -1, ASSOC_RIGHT, &type_string, NULL, &type_void}, + + {7, "!", "NOT_I", -1, ASSOC_LEFT, &type_integer, &type_void, &type_integer}, + + {7, "/", "DIV_VF", 3, ASSOC_LEFT, &type_vector, &type_float, &type_float}, + + {7, "^", "XOR_I", 3, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, + {7, ">>", "RSHIFT_I", 3, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, + {7, "<<", "LSHIFT_I", 3, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, + + //var, offset return + {7, "", "GET_POINTER", -1, ASSOC_LEFT, &type_float, &type_integer, &type_pointer}, + {7, "", "ARRAY_OFS", -1, ASSOC_LEFT, &type_pointer, &type_integer, &type_pointer}, + + {7, "=", "LOADA_F", 6, ASSOC_LEFT, &type_float, &type_integer, &type_float}, + {7, "=", "LOADA_V", 6, ASSOC_LEFT, &type_vector, &type_integer, &type_vector}, + {7, "=", "LOADA_S", 6, ASSOC_LEFT, &type_string, &type_integer, &type_string}, + {7, "=", "LOADA_ENT", 6, ASSOC_LEFT, &type_entity, &type_integer, &type_entity}, + {7, "=", "LOADA_FLD", 6, ASSOC_LEFT, &type_field, &type_integer, &type_field}, + {7, "=", "LOADA_FNC", 6, ASSOC_LEFT, &type_function, &type_integer, &type_function}, + {7, "=", "LOADA_I", 6, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, + + {7, "=", "STORE_P", 6, ASSOC_RIGHT, &type_pointer, &type_pointer, &type_void}, + {7, ".", "INDIRECT_P", 1, ASSOC_LEFT, &type_entity, &type_field, &type_pointer}, + + {7, "=", "LOADP_F", 6, ASSOC_LEFT, &type_pointer, &type_integer, &type_float}, + {7, "=", "LOADP_V", 6, ASSOC_LEFT, &type_pointer, &type_integer, &type_vector}, + {7, "=", "LOADP_S", 6, ASSOC_LEFT, &type_pointer, &type_integer, &type_string}, + {7, "=", "LOADP_ENT", 6, ASSOC_LEFT, &type_pointer, &type_integer, &type_entity}, + {7, "=", "LOADP_FLD", 6, ASSOC_LEFT, &type_pointer, &type_integer, &type_field}, + {7, "=", "LOADP_FNC", 6, ASSOC_LEFT, &type_pointer, &type_integer, &type_function}, + {7, "=", "LOADP_I", 6, ASSOC_LEFT, &type_pointer, &type_integer, &type_integer}, + + + {7, "<=", "LE_I", 5, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, + {7, ">=", "GE_I", 5, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, + {7, "<", "LT_I", 5, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, + {7, ">", "GT_I", 5, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, + + {7, "<=", "LE_IF", 5, ASSOC_LEFT, &type_integer, &type_float, &type_integer}, + {7, ">=", "GE_IF", 5, ASSOC_LEFT, &type_integer, &type_float, &type_integer}, + {7, "<", "LT_IF", 5, ASSOC_LEFT, &type_integer, &type_float, &type_integer}, + {7, ">", "GT_IF", 5, ASSOC_LEFT, &type_integer, &type_float, &type_integer}, + + {7, "<=", "LE_FI", 5, ASSOC_LEFT, &type_float, &type_integer, &type_integer}, + {7, ">=", "GE_FI", 5, ASSOC_LEFT, &type_float, &type_integer, &type_integer}, + {7, "<", "LT_FI", 5, ASSOC_LEFT, &type_float, &type_integer, &type_integer}, + {7, ">", "GT_FI", 5, ASSOC_LEFT, &type_float, &type_integer, &type_integer}, + + {7, "==", "EQ_IF", 5, ASSOC_LEFT, &type_integer, &type_float, &type_integer}, + {7, "==", "EQ_FI", 5, ASSOC_LEFT, &type_float, &type_integer, &type_float}, + + //------------------------------------- + //string manipulation. + {7, "+", "ADD_SF", 4, ASSOC_LEFT, &type_string, &type_float, &type_string}, + {7, "-", "SUB_S", 4, ASSOC_LEFT, &type_string, &type_string, &type_float}, + {7, "", "STOREP_C", 1, ASSOC_RIGHT, &type_string, &type_float, &type_float}, + {7, "", "LOADP_C", 1, ASSOC_LEFT, &type_string, &type_void, &type_float}, + //------------------------------------- + + + +{7, "*", "MUL_IF", 5, ASSOC_LEFT, &type_integer, &type_float, &type_integer}, +{7, "*", "MUL_FI", 5, ASSOC_LEFT, &type_float, &type_integer, &type_float}, +{7, "*", "MUL_VI", 5, ASSOC_LEFT, &type_vector, &type_integer, &type_vector}, +{7, "*", "MUL_IV", 5, ASSOC_LEFT, &type_integer, &type_vector, &type_vector}, + +{7, "/", "DIV_IF", 5, ASSOC_LEFT, &type_integer, &type_float, &type_integer}, +{7, "/", "DIV_FI", 5, ASSOC_LEFT, &type_float, &type_integer, &type_float}, + +{7, "&", "BITAND_IF", 5, ASSOC_LEFT, &type_integer, &type_float, &type_integer}, +{7, "|", "BITOR_IF", 5, ASSOC_LEFT, &type_integer, &type_float, &type_integer}, +{7, "&", "BITAND_FI", 5, ASSOC_LEFT, &type_float, &type_integer, &type_float}, +{7, "|", "BITOR_FI", 5, ASSOC_LEFT, &type_float, &type_integer, &type_float}, + +{7, "&&", "AND_I", 7, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, +{7, "||", "OR_I", 7, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, +{7, "&&", "AND_IF", 7, ASSOC_LEFT, &type_integer, &type_float, &type_integer}, +{7, "||", "OR_IF", 7, ASSOC_LEFT, &type_integer, &type_float, &type_integer}, +{7, "&&", "AND_FI", 7, ASSOC_LEFT, &type_float, &type_integer, &type_integer}, +{7, "||", "OR_FI", 7, ASSOC_LEFT, &type_float, &type_integer, &type_integer}, +{7, "!=", "NE_IF", 5, ASSOC_LEFT, &type_integer, &type_float, &type_integer}, +{7, "!=", "NE_FI", 5, ASSOC_LEFT, &type_float, &type_float, &type_integer}, + + + + + + +{7, "<>", "GSTOREP_I", -1, ASSOC_LEFT, &type_float, &type_float, &type_float}, +{7, "<>", "GSTOREP_F", -1, ASSOC_LEFT, &type_float, &type_float, &type_float}, +{7, "<>", "GSTOREP_ENT", -1, ASSOC_LEFT, &type_float, &type_float, &type_float}, +{7, "<>", "GSTOREP_FLD", -1, ASSOC_LEFT, &type_float, &type_float, &type_float}, +{7, "<>", "GSTOREP_S", -1, ASSOC_LEFT, &type_float, &type_float, &type_float}, +{7, "<>", "GSTOREP_FNC", -1, ASSOC_LEFT, &type_float, &type_float, &type_float}, +{7, "<>", "GSTOREP_V", -1, ASSOC_LEFT, &type_float, &type_float, &type_float}, + +{7, "<>", "GADDRESS", -1, ASSOC_LEFT, &type_float, &type_float, &type_float}, + +{7, "<>", "GLOAD_I", -1, ASSOC_LEFT, &type_float, &type_float, &type_float}, +{7, "<>", "GLOAD_F", -1, ASSOC_LEFT, &type_float, &type_float, &type_float}, +{7, "<>", "GLOAD_FLD", -1, ASSOC_LEFT, &type_float, &type_float, &type_float}, +{7, "<>", "GLOAD_ENT", -1, ASSOC_LEFT, &type_float, &type_float, &type_float}, +{7, "<>", "GLOAD_S", -1, ASSOC_LEFT, &type_float, &type_float, &type_float}, +{7, "<>", "GLOAD_FNC", -1, ASSOC_LEFT, &type_float, &type_float, &type_float}, + +{7, "<>", "BOUNDCHECK", -1, ASSOC_LEFT, &type_float, &type_float, &type_float}, + +{7, "=", "STOREP_P", 6, ASSOC_RIGHT, &type_pointer, &type_pointer, &type_void}, +{7, "", "PUSH", -1, ASSOC_RIGHT, &type_float, &type_void, &type_pointer}, +{7, "", "POP", -1, ASSOC_RIGHT, &type_float, &type_void, &type_void}, + +{7, "", "SWITCH_I", -1, ASSOC_LEFT, &type_void, NULL, &type_void}, +{7, "<>", "GLOAD_S", -1, ASSOC_LEFT, &type_float, &type_float, &type_float}, + +{6, "", "IF_F", -1, ASSOC_RIGHT, &type_float, NULL, &type_void}, +{6, "","IFNOT_F", -1, ASSOC_RIGHT, &type_float, NULL, &type_void}, + +/* emulated ops begin here */ + {7, "<>", "OP_EMULATED", -1, ASSOC_LEFT, &type_float, &type_float, &type_float}, + + + {7, "|=", "BITSET_I", 6, ASSOC_RIGHT, &type_integer, &type_integer, &type_integer}, + {7, "|=", "BITSETP_I", 6, ASSOC_RIGHT, &type_pointer, &type_integer, &type_integer}, + + + {7, "*=", "MULSTORE_I", 6, ASSOC_RIGHT_RESULT, &type_integer, &type_integer, &type_integer}, + {7, "/=", "DIVSTORE_I", 6, ASSOC_RIGHT_RESULT, &type_integer, &type_integer, &type_integer}, + {7, "+=", "ADDSTORE_I", 6, ASSOC_RIGHT_RESULT, &type_integer, &type_integer, &type_integer}, + {7, "-=", "SUBSTORE_I", 6, ASSOC_RIGHT_RESULT, &type_integer, &type_integer, &type_integer}, + + {7, "*=", "MULSTOREP_I", 6, ASSOC_RIGHT_RESULT, &type_pointer, &type_integer, &type_integer}, + {7, "/=", "DIVSTOREP_I", 6, ASSOC_RIGHT_RESULT, &type_pointer, &type_integer, &type_integer}, + {7, "+=", "ADDSTOREP_I", 6, ASSOC_RIGHT_RESULT, &type_pointer, &type_integer, &type_integer}, + {7, "-=", "SUBSTOREP_I", 6, ASSOC_RIGHT_RESULT, &type_pointer, &type_integer, &type_integer}, + + {7, "*=", "OP_MULSTORE_IF", 6, ASSOC_RIGHT_RESULT, &type_pointer, &type_float, &type_integer}, + {7, "*=", "OP_MULSTOREP_IF", 6, ASSOC_RIGHT_RESULT, &type_pointer, &type_float, &type_integer}, + {7, "/=", "OP_DIVSTORE_IF", 6, ASSOC_RIGHT_RESULT, &type_pointer, &type_float, &type_integer}, + {7, "/=", "OP_DIVSTOREP_IF", 6, ASSOC_RIGHT_RESULT, &type_pointer, &type_float, &type_integer}, + {7, "+=", "OP_ADDSTORE_IF", 6, ASSOC_RIGHT_RESULT, &type_pointer, &type_float, &type_integer}, + {7, "+=", "OP_ADDSTOREP_IF", 6, ASSOC_RIGHT_RESULT, &type_pointer, &type_float, &type_integer}, + {7, "-=", "OP_SUBSTORE_IF", 6, ASSOC_RIGHT_RESULT, &type_pointer, &type_float, &type_integer}, + {7, "-=", "OP_SUBSTOREP_IF", 6, ASSOC_RIGHT_RESULT, &type_pointer, &type_float, &type_integer}, + + {7, "*=", "OP_MULSTORE_FI", 6, ASSOC_RIGHT_RESULT, &type_pointer, &type_integer, &type_float}, + {7, "*=", "OP_MULSTOREP_FI", 6, ASSOC_RIGHT_RESULT, &type_pointer, &type_integer, &type_float}, + {7, "/=", "OP_DIVSTORE_FI", 6, ASSOC_RIGHT_RESULT, &type_pointer, &type_integer, &type_float}, + {7, "/=", "OP_DIVSTOREP_FI", 6, ASSOC_RIGHT_RESULT, &type_pointer, &type_integer, &type_float}, + {7, "+=", "OP_ADDSTORE_FI", 6, ASSOC_RIGHT_RESULT, &type_pointer, &type_integer, &type_float}, + {7, "+=", "OP_ADDSTOREP_FI", 6, ASSOC_RIGHT_RESULT, &type_pointer, &type_integer, &type_float}, + {7, "-=", "OP_SUBSTORE_FI", 6, ASSOC_RIGHT_RESULT, &type_pointer, &type_integer, &type_float}, + {7, "-=", "OP_SUBSTOREP_FI", 6, ASSOC_RIGHT_RESULT, &type_pointer, &type_integer, &type_float}, + + {0, NULL} +}; + + +pbool OpAssignsToC(unsigned int op) +{ + // calls, switches and cases DON'T + if(pr_opcodes[op].type_c == &type_void) + return false; + if(op >= OP_SWITCH_F && op <= OP_CALL8H) + return false; + if(op >= OP_RAND0 && op <= OP_RANDV2) + return false; + // they use a and b, but have 3 types + // safety + if(op >= OP_BITSET && op <= OP_BITCLRP) + return false; + /*if(op >= OP_STORE_I && op <= OP_STORE_FI) + return false; <- add STOREP_*?*/ + if(op == OP_STOREP_C || op == OP_LOADP_C) + return false; + if(op >= OP_MULSTORE_F && op <= OP_SUBSTOREP_V) + return false; + return true; +} +pbool OpAssignsToB(unsigned int op) +{ + if(op >= OP_BITSET && op <= OP_BITCLRP) + return true; + if(op >= OP_STORE_I && op <= OP_STORE_FI) + return true; + if(op == OP_STOREP_C || op == OP_LOADP_C) + return true; + if(op >= OP_MULSTORE_F && op <= OP_SUBSTOREP_V) + return true; + if(op >= OP_STORE_F && op <= OP_STOREP_FNC) + return true; + return false; +} +/*pbool OpAssignedTo(QCC_def_t *v, unsigned int op) +{ + if(OpAssignsToC(op)) + { + } + else if(OpAssignsToB(op)) + { + } + return false; +} +*/ +#undef ASSOC_RIGHT_RESULT + +#define TOP_PRIORITY 7 +#define UNARY_PRIORITY 1 +#define NOT_PRIORITY 5 +//conditional and/or +#define CONDITION_PRIORITY 7 + + +//this system cuts out 10/120 +//these evaluate as top first. +QCC_opcode_t *opcodeprioritized[TOP_PRIORITY+1][128] = +{ + { //don't use +/* &pr_opcodes[OP_DONE], + &pr_opcodes[OP_RETURN], + + &pr_opcodes[OP_NOT_F], + &pr_opcodes[OP_NOT_V], + &pr_opcodes[OP_NOT_S], + &pr_opcodes[OP_NOT_ENT], + &pr_opcodes[OP_NOT_FNC], + + &pr_opcodes[OP_IF], + &pr_opcodes[OP_IFNOT], + &pr_opcodes[OP_CALL0], + &pr_opcodes[OP_CALL1], + &pr_opcodes[OP_CALL2], + &pr_opcodes[OP_CALL3], + &pr_opcodes[OP_CALL4], + &pr_opcodes[OP_CALL5], + &pr_opcodes[OP_CALL6], + &pr_opcodes[OP_CALL7], + &pr_opcodes[OP_CALL8], + &pr_opcodes[OP_STATE], + &pr_opcodes[OP_GOTO], + + &pr_opcodes[OP_IFNOTS], + &pr_opcodes[OP_IFS], + + &pr_opcodes[OP_NOT_I], +*/ NULL + }, { //1 + + &pr_opcodes[OP_LOAD_F], + &pr_opcodes[OP_LOAD_V], + &pr_opcodes[OP_LOAD_S], + &pr_opcodes[OP_LOAD_ENT], + &pr_opcodes[OP_LOAD_FLD], + &pr_opcodes[OP_LOAD_FNC], + &pr_opcodes[OP_LOAD_I], + &pr_opcodes[OP_LOAD_P], + &pr_opcodes[OP_ADDRESS], + NULL + }, { //2 +/* //conversion. don't use + &pr_opcodes[OP_C_ITOF], + &pr_opcodes[OP_C_FTOI], + &pr_opcodes[OP_CP_ITOF], + &pr_opcodes[OP_CP_FTOI], +*/ NULL + }, { //3 + &pr_opcodes[OP_MUL_F], + &pr_opcodes[OP_MUL_V], + &pr_opcodes[OP_MUL_FV], + &pr_opcodes[OP_MUL_VF], + &pr_opcodes[OP_MUL_I], + + &pr_opcodes[OP_DIV_F], + &pr_opcodes[OP_DIV_I], + &pr_opcodes[OP_DIV_VF], + + &pr_opcodes[OP_BITAND], + &pr_opcodes[OP_BITAND_I], + &pr_opcodes[OP_BITAND_IF], + &pr_opcodes[OP_BITAND_FI], + + &pr_opcodes[OP_BITOR], + &pr_opcodes[OP_BITOR_I], + &pr_opcodes[OP_BITOR_IF], + &pr_opcodes[OP_BITOR_FI], + + &pr_opcodes[OP_XOR_I], + &pr_opcodes[OP_RSHIFT_I], + &pr_opcodes[OP_LSHIFT_I], + + NULL + }, { //4 + + &pr_opcodes[OP_ADD_F], + &pr_opcodes[OP_ADD_V], + &pr_opcodes[OP_ADD_I], + &pr_opcodes[OP_ADD_FI], + &pr_opcodes[OP_ADD_IF], + &pr_opcodes[OP_ADD_SF], + + &pr_opcodes[OP_SUB_F], + &pr_opcodes[OP_SUB_V], + &pr_opcodes[OP_SUB_I], + &pr_opcodes[OP_SUB_FI], + &pr_opcodes[OP_SUB_IF], + &pr_opcodes[OP_SUB_S], + NULL + }, { //5 + + &pr_opcodes[OP_EQ_F], + &pr_opcodes[OP_EQ_V], + &pr_opcodes[OP_EQ_S], + &pr_opcodes[OP_EQ_E], + &pr_opcodes[OP_EQ_FNC], + &pr_opcodes[OP_EQ_I], + &pr_opcodes[OP_EQ_IF], + &pr_opcodes[OP_EQ_FI], + + &pr_opcodes[OP_NE_F], + &pr_opcodes[OP_NE_V], + &pr_opcodes[OP_NE_S], + &pr_opcodes[OP_NE_E], + &pr_opcodes[OP_NE_FNC], + &pr_opcodes[OP_NE_I], + &pr_opcodes[OP_NE_IF], + &pr_opcodes[OP_NE_FI], + + &pr_opcodes[OP_LE], + &pr_opcodes[OP_LE_I], + &pr_opcodes[OP_LE_IF], + &pr_opcodes[OP_LE_FI], + &pr_opcodes[OP_GE], + &pr_opcodes[OP_GE_I], + &pr_opcodes[OP_GE_IF], + &pr_opcodes[OP_GE_FI], + &pr_opcodes[OP_LT], + &pr_opcodes[OP_LT_I], + &pr_opcodes[OP_LT_IF], + &pr_opcodes[OP_LT_FI], + &pr_opcodes[OP_GT], + &pr_opcodes[OP_GT_I], + &pr_opcodes[OP_GT_IF], + &pr_opcodes[OP_GT_FI], + + NULL + }, { //6 + &pr_opcodes[OP_STORE_F], + &pr_opcodes[OP_STORE_V], + &pr_opcodes[OP_STORE_S], + &pr_opcodes[OP_STORE_ENT], + &pr_opcodes[OP_STORE_FLD], + &pr_opcodes[OP_STORE_FNC], + &pr_opcodes[OP_STORE_I], + &pr_opcodes[OP_STORE_IF], + &pr_opcodes[OP_STORE_FI], + &pr_opcodes[OP_STORE_P], + + &pr_opcodes[OP_STOREP_F], + &pr_opcodes[OP_STOREP_V], + &pr_opcodes[OP_STOREP_S], + &pr_opcodes[OP_STOREP_ENT], + &pr_opcodes[OP_STOREP_FLD], + &pr_opcodes[OP_STOREP_FNC], + &pr_opcodes[OP_STOREP_I], + &pr_opcodes[OP_STOREP_IF], + &pr_opcodes[OP_STOREP_FI], + &pr_opcodes[OP_STOREP_P], + + &pr_opcodes[OP_DIVSTORE_F], + &pr_opcodes[OP_DIVSTORE_I], + &pr_opcodes[OP_DIVSTORE_FI], + &pr_opcodes[OP_DIVSTORE_IF], + &pr_opcodes[OP_DIVSTOREP_F], + &pr_opcodes[OP_DIVSTOREP_I], + &pr_opcodes[OP_DIVSTOREP_IF], + &pr_opcodes[OP_DIVSTOREP_FI], + &pr_opcodes[OP_MULSTORE_F], + &pr_opcodes[OP_MULSTORE_V], + &pr_opcodes[OP_MULSTORE_I], + &pr_opcodes[OP_MULSTORE_IF], + &pr_opcodes[OP_MULSTORE_FI], + &pr_opcodes[OP_MULSTOREP_F], + &pr_opcodes[OP_MULSTOREP_V], + &pr_opcodes[OP_MULSTOREP_I], + &pr_opcodes[OP_MULSTOREP_IF], + &pr_opcodes[OP_MULSTOREP_FI], + &pr_opcodes[OP_ADDSTORE_F], + &pr_opcodes[OP_ADDSTORE_V], + &pr_opcodes[OP_ADDSTORE_I], + &pr_opcodes[OP_ADDSTORE_IF], + &pr_opcodes[OP_ADDSTORE_FI], + &pr_opcodes[OP_ADDSTOREP_F], + &pr_opcodes[OP_ADDSTOREP_V], + &pr_opcodes[OP_ADDSTOREP_I], + &pr_opcodes[OP_ADDSTOREP_IF], + &pr_opcodes[OP_ADDSTOREP_FI], + &pr_opcodes[OP_SUBSTORE_F], + &pr_opcodes[OP_SUBSTORE_V], + &pr_opcodes[OP_SUBSTORE_I], + &pr_opcodes[OP_SUBSTORE_IF], + &pr_opcodes[OP_SUBSTORE_FI], + &pr_opcodes[OP_SUBSTOREP_F], + &pr_opcodes[OP_SUBSTOREP_V], + &pr_opcodes[OP_SUBSTOREP_I], + &pr_opcodes[OP_SUBSTOREP_IF], + &pr_opcodes[OP_SUBSTOREP_FI], + + &pr_opcodes[OP_BITSET], + &pr_opcodes[OP_BITSET_I], +// &pr_opcodes[OP_BITSET_IF], +// &pr_opcodes[OP_BITSET_FI], + &pr_opcodes[OP_BITSETP], + &pr_opcodes[OP_BITSETP_I], +// &pr_opcodes[OP_BITSETP_IF], +// &pr_opcodes[OP_BITSETP_FI], + &pr_opcodes[OP_BITCLR], + &pr_opcodes[OP_BITCLRP], + + NULL + }, { //7 + &pr_opcodes[OP_AND], + &pr_opcodes[OP_AND_I], + &pr_opcodes[OP_AND_IF], + &pr_opcodes[OP_AND_FI], + &pr_opcodes[OP_OR], + &pr_opcodes[OP_OR_I], + &pr_opcodes[OP_OR_IF], + &pr_opcodes[OP_OR_FI], + NULL + } +}; + +pbool QCC_OPCodeValid(QCC_opcode_t *op) +{ + int num; + num = op - pr_opcodes; + + switch(qcc_targetformat) + { + case QCF_STANDARD: + case QCF_KK7: + if (num < OP_MULSTORE_F) + return true; + return false; + case QCF_HEXEN2: + if (num >= OP_SWITCH_V && num <= OP_SWITCH_FNC) //these were assigned numbers but were never actually implemtented in standard h2. + return false; +// if (num >= OP_MULSTORE_F && num <= OP_SUBSTOREP_V) +// return false; + if (num <= OP_CALL8H) //CALLXH are fixed up. This is to provide more dynamic switching...?? + return true; + return false; + case QCF_FTE: + case QCF_FTEDEBUG: + //no emulated opcodes + if (num >= OP_NUMREALOPS) + return false; + return true; + case QCF_DARKPLACES: + //all id opcodes. + if (num < OP_MULSTORE_F) + return true; + + //no emulated opcodes + if (num >= OP_NUMREALOPS) + return false; + + //extended opcodes. + //DPFIXME: this is a list of the extended opcodes. I was conservative regarding supported ones. + // at the time of writing, these are the ones that look like they'll work just fine in Blub\0's patch. + // the ones that looked too permissive with bounds checks, or would give false positives are disabled. + // if the DP guys want I can change them as desired. + switch(num) + { + //maths and conditionals (simple opcodes that read from specific globals and write to a global) + case OP_ADD_I: + case OP_ADD_IF: + case OP_ADD_FI: + case OP_SUB_I: + case OP_SUB_IF: + case OP_SUB_FI: + case OP_MUL_I: + case OP_MUL_IF: + case OP_MUL_FI: + case OP_MUL_VI: + case OP_DIV_VF: + case OP_DIV_I: + case OP_DIV_IF: + case OP_DIV_FI: + case OP_BITAND_I: + case OP_BITOR_I: + case OP_BITAND_IF: + case OP_BITOR_IF: + case OP_BITAND_FI: + case OP_BITOR_FI: + case OP_GE_I: + case OP_LE_I: + case OP_GT_I: + case OP_LT_I: + case OP_AND_I: + case OP_OR_I: + case OP_GE_IF: + case OP_LE_IF: + case OP_GT_IF: + case OP_LT_IF: + case OP_AND_IF: + case OP_OR_IF: + case OP_GE_FI: + case OP_LE_FI: + case OP_GT_FI: + case OP_LT_FI: + case OP_AND_FI: + case OP_OR_FI: + case OP_NOT_I: + case OP_EQ_I: + case OP_EQ_IF: + case OP_EQ_FI: + case OP_NE_I: + case OP_NE_IF: + case OP_NE_FI: + return true; + + //stores into a pointer (generated from 'ent.field=XXX') + case OP_STOREP_I: //no worse than the other OP_STOREP_X functions + case OP_STOREP_P: + //reads from an entity field + case OP_LOAD_I: //no worse than the other OP_LOAD_X functions. + case OP_LOAD_P: + return true; + + //stores into the globals array. + //they can change any global dynamically, but thats no security risk. + //fteqcc will not automatically generate these. + //fteqw does not support them either. + case OP_GSTOREP_I: + case OP_GSTOREP_F: + case OP_GSTOREP_ENT: + case OP_GSTOREP_FLD: + case OP_GSTOREP_S: + case OP_GSTOREP_FNC: + case OP_GSTOREP_V: + return true; + + //this opcode looks weird + case OP_GADDRESS://floatc = globals[inta + floatb] (fte does not support) + return true; + + //fteqcc will not automatically generate these + //fteqw does not support them either, for that matter. + case OP_GLOAD_I://c = globals[inta] + case OP_GLOAD_F://note: fte does not support these + case OP_GLOAD_FLD: + case OP_GLOAD_ENT: + case OP_GLOAD_S: + case OP_GLOAD_FNC: + return true; + case OP_GLOAD_V: + return false; //DPFIXME: this is commented out in the patch I was given a link to... because the opcode wasn't defined. + + //these are reportedly functional. + case OP_CALL8H: + case OP_CALL7H: + case OP_CALL6H: + case OP_CALL5H: + case OP_CALL4H: + case OP_CALL3H: + case OP_CALL2H: + case OP_CALL1H: + return true; + + case OP_RAND0: + case OP_RAND1: + case OP_RAND2: + case OP_RANDV0: + case OP_RANDV1: + case OP_RANDV2: + return true; + + case OP_BITSET: // b |= a + case OP_BITCLR: // b &= ~a + case OP_BITSETP: // *b |= a + case OP_BITCLRP: // *b &= ~a + return false; //FIXME: I do not fully follow the controversy over these. + + case OP_SWITCH_F: + case OP_SWITCH_V: + case OP_SWITCH_S: + case OP_SWITCH_E: + case OP_SWITCH_FNC: + case OP_CASE: + case OP_CASERANGE: + return true; + + //assuming the pointers here are fine, the return values are a little strange. + //but its fine + case OP_ADDSTORE_F: + case OP_ADDSTORE_V: + case OP_ADDSTOREP_F: // e.f += f + case OP_ADDSTOREP_V: // e.v += v + case OP_SUBSTORE_F: + case OP_SUBSTORE_V: + case OP_SUBSTOREP_F: // e.f += f + case OP_SUBSTOREP_V: // e.v += v + return true; + + case OP_LOADA_I: + case OP_LOADA_F: + case OP_LOADA_FLD: + case OP_LOADA_ENT: + case OP_LOADA_S: + case OP_LOADA_FNC: + case OP_LOADA_V: + return false; //DPFIXME: DP does not bounds check these properly. I won't generate them. + + case OP_CONV_ITOF: + case OP_CONV_FTOI: + return true; //these look fine. + + case OP_STOREP_C: // store a char in a string + return false; //DPFIXME: dp's bounds check may give false positives with expected uses. + + case OP_MULSTORE_F: + case OP_MULSTORE_V: + case OP_MULSTOREP_F: + case OP_MULSTOREP_V: // e.v *= f + case OP_DIVSTORE_F: + case OP_DIVSTOREP_F: + case OP_STORE_IF: + case OP_STORE_FI: + case OP_STORE_P: + case OP_STOREP_IF: // store a value to a pointer + case OP_STOREP_FI: + case OP_IFNOT_S: + case OP_IF_S: + return true; + + case OP_IFNOT_F: //added, but not in dp yet + case OP_IF_F: + return false; + + case OP_CP_ITOF: + case OP_CP_FTOI: + return false; //DPFIXME: These are not bounds checked at all. + case OP_GLOBALADDRESS: + return true; //DPFIXME: DP will reject these pointers if they are ever used. + case OP_POINTER_ADD: + return true; //just maths. + + case OP_ADD_SF: //(char*)c = (char*)a + (float)b + case OP_SUB_S: //(float)c = (char*)a - (char*)b + return true; + case OP_LOADP_C: //load character from a string + return false; //DPFIXME: DP looks like it'll reject these or wrongly allow. + + case OP_LOADP_I: + case OP_LOADP_F: + case OP_LOADP_FLD: + case OP_LOADP_ENT: + case OP_LOADP_S: + case OP_LOADP_FNC: + case OP_LOADP_V: + return true; + + case OP_XOR_I: + case OP_RSHIFT_I: + case OP_LSHIFT_I: + return true; + + case OP_FETCH_GBL_F: + case OP_FETCH_GBL_S: + case OP_FETCH_GBL_E: + case OP_FETCH_GBL_FNC: + case OP_FETCH_GBL_V: + return false; //DPFIXME: DP will not bounds check this properly, it is too permissive. + case OP_CSTATE: + case OP_CWSTATE: + return false; //DP does not support this hexenc opcode. + case OP_THINKTIME: + return true; //but it does support this one. + + default: //anything I forgot to mention is new. + return false; + } + } + return false; +} + +#define EXPR_WARN_ABOVE_1 2 +#define EXPR_DISALLOW_COMMA 4 +QCC_def_t *QCC_PR_Expression (int priority, int exprflags); +int QCC_AStatementJumpsTo(int targ, int first, int last); +pbool QCC_StatementIsAJump(int stnum, int notifdest); + +temp_t *functemps; //floats/strings/funcs/ents... + +//=========================================================================== + + +/* +============ +PR_Statement + +Emits a primitive statement, returning the var it places it's value in +============ +*/ +QCC_def_t *QCC_PR_Statement ( QCC_opcode_t *op, QCC_def_t *var_a, QCC_def_t *var_b, QCC_dstatement_t **outstatement); +static int QCC_ShouldConvert(QCC_def_t *var, etype_t wanted) +{ + if (var->type->type == ev_integer && wanted == ev_function) + return 0; + if (var->type->type == ev_pointer && var->type->aux_type) + { + if (var->type->aux_type->type == ev_float && wanted == ev_integer) + return OP_CP_FTOI; + + if (var->type->aux_type->type == ev_integer && wanted == ev_float) + return OP_CP_ITOF; + } + else + { + if (var->type->type == ev_float && wanted == ev_integer) + return OP_CONV_FTOI; + + if (var->type->type == ev_integer && wanted == ev_float) + return OP_CONV_ITOF; + } + + return -1; +} +QCC_def_t *QCC_SupplyConversion(QCC_def_t *var, etype_t wanted) +{ + int o; + + if (pr_classtype && var->type->type == ev_field && wanted != ev_field) + { + if (pr_classtype) + { //load self.var into a temp + QCC_def_t *self; + self = QCC_PR_GetDef(type_entity, "self", NULL, true, 1, false); + switch(wanted) + { + case ev_float: + return QCC_PR_Statement(pr_opcodes+OP_LOAD_F, self, var, NULL); + case ev_string: + return QCC_PR_Statement(pr_opcodes+OP_LOAD_S, self, var, NULL); + case ev_function: + return QCC_PR_Statement(pr_opcodes+OP_LOAD_FNC, self, var, NULL); + case ev_vector: + return QCC_PR_Statement(pr_opcodes+OP_LOAD_V, self, var, NULL); + case ev_entity: + return QCC_PR_Statement(pr_opcodes+OP_LOAD_ENT, self, var, NULL); + default: + QCC_Error(ERR_INTERNAL, "Inexplicit field load failed, try explicit"); + } + } + } + + o = QCC_ShouldConvert(var, wanted); + + if (o <= 0) //no conversion + return var; + + + return QCC_PR_Statement(&pr_opcodes[o], var, NULL, NULL); //conversion return value +} +QCC_def_t *QCC_MakeStringDef(char *value); +QCC_def_t *QCC_MakeFloatDef(float value); +QCC_def_t *QCC_MakeIntDef(int value); +QCC_def_t *QCC_MakeVectorDef(float a, float b, float c); + +typedef struct freeoffset_s { + struct freeoffset_s *next; + gofs_t ofs; + unsigned int size; +} freeoffset_t; + +freeoffset_t *freeofs; + +//assistant functions. This can safly be bipassed with the old method for more complex things. +gofs_t QCC_GetFreeOffsetSpace(unsigned int size) +{ + int ofs; + if (opt_locals_marshalling) + { + freeoffset_t *fofs, *prev; + for (fofs = freeofs, prev = NULL; fofs; fofs=fofs->next) + { + if (fofs->size == size) + { + if (prev) + prev->next = fofs->next; + else + freeofs = fofs->next; + + return fofs->ofs; + } + prev = fofs; + } + for (fofs = freeofs, prev = NULL; fofs; fofs=fofs->next) + { + if (fofs->size > size) + { + fofs->size -= size; + fofs->ofs += size; + + return fofs->ofs-size; + } + prev = fofs; + } + } + + ofs = numpr_globals; + numpr_globals+=size; + + if (numpr_globals >= MAX_REGS) + { + if (!opt_overlaptemps || !opt_locals_marshalling) + QCC_Error(ERR_TOOMANYGLOBALS, "numpr_globals exceeded MAX_REGS - you'll need to use more optimisations"); + else + QCC_Error(ERR_TOOMANYGLOBALS, "numpr_globals exceeded MAX_REGS"); + } + + return ofs; +} + +void QCC_FreeOffset(gofs_t ofs, unsigned int size) +{ + freeoffset_t *fofs; + if (ofs+size == numpr_globals) + { //fixme: is this a bug? + numpr_globals -= size; + return; + } + + for (fofs = freeofs; fofs; fofs=fofs->next) + { + //fixme: if this means the last block becomes free, free them all. + if (fofs->ofs == ofs + size) + { + fofs->ofs -= size; + fofs->size += size; + return; + } + if (fofs->ofs+fofs->size == ofs) + { + fofs->size += size; + return; + } + } + + fofs = qccHunkAlloc(sizeof(freeoffset_t)); + fofs->next = freeofs; + fofs->ofs = ofs; + fofs->size = size; + + freeofs = fofs; + return; +} + +static QCC_def_t *QCC_GetTemp(QCC_type_t *type) +{ +//#define CRAZYTEMPOPTS //not worth it. saves 2 temps with hexen2 (without even touching numpr_globals) + QCC_def_t *var_c; + temp_t *t; +#ifdef CRAZYTEMPOPTS + temp_t *best = NULL; +#endif + + var_c = (void *)qccHunkAlloc (sizeof(QCC_def_t)); + memset (var_c, 0, sizeof(QCC_def_t)); + var_c->type = type; + var_c->name = "temp"; + + if (opt_overlaptemps) //don't exceed. This lets us allocate a huge block, and still be able to compile smegging big funcs. + { + for (t = functemps; t; t = t->next) + { + if (!t->used && t->size == type->size) + { +#ifdef CRAZYTEMPOPTS + best = t; + if (t->scope == pr_scope) +#endif + break; + } + } +#ifdef CRAZYTEMPOPTS + t = best; +#endif + if (t && t->scope && t->scope != pr_scope) + QCC_Error(ERR_INTERNAL, "Internal error temp has scope not equal to current scope"); + + if (!t) + { + //allocate a new one + t = qccHunkAlloc(sizeof(temp_t)); + t->size = type->size; + t->next = functemps; + functemps = t; + + t->ofs = QCC_GetFreeOffsetSpace(t->size); + + numtemps+=t->size; + } + else + optres_overlaptemps+=t->size; + //use a previous one. + var_c->ofs = t->ofs; + var_c->temp = t; + t->lastfunc = pr_scope; + } + else if (opt_locals_marshalling) + { + //allocate a new one + t = qccHunkAlloc(sizeof(temp_t)); + t->size = type->size; + + t->next = functemps; + functemps = t; + + t->ofs = QCC_GetFreeOffsetSpace(t->size); + + numtemps+=t->size; + + var_c->ofs = t->ofs; + var_c->temp = t; + t->lastfunc = pr_scope; + } + else + { + // we're not going to reallocate any temps so allocate permanently + var_c->ofs = QCC_GetFreeOffsetSpace(type->size); + numtemps+=type->size; + } + + var_c->s_file = s_file; + var_c->s_line = pr_source_line; + + if (var_c->temp) + var_c->temp->used = true; + + return var_c; +} + +//nothing else references this temp. +static void QCC_FreeTemp(QCC_def_t *t) +{ + if (t && t->temp) + t->temp->used = false; +} + +static void QCC_UnFreeTemp(QCC_def_t *t) +{ + if (t->temp) + t->temp->used = true; +} + +//We've just parsed a statement. +//We can gaurentee that any used temps are now not used. +#ifdef _DEBUG +static void QCC_FreeTemps(void) +{ + temp_t *t; + + if (def_ret.temp && def_ret.temp->used) + { + QCC_PR_ParseWarning(WARN_DEBUGGING, "Return value still in use in %s", pr_scope->name); + def_ret.temp->used = false; + } + + t = functemps; + while(t) + { + if (t->used && !pr_error_count) //don't print this after an error jump out. + { + QCC_PR_ParseWarning(WARN_DEBUGGING, "Temp was used in %s", pr_scope->name); + t->used = false; + } + t = t->next; + } +} +#else +#define QCC_FreeTemps() +#endif + +//temps that are still in use over a function call can be considered dodgy. +//we need to remap these to locally defined temps, on return from the function so we know we got them all. +static void QCC_LockActiveTemps(void) +{ + temp_t *t; + + t = functemps; + while(t) + { + if (t->used) + t->scope = pr_scope; + t = t->next; + } +} + +static void QCC_LockTemp(QCC_def_t *d) +{ + if (d->temp && d->temp->used) + d->temp->scope = pr_scope; +} + +static void QCC_RemapLockedTemp(temp_t *t, int firststatement, int laststatement) +{ +#ifdef WRITEASM + char buffer[128]; +#endif + + QCC_def_t *def; + int newofs; + QCC_dstatement_t *st; + int i; + + newofs = 0; + for (i = firststatement, st = &statements[i]; i < laststatement; i++, st++) + { + if (pr_opcodes[st->op].type_a && st->a == t->ofs) + { + if (!newofs) + { + newofs = QCC_GetFreeOffsetSpace(t->size); + numtemps+=t->size; + + def = QCC_PR_DummyDef(type_float, NULL, pr_scope, t->size, newofs, false, false); + def->nextlocal = pr.localvars; + def->constant = false; +#ifdef WRITEASM + sprintf(buffer, "locked_%i", t->ofs); + def->name = qccHunkAlloc(strlen(buffer)+1); + strcpy(def->name, buffer); +#endif + pr.localvars = def; + } + st->a = newofs; + } + if (pr_opcodes[st->op].type_b && st->b == t->ofs) + { + if (!newofs) + { + newofs = QCC_GetFreeOffsetSpace(t->size); + numtemps+=t->size; + + def = QCC_PR_DummyDef(type_float, NULL, pr_scope, t->size, newofs, false, false); + def->nextlocal = pr.localvars; + def->constant = false; +#ifdef WRITEASM + sprintf(buffer, "locked_%i", t->ofs); + def->name = qccHunkAlloc(strlen(buffer)+1); + strcpy(def->name, buffer); +#endif + pr.localvars = def; + } + st->b = newofs; + } + if (pr_opcodes[st->op].type_c && st->c == t->ofs) + { + if (!newofs) + { + newofs = QCC_GetFreeOffsetSpace(t->size); + numtemps+=t->size; + + def = QCC_PR_DummyDef(type_float, NULL, pr_scope, t->size, newofs, false, false); + def->nextlocal = pr.localvars; + def->constant = false; +#ifdef WRITEASM + sprintf(buffer, "locked_%i", t->ofs); + def->name = qccHunkAlloc(strlen(buffer)+1); + strcpy(def->name, buffer); +#endif + pr.localvars = def; + } + st->c = newofs; + } + } +} + +static void QCC_RemapLockedTemps(int firststatement, int laststatement) +{ + temp_t *t; + + t = functemps; + while(t) + { + if (t->scope || opt_locals_marshalling) + { + QCC_RemapLockedTemp(t, firststatement, laststatement); + t->scope = NULL; + t->lastfunc = NULL; + } + t = t->next; + } +} + +static void QCC_fprintfLocals(FILE *f, gofs_t paramstart, gofs_t paramend) +{ + QCC_def_t *var; + temp_t *t; + int i; + + for (var = pr.localvars; var; var = var->nextlocal) + { + if (var->ofs >= paramstart && var->ofs < paramend) + continue; + fprintf(f, "local %s %s;\n", TypeName(var->type), var->name); + } + + for (t = functemps, i = 0; t; t = t->next, i++) + { + if (t->lastfunc == pr_scope) + { + fprintf(f, "local %s temp_%i;\n", (t->size == 1)?"float":"vector", i); + } + } +} + +#ifdef WRITEASM +void QCC_WriteAsmFunction(QCC_def_t *sc, unsigned int firststatement, gofs_t firstparm); +static const char *QCC_VarAtOffset(unsigned int ofs, unsigned int size) +{ + static char message[1024]; + QCC_def_t *var; + //check the temps + temp_t *t; + int i; + + for (t = functemps, i = 0; t; t = t->next, i++) + { + if (ofs >= t->ofs && ofs < t->ofs + t->size) + { + if (size < t->size) + sprintf(message, "temp_%i_%c", i, 'x' + (ofs-t->ofs)%3); + else + sprintf(message, "temp_%i", i); + return message; + } + } + + for (var = pr.localvars; var; var = var->nextlocal) + { + if (var->scope && var->scope != pr_scope) + continue; //this should be an error + if (ofs >= var->ofs && ofs < var->ofs + var->type->size) + { + if (*var->name) + { + if (!STRCMP(var->name, "IMMEDIATE")) //continue, don't get bogged down by multiple bits of code + continue; + if (size < var->type->size) + sprintf(message, "%s_%c", var->name, 'x' + (ofs-var->ofs)%3); + else + sprintf(message, "%s", var->name); + return message; + } + } + } + + for (var = pr.def_head.next; var; var = var->next) + { + if (var->scope && var->scope != pr_scope) + continue; + + if (ofs >= var->ofs && ofs < var->ofs + var->type->size) + { + if (*var->name) + { + if (!STRCMP(var->name, "IMMEDIATE")) + { + switch(var->type->type) + { + case ev_string: + sprintf(message, "\"%.1020s\"", &strings[((int *)qcc_pr_globals)[var->ofs]]); + return message; + case ev_integer: + sprintf(message, "%i", ((int *)qcc_pr_globals)[var->ofs]); + return message; + case ev_float: + sprintf(message, "%f", qcc_pr_globals[var->ofs]); + return message; + case ev_vector: + sprintf(message, "'%f %f %f'", qcc_pr_globals[var->ofs], qcc_pr_globals[var->ofs+1], qcc_pr_globals[var->ofs+2]); + return message; + default: + sprintf(message, "IMMEDIATE"); + return message; + } + } + if (size < var->type->size) + sprintf(message, "%s_%c", var->name, 'x' + (ofs-var->ofs)%3); + else + sprintf(message, "%s", var->name); + return message; + } + } + } + + if (size >= 3) + { + if (ofs >= OFS_RETURN && ofs < OFS_PARM0) + sprintf(message, "return"); + else if (ofs >= OFS_PARM0 && ofs < RESERVED_OFS) + sprintf(message, "parm%i", (ofs-OFS_PARM0)/3); + else + sprintf(message, "offset_%i", ofs); + } + else + { + if (ofs >= OFS_RETURN && ofs < OFS_PARM0) + sprintf(message, "return_%c", 'x' + ofs-OFS_RETURN); + else if (ofs >= OFS_PARM0 && ofs < RESERVED_OFS) + sprintf(message, "parm%i_%c", (ofs-OFS_PARM0)/3, 'x' + (ofs-OFS_PARM0)%3); + else + sprintf(message, "offset_%i", ofs); + } + return message; +} +#endif + +QCC_def_t *QCC_PR_Statement ( QCC_opcode_t *op, QCC_def_t *var_a, QCC_def_t *var_b, QCC_dstatement_t **outstatement) +{ + QCC_dstatement_t *statement; + QCC_def_t *var_c=NULL, *temp=NULL; + + if (outstatement == (QCC_dstatement_t **)0xffffffff) + outstatement = NULL; + else if (op->priority != -1) + { + if (op->associative!=ASSOC_LEFT) + { + if (op->type_a == &type_pointer) + var_b = QCC_SupplyConversion(var_b, (*op->type_b)->type); + else + var_b = QCC_SupplyConversion(var_b, (*op->type_a)->type); + } + else + { + if (var_a) + var_a = QCC_SupplyConversion(var_a, (*op->type_a)->type); + if (var_b) + var_b = QCC_SupplyConversion(var_b, (*op->type_b)->type); +// if (op->type_a == &def_pointer) +// var_a = QCC_SupplyConversion(var_a, (*op->type_b)->type); +// else +// var_a = QCC_SupplyConversion(var_a, (*op->type_a)->type); +// } +// //can't convert the left componant of an assignment operation +// if (var_b && var_b->type && var_b->type != op->type_b->type) +// var_b = QCC_SupplyConversion(var_b, op->type_b->type->type); + } + } + + if (var_a) + { + var_a->references++; + QCC_FreeTemp(var_a); + } + if (var_b) + { + var_b->references++; + QCC_FreeTemp(var_b); + } + + if (keyword_class && var_a && var_b) + { + if (var_a->type->type == ev_entity && var_b->type->type == ev_entity) + if (var_a->type != var_b->type) + if (strcmp(var_a->type->name, var_b->type->name)) + QCC_PR_ParseWarning(0, "Implicit cast from '%s' to '%s'", var_a->type->name, var_b->type->name); + } + + //maths operators + if (opt_constantarithmatic) + { + if (var_a && var_a->constant) + { + if (var_b && var_b->constant) + { + //both are constants + switch (op - pr_opcodes) //improve some of the maths. + { + case OP_BITOR: + optres_constantarithmatic++; + return QCC_MakeFloatDef((float)((int)G_FLOAT(var_a->ofs) | (int)G_FLOAT(var_b->ofs))); + case OP_BITAND: + optres_constantarithmatic++; + return QCC_MakeFloatDef((float)((int)G_FLOAT(var_a->ofs) & (int)G_FLOAT(var_b->ofs))); + case OP_MUL_F: + optres_constantarithmatic++; + return QCC_MakeFloatDef(G_FLOAT(var_a->ofs) * G_FLOAT(var_b->ofs)); + case OP_DIV_F: + optres_constantarithmatic++; + return QCC_MakeFloatDef(G_FLOAT(var_a->ofs) / G_FLOAT(var_b->ofs)); + case OP_ADD_F: + optres_constantarithmatic++; + return QCC_MakeFloatDef(G_FLOAT(var_a->ofs) + G_FLOAT(var_b->ofs)); + case OP_SUB_F: + optres_constantarithmatic++; + return QCC_MakeFloatDef(G_FLOAT(var_a->ofs) - G_FLOAT(var_b->ofs)); + + case OP_BITOR_I: + optres_constantarithmatic++; + return QCC_MakeIntDef(G_INT(var_a->ofs) | G_INT(var_b->ofs)); + case OP_BITAND_I: + optres_constantarithmatic++; + return QCC_MakeIntDef(G_INT(var_a->ofs) & G_INT(var_b->ofs)); + case OP_MUL_I: + optres_constantarithmatic++; + return QCC_MakeIntDef(G_INT(var_a->ofs) * G_INT(var_b->ofs)); + case OP_DIV_I: + optres_constantarithmatic++; + return QCC_MakeIntDef(G_INT(var_a->ofs) / G_INT(var_b->ofs)); + case OP_ADD_I: + optres_constantarithmatic++; + return QCC_MakeIntDef(G_INT(var_a->ofs) + G_INT(var_b->ofs)); + case OP_SUB_I: + optres_constantarithmatic++; + return QCC_MakeIntDef(G_INT(var_a->ofs) - G_INT(var_b->ofs)); + + case OP_AND: + optres_constantarithmatic++; + return QCC_MakeIntDef(G_INT(var_a->ofs) && G_INT(var_b->ofs)); + case OP_OR: + optres_constantarithmatic++; + return QCC_MakeIntDef(G_INT(var_a->ofs) || G_INT(var_b->ofs)); + case OP_MUL_V: //mul_f is actually a dot-product + optres_constantarithmatic++; + return QCC_MakeFloatDef( G_FLOAT(var_a->ofs) * G_FLOAT(var_b->ofs+0) + + G_FLOAT(var_a->ofs) * G_FLOAT(var_b->ofs+1) + + G_FLOAT(var_a->ofs) * G_FLOAT(var_b->ofs+2)); + case OP_MUL_FV: + optres_constantarithmatic++; + return QCC_MakeVectorDef( G_FLOAT(var_a->ofs) * G_FLOAT(var_b->ofs+0), + G_FLOAT(var_a->ofs) * G_FLOAT(var_b->ofs+1), + G_FLOAT(var_a->ofs) * G_FLOAT(var_b->ofs+2)); + case OP_MUL_VF: + optres_constantarithmatic++; + return QCC_MakeVectorDef( G_FLOAT(var_a->ofs+0) * G_FLOAT(var_b->ofs), + G_FLOAT(var_a->ofs+1) * G_FLOAT(var_b->ofs), + G_FLOAT(var_a->ofs+2) * G_FLOAT(var_b->ofs)); + case OP_ADD_V: + optres_constantarithmatic++; + return QCC_MakeVectorDef( G_FLOAT(var_a->ofs+0) + G_FLOAT(var_b->ofs+0), + G_FLOAT(var_a->ofs+1) + G_FLOAT(var_b->ofs+1), + G_FLOAT(var_a->ofs+2) + G_FLOAT(var_b->ofs+2)); + case OP_SUB_V: + optres_constantarithmatic++; + return QCC_MakeVectorDef( G_FLOAT(var_a->ofs+0) - G_FLOAT(var_b->ofs+0), + G_FLOAT(var_a->ofs+1) - G_FLOAT(var_b->ofs+1), + G_FLOAT(var_a->ofs+2) - G_FLOAT(var_b->ofs+2)); + } + } + else + { + //a is const, b is not + switch (op - pr_opcodes) + { + case OP_BITOR: + case OP_OR: + case OP_ADD_F: + if (G_FLOAT(var_a->ofs) == 0) + { + optres_constantarithmatic++; + QCC_UnFreeTemp(var_b); + return var_b; + } + break; + case OP_MUL_F: + if (G_FLOAT(var_a->ofs) == 1) + { + optres_constantarithmatic++; + QCC_UnFreeTemp(var_b); + return var_b; + } + break; + case OP_BITAND: + case OP_AND: + if (G_FLOAT(var_a->ofs) != 0) + { + optres_constantarithmatic++; + QCC_UnFreeTemp(var_b); + return var_b; + } + break; + + case OP_BITOR_I: + case OP_OR_I: + case OP_ADD_I: + if (G_INT(var_a->ofs) == 0) + { + optres_constantarithmatic++; + QCC_UnFreeTemp(var_b); + return var_b; + } + break; + case OP_MUL_I: + if (G_INT(var_a->ofs) == 1) + { + optres_constantarithmatic++; + QCC_UnFreeTemp(var_b); + return var_b; + } + break; + case OP_BITAND_I: + case OP_AND_I: + if (G_INT(var_a->ofs) != 0) + { + optres_constantarithmatic++; + QCC_UnFreeTemp(var_b); + return var_b; + } + break; + } + } + } + else if (var_b && var_b->constant) + { + //b is const, a is not + switch (op - pr_opcodes) + { + case OP_BITOR: + case OP_OR: + case OP_SUB_F: + case OP_ADD_F: + if (G_FLOAT(var_b->ofs) == 0) + { + optres_constantarithmatic++; + QCC_UnFreeTemp(var_a); + return var_a; + } + break; + case OP_DIV_F: + case OP_MUL_F: + if (G_FLOAT(var_b->ofs) == 1) + { + optres_constantarithmatic++; + QCC_UnFreeTemp(var_a); + return var_a; + } + break; + //no bitand_f, I don't trust the casts + case OP_AND: + if (G_FLOAT(var_b->ofs) != 0) + { + optres_constantarithmatic++; + QCC_UnFreeTemp(var_a); + return var_a; + } + break; + + case OP_BITOR_I: + case OP_OR_I: + case OP_SUB_I: + case OP_ADD_I: + if (G_INT(var_b->ofs) == 0) + { + optres_constantarithmatic++; + QCC_UnFreeTemp(var_a); + return var_a; + } + break; + case OP_DIV_I: + case OP_MUL_I: + if (G_INT(var_b->ofs) == 1) + { + optres_constantarithmatic++; + QCC_UnFreeTemp(var_a); + return var_a; + } + break; + case OP_BITAND_I: + if (G_INT(var_b->ofs) == 0xffffffff) + { + optres_constantarithmatic++; + QCC_UnFreeTemp(var_a); + return var_a; + } + case OP_AND_I: + if (G_INT(var_b->ofs) != 0) + { + optres_constantarithmatic++; + QCC_UnFreeTemp(var_a); + return var_a; + } + break; + } + } + } + + switch (op - pr_opcodes) + { + case OP_AND: + if (var_a->ofs == var_b->ofs) + QCC_PR_ParseWarning(WARN_CONSTANTCOMPARISON, "Parameter offsets for && are the same"); + if (var_a->constant || var_b->constant) + QCC_PR_ParseWarning(WARN_CONSTANTCOMPARISON, "Result of comparison is constant"); + break; + case OP_OR: + if (var_a->ofs == var_b->ofs) + QCC_PR_ParseWarning(WARN_CONSTANTCOMPARISON, "Parameters for || are the same"); + if (var_a->constant || var_b->constant) + QCC_PR_ParseWarning(WARN_CONSTANTCOMPARISON, "Result of comparison is constant"); + break; + case OP_EQ_F: + case OP_EQ_S: + case OP_EQ_E: + case OP_EQ_FNC: +// if (opt_shortenifnots) +// if (var_b->constant && ((int*)qcc_pr_globals)[var_b->ofs]==0) // (a == 0) becomes (!a) +// op = &pr_opcodes[(op - pr_opcodes) - OP_EQ_F + OP_NOT_F]; + case OP_EQ_V: + + case OP_NE_F: + case OP_NE_V: + case OP_NE_S: + case OP_NE_E: + case OP_NE_FNC: + + case OP_LE: + case OP_GE: + case OP_LT: + case OP_GT: + if ((var_a->constant && var_b->constant && !var_a->temp && !var_b->temp) || var_a->ofs == var_b->ofs) + QCC_PR_ParseWarning(WARN_CONSTANTCOMPARISON, "Result of comparison is constant"); + break; + case OP_IF_S: + case OP_IFNOT_S: + case OP_IF_F: + case OP_IFNOT_F: + case OP_IF: + case OP_IFNOT: +// if (var_a->type->type == ev_function && !var_a->temp) +// QCC_PR_ParseWarning(WARN_CONSTANTCOMPARISON, "Result of comparison is constant"); + if (var_a->constant && !var_a->temp) + QCC_PR_ParseWarning(WARN_CONSTANTCOMPARISON, "Result of comparison is constant"); + break; + default: + break; + } + + if (numstatements) + { //optimise based on last statement. + if (op - pr_opcodes == OP_IFNOT) + { + if (opt_shortenifnots && var_a && (statements[numstatements-1].op == OP_NOT_F || statements[numstatements-1].op == OP_NOT_FNC || statements[numstatements-1].op == OP_NOT_ENT)) + { + if (statements[numstatements-1].c == var_a->ofs) + { + static QCC_def_t nvara; + if (statements[numstatements-1].op == OP_NOT_F) + op = &pr_opcodes[OP_IF_F]; + else + op = &pr_opcodes[OP_IF]; + numstatements--; + QCC_FreeTemp(var_a); + memcpy(&nvara, var_a, sizeof(nvara)); + nvara.ofs = statements[numstatements].a; + var_a = &nvara; + + optres_shortenifnots++; + } + } + } + else if (op - pr_opcodes == OP_IFNOT_F) + { + if (opt_shortenifnots && var_a && statements[numstatements-1].op == OP_NOT_F) + { + if (statements[numstatements-1].c == var_a->ofs) + { + static QCC_def_t nvara; + op = &pr_opcodes[OP_IF_F]; + numstatements--; + QCC_FreeTemp(var_a); + memcpy(&nvara, var_a, sizeof(nvara)); + nvara.ofs = statements[numstatements].a; + var_a = &nvara; + + optres_shortenifnots++; + } + } + } + else if (op - pr_opcodes == OP_IFNOT_S) + { + if (opt_shortenifnots && var_a && statements[numstatements-1].op == OP_NOT_S) + { + if (statements[numstatements-1].c == var_a->ofs) + { + static QCC_def_t nvara; + op = &pr_opcodes[OP_IF_S]; + numstatements--; + QCC_FreeTemp(var_a); + memcpy(&nvara, var_a, sizeof(nvara)); + nvara.ofs = statements[numstatements].a; + var_a = &nvara; + + optres_shortenifnots++; + } + } + } + else if (((unsigned) ((op - pr_opcodes) - OP_STORE_F) < 6) || (op-pr_opcodes) == OP_STORE_P) + { + // remove assignments if what should be assigned is the 3rd operand of the previous statement? + // don't if it's a call, callH, switch or case + // && var_a->ofs >RESERVED_OFS) + if (OpAssignsToC(statements[numstatements-1].op) && + opt_assignments && var_a && var_a->ofs == statements[numstatements-1].c) + { + if (var_a->type->type == var_b->type->type) + { + if (var_a->temp) + { + statement = &statements[numstatements-1]; + statement->c = var_b->ofs; + + if (var_a->type->type != var_b->type->type) + QCC_PR_ParseWarning(0, "store type mismatch"); + var_b->references++; + var_a->references--; + QCC_FreeTemp(var_a); + optres_assignments++; + + simplestore=true; + + QCC_UnFreeTemp(var_b); + return var_b; + } + } + } + } + } + simplestore=false; + + statement = &statements[numstatements]; + numstatements++; + + if (!QCC_OPCodeValid(op)) + { + switch(op - pr_opcodes) + { + case OP_IF_S: + var_c = QCC_PR_GetDef(type_string, "string_null", NULL, true, 1, false); + numstatements--; + var_a = QCC_PR_Statement(&pr_opcodes[OP_NE_S], var_a, var_c, NULL); + statement = &statements[numstatements]; + numstatements++; + + QCC_FreeTemp(var_a); + op = &pr_opcodes[OP_IF]; + break; + + case OP_IFNOT_S: + var_c = QCC_PR_GetDef(type_string, "string_null", NULL, true, 1, false); + numstatements--; + var_a = QCC_PR_Statement(&pr_opcodes[OP_NE_S], var_a, var_c, NULL); + statement = &statements[numstatements]; + numstatements++; + + QCC_FreeTemp(var_a); + op = &pr_opcodes[OP_IFNOT]; + break; + + case OP_IF_F: + var_c = QCC_MakeFloatDef(0); + numstatements--; + var_a = QCC_PR_Statement(&pr_opcodes[OP_NE_F], var_a, var_c, NULL); + statement = &statements[numstatements]; + numstatements++; + + QCC_FreeTemp(var_a); + op = &pr_opcodes[OP_IF]; + break; + + case OP_IFNOT_F: + var_c = QCC_MakeFloatDef(0); + numstatements--; + var_a = QCC_PR_Statement(&pr_opcodes[OP_NE_F], var_a, var_c, NULL); + statement = &statements[numstatements]; + numstatements++; + + QCC_FreeTemp(var_a); + op = &pr_opcodes[OP_IFNOT]; + break; + + case OP_ADDSTORE_F: + op = &pr_opcodes[OP_ADD_F]; + var_c = var_b; + var_b = var_a; + var_a = var_c; + var_c = var_a; + break; + case OP_ADDSTORE_FI: + op = &pr_opcodes[OP_ADD_FI]; + var_c = var_b; + var_b = var_a; + var_a = var_c; + var_c = var_a; + break; + case OP_ADDSTORE_IF: + op = &pr_opcodes[OP_ADD_IF]; + var_c = var_b; + var_b = var_a; + var_a = var_c; + var_c = var_a; + break; + + case OP_SUBSTORE_F: + op = &pr_opcodes[OP_SUB_F]; + var_c = var_b; + var_b = var_a; + var_a = var_c; + var_c = var_a; + break; + case OP_SUBSTORE_FI: + op = &pr_opcodes[OP_SUB_FI]; + var_c = var_b; + var_b = var_a; + var_a = var_c; + var_c = var_a; + break; + case OP_SUBSTORE_IF: + op = &pr_opcodes[OP_SUB_IF]; + var_c = var_b; + var_b = var_a; + var_a = var_c; + var_c = var_a; + break; + + case OP_DIVSTORE_F: + op = &pr_opcodes[OP_DIV_F]; + var_c = var_b; + var_b = var_a; + var_a = var_c; + var_c = var_a; + break; + case OP_DIVSTORE_FI: + op = &pr_opcodes[OP_DIV_FI]; + var_c = var_b; + var_b = var_a; + var_a = var_c; + var_c = var_a; + break; + case OP_DIVSTORE_IF: + op = &pr_opcodes[OP_DIV_IF]; + var_c = var_b; + var_b = var_a; + var_a = var_c; + var_c = var_a; + break; + + case OP_MULSTORE_F: + op = &pr_opcodes[OP_MUL_F]; + var_c = var_b; + var_b = var_a; + var_a = var_c; + var_c = var_a; + break; + case OP_MULSTORE_IF: + op = &pr_opcodes[OP_MUL_IF]; + var_c = var_b; + var_b = var_a; + var_a = var_c; + var_c = var_a; + break; + case OP_MULSTORE_FI: + op = &pr_opcodes[OP_MUL_FI]; + var_c = var_b; + var_b = var_a; + var_a = var_c; + var_c = var_a; + break; + + case OP_ADDSTORE_V: + op = &pr_opcodes[OP_ADD_V]; + var_c = var_b; + var_b = var_a; + var_a = var_c; + var_c = var_a; + break; + + case OP_SUBSTORE_V: + op = &pr_opcodes[OP_SUB_V]; + var_c = var_b; + var_b = var_a; + var_a = var_c; + var_c = var_a; + break; + + case OP_MULSTORE_V: + op = &pr_opcodes[OP_MUL_VF]; + var_c = var_b; + var_b = var_a; + var_a = var_c; + var_c = var_a; + break; + + case OP_BITSET_I: + op = &pr_opcodes[OP_BITOR_I]; + var_c = var_b; + var_b = var_a; + var_a = var_c; + var_c = var_a; + break; + case OP_BITSET: + op = &pr_opcodes[OP_BITOR]; + var_c = var_b; + var_b = var_a; + var_a = var_c; + var_c = var_a; + break; + + case OP_BITCLR: + //b = var, a = bit field. + + QCC_UnFreeTemp(var_a); + QCC_UnFreeTemp(var_b); + + numstatements--; + var_c = QCC_PR_Statement(&pr_opcodes[OP_BITAND], var_b, var_a, NULL); + QCC_FreeTemp(var_c); + statement = &statements[numstatements]; + numstatements++; + + QCC_FreeTemp(var_a); + QCC_FreeTemp(var_b); + + op = &pr_opcodes[OP_SUB_F]; + var_a = var_b; + var_b = var_c; + var_c = var_a; + break; + + case OP_SUBSTOREP_FI: + case OP_SUBSTOREP_IF: + case OP_ADDSTOREP_FI: + case OP_ADDSTOREP_IF: + case OP_MULSTOREP_FI: + case OP_MULSTOREP_IF: + case OP_DIVSTOREP_FI: + case OP_DIVSTOREP_IF: + + + case OP_SUBSTOREP_F: + case OP_ADDSTOREP_F: + case OP_MULSTOREP_F: + case OP_DIVSTOREP_F: + case OP_BITSETP: + case OP_BITSETP_I: + case OP_BITCLRP: +// QCC_PR_ParseWarning(0, "XSTOREP_F emulation is still experimental"); + QCC_UnFreeTemp(var_a); + QCC_UnFreeTemp(var_b); + //don't chain these... this expansion is not the same. + { + int st; + int need_lock = false; + for (st = numstatements-2; st>=0; st--) + { + if (statements[st].op == OP_ADDRESS) + if (statements[st].c == var_b->ofs) + break; + + if (statements[st].op >= OP_CALL0 && statements[st].op <= OP_CALL8 || statements[st].op >= OP_CALL1H && statements[st].op <= OP_CALL8H) + need_lock = true; + + //printf("%s\n", pr_opcodes[statements[st].op].opname); + + if (statements[st].c == var_b->ofs) + QCC_PR_ParseWarning(0, "Temp-reuse may have broken your %s", op->name); + } + if (st < 0) + QCC_PR_ParseError(ERR_INTERNAL, "XSTOREP_F: pointer was not generated from previous statement"); + var_c = QCC_GetTemp(*op->type_c); + if(need_lock) + QCC_LockTemp(var_c); // this will cause the temp to be remapped by QCC_RemapLockedTemps + + statement_linenums[statement-statements] = statement_linenums[st]; + statement->op = OP_ADDRESS; + statement->a = statements[st].a; + statement->b = statements[st].b; + statement->c = statements[st].c; + + statement_linenums[st] = pr_source_line; + statements[st].op = OP_LOAD_F; + statements[st].a = statements[st].a; + statements[st].b = statements[st].b; + statements[st].c = var_c->ofs; + } + + statement = &statements[numstatements]; + numstatements++; + + statement_linenums[statement-statements] = pr_source_line; + switch(op - pr_opcodes) + { + case OP_SUBSTOREP_F: + statement->op = OP_SUB_F; + break; + case OP_SUBSTOREP_IF: + statement->op = OP_SUB_IF; + break; + case OP_SUBSTOREP_FI: + statement->op = OP_SUB_FI; + break; + case OP_ADDSTOREP_IF: + statement->op = OP_ADD_IF; + break; + case OP_ADDSTOREP_FI: + statement->op = OP_ADD_FI; + break; + case OP_MULSTOREP_IF: + statement->op = OP_MUL_IF; + break; + case OP_MULSTOREP_FI: + statement->op = OP_MUL_FI; + break; + case OP_DIVSTOREP_IF: + statement->op = OP_DIV_IF; + break; + case OP_DIVSTOREP_FI: + statement->op = OP_DIV_FI; + break; + case OP_ADDSTOREP_F: + statement->op = OP_ADD_F; + break; + case OP_MULSTOREP_F: + statement->op = OP_MUL_F; + break; + case OP_DIVSTOREP_F: + statement->op = OP_DIV_F; + break; + case OP_BITSETP: + statement->op = OP_BITOR; + break; + case OP_BITSETP_I: + statement->op = OP_BITOR_I; + break; + case OP_BITCLRP: + //float pointer float + temp = QCC_GetTemp(type_float); + statement->op = OP_BITAND; + statement->a = var_c ? var_c->ofs : 0; + statement->b = var_a ? var_a->ofs : 0; + statement->c = temp->ofs; + + statement = &statements[numstatements]; + numstatements++; + + statement_linenums[statement-statements] = pr_source_line; + statement->op = OP_SUB_F; + + //t = c & i + //c = c - t + break; + default: //no way will this be hit... + QCC_PR_ParseError(ERR_INTERNAL, "opcode invalid 3 times %i", op - pr_opcodes); + } + if (op - pr_opcodes == OP_BITCLRP) + { + statement->a = var_c ? var_c->ofs : 0; + statement->b = temp ? temp->ofs : 0; + statement->c = var_c->ofs; + QCC_FreeTemp(temp); + var_b = var_b; //this is the ptr. + QCC_FreeTemp(var_a); + var_a = var_c; //this is the value. + } + else + { + statement->a = var_c ? var_c->ofs : 0; + statement->b = var_a ? var_a->ofs : 0; + statement->c = var_c->ofs; + var_b = var_b; //this is the ptr. + QCC_FreeTemp(var_a); + var_a = var_c; //this is the value. + } + + op = &pr_opcodes[OP_STOREP_F]; + QCC_FreeTemp(var_c); + + var_c = NULL; + QCC_FreeTemp(var_b); + + statement = &statements[numstatements]; + numstatements++; + break; + + case OP_MULSTOREP_V: + case OP_SUBSTOREP_V: + case OP_ADDSTOREP_V: +// QCC_PR_ParseWarning(0, "XSTOREP_V emulation is still experimental"); + QCC_UnFreeTemp(var_a); + QCC_UnFreeTemp(var_b); + //don't chain these... this expansion is not the same. + { + int st; + int need_lock = false; + for (st = numstatements-2; st>=0; st--) + { + if (statements[st].op == OP_ADDRESS) + if (statements[st].c == var_b->ofs) + break; + + if (statements[st].op >= OP_CALL0 && statements[st].op <= OP_CALL8 || statements[st].op >= OP_CALL1H && statements[st].op <= OP_CALL8H) + need_lock = true; + + if (statements[st].c == var_b->ofs) + QCC_PR_ParseWarning(0, "Temp-reuse may have broken your %s", op->name); + } + if (st < 0) + QCC_PR_ParseError(ERR_INTERNAL, "XSTOREP_V couldn't find pointer generation"); + var_c = QCC_GetTemp(*op->type_c); + if(need_lock) + QCC_LockTemp(var_c); // this will cause the temp to be remapped by QCC_RemapLockedTemps + + statement_linenums[statement-statements] = statement_linenums[st]; + statement->op = OP_ADDRESS; + statement->a = statements[st].a; + statement->b = statements[st].b; + statement->c = statements[st].c; + + statement_linenums[st] = pr_source_line; + statements[st].op = OP_LOAD_V; + statements[st].a = statements[st].a; + statements[st].b = statements[st].b; + statements[st].c = var_c->ofs; + } + + statement = &statements[numstatements]; + numstatements++; + + statement_linenums[statement-statements] = pr_source_line; + switch(op - pr_opcodes) + { + case OP_SUBSTOREP_V: + statement->op = OP_SUB_V; + break; + case OP_ADDSTOREP_V: + statement->op = OP_ADD_V; + break; + case OP_MULSTOREP_V: + statement->op = OP_MUL_VF; + break; + default: //no way will this be hit... + QCC_PR_ParseError(ERR_INTERNAL, "opcode invalid 3 times %i", op - pr_opcodes); + } + statement->a = var_a ? var_a->ofs : 0; + statement->b = var_c ? var_c->ofs : 0; + QCC_FreeTemp(var_c); + var_c = QCC_GetTemp(*op->type_c); + statement->c = var_c ? var_c->ofs : 0; + + var_b = var_b; //this is the ptr. + QCC_FreeTemp(var_a); + var_a = var_c; //this is the value. + op = &pr_opcodes[OP_STOREP_V]; + + + + + QCC_FreeTemp(var_c); + var_c = NULL; + QCC_FreeTemp(var_b); + + statement = &statements[numstatements]; + numstatements++; + break; + default: + QCC_PR_ParseError(ERR_BADEXTENSION, "Opcode \"%s|%s\" not valid for target", op->name, op->opname); + break; + } + } + + if (outstatement) + *outstatement = statement; + + statement_linenums[statement-statements] = pr_source_line; + statement->op = op - pr_opcodes; + statement->a = var_a ? var_a->ofs : 0; + statement->b = var_b ? var_b->ofs : 0; + if (var_c != NULL) + { + statement->c = var_c->ofs; + } + else if (op->type_c == &type_void || op->associative==ASSOC_RIGHT || op->type_c == NULL) + { + var_c = NULL; + statement->c = 0; // ifs, gotos, and assignments + // don't need vars allocated + } + else + { // allocate result space + var_c = QCC_GetTemp(*op->type_c); + statement->c = var_c->ofs; + if (op->type_b == &type_field) + { + var_c->name = var_b->name; + var_c->s_file = var_b->s_file; + var_c->s_line = var_b->s_line; + } + } + + if ((op - pr_opcodes >= OP_LOAD_F && op - pr_opcodes <= OP_LOAD_FNC) || + op - pr_opcodes == OP_LOAD_I) + { + if (var_b->constant == 2) + var_c->constant = true; + } + + if (!var_c) + { + if (var_a) + QCC_UnFreeTemp(var_a); + return var_a; + } + return var_c; +} + +/* +============ +QCC_PR_SimpleStatement + +Emits a primitive statement, returning the var it places it's value in +============ +*/ +QCC_dstatement_t *QCC_PR_SimpleStatement( int op, int var_a, int var_b, int var_c, int force) +{ + QCC_dstatement_t *statement; + + if (!force && !QCC_OPCodeValid(pr_opcodes+op)) + { + QCC_PR_ParseError(ERR_BADEXTENSION, "Opcode \"%s|%s\" not valid for target\n", pr_opcodes[op].name, pr_opcodes[op].opname); + } + + statement_linenums[numstatements] = pr_source_line; + statement = &statements[numstatements]; + + numstatements++; + statement->op = op; + statement->a = var_a; + statement->b = var_b; + statement->c = var_c; + return statement; +} + +void QCC_PR_Statement3 ( QCC_opcode_t *op, QCC_def_t *var_a, QCC_def_t *var_b, QCC_def_t *var_c, int force) +{ + QCC_dstatement_t *statement; + + if (!force && !QCC_OPCodeValid(op)) + { +// outputversion = op->extension; +// if (noextensions) + QCC_PR_ParseError(ERR_BADEXTENSION, "Opcode \"%s|%s\" not valid for target\n", op->name, op->opname); + } + + statement = &statements[numstatements]; + numstatements++; + + statement_linenums[statement-statements] = pr_source_line; + statement->op = op - pr_opcodes; + statement->a = var_a ? var_a->ofs : 0; + statement->b = var_b ? var_b->ofs : 0; + statement->c = var_c ? var_c->ofs : 0; +} + +/* +============ +PR_ParseImmediate + +Looks for a preexisting constant +============ +*/ +QCC_def_t *QCC_PR_ParseImmediate (void) +{ + QCC_def_t *cn; + + if (pr_immediate_type == type_float) + { + cn = QCC_MakeFloatDef(pr_immediate._float); + QCC_PR_Lex (); + return cn; + } + if (pr_immediate_type == type_integer) + { + cn = QCC_MakeIntDef(pr_immediate._int); + QCC_PR_Lex (); + return cn; + } + + if (pr_immediate_type == type_string) + { + cn = QCC_MakeStringDef(pr_immediate_string); + QCC_PR_Lex (); + return cn; + } + +// check for a constant with the same value + for (cn=pr.def_head.next ; cn ; cn=cn->next) //FIXME - hashtable. + { + if (!cn->initialized) + continue; + if (!cn->constant) + continue; + if (cn->type != pr_immediate_type) + continue; + if (pr_immediate_type == type_string) + { + if (!STRCMP(G_STRING(cn->ofs), pr_immediate_string) ) + { + QCC_PR_Lex (); + return cn; + } + } + else if (pr_immediate_type == type_float) + { + if ( G_FLOAT(cn->ofs) == pr_immediate._float ) + { + QCC_PR_Lex (); + return cn; + } + } + else if (pr_immediate_type == type_integer) + { + if ( G_INT(cn->ofs) == pr_immediate._int ) + { + QCC_PR_Lex (); + return cn; + } + } + else if (pr_immediate_type == type_vector) + { + if ( ( G_FLOAT(cn->ofs) == pr_immediate.vector[0] ) + && ( G_FLOAT(cn->ofs+1) == pr_immediate.vector[1] ) + && ( G_FLOAT(cn->ofs+2) == pr_immediate.vector[2] ) ) + { + QCC_PR_Lex (); + return cn; + } + } + else + QCC_PR_ParseError (ERR_BADIMMEDIATETYPE, "weird immediate type"); + } + +// allocate a new one + cn = (void *)qccHunkAlloc (sizeof(QCC_def_t)); + cn->next = NULL; + pr.def_tail->next = cn; + pr.def_tail = cn; + + cn->type = pr_immediate_type; + cn->name = "IMMEDIATE"; + cn->constant = true; + cn->initialized = 1; + cn->scope = NULL; // always share immediates + +// copy the immediate to the global area + cn->ofs = QCC_GetFreeOffsetSpace(type_size[pr_immediate_type->type]); + + if (pr_immediate_type == type_string) + pr_immediate.string = QCC_CopyString (pr_immediate_string); + + memcpy (qcc_pr_globals + cn->ofs, &pr_immediate, 4*type_size[pr_immediate_type->type]); + + QCC_PR_Lex (); + + return cn; +} + + +void QCC_PrecacheSound (QCC_def_t *e, int ch) +{ + char *n; + int i; + + if (e->type->type != ev_string) + return; + + if (!e->ofs || e->temp || !e->constant) + return; + n = G_STRING(e->ofs); + if (!*n) + return; + for (i=0 ; i= '1' && ch <= '9') + precache_sounds_block[i] = ch - '0'; + else + precache_sounds_block[i] = 1; + numsounds++; +} + +void QCC_PrecacheModel (QCC_def_t *e, int ch) +{ + char *n; + int i; + + if (e->type->type != ev_string) + return; + + if (!e->ofs || e->temp || !e->constant) + return; + n = G_STRING(e->ofs); + if (!*n) + return; + for (i=0 ; i= '1' && ch <= '9') + precache_models_block[i] = ch - '0'; + else + precache_models_block[i] = 1; + } + return; + } + if (nummodels == MAX_MODELS) + return; +// QCC_Error ("PrecacheModels: nummodels == MAX_MODELS"); + strcpy (precache_models[i], n); + if (ch >= '1' && ch <= '9') + precache_models_block[i] = ch - '0'; + else + precache_models_block[i] = 1; + nummodels++; +} + +void QCC_SetModel (QCC_def_t *e) +{ + char *n; + int i; + + if (e->type->type != ev_string) + return; + + if (!e->ofs || e->temp || !e->constant) + return; + n = G_STRING(e->ofs); + if (!*n) + return; + for (i=0 ; itype->type != ev_string) + return; + + if (!e->ofs || e->temp || !e->constant) + return; + n = G_STRING(e->ofs); + if (!*n) + return; + for (i=0 ; i= '1' && ch <= '9') + precache_textures_block[i] = ch - '0'; + else + precache_textures_block[i] = 1; + numtextures++; +} + +void QCC_PrecacheFile (QCC_def_t *e, int ch) +{ + char *n; + int i; + + if (e->type->type != ev_string) + return; + + if (!e->ofs || e->temp || !e->constant) + return; + n = G_STRING(e->ofs); + if (!*n) + return; + for (i=0 ; i= '1' && ch <= '9') + precache_files_block[i] = ch - '0'; + else + precache_files_block[i] = 1; + numfiles++; +} + +void QCC_PrecacheFileOptimised (char *n, int ch) +{ + int i; + + for (i=0 ; i= '1' && ch <= '9') + precache_files_block[i] = ch - '0'; + else + precache_files_block[i] = 1; + numfiles++; +} + +QCC_def_t *QCC_PR_GenerateFunctionCall (QCC_def_t *func, QCC_def_t *arglist[], int argcount) //warning, the func could have no name set if it's a field call. +{ + QCC_def_t *d, *oldret, *oself; + int i; + QCC_type_t *t; + int extraparms=false; + int np; + int laststatement = numstatements; + + int callconvention; + QCC_dstatement_t *st; + + + func->timescalled++; + + if (QCC_OPCodeValid(&pr_opcodes[OP_CALL1H])) + callconvention = OP_CALL1H; //FTE extended + else + callconvention = OP_CALL1; //standard + + t = func->type; + + if (t->type == ev_variant) + { + t->aux_type = type_variant; + } + + if (t->type != ev_function && t->type != ev_variant) + { + QCC_PR_ParseErrorPrintDef (ERR_NOTAFUNCTION, func, "not a function"); + } + +// copy the arguments to the global parameter variables + if (t->type == ev_variant) + { + extraparms = true; + np = 0; + } + else if (t->num_parms < 0) + { + extraparms = true; + np = (t->num_parms * -1) - 1; + } + else + np = t->num_parms; + + if (strchr(func->name, ':') && laststatement && statements[laststatement-1].op == OP_LOAD_FNC && statements[laststatement-1].c == func->ofs) + { //we're entering OO code with a different self. + //eg: other.touch(self) + + //FIXME: problems could occur with hexen2 calling conventions when parm0/1 is 'self' + //thiscall. copy the right ent into 'self' (if it's not the same offset) + d = QCC_PR_GetDef(type_entity, "self", NULL, true, 1, false); + if (statements[laststatement-1].a != d->ofs) + { + oself = QCC_GetTemp(type_entity); + //oself = self + QCC_PR_SimpleStatement(OP_STORE_ENT, d->ofs, oself->ofs, 0, false); + //self = other + QCC_PR_SimpleStatement(OP_STORE_ENT, statements[laststatement-1].a, d->ofs, 0, false); + + //if the args refered to self, update them to refer to oself instead + //(as self is now set to 'other') + for (i = 0; i < argcount; i++) + { + if (arglist[i]->ofs == d->ofs) + { + arglist[i] = oself; + } + } + } + else + { + //it was self.func() anyway + oself = NULL; + d = NULL; + } + } + else + { //regular func call + oself = NULL; + d = NULL; + } + +// write the arguments (except for first two if hexenc) + for (i = 0; i < argcount; i++) + { + if (i>=MAX_PARMS) + d = extra_parms[i - MAX_PARMS]; + else + d = &def_parms[i]; + + if (callconvention == OP_CALL1H) + if (i < 2) + { + //first two args are passed in the call opcode, so don't need to be copied + arglist[i]->references++; + d->references++; + QCC_FreeTemp(arglist[i]); + continue; + } + + if (arglist[i]->type->size>1 || !opt_nonvec_parms) + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_STORE_V], arglist[i], d, (QCC_dstatement_t **)0xffffffff)); + else + { + d->type = arglist[i]->type; + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_STORE_F], arglist[i], d, (QCC_dstatement_t **)0xffffffff)); + optres_nonvec_parms++; + } + } + + //if the return value was in use, save it off now, so that it doesn't get clobbered + if (def_ret.temp->used) + { + oldret = QCC_GetTemp(def_ret.type); + if (def_ret.type->size == 3) + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_V], &def_ret, oldret, NULL)); + else + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_F], &def_ret, oldret, NULL)); + QCC_UnFreeTemp(oldret); + QCC_UnFreeTemp(&def_ret); + QCC_PR_ParseWarning(WARN_FIXEDRETURNVALUECONFLICT, "Return value conflict - output is inefficient"); + } + else + oldret = NULL; + + //we dont need to lock the local containing the function index because its thrown away after the call anyway + //(if a function is called in the argument list then it'll be locked as part of that call) + QCC_FreeTemp(func); + QCC_LockActiveTemps(); //any temps before are likly to be used with the return value. + QCC_UnFreeTemp(func); + + //generate the call + if (argcount>MAX_PARMS) + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[callconvention-1+MAX_PARMS], func, 0, (QCC_dstatement_t **)&st)); + else if (argcount) + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[callconvention-1+argcount], func, 0, (QCC_dstatement_t **)&st)); + else + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_CALL0], func, 0, (QCC_dstatement_t **)&st)); + + if (callconvention == OP_CALL1H) + { + if (argcount) + { + st->b = arglist[0]->ofs; +// QCC_FreeTemp(param[0]); + if (argcount>1) + { + st->c = arglist[1]->ofs; +// QCC_FreeTemp(param[1]); + } + } + } + + //restore the class owner + if (oself) + QCC_PR_SimpleStatement(OP_STORE_ENT, oself->ofs, d->ofs, 0, false); + + for(; argcount; argcount--) + { + QCC_FreeTemp(arglist[argcount-1]); + } + + if (oldret) + { + // Make sure our def_ret backup temp wasn't freed above + QCC_UnFreeTemp(oldret); //this bug fix was brought to you by Blub, the character \ and the number 0. + + + //if we preserved the ofs_ret global, restore it here + if (t->type == ev_variant) + { + d = QCC_GetTemp(type_variant); + QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_STORE_F, &def_ret, d, NULL)); + } + else + { + d = QCC_GetTemp(t->aux_type); + if (t->aux_type->size == 3) + QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_STORE_V, &def_ret, d, NULL)); + else + QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_STORE_F, &def_ret, d, NULL)); + } + if (def_ret.type->size == 3) + QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_STORE_V, oldret, &def_ret, NULL)); + else + QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_STORE_F, oldret, &def_ret, NULL)); + QCC_FreeTemp(oldret); + QCC_UnFreeTemp(&def_ret); + QCC_UnFreeTemp(d); + + return d; + } + + if (t->type == ev_variant) + def_ret.type = type_variant; + else + def_ret.type = t->aux_type; + if (def_ret.temp->used) + QCC_PR_ParseWarning(WARN_FIXEDRETURNVALUECONFLICT, "Return value conflict - output is inefficient"); + def_ret.temp->used = true; + + return &def_ret; +} + +/* +============ +PR_ParseFunctionCall +============ +*/ +QCC_def_t *QCC_PR_ParseFunctionCall (QCC_def_t *func) //warning, the func could have no name set if it's a field call. +{ + QCC_def_t *e, *d, *old, *oself, *out; + int arg; + QCC_type_t *t, *p; + int extraparms=false; + int np; + int laststatement = numstatements; + + int callconvention; + + QCC_def_t *param[MAX_PARMS+MAX_EXTRA_PARMS]; + + func->timescalled++; + + if (QCC_OPCodeValid(&pr_opcodes[OP_CALL1H])) + callconvention = OP_CALL1H; //FTE extended + else + callconvention = OP_CALL1; //standard + + t = func->type; + + if (t->type == ev_variant) + { + t->aux_type = type_variant; + } + + if (t->type != ev_function && t->type != ev_variant) + { + QCC_PR_ParseErrorPrintDef (ERR_NOTAFUNCTION, func, "not a function"); + } + + if (!t->num_parms&&t->type != ev_variant) //intrinsics. These base functions have variable arguments. I would check for (...) args too, but that might be used for extended builtin functionality. (this code wouldn't compile otherwise) + { + if (!strcmp(func->name, "random")) + { + old = NULL; + func->references++; + if (!QCC_PR_CheckToken(")")) + { + e = QCC_PR_Expression (TOP_PRIORITY, EXPR_DISALLOW_COMMA); + if (e->type->type != ev_float) + QCC_PR_ParseErrorPrintDef (ERR_TYPEMISMATCHPARM, func, "type mismatch on parm %i", 1); + if (!QCC_PR_CheckToken(")")) + { + QCC_PR_Expect(","); + d = QCC_PR_Expression (TOP_PRIORITY, EXPR_DISALLOW_COMMA); + if (d->type->type != ev_float) + QCC_PR_ParseErrorPrintDef (ERR_TYPEMISMATCHPARM, func, "type mismatch on parm %i", 2); + QCC_PR_Expect(")"); + } + else + d = NULL; + } + else + { + e = NULL; + d = NULL; + } + + if (QCC_OPCodeValid(&pr_opcodes[OP_RAND0])) + { + if(def_ret.temp->used) + out = QCC_GetTemp(type_float); + else + out = &def_ret; + + if (e) + { + if (d) + QCC_PR_SimpleStatement(OP_RAND2, e->ofs, d->ofs, out->ofs, false); + else + QCC_PR_SimpleStatement(OP_RAND1, e->ofs, 0, out->ofs, false); + } + else + QCC_PR_SimpleStatement(OP_RAND0, 0, 0, out->ofs, false); + } + else + { + if (def_ret.temp->used) + { + old = QCC_GetTemp(def_ret.type); + if (def_ret.type->size == 3) + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_V], &def_ret, old, NULL)); + else + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_F], &def_ret, old, NULL)); + QCC_UnFreeTemp(old); + QCC_UnFreeTemp(&def_ret); + QCC_PR_ParseWarning(WARN_FIXEDRETURNVALUECONFLICT, "Return value conflict - output is inefficient"); + } + else + old = NULL; + + if (e) + { + if (d) + { + QCC_dstatement_t *st; + QCC_def_t *t; + QCC_PR_SimpleStatement(OP_CALL0, func->ofs, 0, 0, false); + + if ((!d->constant || !e->constant) && G_FLOAT(d->ofs) >= G_FLOAT(d->ofs)) + { + t = QCC_PR_Statement(&pr_opcodes[OP_GT], d, e, NULL); + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_IFNOT], t, 0, &st)); + st->b = 3; + + t = QCC_PR_Statement(&pr_opcodes[OP_SUB_F], d, e, NULL); + QCC_PR_SimpleStatement(OP_MUL_F, OFS_RETURN, t->ofs, OFS_RETURN, false); + QCC_FreeTemp(t); + QCC_PR_SimpleStatement(OP_ADD_F, OFS_RETURN, e->ofs, OFS_RETURN, false); + + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_GOTO], 0, 0, &st)); + st->a = 3; + } + + t = QCC_PR_Statement(&pr_opcodes[OP_SUB_F], e, d, NULL); + QCC_PR_SimpleStatement(OP_MUL_F, OFS_RETURN, t->ofs, OFS_RETURN, false); + QCC_FreeTemp(t); + QCC_PR_SimpleStatement(OP_ADD_F, OFS_RETURN, d->ofs, OFS_RETURN, false); + } + else + { + QCC_PR_SimpleStatement(OP_CALL0, func->ofs, 0, 0, false); + QCC_PR_SimpleStatement(OP_MUL_F, OFS_RETURN, e->ofs, OFS_RETURN, false); + } + } + else + QCC_PR_SimpleStatement(OP_CALL0, func->ofs, 0, 0, false); + } + + if (e) + { + QCC_FreeTemp(e); + e->references++; + } + if (d) + { + d->references++; + QCC_FreeTemp(d); + } + + if (old) + { + d = QCC_GetTemp(type_float); + QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_STORE_F, &def_ret, d, NULL)); + if (def_ret.type->size == 3) + QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_STORE_V, old, &def_ret, NULL)); + else + QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_STORE_F, old, &def_ret, NULL)); + QCC_FreeTemp(old); + QCC_UnFreeTemp(d); + QCC_UnFreeTemp(&def_ret); + + return d; + } + + if (def_ret.temp->used) + QCC_PR_ParseWarning(0, "Return value conflict - output is likly to be invalid"); + def_ret.temp->used = true; + def_ret.type = type_float; + return &def_ret; + + } + if (!strcmp(func->name, "randomv")) + { + out = NULL; + func->references++; + if (!QCC_PR_CheckToken(")")) + { + e = QCC_PR_Expression (TOP_PRIORITY, EXPR_DISALLOW_COMMA); + if (e->type->type != ev_vector) + QCC_PR_ParseErrorPrintDef (ERR_TYPEMISMATCHPARM, func, "type mismatch on parm %i", 1); + if (!QCC_PR_CheckToken(")")) + { + QCC_PR_Expect(","); + d = QCC_PR_Expression (TOP_PRIORITY, EXPR_DISALLOW_COMMA); + if (d->type->type != ev_vector) + QCC_PR_ParseErrorPrintDef (ERR_TYPEMISMATCHPARM, func, "type mismatch on parm %i", 2); + QCC_PR_Expect(")"); + } + else + d = NULL; + } + else + { + e = NULL; + d = NULL; + } + + if (QCC_OPCodeValid(&pr_opcodes[OP_RANDV0])) + { + if(def_ret.temp->used) + out = QCC_GetTemp(type_vector); + else + out = &def_ret; + + if (e) + { + if (d) + QCC_PR_SimpleStatement(OP_RANDV2, e->ofs, d->ofs, out->ofs, false); + else + QCC_PR_SimpleStatement(OP_RANDV1, e->ofs, 0, out->ofs, false); + } + else + QCC_PR_SimpleStatement(OP_RANDV0, 0, 0, out->ofs, false); + } + else + { + if (def_ret.temp->used) + { + old = QCC_GetTemp(def_ret.type); + if (def_ret.type->size == 3) + QCC_PR_Statement(&pr_opcodes[OP_STORE_V], &def_ret, old, NULL); + else + QCC_PR_Statement(&pr_opcodes[OP_STORE_F], &def_ret, old, NULL); + QCC_UnFreeTemp(old); + QCC_UnFreeTemp(&def_ret); + QCC_PR_ParseWarning(WARN_FIXEDRETURNVALUECONFLICT, "Return value conflict - output is inefficient"); + } + else + old = NULL; + + if (e) + { + if (d) + { + QCC_def_t *t; + QCC_PR_SimpleStatement(OP_CALL0, func->ofs, 0, 0, false); + + if ((!d->constant || !e->constant) && G_FLOAT(d->ofs) >= G_FLOAT(d->ofs)) + { + t = QCC_GetTemp(type_float); + QCC_PR_SimpleStatement(OP_GT, d->ofs+2, e->ofs+2, t->ofs, false); + QCC_PR_SimpleStatement(OP_IFNOT, t->ofs, 3, 0, false); + + QCC_PR_SimpleStatement(OP_SUB_F, d->ofs+2, e->ofs+2, t->ofs, false); + QCC_PR_SimpleStatement(OP_MUL_F, OFS_RETURN, t->ofs, OFS_RETURN+2, false); + QCC_FreeTemp(t); + QCC_PR_SimpleStatement(OP_ADD_F, OFS_RETURN, e->ofs+2, OFS_RETURN+2, false); + + QCC_PR_SimpleStatement(OP_GOTO, 3, 0, 0, false); + } + + t = QCC_GetTemp(type_float); + QCC_PR_SimpleStatement(OP_SUB_F, d->ofs+2, e->ofs+2, t->ofs, false); + QCC_PR_SimpleStatement(OP_MUL_F, OFS_RETURN, t->ofs, OFS_RETURN+2, false); + QCC_FreeTemp(t); + QCC_PR_SimpleStatement(OP_ADD_F, OFS_RETURN, d->ofs+2, OFS_RETURN+2, false); + + + + QCC_PR_SimpleStatement(OP_CALL0, func->ofs, 0, 0, false); + + if ((!d->constant || !e->constant) && G_FLOAT(d->ofs) >= G_FLOAT(d->ofs)) + { + t = QCC_GetTemp(type_float); + QCC_PR_SimpleStatement(OP_GT, d->ofs+1, e->ofs+1, t->ofs, false); + QCC_PR_SimpleStatement(OP_IFNOT, t->ofs, 3, 0, false); + + QCC_PR_SimpleStatement(OP_SUB_F, d->ofs+1, e->ofs+1, t->ofs, false); + QCC_PR_SimpleStatement(OP_MUL_F, OFS_RETURN, t->ofs, OFS_RETURN+1, false); + QCC_FreeTemp(t); + QCC_PR_SimpleStatement(OP_ADD_F, OFS_RETURN, e->ofs+1, OFS_RETURN+1, false); + + QCC_PR_SimpleStatement(OP_GOTO, 3, 0, 0, false); + } + + t = QCC_GetTemp(type_float); + QCC_PR_SimpleStatement(OP_SUB_F, d->ofs+1, e->ofs+1, t->ofs, false); + QCC_PR_SimpleStatement(OP_MUL_F, OFS_RETURN, t->ofs, OFS_RETURN+1, false); + QCC_FreeTemp(t); + QCC_PR_SimpleStatement(OP_ADD_F, OFS_RETURN, d->ofs+1, OFS_RETURN+1, false); + + + QCC_PR_SimpleStatement(OP_CALL0, func->ofs, 0, 0, false); + + if ((!d->constant || !e->constant) && G_FLOAT(d->ofs) >= G_FLOAT(d->ofs)) + { + t = QCC_GetTemp(type_float); + QCC_PR_SimpleStatement(OP_GT, d->ofs, e->ofs, t->ofs, false); + QCC_PR_SimpleStatement(OP_IFNOT, t->ofs, 3, 0, false); + + QCC_PR_SimpleStatement(OP_SUB_F, d->ofs, e->ofs, t->ofs, false); + QCC_PR_SimpleStatement(OP_MUL_F, OFS_RETURN, t->ofs, OFS_RETURN, false); + QCC_FreeTemp(t); + QCC_PR_SimpleStatement(OP_ADD_F, OFS_RETURN, e->ofs, OFS_RETURN, false); + + QCC_PR_SimpleStatement(OP_GOTO, 3, 0, 0, false); + } + + t = QCC_GetTemp(type_float); + QCC_PR_SimpleStatement(OP_SUB_F, d->ofs, e->ofs, t->ofs, false); + QCC_PR_SimpleStatement(OP_MUL_F, OFS_RETURN, t->ofs, OFS_RETURN, false); + QCC_FreeTemp(t); + QCC_PR_SimpleStatement(OP_ADD_F, OFS_RETURN, d->ofs, OFS_RETURN, false); + } + else + { + QCC_PR_SimpleStatement(OP_CALL0, func->ofs, 0, 0, false); + QCC_PR_SimpleStatement(OP_MUL_F, OFS_RETURN, e->ofs, OFS_RETURN+2, false); + QCC_PR_SimpleStatement(OP_CALL0, func->ofs, 0, 0, false); + QCC_PR_SimpleStatement(OP_MUL_F, OFS_RETURN, e->ofs, OFS_RETURN+1, false); + QCC_PR_SimpleStatement(OP_CALL0, func->ofs, 0, 0, false); + QCC_PR_SimpleStatement(OP_MUL_F, OFS_RETURN, e->ofs, OFS_RETURN, false); + } + } + else + { + QCC_PR_SimpleStatement(OP_CALL0, func->ofs, 0, 0, false); + QCC_PR_SimpleStatement(OP_STORE_F, OFS_RETURN, OFS_RETURN+2, 0, false); + QCC_PR_SimpleStatement(OP_CALL0, func->ofs, 0, 0, false); + QCC_PR_SimpleStatement(OP_STORE_F, OFS_RETURN, OFS_RETURN+1, 0, false); + QCC_PR_SimpleStatement(OP_CALL0, func->ofs, 0, 0, false); + } + } + + + if (e) + { + QCC_FreeTemp(e); + e->references++; + } + if (d) + { + d->references++; + QCC_FreeTemp(d); + } + + if (old) + { + d = QCC_GetTemp(type_vector); + QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_STORE_V, &def_ret, d, NULL)); + if (def_ret.type->size == 3) + { + QCC_PR_Statement(pr_opcodes+OP_STORE_V, old, &def_ret, NULL); + } + else + { + QCC_PR_Statement(pr_opcodes+OP_STORE_F, old, &def_ret, NULL); + } + QCC_FreeTemp(old); + QCC_UnFreeTemp(d); + QCC_UnFreeTemp(&def_ret); + + return d; + } + + if (def_ret.temp->used) + QCC_PR_ParseWarning(0, "Return value conflict - output is likly to be invalid"); + def_ret.temp->used = true; + def_ret.type = type_vector; + return &def_ret; + } + else if (!strcmp(func->name, "spawn")) + { + QCC_type_t *rettype; + if (QCC_PR_CheckToken(")")) + { + rettype = type_entity; + } + else + { + rettype = QCC_TypeForName(QCC_PR_ParseName()); + if (!rettype || rettype->type != ev_entity) + QCC_PR_ParseError(ERR_NOTANAME, "Spawn operator with undefined class"); + + QCC_PR_Expect(")"); + } + + + if (def_ret.temp->used) + { + old = QCC_GetTemp(def_ret.type); + if (def_ret.type->size == 3) + QCC_PR_Statement(&pr_opcodes[OP_STORE_V], &def_ret, old, NULL); + else + QCC_PR_Statement(&pr_opcodes[OP_STORE_F], &def_ret, old, NULL); + QCC_UnFreeTemp(old); + QCC_UnFreeTemp(&def_ret); + QCC_PR_ParseWarning(WARN_FIXEDRETURNVALUECONFLICT, "Return value conflict - output is inefficient"); + } + else + old = NULL; + /* + if (def_ret.temp->used) + QCC_PR_ParseWarning(0, "Return value conflict - output is likly to be invalid"); + def_ret.temp->used = true; + */ + + if (rettype != type_entity) + { + char genfunc[2048]; + sprintf(genfunc, "Class*%s", rettype->name); + func = QCC_PR_GetDef(type_function, genfunc, NULL, true, 1, false); + func->references++; + } + QCC_PR_SimpleStatement(OP_CALL0, func->ofs, 0, 0, false); + + if (old) + { + d = QCC_GetTemp(rettype); + QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_STORE_ENT, &def_ret, d, NULL)); + if (def_ret.type->size == 3) + QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_STORE_V, old, &def_ret, NULL)); + else + QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_STORE_F, old, &def_ret, NULL)); + QCC_FreeTemp(old); + QCC_UnFreeTemp(d); + QCC_UnFreeTemp(&def_ret); + + return d; + } + + def_ret.type = rettype; + return &def_ret; + } + else if (!strcmp(func->name, "entnum") && !QCC_PR_CheckToken(")")) + { + //t = (a/%1) / (nextent(world)/%1) + //a/%1 does a (int)entity to float conversion type thing + func->initialized = 1; + + e = QCC_PR_Expression(TOP_PRIORITY, EXPR_DISALLOW_COMMA); + QCC_PR_Expect(")"); + e = QCC_PR_Statement(&pr_opcodes[OP_DIV_F], e, QCC_MakeIntDef(1), (QCC_dstatement_t **)0xffffffff); + + d = QCC_PR_GetDef(NULL, "nextent", NULL, false, 0, false); + if (!d) + QCC_PR_ParseError(0, "the nextent builtin is not defined"); + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_F], e, &def_parms[0], (QCC_dstatement_t **)0xffffffff)); + d = QCC_PR_Statement(&pr_opcodes[OP_CALL0], d, NULL, NULL); + d = QCC_PR_Statement(&pr_opcodes[OP_DIV_F], d, QCC_MakeIntDef(1), (QCC_dstatement_t **)0xffffffff); + + e = QCC_PR_Statement(&pr_opcodes[OP_DIV_F], e, d, (QCC_dstatement_t **)0xffffffff); + + return e; + } + else if (!strcmp(func->name, "_") && !QCC_PR_CheckToken(")")) + { + // return string as is, but set the dotranslate flag + ++dotranslate; + e = QCC_PR_Expression(TOP_PRIORITY, EXPR_DISALLOW_COMMA); + --dotranslate; + QCC_PR_Expect(")"); + return e; + } + } //so it's not an intrinsic. + + if (opt_precache_file) //should we strip out all precache_file calls? + { + if (!strncmp(func->name,"precache_file", 13)) + { + if (pr_token_type == tt_immediate && pr_immediate_type->type == ev_string) + { + optres_precache_file += strlen(pr_immediate_string); + QCC_PR_Lex(); + QCC_PR_Expect(")"); + QCC_PrecacheFileOptimised (pr_immediate_string, func->name[13]); + def_ret.type = type_void; + return &def_ret; + } + } + } + +// copy the arguments to the global parameter variables + arg = 0; + if (t->type == ev_variant) + { + extraparms = true; + np = 0; + } + else if (t->num_parms < 0) + { + extraparms = true; + np = (t->num_parms * -1) - 1; + } + else + np = t->num_parms; + + //any temps referenced to build the parameters don't need to be locked. + if (!QCC_PR_CheckToken(")")) + { + p = t->param; + do + { + if (extraparms && arg >= MAX_PARMS) + QCC_PR_ParseErrorPrintDef (ERR_TOOMANYPARAMETERSVARARGS, func, "More than %i parameters on varargs function", MAX_PARMS); + else if (arg >= MAX_PARMS+MAX_EXTRA_PARMS) + QCC_PR_ParseErrorPrintDef (ERR_TOOMANYTOTALPARAMETERS, func, "More than %i parameters", MAX_PARMS+MAX_EXTRA_PARMS); + if (!extraparms && arg >= t->num_parms) + { + QCC_PR_ParseWarning (WARN_TOOMANYPARAMETERSFORFUNC, "too many parameters"); + QCC_PR_ParsePrintDef(WARN_TOOMANYPARAMETERSFORFUNC, func); + } + + + //with vectorcalls, we store the vector into the args as individual floats + //this allows better reuse of vector constants. + //copy it into the offset now, because we can. + if (opt_vectorcalls && pr_token_type == tt_immediate && pr_immediate_type == type_vector && arg < MAX_PARMS && !def_parms[arg].temp->used) + { + e = &def_parms[arg]; + + e->ofs = OFS_PARM0+0; + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_STORE_F], QCC_MakeFloatDef(pr_immediate.vector[0]), e, (QCC_dstatement_t **)0xffffffff)); + e->ofs = OFS_PARM0+1; + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_STORE_F], QCC_MakeFloatDef(pr_immediate.vector[1]), e, (QCC_dstatement_t **)0xffffffff)); + e->ofs = OFS_PARM0+2; + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_STORE_F], QCC_MakeFloatDef(pr_immediate.vector[2]), e, (QCC_dstatement_t **)0xffffffff)); + e->ofs = OFS_PARM0; + + QCC_PR_Lex(); + } + else + e = QCC_PR_Expression (TOP_PRIORITY, EXPR_DISALLOW_COMMA); + + if (arg == 0 && func->name) + { + // save information for model and sound caching + if (!strncmp(func->name,"precache_", 9)) + { + if (!strncmp(func->name+9,"sound", 5)) + QCC_PrecacheSound (e, func->name[14]); + else if (!strncmp(func->name+9,"model", 5)) + QCC_PrecacheModel (e, func->name[14]); + else if (!strncmp(func->name+9,"texture", 7)) + QCC_PrecacheTexture (e, func->name[16]); + else if (!strncmp(func->name+9,"file", 4)) + QCC_PrecacheFile (e, func->name[13]); + } + } + + if (arg>=MAX_PARMS) + { + if (!extra_parms[arg - MAX_PARMS]) + { + d = (QCC_def_t *) qccHunkAlloc (sizeof(QCC_def_t)); + d->name = "extra parm"; + d->ofs = QCC_GetFreeOffsetSpace (3); + extra_parms[arg - MAX_PARMS] = d; + } + d = extra_parms[arg - MAX_PARMS]; + } + else + d = &def_parms[arg]; + + if (pr_classtype && e->type->type == ev_field && p->type != ev_field) + { //convert. + oself = QCC_PR_GetDef(type_entity, "self", NULL, true, 1, false); + switch(e->type->aux_type->type) + { + case ev_string: + e = QCC_PR_Statement(pr_opcodes+OP_LOAD_S, oself, e, NULL); + break; + case ev_integer: + e = QCC_PR_Statement(pr_opcodes+OP_LOAD_I, oself, e, NULL); + break; + case ev_float: + e = QCC_PR_Statement(pr_opcodes+OP_LOAD_F, oself, e, NULL); + break; + case ev_function: + e = QCC_PR_Statement(pr_opcodes+OP_LOAD_FNC, oself, e, NULL); + break; + case ev_vector: + e = QCC_PR_Statement(pr_opcodes+OP_LOAD_V, oself, e, NULL); + break; + case ev_entity: + e = QCC_PR_Statement(pr_opcodes+OP_LOAD_ENT, oself, e, NULL); + break; + default: + QCC_Error(ERR_INTERNAL, "Bad member type. Try forced expansion"); + } + } + + if (p) + { + if (typecmp(e->type, p)) + /*if (e->type->type != ev_integer && p->type != ev_function) + if (e->type->type != ev_function && p->type != ev_integer) + if ( e->type->type != p->type )*/ + { + if (p->type == ev_integer && e->type->type == ev_float) //convert float -> int... is this a constant? + e = QCC_PR_Statement(pr_opcodes+OP_CONV_FTOI, e, NULL, NULL); + else if (p->type == ev_float && e->type->type == ev_integer) //convert float -> int... is this a constant? + e = QCC_PR_Statement(pr_opcodes+OP_CONV_ITOF, e, NULL, NULL); + else if (p->type == ev_function && e->type->type == ev_integer && e->constant && !((int*)qcc_pr_globals)[e->ofs]) + { //you're allowed to use int 0 to pass a null function pointer + //this is basically because __NULL__ is defined as ~0 (int 0) + } + else if (p->type != ev_variant) //can cast to variant whatever happens + { + if (flag_laxcasts || (p->type == ev_function && e->type->type == ev_function)) + { + QCC_PR_ParseWarning(WARN_LAXCAST, "type mismatch on parm %i - (%s should be %s)", arg+1, TypeName(e->type), TypeName(p)); + QCC_PR_ParsePrintDef(WARN_LAXCAST, func); + } + else + QCC_PR_ParseErrorPrintDef (ERR_TYPEMISMATCHPARM, func, "type mismatch on parm %i - (%s should be %s)", arg+1, TypeName(e->type), TypeName(p)); + } + } + + d->type = p; + + p=p->next; + } + // a vector copy will copy everything + else + d->type = type_void; + + if (arg == 1 && !STRCMP(func->name, "setmodel")) + { + QCC_SetModel(e); + } + + param[arg] = e; +/* if (e->type->size>1) + QCC_PR_Statement (&pr_opcodes[OP_STORE_V], e, d, (QCC_dstatement_t **)0xffffffff); + else + QCC_PR_Statement (&pr_opcodes[OP_STORE_F], e, d, (QCC_dstatement_t **)0xffffffff); + */ + arg++; + } while (QCC_PR_CheckToken (",")); + + if (t->num_parms != -1 && arg < np) + QCC_PR_ParseWarning (WARN_TOOFEWPARAMS, "too few parameters on call to %s", func->name); + QCC_PR_Expect (")"); + } + else if (np) + { + QCC_PR_ParseWarning (WARN_TOOFEWPARAMS, "%s: Too few parameters", func->name); + QCC_PR_ParsePrintDef (WARN_TOOFEWPARAMS, func); + } + + return QCC_PR_GenerateFunctionCall(func, param, arg); +} + +int constchecks; +int varchecks; +int typechecks; +QCC_def_t *QCC_MakeIntDef(int value) +{ + QCC_def_t *cn; + +// check for a constant with the same value + for (cn=pr.def_head.next ; cn ; cn=cn->next) + { + varchecks++; + if (!cn->initialized) + continue; + if (!cn->constant) + continue; + constchecks++; + if (cn->type != type_integer) + continue; + typechecks++; + + if ( G_INT(cn->ofs) == value ) + { + return cn; + } + } + +// allocate a new one + cn = (void *)qccHunkAlloc (sizeof(QCC_def_t)); + cn->next = NULL; + pr.def_tail->next = cn; + pr.def_tail = cn; + + cn->type = type_integer; + cn->name = "IMMEDIATE"; + cn->constant = true; + cn->initialized = 1; + cn->scope = NULL; // always share immediates + cn->arraysize = 1; + +// copy the immediate to the global area + cn->ofs = QCC_GetFreeOffsetSpace (type_size[type_integer->type]); + + G_INT(cn->ofs) = value; + + + return cn; +} + +QCC_def_t *QCC_MakeVectorDef(float a, float b, float c) +{ + QCC_def_t *cn; + +// check for a constant with the same value + for (cn=pr.def_head.next ; cn ; cn=cn->next) + { + varchecks++; + if (!cn->initialized) + continue; + if (!cn->constant) + continue; + constchecks++; + if (cn->type != type_vector) + continue; + typechecks++; + + if ( G_FLOAT(cn->ofs+0) == a && + G_FLOAT(cn->ofs+1) == b && + G_FLOAT(cn->ofs+2) == c) + { + return cn; + } + } + +// allocate a new one + cn = (void *)qccHunkAlloc (sizeof(QCC_def_t)); + cn->next = NULL; + pr.def_tail->next = cn; + pr.def_tail = cn; + + cn->type = type_vector; + cn->name = "IMMEDIATE"; + cn->constant = true; + cn->initialized = 1; + cn->scope = NULL; // always share immediates + cn->arraysize = 1; + +// copy the immediate to the global area + cn->ofs = QCC_GetFreeOffsetSpace (type_size[type_vector->type]); + + G_FLOAT(cn->ofs+0) = a; + G_FLOAT(cn->ofs+1) = b; + G_FLOAT(cn->ofs+2) = c; + + return cn; +} + +hashtable_t floatconstdefstable; +QCC_def_t *QCC_MakeFloatDef(float value) +{ + QCC_def_t *cn; + + union { + float f; + int i; + } fi; + + fi.f = value; + + cn = Hash_GetKey(&floatconstdefstable, fi.i); + if (cn) + return cn; + +// allocate a new one + cn = (void *)qccHunkAlloc (sizeof(QCC_def_t)); + cn->next = NULL; + pr.def_tail->next = cn; + pr.def_tail = cn; + + cn->s_file = s_file; + cn->s_line = pr_source_line; + cn->type = type_float; + cn->name = "IMMEDIATE"; + cn->constant = true; + cn->initialized = 1; + cn->scope = NULL; // always share immediates + cn->arraysize = 1; + +// copy the immediate to the global area + cn->ofs = QCC_GetFreeOffsetSpace (type_size[type_integer->type]); + + Hash_AddKey(&floatconstdefstable, fi.i, cn, qccHunkAlloc(sizeof(bucket_t))); + + G_FLOAT(cn->ofs) = value; + + + return cn; +} + +extern hashtable_t stringconstdefstable; +extern hashtable_t stringconstdefstable_dotranslate; +QCC_def_t *QCC_MakeStringDef(char *value) +{ + QCC_def_t *cn; + int string; + hashtable_t *tbl = dotranslate ? &stringconstdefstable_dotranslate : &stringconstdefstable; + char buf[64]; + + cn = pHash_Get(tbl, value); + if (cn) + return cn; + +// allocate a new one + cn = (void *)qccHunkAlloc (sizeof(QCC_def_t)); + cn->next = NULL; + pr.def_tail->next = cn; + pr.def_tail = cn; + + cn->type = type_string; + if(dotranslate > 0) + { + sprintf(buf, "dotranslate_%d", ++dotranslate_count); + cn->name = strdup(buf); + } + else + cn->name = "IMMEDIATE"; + cn->constant = true; + cn->initialized = 1; + cn->scope = NULL; // always share immediates + cn->arraysize = 1; + +// copy the immediate to the global area + cn->ofs = QCC_GetFreeOffsetSpace (type_size[type_integer->type]); + + string = QCC_CopyString (value); + + pHash_Add(tbl, strings+string, cn, qccHunkAlloc(sizeof(bucket_t))); + + G_INT(cn->ofs) = string; + + + return cn; +} + +QCC_type_t *QCC_PR_NewType (char *name, int basictype); +QCC_type_t *QCC_PointerTypeTo(QCC_type_t *type) +{ + QCC_type_t *newtype; + newtype = QCC_PR_NewType("POINTER TYPE", ev_pointer); + newtype->aux_type = type; + return newtype; +} + +int basictypefield[ev_union+1]; +char *basictypenames[] = { + "void", + "string", + "float", + "vector", + "entity", + "field", + "function", + "pointer", + "integer", + "struct", + "union" +}; + +QCC_def_t *QCC_MemberInParentClass(char *name, QCC_type_t *clas) +{ //if a member exists, return the member field (rather than mapped-to field) + QCC_type_t *mt; + QCC_def_t *def; + int p, np; + char membername[2048]; + + if (!clas) + { + def = QCC_PR_GetDef(NULL, name, NULL, 0, 0, false); + if (def && def->type->type == ev_field) //the member existed as a normal entity field. + return def; + return NULL; + } + + np = clas->num_parms; + for (p = 0, mt = clas->param; p < np; p++, mt = mt->next) + { + if (strcmp(mt->name, name)) + continue; + + //the parent has it. + + sprintf(membername, "%s::"MEMBERFIELDNAME, clas->name, mt->name); + def = QCC_PR_GetDef(NULL, membername, NULL, false, 0, false); + return def; + } + + return QCC_MemberInParentClass(name, clas->parentclass); +} + +//create fields for the types, instanciate the members to the fields. +//we retouch the parents each time to guarentee polymorphism works. +//FIXME: virtual methods will not work properly. Need to trace down to see if a parent already defined it +void QCC_PR_EmitFieldsForMembers(QCC_type_t *clas) +{ +//we created fields for each class when we defined the actual classes. +//we need to go through each member and match it to the offset of it's parent class, if overloaded, or create a new field if not.. + +//basictypefield is cleared before we do this +//we emit the parent's fields first (every time), thus ensuring that we don't reuse parent fields on a child class. + + char membername[2048]; + int p, np, a; + unsigned int o; + QCC_type_t *mt, *ft; + QCC_def_t *f, *m; + if (clas->parentclass != type_entity) //parents MUST have all their fields set or inheritance would go crazy. + QCC_PR_EmitFieldsForMembers(clas->parentclass); + + np = clas->num_parms; + mt = clas->param; + for (p = 0; p < np; p++, mt = mt->next) + { + sprintf(membername, "%s::"MEMBERFIELDNAME, clas->name, mt->name); + m = QCC_PR_GetDef(NULL, membername, NULL, false, 0, false); + + f = QCC_MemberInParentClass(mt->name, clas->parentclass); + if (f) + { + if (m->arraysize>1) + QCC_Error(ERR_INTERNAL, "FTEQCC does not support overloaded arrays of members"); + a=0; + for (o = 0; o < m->type->size; o++) + ((int *)qcc_pr_globals)[o+a*mt->size+m->ofs] = ((int *)qcc_pr_globals)[o+f->ofs]; + continue; + } + + for (a = 0; a < m->arraysize; a++) + { + //we need the type in here so saved games can still work without saving ints as floats. (would be evil) + ft = QCC_PR_NewType(basictypenames[mt->type], ev_field); + ft->aux_type = QCC_PR_NewType(basictypenames[mt->type], mt->type); + ft->aux_type->aux_type = type_void; + ft->size = ft->aux_type->size; + ft = QCC_PR_FindType(ft); + sprintf(membername, "__f_%s_%i", ft->name, ++basictypefield[mt->type]); + f = QCC_PR_GetDef(ft, membername, NULL, true, 1, true); + + for (o = 0; o < m->type->size; o++) + ((int *)qcc_pr_globals)[o+a*mt->size+m->ofs] = ((int *)qcc_pr_globals)[o+f->ofs]; + + f->references++; + } + } +} + +void QCC_PR_EmitClassFunctionTable(QCC_type_t *clas, QCC_type_t *childclas, QCC_def_t *ed, QCC_def_t **constructor) +{ //go through clas, do the virtual thing only if the child class does not override. + + char membername[2048]; + QCC_type_t *type; + QCC_type_t *oc; + int p; + + QCC_def_t *point, *member; + QCC_def_t *virt; + + if (clas->parentclass) + QCC_PR_EmitClassFunctionTable(clas->parentclass, childclas, ed, constructor); + + type = clas->param; + for (p = 0; p < clas->num_parms; p++, type = type->next) + { + for (oc = childclas; oc != clas; oc = oc->parentclass) + { + sprintf(membername, "%s::"MEMBERFIELDNAME, oc->name, type->name); + if (QCC_PR_GetDef(NULL, membername, NULL, false, 0, false)) + break; //a child class overrides. + } + if (oc != clas) + continue; + + if (type->type == ev_function) //FIXME: inheritance will not install all the member functions. + { + sprintf(membername, "%s::"MEMBERFIELDNAME, clas->name, type->name); + member = QCC_PR_GetDef(NULL, membername, NULL, false, 1, false); + if (!member) + { + QCC_PR_Warning(0, NULL, 0, "Member function %s was not defined", membername); + continue; + } + if (!strcmp(type->name, clas->name)) + { + *constructor = member; + } + point = QCC_PR_Statement(&pr_opcodes[OP_ADDRESS], ed, member, NULL); + sprintf(membername, "%s::%s", clas->name, type->name); + virt = QCC_PR_GetDef(type, membername, NULL, false, 1, false); + QCC_PR_Statement(&pr_opcodes[OP_STOREP_FNC], virt, point, NULL); + } + } +} + +//take all functions in the type, and parent types, and make sure the links all work properly. +void QCC_PR_EmitClassFromFunction(QCC_def_t *scope, char *tname) +{ + QCC_type_t *basetype; + + QCC_dfunction_t *df; + + QCC_def_t *virt; + QCC_def_t *ed, *oself, *self; + QCC_def_t *constructor = NULL; + +// int func; + + basetype = QCC_TypeForName(tname); + if (!basetype) + QCC_PR_ParseError(ERR_INTERNAL, "Type %s was not defined...", tname); + + + pr_scope = NULL; + memset(basictypefield, 0, sizeof(basictypefield)); + QCC_PR_EmitFieldsForMembers(basetype); + + + + + pr_scope = scope; + + df = &functions[numfunctions]; + numfunctions++; + + df->s_file = 0; + df->s_name = 0; + df->first_statement = numstatements; + df->parm_size[0] = 1; + df->numparms = 0; + df->parm_start = numpr_globals; + + G_FUNCTION(scope->ofs) = df - functions; + + //locals here... + ed = QCC_PR_GetDef(type_entity, "ent", pr_scope, true, 1, false); + + virt = QCC_PR_GetDef(type_function, "spawn", NULL, false, 0, false); + if (!virt) + QCC_Error(ERR_INTERNAL, "spawn function was not defined\n"); + QCC_PR_SimpleStatement(OP_CALL0, virt->ofs, 0, 0, false); //calling convention doesn't come into it. + + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_ENT], &def_ret, ed, NULL)); + + ed->references = 1; //there may be no functions. + + + QCC_PR_EmitClassFunctionTable(basetype, basetype, ed, &constructor); + + if (constructor) + { //self = ent; + self = QCC_PR_GetDef(type_entity, "self", NULL, false, 0, false); + oself = QCC_PR_GetDef(type_entity, "oself", scope, true, 1, false); + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_ENT], self, oself, NULL)); + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_ENT], ed, self, NULL)); //return to our old self. boom boom. + QCC_PR_SimpleStatement(OP_CALL0, constructor->ofs, 0, 0, false); + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_ENT], oself, self, NULL)); + } + + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_RETURN], ed, NULL, NULL)); //apparently we do actually have to return something. *sigh*... + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_DONE], NULL, NULL, NULL)); + + + + QCC_WriteAsmFunction(scope, df->first_statement, df->parm_start); + pr.localvars = NULL; + + + locals_end = numpr_globals + basetype->size; + df->locals = locals_end - df->parm_start; +} +/* +============ +PR_ParseValue + +Returns the global ofs for the current token +============ +*/ +QCC_def_t *QCC_PR_ParseValue (QCC_type_t *assumeclass, pbool allowarrayassign) +{ + QCC_def_t *ao=NULL; //arrayoffset + QCC_def_t *d, *nd, *od; + char *name; + QCC_dstatement_t *st; + int i; + static QCC_def_t intrinsic = { + NULL, // QCC_type_t *type; + NULL, // char *name; + NULL, // struct QCC_def_s *next; + NULL, // struct QCC_def_s *nextlocal; //provides a chain of local variables for the opt_locals_marshalling optimisation. + 0, // gofs_t ofs; + NULL, // struct QCC_def_s *scope; // function the var was defined in, or NULL + 1, // int initialized; // 1 when a declaration included "= immediate" + 1, // int constant; // 1 says we can use the value over and over again + 1, // int references; + 0, // int timescalled; //part of the opt_stripfunctions optimisation. + 0, // int s_file; + 0, // int s_line; + 0, // int arraysize; + 0, // pbool shared; + 0, // pbool saved; + 0, // pbool isstatic; + NULL // temp_t *temp; + }; + + char membername[2048]; + +// if the token is an immediate, allocate a constant for it + if (pr_token_type == tt_immediate) + return QCC_PR_ParseImmediate (); + + if (QCC_PR_CheckToken("[")) //reacc support + { //looks like a funky vector. :) + vec3_t v; + pr_immediate_type = type_vector; + v[0] = pr_immediate._float; + QCC_PR_Lex(); + v[1] = pr_immediate._float; + QCC_PR_Lex(); + v[2] = pr_immediate._float; + pr_immediate.vector[0] = v[0]; + pr_immediate.vector[1] = v[1]; + pr_immediate.vector[2] = v[2]; + pr_immediate_type = type_vector; + d = QCC_PR_ParseImmediate(); + QCC_PR_Expect("]"); + return d; + } + name = QCC_PR_ParseName (); + + if (assumeclass && assumeclass->parentclass) // 'testvar' becomes 'self::testvar' + { //try getting a member. + QCC_type_t *type; + type = assumeclass; + d = NULL; + while(type != type_entity && type) + { + sprintf(membername, "%s::"MEMBERFIELDNAME, type->name, name); + od = d = QCC_PR_GetDef (NULL, membername, pr_scope, false, 0, false); + if (d) + break; + + type = type->parentclass; + } + if (!d) + od = d = QCC_PR_GetDef (NULL, name, pr_scope, false, 0, false); + } + else + +// look through the defs + od = d = QCC_PR_GetDef (NULL, name, pr_scope, false, 0, false); + + if (!d) + { + if ( (!strcmp(name, "random" )) || + (!strcmp(name, "randomv")) || + (!strcmp(name, "entnum")) || + (!strcmp(name, "_")) ) //intrinsics, any old function with no args will do. + { + intrinsic.name = name; + intrinsic.type = type_function; // can't put that in the static var + od = d = &intrinsic; + } + else if (keyword_class && !strcmp(name, "this")) + { + if (!pr_classtype) + QCC_PR_ParseError(ERR_NOTANAME, "Cannot use 'this' outside of an OO function\n"); + od = QCC_PR_GetDef(NULL, "self", NULL, true, 1, false); + od = d = QCC_PR_DummyDef(pr_classtype, "this", pr_scope, 1, od->ofs, true, false); + } + else if (keyword_class && !strcmp(name, "super")) + { + if (!pr_classtype) + QCC_PR_ParseError(ERR_NOTANAME, "Cannot use 'super' outside of an OO function\n"); + od = QCC_PR_GetDef(NULL, "self", NULL, true, 1, false); + od = d = QCC_PR_DummyDef(pr_classtype, "super", pr_scope, 1, od->ofs, true, false); + } + else + { + od = d = QCC_PR_GetDef (type_variant, name, pr_scope, true, 1, false); + if (!d) + QCC_PR_ParseError (ERR_UNKNOWNVALUE, "Unknown value \"%s\"", name); + else + { + QCC_PR_ParseWarning (ERR_UNKNOWNVALUE, "Unknown value \"%s\".", name); + } + } + } + +reloop: + + +//FIXME: Make this work with double arrays/2nd level structures. +//Should they just jump back to here? + if (QCC_PR_CheckToken("[")) + { + QCC_type_t *newtype; + if (ao) + { + numstatements--; //remove the last statement + + nd = QCC_PR_Expression (TOP_PRIORITY, 0); + QCC_PR_Expect("]"); + + if (d->type->size != 1) //we need to multiply it to find the offset. + { + if (ao->type->type == ev_integer) + nd = QCC_PR_Statement(&pr_opcodes[OP_MUL_I], nd, QCC_MakeIntDef(d->type->size), NULL); //get add part + else if (ao->type->type == ev_float) + nd = QCC_PR_Statement(&pr_opcodes[OP_MUL_F], nd, QCC_MakeFloatDef((float)d->type->size), NULL); //get add part + else + { + QCC_PR_ParseError(ERR_BADARRAYINDEXTYPE, "Array offset is not of integer or float type"); + nd = NULL; + } + } + + if (nd->type->type == ao->type->type) + { + if (ao->type->type == ev_integer) + ao = QCC_PR_Statement(&pr_opcodes[OP_ADD_I], ao, nd, NULL); //get add part + else if (ao->type->type == ev_float) + ao = QCC_PR_Statement(&pr_opcodes[OP_ADD_F], ao, nd, NULL); //get add part + else + { + QCC_PR_ParseError(ERR_BADARRAYINDEXTYPE, "Array offset is not of integer or float type"); + nd = NULL; + } + } + else + { + if (nd->type->type == ev_float) + nd = QCC_PR_Statement (&pr_opcodes[OP_CONV_FTOI], nd, 0, NULL); + ao = QCC_PR_Statement(&pr_opcodes[OP_ADD_I], ao, nd, NULL); //get add part + } + + newtype = d->type; + d = od; + } + else + { + ao = QCC_PR_Expression (TOP_PRIORITY, 0); + QCC_PR_Expect("]"); + + if (QCC_OPCodeValid(&pr_opcodes[OP_LOADA_F]) && d->type->size != 1) //we need to multiply it to find the offset. + { + if (ao->type->type == ev_integer) + ao = QCC_PR_Statement(&pr_opcodes[OP_MUL_I], ao, QCC_MakeIntDef(d->type->size), NULL); //get add part + else if (ao->type->type == ev_float) + ao = QCC_PR_Statement(&pr_opcodes[OP_MUL_F], ao, QCC_MakeFloatDef((float)d->type->size), NULL); //get add part + else + { + nd = NULL; + QCC_PR_ParseError(ERR_BADARRAYINDEXTYPE, "Array offset is not of integer or float type"); + } + } + + newtype = d->type; + } + if (ao->type->type == ev_integer) + { + switch(newtype->type) + { + case ev_float: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_F], d, ao, NULL); //get pointer to precise def. + break; + case ev_string: + if (d->arraysize <= 1) + { + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADP_C], d, QCC_PR_Statement (&pr_opcodes[OP_CONV_ITOF], ao, 0, NULL), NULL); //get pointer to precise def. + newtype = nd->type;//don't be fooled + } + else + { + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_S], d, ao, NULL); //get pointer to precise def. + } + break; + case ev_vector: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_V], d, ao, NULL); //get pointer to precise def. + break; + case ev_entity: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_ENT], d, ao, NULL); //get pointer to precise def. + break; + case ev_field: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_FLD], d, ao, NULL); //get pointer to precise def. + break; + case ev_function: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_FNC], d, ao, NULL); //get pointer to precise def. + nd->type = d->type; + break; + case ev_pointer: + if (ao->constant && !G_INT(ao->ofs)) + ao->ofs = 0; + if (d->arraysize>1) //use the array + { + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_I], d, ao, NULL); //get pointer to precise def. + nd->type = d->type->aux_type; + } + else + { //dereference the pointer. + switch(newtype->aux_type->type) + { + case ev_pointer: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADP_I], d, ao, NULL); //get pointer to precise def. + nd->type = d->type->aux_type; + break; + case ev_float: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADP_F], d, ao, NULL); //get pointer to precise def. + nd->type = d->type->aux_type; + break; + case ev_vector: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADP_V], d, ao, NULL); //get pointer to precise def. + nd->type = d->type->aux_type; + break; + case ev_integer: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADP_I], d, ao, NULL); //get pointer to precise def. + nd->type = d->type->aux_type; + break; + default: + QCC_PR_ParseError(ERR_NOVALIDOPCODES, "No op available. Try assembler"); + nd = NULL; + break; + } + } + break; + case ev_integer: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_I], d, ao, NULL); //get pointer to precise def. + break; + case ev_struct: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_I], d, ao, NULL); //get pointer to precise def. + nd->type = d->type; + break; + default: + QCC_PR_ParseError(ERR_NOVALIDOPCODES, "No op available. Try assembler"); + nd = NULL; + break; + } + d=nd; + } + else if (ao->type->type == ev_float) + { + if (qcc_targetformat == QCF_HEXEN2) + { //hexen2 style retrieval, mixed with q1 style assignments... + if (QCC_PR_CheckToken("=")) //(hideous concept) + { + QCC_def_t *funcretr; + QCC_def_t *args[2]; + if (d->scope) + QCC_PR_ParseError(0, "Scoped array without specific engine support"); + + funcretr = QCC_PR_GetDef(type_function, qcva("ArraySet*%s", d->name), NULL, true, 1, false); + nd = QCC_PR_Expression(TOP_PRIORITY, 0); + if (nd->type->type != d->type->type) + QCC_PR_ParseErrorPrintDef(ERR_TYPEMISMATCH, d, "Type Mismatch on array assignment"); + + args[0] = ao; + args[1] = nd; + return QCC_PR_GenerateFunctionCall(funcretr, args, 2); + } + + switch(newtype->type) + { + case ev_float: + nd = QCC_PR_Statement(&pr_opcodes[OP_FETCH_GBL_F], d, ao, &st); //get pointer to precise def. + st->a = d->ofs; + break; + case ev_vector: + nd = QCC_PR_Statement(&pr_opcodes[OP_FETCH_GBL_V], d, ao, &st); //get pointer to precise def. + st->a = d->ofs; + break; + case ev_string: + nd = QCC_PR_Statement(&pr_opcodes[OP_FETCH_GBL_S], d, ao, &st); //get pointer to precise def. + st->a = d->ofs; + break; + case ev_entity: + nd = QCC_PR_Statement(&pr_opcodes[OP_FETCH_GBL_E], d, ao, &st); //get pointer to precise def. + st->a = d->ofs; + break; + case ev_function: + nd = QCC_PR_Statement(&pr_opcodes[OP_FETCH_GBL_FNC], d, ao, &st); //get pointer to precise def. + st->a = d->ofs; + break; + default: + QCC_PR_ParseError(ERR_NOVALIDOPCODES, "No op available. Try assembler"); + nd = NULL; + break; + } + QCC_FreeTemp(d); + QCC_FreeTemp(ao); + + d=nd; + d->type = newtype; + return d; + } + else + { + if (!QCC_OPCodeValid(&pr_opcodes[OP_LOADA_F])) //q1 compatible. + { //you didn't see this, okay? + QCC_def_t *funcretr; + if (d->scope) + QCC_PR_ParseError(0, "Scoped array without specific engine support"); + + if (allowarrayassign && QCC_PR_CheckToken("=")) + { + QCC_def_t *args[2]; + + funcretr = QCC_PR_GetDef(type_function, qcva("ArraySet*%s", d->name), NULL, true, 1, false); + + nd = QCC_PR_Expression(TOP_PRIORITY, 0); + if (nd->type->type != d->type->type) + QCC_PR_ParseErrorPrintDef(ERR_TYPEMISMATCH, d, "Type Mismatch on array assignment"); + + args[0] = ao; + args[1] = nd; + qcc_usefulstatement=true; + nd = QCC_PR_GenerateFunctionCall(funcretr, args, 2); + nd->type = d->type->aux_type; + } + else + { + QCC_def_t *args[1]; + + def_parms[0].type = type_float; + funcretr = QCC_PR_GetDef(type_function, qcva("ArrayGet*%s", d->name), NULL, true, 1, false); + + args[0] = ao; + nd = QCC_PR_GenerateFunctionCall(funcretr, args, 1); + nd->type = d->type->aux_type; + } + } + else + { + switch(newtype->type) + { + case ev_pointer: + if (d->arraysize>1) //use the array + { + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_I], d, QCC_PR_Statement (&pr_opcodes[OP_CONV_FTOI], ao, 0, NULL), NULL); //get pointer to precise def. + nd->type = d->type->aux_type; + } + else + { //dereference the pointer. + switch(newtype->aux_type->type) + { + case ev_pointer: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADP_I], d, QCC_PR_Statement (&pr_opcodes[OP_CONV_FTOI], ao, 0, NULL), NULL); //get pointer to precise def. + nd->type = d->type->aux_type; + break; + case ev_float: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADP_F], d, QCC_PR_Statement (&pr_opcodes[OP_CONV_FTOI], ao, 0, NULL), NULL); //get pointer to precise def. + nd->type = d->type->aux_type; + break; + case ev_vector: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADP_V], d, QCC_PR_Statement (&pr_opcodes[OP_CONV_FTOI], ao, 0, NULL), NULL); //get pointer to precise def. + nd->type = d->type->aux_type; + break; + case ev_integer: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADP_I], d, QCC_PR_Statement (&pr_opcodes[OP_CONV_FTOI], ao, 0, NULL), NULL); //get pointer to precise def. + nd->type = d->type->aux_type; + break; + default: + QCC_PR_ParseError(ERR_NOVALIDOPCODES, "No op available. Try assembler"); + nd = NULL; + break; + } + } + break; + + case ev_float: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_F], d, QCC_PR_Statement (&pr_opcodes[OP_CONV_FTOI], ao, 0, NULL), NULL); //get pointer to precise def. + break; + case ev_string: + if (d->arraysize <= 1) + { + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADP_C], d, ao, NULL); //get pointer to precise def. + newtype = nd->type;//don't be fooled + } + else + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_S], d, QCC_PR_Statement (&pr_opcodes[OP_CONV_FTOI], ao, 0, NULL), NULL); //get pointer to precise def. + break; + case ev_vector: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_V], d, QCC_PR_Statement (&pr_opcodes[OP_CONV_FTOI], ao, 0, NULL), NULL); //get pointer to precise def. + break; + case ev_entity: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_ENT], d, QCC_PR_Statement (&pr_opcodes[OP_CONV_FTOI], ao, 0, NULL), NULL); //get pointer to precise def. + break; + case ev_field: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_FLD], d, QCC_PR_Statement (&pr_opcodes[OP_CONV_FTOI], ao, 0, NULL), NULL); //get pointer to precise def. + break; + case ev_function: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_FNC], d, QCC_PR_Statement (&pr_opcodes[OP_CONV_FTOI], ao, 0, NULL), NULL); //get pointer to precise def. + nd->type = d->type; + break; + case ev_integer: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_I], d, QCC_PR_Statement (&pr_opcodes[OP_CONV_FTOI], ao, 0, NULL), NULL); //get pointer to precise def. + break; + + case ev_struct: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_I], d, QCC_PR_Statement (&pr_opcodes[OP_CONV_FTOI], ao, 0, NULL), NULL); //get pointer to precise def. + nd->type = d->type; + break; + default: + QCC_PR_ParseError(ERR_NOVALIDOPCODES, "No op available. Try assembler"); + nd = NULL; + break; + } + } + } + d=nd; + } + else + QCC_PR_ParseError(ERR_BADARRAYINDEXTYPE, "Array offset is not of integer or float type"); + + d->type = newtype; + goto reloop; + } + + + i = d->type->type; + if (i == ev_pointer) + { + int j; + QCC_type_t *type; + if (QCC_PR_CheckToken(".") || QCC_PR_CheckToken("->")) + { + for (i = d->type->num_parms, type = d->type+1; i; i--, type++) + { + if (QCC_PR_CheckName(type->name)) + { + //give result + if (ao) + { + numstatements--; //remove the last statement + d = od; + + nd = QCC_MakeIntDef(type->ofs); + ao = QCC_PR_Statement(&pr_opcodes[OP_ADD_I], ao, nd, NULL); //get add part + + //so that we may offset it and readd it. + } + else + ao = QCC_MakeIntDef(type->ofs); + switch (type->type) + { + case ev_float: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADP_F], d, ao, NULL); //get pointer to precise def. + break; + case ev_string: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADP_S], d, ao, NULL); //get pointer to precise def. + break; + case ev_vector: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADP_V], d, ao, NULL); //get pointer to precise def. + break; + case ev_entity: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADP_ENT], d, ao, NULL); //get pointer to precise def. + break; + case ev_field: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADP_FLD], d, ao, NULL); //get pointer to precise def. + break; + case ev_function: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADP_FNC], d, ao, NULL); //get pointer to precise def. + nd->type = type; + break; + case ev_integer: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADP_I], d, ao, NULL); //get pointer to precise def. + break; + +// case ev_struct: + //no suitable op. +// nd = QCC_PR_Statement(&pr_opcodes[OP_LOADP_I], d, ao, NULL); //get pointer to precise def. +// nd->type = type; +// break; + default: + QCC_PR_ParseError(ERR_NOVALIDOPCODES, "No op available. Try assembler"); + nd = NULL; + break; + } + + d=nd; + break; + } + if (type->num_parms) + { + for (j = type->num_parms; j;j--) + type++; + } + } + if (!i) + QCC_PR_ParseError (ERR_MEMBERNOTVALID, "\"%s\" is not a member of \"%s\"", pr_token, od->type->name); + + goto reloop; + } + } + else if (i == ev_struct || i == ev_union) + { + int j; + QCC_type_t *type; + if (QCC_PR_CheckToken(".") || QCC_PR_CheckToken("->")) + { + for (i = d->type->num_parms, type = d->type+1; i; i--, type++) + { + if (QCC_PR_CheckName(type->name)) + { + //give result + if (ao) + { + numstatements--; //remove the last statement + d = od; + + nd = QCC_MakeIntDef(type->ofs); + ao = QCC_PR_Statement(&pr_opcodes[OP_ADD_I], ao, nd, NULL); //get add part + + //so that we may offset it and readd it. + } + else + ao = QCC_MakeIntDef(type->ofs); + switch (type->type) + { + case ev_float: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_F], d, ao, NULL); //get pointer to precise def. + break; + case ev_string: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_S], d, ao, NULL); //get pointer to precise def. + break; + case ev_vector: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_V], d, ao, NULL); //get pointer to precise def. + break; + case ev_entity: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_ENT], d, ao, NULL); //get pointer to precise def. + break; + case ev_field: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_FLD], d, ao, NULL); //get pointer to precise def. + break; + case ev_function: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_FNC], d, ao, NULL); //get pointer to precise def. + nd->type = type; + break; + case ev_integer: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_I], d, ao, NULL); //get pointer to precise def. + break; + +// case ev_struct: + //no suitable op. +// nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_I], d, ao, NULL); //get pointer to precise def. +// nd->type = type; +// break; + default: + QCC_PR_ParseError(ERR_NOVALIDOPCODES, "No op available. Try assembler"); + nd = NULL; + break; + } + + d=nd; + break; + } + if (type->num_parms) + { + for (j = type->num_parms; j;j--) + type++; + } + } + if (!i) + QCC_PR_ParseError (ERR_MEMBERNOTVALID, "\"%s\" is not a member of \"%s\"", pr_token, od->type->name); + + goto reloop; + } + } + +/* if (d->type->type == ev_pointer) + { //expand now, not in function call/maths parsing + switch(d->type->aux_type->type) + { + case ev_string: + d = QCC_PR_Statement(&pr_opcodes[OP_LOADP_S], d, NULL, NULL); + break; + case ev_float: + d = QCC_PR_Statement(&pr_opcodes[OP_LOADP_F], d, NULL, NULL); + break; + } + } +*/ + if (!keyword_class) + return d; + + if (d->type->parentclass||d->type->type == ev_entity) //class + { + if (QCC_PR_CheckToken(".") || QCC_PR_CheckToken("->")) + { + QCC_def_t *field; + if (QCC_PR_CheckToken("(")) + { + field = QCC_PR_Expression(TOP_PRIORITY, 0); + QCC_PR_Expect(")"); + } + else + field = QCC_PR_ParseValue(d->type, false); + if (field->type->type == ev_field) + { + if (!field->type->aux_type) + { + QCC_PR_ParseWarning(ERR_INTERNAL, "Field with null aux_type"); + return QCC_PR_Statement(&pr_opcodes[OP_LOAD_FLD], d, field, NULL); + } + else + { + switch(field->type->aux_type->type) + { + default: + QCC_PR_ParseError(ERR_INTERNAL, "Bad field type"); + return d; + case ev_integer: + return QCC_PR_Statement(&pr_opcodes[OP_LOAD_I], d, field, NULL); + case ev_field: + d = QCC_PR_Statement(&pr_opcodes[OP_LOAD_FLD], d, field, NULL); + nd = (void *)qccHunkAlloc (sizeof(QCC_def_t)); + memset (nd, 0, sizeof(QCC_def_t)); + nd->type = field->type->aux_type; + nd->ofs = d->ofs; + nd->temp = d->temp; + nd->constant = false; + nd->name = d->name; + return nd; + case ev_float: + return QCC_PR_Statement(&pr_opcodes[OP_LOAD_F], d, field, NULL); + case ev_string: + return QCC_PR_Statement(&pr_opcodes[OP_LOAD_S], d, field, NULL); + case ev_vector: + return QCC_PR_Statement(&pr_opcodes[OP_LOAD_V], d, field, NULL); + case ev_function: + { //complicated for a typecast + d = QCC_PR_Statement(&pr_opcodes[OP_LOAD_FNC], d, field, NULL); + nd = (void *)qccHunkAlloc (sizeof(QCC_def_t)); + memset (nd, 0, sizeof(QCC_def_t)); + nd->type = field->type->aux_type; + nd->ofs = d->ofs; + nd->temp = d->temp; + nd->constant = false; + nd->name = d->name; + return nd; + + } + case ev_entity: + return QCC_PR_Statement(&pr_opcodes[OP_LOAD_ENT], d, field, NULL); + } + } + } + else + QCC_PR_IncludeChunk(".", false, NULL); + } + } + + return d; +} + + +/* +============ +PR_Term +============ +*/ +QCC_def_t *QCC_PR_Term (void) +{ + QCC_def_t *e, *e2; + etype_t t; + if (pr_token_type == tt_punct) //a little extra speed... + { + if (QCC_PR_CheckToken("++")) + { + qcc_usefulstatement=true; + e = QCC_PR_Term (); + if (e->constant) + QCC_PR_ParseWarning(WARN_ASSIGNMENTTOCONSTANT, "Assignment to constant %s", e->name); + if (e->temp) + QCC_PR_ParseWarning(WARN_ASSIGNMENTTOCONSTANT, "Hey! That's a temp! ++ operators cannot work on temps!"); + switch (e->type->type) + { + case ev_integer: + QCC_PR_Statement3(&pr_opcodes[OP_ADD_I], e, QCC_MakeIntDef(1), e, false); + break; + case ev_float: + QCC_PR_Statement3(&pr_opcodes[OP_ADD_F], e, QCC_MakeFloatDef(1), e, false); + break; + default: + QCC_PR_ParseError(ERR_BADPLUSPLUSOPERATOR, "++ operator on unsupported type"); + break; + } + return e; + } + else if (QCC_PR_CheckToken("--")) + { + qcc_usefulstatement=true; + e = QCC_PR_Term (); + if (e->constant) + QCC_PR_ParseWarning(WARN_ASSIGNMENTTOCONSTANT, "Assignment to constant %s", e->name); + if (e->temp) + QCC_PR_ParseWarning(WARN_ASSIGNMENTTOCONSTANT, "Hey! That's a temp! -- operators cannot work on temps!"); + switch (e->type->type) + { + case ev_integer: + QCC_PR_Statement3(&pr_opcodes[OP_SUB_I], e, QCC_MakeIntDef(1), e, false); + break; + case ev_float: + QCC_PR_Statement3(&pr_opcodes[OP_SUB_F], e, QCC_MakeFloatDef(1), e, false); + break; + default: + QCC_PR_ParseError(ERR_BADPLUSPLUSOPERATOR, "-- operator on unsupported type"); + break; + } + return e; + } + + if (QCC_PR_CheckToken ("!")) + { + e = QCC_PR_Expression (NOT_PRIORITY, EXPR_DISALLOW_COMMA|EXPR_WARN_ABOVE_1); + t = e->type->type; + if (t == ev_float) + e2 = QCC_PR_Statement (&pr_opcodes[OP_NOT_F], e, 0, NULL); + else if (t == ev_string) + e2 = QCC_PR_Statement (&pr_opcodes[OP_NOT_S], e, 0, NULL); + else if (t == ev_entity) + e2 = QCC_PR_Statement (&pr_opcodes[OP_NOT_ENT], e, 0, NULL); + else if (t == ev_vector) + e2 = QCC_PR_Statement (&pr_opcodes[OP_NOT_V], e, 0, NULL); + else if (t == ev_function) + e2 = QCC_PR_Statement (&pr_opcodes[OP_NOT_FNC], e, 0, NULL); + else if (t == ev_integer) + e2 = QCC_PR_Statement (&pr_opcodes[OP_NOT_FNC], e, 0, NULL); //functions are integer values too. + else if (t == ev_pointer) + e2 = QCC_PR_Statement (&pr_opcodes[OP_NOT_FNC], e, 0, NULL); //Pointers are too. + else + { + e2 = NULL; // shut up compiler warning; + QCC_PR_ParseError (ERR_BADNOTTYPE, "type mismatch for !"); + } + return e2; + } + + else if (QCC_PR_CheckToken ("&")) + { + int st = numstatements; + e = QCC_PR_Expression (UNARY_PRIORITY, 0); + t = e->type->type; + + if (st != numstatements) + //woo, something like ent.field? + { + if ((unsigned)(statements[numstatements-1].op - OP_LOAD_F) < 6 || statements[numstatements-1].op == OP_LOAD_I || statements[numstatements-1].op == OP_LOAD_P) + { + statements[numstatements-1].op = OP_ADDRESS; + e->type = QCC_PR_PointerType(e->type); + return e; + } + else //this is a restriction that could be lifted, I just want to make sure that I got all the bits first. + { + QCC_PR_ParseError (ERR_BADNOTTYPE, "type mismatch for '&' Must be singular expression or field reference"); + return e; + } + } +// QCC_PR_ParseWarning(0, "debug: &global"); + + if (!QCC_OPCodeValid(&pr_opcodes[OP_GLOBALADDRESS])) + QCC_PR_ParseError (ERR_BADEXTENSION, "Cannot use addressof operator ('&') on a global. Please use the FTE target."); + + e2 = QCC_PR_Statement (&pr_opcodes[OP_GLOBALADDRESS], e, 0, NULL); + e2->type = QCC_PR_PointerType(e->type); + return e2; + } + else if (QCC_PR_CheckToken ("*")) + { + e = QCC_PR_Expression (UNARY_PRIORITY, 0); + t = e->type->type; + + if (t != ev_pointer) + QCC_PR_ParseError (ERR_BADNOTTYPE, "type mismatch for *"); + + switch(e->type->aux_type->type) + { + case ev_float: + e2 = QCC_PR_Statement (&pr_opcodes[OP_LOADP_F], e, 0, NULL); + break; + case ev_string: + e2 = QCC_PR_Statement (&pr_opcodes[OP_LOADP_S], e, 0, NULL); + break; + case ev_vector: + e2 = QCC_PR_Statement (&pr_opcodes[OP_LOADP_V], e, 0, NULL); + break; + case ev_entity: + e2 = QCC_PR_Statement (&pr_opcodes[OP_LOADP_ENT], e, 0, NULL); + break; + case ev_field: + e2 = QCC_PR_Statement (&pr_opcodes[OP_LOADP_FLD], e, 0, NULL); + break; + case ev_function: + e2 = QCC_PR_Statement (&pr_opcodes[OP_LOADP_FLD], e, 0, NULL); + break; + case ev_integer: + e2 = QCC_PR_Statement (&pr_opcodes[OP_LOADP_I], e, 0, NULL); + break; + case ev_pointer: + e2 = QCC_PR_Statement (&pr_opcodes[OP_LOADP_I], e, 0, NULL); + break; + + default: + QCC_PR_ParseError (ERR_BADNOTTYPE, "type mismatch for * (unrecognised type)"); + e2 = NULL; + break; + } + + e2->type = e->type->aux_type; + return e2; + } + else if (QCC_PR_CheckToken ("-")) + { + e = QCC_PR_Expression (UNARY_PRIORITY, 0); + + switch(e->type->type) + { + case ev_float: + e2 = QCC_PR_Statement (&pr_opcodes[OP_SUB_F], QCC_MakeFloatDef(0), e, NULL); + break; + case ev_vector: + e2 = QCC_PR_Statement (&pr_opcodes[OP_SUB_V], QCC_MakeVectorDef(0, 0, 0), e, NULL); + break; + case ev_integer: + e2 = QCC_PR_Statement (&pr_opcodes[OP_SUB_I], QCC_MakeIntDef(0), e, NULL); + break; + default: + QCC_PR_ParseError (ERR_BADNOTTYPE, "type mismatch for -"); + e2 = NULL; + break; + } + return e2; + } + else if (QCC_PR_CheckToken ("+")) + { + e = QCC_PR_Expression (UNARY_PRIORITY, 0); + + switch(e->type->type) + { + case ev_float: + e2 = e; + break; + case ev_vector: + e2 = e; + break; + case ev_integer: + e2 = e; + break; + default: + QCC_PR_ParseError (ERR_BADNOTTYPE, "type mismatch for +"); + e2 = NULL; + break; + } + return e2; + } + + if (QCC_PR_CheckToken ("(")) + { + if (QCC_PR_CheckKeyword(keyword_float, "float")) //check for type casts + { + QCC_PR_Expect (")"); + e = QCC_PR_Term(); + if (e->type->type == ev_float) + return e; + else if (e->type->type == ev_integer) + return QCC_PR_Statement (&pr_opcodes[OP_CONV_ITOF], e, 0, NULL); + else if (e->type->type == ev_function) + return e; + // else + // QCC_PR_ParseError ("invalid typecast"); + + QCC_PR_ParseWarning (0, "Not all vars make sence as floats"); + + e2 = (void *)qccHunkAlloc (sizeof(QCC_def_t)); + memset (e2, 0, sizeof(QCC_def_t)); + e2->type = type_float; + e2->ofs = e->ofs; + e2->constant = true; + e2->temp = e->temp; + return e2; + } + else if (QCC_PR_CheckKeyword(keyword_class, "class")) + { + QCC_type_t *classtype = QCC_TypeForName(QCC_PR_ParseName()); + if (!classtype) + QCC_PR_ParseError(ERR_NOTANAME, "Class not defined for cast"); + + QCC_PR_Expect (")"); + e = QCC_PR_Term(); + e2 = (void *)qccHunkAlloc (sizeof(QCC_def_t)); + memset (e2, 0, sizeof(QCC_def_t)); + e2->type = classtype; + e2->ofs = e->ofs; + e2->constant = true; + e2->temp = e->temp; + return e2; + } + else if (QCC_PR_CheckKeyword(keyword_integer, "integer")) //check for type casts + { + QCC_PR_Expect (")"); + e = QCC_PR_Term(); + if (e->type->type == ev_integer) + return e; + else if (e->type->type == ev_float) + return QCC_PR_Statement (&pr_opcodes[OP_CONV_FTOI], e, 0, NULL); + else + QCC_PR_ParseError (ERR_BADTYPECAST, "invalid typecast"); + } + else if (QCC_PR_CheckKeyword(keyword_int, "int")) //check for type casts + { + QCC_PR_Expect (")"); + e = QCC_PR_Term(); + if (e->type->type == ev_integer) + return e; + else if (e->type->type == ev_float) + return QCC_PR_Statement (&pr_opcodes[OP_CONV_FTOI], e, 0, NULL); + else + QCC_PR_ParseError (ERR_BADTYPECAST, "invalid typecast"); + } + else + { + pbool oldcond = conditional; + conditional = conditional?2:0; + e = QCC_PR_Expression (TOP_PRIORITY, 0); + QCC_PR_Expect (")"); + conditional = oldcond; + } + return e; + } + } + return QCC_PR_ParseValue (pr_classtype, true); +} + + +int QCC_canConv(QCC_def_t *from, etype_t to) +{ + if (from->type->type == to) + return 0; + + if (from->type->type == ev_vector && to == ev_float) + return 4; + + if (pr_classtype) + { + if (from->type->type == ev_field) + { + if (from->type->aux_type->type == to) + return 1; + } + } + +/* if (from->type->type == ev_pointer && from->type->aux_type->type == to) + return 1; + + if (QCC_ShouldConvert(from, to)>=0) + return 1; +*/ + if (from->type->type == ev_integer && to == ev_function) + return 1; + + return -100; +} +/* +============== +PR_Expression +============== +*/ + +QCC_def_t *QCC_PR_Expression (int priority, int exprflags) +{ + QCC_dstatement32_t *st; + QCC_opcode_t *op, *oldop; + + QCC_opcode_t *bestop; + int numconversions, c; + + int opnum; + + QCC_def_t *e, *e2; + etype_t type_a, type_b, type_c; + + if (priority == 0) + return QCC_PR_Term (); + + e = QCC_PR_Expression (priority-1, exprflags); + + while (1) + { + if (priority == 1) + { + if (QCC_PR_CheckToken ("(") ) + { + qcc_usefulstatement=true; + e = QCC_PR_ParseFunctionCall (e); + } + if (QCC_PR_CheckToken ("?")) + { + QCC_dstatement32_t *fromj, *elsej; + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_IFNOT], e, NULL, &fromj)); + e = QCC_PR_Expression(TOP_PRIORITY, 0); + e2 = QCC_GetTemp(e->type); + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[(e2->type->size>=3)?OP_STORE_V:OP_STORE_F], e, e2, NULL)); + //e2 can be stomped upon until its reused anyway + QCC_UnFreeTemp(e2); + + QCC_PR_Expect(":"); + QCC_PR_Statement(&pr_opcodes[OP_GOTO], NULL, NULL, &elsej); + fromj->b = &statements[numstatements] - fromj; + e = QCC_PR_Expression(TOP_PRIORITY, 0); + + if (typecmp(e->type, e2->type) != 0) + QCC_PR_ParseError(0, "Ternary operator with mismatching types\n"); + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[(e2->type->size>=3)?OP_STORE_V:OP_STORE_F], e, e2, NULL)); + QCC_UnFreeTemp(e2); + + elsej->a = &statements[numstatements] - elsej; + return e2; + } + } + + opnum=0; + + if (pr_token_type == tt_immediate) + { + if (pr_immediate_type->type == ev_float) + if (pr_immediate._float < 0) //hehehe... was a minus all along... + { + QCC_PR_IncludeChunk(pr_token, true, NULL); + strcpy(pr_token, "+");//two negatives would make a positive. + pr_token_type = tt_punct; + } + } + + if (pr_token_type != tt_punct) + { + QCC_PR_ParseWarning(WARN_UNEXPECTEDPUNCT, "Expected punctuation"); + } + + //go straight for the correct priority. + for (op = opcodeprioritized[priority][opnum]; op; op = opcodeprioritized[priority][++opnum]) +// for (op=pr_opcodes ; op->name ; op++) + { +// if (op->priority != priority) +// continue; + if (!QCC_PR_CheckToken (op->name)) + continue; + st = NULL; + if ( op->associative!=ASSOC_LEFT ) + { + // if last statement is an indirect, change it to an address of + if (!simplestore && ((unsigned)(statements[numstatements-1].op - OP_LOAD_F) < 6 || statements[numstatements-1].op == OP_LOAD_I || statements[numstatements-1].op == OP_LOAD_P) && statements[numstatements-1].c == e->ofs) + { + qcc_usefulstatement=true; + statements[numstatements-1].op = OP_ADDRESS; + type_pointer->aux_type->type = e->type->type; + e->type = type_pointer; + } + //if last statement retrieved a value, switch it to retrieve a usable pointer. + if ( !simplestore && (unsigned)(statements[numstatements-1].op - OP_LOADA_F) < 7)// || statements[numstatements-1].op == OP_LOADA_C) + { + statements[numstatements-1].op = OP_GLOBALADDRESS; + type_pointer->aux_type->type = e->type->type; + e->type = type_pointer; + } + if ( !simplestore && (unsigned)(statements[numstatements-1].op - OP_LOADP_F) < 7 && statements[numstatements-1].c == e->ofs) + { + if (!statements[numstatements-1].b) + { + //if the loadp has no offset, remove the instruction and convert the dest of this instruction directly to the pointer's load address + //this kills the add 0. + e->ofs = statements[numstatements-1].a; + numstatements--; + + if (e->type->type != ev_pointer) + { + type_pointer->aux_type->type = e->type->type; + e->type = type_pointer; + } + } + else + { + statements[numstatements-1].op = OP_ADD_I; + if (e->type->type != ev_pointer) + { + type_pointer->aux_type->type = e->type->type; + e->type = type_pointer; + } + } + } + if ( !simplestore && statements[numstatements-1].op == OP_LOADP_C && e->ofs == statements[numstatements-1].c) + { + statements[numstatements-1].op = OP_ADD_SF; + e->type = type_string; + + //now we want to make sure that string = float can't work without it being a dereferenced pointer. (we don't want to allow storep_c without dereferece) + e2 = QCC_PR_Expression (priority, exprflags); + if (e2->type->type == ev_float) + op = &pr_opcodes[OP_STOREP_C]; + } + else + e2 = QCC_PR_Expression (priority, exprflags); + } + else + { + if (op->priority == 7 && opt_logicops) + { + optres_logicops++; + st = &statements[numstatements]; + if (*op->name == '&') //statement 3 because we don't want to optimise this into if from not ifnot + QCC_PR_Statement3(&pr_opcodes[OP_IFNOT], e, NULL, NULL, false); + else + QCC_PR_Statement3(&pr_opcodes[OP_IF], e, NULL, NULL, false); + } + + e2 = QCC_PR_Expression (priority-1, exprflags); + } + + // type check + type_a = e->type->type; + type_b = e2->type->type; + +// if (type_a == ev_pointer && type_b == ev_pointer) +// QCC_PR_ParseWarning(0, "Debug: pointer op pointer"); + + if (op->name[0] == '.')// field access gets type from field + { + if (e2->type->aux_type) + type_c = e2->type->aux_type->type; + else + type_c = -1; // not a field + } + else + type_c = ev_void; + + oldop = op; + bestop = NULL; + numconversions = 32767; + while (op) + { + if (!(type_c != ev_void && type_c != (*op->type_c)->type)) + { + if (!STRCMP (op->name , oldop->name)) //matches + { + //return values are never converted - what to? + // if (type_c != ev_void && type_c != op->type_c->type->type) + // { + // op++; + // continue; + // } + + if (op->associative!=ASSOC_LEFT) + {//assignment + if (op->type_a == &type_pointer) //ent var + { + if (e->type->type != ev_pointer) + c = -200; //don't cast to a pointer. + else if ((*op->type_c)->type == ev_void && op->type_b == &type_pointer && e2->type->type == ev_pointer) + c = 0; //generic pointer... fixme: is this safe? make sure both sides are equivelent + else if (e->type->aux_type->type != (*op->type_b)->type) //if e isn't a pointer to a type_b + c = -200; //don't let the conversion work + else + c = QCC_canConv(e2, (*op->type_c)->type); + } + else + { + c=QCC_canConv(e2, (*op->type_b)->type); + if (type_a != (*op->type_a)->type) //in this case, a is the final assigned value + c = -300; //don't use this op, as we must not change var b's type + } + } + else + { + if (op->type_a == &type_pointer) //ent var + { + if (e2->type->type != ev_pointer || e2->type->aux_type->type != (*op->type_b)->type) //if e isn't a pointer to a type_b + c = -200; //don't let the conversion work + else + c = 0; + } + else + { + c=QCC_canConv(e, (*op->type_a)->type); + c+=QCC_canConv(e2, (*op->type_b)->type); + } + } + + if (c>=0 && c < numconversions) + { + bestop = op; + numconversions=c; + if (c == 0)//can't get less conversions than 0... + break; + } + } + else + break; + } + op = opcodeprioritized[priority][++opnum]; + } + if (bestop == NULL) + { + if (oldop->priority == CONDITION_PRIORITY) + op = oldop; + else + { + if (flag_laxcasts) + { + op = oldop; + QCC_PR_ParseWarning(WARN_LAXCAST, "type mismatch for %s (%s and %s)", oldop->name, e->type->name, e2->type->name); + } + else + QCC_PR_ParseError (ERR_TYPEMISMATCH, "type mismatch for %s (%s and %s)", oldop->name, e->type->name, e2->type->name); + } + } + else + { + if (numconversions>3) + QCC_PR_ParseWarning(WARN_IMPLICITCONVERSION, "Implicit conversion"); + op = bestop; + } + +// if (type_a == ev_pointer && type_b != e->type->aux_type->type) +// QCC_PR_ParseError ("type mismatch for %s", op->name); + + if (st) + st->b = &statements[numstatements] - st; + + + if (op->associative!=ASSOC_LEFT) + { + qcc_usefulstatement = true; + if (e->constant || e->ofs < OFS_PARM0) + { + if (e->type->type == ev_function) + { + QCC_PR_ParseWarning(WARN_ASSIGNMENTTOCONSTANTFUNC, "Assignment to function %s", e->name); + QCC_PR_ParsePrintDef(WARN_ASSIGNMENTTOCONSTANTFUNC, e); + } + else + { + QCC_PR_ParseWarning(WARN_ASSIGNMENTTOCONSTANT, "Assignment to constant %s", e->name); + QCC_PR_ParsePrintDef(WARN_ASSIGNMENTTOCONSTANT, e); + } +#ifndef QCC + editbadfile(strings+s_file, pr_source_line); +#endif + } + if (conditional&1) + QCC_PR_ParseWarning(WARN_ASSIGNMENTINCONDITIONAL, "Assignment in conditional"); + + e = QCC_PR_Statement (op, e2, e, NULL); + } + else + e = QCC_PR_Statement (op, e, e2, NULL); + + if (type_c != ev_void/* && type_c != ev_string*/) // field access gets type from field + e->type = e2->type->aux_type; + + if (priority > 1 && exprflags & EXPR_WARN_ABOVE_1) + QCC_PR_ParseWarning(0, "You may wish to add brackets after that ! operator"); + + break; + } + if (!op) + { + if (e == NULL) + QCC_PR_ParseError(ERR_INTERNAL, "e == null"); + + + if (!STRCMP(pr_token, "++")) + { + //if the last statement was an ent.float (or something) + if (((unsigned)(statements[numstatements-1].op - OP_LOAD_F) < 6 || statements[numstatements-1].op == OP_LOAD_I) && statements[numstatements-1].c == e->ofs) + { //we have our load. + QCC_def_t *e3; +//the only inefficiency here is with an extra temp (we can't reuse the original) +//this is not a problem, as the optimise temps or locals marshalling can clean these up for us + qcc_usefulstatement=true; +//load +//add to temp +//store temp to offset +//return original loaded (which is not at the same offset as the pointer we store to) + e2 = QCC_GetTemp(type_float); + e3 = QCC_GetTemp(type_pointer); + QCC_PR_SimpleStatement(OP_ADDRESS, statements[numstatements-1].a, statements[numstatements-1].b, e3->ofs, false); + if (e->type->type == ev_float) + { + QCC_PR_Statement3(&pr_opcodes[OP_ADD_F], e, QCC_MakeFloatDef(1), e2, false); + QCC_PR_Statement3(&pr_opcodes[OP_STOREP_F], e2, e3, NULL, false); + } + else if (e->type->type == ev_integer) + { + QCC_PR_Statement3(&pr_opcodes[OP_ADD_I], e, QCC_MakeIntDef(1), e2, false); + QCC_PR_Statement3(&pr_opcodes[OP_STOREP_I], e2, e3, NULL, false); + } + else + { + QCC_PR_ParseError(ERR_PARSEERRORS, "-- suffix operator results in nonstandard behaviour. Use -=1 or prefix form instead"); + QCC_PR_IncludeChunk("-=1", false, NULL); + } + QCC_FreeTemp(e2); + QCC_FreeTemp(e3); + } + else if (e->type->type == ev_float) + { +//copy to temp +//add to original +//return temp (which == original) + QCC_PR_ParseWarning(WARN_INEFFICIENTPLUSPLUS, "++ suffix operator results in inefficient behaviour. Use +=1 or prefix form instead"); + qcc_usefulstatement=true; + + e2 = QCC_GetTemp(type_float); + QCC_PR_Statement3(&pr_opcodes[OP_STORE_F], e, e2, NULL, false); + QCC_PR_Statement3(&pr_opcodes[OP_ADD_F], e, QCC_MakeFloatDef(1), e, false); + QCC_FreeTemp(e); + e = e2; + } + else if (e->type->type == ev_integer) + { + QCC_PR_ParseWarning(WARN_INEFFICIENTPLUSPLUS, "++ suffix operator results in inefficient behaviour. Use +=1 or prefix form instead"); + qcc_usefulstatement=true; + + e2 = QCC_GetTemp(type_integer); + QCC_PR_Statement3(&pr_opcodes[OP_STORE_I], e, e2, NULL, false); + QCC_PR_Statement3(&pr_opcodes[OP_ADD_I], e, QCC_MakeIntDef(1), e, false); + QCC_FreeTemp(e); + e = e2; + } + else + { + QCC_PR_ParseWarning(WARN_NOTSTANDARDBEHAVIOUR, "++ suffix operator results in nonstandard behaviour. Use +=1 or prefix form instead"); + QCC_PR_IncludeChunk("+=1", false, NULL); + } + QCC_PR_Lex(); + } + else if (!STRCMP(pr_token, "--")) + { + if (((unsigned)(statements[numstatements-1].op - OP_LOAD_F) < 6 || statements[numstatements-1].op == OP_LOAD_I) && statements[numstatements-1].c == e->ofs) + { //we have our load. + QCC_def_t *e3; +//load +//add to temp +//store temp to offset +//return original loaded (which is not at the same offset as the pointer we store to) + e2 = QCC_GetTemp(type_float); + e3 = QCC_GetTemp(type_pointer); + QCC_PR_SimpleStatement(OP_ADDRESS, statements[numstatements-1].a, statements[numstatements-1].b, e3->ofs, false); + if (e->type->type == ev_float) + { + QCC_PR_Statement3(&pr_opcodes[OP_SUB_F], e, QCC_MakeFloatDef(1), e2, false); + QCC_PR_Statement3(&pr_opcodes[OP_STOREP_F], e2, e3, NULL, false); + } + else if (e->type->type == ev_integer) + { + QCC_PR_Statement3(&pr_opcodes[OP_SUB_I], e, QCC_MakeIntDef(1), e2, false); + QCC_PR_Statement3(&pr_opcodes[OP_STOREP_I], e2, e3, NULL, false); + } + else + { + QCC_PR_ParseError(ERR_PARSEERRORS, "-- suffix operator results in nonstandard behaviour. Use -=1 or prefix form instead"); + QCC_PR_IncludeChunk("-=1", false, NULL); + } + QCC_FreeTemp(e2); + QCC_FreeTemp(e3); + } + else if (e->type->type == ev_float) + { + QCC_PR_ParseWarning(WARN_INEFFICIENTPLUSPLUS, "-- suffix operator results in inefficient behaviour. Use -=1 or prefix form instead"); + qcc_usefulstatement=true; + + e2 = QCC_GetTemp(type_float); + QCC_PR_Statement3(&pr_opcodes[OP_STORE_F], e, e2, NULL, false); + QCC_PR_Statement3(&pr_opcodes[OP_SUB_F], e, QCC_MakeFloatDef(1), e, false); + QCC_FreeTemp(e); + e = e2; + } + else if (e->type->type == ev_integer) + { + QCC_PR_ParseWarning(WARN_INEFFICIENTPLUSPLUS, "-- suffix operator results in inefficient behaviour. Use -=1 or prefix form instead"); + qcc_usefulstatement=true; + + e2 = QCC_GetTemp(type_integer); + QCC_PR_Statement3(&pr_opcodes[OP_STORE_I], e, e2, NULL, false); + QCC_PR_Statement3(&pr_opcodes[OP_SUB_I], e, QCC_MakeIntDef(1), e, false); + QCC_FreeTemp(e); + e = e2; + } + else + { + QCC_PR_ParseWarning(WARN_NOTSTANDARDBEHAVIOUR, "-- suffix operator results in nonstandard behaviour. Use -=1 or prefix form instead"); + QCC_PR_IncludeChunk("-=1", false, NULL); + } + QCC_PR_Lex(); + } + break; // next token isn't at this priority level + } + } + if (e == NULL) + QCC_PR_ParseError(ERR_INTERNAL, "e == null"); + + if (!(exprflags&EXPR_DISALLOW_COMMA) && priority == TOP_PRIORITY && QCC_PR_CheckToken (",")) + { + QCC_FreeTemp(e); + return QCC_PR_Expression(TOP_PRIORITY, exprflags); + } + + return e; +} + +void QCC_PR_GotoStatement (QCC_dstatement_t *patch2, char *labelname) +{ + if (num_gotos >= max_gotos) + { + max_gotos += 8; + pr_gotos = realloc(pr_gotos, sizeof(*pr_gotos)*max_gotos); + } + + strncpy(pr_gotos[num_gotos].name, labelname, sizeof(pr_gotos[num_gotos].name) -1); + pr_gotos[num_gotos].lineno = pr_source_line; + pr_gotos[num_gotos].statementno = patch2 - statements; + + num_gotos++; +} + +pbool QCC_PR_StatementBlocksMatch(QCC_dstatement_t *p1, int p1count, QCC_dstatement_t *p2, int p2count) +{ + if (p1count != p2count) + return false; + + while(p1count>0) + { + if (p1->op != p2->op) + return false; + if (p1->a != p2->a) + return false; + if (p1->b != p2->b) + return false; + if (p1->c != p2->c) + return false; + p1++; + p2++; + p1count--; + } + + return true; +} + +/* +============ +PR_ParseStatement + +============ +*/ +void QCC_PR_ParseStatement (void) +{ + int continues; + int breaks; + int cases; + int i; + QCC_def_t *e, *e2; + QCC_dstatement_t *patch1, *patch2, *patch3; + int statementstart = pr_source_line; + + if (QCC_PR_CheckToken ("{")) + { + e = pr.localvars; + while (!QCC_PR_CheckToken("}")) + QCC_PR_ParseStatement (); + + if (pr_subscopedlocals) + { + for (e2 = pr.localvars; e2 != e; e2 = e2->nextlocal) + { + Hash_RemoveData(&localstable, e2->name, e2); + } + } + return; + } + + if (QCC_PR_CheckKeyword(keyword_return, "return")) + { + /*if (pr_classtype) + { + e = QCC_PR_GetDef(NULL, "__oself", pr_scope, false, 0); + e2 = QCC_PR_GetDef(NULL, "self", NULL, false, 0); + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_ENT], e, QCC_PR_DummyDef(pr_classtype, "self", pr_scope, 1, e2->ofs, false), NULL)); + }*/ + + if (QCC_PR_CheckToken (";")) + { + if (pr_scope->type->aux_type->type != ev_void) + QCC_PR_ParseWarning(WARN_MISSINGRETURNVALUE, "\'%s\' should return %s", pr_scope->name, pr_scope->type->aux_type->name); + if (opt_return_only) + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_DONE], 0, 0, NULL)); + else + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_RETURN], 0, 0, NULL)); + return; + } + e = QCC_PR_Expression (TOP_PRIORITY, 0); + e2 = QCC_SupplyConversion(e, pr_scope->type->aux_type->type); + if (e != e2) + { + QCC_PR_ParseWarning(WARN_CORRECTEDRETURNTYPE, "\'%s\' returned %s, expected %s, conversion supplied", pr_scope->name, e->type->name, pr_scope->type->aux_type->name); + e = e2; + } + QCC_PR_Expect (";"); + if (pr_scope->type->aux_type->type != e->type->type) + QCC_PR_ParseWarning(WARN_WRONGRETURNTYPE, "\'%s\' returned %s, expected %s", pr_scope->name, e->type->name, pr_scope->type->aux_type->name); + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_RETURN], e, 0, NULL)); + return; + } + if (QCC_PR_CheckKeyword(keyword_exit, "exit")) + { + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_DONE], 0, 0, NULL)); + QCC_PR_Expect (";"); + return; + } + + if (QCC_PR_CheckKeyword(keyword_while, "while")) + { + continues = num_continues; + breaks = num_breaks; + + QCC_PR_Expect ("("); + patch2 = &statements[numstatements]; + conditional = 1; + e = QCC_PR_Expression (TOP_PRIORITY, 0); + conditional = 0; + if (((e->constant && !e->temp) || !STRCMP(e->name, "IMMEDIATE")) && opt_compound_jumps) + { + optres_compound_jumps++; + if (!G_INT(e->ofs)) + { + QCC_PR_ParseWarning(0, "while(0)?"); + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_GOTO], 0, 0, &patch1)); + } + else + { + patch1 = NULL; + } + } + else + { + if (e->constant && !e->temp) + { + if (!G_FLOAT(e->ofs)) + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_GOTO], 0, 0, &patch1)); + else + patch1 = NULL; + } + else if (!typecmp( e->type, type_string) && flag_ifstring) //special case, as strings are now pointers, not offsets from string table + { + QCC_PR_ParseWarning(WARN_IFSTRING_USED, "while(string) can result in bizzare behaviour"); + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IFNOT_S], e, 0, &patch1)); + } + else if (!typecmp( e->type, type_float) && (flag_iffloat||QCC_OPCodeValid(&pr_opcodes[OP_IFNOT_F]))) //special case, as negative 0 is also zero + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IFNOT_F], e, 0, &patch1)); + else + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IFNOT], e, 0, &patch1)); + } + QCC_PR_Expect (")"); //after the line number is noted.. + QCC_PR_ParseStatement (); + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_GOTO], NULL, 0, &patch3)); + patch3->a = patch2 - patch3; + if (patch1) + { + if (patch1->op == OP_GOTO) + patch1->a = &statements[numstatements] - patch1; + else + patch1->b = &statements[numstatements] - patch1; + } + + if (breaks != num_breaks) + { + for(i = breaks; i < num_breaks; i++) + { + patch1 = &statements[pr_breaks[i]]; + statements[pr_breaks[i]].a = &statements[numstatements] - patch1; //jump to after the return-to-top goto + } + num_breaks = breaks; + } + if (continues != num_continues) + { + for(i = continues; i < num_continues; i++) + { + patch1 = &statements[pr_continues[i]]; + statements[pr_continues[i]].a = patch2 - patch1; //jump back to top + } + num_continues = continues; + } + return; + } + if (QCC_PR_CheckKeyword(keyword_for, "for")) + { + int old_numstatements; + int numtemp, i; + + int linenum[32]; + QCC_dstatement_t temp[sizeof(linenum)/sizeof(linenum[0])]; + + continues = num_continues; + breaks = num_breaks; + + QCC_PR_Expect("("); + if (!QCC_PR_CheckToken(";")) + { + QCC_FreeTemp(QCC_PR_Expression(TOP_PRIORITY, 0)); + QCC_PR_Expect(";"); + } + + patch2 = &statements[numstatements]; + if (!QCC_PR_CheckToken(";")) + { + conditional = 1; + e = QCC_PR_Expression(TOP_PRIORITY, 0); + conditional = 0; + QCC_PR_Expect(";"); + } + else + e = NULL; + + if (!QCC_PR_CheckToken(")")) + { + old_numstatements = numstatements; + QCC_FreeTemp(QCC_PR_Expression(TOP_PRIORITY, 0)); + + numtemp = numstatements - old_numstatements; + if (numtemp > sizeof(linenum)/sizeof(linenum[0])) + QCC_PR_ParseError(ERR_TOOCOMPLEX, "Update expression too large"); + numstatements = old_numstatements; + for (i = 0 ; i < numtemp ; i++) + { + linenum[i] = statement_linenums[numstatements + i]; + temp[i] = statements[numstatements + i]; + } + + QCC_PR_Expect(")"); + } + else + numtemp = 0; + + if (e) + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_IFNOT], e, 0, &patch1)); + else + patch1 = NULL; + if (!QCC_PR_CheckToken(";")) + QCC_PR_ParseStatement(); //don't give the hanging ';' warning. + patch3 = &statements[numstatements]; + for (i = 0 ; i < numtemp ; i++) + { + statement_linenums[numstatements] = linenum[i]; + statements[numstatements++] = temp[i]; + } + QCC_PR_SimpleStatement(OP_GOTO, patch2 - &statements[numstatements], 0, 0, false); + if (patch1) + patch1->b = &statements[numstatements] - patch1; + + if (breaks != num_breaks) + { + for(i = breaks; i < num_breaks; i++) + { + patch1 = &statements[pr_breaks[i]]; + statements[pr_breaks[i]].a = &statements[numstatements] - patch1; + } + num_breaks = breaks; + } + if (continues != num_continues) + { + for(i = continues; i < num_continues; i++) + { + patch1 = &statements[pr_continues[i]]; + statements[pr_continues[i]].a = patch3 - patch1; + } + num_continues = continues; + } + + return; + } + if (QCC_PR_CheckKeyword(keyword_do, "do")) + { + continues = num_continues; + breaks = num_breaks; + + patch1 = &statements[numstatements]; + QCC_PR_ParseStatement (); + QCC_PR_Expect ("while"); + QCC_PR_Expect ("("); + conditional = 1; + e = QCC_PR_Expression (TOP_PRIORITY, 0); + conditional = 0; + + if (e->constant && !e->temp) + { + if (G_FLOAT(e->ofs)) + { + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_GOTO], NULL, 0, &patch2)); + patch2->a = patch1 - patch2; + } + } + else + { + if (!typecmp( e->type, type_string) && flag_ifstring) + { + QCC_PR_ParseWarning(WARN_IFSTRING_USED, "do {} while(string) can result in bizzare behaviour"); + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IF_S], e, NULL, &patch2)); + } + else if (!typecmp( e->type, type_float) && (flag_iffloat||QCC_OPCodeValid(&pr_opcodes[OP_IFNOT_F]))) + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IF_F], e, NULL, &patch2)); + else + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IF], e, NULL, &patch2)); + + patch2->b = patch1 - patch2; + } + + QCC_PR_Expect (")"); + QCC_PR_Expect (";"); + + if (breaks != num_breaks) + { + for(i = breaks; i < num_breaks; i++) + { + patch2 = &statements[pr_breaks[i]]; + statements[pr_breaks[i]].a = &statements[numstatements] - patch2; + } + num_breaks = breaks; + } + if (continues != num_continues) + { + for(i = continues; i < num_continues; i++) + { + patch2 = &statements[pr_continues[i]]; + statements[pr_continues[i]].a = patch1 - patch2; + } + num_continues = continues; + } + + return; + } + + if (QCC_PR_CheckKeyword(keyword_local, "local")) + { + QCC_type_t *functionsclasstype = pr_classtype; +// if (locals_end != numpr_globals) //is this breaking because of locals? +// QCC_PR_ParseWarning("local vars after temp vars\n"); + QCC_PR_ParseDefs (NULL); + pr_classtype = functionsclasstype; + locals_end = numpr_globals; + return; + } + + if (pr_token_type == tt_name) + if ((keyword_var && !STRCMP ("var", pr_token)) || + (keyword_string && !STRCMP ("string", pr_token)) || + (keyword_float && !STRCMP ("float", pr_token)) || + (keyword_entity && !STRCMP ("entity", pr_token)) || + (keyword_vector && !STRCMP ("vector", pr_token)) || + (keyword_integer && !STRCMP ("integer", pr_token)) || + (keyword_int && !STRCMP ("int", pr_token)) || + (keyword_class && !STRCMP ("class", pr_token)) || + (keyword_const && !STRCMP ("const", pr_token))) + { +// if (locals_end != numpr_globals) //is this breaking because of locals? +// QCC_PR_ParseWarning("local vars after temp vars\n"); + QCC_PR_ParseDefs (NULL); + locals_end = numpr_globals; + return; + } + + if (QCC_PR_CheckKeyword(keyword_state, "state")) + { + QCC_PR_Expect("["); + QCC_PR_ParseState(); + QCC_PR_Expect(";"); + return; + } + if (QCC_PR_CheckToken("#")) + { + char *name; + float frame = pr_immediate._float; + QCC_PR_Lex(); + name = QCC_PR_ParseName(); + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_STATE], QCC_MakeFloatDef(frame), QCC_PR_GetDef(type_function, name, NULL, false, 0, false), NULL)); + QCC_PR_Expect(";"); + return; + } + + if (QCC_PR_CheckKeyword(keyword_if, "if")) + { + pbool negate = QCC_PR_CheckKeyword(keyword_not, "not"); + + QCC_PR_Expect ("("); + conditional = 1; + e = QCC_PR_Expression (TOP_PRIORITY, 0); + conditional = 0; + +// negate = negate != 0; + + if (negate) + { + if (!typecmp( e->type, type_string) && flag_ifstring) + { + QCC_PR_ParseWarning(WARN_IFSTRING_USED, "if not(string) can result in bizzare behaviour"); + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IF_S], e, 0, &patch1)); + } + else if (!typecmp( e->type, type_float) && (flag_iffloat||QCC_OPCodeValid(&pr_opcodes[OP_IFNOT_F]))) + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IF_F], e, 0, &patch1)); + else + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IF], e, 0, &patch1)); + } + else + { + if (!typecmp( e->type, type_string) && flag_ifstring) + { + QCC_PR_ParseWarning(WARN_IFSTRING_USED, "if (string) can result in bizzare behaviour"); + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IFNOT_S], e, 0, &patch1)); + } + else if (!typecmp( e->type, type_float) && (flag_iffloat||QCC_OPCodeValid(&pr_opcodes[OP_IFNOT_F]))) + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IFNOT_F], e, 0, &patch1)); + else + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IFNOT], e, 0, &patch1)); + } + + QCC_PR_Expect (")"); //close bracket is after we save the statement to mem (so debugger does not show the if statement as being on the line after + + QCC_PR_ParseStatement (); + + if (QCC_PR_CheckKeyword (keyword_else, "else")) + { + int lastwasreturn; + lastwasreturn = statements[numstatements-1].op == OP_RETURN || statements[numstatements-1].op == OP_DONE || + statements[numstatements-1].op == OP_GOTO; + + //the last statement of the if was a return, so we don't need the goto at the end + if (lastwasreturn && opt_compound_jumps && !QCC_AStatementJumpsTo(numstatements, patch1-statements, numstatements)) + { +// QCC_PR_ParseWarning(0, "optimised the else"); + optres_compound_jumps++; + patch1->b = &statements[numstatements] - patch1; + QCC_PR_ParseStatement (); + } + else + { +// QCC_PR_ParseWarning(0, "using the else"); + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_GOTO], 0, 0, &patch2)); + patch1->b = &statements[numstatements] - patch1; + QCC_PR_ParseStatement (); + patch2->a = &statements[numstatements] - patch2; + + if (QCC_PR_StatementBlocksMatch(patch1+1, patch2-patch1, patch2+1, &statements[numstatements] - patch2)) + QCC_PR_ParseWarning(0, "Two identical blocks each side of an else"); + } + } + else + patch1->b = &statements[numstatements] - patch1; + + return; + } + if (QCC_PR_CheckKeyword(keyword_switch, "switch")) + { + int op; + int hcstyle; + int defaultcase = -1; + temp_t *et; + int oldst; + QCC_type_t *switchtype; + + breaks = num_breaks; + cases = num_cases; + + + QCC_PR_Expect ("("); + + conditional = 1; + e = QCC_PR_Expression (TOP_PRIORITY, 0); + conditional = 0; + + if (e == &def_ret) + { //copy it out, so our hack just below doesn't crash us +/* if (e->type->type == ev_vector) + e = QCC_PR_Statement(pr_opcodes+OP_STORE_V, e, QCC_GetTemp(type_vector), NULL); + else + e = QCC_PR_Statement(pr_opcodes+OP_STORE_F, e, QCC_GetTemp(type_float), NULL); + + if (e == &def_ret) //this shouldn't be happening + QCC_Error(ERR_INTERNAL, "internal error: switch: e == &def_ret"); +*/ + et = NULL; + } + else + { + et = e->temp; + e->temp = NULL; //so noone frees it until we finish this loop + } + + //expands + + //switch (CONDITION) + //{ + //case 1: + // break; + //case 2: + //default: + // break; + //} + + //to + + // x = CONDITION, goto start + // l1: + // goto end + // l2: + // def: + // goto end + // goto end P1 + // start: + // if (x == 1) goto l1; + // if (x == 2) goto l2; + // goto def + // end: + + //x is emitted in an opcode, stored as a register that we cannot access later. + //it should be possible to nest these. + + switchtype = e->type; + switch(switchtype->type) + { + case ev_float: + op = OP_SWITCH_F; + break; + case ev_entity: //whu??? + op = OP_SWITCH_E; + break; + case ev_vector: + op = OP_SWITCH_V; + break; + case ev_string: + op = OP_SWITCH_S; + break; + case ev_function: + op = OP_SWITCH_FNC; + break; + default: //err hmm. + op = 0; + break; + } + + if (op) + hcstyle = QCC_OPCodeValid(&pr_opcodes[op]); + else + hcstyle = false; + + + if (hcstyle) + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[op], e, 0, &patch1)); + else + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_GOTO], e, 0, &patch1)); + + QCC_PR_Expect (")"); //close bracket is after we save the statement to mem (so debugger does not show the if statement as being on the line after + + oldst = numstatements; + QCC_PR_ParseStatement (); + + //this is so that a missing goto at the end of your switch doesn't end up in the jumptable again + if (oldst == numstatements || !QCC_StatementIsAJump(numstatements-1, numstatements-1)) + { + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_GOTO], 0, 0, &patch2)); //the P1 statement/the theyforgotthebreak statement. +// QCC_PR_ParseWarning(0, "emitted goto"); + } + else + { + patch2 = NULL; +// QCC_PR_ParseWarning(0, "No goto"); + } + + if (hcstyle) + patch1->b = &statements[numstatements] - patch1; //the goto start part + else + patch1->a = &statements[numstatements] - patch1; //the goto start part + + if (e == &def_ret) + e->type = switchtype; //set it back to the type it was actually meant to be. + + for (i = cases; i < num_cases; i++) + { + if (!pr_casesdef[i]) + { + if (defaultcase >= 0) + QCC_PR_ParseError(ERR_MULTIPLEDEFAULTS, "Duplicated default case"); + defaultcase = i; + } + else + { + if (pr_casesdef[i]->type->type != e->type->type) + { + if (e->type->type == ev_integer && pr_casesdef[i]->type->type == ev_float) + pr_casesdef[i] = QCC_MakeIntDef((int)qcc_pr_globals[pr_casesdef[i]->ofs]); + else + QCC_PR_ParseWarning(WARN_SWITCHTYPEMISMATCH, "switch case type mismatch"); + } + if (pr_casesdef2[i]) + { + if (pr_casesdef2[i]->type->type != e->type->type) + { + if (e->type->type == ev_integer && pr_casesdef[i]->type->type == ev_float) + pr_casesdef2[i] = QCC_MakeIntDef((int)qcc_pr_globals[pr_casesdef2[i]->ofs]); + else + QCC_PR_ParseWarning(WARN_SWITCHTYPEMISMATCH, "switch caserange type mismatch"); + } + + if (hcstyle) + { + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_CASERANGE], pr_casesdef[i], pr_casesdef2[i], &patch3)); + patch3->c = &statements[pr_cases[i]] - patch3; + } + else + { + QCC_def_t *e3; + + if (e->type->type == ev_float) + { + e2 = QCC_PR_Statement (&pr_opcodes[OP_GE], e, pr_casesdef[i], NULL); + e3 = QCC_PR_Statement (&pr_opcodes[OP_LE], e, pr_casesdef2[i], NULL); + e2 = QCC_PR_Statement (&pr_opcodes[OP_AND], e2, e3, NULL); + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IF], e2, 0, &patch3)); + patch3->b = &statements[pr_cases[i]] - patch3; + } + else if (e->type->type == ev_integer) + { + e2 = QCC_PR_Statement (&pr_opcodes[OP_GE_I], e, pr_casesdef[i], NULL); + e3 = QCC_PR_Statement (&pr_opcodes[OP_LE_I], e, pr_casesdef2[i], NULL); + e2 = QCC_PR_Statement (&pr_opcodes[OP_AND], e2, e3, NULL); + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IF], e2, 0, &patch3)); + patch3->b = &statements[pr_cases[i]] - patch3; + } + else + QCC_PR_ParseWarning(WARN_SWITCHTYPEMISMATCH, "switch caserange MUST be a float or integer"); + } + } + else + { + if (hcstyle) + { + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_CASE], pr_casesdef[i], 0, &patch3)); + patch3->b = &statements[pr_cases[i]] - patch3; + } + else + { + if (!pr_casesdef[i]->constant || G_INT(pr_casesdef[i]->ofs)) + { + switch(e->type->type) + { + case ev_float: + e2 = QCC_PR_Statement (&pr_opcodes[OP_EQ_F], e, pr_casesdef[i], NULL); + break; + case ev_entity: //whu??? + e2 = QCC_PR_Statement (&pr_opcodes[OP_EQ_E], e, pr_casesdef[i], &patch1); + break; + case ev_vector: + e2 = QCC_PR_Statement (&pr_opcodes[OP_EQ_V], e, pr_casesdef[i], &patch1); + break; + case ev_string: + e2 = QCC_PR_Statement (&pr_opcodes[OP_EQ_S], e, pr_casesdef[i], &patch1); + break; + case ev_function: + e2 = QCC_PR_Statement (&pr_opcodes[OP_EQ_FNC], e, pr_casesdef[i], &patch1); + break; + case ev_field: + e2 = QCC_PR_Statement (&pr_opcodes[OP_EQ_FNC], e, pr_casesdef[i], &patch1); + break; + case ev_integer: + e2 = QCC_PR_Statement (&pr_opcodes[OP_EQ_I], e, pr_casesdef[i], &patch1); + break; + default: + QCC_PR_ParseError(ERR_BADSWITCHTYPE, "Bad switch type"); + e2 = NULL; + break; + } + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IF], e2, 0, &patch3)); + } + else + { + if (e->type->type == ev_string) + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IFNOT_S], e, 0, &patch3)); + else if (e->type->type == ev_float) + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IFNOT_F], e, 0, &patch3)); + else + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IFNOT], e, 0, &patch3)); + } + patch3->b = &statements[pr_cases[i]] - patch3; + } + } + } + } + if (defaultcase>=0) + { + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_GOTO], 0, 0, &patch3)); + patch3->a = &statements[pr_cases[defaultcase]] - patch3; + } + + num_cases = cases; + + + patch3 = &statements[numstatements]; + if (patch2) + patch2->a = patch3 - patch2; //set P1 jump + + if (breaks != num_breaks) + { + for(i = breaks; i < num_breaks; i++) + { + patch2 = &statements[pr_breaks[i]]; + patch2->a = patch3 - patch2; + } + num_breaks = breaks; + } + + if (et) + { + e->temp = et; + QCC_FreeTemp(e); + } + return; + } + + if (QCC_PR_CheckKeyword(keyword_asm, "asm")) + { + if (QCC_PR_CheckToken("{")) + { + while (!QCC_PR_CheckToken("}")) + QCC_PR_ParseAsm (); + } + else + QCC_PR_ParseAsm (); + return; + } + + if (QCC_PR_CheckToken(":")) + { + if (pr_token_type != tt_name) + { + QCC_PR_ParseError(ERR_BADLABELNAME, "invalid label name \"%s\"", pr_token); + return; + } + + for (i = 0; i < num_labels; i++) + if (!STRNCMP(pr_labels[i].name, pr_token, sizeof(pr_labels[num_labels].name) -1)) + { + QCC_PR_ParseWarning(WARN_DUPLICATELABEL, "Duplicate label %s", pr_token); + QCC_PR_Lex(); + return; + } + + if (num_labels >= max_labels) + { + max_labels += 8; + pr_labels = realloc(pr_labels, sizeof(*pr_labels)*max_labels); + } + + strncpy(pr_labels[num_labels].name, pr_token, sizeof(pr_labels[num_labels].name) -1); + pr_labels[num_labels].lineno = pr_source_line; + pr_labels[num_labels].statementno = numstatements; + + num_labels++; + +// QCC_PR_ParseWarning("Gotos are evil"); + QCC_PR_Lex(); + return; + } + if (QCC_PR_CheckKeyword(keyword_goto, "goto")) + { + if (pr_token_type != tt_name) + { + QCC_PR_ParseError(ERR_NOLABEL, "invalid label name \"%s\"", pr_token); + return; + } + + QCC_PR_Statement (&pr_opcodes[OP_GOTO], 0, 0, &patch2); + + QCC_PR_GotoStatement (patch2, pr_token); + +// QCC_PR_ParseWarning("Gotos are evil"); + QCC_PR_Lex(); + QCC_PR_Expect(";"); + return; + } + + if (QCC_PR_CheckKeyword(keyword_break, "break")) + { + if (!STRCMP ("(", pr_token)) + { //make sure it wasn't a call to the break function. + QCC_PR_IncludeChunk("break(", true, NULL); + QCC_PR_Lex(); //so it sees the break. + } + else + { + if (num_breaks >= max_breaks) + { + max_breaks += 8; + pr_breaks = realloc(pr_breaks, sizeof(*pr_breaks)*max_breaks); + } + pr_breaks[num_breaks] = numstatements; + QCC_PR_Statement (&pr_opcodes[OP_GOTO], 0, 0, NULL); + num_breaks++; + QCC_PR_Expect(";"); + return; + } + } + if (QCC_PR_CheckKeyword(keyword_continue, "continue")) + { + if (num_continues >= max_continues) + { + max_continues += 8; + pr_continues = realloc(pr_continues, sizeof(*pr_continues)*max_continues); + } + pr_continues[num_continues] = numstatements; + QCC_PR_Statement (&pr_opcodes[OP_GOTO], 0, 0, NULL); + num_continues++; + QCC_PR_Expect(";"); + return; + } + if (QCC_PR_CheckKeyword(keyword_case, "case")) + { + if (num_cases >= max_cases) + { + max_cases += 8; + pr_cases = realloc(pr_cases, sizeof(*pr_cases)*max_cases); + pr_casesdef = realloc(pr_casesdef, sizeof(*pr_casesdef)*max_cases); + pr_casesdef2 = realloc(pr_casesdef2, sizeof(*pr_casesdef2)*max_cases); + } + pr_cases[num_cases] = numstatements; + pr_casesdef[num_cases] = QCC_PR_Expression (TOP_PRIORITY, EXPR_DISALLOW_COMMA); + if (QCC_PR_CheckToken("..")) + { + pr_casesdef2[num_cases] = QCC_PR_Expression (TOP_PRIORITY, EXPR_DISALLOW_COMMA); + if (pr_casesdef[num_cases]->constant && pr_casesdef2[num_cases]->constant && + !pr_casesdef[num_cases]->temp && !pr_casesdef2[num_cases]->temp) + if (G_FLOAT(pr_casesdef[num_cases]->ofs) >= G_FLOAT(pr_casesdef2[num_cases]->ofs)) + QCC_PR_ParseError(ERR_CASENOTIMMEDIATE, "Caserange statement uses backwards range\n"); + } + else + pr_casesdef2[num_cases] = NULL; + + if (numstatements != pr_cases[num_cases]) + QCC_PR_ParseError(ERR_CASENOTIMMEDIATE, "Case statements may not use formulas\n"); + num_cases++; + QCC_PR_Expect(":"); + return; + } + if (QCC_PR_CheckKeyword(keyword_default, "default")) + { + if (num_cases >= max_cases) + { + max_cases += 8; + pr_cases = realloc(pr_cases, sizeof(*pr_cases)*max_cases); + pr_casesdef = realloc(pr_casesdef, sizeof(*pr_casesdef)*max_cases); + pr_casesdef2 = realloc(pr_casesdef2, sizeof(*pr_casesdef2)*max_cases); + } + pr_cases[num_cases] = numstatements; + pr_casesdef[num_cases] = NULL; + pr_casesdef2[num_cases] = NULL; + num_cases++; + QCC_PR_Expect(":"); + return; + } + + if (QCC_PR_CheckKeyword(keyword_thinktime, "thinktime")) + { + QCC_def_t *nextthink; + QCC_def_t *time; + e = QCC_PR_Expression (TOP_PRIORITY, 0); + QCC_PR_Expect(":"); + e2 = QCC_PR_Expression (TOP_PRIORITY, 0); + if (e->type->type != ev_entity || e2->type->type != ev_float) + QCC_PR_ParseError(ERR_THINKTIMETYPEMISMATCH, "thinktime type mismatch"); + + if (QCC_OPCodeValid(&pr_opcodes[OP_THINKTIME])) + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_THINKTIME], e, e2, NULL)); + else + { + nextthink = QCC_PR_GetDef(NULL, "nextthink", NULL, false, 0, false); + if (!nextthink) + QCC_PR_ParseError (ERR_UNKNOWNVALUE, "Unknown value \"%s\"", "nextthink"); + time = QCC_PR_GetDef(type_float, "time", NULL, false, 0, false); + if (!time) + QCC_PR_ParseError (ERR_UNKNOWNVALUE, "Unknown value \"%s\"", "time"); + nextthink = QCC_PR_Statement(&pr_opcodes[OP_ADDRESS], e, nextthink, NULL); + time = QCC_PR_Statement(&pr_opcodes[OP_ADD_F], time, e2, NULL); + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STOREP_F], time, nextthink, NULL)); + } + QCC_PR_Expect(";"); + return; + } + if (QCC_PR_CheckToken(";")) + { + int osl = pr_source_line; + pr_source_line = statementstart; + if (!expandedemptymacro) + QCC_PR_ParseWarning(WARN_POINTLESSSTATEMENT, "Hanging ';'"); + pr_source_line = osl; + return; + } + +// qcc_functioncalled=0; + + qcc_usefulstatement = false; + e = QCC_PR_Expression (TOP_PRIORITY, 0); + expandedemptymacro = false; + QCC_PR_Expect (";"); + + if (e->type->type != ev_void && !qcc_usefulstatement) + { + int osl = pr_source_line; + pr_source_line = statementstart; + QCC_PR_ParseWarning(WARN_POINTLESSSTATEMENT, "Effectless statement"); + pr_source_line = osl; + } + + QCC_FreeTemp(e); + +// qcc_functioncalled=false; +} + + +/* +============== +PR_ParseState + +States are special functions made for convenience. They automatically +set frame, nextthink (implicitly), and think (allowing forward definitions). + +// void() name = [framenum, nextthink] {code} +// expands to: +// function void name () +// { +// self.frame=framenum; +// self.nextthink = time + 0.1; +// self.think = nextthink +// +// }; +============== +*/ +void QCC_PR_ParseState (void) +{ + char *name; + QCC_def_t *s1, *def, *sc = pr_scope; + char f; + + f = *pr_token; + if (QCC_PR_CheckToken("++") || QCC_PR_CheckToken("--")) + { + s1 = QCC_PR_ParseImmediate (); + QCC_PR_Expect(".."); + def = QCC_PR_ParseImmediate (); + QCC_PR_Expect ("]"); + + if (s1->type->type != ev_float || def->type->type != ev_float) + QCC_PR_ParseError(ERR_STATETYPEMISMATCH, "state type mismatch"); + + + if (QCC_OPCodeValid(&pr_opcodes[OP_CSTATE])) + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_CSTATE], s1, def, NULL)); + else + { + QCC_def_t *t1, *t2; + QCC_def_t *framef, *frame; + QCC_def_t *self; + QCC_def_t *cycle_wrapped; + temp_t *ftemp; + + self = QCC_PR_GetDef(type_entity, "self", NULL, false, 0, false); + framef = QCC_PR_GetDef(NULL, "frame", NULL, false, 0, false); + cycle_wrapped = QCC_PR_GetDef(type_float, "cycle_wrapped", NULL, false, 0, false); + + frame = QCC_PR_Statement(&pr_opcodes[OP_LOAD_F], self, framef, NULL); + if (cycle_wrapped) + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_F], QCC_MakeFloatDef(0), cycle_wrapped, NULL)); + QCC_UnFreeTemp(frame); + + //make sure the frame is within the bounds given. + ftemp = frame->temp; + frame->temp = NULL; + t1 = QCC_PR_Statement(&pr_opcodes[OP_LT], frame, s1, NULL); + t2 = QCC_PR_Statement(&pr_opcodes[OP_GT], frame, def, NULL); + t1 = QCC_PR_Statement(&pr_opcodes[OP_OR], t1, t2, NULL); + QCC_PR_SimpleStatement(OP_IFNOT, t1->ofs, 2, 0, false); + QCC_FreeTemp(t1); + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_F], s1, frame, NULL)); + QCC_PR_SimpleStatement(OP_GOTO, t1->ofs, 13, 0, false); + + t1 = QCC_PR_Statement(&pr_opcodes[OP_GE], def, s1, NULL); + QCC_PR_SimpleStatement(OP_IFNOT, t1->ofs, 7, 0, false); + QCC_FreeTemp(t1); //this block is the 'it's in a forwards direction' + QCC_PR_SimpleStatement(OP_ADD_F, frame->ofs, QCC_MakeFloatDef(1)->ofs, frame->ofs, false); + t1 = QCC_PR_Statement(&pr_opcodes[OP_GT], frame, def, NULL); + QCC_PR_SimpleStatement(OP_IFNOT, t1->ofs,2, 0, false); + QCC_FreeTemp(t1); + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_F], s1, frame, NULL)); + QCC_UnFreeTemp(frame); + if (cycle_wrapped) + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_F], QCC_MakeFloatDef(1), cycle_wrapped, NULL)); + + QCC_PR_SimpleStatement(OP_GOTO, 6, 0, 0, false); + //reverse animation. + QCC_PR_SimpleStatement(OP_SUB_F, frame->ofs, QCC_MakeFloatDef(1)->ofs, frame->ofs, false); + t1 = QCC_PR_Statement(&pr_opcodes[OP_LT], frame, s1, NULL); + QCC_PR_SimpleStatement(OP_IFNOT, t1->ofs,2, 0, false); + QCC_FreeTemp(t1); + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_F], def, frame, NULL)); + QCC_UnFreeTemp(frame); + if (cycle_wrapped) + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_F], QCC_MakeFloatDef(1), cycle_wrapped, NULL)); + + //self.frame = frame happens with the normal state opcode. + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_STATE], frame, pr_scope, NULL)); + + frame->temp = ftemp; + QCC_FreeTemp(frame); + } + return; + } + + if (pr_token_type != tt_immediate || pr_immediate_type != type_float) + QCC_PR_ParseError (ERR_STATETYPEMISMATCH, "state frame must be a number"); + s1 = QCC_PR_ParseImmediate (); + + QCC_PR_CheckToken (","); + + name = QCC_PR_ParseName (); + pr_scope = NULL; + def = QCC_PR_GetDef (type_function, name, NULL, true, 1, false); + pr_scope = sc; + + QCC_PR_Expect ("]"); + + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_STATE], s1, def, NULL)); +} + +void QCC_PR_ParseAsm(void) +{ + QCC_dstatement_t *patch1; + int op, p; + QCC_def_t *a, *b, *c; + + if (QCC_PR_CheckKeyword(keyword_local, "local")) + { + QCC_PR_ParseDefs (NULL); + locals_end = numpr_globals; + return; + } + + for (op = 0; op < OP_NUMOPS; op++) + { + if (!STRCMP(pr_token, pr_opcodes[op].opname)) + { + QCC_PR_Lex(); + if (pr_opcodes[op].priority==-1 && pr_opcodes[op].associative!=ASSOC_LEFT) + { + if (pr_opcodes[op].type_a==NULL) + { + patch1 = &statements[numstatements]; + + QCC_PR_Statement3(&pr_opcodes[op], NULL, NULL, NULL, true); + + if (pr_token_type == tt_name) + { + QCC_PR_GotoStatement(patch1, QCC_PR_ParseName()); + } + else + { + p = (int)pr_immediate._float; + patch1->a = (int)p; + } + + QCC_PR_Lex(); + } + else if (pr_opcodes[op].type_b==NULL) + { + patch1 = &statements[numstatements]; + + a = QCC_PR_ParseValue(pr_classtype, false); + QCC_PR_Statement3(&pr_opcodes[op], a, NULL, NULL, true); + + if (pr_token_type == tt_name) + { + QCC_PR_GotoStatement(patch1, QCC_PR_ParseName()); + } + else + { + p = (int)pr_immediate._float; + patch1->b = (int)p; + } + + QCC_PR_Lex(); + } + else + { + patch1 = &statements[numstatements]; + + a = QCC_PR_ParseValue(pr_classtype, false); + b = QCC_PR_ParseValue(pr_classtype, false); + QCC_PR_Statement3(&pr_opcodes[op], a, b, NULL, true); + + if (pr_token_type == tt_name) + { + QCC_PR_GotoStatement(patch1, QCC_PR_ParseName()); + } + else + { + p = (int)pr_immediate._float; + patch1->c = (int)p; + } + + QCC_PR_Lex(); + } + } + else + { + if (pr_opcodes[op].type_a != &type_void) + a = QCC_PR_ParseValue(pr_classtype, false); + else + a=NULL; + if (pr_opcodes[op].type_b != &type_void) + b = QCC_PR_ParseValue(pr_classtype, false); + else + b=NULL; + if (pr_opcodes[op].associative==ASSOC_LEFT && pr_opcodes[op].type_c != &type_void) + c = QCC_PR_ParseValue(pr_classtype, false); + else + c=NULL; + + QCC_PR_Statement3(&pr_opcodes[op], a, b, c, true); + } + + QCC_PR_Expect(";"); + return; + } + } + QCC_PR_ParseError(ERR_BADOPCODE, "Bad op code name %s", pr_token); +} + +pbool QCC_FuncJumpsTo(int first, int last, int statement) +{ + int st; + for (st = first; st < last; st++) + { + if (pr_opcodes[statements[st].op].type_a == NULL) + { + if (st + (signed)statements[st].a == statement) + { + if (st != first) + { + if (statements[st-1].op == OP_RETURN) + continue; + if (statements[st-1].op == OP_DONE) + continue; + return true; + } + } + } + if (pr_opcodes[statements[st].op].type_b == NULL) + { + if (st + (signed)statements[st].b == statement) + { + if (st != first) + { + if (statements[st-1].op == OP_RETURN) + continue; + if (statements[st-1].op == OP_DONE) + continue; + return true; + } + } + } + if (pr_opcodes[statements[st].op].type_c == NULL) + { + if (st + (signed)statements[st].c == statement) + { + if (st != first) + { + if (statements[st-1].op == OP_RETURN) + continue; + if (statements[st-1].op == OP_DONE) + continue; + return true; + } + } + } + } + return false; +} + +pbool QCC_FuncJumpsToRange(int first, int last, int firstr, int lastr) +{ + int st; + for (st = first; st < last; st++) + { + if (pr_opcodes[statements[st].op].type_a == NULL) + { + if (st + (signed)statements[st].a >= firstr && st + (signed)statements[st].a <= lastr) + { + if (st != first) + { + if (statements[st-1].op == OP_RETURN) + continue; + if (statements[st-1].op == OP_DONE) + continue; + return true; + } + } + } + if (pr_opcodes[statements[st].op].type_b == NULL) + { + if (st + (signed)statements[st].b >= firstr && st + (signed)statements[st].b <= lastr) + { + if (st != first) + { + if (statements[st-1].op == OP_RETURN) + continue; + if (statements[st-1].op == OP_DONE) + continue; + return true; + } + } + } + if (pr_opcodes[statements[st].op].type_c == NULL) + { + if (st + (signed)statements[st].c >= firstr && st + (signed)statements[st].c <= lastr) + { + if (st != first) + { + if (statements[st-1].op == OP_RETURN) + continue; + if (statements[st-1].op == OP_DONE) + continue; + return true; + } + } + } + } + return false; +} + +#if 0 +void QCC_CompoundJumps(int first, int last) +{ + //jumps to jumps are reordered so they become jumps to the final target. + int statement; + int st; + for (st = first; st < last; st++) + { + if (pr_opcodes[statements[st].op].type_a == NULL) + { + statement = st + (signed)statements[st].a; + if (statements[statement].op == OP_RETURN || statements[statement].op == OP_DONE) + { //goto leads to return. Copy the command out to remove the goto. + statements[st].op = statements[statement].op; + statements[st].a = statements[statement].a; + statements[st].b = statements[statement].b; + statements[st].c = statements[statement].c; + optres_compound_jumps++; + } + while (statements[statement].op == OP_GOTO) + { + statements[st].a = statement+statements[statement].a - st; + statement = st + (signed)statements[st].a; + optres_compound_jumps++; + } + } + if (pr_opcodes[statements[st].op].type_b == NULL) + { + statement = st + (signed)statements[st].b; + while (statements[statement].op == OP_GOTO) + { + statements[st].b = statement+statements[statement].a - st; + statement = st + (signed)statements[st].b; + optres_compound_jumps++; + } + } + if (pr_opcodes[statements[st].op].type_c == NULL) + { + statement = st + (signed)statements[st].c; + while (statements[statement].op == OP_GOTO) + { + statements[st].c = statement+statements[statement].a - st; + statement = st + (signed)statements[st].c; + optres_compound_jumps++; + } + } + } +} +#else +void QCC_CompoundJumps(int first, int last) +{ + //jumps to jumps are reordered so they become jumps to the final target. + int statement; + int st; + int infloop; + for (st = first; st < last; st++) + { + if (pr_opcodes[statements[st].op].type_a == NULL) + { + statement = st + (signed)statements[st].a; + if (statements[statement].op == OP_RETURN || statements[statement].op == OP_DONE) + { //goto leads to return. Copy the command out to remove the goto. + statements[st].op = statements[statement].op; + statements[st].a = statements[statement].a; + statements[st].b = statements[statement].b; + statements[st].c = statements[statement].c; + optres_compound_jumps++; + } + infloop = 1000; + while (statements[statement].op == OP_GOTO) + { + if (!infloop--) + { + QCC_PR_ParseWarning(0, "Infinate loop detected"); + break; + } + statements[st].a = (statement+statements[statement].a - st); + statement = st + (signed)statements[st].a; + optres_compound_jumps++; + } + } + if (pr_opcodes[statements[st].op].type_b == NULL) + { + statement = st + (signed)statements[st].b; + infloop = 1000; + while (statements[statement].op == OP_GOTO) + { + if (!infloop--) + { + QCC_PR_ParseWarning(0, "Infinate loop detected"); + break; + } + statements[st].b = (statement+statements[statement].a - st); + statement = st + (signed)statements[st].b; + optres_compound_jumps++; + } + } + if (pr_opcodes[statements[st].op].type_c == NULL) + { + statement = st + (signed)statements[st].c; + infloop = 1000; + while (statements[statement].op == OP_GOTO) + { + if (!infloop--) + { + QCC_PR_ParseWarning(0, "Infinate loop detected"); + break; + } + statements[st].c = (statement+statements[statement].a - st); + statement = st + (signed)statements[st].c; + optres_compound_jumps++; + } + } + } +} +#endif + +void QCC_CheckForDeadAndMissingReturns(int first, int last, int rettype) +{ + int st, st2; + + if (statements[last-1].op == OP_DONE) + last--; //don't want the done + + if (rettype != ev_void) + if (statements[last-1].op != OP_RETURN) + { + if (statements[last-1].op != OP_GOTO || (signed)statements[last-1].a > 0) + { + QCC_PR_ParseWarning(WARN_MISSINGRETURN, "%s: not all control paths return a value", pr_scope->name ); + return; + } + } + + for (st = first; st < last; st++) + { + if (statements[st].op == OP_RETURN || statements[st].op == OP_GOTO) + { + st++; + if (st == last) + continue; //erm... end of function doesn't count as unreachable. + + if (!opt_compound_jumps) + { //we can ignore single statements like these without compound jumps (compound jumps correctly removes all). + if (statements[st].op == OP_GOTO) //inefficient compiler, we can ignore this. + continue; + if (statements[st].op == OP_DONE) //inefficient compiler, we can ignore this. + continue; + if (statements[st].op == OP_RETURN) //inefficient compiler, we can ignore this. + continue; + } + + //make sure something goes to just after this return. + for (st2 = first; st2 < last; st2++) + { + if (pr_opcodes[statements[st2].op].type_a == NULL) + { + if (st2 + (signed)statements[st2].a == st) + break; + } + if (pr_opcodes[statements[st2].op].type_b == NULL) + { + if (st2 + (signed)statements[st2].b == st) + break; + } + if (pr_opcodes[statements[st2].op].type_c == NULL) + { + if (st2 + (signed)statements[st2].c == st) + break; + } + } + if (st2 == last) + { + QCC_PR_ParseWarning(WARN_UNREACHABLECODE, "%s: contains unreachable code", pr_scope->name ); + } + continue; + } + if (rettype != ev_void) + { + if (pr_opcodes[statements[st].op].type_a == NULL) + { + if (st + (signed)statements[st].a == last) + { + QCC_PR_ParseWarning(WARN_MISSINGRETURN, "%s: not all control paths return a value", pr_scope->name ); + return; + } + } + if (pr_opcodes[statements[st].op].type_b == NULL) + { + if (st + (signed)statements[st].b == last) + { + QCC_PR_ParseWarning(WARN_MISSINGRETURN, "%s: not all control paths return a value", pr_scope->name ); + return; + } + } + if (pr_opcodes[statements[st].op].type_c == NULL) + { + if (st + (signed)statements[st].c == last) + { + QCC_PR_ParseWarning(WARN_MISSINGRETURN, "%s: not all control paths return a value", pr_scope->name ); + return; + } + } + } + } +} + +pbool QCC_StatementIsAJump(int stnum, int notifdest) //only the unconditionals. +{ + if (statements[stnum].op == OP_RETURN) + return true; + if (statements[stnum].op == OP_DONE) + return true; + if (statements[stnum].op == OP_GOTO) + if ((int)statements[stnum].a != notifdest) + return true; + return false; +} + +int QCC_AStatementJumpsTo(int targ, int first, int last) +{ + int st; + for (st = first; st < last; st++) + { + if (pr_opcodes[statements[st].op].type_a == NULL) + { + if (st + (signed)statements[st].a == targ && statements[st].a) + { + return true; + } + } + if (pr_opcodes[statements[st].op].type_b == NULL) + { + if (st + (signed)statements[st].b == targ) + { + return true; + } + } + if (pr_opcodes[statements[st].op].type_c == NULL) + { + if (st + (signed)statements[st].c == targ) + { + return true; + } + } + } + + for (st = 0; st < num_labels; st++) //assume it's used. + { + if (pr_labels[st].statementno == targ) + return true; + } + + + return false; +} +/* +//goes through statements, if it sees a matching statement earlier, it'll strim out the current. +void QCC_CommonSubExpressionRemoval(int first, int last) +{ + int cur; //the current + int prev; //the earlier statement + for (cur = last-1; cur >= first; cur--) + { + if (pr_opcodes[statements[cur].op].priority == -1) + continue; + for (prev = cur-1; prev >= first; prev--) + { + if (statements[prev].op >= OP_CALL0 && statements[prev].op <= OP_CALL8) + { + optres_test1++; + break; + } + if (statements[prev].op >= OP_CALL1H && statements[prev].op <= OP_CALL8H) + { + optres_test1++; + break; + } + if (pr_opcodes[statements[prev].op].right_associative) + { //make sure no changes to var_a occur. + if (statements[prev].b == statements[cur].a) + { + optres_test2++; + break; + } + if (statements[prev].b == statements[cur].b && !pr_opcodes[statements[cur].op].right_associative) + { + optres_test2++; + break; + } + } + else + { + if (statements[prev].c == statements[cur].a) + { + optres_test2++; + break; + } + if (statements[prev].c == statements[cur].b && !pr_opcodes[statements[cur].op].right_associative) + { + optres_test2++; + break; + } + } + + if (statements[prev].op == statements[cur].op) + if (statements[prev].a == statements[cur].a) + if (statements[prev].b == statements[cur].b) + if (statements[prev].c == statements[cur].c) + { + if (!QCC_FuncJumpsToRange(first, last, prev, cur)) + { + statements[cur].op = OP_STORE_F; + statements[cur].a = 28; + statements[cur].b = 28; + optres_comexprremoval++; + } + else + optres_test1++; + break; + } + } + } +} +*/ + +void QCC_RemapOffsets(unsigned int firststatement, unsigned int laststatement, unsigned int min, unsigned int max, unsigned int newmin) +{ + QCC_dstatement_t *st; + unsigned int i; + + for (i = firststatement, st = &statements[i]; i < laststatement; i++, st++) + { + if (pr_opcodes[st->op].type_a && st->a >= min && st->a < max) + st->a = st->a - min + newmin; + if (pr_opcodes[st->op].type_b && st->b >= min && st->b < max) + st->b = st->b - min + newmin; + if (pr_opcodes[st->op].type_c && st->c >= min && st->c < max) + st->c = st->c - min + newmin; + } +} + +void QCC_Marshal_Locals(int first, int laststatement) +{ + QCC_def_t *local; + unsigned int newofs; + +// if (!opt_overlaptemps) //clear these after each function. we arn't overlapping them so why do we need to keep track of them? +// { +// temp_t *t; +// for (t = functemps; t; t = t->next) +// QCC_FreeOffset(t->ofs, t->size); +// functemps = NULL; +// } + + if (!pr.localvars) //nothing to marshal + { + locals_start = numpr_globals; + locals_end = numpr_globals; + return; + } + + if (!opt_locals_marshalling) + { + pr.localvars = NULL; + return; + } + + //initial backwards bounds. + locals_start = MAX_REGS; + locals_end = 0; + + newofs = MAX_REGS; //this is a handy place to put it. :) + + //the params need to be in the order that they were allocated + //so we allocate in a backwards order. + for (local = pr.localvars; local; local = local->nextlocal) + { + if (local->constant) + continue; + + newofs += local->type->size*local->arraysize; + if (local->arraysize>1) + newofs++; + } + + locals_start = MAX_REGS; + locals_end = newofs; + + + optres_locals_marshalling+=newofs-MAX_REGS; + + for (local = pr.localvars; local; local = local->nextlocal) + { + if (local->constant) + continue; + + if (((int*)qcc_pr_globals)[local->ofs]) + QCC_PR_ParseError(ERR_INTERNAL, "Marshall of a set value"); + + newofs -= local->type->size*local->arraysize; + if (local->arraysize>1) + newofs--; + + QCC_RemapOffsets(first, laststatement, local->ofs, local->ofs+local->type->size*local->arraysize, newofs); + QCC_FreeOffset(local->ofs, local->type->size*local->arraysize); + + local->ofs = newofs; + } + + + pr.localvars = NULL; +} + +#ifdef WRITEASM +void QCC_WriteAsmFunction(QCC_def_t *sc, unsigned int firststatement, gofs_t firstparm) +{ + unsigned int i; + unsigned int p; + gofs_t o; + QCC_type_t *type; + QCC_def_t *param; + + if (!asmfile) + return; + + type = sc->type; + fprintf(asmfile, "%s(", TypeName(type->aux_type)); + p = type->num_parms; + for (o = firstparm, i = 0, type = type->param; i < p; i++, type = type->next) + { + if (i) + fprintf(asmfile, ", "); + + for (param = pr.localvars; param; param = param->nextlocal) + { + if (param->ofs == o) + break; + } + if (param) + fprintf(asmfile, "%s %s", TypeName(type), param->name); + else + fprintf(asmfile, "%s", TypeName(type)); + + o += type->size; + } + + fprintf(asmfile, ") %s = asm\n{\n", sc->name); + + QCC_fprintfLocals(asmfile, firstparm, o); + + for (i = firststatement; i < (unsigned int)numstatements; i++) + { + fprintf(asmfile, "\t%s", pr_opcodes[statements[i].op].opname); + if (pr_opcodes[statements[i].op].type_a != &type_void) + { + if (strlen(pr_opcodes[statements[i].op].opname)<6) + fprintf(asmfile, "\t"); + if (pr_opcodes[statements[i].op].type_a) + fprintf(asmfile, "\t%s", QCC_VarAtOffset(statements[i].a, (*pr_opcodes[statements[i].op].type_a)->size)); + else + fprintf(asmfile, "\t%i", statements[i].a); + if (pr_opcodes[statements[i].op].type_b != &type_void) + { + if (pr_opcodes[statements[i].op].type_b) + fprintf(asmfile, ",\t%s", QCC_VarAtOffset(statements[i].b, (*pr_opcodes[statements[i].op].type_b)->size)); + else + fprintf(asmfile, ",\t%i", statements[i].b); + if (pr_opcodes[statements[i].op].type_c != &type_void && pr_opcodes[statements[i].op].associative==ASSOC_LEFT) + { + if (pr_opcodes[statements[i].op].type_c) + fprintf(asmfile, ",\t%s", QCC_VarAtOffset(statements[i].c, (*pr_opcodes[statements[i].op].type_c)->size)); + else + fprintf(asmfile, ",\t%i", statements[i].c); + } + } + else + { + if (pr_opcodes[statements[i].op].type_c != &type_void) + { + if (pr_opcodes[statements[i].op].type_c) + fprintf(asmfile, ",\t%s", QCC_VarAtOffset(statements[i].c, (*pr_opcodes[statements[i].op].type_c)->size)); + else + fprintf(asmfile, ",\t%i", statements[i].c); + } + } + } + fprintf(asmfile, ";\n"); + } + + fprintf(asmfile, "}\n\n"); +} +#endif + +/* +============ +PR_ParseImmediateStatements + +Parse a function body +============ +*/ +QCC_function_t *QCC_PR_ParseImmediateStatements (QCC_type_t *type) +{ + int i; + QCC_function_t *f; + QCC_def_t *defs[MAX_PARMS+MAX_EXTRA_PARMS], *e2; + + QCC_type_t *parm; + pbool needsdone=false; + freeoffset_t *oldfofs; + + conditional = 0; + + expandedemptymacro = false; + + + f = (void *)qccHunkAlloc (sizeof(QCC_function_t)); + +// +// check for builtin function definition #1, #2, etc +// +// hexenC has void name() : 2; + if (QCC_PR_CheckToken ("#") || QCC_PR_CheckToken (":")) + { + int binum = 0; + if (pr_token_type == tt_immediate + && pr_immediate_type == type_float + && pr_immediate._float == (int)pr_immediate._float) + binum = (int)pr_immediate._float; + else if (pr_token_type == tt_immediate && pr_immediate_type == type_integer) + binum = pr_immediate._int; + else + QCC_PR_ParseError (ERR_BADBUILTINIMMEDIATE, "Bad builtin immediate"); + f->builtin = binum; + QCC_PR_Lex (); + + locals_start = locals_end = OFS_PARM0; //hmm... + return f; + } + if (QCC_PR_CheckKeyword(keyword_external, "external")) + { //reacc style builtin + if (pr_token_type != tt_immediate + || pr_immediate_type != type_float + || pr_immediate._float != (int)pr_immediate._float) + QCC_PR_ParseError (ERR_BADBUILTINIMMEDIATE, "Bad builtin immediate"); + f->builtin = (int)-pr_immediate._float; + QCC_PR_Lex (); + QCC_PR_Expect(";"); + + locals_start = locals_end = OFS_PARM0; //hmm... + return f; + } + + if (type->num_parms < 0) + QCC_PR_ParseError (ERR_FUNCTIONWITHVARGS, "QC function with variable arguments and function body"); + + f->builtin = 0; +// +// define the parms +// + + locals_start = locals_end = numpr_globals; + + oldfofs = freeofs; + freeofs = NULL; + + parm = type->param; + for (i=0 ; inum_parms ; i++) + { + if (!*pr_parm_names[i]) + QCC_PR_ParseError(ERR_PARAMWITHNONAME, "Parameter is not named"); + defs[i] = QCC_PR_GetDef (parm, pr_parm_names[i], pr_scope, true, 1, false); + + defs[i]->references++; + if (i < MAX_PARMS) + { + f->parm_ofs[i] = defs[i]->ofs; + if (i > 0 && f->parm_ofs[i] < f->parm_ofs[i-1]) + QCC_Error (ERR_BADPARAMORDER, "bad parm order"); + if (i > 0 && f->parm_ofs[i] != f->parm_ofs[i-1]+defs[i-1]->type->size) + QCC_Error (ERR_BADPARAMORDER, "parms not packed"); + } + parm = parm->next; + } + + if (type->num_parms) + locals_start = locals_end = defs[0]->ofs; + + freeofs = oldfofs; + + f->code = numstatements; + + if (type->num_parms > MAX_PARMS) + { + for (i = MAX_PARMS; i < type->num_parms; i++) + { + if (!extra_parms[i - MAX_PARMS]) + { + e2 = (QCC_def_t *) qccHunkAlloc (sizeof(QCC_def_t)); + e2->name = "extra parm"; + e2->ofs = QCC_GetFreeOffsetSpace(3); + extra_parms[i - MAX_PARMS] = e2; + } + extra_parms[i - MAX_PARMS]->type = defs[i]->type; + if (defs[i]->type->type != ev_vector) + QCC_PR_Statement (&pr_opcodes[OP_STORE_F], extra_parms[i - MAX_PARMS], defs[i], NULL); + else + QCC_PR_Statement (&pr_opcodes[OP_STORE_V], extra_parms[i - MAX_PARMS], defs[i], NULL); + } + } + + QCC_RemapLockedTemps(-1, -1); + + /*if (pr_classtype) + { + QCC_def_t *e, *e2; + e = QCC_PR_GetDef(pr_classtype, "__oself", pr_scope, true, 1); + e2 = QCC_PR_GetDef(type_entity, "self", NULL, true, 1); + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_ENT], QCC_PR_DummyDef(pr_classtype, "self", pr_scope, 1, e2->ofs, false), e, NULL)); + }*/ + +// +// check for a state opcode +// + if (QCC_PR_CheckToken ("[")) + QCC_PR_ParseState (); + + if (QCC_PR_CheckKeyword (keyword_asm, "asm")) + { + QCC_PR_Expect ("{"); + while (!QCC_PR_CheckToken("}")) + QCC_PR_ParseAsm (); + } + else + { + if (QCC_PR_CheckKeyword (keyword_var, "var")) //reacc support + { //parse lots of locals + char *name; + do { + name = QCC_PR_ParseName(); + QCC_PR_Expect(":"); + e2 = QCC_PR_GetDef(QCC_PR_ParseType(false), name, pr_scope, true, 1, false); + QCC_PR_Expect(";"); + } while(!QCC_PR_CheckToken("{")); + } + else + QCC_PR_Expect ("{"); +// +// parse regular statements +// + while (!QCC_PR_CheckToken("}")) + { + QCC_PR_ParseStatement (); + QCC_FreeTemps(); + } + } + QCC_FreeTemps(); + + // this is cheap +// if (type->aux_type->type) +// if (statements[numstatements - 1].op != OP_RETURN) +// QCC_PR_ParseWarning(WARN_MISSINGRETURN, "%s: not all control paths return a value", pr_scope->name ); + + if (f->code == numstatements) + needsdone = true; + else if (statements[numstatements - 1].op != OP_RETURN && statements[numstatements - 1].op != OP_DONE) + needsdone = true; + + if (num_gotos) + { + int j; + for (i = 0; i < num_gotos; i++) + { + for (j = 0; j < num_labels; j++) + { + if (!strcmp(pr_gotos[i].name, pr_labels[j].name)) + { + if (!pr_opcodes[statements[pr_gotos[i].statementno].op].type_a) + statements[pr_gotos[i].statementno].a += pr_labels[j].statementno - pr_gotos[i].statementno; + else if (!pr_opcodes[statements[pr_gotos[i].statementno].op].type_b) + statements[pr_gotos[i].statementno].b += pr_labels[j].statementno - pr_gotos[i].statementno; + else + statements[pr_gotos[i].statementno].c += pr_labels[j].statementno - pr_gotos[i].statementno; + break; + } + } + if (j == num_labels) + { + num_gotos = 0; + QCC_PR_ParseError(ERR_NOLABEL, "Goto statement with no matching label \"%s\"", pr_gotos[i].name); + } + } + num_gotos = 0; + } + + if (opt_return_only && !needsdone) + needsdone = QCC_FuncJumpsTo(f->code, numstatements, numstatements); + + // emit an end of statements opcode + if (!opt_return_only || needsdone) + { + /*if (pr_classtype) + { + QCC_def_t *e, *e2; + e = QCC_PR_GetDef(NULL, "__oself", pr_scope, false, 0); + e2 = QCC_PR_GetDef(NULL, "self", NULL, false, 0); + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_ENT], e, QCC_PR_DummyDef(pr_classtype, "self", pr_scope, 1, e2->ofs, false), NULL)); + }*/ + + QCC_PR_Statement (pr_opcodes, 0,0, NULL); + } + else + optres_return_only++; + + QCC_CheckForDeadAndMissingReturns(f->code, numstatements, type->aux_type->type); + + if (opt_compound_jumps) + QCC_CompoundJumps(f->code, numstatements); +// if (opt_comexprremoval) +// QCC_CommonSubExpressionRemoval(f->code, numstatements); + + + QCC_RemapLockedTemps(f->code, numstatements); + locals_end = numpr_globals; + + QCC_WriteAsmFunction(pr_scope, f->code, locals_start); + + QCC_Marshal_Locals(f->code, numstatements); + + if (num_labels) + num_labels = 0; + + + if (num_continues) + { + num_continues=0; + QCC_PR_ParseError(ERR_ILLEGALCONTINUES, "%s: function contains illegal continues", pr_scope->name); + } + if (num_breaks) + { + num_breaks=0; + QCC_PR_ParseError(ERR_ILLEGALBREAKS, "%s: function contains illegal breaks", pr_scope->name); + } + if (num_cases) + { + num_cases = 0; + QCC_PR_ParseError(ERR_ILLEGALCASES, "%s: function contains illegal cases", pr_scope->name); + } + + return f; +} + +void QCC_PR_ArrayRecurseDivideRegular(QCC_def_t *array, QCC_def_t *index, int min, int max) +{ + QCC_dstatement_t *st; + QCC_def_t *eq; + if (min == max || min+1 == max) + { + eq = QCC_PR_Statement(pr_opcodes+OP_LT, index, QCC_MakeFloatDef(min+0.5f), NULL); + QCC_UnFreeTemp(index); + QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_IFNOT, eq, 0, &st)); + st->b = 2; + QCC_PR_Statement(pr_opcodes+OP_RETURN, 0, 0, &st); + st->a = array->ofs + min*array->type->size; + } + else + { + int mid = min + (max-min)/2; + + if (max-min>4) + { + eq = QCC_PR_Statement(pr_opcodes+OP_LT, index, QCC_MakeFloatDef(mid+0.5f), NULL); + QCC_UnFreeTemp(index); + QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_IFNOT, eq, 0, &st)); + } + else + st = NULL; + QCC_PR_ArrayRecurseDivideRegular(array, index, min, mid); + if (st) + st->b = numstatements - (st-statements); + QCC_PR_ArrayRecurseDivideRegular(array, index, mid, max); + } +} + +//the idea here is that we return a vector, the caller then figures out the extra 3rd. +//This is useful when we have a load of indexes. +void QCC_PR_ArrayRecurseDivideUsingVectors(QCC_def_t *array, QCC_def_t *index, int min, int max) +{ + QCC_dstatement_t *st; + QCC_def_t *eq; + if (min == max || min+1 == max) + { + eq = QCC_PR_Statement(pr_opcodes+OP_LT, index, QCC_MakeFloatDef(min+0.5f), NULL); + QCC_UnFreeTemp(index); + QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_IFNOT, eq, 0, &st)); + st->b = 2; + QCC_PR_Statement(pr_opcodes+OP_RETURN, 0, 0, &st); + st->a = array->ofs + min*3; + } + else + { + int mid = min + (max-min)/2; + + if (max-min>4) + { + eq = QCC_PR_Statement(pr_opcodes+OP_LT, index, QCC_MakeFloatDef(mid+0.5f), NULL); + QCC_UnFreeTemp(index); + QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_IFNOT, eq, 0, &st)); + } + else + st = NULL; + QCC_PR_ArrayRecurseDivideUsingVectors(array, index, min, mid); + if (st) + st->b = numstatements - (st-statements); + QCC_PR_ArrayRecurseDivideUsingVectors(array, index, mid, max); + } +} + +//returns a vector overlapping the result needed. +QCC_def_t *QCC_PR_EmitArrayGetVector(QCC_def_t *array) +{ + QCC_dfunction_t *df; + QCC_def_t *temp, *index, *func; + + func = QCC_PR_GetDef(type_function, qcva("ArrayGetVec*%s", array->name), NULL, true, 1, false); + + pr_scope = func; + + df = &functions[numfunctions]; + numfunctions++; + + df->s_file = 0; + df->s_name = QCC_CopyString(func->name); + df->first_statement = numstatements; + df->parm_size[0] = 1; + df->numparms = 1; + df->parm_start = numpr_globals; + index = QCC_PR_GetDef(type_float, "index___", func, true, 1, false); + index->references++; + temp = QCC_PR_GetDef(type_float, "div3___", func, true, 1, false); + locals_end = numpr_globals; + df->locals = locals_end - df->parm_start; + QCC_PR_Statement3(pr_opcodes+OP_DIV_F, index, QCC_MakeFloatDef(3), temp, false); + QCC_PR_Statement3(pr_opcodes+OP_BITAND, temp, temp, temp, false);//round down to int + + QCC_PR_ArrayRecurseDivideUsingVectors(array, temp, 0, (array->arraysize+2)/3); //round up + + QCC_PR_Statement(pr_opcodes+OP_RETURN, QCC_MakeFloatDef(0), 0, NULL); //err... we didn't find it, give up. + QCC_PR_Statement(pr_opcodes+OP_DONE, 0, 0, NULL); //err... we didn't find it, give up. + + G_FUNCTION(func->ofs) = df - functions; + func->initialized = 1; + return func; +} + +void QCC_PR_EmitArrayGetFunction(QCC_def_t *scope, char *arrayname) +{ + QCC_def_t *vectortrick; + QCC_dfunction_t *df; + QCC_def_t *def, *index; + + QCC_dstatement_t *st; + QCC_def_t *eq; + + QCC_def_t *fasttrackpossible; + + if (flag_fasttrackarrays) + fasttrackpossible = QCC_PR_GetDef(type_float, "__ext__fasttrackarrays", NULL, true, 1, false); + else + fasttrackpossible = NULL; + + def = QCC_PR_GetDef(NULL, arrayname, NULL, false, 0, false); + + if (def->arraysize >= 15 && def->type->size == 1) + { + vectortrick = QCC_PR_EmitArrayGetVector(def); + } + else + vectortrick = NULL; + + pr_scope = scope; + + df = &functions[numfunctions]; + numfunctions++; + + df->s_file = 0; + df->s_name = QCC_CopyString(scope->name); + df->first_statement = numstatements; + df->parm_size[0] = 1; + df->numparms = 1; + df->parm_start = numpr_globals; + index = QCC_PR_GetDef(type_float, "indexg___", def, true, 1, false); + + G_FUNCTION(scope->ofs) = df - functions; + + if (fasttrackpossible) + { + QCC_PR_Statement(pr_opcodes+OP_IFNOT, fasttrackpossible, NULL, &st); + //fetch_gbl takes: (float size, variant array[]), float index, variant pos + //note that the array size is coded into the globals, one index before the array. + + if (def->type->size >= 3) + QCC_PR_Statement3(&pr_opcodes[OP_FETCH_GBL_V], def, index, &def_ret, true); + else + QCC_PR_Statement3(&pr_opcodes[OP_FETCH_GBL_F], def, index, &def_ret, true); + + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_RETURN], &def_ret, NULL, NULL)); + + //finish the jump + st->b = &statements[numstatements] - st; + } + + if (vectortrick) + { + QCC_def_t *div3, *intdiv3, *ret; + + //okay, we've got a function to retrieve the var as part of a vector. + //we need to work out which part, x/y/z that it's stored in. + //0,1,2 = i - ((int)i/3 *) 3; + + div3 = QCC_PR_GetDef(type_float, "div3___", def, true, 1, false); + intdiv3 = QCC_PR_GetDef(type_float, "intdiv3___", def, true, 1, false); + + eq = QCC_PR_Statement(pr_opcodes+OP_GE, index, QCC_MakeFloatDef((float)def->arraysize), NULL); //escape clause - should call some sort of error function instead.. that'd rule! + QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_IFNOT, eq, 0, &st)); + st->b = 2; + QCC_PR_Statement(pr_opcodes+OP_RETURN, QCC_MakeFloatDef(0), 0, &st); + + div3->references++; + QCC_PR_Statement3(pr_opcodes+OP_BITAND, index, index, index, false); + QCC_PR_Statement3(pr_opcodes+OP_DIV_F, index, QCC_MakeFloatDef(3), div3, false); + QCC_PR_Statement3(pr_opcodes+OP_BITAND, div3, div3, intdiv3, false); + + QCC_PR_Statement3(pr_opcodes+OP_STORE_F, index, &def_parms[0], NULL, false); + QCC_PR_Statement3(pr_opcodes+OP_CALL1, vectortrick, NULL, NULL, false); + vectortrick->references++; + ret = QCC_PR_GetDef(type_vector, "vec__", pr_scope, true, 1, false); + ret->references+=4; + QCC_PR_Statement3(pr_opcodes+OP_STORE_V, &def_ret, ret, NULL, false); + QCC_FreeTemp(&def_ret); + + div3 = QCC_PR_Statement(pr_opcodes+OP_MUL_F, intdiv3, QCC_MakeFloatDef(3), NULL); + QCC_PR_Statement3(pr_opcodes+OP_SUB_F, index, div3, index, false); + QCC_FreeTemp(div3); + + eq = QCC_PR_Statement(pr_opcodes+OP_LT, index, QCC_MakeFloatDef(0+0.5f), NULL); + QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_IFNOT, eq, 0, &st)); + st->b = 2; + QCC_PR_Statement(pr_opcodes+OP_RETURN, 0, 0, &st); + st->a = ret->ofs + 0; + + eq = QCC_PR_Statement(pr_opcodes+OP_LT, index, QCC_MakeFloatDef(1+0.5f), NULL); + QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_IFNOT, eq, 0, &st)); + st->b = 2; + QCC_PR_Statement(pr_opcodes+OP_RETURN, 0, 0, &st); + st->a = ret->ofs + 1; + + eq = QCC_PR_Statement(pr_opcodes+OP_LT, index, QCC_MakeFloatDef(2+0.5), NULL); + QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_IFNOT, eq, 0, &st)); + st->b = 2; + QCC_PR_Statement(pr_opcodes+OP_RETURN, 0, 0, &st); + st->a = ret->ofs + 2; + QCC_FreeTemp(ret); + QCC_FreeTemp(index); + } + else + { + QCC_PR_Statement3(pr_opcodes+OP_BITAND, index, index, index, false); + QCC_PR_ArrayRecurseDivideRegular(def, index, 0, def->arraysize); + } + + QCC_PR_Statement(pr_opcodes+OP_RETURN, QCC_MakeFloatDef(0), 0, NULL); + + QCC_PR_Statement(pr_opcodes+OP_DONE, 0, 0, NULL); + + locals_end = numpr_globals; + df->locals = locals_end - df->parm_start; + + + QCC_WriteAsmFunction(pr_scope, df->first_statement, df->parm_start); + + QCC_FreeTemps(); +} + +void QCC_PR_ArraySetRecurseDivide(QCC_def_t *array, QCC_def_t *index, QCC_def_t *value, int min, int max) +{ + QCC_dstatement_t *st; + QCC_def_t *eq; + if (min == max || min+1 == max) + { + eq = QCC_PR_Statement(pr_opcodes+OP_EQ_F, index, QCC_MakeFloatDef((float)min), NULL); + QCC_UnFreeTemp(index); + QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_IFNOT, eq, 0, &st)); + st->b = 3; + if (array->type->size == 3) + QCC_PR_Statement(pr_opcodes+OP_STORE_V, value, array, &st); + else + QCC_PR_Statement(pr_opcodes+OP_STORE_F, value, array, &st); + st->b = array->ofs + min*array->type->size; + QCC_PR_Statement(pr_opcodes+OP_RETURN, 0, 0, &st); + } + else + { + int mid = min + (max-min)/2; + + if (max-min>4) + { + eq = QCC_PR_Statement(pr_opcodes+OP_LT, index, QCC_MakeFloatDef((float)mid), NULL); + QCC_UnFreeTemp(index); + QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_IFNOT, eq, 0, &st)); + } + else + st = NULL; + QCC_PR_ArraySetRecurseDivide(array, index, value, min, mid); + if (st) + st->b = numstatements - (st-statements); + QCC_PR_ArraySetRecurseDivide(array, index, value, mid, max); + } +} + +void QCC_PR_EmitArraySetFunction(QCC_def_t *scope, char *arrayname) +{ + QCC_dfunction_t *df; + QCC_def_t *def, *index, *value; + + QCC_def_t *fasttrackpossible; + + if (flag_fasttrackarrays) + fasttrackpossible = QCC_PR_GetDef(type_float, "__ext__fasttrackarrays", NULL, true, 1, false); + else + fasttrackpossible = NULL; + + def = QCC_PR_GetDef(NULL, arrayname, NULL, false, 0, false); + pr_scope = scope; + + df = &functions[numfunctions]; + numfunctions++; + + df->s_file = 0; + df->s_name = QCC_CopyString(scope->name); + df->first_statement = numstatements; + df->parm_size[0] = 1; + df->parm_size[1] = def->type->size; + df->numparms = 2; + df->parm_start = numpr_globals; + index = QCC_PR_GetDef(type_float, "indexs___", def, true, 1, false); + value = QCC_PR_GetDef(def->type, "value___", def, true, 1, false); + locals_end = numpr_globals; + df->locals = locals_end - df->parm_start; + + G_FUNCTION(scope->ofs) = df - functions; + + if (fasttrackpossible) + { + QCC_dstatement_t *st; + + QCC_PR_Statement(pr_opcodes+OP_IFNOT, fasttrackpossible, NULL, &st); + //note that the array size is coded into the globals, one index before the array. + + QCC_PR_Statement3(&pr_opcodes[OP_CONV_FTOI], index, NULL, index, true); //address stuff is integer based, but standard qc (which this accelerates in supported engines) only supports floats + QCC_PR_SimpleStatement (OP_BOUNDCHECK, index->ofs, ((int*)qcc_pr_globals)[def->ofs-1]+1, 0, true);//annoy the programmer. :p + if (def->type->size != 1)//shift it upwards for larger types + QCC_PR_Statement3(&pr_opcodes[OP_MUL_I], index, QCC_MakeIntDef(def->type->size), index, true); + QCC_PR_Statement3(&pr_opcodes[OP_GLOBALADDRESS], def, index, index, true); //comes with built in add + if (def->type->size >= 3) + QCC_PR_Statement3(&pr_opcodes[OP_STOREP_V], value, index, NULL, true); //*b = a + else + QCC_PR_Statement3(&pr_opcodes[OP_STOREP_F], value, index, NULL, true); + QCC_PR_Statement(&pr_opcodes[OP_RETURN], value, NULL, NULL); + + //finish the jump + st->b = &statements[numstatements] - st; + } + + QCC_PR_Statement3(pr_opcodes+OP_BITAND, index, index, index, false); + QCC_PR_ArraySetRecurseDivide(def, index, value, 0, def->arraysize); + + QCC_PR_Statement(pr_opcodes+OP_DONE, 0, 0, NULL); + + + + QCC_WriteAsmFunction(pr_scope, df->first_statement, df->parm_start); + + QCC_FreeTemps(); +} + +//register a def, and all of it's sub parts. +//only the main def is of use to the compiler. +//the subparts are emitted to the compiler and allow correct saving/loading +//be careful with fields, this doesn't allocated space, so will it allocate fields. It only creates defs at specified offsets. +QCC_def_t *QCC_PR_DummyDef(QCC_type_t *type, char *name, QCC_def_t *scope, int arraysize, unsigned int ofs, int referable, pbool saved) +{ + char array[64]; + char newname[256]; + int a; + QCC_def_t *def, *first=NULL; + +#define KEYWORD(x) if (!STRCMP(name, #x) && keyword_##x) {if (keyword_##x)QCC_PR_ParseWarning(WARN_KEYWORDDISABLED, "\""#x"\" keyword used as variable name%s", keywords_coexist?" - coexisting":" - disabling");keyword_##x=keywords_coexist;} + if (name) + { + KEYWORD(var); + KEYWORD(thinktime); + KEYWORD(for); + KEYWORD(switch); + KEYWORD(case); + KEYWORD(default); + KEYWORD(goto); + if (type->type != ev_function) + KEYWORD(break); + KEYWORD(continue); + KEYWORD(state); + KEYWORD(string); + if (qcc_targetformat != QCF_HEXEN2) + KEYWORD(float); //hmm... hexen2 requires this... + KEYWORD(entity); + KEYWORD(vector); + KEYWORD(const); + KEYWORD(asm); + } + + for (a = 0; a < arraysize; a++) + { + if (a == 0) + *array = '\0'; + else + sprintf(array, "[%i]", a); + + if (name) + sprintf(newname, "%s%s", name, array); + else + *newname = *""; + + // allocate a new def + def = (void *)qccHunkAlloc (sizeof(QCC_def_t)); + memset (def, 0, sizeof(*def)); + def->next = NULL; + def->arraysize = arraysize; + if (name) + { + pr.def_tail->next = def; + pr.def_tail = def; + } + + if (a > 0) + def->references++; + + def->s_line = pr_source_line; + def->s_file = s_file; + + def->name = (void *)qccHunkAlloc (strlen(newname)+1); + strcpy (def->name, newname); + def->type = type; + + def->scope = scope; + def->saved = saved; + + // if (arraysize>1) + def->constant = true; + + if (ofs + type->size*a >= MAX_REGS) + QCC_Error(ERR_TOOMANYGLOBALS, "MAX_REGS is too small"); + def->ofs = ofs + type->size*a; + if (!first) + first = def; + +// printf("Emited %s\n", newname); + + if (type->type == ev_struct) + { + int partnum; + QCC_type_t *parttype; + parttype = type->param; + for (partnum = 0; partnum < type->num_parms; partnum++) + { + switch (parttype->type) + { + case ev_vector: + sprintf(newname, "%s%s.%s", name, array, parttype->name); + QCC_PR_DummyDef(parttype, newname, scope, 1, ofs + type->size*a + parttype->ofs, false, saved); + + sprintf(newname, "%s%s.%s_x", name, array, parttype->name); + QCC_PR_DummyDef(type_float, newname, scope, 1, ofs + type->size*a + parttype->ofs, false, false); + sprintf(newname, "%s%s.%s_y", name, array, parttype->name); + QCC_PR_DummyDef(type_float, newname, scope, 1, ofs + type->size*a + parttype->ofs+1, false, false); + sprintf(newname, "%s%s.%s_z", name, array, parttype->name); + QCC_PR_DummyDef(type_float, newname, scope, 1, ofs + type->size*a + parttype->ofs+2, false, false); + break; + + case ev_float: + case ev_string: + case ev_entity: + case ev_field: + case ev_pointer: + case ev_integer: + case ev_struct: + case ev_union: + case ev_variant: //for lack of any better alternative + sprintf(newname, "%s%s.%s", name, array, parttype->name); + QCC_PR_DummyDef(parttype, newname, scope, 1, ofs + type->size*a + parttype->ofs, false, saved); + break; + + case ev_function: + sprintf(newname, "%s%s.%s", name, array, parttype->name); + QCC_PR_DummyDef(parttype, newname, scope, 1, ofs + type->size*a +parttype->ofs, false, saved)->initialized = true; + break; + case ev_void: + break; + } + parttype=parttype->next; + } + } + else if (type->type == ev_vector) + { //do the vector thing. + sprintf(newname, "%s%s_x", name, array); + QCC_PR_DummyDef(type_float, newname, scope, 1, ofs + type->size*a+0, referable, false); + sprintf(newname, "%s%s_y", name, array); + QCC_PR_DummyDef(type_float, newname, scope, 1, ofs + type->size*a+1, referable, false); + sprintf(newname, "%s%s_z", name, array); + QCC_PR_DummyDef(type_float, newname, scope, 1, ofs + type->size*a+2, referable, false); + } + else if (type->type == ev_field) + { + if (type->aux_type->type == ev_vector) + { + //do the vector thing. + sprintf(newname, "%s%s_x", name, array); + QCC_PR_DummyDef(type_floatfield, newname, scope, 1, ofs + type->size*a+0, referable, false); + sprintf(newname, "%s%s_y", name, array); + QCC_PR_DummyDef(type_floatfield, newname, scope, 1, ofs + type->size*a+1, referable, false); + sprintf(newname, "%s%s_z", name, array); + QCC_PR_DummyDef(type_floatfield, newname, scope, 1, ofs + type->size*a+2, referable, false); + } + } + } + + if (referable) + { + if (!pHash_Get(&globalstable, "end_sys_fields")) + first->references++; //anything above needs to be left in, and so warning about not using it is just going to pee people off. + if (arraysize <= 1) + first->constant = false; + if (scope) + pHash_Add(&localstable, first->name, first, qccHunkAlloc(sizeof(bucket_t))); + else + pHash_Add(&globalstable, first->name, first, qccHunkAlloc(sizeof(bucket_t))); + + if (!scope && asmfile) + fprintf(asmfile, "%s %s;\n", TypeName(first->type), first->name); + } + + return first; +} + +/* +============ +PR_GetDef + +If type is NULL, it will match any type +If allocate is true, a new def will be allocated if it can't be found +============ +*/ + +QCC_def_t *QCC_PR_GetDef (QCC_type_t *type, char *name, QCC_def_t *scope, pbool allocate, int arraysize, pbool saved) +{ + int ofs; + QCC_def_t *def; +// char element[MAX_NAME]; + unsigned int i; + QCC_def_t *foundstatic = NULL; + + if (scope) + { + def = Hash_Get(&localstable, name); + + while(def) + { + if ( def->scope && def->scope != scope) + { + def = Hash_GetNext(&localstable, name, def); + continue; // in a different function + } + + if (type && typecmp(def->type, type)) + QCC_PR_ParseErrorPrintDef (ERR_TYPEMISMATCHREDEC, def, "Type mismatch on redeclaration of %s. %s, should be %s",name, TypeName(type), TypeName(def->type)); + if (def->arraysize != arraysize && arraysize) + QCC_PR_ParseErrorPrintDef (ERR_TYPEMISMATCHARRAYSIZE, def, "Array sizes for redecleration of %s do not match",name); + if (allocate && scope) + { + QCC_PR_ParseWarning (WARN_DUPLICATEDEFINITION, "%s duplicate definition ignored", name); + QCC_PR_ParsePrintDef(WARN_DUPLICATEDEFINITION, def); +// if (!scope) +// QCC_PR_ParsePrintDef(def); + } + return def; + } + } + + + def = Hash_Get(&globalstable, name); + + while(def) + { + if ( def->scope && def->scope != scope) + { + def = Hash_GetNext(&globalstable, name, def); + continue; // in a different function + } + + if (def->isstatic && def->s_file != s_file) + { //warn? or would that be pointless? + foundstatic = def; + def = Hash_GetNext(&globalstable, name, def); + continue; // in a different function + } + + if (type && typecmp(def->type, type)) + { + if (!pr_scope) + QCC_PR_ParseErrorPrintDef (ERR_TYPEMISMATCHREDEC, def, "Type mismatch on redeclaration of %s. %s, should be %s",name, TypeName(type), TypeName(def->type)); + } + if (def->arraysize != arraysize && arraysize) + QCC_PR_ParseErrorPrintDef(ERR_TYPEMISMATCHARRAYSIZE, def, "Array sizes for redecleration of %s do not match",name); + if (allocate && scope) + { + if (pr_scope) + { //warn? or would that be pointless? + def = Hash_GetNext(&globalstable, name, def); + continue; // in a different function + } + + QCC_PR_ParseWarning (WARN_DUPLICATEDEFINITION, "%s duplicate definition ignored", name); + QCC_PR_ParsePrintDef(WARN_DUPLICATEDEFINITION, def); +// if (!scope) +// QCC_PR_ParsePrintDef(def); + } + return def; + } + + if (pHash_Get != &Hash_Get && !allocate) //do we want to try case insensative too? + { + if (scope) + { + def = pHash_Get(&localstable, name); + + while(def) + { + if ( def->scope && def->scope != scope) + { + def = pHash_GetNext(&localstable, name, def); + continue; // in a different function + } + + if (type && typecmp(def->type, type)) + QCC_PR_ParseError (ERR_TYPEMISMATCHREDEC, "Type mismatch on redeclaration of %s. %s, should be %s",name, TypeName(type), TypeName(def->type)); + if (def->arraysize != arraysize && arraysize) + QCC_PR_ParseError (ERR_TYPEMISMATCHARRAYSIZE, "Array sizes for redecleration of %s do not match",name); + if (allocate && scope) + { + QCC_PR_ParseWarning (WARN_DUPLICATEDEFINITION, "%s duplicate definition ignored", name); + QCC_PR_ParsePrintDef(WARN_DUPLICATEDEFINITION, def); + // if (!scope) + // QCC_PR_ParsePrintDef(def); + } + return def; + } + } + + + def = pHash_Get(&globalstable, name); + + while(def) + { + if ( def->scope && def->scope != scope) + { + def = pHash_GetNext(&globalstable, name, def); + continue; // in a different function + } + + if (def->isstatic && def->s_file != s_file) + { //warn? or would that be pointless? + foundstatic = def; + def = Hash_GetNext(&globalstable, name, def); + continue; // in a different function + } + + if (type && typecmp(def->type, type)) + { + if (!pr_scope) + QCC_PR_ParseError (ERR_TYPEMISMATCHREDEC, "Type mismatch on redeclaration of %s. %s, should be %s",name, TypeName(type), TypeName(def->type)); + } + if (def->arraysize != arraysize && arraysize) + QCC_PR_ParseError (ERR_TYPEMISMATCHARRAYSIZE, "Array sizes for redecleration of %s do not match",name); + if (allocate && scope) + { + if (pr_scope) + { //warn? or would that be pointless? + def = pHash_GetNext(&globalstable, name, def); + continue; // in a different function + } + + QCC_PR_ParseWarning (WARN_DUPLICATEDEFINITION, "%s duplicate definition ignored", name); + QCC_PR_ParsePrintDef(WARN_DUPLICATEDEFINITION, def); + // if (!scope) + // QCC_PR_ParsePrintDef(def); + } + return def; + } + } + + if (foundstatic && !allocate) + { + QCC_PR_ParseWarning (WARN_DUPLICATEDEFINITION, "%s defined static", name); + QCC_PR_ParsePrintDef(WARN_DUPLICATEDEFINITION, foundstatic); + } + + if (!allocate) + return NULL; + + if (arraysize < 1) + { + QCC_PR_ParseError (ERR_ARRAYNEEDSSIZE, "First declaration of array %s with no size",name); + } + + if (scope) + { + if (QCC_PR_GetDef(type, name, NULL, false, arraysize, false)) + QCC_PR_ParseWarning(WARN_SAMENAMEASGLOBAL, "Local \"%s\" defined with name of a global", name); + } + + ofs = numpr_globals; + if (arraysize > 1) + { //write the array size + ofs = QCC_GetFreeOffsetSpace(1 + (type->size * arraysize)); + + ((int *)qcc_pr_globals)[ofs] = arraysize-1; //An array needs the size written first. This is a hexen2 opcode thing. + ofs++; + } + else + ofs = QCC_GetFreeOffsetSpace(type->size * arraysize); + + def = QCC_PR_DummyDef(type, name, scope, arraysize, ofs, true, saved); + + //fix up fields. + if (type->type == ev_field && allocate != 2) + { + for (i = 0; i < type->size*arraysize; i++) //make arrays of fields work. + *(int *)&qcc_pr_globals[def->ofs+i] = pr.size_fields+i; + + pr.size_fields += i; + } + + if (scope) + { + def->nextlocal = pr.localvars; + pr.localvars = def; + } + + return def; +} + +QCC_def_t *QCC_PR_DummyFieldDef(QCC_type_t *type, char *name, QCC_def_t *scope, int arraysize, unsigned int *fieldofs, pbool saved) +{ + char array[64]; + char newname[256]; + int a, parms; + QCC_def_t *def, *first=NULL; + unsigned int maxfield, startfield; + QCC_type_t *ftype; + pbool isunion; + startfield = *fieldofs; + maxfield = startfield; + + for (a = 0; a < arraysize; a++) + { + if (a == 0) + *array = '\0'; + else + sprintf(array, "[%i]", a); + + if (*name) + { + sprintf(newname, "%s%s", name, array); + + // allocate a new def + def = (void *)qccHunkAlloc (sizeof(QCC_def_t)); + memset (def, 0, sizeof(*def)); + def->next = NULL; + def->arraysize = arraysize; + + pr.def_tail->next = def; + pr.def_tail = def; + + def->s_line = pr_source_line; + def->s_file = s_file; + + def->name = (void *)qccHunkAlloc (strlen(newname)+1); + strcpy (def->name, newname); + def->type = type; + + def->scope = scope; + + def->ofs = QCC_GetFreeOffsetSpace(1); + ((int *)qcc_pr_globals)[def->ofs] = *fieldofs; + *fieldofs++; + if (!first) + first = def; + } + else + { + def=NULL; + } + +// printf("Emited %s\n", newname); + + if ((type)->type == ev_struct||(type)->type == ev_union) + { + int partnum; + QCC_type_t *parttype; + if (def) + def->references++; + parttype = (type)->param; + isunion = ((type)->type == ev_union); + for (partnum = 0, parms = (type)->num_parms; partnum < parms; partnum++) + { + switch (parttype->type) + { + case ev_union: + case ev_struct: + if (*name) + sprintf(newname, "%s%s.%s", name, array, parttype->name); + else + sprintf(newname, "%s%s", parttype->name, array); + def = QCC_PR_DummyFieldDef(parttype, newname, scope, 1, fieldofs, saved); + break; + case ev_float: + case ev_string: + case ev_vector: + case ev_entity: + case ev_field: + case ev_pointer: + case ev_integer: + case ev_variant: + if (*name) + sprintf(newname, "%s%s.%s", name, array, parttype->name); + else + sprintf(newname, "%s%s", parttype->name, array); + ftype = QCC_PR_NewType("FIELD TYPE", ev_field); + ftype->aux_type = parttype; + if (parttype->type == ev_vector) + ftype->size = parttype->size; //vector fields create a _y and _z too, so we need this still. + def = QCC_PR_GetDef(NULL, newname, scope, false, 1, saved); + if (!def) + { + def = QCC_PR_GetDef(ftype, newname, scope, true, 1, saved); + } + else + { + QCC_PR_ParseWarning(WARN_CONFLICTINGUNIONMEMBER, "conflicting offsets for union/struct expansion of %s. Ignoring new def.", newname); + QCC_PR_ParsePrintDef(WARN_CONFLICTINGUNIONMEMBER, def); + } + break; + + case ev_function: + if (*name) + sprintf(newname, "%s%s.%s", name, array, parttype->name); + else + sprintf(newname, "%s%s", parttype->name, array); + ftype = QCC_PR_NewType("FIELD TYPE", ev_field); + ftype->aux_type = parttype; + def = QCC_PR_GetDef(ftype, newname, scope, true, 1, saved); + def->initialized = true; + ((int *)qcc_pr_globals)[def->ofs] = *fieldofs; + *fieldofs += parttype->size; + break; + case ev_void: + break; + } + if (*fieldofs > maxfield) + maxfield = *fieldofs; + if (isunion) + *fieldofs = startfield; + + type = parttype; + parttype=parttype->next; + } + } + } + + *fieldofs = maxfield; //final size of the union. + return first; +} + + + +void QCC_PR_ExpandUnionToFields(QCC_type_t *type, int *fields) +{ + QCC_type_t *pass = type->aux_type; + QCC_PR_DummyFieldDef(pass, "", pr_scope, 1, fields, true); +} + +int accglobalsblock; //0 = error, 1 = var, 2 = function, 3 = objdata +/* +================ +PR_ParseDefs + +Called at the outer layer and when a local statement is hit +================ +*/ +void QCC_PR_ParseDefs (char *classname) +{ + char *name; + QCC_type_t *type, *parm; + QCC_def_t *def, *d; + QCC_function_t *f; + QCC_dfunction_t *df; + int i; + extern pbool defaultstatic; + pbool shared=false; + pbool isstatic=defaultstatic; + pbool externfnc=false; + pbool isconstant = false; + pbool isvar = false; + pbool noref = false; + pbool nosave = false; + pbool allocatenew = true; + pbool inlinefunction = false; + int ispointer; + gofs_t oldglobals; + int arraysize; + + if (QCC_PR_CheckKeyword(keyword_enum, "enum")) + { + if (QCC_PR_CheckKeyword(keyword_integer, "integer") || QCC_PR_CheckKeyword(keyword_int, "int")) + { + int iv = 0; + QCC_PR_Expect("{"); + i = 0; + d = NULL; + while(1) + { + name = QCC_PR_ParseName(); + if (QCC_PR_CheckToken("=")) + { + if (pr_token_type != tt_immediate && pr_immediate_type->type != ev_integer) + { + def = QCC_PR_GetDef(NULL, QCC_PR_ParseName(), NULL, false, 0, false); + if (def) + { + if (!def->constant) + QCC_PR_ParseError(ERR_NOTANUMBER, "enum - %s is not a constant", def->name); + else + iv = G_INT(def->ofs); + } + else + QCC_PR_ParseError(ERR_NOTANUMBER, "enum - not a number"); + } + else + { + iv = pr_immediate._int; + QCC_PR_Lex(); + } + } + def = QCC_MakeIntDef(iv); + pHash_Add(&globalstable, name, def, qccHunkAlloc(sizeof(bucket_t))); + iv++; + + if (QCC_PR_CheckToken("}")) + break; + QCC_PR_Expect(","); + } + } + else + { + float fv = 0; + QCC_PR_CheckKeyword(keyword_float, "float"); + QCC_PR_Expect("{"); + i = 0; + d = NULL; + while(1) + { + name = QCC_PR_ParseName(); + if (QCC_PR_CheckToken("=")) + { + if (pr_token_type != tt_immediate && pr_immediate_type->type != ev_float) + { + def = QCC_PR_GetDef(NULL, QCC_PR_ParseName(), NULL, false, 0, false); + if (def) + { + if (!def->constant) + QCC_PR_ParseError(ERR_NOTANUMBER, "enum - %s is not a constant", def->name); + else + fv = G_FLOAT(def->ofs); + } + else + QCC_PR_ParseError(ERR_NOTANUMBER, "enum - not a number"); + } + else + { + fv = pr_immediate._float; + QCC_PR_Lex(); + } + } + def = QCC_MakeFloatDef(fv); + pHash_Add(&globalstable, name, def, qccHunkAlloc(sizeof(bucket_t))); + fv++; + + if (QCC_PR_CheckToken("}")) + break; + QCC_PR_Expect(","); + } + } + QCC_PR_Expect(";"); + return; + } + + if (QCC_PR_CheckKeyword(keyword_enumflags, "enumflags")) + { + int bits; + + if (QCC_PR_CheckKeyword(keyword_integer, "integer") || QCC_PR_CheckKeyword(keyword_int, "int")) + { + int iv = 1; + QCC_PR_Expect("{"); + i = 0; + d = NULL; + while(1) + { + name = QCC_PR_ParseName(); + if (QCC_PR_CheckToken("=")) + { + if (pr_token_type != tt_immediate && pr_immediate_type->type != ev_integer) + { + def = QCC_PR_GetDef(NULL, QCC_PR_ParseName(), NULL, false, 0, false); + if (def) + { + if (!def->constant) + QCC_PR_ParseError(ERR_NOTANUMBER, "enumflags - %s is not a constant", def->name); + else + iv = G_INT(def->ofs); + } + else + QCC_PR_ParseError(ERR_NOTANUMBER, "enumflags - not a number"); + } + else + { + iv = pr_immediate._int; + QCC_PR_Lex(); + } + } + + bits = 0; + i = (int)iv; + if (i != iv) + QCC_PR_ParseWarning(WARN_ENUMFLAGS_NOTINTEGER, "enumflags - %f not an integer", iv); + else + { + while(i) + { + if (((i>>1)<<1) != i) + bits++; + i>>=1; + } + if (bits != 1) + QCC_PR_ParseWarning(WARN_ENUMFLAGS_NOTBINARY, "enumflags - value %i not a single bit", (int)iv); + } + + def = QCC_MakeIntDef(iv); + pHash_Add(&globalstable, name, def, qccHunkAlloc(sizeof(bucket_t))); + + iv*=2; + + if (QCC_PR_CheckToken("}")) + break; + QCC_PR_Expect(","); + } + } + else + { + float fv = 1; + QCC_PR_CheckKeyword(keyword_float, "float"); + + QCC_PR_Expect("{"); + i = 0; + d = NULL; + while(1) + { + name = QCC_PR_ParseName(); + if (QCC_PR_CheckToken("=")) + { + if (pr_token_type != tt_immediate && pr_immediate_type->type != ev_float) + { + def = QCC_PR_GetDef(NULL, QCC_PR_ParseName(), NULL, false, 0, false); + if (def) + { + if (!def->constant) + QCC_PR_ParseError(ERR_NOTANUMBER, "enumflags - %s is not a constant", def->name); + else + fv = G_FLOAT(def->ofs); + } + else + QCC_PR_ParseError(ERR_NOTANUMBER, "enumflags - not a number"); + } + else + { + fv = pr_immediate._float; + QCC_PR_Lex(); + } + } + + bits = 0; + i = (int)fv; + if (i != fv) + QCC_PR_ParseWarning(WARN_ENUMFLAGS_NOTINTEGER, "enumflags - %f not an integer", fv); + else + { + while(i) + { + if (((i>>1)<<1) != i) + bits++; + i>>=1; + } + if (bits != 1) + QCC_PR_ParseWarning(WARN_ENUMFLAGS_NOTBINARY, "enumflags - value %i not a single bit", (int)fv); + } + + def = QCC_MakeFloatDef(fv); + pHash_Add(&globalstable, name, def, qccHunkAlloc(sizeof(bucket_t))); + + fv*=2; + + if (QCC_PR_CheckToken("}")) + break; + QCC_PR_Expect(","); + } + } + QCC_PR_Expect(";"); + return; + } + + if (QCC_PR_CheckKeyword (keyword_typedef, "typedef")) + { + type = QCC_PR_ParseType(true); + if (!type) + { + QCC_PR_ParseError(ERR_NOTANAME, "typedef found unexpected tokens"); + } + type->name = QCC_CopyString(pr_token)+strings; + QCC_PR_Lex(); + QCC_PR_Expect(";"); + return; + } + + if (flag_acc) + { + char *oldp; + if (QCC_PR_CheckKeyword (keyword_codesys, "CodeSys")) //reacc support. + { + if (ForcedCRC) + QCC_PR_ParseError(ERR_BADEXTENSION, "progs crc was already specified - only one is allowed"); + ForcedCRC = (int)pr_immediate._float; + QCC_PR_Lex(); + QCC_PR_Expect(";"); + return; + } + + oldp = pr_file_p; + if (QCC_PR_CheckKeyword (keyword_var, "var")) //reacc support. + { + if (accglobalsblock == 3) + { + if (!QCC_PR_GetDef(type_void, "end_sys_fields", NULL, false, 0, false)) + QCC_PR_GetDef(type_void, "end_sys_fields", NULL, true, 1, false); + } + + QCC_PR_ParseName(); + if (QCC_PR_CheckToken(":")) + accglobalsblock = 1; + pr_file_p = oldp; + QCC_PR_Lex(); + } + + if (QCC_PR_CheckKeyword (keyword_function, "function")) //reacc support. + { + accglobalsblock = 2; + } + if (QCC_PR_CheckKeyword (keyword_objdata, "objdata")) //reacc support. + { + if (accglobalsblock == 3) + { + if (!QCC_PR_GetDef(type_void, "end_sys_fields", NULL, false, 0, false)) + QCC_PR_GetDef(type_void, "end_sys_fields", NULL, true, 1, false); + } + else + if (!QCC_PR_GetDef(type_void, "end_sys_globals", NULL, false, 0, false)) + QCC_PR_GetDef(type_void, "end_sys_globals", NULL, true, 1, false); + accglobalsblock = 3; + } + } + + if (!pr_scope) + switch(accglobalsblock)//reacc support. + { + case 1: + { + char *oldp = pr_file_p; + name = QCC_PR_ParseName(); + if (!QCC_PR_CheckToken(":")) //nope, it wasn't! + { + QCC_PR_IncludeChunk(name, true, NULL); + QCC_PR_Lex(); + QCC_PR_UnInclude(); + pr_file_p = oldp; + break; + } + if (QCC_PR_CheckKeyword(keyword_object, "object")) + QCC_PR_GetDef(type_entity, name, NULL, true, 1, true); + else if (QCC_PR_CheckKeyword(keyword_string, "string")) + QCC_PR_GetDef(type_string, name, NULL, true, 1, true); + else if (QCC_PR_CheckKeyword(keyword_real, "real")) + { + def = QCC_PR_GetDef(type_float, name, NULL, true, 1, true); + if (QCC_PR_CheckToken("=")) + { + G_FLOAT(def->ofs) = pr_immediate._float; + QCC_PR_Lex(); + } + } + else if (QCC_PR_CheckKeyword(keyword_vector, "vector")) + { + def = QCC_PR_GetDef(type_vector, name, NULL, true, 1, true); + if (QCC_PR_CheckToken("=")) + { + QCC_PR_Expect("["); + G_FLOAT(def->ofs+0) = pr_immediate._float; + QCC_PR_Lex(); + G_FLOAT(def->ofs+1) = pr_immediate._float; + QCC_PR_Lex(); + G_FLOAT(def->ofs+2) = pr_immediate._float; + QCC_PR_Lex(); + QCC_PR_Expect("]"); + } + } + else if (QCC_PR_CheckKeyword(keyword_pfunc, "pfunc")) + QCC_PR_GetDef(type_function, name, NULL, true, 1, true); + else + QCC_PR_ParseError(ERR_BADNOTTYPE, "Bad type\n"); + QCC_PR_Expect (";"); + + if (QCC_PR_CheckKeyword (keyword_system, "system")) + QCC_PR_Expect (";"); + return; + } + case 2: + name = QCC_PR_ParseName(); + QCC_PR_GetDef(type_function, name, NULL, true, 1, true); + QCC_PR_CheckToken (";"); + return; + case 3: + { + char *oldp = pr_file_p; + name = QCC_PR_ParseName(); + if (!QCC_PR_CheckToken(":")) //nope, it wasn't! + { + QCC_PR_IncludeChunk(name, true, NULL); + QCC_PR_Lex(); + QCC_PR_UnInclude(); + pr_file_p = oldp; + break; + } + if (QCC_PR_CheckKeyword(keyword_object, "object")) + QCC_PR_GetDef(QCC_PR_FieldType(type_entity), name, NULL, true, 1, true); + else if (QCC_PR_CheckKeyword(keyword_string, "string")) + QCC_PR_GetDef(QCC_PR_FieldType(type_string), name, NULL, true, 1, true); + else if (QCC_PR_CheckKeyword(keyword_real, "real")) + QCC_PR_GetDef(QCC_PR_FieldType(type_float), name, NULL, true, 1, true); + else if (QCC_PR_CheckKeyword(keyword_vector, "vector")) + QCC_PR_GetDef(QCC_PR_FieldType(type_vector), name, NULL, true, 1, true); + else if (QCC_PR_CheckKeyword(keyword_pfunc, "pfunc")) + QCC_PR_GetDef(QCC_PR_FieldType(type_function), name, NULL, true, 1, true); + else + QCC_PR_ParseError(ERR_BADNOTTYPE, "Bad type\n"); + QCC_PR_Expect (";"); + return; + } + } + + while(1) + { + if (QCC_PR_CheckKeyword(keyword_extern, "extern")) + externfnc=true; + else if (QCC_PR_CheckKeyword(keyword_shared, "shared")) + { + shared=true; + if (pr_scope) + QCC_PR_ParseError (ERR_NOSHAREDLOCALS, "Cannot have shared locals"); + } + else if (QCC_PR_CheckKeyword(keyword_const, "const")) + isconstant = true; + else if (QCC_PR_CheckKeyword(keyword_var, "var")) + isvar = true; + else if (!pr_scope && QCC_PR_CheckKeyword(keyword_var, "static")) + isstatic = true; + else if (!pr_scope && QCC_PR_CheckKeyword(keyword_var, "nonstatic")) + isstatic = false; + else if (QCC_PR_CheckKeyword(keyword_noref, "noref")) + noref=true; + else if (QCC_PR_CheckKeyword(keyword_nosave, "nosave")) + nosave = true; + else + break; + } + + type = QCC_PR_ParseType (false); + if (type == NULL) //ignore + return; + + inlinefunction = type_inlinefunction; + + if (externfnc && type->type != ev_function) + { + printf ("Only functions may be defined as external (yet)\n"); + externfnc=false; + } + + if (!pr_scope && QCC_PR_CheckKeyword(keyword_function, "function")) //reacc support. + { + name = QCC_PR_ParseName (); + QCC_PR_Expect("("); + type = QCC_PR_ParseFunctionTypeReacc(false, type); + QCC_PR_Expect(";"); + if (!stricmp(name, "null")) + printf("null!\n"); + def = QCC_PR_GetDef (type, name, NULL, true, 1, false); + + if (autoprototype) + { //ignore the code and stuff + + if (QCC_PR_CheckKeyword(keyword_external, "external")) + { //builtin + QCC_PR_Lex(); + QCC_PR_Expect(";"); + } + else + { + int blev = 1; + + while (!QCC_PR_CheckToken("{")) //skip over the locals. + { + if (pr_token_type == tt_eof) + { + QCC_PR_ParseError(0, "Unexpected EOF"); + break; + } + QCC_PR_Lex(); + } + + //balance out the { and } + while(blev) + { + if (pr_token_type == tt_eof) + break; + if (QCC_PR_CheckToken("{")) + blev++; + else if (QCC_PR_CheckToken("}")) + blev--; + else + QCC_PR_Lex(); //ignore it. + } + } + return; + } + + def->references++; + + pr_scope = def; + f = QCC_PR_ParseImmediateStatements (type); + pr_scope = NULL; + def->initialized = 1; + def->isstatic = isstatic; + G_FUNCTION(def->ofs) = numfunctions; + f->def = def; +// if (pr_dumpasm) +// PR_PrintFunction (def); + +// fill in the dfunction + df = &functions[numfunctions]; + numfunctions++; + if (f->builtin) + df->first_statement = -f->builtin; + else + df->first_statement = f->code; + + if (f->builtin && opt_function_names) + optres_function_names += strlen(f->def->name); + else + df->s_name = QCC_CopyString (f->def->name); + df->s_file = s_file2; + df->numparms = f->def->type->num_parms; + df->locals = locals_end - locals_start; + df->parm_start = locals_start; + for (i=0,parm = type->param ; inumparms ; i++, parm = parm->next) + { + df->parm_size[i] = parm->size; + } + + return; + } + +// if (pr_scope && (type->type == ev_field) ) +// QCC_PR_ParseError ("Fields must be global"); + + do + { + if (QCC_PR_CheckToken ("*")) + { + ispointer = 1; + while(QCC_PR_CheckToken ("*")) + ispointer++; + name = QCC_PR_ParseName (); + } + else if (QCC_PR_CheckToken (";")) + { + if (type->type == ev_field && (type->aux_type->type == ev_union || type->aux_type->type == ev_struct)) + { + QCC_PR_ExpandUnionToFields(type, &pr.size_fields); + return; + } +// if (type->type == ev_union) +// { +// return; +// } + QCC_PR_ParseError (ERR_TYPEWITHNONAME, "type with no name"); + name = NULL; + ispointer = false; + } + else + { + name = QCC_PR_ParseName (); + ispointer = false; + } + + if (QCC_PR_CheckToken("::") && !classname) + { + classname = name; + name = QCC_PR_ParseName(); + } + +//check for an array + + if ( QCC_PR_CheckToken ("[") ) + { + char *oldprfile = pr_file_p; + arraysize = 0; + if (QCC_PR_CheckToken("]")) + { + QCC_PR_Expect("="); + QCC_PR_Expect("{"); + QCC_PR_Lex(); + arraysize++; + while(1) + { + if(pr_token_type == tt_eof) + break; + if (QCC_PR_CheckToken(",")) + arraysize++; + if (QCC_PR_CheckToken("}")) + break; + QCC_PR_Lex(); + } + pr_file_p = oldprfile; + QCC_PR_Lex(); + } + else + { + def = QCC_PR_Expression(TOP_PRIORITY, 0); + if (!def->constant) + QCC_PR_ParseError(ERR_BADARRAYSIZE, "Array size is not a constant value"); + else if (def->type->type == ev_integer) + arraysize = G_INT(def->ofs); + else if (def->type->type == ev_float) + { + arraysize = (int)G_FLOAT(def->ofs); + if ((float)arraysize != G_FLOAT(def->ofs)) + QCC_PR_ParseError(ERR_BADARRAYSIZE, "Array size is not a constant value"); + } + else + QCC_PR_ParseError(ERR_BADARRAYSIZE, "Array size must be of int value"); +/* if(pr_token_type == tt_name) + { + def = QCC_PR_GetDef(NULL, QCC_PR_ParseName(), pr_scope, false, 0); + if (def && def->arraysize==1) + { + if (def->type->type == ev_integer) + arraysize = G_INT(def->ofs); + else if (def->type->type == ev_float && (float)(int)G_FLOAT(def->ofs) == G_FLOAT(def->ofs)) + arraysize = (int)G_FLOAT(def->ofs); + } + } + else if (pr_token_type == tt_immediate) + { + arraysize = atoi (pr_token); + QCC_PR_Lex(); + } +*/ QCC_PR_Expect("]"); + } + + if (arraysize < 1) + { + QCC_PR_ParseError (ERR_BADARRAYSIZE, "Definition of array (%s) size is not of a numerical value", name); + arraysize=0; //grrr... + } + } + else + arraysize = 1; + + if (QCC_PR_CheckToken("(")) + { + if (inlinefunction) + QCC_PR_ParseWarning(WARN_UNSAFEFUNCTIONRETURNTYPE, "Function returning function. Is this what you meant? (suggestion: use typedefs)"); + inlinefunction = false; + type = QCC_PR_ParseFunctionType(false, type); + } + + if (classname) + { + char *membername = name; + name = qccHunkAlloc(strlen(classname) + strlen(name) + 3); + sprintf(name, "%s::"MEMBERFIELDNAME, classname, membername); + if (!QCC_PR_GetDef(NULL, name, NULL, false, 0, false)) + QCC_PR_ParseError(ERR_NOTANAME, "%s %s is not a member of class %s\n", TypeName(type), membername, classname); + sprintf(name, "%s::%s", classname, membername); + + pr_classtype = QCC_TypeForName(classname); + if (!pr_classtype || !pr_classtype->parentclass) + QCC_PR_ParseError(ERR_NOTANAME, "%s is not a class\n", classname); + } + else + pr_classtype = NULL; + + oldglobals = numpr_globals; + + if (ispointer) + { + parm = type; + while(ispointer) + { + ispointer--; + parm = QCC_PointerTypeTo(parm); + } + + def = QCC_PR_GetDef (parm, name, pr_scope, allocatenew, arraysize, !nosave); + } + else + def = QCC_PR_GetDef (type, name, pr_scope, allocatenew, arraysize, !nosave); + + if (!def) + QCC_PR_ParseError(ERR_NOTANAME, "%s is not part of class %s", name, classname); + + if (noref) + def->references++; + + if (!def->initialized && shared) //shared count as initiialised + { + def->shared = shared; + def->initialized = true; + } + if (externfnc) + def->initialized = 2; + + if (isstatic) + { + if (def->s_file == s_file) + def->isstatic = isstatic; + else //if (type->type != ev_function && defaultstatic) //functions don't quite consitiute a definition + QCC_PR_ParseErrorPrintDef (ERR_REDECLARATION, def, "can't redefine non-static as static"); + } + +// check for an initialization + if (type->type == ev_function && (pr_scope)) + { + if ( QCC_PR_CheckToken ("=") ) + { + QCC_PR_ParseError (ERR_INITIALISEDLOCALFUNCTION, "local functions may not be initialised"); + } + + arraysize = def->arraysize; + d = def; //apply to ALL elements + while(arraysize--) + { + d->initialized = 1; //fake function + G_FUNCTION(d->ofs) = 0; + d = d->next; + } + + continue; + } + + if (type->type == ev_field && QCC_PR_CheckName ("alias")) + { + QCC_PR_ParseError(ERR_INTERNAL, "FTEQCC does not support this variant of decompiled hexenc\nPlease obtain the original version released by Raven Software instead."); + name = QCC_PR_ParseName(); + } + else if ( QCC_PR_CheckToken ("=") || ((type->type == ev_function) && (pr_token[0] == '{' || pr_token[0] == '[' || pr_token[0] == ':'))) //this is an initialisation (or a function) + { + if (def->shared) + QCC_PR_ParseError (ERR_SHAREDINITIALISED, "shared values may not be assigned an initial value", name); + if (def->initialized == 1) + { + if (def->type->type == ev_function) + { + i = G_FUNCTION(def->ofs); + df = &functions[i]; + QCC_PR_ParseErrorPrintDef (ERR_REDECLARATION, def, "%s redeclared, prev instance is in %s", name, strings+df->s_file); + } + else + QCC_PR_ParseErrorPrintDef(ERR_REDECLARATION, def, "%s redeclared", name); + } + + if (autoprototype) + { //ignore the code and stuff + if (QCC_PR_CheckToken("[")) + { + while (!QCC_PR_CheckToken("]")) + { + if (pr_token_type == tt_eof) + break; + QCC_PR_Lex(); + } + } + if (QCC_PR_CheckToken("{")) + { + int blev = 1; + //balance out the { and } + while(blev) + { + if (pr_token_type == tt_eof) + break; + if (QCC_PR_CheckToken("{")) + blev++; + else if (QCC_PR_CheckToken("}")) + blev--; + else + QCC_PR_Lex(); //ignore it. + } + } + else + { + QCC_PR_CheckToken("#"); + QCC_PR_Lex(); + } + continue; + } + +#pragma message("this is experimental") + if (pr_scope) + { + d = QCC_PR_Expression(TOP_PRIORITY, EXPR_DISALLOW_COMMA); + if (d->constant) + { + for (i = 0; i < d->type->size; i++) + G_INT(def->ofs+i) = G_INT(d->ofs+i); + def->constant = !isvar; + def->initialized = 1; + continue; + } + else if (def->type->size >= 3) + { + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_V], d, def, NULL)); + def->constant = false; + def->initialized = false; + continue; + } + else + { + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_F], d, def, NULL)); + def->constant = false; + def->initialized = false; + continue; + } + } + else + if (pr_token_type == tt_name && strcmp(pr_token, "_")) + { + unsigned int i; + + if (def->arraysize>1) + QCC_PR_ParseError(ERR_ARRAYNEEDSBRACES, "Array initialisation requires curly braces"); + + d = QCC_PR_GetDef(NULL, pr_token, pr_scope, false, 0, false); + if (!d) + QCC_PR_ParseError(ERR_NOTDEFINED, "%s was not defined\n", pr_token); + if (typecmp(def->type, d->type)) + QCC_PR_ParseError (ERR_BADIMMEDIATETYPE, "wrong immediate type for %s", name); + + + for (i = 0; i < d->type->size; i++) + G_INT(def->ofs) = G_INT(d->ofs); + QCC_PR_Lex(); + + if (type->type == ev_function) + { + def->initialized = 1; + def->constant = !isvar; + } + continue; + } + + else if (type->type == ev_function) + { + if (isvar) + def->constant = false; + else + def->constant = true; + if (QCC_PR_CheckImmediate("0")) + { + def->constant = 0; + def->initialized = 1; //fake function + G_FUNCTION(def->ofs) = 0; + continue; + } + + if (!def->constant && arraysize==1) + { + def->constant = 0; + def->initialized = 1; //fake function + + name = QCC_PR_ParseName (); + d = QCC_PR_GetDef (NULL, name, pr_scope, false, 0, false); + if (!d) + QCC_PR_ParseError(ERR_NOTDEFINED, "%s was not previously defined", name); + G_FUNCTION(def->ofs+i) = G_FUNCTION(d->ofs); + continue; + } + + if (arraysize>1) + { + int i; + def->initialized = 1; //fake function + QCC_PR_Expect ("{"); + i = 0; + do + { + if (pr_token_type == tt_immediate && ( + (pr_immediate_type == type_integer && pr_immediate._int == 0) || + (pr_immediate_type == type_float && pr_immediate._float == 0))) + { + QCC_PR_Lex(); + G_FUNCTION(def->ofs+i) = 0; + } + else + { + name = QCC_PR_ParseName (); + + d = QCC_PR_GetDef (NULL, name, pr_scope, false, 0, false); + if (!d) + QCC_PR_ParseError(ERR_NOTDEFINED, "%s was not defined", name); + else + { + if (!d->initialized) + QCC_PR_ParseWarning(WARN_NOTDEFINED, "initialisation of function arrays must be placed after the body of all functions used (%s)", name); + G_FUNCTION(def->ofs+i) = G_FUNCTION(d->ofs); + } + } + + i++; + } while(QCC_PR_CheckToken(",")); + + arraysize = def->arraysize; + d = def; //apply to ALL elements + while(arraysize--) + { + d->initialized = 1; //fake function + d = d->next; + } + + QCC_PR_Expect("}"); + if (i > def->arraysize) + QCC_PR_ParseError(ERR_TOOMANYINITIALISERS, "Too many initializers"); + continue; + } + if (!def->constant) + QCC_PR_ParseError(0, "Initialised functions must be constant"); + + def->references++; + pr_scope = def; + f = QCC_PR_ParseImmediateStatements (type); + pr_scope = NULL; + def->initialized = 1; + G_FUNCTION(def->ofs) = numfunctions; + f->def = def; +// if (pr_dumpasm) +// PR_PrintFunction (def); + + // fill in the dfunction + df = &functions[numfunctions]; + numfunctions++; + if (f->builtin) + df->first_statement = -f->builtin; + else + df->first_statement = f->code; + + if (f->builtin && opt_function_names) + optres_function_names += strlen(f->def->name); + else + df->s_name = QCC_CopyString (f->def->name); + df->s_file = s_file2; + df->numparms = f->def->type->num_parms; + df->locals = locals_end - locals_start; + df->parm_start = locals_start; + for (i=0,parm = type->param ; inumparms ; i++, parm = parm->next) + { + df->parm_size[i] = parm->size; + } + + continue; + } + + else if (type->type == ev_struct) + { + int arraypart, partnum; + QCC_type_t *parttype; + def->initialized = 1; + if (isvar) + def->constant = true; + else + def->constant = false; +// if (constant) +// QCC_PR_ParseError("const used on a struct isn't useful"); + + //FIXME: should do this recursivly + QCC_PR_Expect("{"); + for (arraypart = 0; arraypart < arraysize; arraypart++) + { + parttype = type->param; + QCC_PR_Expect("{"); + for (partnum = 0; partnum < type->num_parms; partnum++) + { + switch (parttype->type) + { + case ev_float: + case ev_integer: + case ev_vector: + if (pr_token_type == tt_punct) + { + if (QCC_PR_CheckToken("{")) + { + QCC_PR_Expect("}"); + } + else + QCC_PR_ParseError(ERR_UNEXPECTEDPUNCTUATION, "Unexpected punctuation"); + + } + else if (pr_token_type == tt_immediate) + { + if (pr_immediate_type->type == ev_float && parttype->type == ev_integer) + G_INT(def->ofs + arraypart*type->size + parttype->ofs) = (int)pr_immediate._float; + else if (pr_immediate_type->type != parttype->type) + QCC_PR_ParseError (ERR_BADIMMEDIATETYPE, "wrong immediate subtype for %s.%s", def->name, parttype->name); + else + memcpy (qcc_pr_globals + def->ofs + arraypart*type->size + parttype->ofs, &pr_immediate, 4*type_size[pr_immediate_type->type]); + } + else if (pr_token_type == tt_name) + { + d = QCC_PR_GetDef(NULL, pr_token, pr_scope, false, 0, false); + if (!d) + QCC_PR_ParseError(ERR_NOTDEFINED, "%s was not defined\n", pr_token); + else if (d->type->type != parttype->type) + QCC_PR_ParseError (ERR_WRONGSUBTYPE, "wrong subtype for %s.%s", def->name, parttype->name); + else if (!d->constant) + QCC_PR_ParseError(ERR_NOTACONSTANT, "%s isn't a constant\n", pr_token); + + memcpy (qcc_pr_globals + def->ofs + arraypart*type->size + parttype->ofs, qcc_pr_globals + d->ofs, 4*d->type->size); + } + else + QCC_PR_ParseError (ERR_BADIMMEDIATETYPE, "wrong immediate subtype for %s.%s", def->name, parttype->name); + QCC_PR_Lex (); + + break; + case ev_string: + if (pr_token_type == tt_punct) + { + if (QCC_PR_CheckToken("{")) + { + unsigned int i; + for (i = 0; i < parttype->size; i++) + { +/* //the executor defines strings as true c strings, but reads in index from string table. + //structures can hide these strings. + d = (void *)qccHunkAlloc (sizeof(QCC_def_t)); + d->next = NULL; + pr.def_tail->next = d; + pr.def_tail = d; + + d->type = parttype; + d->name = "STRUCTIMMEDIATE"; + d->constant = constant; + d->initialized = 1; + d->scope = NULL; + + d->ofs = def->ofs+arraypart*type->size+parttype->ofs+i; +*/ + G_INT(def->ofs+arraypart*type->size+parttype->ofs+i) = QCC_CopyString(pr_immediate_string); + QCC_PR_Lex (); + + if (!QCC_PR_CheckToken(",")) + { + i++; + break; + } + } + for (; i < parttype->size; i++) + { +/* //the executor defines strings as true c strings, but reads in index from string table. + //structures can hide these strings. + d = (void *)qccHunkAlloc (sizeof(QCC_def_t)); + d->next = NULL; + pr.def_tail->next = d; + pr.def_tail = d; + + d->type = parttype; + d->name = "STRUCTIMMEDIATE"; + d->constant = constant; + d->initialized = 1; + d->scope = NULL; + + d->ofs = def->ofs+arraypart*type->size+parttype->ofs+i; +*/ + G_INT(def->ofs+arraypart*type->size+parttype->ofs+i) = 0; + } + QCC_PR_Expect("}"); + } + else + QCC_PR_ParseError(ERR_UNEXPECTEDPUNCTUATION, "Unexpected punctuation"); + } + else + { +/* //the executor defines strings as true c strings, but reads in index from string table. + //structures can hide these strings. + d = (void *)qccHunkAlloc (sizeof(QCC_def_t)); + d->next = NULL; + pr.def_tail->next = d; + pr.def_tail = d; + + d->type = parttype; + d->name = "STRUCTIMMEDIATE"; + d->constant = constant; + d->initialized = 1; + d->scope = NULL; + + d->ofs = def->ofs+arraypart*type->size+parttype->ofs; +*/ + G_INT(def->ofs+arraypart*type->size+parttype->ofs) = QCC_CopyString(pr_immediate_string); + QCC_PR_Lex (); + } + break; + case ev_function: + if (pr_token_type == tt_immediate) + { + if (pr_immediate._int != 0) + QCC_PR_ParseError(ERR_NOTFUNCTIONTYPE, "Expected function name or NULL"); + G_FUNCTION(def->ofs+arraypart*type->size+parttype->ofs) = 0; + QCC_PR_Lex(); + } + else + { + name = QCC_PR_ParseName (); + + d = QCC_PR_GetDef (NULL, name, pr_scope, false, 0, false); + if (!d) + QCC_PR_ParseError(ERR_NOTDEFINED, "%s was not defined\n", name); + else + G_FUNCTION(def->ofs+arraypart*type->size+parttype->ofs) = G_FUNCTION(d->ofs); + } + break; + default: + QCC_PR_ParseError(ERR_TYPEINVALIDINSTRUCT, "type %i not valid in a struct", parttype->type); + QCC_PR_Lex(); + break; + } + if (!QCC_PR_CheckToken(",")) + break; + + parttype=parttype->next; + } + QCC_PR_Expect("}"); + if (!QCC_PR_CheckToken(",")) + break; + } + QCC_PR_Expect("}"); + continue; + } + + else if (type->type == ev_integer) //handle these differently, because they may need conversions + { + if (isvar) + def->constant = false; + else + def->constant = true; + + def->initialized = 1; + memcpy (qcc_pr_globals + def->ofs, &pr_immediate, 4*type_size[pr_immediate_type->type]); + QCC_PR_Lex (); + + if (pr_immediate_type->type == ev_float) + G_INT(def->ofs) = (int)pr_immediate._float; + else if (pr_immediate_type->type != ev_integer) + QCC_PR_ParseError (ERR_BADIMMEDIATETYPE, "wrong immediate type for %s", name); + + continue; + } + else if (type->type == ev_string) + { + int dotranslate = 0; + char buf[64]; + if (!strcmp(pr_token, "_")) + { + dotranslate = 1; + QCC_PR_Lex(); + } + if(dotranslate) + QCC_PR_Expect("("); + + if (arraysize>=1 && QCC_PR_CheckToken("{")) + { + int i; + for (i = 0; i < arraysize; i++) + { + int dotranslate2; + + //the executor defines strings as true c strings, but reads in index from string table. + //structures can hide these strings. + if (i != 0) //not for the first entry - already a string def for that + { + d = (void *)qccHunkAlloc (sizeof(QCC_def_t)); + d->next = NULL; + pr.def_tail->next = d; + pr.def_tail = d; + + d->name = "IMMEDIATE"; + d->type = type_string; + if (isvar) + d->constant = false; + else + d->constant = true; + d->initialized = 1; + d->scope = NULL; + + d->ofs = def->ofs+i; + if (d->ofs >= MAX_REGS) + QCC_Error(ERR_TOOMANYGLOBALS, "MAX_REGS is too small"); + } + + if (!strcmp(pr_token, "_")) + { + dotranslate2 = 1; + QCC_PR_Lex(); + } + if(dotranslate2) + QCC_PR_Expect("("); + + if(dotranslate || dotranslate2) + { + sprintf(buf, "dotranslate_%d", ++dotranslate_count); + d->name = strdup(buf); + } + + (((int *)qcc_pr_globals)[def->ofs+i]) = QCC_CopyString(pr_immediate_string); + QCC_PR_Lex (); + + if(dotranslate2) + QCC_PR_Expect(")"); + + if (!QCC_PR_CheckToken(",")) + break; + } + QCC_PR_Expect("}"); + } + else if (arraysize<=1) + { + if (isvar) + def->constant = false; + else + def->constant = true; + def->initialized = 1; + (((int *)qcc_pr_globals)[def->ofs]) = QCC_CopyString(pr_immediate_string); + QCC_PR_Lex (); + + if(dotranslate) + { + sprintf(buf, "dotranslate_%d", ++dotranslate_count); + def->name = strdup(buf); + } + + if (pr_immediate_type->type == ev_float) + G_INT(def->ofs) = (int)pr_immediate._float; + else if (pr_immediate_type->type != ev_string) + QCC_PR_ParseError (ERR_BADIMMEDIATETYPE, "wrong immediate type for %s", name); + } + else + QCC_PR_ParseError(ERR_ARRAYNEEDSBRACES, "Array initialisation requires curly brasces"); + + if(dotranslate) + QCC_PR_Expect(")"); + continue; + } + else if (type->type == ev_float) + { + if (arraysize>=1 && QCC_PR_CheckToken("{")) + { + int i; + for (i = 0; i < arraysize; i++) + { + if (pr_immediate_type->type != ev_float) + QCC_PR_ParseError (ERR_BADIMMEDIATETYPE, "wrong immediate type for %s", name); + (((float *)qcc_pr_globals)[def->ofs+i]) = pr_immediate._float; + QCC_PR_Lex (); + + if (!QCC_PR_CheckToken(",")) + break; + } + QCC_PR_Expect("}"); + + continue; + } + else if (arraysize<=1) + { + if (isvar) + def->constant = false; + else + def->constant = true; + + def->initialized = 1; + + if (pr_immediate_type->type != ev_float) + QCC_PR_ParseError (ERR_BADIMMEDIATETYPE, "wrong immediate type for %s", name); + + if (def->constant && opt_dupconstdefs) + { + if (def->ofs == oldglobals) + { + if (Hash_GetKey(&floatconstdefstable, *(int*)&pr_immediate._float)) + optres_dupconstdefs++; + QCC_FreeOffset(def->ofs, def->type->size); + d = QCC_MakeFloatDef(pr_immediate._float); + d->references++; + def->ofs = d->ofs; + QCC_PR_Lex(); + continue; + } + } + + (((float *)qcc_pr_globals)[def->ofs]) = pr_immediate._float; + QCC_PR_Lex (); + + continue; + } + else + QCC_PR_ParseError(ERR_ARRAYNEEDSBRACES, "Array initialisation requires curly brasces"); + } + else if (type->type == ev_vector) + { + if (arraysize>=1 && QCC_PR_CheckToken("{")) + { + int i; + for (i = 0; i < arraysize; i++) + { + if (pr_immediate_type->type != ev_vector) + QCC_PR_ParseError (ERR_BADIMMEDIATETYPE, "wrong immediate type for %s", name); + (((float *)qcc_pr_globals)[def->ofs+i*3+0]) = pr_immediate.vector[0]; + (((float *)qcc_pr_globals)[def->ofs+i*3+1]) = pr_immediate.vector[1]; + (((float *)qcc_pr_globals)[def->ofs+i*3+2]) = pr_immediate.vector[2]; + QCC_PR_Lex (); + + if (!QCC_PR_CheckToken(",")) + break; + } + QCC_PR_Expect("}"); + + continue; + } + else if (arraysize<=1) + { + if (isvar) + def->constant = false; + else + def->constant = true; + def->initialized = 1; + + if (pr_immediate_type->type != ev_vector) + QCC_PR_ParseError (ERR_BADIMMEDIATETYPE, "wrong immediate type for %s", name); + (((float *)qcc_pr_globals)[def->ofs+0]) = pr_immediate.vector[0]; + (((float *)qcc_pr_globals)[def->ofs+1]) = pr_immediate.vector[1]; + (((float *)qcc_pr_globals)[def->ofs+2]) = pr_immediate.vector[2]; + QCC_PR_Lex (); + + continue; + } + else + QCC_PR_ParseError(ERR_ARRAYNEEDSBRACES, "Array initialisation requires curly brasces"); + } + else if (pr_token_type == tt_name) + { +// if (pr_scope)//create a new instance, emit a copy op +// { +// QCC_PR_ParseError ("name defined for local : %s", name); +// } +// else + { + d = QCC_PR_GetDef (NULL, pr_token, pr_scope, false, 0, false); + if (!d) + QCC_PR_ParseError (ERR_NOTDEFINED, "initialisation name not defined : %s", pr_token); + if (!d->constant) + { + QCC_PR_ParseWarning (WARN_NOTCONSTANT, "initialisation name not a constant : %s", pr_token); + QCC_PR_ParsePrintDef(WARN_NOTCONSTANT, d); + } + memcpy (def, d, sizeof(*d)); + def->name = name; + def->initialized = true; + } + } + else if (pr_token_type != tt_immediate) + QCC_PR_ParseError (ERR_BADIMMEDIATETYPE, "not an immediate for %s - %s", name, pr_token); + else if (pr_immediate_type->type != type->type) + QCC_PR_ParseError (ERR_BADIMMEDIATETYPE, "wrong immediate type for %s - %s", name, pr_token); + else + memcpy (qcc_pr_globals + def->ofs, &pr_immediate, 4*type_size[pr_immediate_type->type]); + + if (isvar) + def->constant = false; + else + def->constant = true; + def->initialized = true; + QCC_PR_Lex (); + } + else + { + if (type->type == ev_function && isvar) + { + isconstant = !isvar; + def->initialized = 1; + } + + if (isconstant && type->type == ev_field) + def->constant = 2; //special flag on fields, 2, makes the pointer obtained from them also constant. + else + def->constant = isconstant; + } + + + } while (QCC_PR_CheckToken (",")); + + if (type->type == ev_function) + QCC_PR_CheckToken (";"); + else + { + if (!QCC_PR_CheckToken (";")) + QCC_PR_ParseWarning(WARN_UNDESIRABLECONVENTION, "Missing semicolon at end of definition"); + } +} + +/* +============ +PR_CompileFile + +compiles the 0 terminated text, adding defintions to the pr structure +============ +*/ +pbool QCC_PR_CompileFile (char *string, char *filename) +{ + jmp_buf oldjb; + if (!pr.memory) + QCC_Error (ERR_INTERNAL, "PR_CompileFile: Didn't clear"); + + QCC_PR_ClearGrabMacros (); // clear the frame macros + + compilingfile = filename; + + if (opt_filenames) + { + optres_filenames += strlen(filename); + pr_file_p = qccHunkAlloc(strlen(filename)+1); + strcpy(pr_file_p, filename); + s_file = pr_file_p - strings; + s_file2 = 0; + } + else + { + s_file = s_file2 = QCC_CopyString (filename); + } + pr_file_p = string; + + pr_source_line = 0; + + memcpy(&oldjb, &pr_parse_abort, sizeof(oldjb)); + + if( setjmp( pr_parse_abort ) ) { + // dont count it as error + } else { + //clock up the first line + QCC_PR_NewLine (false); + + QCC_PR_Lex (); // read first token + } + + while (pr_token_type != tt_eof) + { + if (setjmp(pr_parse_abort)) + { + if (++pr_error_count > MAX_ERRORS) + { + memcpy(&pr_parse_abort, &oldjb, sizeof(oldjb)); + return false; + } + num_continues = 0; + num_breaks = 0; + num_cases = 0; + QCC_PR_SkipToSemicolon (); + if (pr_token_type == tt_eof) + { + memcpy(&pr_parse_abort, &oldjb, sizeof(oldjb)); + return false; + } + } + + pr_scope = NULL; // outside all functions + + QCC_PR_ParseDefs (NULL); + } + memcpy(&pr_parse_abort, &oldjb, sizeof(oldjb)); + + return (pr_error_count == 0); +} + +pbool QCC_Include(char *filename) +{ + char *newfile; + char fname[512]; + char *opr_file_p; + QCC_string_t os_file, os_file2; + int opr_source_line; + char *ocompilingfile; + struct qcc_includechunk_s *oldcurrentchunk; + extern struct qcc_includechunk_s *currentchunk; + + ocompilingfile = compilingfile; + os_file = s_file; + os_file2 = s_file2; + opr_source_line = pr_source_line; + opr_file_p = pr_file_p; + oldcurrentchunk = currentchunk; + + strcpy(fname, filename); + QCC_LoadFile(fname, (void*)&newfile); + currentchunk = NULL; + pr_file_p = newfile; + QCC_PR_CompileFile(newfile, fname); + currentchunk = oldcurrentchunk; + + compilingfile = ocompilingfile; + s_file = os_file; + s_file2 = os_file2; + pr_source_line = opr_source_line; + pr_file_p = opr_file_p; + +// QCC_PR_IncludeChunk(newfile, false, fname); + + return true; +} + +#endif diff --git a/misc/mediasource/extra/fteqcc-src/qcc_pr_lex.c b/misc/mediasource/extra/fteqcc-src/qcc_pr_lex.c new file mode 100644 index 00000000..286da96f --- /dev/null +++ b/misc/mediasource/extra/fteqcc-src/qcc_pr_lex.c @@ -0,0 +1,3542 @@ +#ifndef MINIMAL + +#include "qcc.h" +#ifdef QCC +#define print printf +#endif +#include "time.h" + +#define MEMBERFIELDNAME "__m%s" + +#define STRCMP(s1,s2) (((*s1)!=(*s2)) || strcmp(s1+1,s2+1)) //saves about 2-6 out of 120 - expansion of idea from fastqcc + +void QCC_PR_ConditionCompilation(void); +pbool QCC_PR_UndefineName(char *name); +char *QCC_PR_CheakCompConstString(char *def); +CompilerConstant_t *QCC_PR_CheckCompConstDefined(char *def); +pbool QCC_Include(char *filename); + +char *compilingfile; + +int pr_source_line; + +char *pr_file_p; +char *pr_line_start; // start of current source line + +int pr_bracelevel; + +char pr_token[8192]; +token_type_t pr_token_type; +QCC_type_t *pr_immediate_type; +QCC_eval_t pr_immediate; + +char pr_immediate_string[8192]; + +int pr_error_count; +int pr_warning_count; + + +CompilerConstant_t *CompilerConstant; +int numCompilerConstants; +extern pbool expandedemptymacro; + + + +char *pr_punctuation[] = +// longer symbols must be before a shorter partial match +{"&&", "||", "<=", ">=","==", "!=", "/=", "*=", "+=", "-=", "(+)", "(-)", "|=", "&~=", "++", "--", "->", "::", ";", ",", "!", "*", "/", "(", ")", "-", "+", "=", "[", "]", "{", "}", "...", "..", ".", "<<", "<", ">>", ">" , "?", "#" , "@", "&" , "|", "^", ":", NULL}; + +char *pr_punctuationremap[] = //a nice bit of evilness. +//(+) -> |= +//-> -> . +//(-) -> &~= +{"&&", "||", "<=", ">=","==", "!=", "/=", "*=", "+=", "-=", "|=", "&~=", "|=", "&~=", "++", "--", ".", "::", ";", ",", "!", "*", "/", "(", ")", "-", "+", "=", "[", "]", "{", "}", "...", "..", ".", "<<", "<", ">>", ">" , "?", "#" , "@", "&" , "|", "^", ":", NULL}; + +// simple types. function types are dynamically allocated +QCC_type_t *type_void;// = {ev_void/*, &def_void*/}; +QCC_type_t *type_string;// = {ev_string/*, &def_string*/}; +QCC_type_t *type_float;// = {ev_float/*, &def_float*/}; +QCC_type_t *type_vector;// = {ev_vector/*, &def_vector*/}; +QCC_type_t *type_entity;// = {ev_entity/*, &def_entity*/}; +QCC_type_t *type_field;// = {ev_field/*, &def_field*/}; +QCC_type_t *type_function;// = {ev_function/*, &def_function*/,NULL,&type_void}; +// type_function is a void() function used for state defs +QCC_type_t *type_pointer;// = {ev_pointer/*, &def_pointer*/}; +QCC_type_t *type_integer;// = {ev_integer/*, &def_integer*/}; +QCC_type_t *type_variant;// = {ev_integer/*, &def_integer*/}; + +QCC_type_t *type_floatfield;// = {ev_field/*, &def_field*/, NULL, &type_float}; + +/*QCC_def_t def_void = {type_void, "temp"}; +QCC_def_t def_string = {type_string, "temp"}; +QCC_def_t def_float = {type_float, "temp"}; +QCC_def_t def_vector = {type_vector, "temp"}; +QCC_def_t def_entity = {type_entity, "temp"}; +QCC_def_t def_field = {type_field, "temp"}; +QCC_def_t def_function = {type_function, "temp"}; +QCC_def_t def_pointer = {type_pointer, "temp"}; +QCC_def_t def_integer = {type_integer, "temp"}; +*/ +QCC_def_t def_ret, def_parms[MAX_PARMS]; + +//QCC_def_t *def_for_type[9] = {&def_void, &def_string, &def_float, &def_vector, &def_entity, &def_field, &def_function, &def_pointer, &def_integer}; + +void QCC_PR_LexWhitespace (void); + + + + +//for compiler constants and file includes. + +typedef struct qcc_includechunk_s { + struct qcc_includechunk_s *prev; + char *filename; + char *currentdatapoint; + int currentlinenumber; + CompilerConstant_t *cnst; +} qcc_includechunk_t; +qcc_includechunk_t *currentchunk; +void QCC_PR_IncludeChunkEx (char *data, pbool duplicate, char *filename, CompilerConstant_t *cnst) +{ + qcc_includechunk_t *chunk = qccHunkAlloc(sizeof(qcc_includechunk_t)); + chunk->prev = currentchunk; + currentchunk = chunk; + + chunk->currentdatapoint = pr_file_p; + chunk->currentlinenumber = pr_source_line; + chunk->cnst = cnst; + if( cnst ) + { + cnst->inside++; + } + + if (duplicate) + { + pr_file_p = qccHunkAlloc(strlen(data)+1); + strcpy(pr_file_p, data); + } + else + pr_file_p = data; +} +void QCC_PR_IncludeChunk (char *data, pbool duplicate, char *filename) +{ + QCC_PR_IncludeChunkEx(data, duplicate, filename, NULL); +} + +pbool QCC_PR_UnInclude(void) +{ + if (!currentchunk) + return false; + + if( currentchunk->cnst ) + currentchunk->cnst->inside--; + + pr_file_p = currentchunk->currentdatapoint; + pr_source_line = currentchunk->currentlinenumber; + + currentchunk = currentchunk->prev; + + return true; +} + + +/* +============== +PR_PrintNextLine +============== +*/ +void QCC_PR_PrintNextLine (void) +{ + char *t; + + printf ("%3i:",pr_source_line); + for (t=pr_line_start ; *t && *t != '\n' ; t++) + printf ("%c",*t); + printf ("\n"); +} + +extern char qccmsourcedir[]; +//also meant to include it. +void QCC_FindBestInclude(char *newfile, char *currentfile, char *rootpath) +{ + char fullname[10248]; + char *stripfrom; + int doubledots; + + char *end = fullname; + + if (!*newfile) + return; + + doubledots = 0; + while(!strncmp(newfile, "../", 3) || !strncmp(newfile, "..\\", 3)) + { + newfile+=3; + doubledots++; + } + + currentfile += strlen(rootpath); //could this be bad? + + for(stripfrom = currentfile+strlen(currentfile)-1; stripfrom>currentfile; stripfrom--) + { + if (*stripfrom == '/' || *stripfrom == '\\') + { + if (doubledots>0) + doubledots--; + else + { + stripfrom++; + break; + } + } + } + strcpy(end, rootpath); end = end+strlen(end); + if (*fullname && end[-1] != '/') + { + strcpy(end, "/"); + end = end+strlen(end); + } + strncpy(end, currentfile, stripfrom - currentfile); end += stripfrom - currentfile; *end = '\0'; + strcpy(end, newfile); + + QCC_Include(fullname); +} + +pbool defaultstatic; +int ForcedCRC; +int QCC_PR_LexInteger (void); +void QCC_AddFile (char *filename); +void QCC_PR_LexString (void); +pbool QCC_PR_SimpleGetToken (void); + +int ParsePrecompilerIf(void) +{ + CompilerConstant_t *c; + int eval; + char *start = pr_file_p; + if (!QCC_PR_SimpleGetToken()) + { + if (*pr_file_p == '(') + { + eval = ParsePrecompilerIf(); + while (*pr_file_p == ' ' || *pr_file_p == '\t') + pr_file_p++; + if (*pr_file_p != ')') + QCC_PR_ParseError(ERR_EXPECTED, "unclosed bracket condition\n"); + } + else + QCC_PR_ParseError(ERR_EXPECTED, "expected bracket or constant\n"); + } + else if (!strcmp(pr_token, "defined")) + { + while (*pr_file_p == ' ' || *pr_file_p == '\t') + pr_file_p++; + if (*pr_file_p != '(') + QCC_PR_ParseError(ERR_EXPECTED, "no opening bracket after defined\n"); + else + { + pr_file_p++; + + QCC_PR_SimpleGetToken(); + eval = !!QCC_PR_CheckCompConstDefined(pr_token); + + while (*pr_file_p == ' ' || *pr_file_p == '\t') + pr_file_p++; + if (*pr_file_p != ')') + QCC_PR_ParseError(ERR_EXPECTED, "unclosed defined condition\n"); + pr_file_p++; + } + } + else + { + c = QCC_PR_CheckCompConstDefined(pr_token); + if (!c) + eval = atoi(pr_token); + else + eval = atoi(c->value); + } + + QCC_PR_SimpleGetToken(); + if (!strcmp(pr_token, "||")) + eval = ParsePrecompilerIf()||eval; + else if (!strcmp(pr_token, "&&")) + eval = ParsePrecompilerIf()&&eval; + else if (!strcmp(pr_token, "<=")) + eval = eval <= ParsePrecompilerIf(); + else if (!strcmp(pr_token, ">=")) + eval = eval >= ParsePrecompilerIf(); + else if (!strcmp(pr_token, "<")) + eval = eval < ParsePrecompilerIf(); + else if (!strcmp(pr_token, ">")) + eval = eval > ParsePrecompilerIf(); + else if (!strcmp(pr_token, "!=")) + eval = eval != ParsePrecompilerIf(); + + return eval; +} +/* +============== +QCC_PR_Precompiler +============== + +Runs precompiler stage +*/ +pbool QCC_PR_Precompiler(void) +{ + char msg[1024]; + int ifmode; + int a; + static int ifs = 0; + int level; //#if level + pbool eval = false; + + if (*pr_file_p == '#') + { + char *directive; + for (directive = pr_file_p+1; *directive; directive++) //so # define works + { + if (*directive == '\r' || *directive == '\n') + QCC_PR_ParseError(ERR_UNKNOWNPUCTUATION, "Hanging # with no directive\n"); + if (*directive > ' ') + break; + } + if (!strncmp(directive, "define", 6)) + { + pr_file_p = directive; + QCC_PR_ConditionCompilation(); + while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line + { + pr_file_p++; + } + } + else if (!strncmp(directive, "undef", 5)) + { + pr_file_p = directive+5; + while(*pr_file_p <= ' ') + pr_file_p++; + + QCC_PR_SimpleGetToken (); + QCC_PR_UndefineName(pr_token); + + // QCC_PR_ConditionCompilation(); + while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line + { + pr_file_p++; + } + } + else if (!strncmp(directive, "if", 2)) + { + int originalline = pr_source_line; + pr_file_p = directive+2; + if (!strncmp(pr_file_p, "def ", 4)) + { + ifmode = 0; + pr_file_p+=4; + } + else if (!strncmp(pr_file_p, "ndef ", 5)) + { + ifmode = 1; + pr_file_p+=5; + } + else + { + ifmode = 2; + pr_file_p+=0; + //QCC_PR_ParseError("bad \"#if\" type"); + } + + if (ifmode == 2) + { + eval = ParsePrecompilerIf(); + + if(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line + { + QCC_PR_ParseError (ERR_NOENDIF, "junk on the end of #if line"); + } + } + else + { + QCC_PR_SimpleGetToken (); + + // if (!STRCMP(pr_token, "COOP_MODE")) + // eval = false; + if (QCC_PR_CheckCompConstDefined(pr_token)) + eval = true; + + if (ifmode == 1) + eval = eval?false:true; + } + + while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line + { + pr_file_p++; + } + level = 1; + + if (eval) + ifs+=1; + else + { + while (1) + { + while(*pr_file_p && (*pr_file_p==' ' || *pr_file_p == '\t')) + pr_file_p++; + + if (!*pr_file_p) + { + pr_source_line = originalline; + QCC_PR_ParseError (ERR_NOENDIF, "#if with no endif"); + } + + if (*pr_file_p == '#') + { + pr_file_p++; + while(*pr_file_p==' ' || *pr_file_p == '\t') + pr_file_p++; + if (!strncmp(pr_file_p, "endif", 5)) + level--; + if (!strncmp(pr_file_p, "if", 2)) + level++; + if (!strncmp(pr_file_p, "else", 4) && level == 1) + { + ifs+=1; + while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line + { + pr_file_p++; + } + break; + } + } + + while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line + { + pr_file_p++; + } + if (level <= 0) + break; + pr_file_p++; //next line + pr_source_line++; + } + } + } + else if (!strncmp(directive, "else", 4)) + { + int originalline = pr_source_line; + + ifs -= 1; + level = 1; + + while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line + { + pr_file_p++; + } + while (1) + { + while(*pr_file_p && (*pr_file_p==' ' || *pr_file_p == '\t')) + pr_file_p++; + + if (!*pr_file_p) + { + pr_source_line = originalline; + QCC_PR_ParseError(ERR_NOENDIF, "#if with no endif"); + } + + if (*pr_file_p == '#') + { + pr_file_p++; + while(*pr_file_p==' ' || *pr_file_p == '\t') + pr_file_p++; + + if (!strncmp(pr_file_p, "endif", 5)) + level--; + if (!strncmp(pr_file_p, "if", 2)) + level++; + if (!strncmp(pr_file_p, "else", 4) && level == 1) + { + ifs+=1; + break; + } + } + + while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line + { + pr_file_p++; + } + if (level <= 0) + break; + pr_file_p++; //go off the end + pr_source_line++; + } + } + else if (!strncmp(directive, "endif", 5)) + { + while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line + { + pr_file_p++; + } + if (ifs <= 0) + QCC_PR_ParseError(ERR_NOPRECOMPILERIF, "unmatched #endif"); + else + ifs-=1; + } + else if (!strncmp(directive, "eof", 3)) + { + pr_file_p = NULL; + return true; + } + else if (!strncmp(directive, "error", 5)) + { + pr_file_p = directive+5; + for (a = 0; a < 1023 && pr_file_p[a] != '\n' && pr_file_p[a] != '\0'; a++) + msg[a] = pr_file_p[a]; + + msg[a-1] = '\0'; + + while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line, yes, I KNOW we are going to register an error, and not properly leave this function tree, but... + { + pr_file_p++; + } + + QCC_PR_ParseError(ERR_HASHERROR, "#Error: %s", msg); + } + else if (!strncmp(directive, "warning", 7)) + { + pr_file_p = directive+7; + for (a = 0; a < 1023 && pr_file_p[a] != '\n' && pr_file_p[a] != '\0'; a++) + msg[a] = pr_file_p[a]; + + msg[a-1] = '\0'; + + while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line + { + pr_file_p++; + } + + QCC_PR_ParseWarning(WARN_PRECOMPILERMESSAGE, "#warning: %s", msg); + } + else if (!strncmp(directive, "message", 7)) + { + pr_file_p = directive+7; + for (a = 0; a < 1023 && pr_file_p[a] != '\n' && pr_file_p[a] != '\0'; a++) + msg[a] = pr_file_p[a]; + + msg[a-1] = '\0'; + + while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line + { + pr_file_p++; + } + + printf("#message: %s\n", msg); + } + else if (!strncmp(directive, "copyright", 9)) + { + pr_file_p = directive+9; + for (a = 0; a < 1023 && pr_file_p[a] != '\n' && pr_file_p[a] != '\0'; a++) + msg[a] = pr_file_p[a]; + + msg[a-1] = '\0'; + + while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line + { + pr_file_p++; + } + + if (strlen(msg) >= sizeof(QCC_copyright)) + QCC_PR_ParseWarning(WARN_STRINGTOOLONG, "Copyright message is too long\n"); + strncpy(QCC_copyright, msg, sizeof(QCC_copyright)-1); + } + else if (!strncmp(directive, "pack", 4)) + { + ifmode = 0; + pr_file_p=directive+4; + if (!strncmp(pr_file_p, "id", 2)) + pr_file_p+=3; + else + { + ifmode = QCC_PR_LexInteger(); + if (ifmode == 0) + ifmode = 1; + pr_file_p++; + } + for (a = 0; a < 1023 && pr_file_p[a] != '\n' && pr_file_p[a] != '\0'; a++) + msg[a] = pr_file_p[a]; + + msg[a-1] = '\0'; + + while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line + { + pr_file_p++; + } + + if (ifmode == 0) + QCC_packid = atoi(msg); + else if (ifmode <= 5) + strcpy(QCC_Packname[ifmode-1], msg); + else + QCC_PR_ParseError(ERR_TOOMANYPACKFILES, "No more than 5 packs are allowed"); + } + else if (!strncmp(directive, "forcecrc", 8)) + { + pr_file_p=directive+8; + + ForcedCRC = QCC_PR_LexInteger(); + + pr_file_p++; + + for (a = 0; a < 1023 && pr_file_p[a] != '\n' && pr_file_p[a] != '\0'; a++) + msg[a] = pr_file_p[a]; + + msg[a-1] = '\0'; + + while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line + { + pr_file_p++; + } + } + else if (!strncmp(directive, "includelist", 11)) + { + pr_file_p=directive+11; + + while(*pr_file_p <= ' ') + pr_file_p++; + + while(1) + { + QCC_PR_LexWhitespace(); + if (!QCC_PR_SimpleGetToken()) + { + if (!*pr_file_p) + QCC_Error(ERR_EOF, "eof in includelist"); + else + { + pr_file_p++; + pr_source_line++; + } + continue; + } + if (!strcmp(pr_token, "#endlist")) + break; + + QCC_FindBestInclude(pr_token, compilingfile, qccmsourcedir); + + if (*pr_file_p == '\r') + pr_file_p++; + + for (a = 0; a < 1023 && pr_file_p[a] != '\n' && pr_file_p[a] != '\0'; a++) + msg[a] = pr_file_p[a]; + + msg[a-1] = '\0'; + + while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line + { + pr_file_p++; + } + } + + while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line + { + pr_file_p++; + } + } + else if (!strncmp(directive, "include", 7)) + { + char sm; + + pr_file_p=directive+7; + + while(*pr_file_p <= ' ') + pr_file_p++; + + msg[0] = '\0'; + if (*pr_file_p == '\"') + sm = '\"'; + else if (*pr_file_p == '<') + sm = '>'; + else + { + QCC_PR_ParseError(0, "Not a string literal (on a #include)"); + sm = 0; + } + pr_file_p++; + a=0; + while(*pr_file_p != sm) + { + if (*pr_file_p == '\n') + { + QCC_PR_ParseError(0, "#include continued over line boundy\n"); + break; + } + msg[a++] = *pr_file_p; + pr_file_p++; + } + msg[a] = 0; + + QCC_FindBestInclude(msg, compilingfile, qccmsourcedir); + + pr_file_p++; + + while(*pr_file_p != '\n' && *pr_file_p != '\0' && *pr_file_p <= ' ') + pr_file_p++; + + + while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line + { + pr_file_p++; + } + } + else if (!strncmp(directive, "datafile", 8)) + { + pr_file_p=directive+8; + + while(*pr_file_p <= ' ') + pr_file_p++; + + QCC_PR_LexString(); + printf("Including datafile: %s\n", pr_token); + QCC_AddFile(pr_token); + + pr_file_p++; + + for (a = 0; a < 1023 && pr_file_p[a] != '\n' && pr_file_p[a] != '\0'; a++) + msg[a] = pr_file_p[a]; + + msg[a-1] = '\0'; + + while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line + { + pr_file_p++; + } + } + else if (!strncmp(directive, "output", 6)) + { + extern char destfile[1024]; + pr_file_p=directive+6; + + while(*pr_file_p <= ' ') + pr_file_p++; + + QCC_PR_LexString(); + strcpy(destfile, pr_token); + printf("Outputfile: %s\n", destfile); + + pr_file_p++; + + for (a = 0; a < 1023 && pr_file_p[a] != '\n' && pr_file_p[a] != '\0'; a++) + msg[a] = pr_file_p[a]; + + msg[a-1] = '\0'; + + while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line + { + pr_file_p++; + } + } + else if (!strncmp(directive, "pragma", 6)) + { + pr_file_p=directive+6; + while(*pr_file_p <= ' ') + pr_file_p++; + + qcc_token[0] = '\0'; + for(a = 0; *pr_file_p != '\n' && *pr_file_p != '\0'; pr_file_p++) //read on until the end of the line + { + if ((*pr_file_p == ' ' || *pr_file_p == '\t'|| *pr_file_p == '(') && !*qcc_token) + { + msg[a] = '\0'; + strcpy(qcc_token, msg); + a=0; + continue; + } + msg[a++] = *pr_file_p; + } + + msg[a] = '\0'; + { + char *end; + for (end = msg + a-1; end>=msg && *end <= ' '; end--) + *end = '\0'; + } + + if (!*qcc_token) + { + strcpy(qcc_token, msg); + msg[0] = '\0'; + } + + { + char *end; + for (end = msg + a-1; end>=msg && *end <= ' '; end--) + *end = '\0'; + } + + if (!QC_strcasecmp(qcc_token, "DONT_COMPILE_THIS_FILE")) + { + while (*pr_file_p) + { + while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line + pr_file_p++; + + if (*pr_file_p == '\n') + { + pr_file_p++; + QCC_PR_NewLine(false); + } + } + } + else if (!QC_strcasecmp(qcc_token, "COPYRIGHT")) + { + if (strlen(msg) >= sizeof(QCC_copyright)) + QCC_PR_ParseWarning(WARN_STRINGTOOLONG, "Copyright message is too long\n"); + strncpy(QCC_copyright, msg, sizeof(QCC_copyright)-1); + } + else if (!strncmp(qcc_token, "compress", 8)) + { + extern pbool compressoutput; + compressoutput = atoi(msg); + } + else if (!strncmp(qcc_token, "forcecrc", 8)) + { + ForcedCRC = atoi(msg); + } + else if (!strncmp(qcc_token, "defaultstatic", 13)) + { + defaultstatic = atoi(msg); + } + else if (!strncmp(qcc_token, "sourcefile", 10)) + { + #define MAXSOURCEFILESLIST 8 + extern char sourcefileslist[MAXSOURCEFILESLIST][1024]; + extern int currentsourcefile; + extern int numsourcefiles; + + int i; + + QCC_COM_Parse(msg); + + for (i = 0; i < numsourcefiles; i++) + { + if (!strcmp(sourcefileslist[i], qcc_token)) + break; + } + if (i == numsourcefiles) + strcpy(sourcefileslist[numsourcefiles++], qcc_token); + } + else if (!QC_strcasecmp(qcc_token, "TARGET")) + { + if (qcc_targetformat == QCF_HEXEN2 && numstatements) + QCC_PR_ParseWarning(WARN_BADTARGET, "Cannot switch from hexen2 target \'%s\'. Ignored.", msg); + else if (!QC_strcasecmp(msg, "H2") || !QC_strcasecmp(msg, "HEXEN2")) + { + if (numstatements) + QCC_PR_ParseWarning(WARN_BADTARGET, "Cannot switch from hexen2 target \'%s\'. Ignored.", msg); + else + qcc_targetformat = QCF_HEXEN2; + } + else if (!QC_strcasecmp(msg, "KK7")) + qcc_targetformat = QCF_KK7; + else if (!QC_strcasecmp(msg, "DP") || !QC_strcasecmp(msg, "DARKPLACES")) + qcc_targetformat = QCF_DARKPLACES; + else if (!QC_strcasecmp(msg, "FTEDEBUG")) + qcc_targetformat = QCF_FTEDEBUG; + else if (!QC_strcasecmp(msg, "FTE")) + qcc_targetformat = QCF_FTE; + else if (!QC_strcasecmp(msg, "STANDARD") || !QC_strcasecmp(msg, "ID")) + qcc_targetformat = QCF_STANDARD; + else if (!QC_strcasecmp(msg, "DEBUG")) + qcc_targetformat = QCF_FTEDEBUG; + else + QCC_PR_ParseWarning(WARN_BADTARGET, "Unknown target \'%s\'. Ignored.", msg); + } + else if (!QC_strcasecmp(qcc_token, "PROGS_SRC")) + { //doesn't make sence, but silenced if you are switching between using a certain precompiler app used with CuTF. + } + else if (!QC_strcasecmp(qcc_token, "PROGS_DAT")) + { //doesn't make sence, but silenced if you are switching between using a certain precompiler app used with CuTF. + extern char destfile[1024]; +#ifndef QCCONLY + extern char qccmfilename[1024]; + int p; + char *s, *s2; +#endif + QCC_COM_Parse(msg); + +#ifndef QCCONLY + p=0; + s2 = qcc_token; + if (!strncmp(s2, "./", 2)) + s2+=2; + else + { + while(!strncmp(s2, "../", 3)) + { + s2+=3; + p++; + } + } + strcpy(qccmfilename, qccmsourcedir); + for (s=qccmfilename+strlen(qccmfilename);p && s>=qccmfilename; s--) + { + if (*s == '/' || *s == '\\') + { + *(s+1) = '\0'; + p--; + } + } + sprintf(destfile, "%s", s2); + + while (p>0) + { + memmove(destfile+3, destfile, strlen(destfile)+1); + destfile[0] = '.'; + destfile[1] = '.'; + destfile[2] = '/'; + p--; + } +#else + + strcpy(destfile, qcc_token); +#endif + printf("Outputfile: %s\n", destfile); + } + else if (!QC_strcasecmp(qcc_token, "keyword") || !QC_strcasecmp(qcc_token, "flag")) + { + char *s; + int st; + s = QCC_COM_Parse(msg); + if (!QC_strcasecmp(qcc_token, "enable") || !QC_strcasecmp(qcc_token, "on")) + st = 1; + else if (!QC_strcasecmp(qcc_token, "disable") || !QC_strcasecmp(qcc_token, "off")) + st = 0; + else + { + QCC_PR_ParseWarning(WARN_BADPRAGMA, "compiler flag state not recognised"); + st = -1; + } + if (st < 0) + QCC_PR_ParseWarning(WARN_BADPRAGMA, "warning id not recognised"); + else + { + int f; + s = QCC_COM_Parse(s); + + for (f = 0; compiler_flag[f].enabled; f++) + { + if (!QC_strcasecmp(compiler_flag[f].abbrev, qcc_token)) + { + if (compiler_flag[f].flags & FLAG_MIDCOMPILE) + *compiler_flag[f].enabled = st; + else + QCC_PR_ParseWarning(WARN_BADPRAGMA, "Cannot enable/disable keyword/flag via a pragma"); + break; + } + } + if (!compiler_flag[f].enabled) + QCC_PR_ParseWarning(WARN_BADPRAGMA, "keyword/flag not recognised"); + + } + } + else if (!QC_strcasecmp(qcc_token, "warning")) + { + int st; + char *s; + s = QCC_COM_Parse(msg); + if (!stricmp(qcc_token, "enable") || !stricmp(qcc_token, "on")) + st = 0; + else if (!stricmp(qcc_token, "disable") || !stricmp(qcc_token, "off")) + st = 1; + else if (!stricmp(qcc_token, "toggle")) + st = 2; + else + { + QCC_PR_ParseWarning(WARN_BADPRAGMA, "warning state not recognised"); + st = -1; + } + if (st>=0) + { + int wn; + s = QCC_COM_Parse(s); + wn = QCC_WarningForName(qcc_token); + if (wn < 0) + QCC_PR_ParseWarning(WARN_BADPRAGMA, "warning id not recognised"); + else + { + if (st == 2) //toggle + qccwarningdisabled[wn] = true - qccwarningdisabled[wn]; + else + qccwarningdisabled[wn] = st; + } + } + } + else + QCC_PR_ParseWarning(WARN_BADPRAGMA, "Unknown pragma \'%s\'", qcc_token); + } + return true; + } + + return false; +} + +/* +============== +PR_NewLine + +Call at start of file and when *pr_file_p == '\n' +============== +*/ +void QCC_PR_NewLine (pbool incomment) +{ + pr_source_line++; + pr_line_start = pr_file_p; + while(*pr_file_p==' ' || *pr_file_p == '\t') + pr_file_p++; + if (incomment) //no constants if in a comment. + { + } + else if (QCC_PR_Precompiler()) + { + } + +// if (pr_dumpasm) +// PR_PrintNextLine (); +} + +/* +============== +PR_LexString + +Parses a quoted string +============== +*/ +#if 0 +void QCC_PR_LexString (void) +{ + int c; + int len; + char tmpbuf[2048]; + + char *text; + char *oldf; + int oldline; + + bool fromfile = true; + + len = 0; + + text = pr_file_p; + do + { + QCC_COM_Parse(text); +// print("Next token is \"%s\"\n", com_token); + if (*text == '\"') + { + text++; + if (fromfile) pr_file_p++; + } + do + { + c = *text++; + if (fromfile) pr_file_p++; + if (!c) + QCC_PR_ParseError ("EOF inside quote"); + if (c=='\n') + QCC_PR_ParseError ("newline inside quote"); + if (c=='\\') + { // escape char + c = *text++; + if (fromfile) pr_file_p++; + if (!c) + QCC_PR_ParseError ("EOF inside quote"); + if (c == 'n') + c = '\n'; + else if (c == '"') + c = '"'; + else if (c == '\\') + c = '\\'; + else + QCC_PR_ParseError ("Unknown escape char"); + } + else if (c=='\"') + { + if (fromfile) pr_file_p++; + break; + } + tmpbuf[len] = c; + len++; + } while (1); + tmpbuf[len] = 0; +// if (fromfile) pr_file_p++; + + pr_immediate_type=NULL; + oldline=pr_source_line; + oldf=pr_file_p; + QCC_PR_Lex(); + if (pr_immediate_type == &type_string) + { +// print("Appending \"%s\" to \"%s\"\n", pr_immediate_string, tmpbuf); + strcat(tmpbuf, pr_immediate_string); + len+=strlen(pr_immediate_string); + } + else + { + pr_source_line = oldline; + pr_file_p = oldf-1; + QCC_PR_LexWhitespace(); + if (*pr_file_p != '\"') //annother string + break; + } + + QCC_PR_LexWhitespace(); + text = pr_file_p; + + } while (1); + + strcpy(pr_token, tmpbuf); + pr_token_type = tt_immediate; + pr_immediate_type = &type_string; + strcpy (pr_immediate_string, pr_token); + +// print("Found \"%s\"\n", pr_immediate_string); +} +#else +void QCC_PR_LexString (void) +{ + int c; + int len; + char *end, *cnst; + + int texttype=0; + + len = 0; + pr_file_p++; + do + { + c = *pr_file_p++; + if (!c) + QCC_PR_ParseError (ERR_EOF, "EOF inside quote"); + if (c=='\n') + QCC_PR_ParseError (ERR_INVALIDSTRINGIMMEDIATE, "newline inside quote"); + if (c=='\\') + { // escape char + c = *pr_file_p++; + if (!c) + QCC_PR_ParseError (ERR_EOF, "EOF inside quote"); + if (c == 'n') + c = '\n'; + else if (c == 'r') + c = '\r'; + else if (c == '"') + c = '"'; + else if (c == 't') + c = '\t'; + else if (c == 'a') + c = '\a'; + else if (c == 'v') + c = '\v'; + else if (c == 'f') + c = '\f'; + else if (c == 's' || c == 'b') + { + texttype ^= 128; + continue; + } + else if (c == '[') + c = 16; + else if (c == ']') + c = 17; + else if (c == '{') + { + int d; + c = 0; + while ((d = *pr_file_p++) != '}') + { + c = c * 10 + d - '0'; + if (d < '0' || d > '9' || c > 255) + QCC_PR_ParseError(ERR_BADCHARACTERCODE, "Bad character code"); + } + } + else if (c == '<') + c = 29; + else if (c == '-') + c = 30; + else if (c == '>') + c = 31; + else if (c == 'x' || c == 'X') + { + int d; + c = 0; + + d = (unsigned char)*pr_file_p++; + if (d >= '0' && d <= '9') + c += d - '0'; + else if (d >= 'A' && d <= 'F') + c += d - 'A' + 10; + else if (d >= 'a' && d <= 'f') + c += d - 'a' + 10; + else + QCC_PR_ParseError(ERR_BADCHARACTERCODE, "Bad character code"); + + c *= 16; + + d = (unsigned char)*pr_file_p++; + if (d >= '0' && d <= '9') + c += d - '0'; + else if (d >= 'A' && d <= 'F') + c += d - 'A' + 10; + else if (d >= 'a' && d <= 'f') + c += d - 'a' + 10; + else + QCC_PR_ParseError(ERR_BADCHARACTERCODE, "Bad character code"); + } + else if (c == '\\') + c = '\\'; + else if (c == '\'') + c = '\''; + else if (c >= '0' && c <= '9') + c = 18 + c - '0'; + else if (c == '\r') + { //sigh + c = *pr_file_p++; + if (c != '\n') + QCC_PR_ParseWarning(WARN_HANGINGSLASHR, "Hanging \\\\\r"); + pr_source_line++; + } + else if (c == '\n') + { //sigh + pr_source_line++; + } + else + QCC_PR_ParseError (ERR_INVALIDSTRINGIMMEDIATE, "Unknown escape char %c", c); + } + else if (c=='\"') + { + if (len >= sizeof(pr_immediate_string)-1) + QCC_Error(ERR_INVALIDSTRINGIMMEDIATE, "String length exceeds %i", sizeof(pr_immediate_string)-1); + + while(*pr_file_p && *pr_file_p <= ' ') + { + if (*pr_file_p == '\n') + { + pr_file_p++; + QCC_PR_NewLine(false); + } + else + pr_file_p++; + } + if (*pr_file_p == '\"') //have annother go + { + pr_file_p++; + continue; + } + pr_token[len] = 0; + pr_token_type = tt_immediate; + pr_immediate_type = type_string; + strcpy (pr_immediate_string, pr_token); + return; + } + else if (c == '#') + { + for (end = pr_file_p; ; end++) + { + if (*end <= ' ') + break; + + if (*end == ')' + || *end == '(' + || *end == '+' + || *end == '-' + || *end == '*' + || *end == '/' + || *end == '\\' + || *end == '|' + || *end == '&' + || *end == '=' + || *end == '^' + || *end == '~' + || *end == '[' + || *end == ']' + || *end == '\"' + || *end == '{' + || *end == '}' + || *end == ';' + || *end == ':' + || *end == ',' + || *end == '.' + || *end == '#') + break; + } + + c = *end; + *end = '\0'; + cnst = QCC_PR_CheakCompConstString(pr_file_p); + if (cnst==pr_file_p) + cnst=NULL; + *end = c; + c = '#'; //undo + if (cnst) + { + QCC_PR_ParseWarning(WARN_MACROINSTRING, "Macro expansion in string"); + + if (len+strlen(cnst) >= sizeof(pr_token)-1) + QCC_Error(ERR_INVALIDSTRINGIMMEDIATE, "String length exceeds %i", sizeof(pr_token)-1); + + strcpy(pr_token+len, cnst); + len+=strlen(cnst); + pr_file_p = end; + continue; + } + } + else if (c == 0x7C && flag_acc) //reacc support... reacc is strange. + c = '\n'; + else + c |= texttype; + + pr_token[len] = c; + len++; + if (len >= sizeof(pr_token)-1) + QCC_Error(ERR_INVALIDSTRINGIMMEDIATE, "String length exceeds %i", sizeof(pr_token)-1); + } while (1); +} +#endif + +/* +============== +PR_LexNumber +============== +*/ +int QCC_PR_LexInteger (void) +{ + int c; + int len; + + len = 0; + c = *pr_file_p; + if (pr_file_p[0] == '0' && pr_file_p[1] == 'x') + { + pr_token[0] = '0'; + pr_token[1] = 'x'; + len = 2; + c = *(pr_file_p+=2); + } + do + { + pr_token[len] = c; + len++; + pr_file_p++; + c = *pr_file_p; + } while ((c >= '0' && c<= '9') || c == '.' || (c>='a' && c <= 'f')); + pr_token[len] = 0; + return atoi (pr_token); +} + +void QCC_PR_LexNumber (void) +{ + int tokenlen = 0; + int num=0; + int base=0; + int c; + int sign=1; + if (*pr_file_p == '-') + { + sign=-1; + pr_file_p++; + + pr_token[tokenlen++] = '-'; + } + if (pr_file_p[0] == '0' && pr_file_p[1] == 'x') + { + pr_file_p+=2; + base = 16; + + pr_token[tokenlen++] = '0'; + pr_token[tokenlen++] = 'x'; + } + + pr_immediate_type = NULL; + //assume base 10 if not stated + if (!base) + base = 10; + + while((c = *pr_file_p)) + { + if (c >= '0' && c <= '9') + { + pr_token[tokenlen++] = c; + num*=base; + num += c-'0'; + } + else if (c >= 'a' && c <= 'f' && base > 10) + { + pr_token[tokenlen++] = c; + num*=base; + num += c -'a'+10; + } + else if (c >= 'A' && c <= 'F' && base > 10) + { + pr_token[tokenlen++] = c; + num*=base; + num += c -'A'+10; + } + else if (c == '.') + { + pr_token[tokenlen++] = c; + pr_file_p++; + pr_immediate_type = type_float; + while(1) + { + c = *pr_file_p; + if (c >= '0' && c <= '9') + { + pr_token[tokenlen++] = c; + } + else if (c == 'f') + { + pr_file_p++; + break; + } + else + { + break; + } + pr_file_p++; + } + pr_token[tokenlen++] = 0; + pr_immediate._float = (float)atof(pr_token); + return; + } + else if (c == 'i') + { + pr_token[tokenlen++] = c; + pr_token[tokenlen++] = 0; + pr_file_p++; + pr_immediate_type = type_integer; + pr_immediate._int = num*sign; + return; + } + else + break; + pr_file_p++; + } + pr_token[tokenlen++] = 0; + + if (!pr_immediate_type) + { + if (flag_assume_integer) + pr_immediate_type = type_integer; + else + pr_immediate_type = type_float; + } + + if (pr_immediate_type == type_integer) + { + pr_immediate_type = type_integer; + pr_immediate._int = num*sign; + } + else + { + pr_immediate_type = type_float; + // at this point, we know there's no . in it, so the NaN bug shouldn't happen + // and we cannot use atof on tokens like 0xabc, so use num*sign, it SHOULD be safe + //pr_immediate._float = atof(pr_token); + pr_immediate._float = (float)(num*sign); + } +} + + +float QCC_PR_LexFloat (void) +{ + int c; + int len; + + len = 0; + c = *pr_file_p; + do + { + pr_token[len] = c; + len++; + pr_file_p++; + c = *pr_file_p; + } while ((c >= '0' && c<= '9') || (c == '.'&&pr_file_p[1]!='.')); //only allow a . if the next isn't too... + pr_token[len] = 0; + return (float)atof (pr_token); +} + +/* +============== +PR_LexVector + +Parses a single quoted vector +============== +*/ +void QCC_PR_LexVector (void) +{ + int i; + + pr_file_p++; + + if (*pr_file_p == '\\') + {//extended character constant + pr_token_type = tt_immediate; + pr_immediate_type = type_float; + pr_file_p++; + switch(*pr_file_p) + { + case 'n': + pr_immediate._float = '\n'; + break; + case 'r': + pr_immediate._float = '\r'; + break; + case 't': + pr_immediate._float = '\t'; + break; + case '\'': + pr_immediate._float = '\''; + break; + case '\"': + pr_immediate._float = '\"'; + break; + case '\\': + pr_immediate._float = '\\'; + break; + default: + QCC_PR_ParseError (ERR_INVALIDVECTORIMMEDIATE, "Bad character constant"); + } + if (*pr_file_p != '\'') + QCC_PR_ParseError (ERR_INVALIDVECTORIMMEDIATE, "Bad character constant"); + pr_file_p++; + return; + } + if (pr_file_p[1] == '\'') + {//character constant + pr_token_type = tt_immediate; + pr_immediate_type = type_float; + pr_immediate._float = pr_file_p[0]; + pr_file_p+=2; + return; + } + pr_token_type = tt_immediate; + pr_immediate_type = type_vector; + QCC_PR_LexWhitespace (); + for (i=0 ; i<3 ; i++) + { + pr_immediate.vector[i] = QCC_PR_LexFloat (); + QCC_PR_LexWhitespace (); + + if (*pr_file_p == '\'' && i == 1) + { + if (i < 2) + QCC_PR_ParseWarning (WARN_FTE_SPECIFIC, "Bad vector"); + + for (i++ ; i<3 ; i++) + pr_immediate.vector[i] = 0; + break; + } + } + if (*pr_file_p != '\'') + QCC_PR_ParseError (ERR_INVALIDVECTORIMMEDIATE, "Bad vector"); + pr_file_p++; +} + +/* +============== +PR_LexName + +Parses an identifier +============== +*/ +void QCC_PR_LexName (void) +{ + int c; + int len; + + len = 0; + c = *pr_file_p; + do + { + pr_token[len] = c; + len++; + pr_file_p++; + c = *pr_file_p; + } while ( (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' + || (c >= '0' && c <= '9')); + + pr_token[len] = 0; + pr_token_type = tt_name; +} + +/* +============== +PR_LexPunctuation +============== +*/ +void QCC_PR_LexPunctuation (void) +{ + int i; + int len; + char *p; + + pr_token_type = tt_punct; + + for (i=0 ; (p = pr_punctuation[i]) != NULL ; i++) + { + len = strlen(p); + if (!strncmp(p, pr_file_p, len) ) + { + strcpy (pr_token, pr_punctuationremap[i]); + if (p[0] == '{') + pr_bracelevel++; + else if (p[0] == '}') + pr_bracelevel--; + pr_file_p += len; + return; + } + } + + QCC_PR_ParseError (ERR_UNKNOWNPUCTUATION, "Unknown punctuation"); +} + + +/* +============== +PR_LexWhitespace +============== +*/ +void QCC_PR_LexWhitespace (void) +{ + int c; + + while (1) + { + // skip whitespace + while ( (c = *pr_file_p) <= ' ') + { + if (c=='\n') + { + pr_file_p++; + QCC_PR_NewLine (false); + if (!pr_file_p) + return; + } + else + { + if (c == 0) + return; // end of file + pr_file_p++; + } + } + + // skip // comments + if (c=='/' && pr_file_p[1] == '/') + { + while (*pr_file_p && *pr_file_p != '\n') + pr_file_p++; + + if (*pr_file_p == '\n') + pr_file_p++; //don't break on eof. + QCC_PR_NewLine(false); + continue; + } + + // skip /* */ comments + if (c=='/' && pr_file_p[1] == '*') + { + pr_file_p+=2; + do + { + if (pr_file_p[0]=='\n') + { + QCC_PR_NewLine(true); + } + if (pr_file_p[1] == 0) + { + pr_file_p++; + return; + } + pr_file_p++; + } while (pr_file_p[0] != '*' || pr_file_p[1] != '/'); + pr_file_p+=2; + continue; + } + + break; // a real character has been found + } +} + +//============================================================================ + +#define MAX_FRAMES 8192 +char pr_framemodelname[64]; +char pr_framemacros[MAX_FRAMES][64]; +int pr_framemacrovalue[MAX_FRAMES]; +int pr_nummacros, pr_oldmacros; +int pr_macrovalue; +int pr_savedmacro; + +void QCC_PR_ClearGrabMacros (void) +{ + pr_oldmacros = pr_nummacros; +// pr_nummacros = 0; + pr_macrovalue = 0; + pr_savedmacro = -1; +} + +int QCC_PR_FindMacro (char *name) +{ + int i; + + for (i=pr_nummacros-1 ; i>=0 ; i--) + { + if (!STRCMP (name, pr_framemacros[i])) + { + return pr_framemacrovalue[i]; + } + } + for (i=pr_nummacros-1 ; i>=0 ; i--) + { + if (!stricmp (name, pr_framemacros[i])) + { + QCC_PR_ParseWarning(WARN_CASEINSENSATIVEFRAMEMACRO, "Case insensative frame macro"); + return pr_framemacrovalue[i]; + } + } + return -1; +} + +void QCC_PR_ExpandMacro(void) +{ + int i = QCC_PR_FindMacro(pr_token); + + if (i < 0) + QCC_PR_ParseError (ERR_BADFRAMEMACRO, "Unknown frame macro $%s", pr_token); + + sprintf (pr_token,"%d", i); + pr_token_type = tt_immediate; + pr_immediate_type = type_float; + pr_immediate._float = (float)i; +} + +// just parses text, returning false if an eol is reached +pbool QCC_PR_SimpleGetToken (void) +{ + int c; + int i; + + pr_token[0] = 0; + +// skip whitespace + while ( (c = *pr_file_p) <= ' ') + { + if (c=='\n' || c == 0) + return false; + pr_file_p++; + } + if (pr_file_p[0] == '/') + { + if (pr_file_p[1] == '/') + { //comment alert + while(*pr_file_p && *pr_file_p != '\n') + pr_file_p++; + return false; + } + if (pr_file_p[1] == '*') + return false; + } + + i = 0; + while ( (c = *pr_file_p) > ' ' && c != ',' && c != ';' && c != ')' && c != '(' && c != ']') + { + pr_token[i] = c; + i++; + pr_file_p++; + } + pr_token[i] = 0; + return i!=0; +} + +pbool QCC_PR_LexMacroName(void) +{ + int c; + int i; + + pr_token[0] = 0; + +// skip whitespace + while ( (c = *pr_file_p) <= ' ') + { + if (c=='\n' || c == 0) + return false; + pr_file_p++; + } + if (pr_file_p[0] == '/') + { + if (pr_file_p[1] == '/') + { //comment alert + while(*pr_file_p && *pr_file_p != '\n') + pr_file_p++; + return false; + } + if (pr_file_p[1] == '*') + return false; + } + + i = 0; + while ( (c = *pr_file_p) > ' ' && c != '\n' && c != ',' && c != ';' && c != ')' && c != '(' && c != ']' && !(pr_file_p[0] == '.' && pr_file_p[1] == '.')) + { + pr_token[i] = c; + i++; + pr_file_p++; + } + pr_token[i] = 0; + return i!=0; +} + +void QCC_PR_MacroFrame(char *name, int value) +{ + int i; + for (i=pr_nummacros-1 ; i>=0 ; i--) + { + if (!STRCMP (name, pr_framemacros[i])) + { + pr_framemacrovalue[i] = value; + if (i>=pr_oldmacros) + QCC_PR_ParseWarning(WARN_DUPLICATEMACRO, "Duplicate macro defined (%s)", pr_token); + //else it's from an old file, and shouldn't be mentioned. + return; + } + } + + if (strlen(name)+1 > sizeof(pr_framemacros[0])) + QCC_PR_ParseWarning(ERR_TOOMANYFRAMEMACROS, "Name for frame macro %s is too long", name); + else + { + strcpy (pr_framemacros[pr_nummacros], name); + pr_framemacrovalue[pr_nummacros] = value; + pr_nummacros++; + if (pr_nummacros >= MAX_FRAMES) + QCC_PR_ParseError(ERR_TOOMANYFRAMEMACROS, "Too many frame macros defined"); + } +} + +void QCC_PR_ParseFrame (void) +{ + while (QCC_PR_LexMacroName ()) + { + QCC_PR_MacroFrame(pr_token, pr_macrovalue++); + } +} + +/* +============== +PR_LexGrab + +Deals with counting sequence numbers and replacing frame macros +============== +*/ +void QCC_PR_LexGrab (void) +{ + pr_file_p++; // skip the $ +// if (!QCC_PR_SimpleGetToken ()) +// QCC_PR_ParseError ("hanging $"); + if (*pr_file_p <= ' ') + QCC_PR_ParseError (ERR_BADFRAMEMACRO, "hanging $"); + QCC_PR_LexMacroName(); + if (!*pr_token) + QCC_PR_ParseError (ERR_BADFRAMEMACRO, "hanging $"); + +// check for $frame + if (!STRCMP (pr_token, "frame") || !STRCMP (pr_token, "framesave")) + { + QCC_PR_ParseFrame (); + QCC_PR_Lex (); + } +// ignore other known $commands - just for model/spritegen + else if (!STRCMP (pr_token, "cd") + || !STRCMP (pr_token, "origin") + || !STRCMP (pr_token, "base") + || !STRCMP (pr_token, "flags") + || !STRCMP (pr_token, "scale") + || !STRCMP (pr_token, "skin") ) + { // skip to end of line + while (QCC_PR_LexMacroName ()) + ; + QCC_PR_Lex (); + } + else if (!STRCMP (pr_token, "flush")) + { + QCC_PR_ClearGrabMacros(); + while (QCC_PR_LexMacroName ()) + ; + QCC_PR_Lex (); + } + else if (!STRCMP (pr_token, "framevalue")) + { + QCC_PR_LexMacroName (); + pr_macrovalue = atoi(pr_token); + + QCC_PR_Lex (); + } + else if (!STRCMP (pr_token, "framerestore")) + { + QCC_PR_LexMacroName (); + QCC_PR_ExpandMacro(); + pr_macrovalue = (int)pr_immediate._float; + + QCC_PR_Lex (); + } + else if (!STRCMP (pr_token, "modelname")) + { + int i; + QCC_PR_LexMacroName (); + + if (*pr_framemodelname) + QCC_PR_MacroFrame(pr_framemodelname, pr_macrovalue); + + strncpy(pr_framemodelname, pr_token, sizeof(pr_framemodelname)-1); + pr_framemodelname[sizeof(pr_framemodelname)-1] = '\0'; + + i = QCC_PR_FindMacro(pr_framemodelname); + if (i) + pr_macrovalue = i; + else + i = 0; + + QCC_PR_Lex (); + } +// look for a frame name macro + else + QCC_PR_ExpandMacro (); +} + +//=========================== +//compiler constants - dmw + +pbool QCC_PR_UndefineName(char *name) +{ +// int a; + CompilerConstant_t *c; + c = pHash_Get(&compconstantstable, name); + if (!c) + { + QCC_PR_ParseWarning(WARN_UNDEFNOTDEFINED, "Precompiler constant %s was not defined", name); + return false; + } + + Hash_Remove(&compconstantstable, name); + return true; + /* + a = c-CompilerConstant; +// for (a = 0; a < numCompilerConstants; a++) + { +// if (!STRCMP(name, CompilerConstant[a].name)) + { + memmove(&CompilerConstant[a], &CompilerConstant[a+1], sizeof(CompilerConstant_t) * (numCompilerConstants-a)); + numCompilerConstants--; + + + + + if (!STRCMP(name, "OP_NODUP")) + qccop_noduplicatestrings = false; + + if (!STRCMP(name, "OP_COMP_ALL")) //group + { + QCC_PR_UndefineName("OP_COMP_STATEMENTS"); + QCC_PR_UndefineName("OP_COMP_DEFS"); + QCC_PR_UndefineName("OP_COMP_FIELDS"); + QCC_PR_UndefineName("OP_COMP_FUNCTIONS"); + QCC_PR_UndefineName("OP_COMP_STRINGS"); + QCC_PR_UndefineName("OP_COMP_GLOBALS"); + QCC_PR_UndefineName("OP_COMP_LINES"); + QCC_PR_UndefineName("OP_COMP_TYPES"); + } + + return true; + } + } +// return false; +*/ +} + +CompilerConstant_t *QCC_PR_DefineName(char *name) +{ + int i; + CompilerConstant_t *cnst; + +// if (numCompilerConstants >= MAX_CONSTANTS) +// QCC_PR_ParseError("Too many compiler constants - %i >= %i", numCompilerConstants, MAX_CONSTANTS); + + if (strlen(name) >= MAXCONSTANTLENGTH || !*name) + QCC_PR_ParseError(ERR_CONSTANTTOOLONG, "Compiler constant name length is too long or short"); + + cnst = pHash_Get(&compconstantstable, name); + if (cnst ) + { + QCC_PR_ParseWarning(WARN_DUPLICATEDEFINITION, "Duplicate definition for Precompiler constant %s", name); + Hash_Remove(&compconstantstable, name); + } + + cnst = qccHunkAlloc(sizeof(CompilerConstant_t)); + + cnst->used = false; + cnst->numparams = 0; + strcpy(cnst->name, name); + cnst->namelen = strlen(name); + *cnst->value = '\0'; + for (i = 0; i < MAXCONSTANTPARAMS; i++) + cnst->params[i][0] = '\0'; + + pHash_Add(&compconstantstable, cnst->name, cnst, qccHunkAlloc(sizeof(bucket_t))); + + if (!STRCMP(name, "OP_NODUP")) + opt_noduplicatestrings = true; + + + if (!STRCMP(name, "OP_TIME")) //group - optimize for a fast compiler + { + QCC_PR_UndefineName("OP_SIZE"); + QCC_PR_UndefineName("OP_SPEED"); + + QCC_PR_UndefineName("OP_NODUP"); + QCC_PR_UndefineName("OP_COMP_ALL"); + } + + if (!STRCMP(name, "OP_SPEED")) //group - optimize run speed + { + QCC_PR_UndefineName("OP_SIZE"); + QCC_PR_UndefineName("OP_TIME"); + +// QCC_PR_UndefineName("OP_NODUP"); + QCC_PR_UndefineName("OP_COMP_ALL"); + } + + if (!STRCMP(name, "OP_SIZE")) //group - produce small output. + { + QCC_PR_UndefineName("OP_SPEED"); + QCC_PR_UndefineName("OP_TIME"); + + QCC_PR_DefineName("OP_NODUP"); + QCC_PR_DefineName("OP_COMP_ALL"); + } + + if (!STRCMP(name, "OP_COMP_ALL")) //group - compress the output + { + QCC_PR_DefineName("OP_COMP_STATEMENTS"); + QCC_PR_DefineName("OP_COMP_DEFS"); + QCC_PR_DefineName("OP_COMP_FIELDS"); + QCC_PR_DefineName("OP_COMP_FUNCTIONS"); + QCC_PR_DefineName("OP_COMP_STRINGS"); + QCC_PR_DefineName("OP_COMP_GLOBALS"); + QCC_PR_DefineName("OP_COMP_LINES"); + QCC_PR_DefineName("OP_COMP_TYPES"); + } + + + + return cnst; +} + +void QCC_PR_Undefine(void) +{ + QCC_PR_SimpleGetToken (); + + QCC_PR_UndefineName(pr_token); +// QCC_PR_ParseError("%s was not defined.", pr_token); +} + +void QCC_PR_ConditionCompilation(void) +{ + char *oldval; + char *d; + char *s; + int quote=false; + CompilerConstant_t *cnst; + + QCC_PR_SimpleGetToken (); + + if (!QCC_PR_SimpleGetToken ()) + QCC_PR_ParseError(ERR_NONAME, "No name defined for compiler constant"); + + cnst = pHash_Get(&compconstantstable, pr_token); + if (cnst) + { + oldval = cnst->value; + Hash_Remove(&compconstantstable, pr_token); + } + else + oldval = NULL; + + cnst = QCC_PR_DefineName(pr_token); + + if (*pr_file_p == '(') + { + s = pr_file_p+1; + while(*pr_file_p++) + { + if (*pr_file_p == ',') + { + if (cnst->numparams >= MAXCONSTANTPARAMS) + QCC_PR_ParseError(ERR_MACROTOOMANYPARMS, "May not have more than %i parameters to a macro", MAXCONSTANTPARAMS); + strncpy(cnst->params[cnst->numparams], s, pr_file_p-s); + cnst->params[cnst->numparams][pr_file_p-s] = '\0'; + cnst->numparams++; + pr_file_p++; + s = pr_file_p; + } + if (*pr_file_p == ')') + { + if (cnst->numparams >= MAXCONSTANTPARAMS) + QCC_PR_ParseError(ERR_MACROTOOMANYPARMS, "May not have more than %i parameters to a macro", MAXCONSTANTPARAMS); + strncpy(cnst->params[cnst->numparams], s, pr_file_p-s); + cnst->params[cnst->numparams][pr_file_p-s] = '\0'; + cnst->numparams++; + pr_file_p++; + break; + } + } + } + else cnst->numparams = -1; + + s = pr_file_p; + d = cnst->value; + while(*s == ' ' || *s == '\t') + s++; + while(1) + { + if( *s == '\\' ) + { + // read over a newline if necessary + if( s[1] == '\n' || s[1] == '\r' ) + { + s++; + QCC_PR_NewLine(false); + *d++ = *s++; + if( s[-1] == '\r' && s[0] == '\n' ) + { + *d++ = *s++; + } + } + } + else if(*s == '\r' || *s == '\n' || *s == '\0') + { + break; + } + + if (!quote && s[0]=='/'&&(s[1]=='/'||s[1]=='*')) + break; + if (*s == '\"') + quote=!quote; + + *d = *s; + d++; + s++; + } + *d = '\0'; + d--; + while(*d<= ' ' && d >= cnst->value) + *d-- = '\0'; + if (strlen(cnst->value) >= sizeof(cnst->value)) //this is too late. + QCC_PR_ParseError(ERR_CONSTANTTOOLONG, "Macro %s too long (%i not %i)", cnst->name, strlen(cnst->value), sizeof(cnst->value)); + + if (oldval) + { //we always warn if it was already defined + //we use different warning codes so that -Wno-mundane can be used to ignore identical redefinitions. + if (strcmp(oldval, cnst->value)) + QCC_PR_ParseWarning(WARN_DUPLICATEPRECOMPILER, "Alternate precompiler definition of %s", pr_token); + else + QCC_PR_ParseWarning(WARN_IDENTICALPRECOMPILER, "Identical precompiler definition of %s", pr_token); + } + + pr_file_p = s; +} + +int QCC_PR_CheakCompConst(void) +{ + char *oldpr_file_p = pr_file_p; + int whitestart; + + CompilerConstant_t *c; + + char *end; + for (end = pr_file_p; ; end++) + { + if (*end <= ' ') + break; + + if (*end == ')' + || *end == '(' + || *end == '+' + || *end == '-' + || *end == '*' + || *end == '/' + || *end == '|' + || *end == '&' + || *end == '=' + || *end == '^' + || *end == '~' + || *end == '[' + || *end == ']' + || *end == '\"' + || *end == '{' + || *end == '}' + || *end == ';' + || *end == ':' + || *end == ',' + || *end == '.' + || *end == '#') + break; + } + strncpy(pr_token, pr_file_p, end-pr_file_p); + pr_token[end-pr_file_p]='\0'; + +// printf("%s\n", pr_token); + c = pHash_Get(&compconstantstable, pr_token); + + if (c && !c->inside) + { + pr_file_p = oldpr_file_p+strlen(c->name); + while(*pr_file_p == ' ' || *pr_file_p == '\t') + pr_file_p++; + if (c->numparams>=0) + { + if (*pr_file_p == '(') + { + int p; + char *start; + char buffer[1024]; + char *paramoffset[MAXCONSTANTPARAMS+1]; + int param=0; + int plevel=0; + + pr_file_p++; + while(*pr_file_p == ' ' || *pr_file_p == '\t') + pr_file_p++; + start = pr_file_p; + while(1) + { + // handle strings correctly by ignoring them + if (*pr_file_p == '\"') + { + do { + pr_file_p++; + } while( (pr_file_p[-1] == '\\' || pr_file_p[0] != '\"') && *pr_file_p && *pr_file_p != '\n' ); + } + if (*pr_file_p == '(') + plevel++; + else if (!plevel && (*pr_file_p == ',' || *pr_file_p == ')')) + { + paramoffset[param++] = start; + start = pr_file_p+1; + if (*pr_file_p == ')') + { + *pr_file_p = '\0'; + pr_file_p++; + break; + } + *pr_file_p = '\0'; + pr_file_p++; + while(*pr_file_p == ' ' || *pr_file_p == '\t') + { + pr_file_p++; + start++; + } + // move back by one char because we move forward by one at the end of the loop + pr_file_p--; + if (param == MAXCONSTANTPARAMS) + QCC_PR_ParseError(ERR_TOOMANYPARAMS, "Too many parameters in macro call"); + } else if (*pr_file_p == ')' ) + plevel--; + else if(*pr_file_p == '\n') + QCC_PR_NewLine(false); + + // see that *pr_file_p = '\0' up there? Must ++ BEFORE checking for !*pr_file_p + pr_file_p++; + if (!*pr_file_p) + QCC_PR_ParseError(ERR_EOF, "EOF on macro call"); + } + if (param < c->numparams) + QCC_PR_ParseError(ERR_TOOFEWPARAMS, "Not enough macro parameters"); + paramoffset[param] = start; + + *buffer = '\0'; + + oldpr_file_p = pr_file_p; + pr_file_p = c->value; + for(;;) + { + whitestart = p = strlen(buffer); + while(*pr_file_p <= ' ') //copy across whitespace + { + if (!*pr_file_p) + break; + buffer[p++] = *pr_file_p++; + } + buffer[p] = 0; + + if(*pr_file_p == '\"') + { + do + { + buffer[p++] = *pr_file_p; + ++pr_file_p; + } while( (pr_file_p[-1] == '\\' || pr_file_p[0] != '\"') && *pr_file_p && *pr_file_p != '\n' ); + buffer[p++] = *pr_file_p; // copy the end-quote too + buffer[p] = 0; + ++pr_file_p; // and skip it + continue; + } + else if (*pr_file_p == '#') //if you ask for #a##b you will be shot. use #a #b instead, or chain macros. + { + if (pr_file_p[1] == '#') + { //concatinate (srip out whitespace) + buffer[whitestart] = '\0'; + pr_file_p+=2; + } + else + { //stringify + pr_file_p++; + pr_file_p = QCC_COM_Parse2(pr_file_p); + if (!pr_file_p) + break; + + for (p = 0; p < param; p++) + { + if (!STRCMP(qcc_token, c->params[p])) + { + strcat(buffer, "\""); + strcat(buffer, paramoffset[p]); + strcat(buffer, "\""); + break; + } + } + if (p == param) + { + strcat(buffer, "#"); + strcat(buffer, qcc_token); + //QCC_PR_ParseWarning(0, "Stringification ignored"); + } + continue; //already did this one + } + } + + pr_file_p = QCC_COM_Parse2(pr_file_p); + if (!pr_file_p) + break; + + for (p = 0; p < param; p++) + { + if (!STRCMP(qcc_token, c->params[p])) + { + strcat(buffer, paramoffset[p]); + break; + } + } + if (p == param) + strcat(buffer, qcc_token); + } + + for (p = 0; p < param-1; p++) + paramoffset[p][strlen(paramoffset[p])] = ','; + paramoffset[p][strlen(paramoffset[p])] = ')'; + + pr_file_p = oldpr_file_p; + if (!*buffer) + expandedemptymacro = true; + QCC_PR_IncludeChunkEx(buffer, true, NULL, c); + } + else + QCC_PR_ParseError(ERR_TOOFEWPARAMS, "Macro without opening brace"); + } + else + { + if (!*c->value) + expandedemptymacro = true; + QCC_PR_IncludeChunkEx(c->value, false, NULL, c); + } + + QCC_PR_Lex(); + return true; + } + + if (!strncmp(pr_file_p, "__TIME__", 8)) + { + static char retbuf[128]; + + time_t long_time; + time( &long_time ); + strftime( retbuf, sizeof(retbuf), + "\"%H:%M\"", localtime( &long_time )); + + pr_file_p = retbuf; + QCC_PR_Lex(); //translate the macro's value + pr_file_p = oldpr_file_p+8; + + return true; + } + if (!strncmp(pr_file_p, "__DATE__", 8)) + { + static char retbuf[128]; + + time_t long_time; + time( &long_time ); + strftime( retbuf, sizeof(retbuf), + "\"%a %d %b %Y\"", localtime( &long_time )); + + pr_file_p = retbuf; + QCC_PR_Lex(); //translate the macro's value + pr_file_p = oldpr_file_p+8; + + return true; + } + if (!strncmp(pr_file_p, "__FILE__", 8)) + { + static char retbuf[256]; + sprintf(retbuf, "\"%s\"", strings + s_file); + pr_file_p = retbuf; + QCC_PR_Lex(); //translate the macro's value + pr_file_p = oldpr_file_p+8; + + return true; + } + if (!strncmp(pr_file_p, "__LINE__", 8)) + { + static char retbuf[256]; + sprintf(retbuf, "\"%i\"", pr_source_line); + pr_file_p = retbuf; + QCC_PR_Lex(); //translate the macro's value + pr_file_p = oldpr_file_p+8; + return true; + } + if (!strncmp(pr_file_p, "__FUNC__", 8)) + { + static char retbuf[256]; + sprintf(retbuf, "\"%s\"",pr_scope->name); + pr_file_p = retbuf; + QCC_PR_Lex(); //translate the macro's value + pr_file_p = oldpr_file_p+8; + return true; + } + if (!strncmp(pr_file_p, "__NULL__", 8)) + { + static char retbuf[256]; + sprintf(retbuf, "0i"); + pr_file_p = retbuf; + QCC_PR_Lex(); //translate the macro's value + pr_file_p = oldpr_file_p+8; + return true; + } + return false; +} + +char *QCC_PR_CheakCompConstString(char *def) +{ + char *s; + + CompilerConstant_t *c; + + c = pHash_Get(&compconstantstable, def); + + if (c) + { + s = QCC_PR_CheakCompConstString(c->value); + return s; + } + return def; +} + +CompilerConstant_t *QCC_PR_CheckCompConstDefined(char *def) +{ + CompilerConstant_t *c = pHash_Get(&compconstantstable, def); + return c; + /*int a; + for (a = 0; a < numCompilerConstants; a++) + { + if (!strncmp(def, CompilerConstant[a].name, CompilerConstant[a].namelen+1)) + return &CompilerConstant[a]; + } + return NULL; + */ +} + +//============================================================================ + +/* +============== +PR_Lex + +Sets pr_token, pr_token_type, and possibly pr_immediate and pr_immediate_type +============== +*/ +void QCC_PR_Lex (void) +{ + int c; + + pr_token[0] = 0; + + if (!pr_file_p) + { + if (QCC_PR_UnInclude()) + { + QCC_PR_Lex(); + return; + } + pr_token_type = tt_eof; + return; + } + + QCC_PR_LexWhitespace (); + + if (!pr_file_p) + { + if (QCC_PR_UnInclude()) + { + QCC_PR_Lex(); + return; + } + pr_token_type = tt_eof; + return; + } + + c = *pr_file_p; + + if (!c) + { + if (QCC_PR_UnInclude()) + { + QCC_PR_Lex(); + return; + } + pr_token_type = tt_eof; + return; + } + +// handle quoted strings as a unit + if (c == '\"') + { + QCC_PR_LexString (); + return; + } + +// handle quoted vectors as a unit + if (c == '\'') + { + QCC_PR_LexVector (); + return; + } + +// if the first character is a valid identifier, parse until a non-id +// character is reached + if ( c == '~' || c == '%') //let's see which one we make into an operator first... possibly both... + { + QCC_PR_ParseWarning(0, "~ or %% prefixes to denote integers are deprecated. Please use a postfix of 'i'"); + pr_file_p++; + pr_token_type = tt_immediate; + pr_immediate_type = type_integer; + pr_immediate._int = QCC_PR_LexInteger (); + return; + } + if ( c == '0' && pr_file_p[1] == 'x') + { + pr_token_type = tt_immediate; + QCC_PR_LexNumber(); + return; + } + if ( (c == '.'&&pr_file_p[1] >='0' && pr_file_p[1] <= '9') || (c >= '0' && c <= '9') || ( c=='-' && pr_file_p[1]>='0' && pr_file_p[1] <='9') ) + { + pr_token_type = tt_immediate; + QCC_PR_LexNumber (); + return; + } + + if (c == '#' && !(pr_file_p[1]=='-' || (pr_file_p[1]>='0' && pr_file_p[1] <='9'))) //hash and not number + { + pr_file_p++; + if (!QCC_PR_CheakCompConst()) + { + if (!QCC_PR_SimpleGetToken()) + strcpy(pr_token, "unknown"); + QCC_PR_ParseError(ERR_CONSTANTNOTDEFINED, "Explicit precompiler usage when not defined %s", pr_token); + } + else + if (pr_token_type == tt_eof) + QCC_PR_Lex(); + + return; + } + + if ( (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' ) + { + if (flag_hashonly || !QCC_PR_CheakCompConst()) //look for a macro. + QCC_PR_LexName (); + else + if (pr_token_type == tt_eof) + { + if (QCC_PR_UnInclude()) + { + QCC_PR_Lex(); + return; + } + pr_token_type = tt_eof; + } + return; + } + + if (c == '$') + { + QCC_PR_LexGrab (); + return; + } + +// parse symbol strings until a non-symbol is found + QCC_PR_LexPunctuation (); +} + +//============================================================================= + + +void QCC_PR_ParsePrintDef (int type, QCC_def_t *def) +{ + if (qccwarningdisabled[type]) + return; + if (def->s_file) + { + if (flag_msvcstyle) + printf ("%s(%i) : %s is defined here\n", strings + def->s_file, def->s_line, def->name); + else + printf ("%s:%i: %s is defined here\n", strings + def->s_file, def->s_line, def->name); + } +} +void *errorscope; +void QCC_PR_PrintScope (void) +{ + if (pr_scope) + { + if (errorscope != pr_scope) + printf ("in function %s (line %i),\n", pr_scope->name, pr_scope->s_line); + errorscope = pr_scope; + } + else + { + if (errorscope) + printf ("at global scope,\n"); + errorscope = NULL; + } +} +void QCC_PR_ResetErrorScope(void) +{ + errorscope = NULL; +} +/* +============ +PR_ParseError + +Aborts the current file load +============ +*/ +#ifndef QCC +void editbadfile(char *file, int line); +#endif +void VARGS QCC_PR_ParseError (int errortype, char *error, ...) +{ + va_list argptr; + char string[1024]; + + va_start (argptr,error); + QC_vsnprintf (string,sizeof(string)-1, error,argptr); + va_end (argptr); + +#ifndef QCC + editbadfile(strings+s_file, pr_source_line); +#endif + + QCC_PR_PrintScope(); + if (flag_msvcstyle) + printf ("%s(%i) : error: %s\n", strings + s_file, pr_source_line, string); + else + printf ("%s:%i: error: %s\n", strings + s_file, pr_source_line, string); + + longjmp (pr_parse_abort, 1); +} +void VARGS QCC_PR_ParseErrorPrintDef (int errortype, QCC_def_t *def, char *error, ...) +{ + va_list argptr; + char string[1024]; + + va_start (argptr,error); + QC_vsnprintf (string,sizeof(string)-1, error,argptr); + va_end (argptr); + +#ifndef QCC + editbadfile(strings+s_file, pr_source_line); +#endif + QCC_PR_PrintScope(); + if (flag_msvcstyle) + printf ("%s(%i) : error: %s\n", strings + s_file, pr_source_line, string); + else + printf ("%s:%i: error: %s\n", strings + s_file, pr_source_line, string); + + QCC_PR_ParsePrintDef(WARN_ERROR, def); + + longjmp (pr_parse_abort, 1); +} +void VARGS QCC_PR_ParseWarning (int type, char *error, ...) +{ + va_list argptr; + char string[1024]; + + if (type < ERR_PARSEERRORS && qccwarningdisabled[type]) + return; + + va_start (argptr,error); + QC_vsnprintf (string,sizeof(string)-1, error,argptr); + va_end (argptr); + + QCC_PR_PrintScope(); + if (type >= ERR_PARSEERRORS) + { + if (flag_msvcstyle) + printf ("%s(%i) : error: %s\n", strings + s_file, pr_source_line, string); + else + printf ("%s:%i: error: %s\n", strings + s_file, pr_source_line, string); + pr_error_count++; + } + else + { + if (flag_msvcstyle) + printf ("%s(%i) : warning: %s\n", strings + s_file, pr_source_line, string); + else + printf ("%s:%i: warning: %s\n", strings + s_file, pr_source_line, string); + pr_warning_count++; + } +} + +void VARGS QCC_PR_Warning (int type, char *file, int line, char *error, ...) +{ + va_list argptr; + char string[1024]; + + if (qccwarningdisabled[type]) + return; + + va_start (argptr,error); + QC_vsnprintf (string,sizeof(string)-1, error,argptr); + va_end (argptr); + + QCC_PR_PrintScope(); + if (file) + { + if (flag_msvcstyle) + printf ("%s(%i) : warning: %s\n", file, line, string); + else + printf ("%s:%i: warning: %s\n", file, line, string); + } + else + printf ("warning: %s\n", string); + pr_warning_count++; +} + + +/* +============= +PR_Expect + +Issues an error if the current token isn't equal to string +Gets the next token +============= +*/ +#ifndef COMMONINLINES +void QCC_PR_Expect (char *string) +{ + if (STRCMP (string, pr_token)) + QCC_PR_ParseError (ERR_EXPECTED, "expected %s, found %s",string, pr_token); + QCC_PR_Lex (); +} +#endif + + +/* +============= +PR_Check + +Returns true and gets the next token if the current token equals string +Returns false and does nothing otherwise +============= +*/ +#ifndef COMMONINLINES +pbool QCC_PR_CheckToken (char *string) +{ + if (pr_token_type != tt_punct) + return false; + + if (STRCMP (string, pr_token)) + return false; + + QCC_PR_Lex (); + return true; +} + +pbool QCC_PR_CheckImmediate (char *string) +{ + if (pr_token_type != tt_immediate) + return false; + + if (STRCMP (string, pr_token)) + return false; + + QCC_PR_Lex (); + return true; +} + +pbool QCC_PR_CheckName(char *string) +{ + if (pr_token_type != tt_name) + return false; + if (flag_caseinsensative) + { + if (stricmp (string, pr_token)) + return false; + } + else + { + if (STRCMP(string, pr_token)) + return false; + } + QCC_PR_Lex (); + return true; +} + +pbool QCC_PR_CheckKeyword(int keywordenabled, char *string) +{ + if (!keywordenabled) + return false; + if (flag_caseinsensative) + { + if (stricmp (string, pr_token)) + return false; + } + else + { + if (STRCMP(string, pr_token)) + return false; + } + QCC_PR_Lex (); + return true; +} +#endif + + +/* +============ +PR_ParseName + +Checks to see if the current token is a valid name +============ +*/ +char *QCC_PR_ParseName (void) +{ + static char ident[MAX_NAME]; + char *ret; + + if (pr_token_type != tt_name) + QCC_PR_ParseError (ERR_NOTANAME, "\"%s\" - not a name", pr_token); + if (strlen(pr_token) >= MAX_NAME-1) + QCC_PR_ParseError (ERR_NAMETOOLONG, "name too long"); + strcpy (ident, pr_token); + QCC_PR_Lex (); + + ret = qccHunkAlloc(strlen(ident)+1); + strcpy(ret, ident); + return ret; +// return ident; +} + +/* +============ +PR_FindType + +Returns a preexisting complex type that matches the parm, or allocates +a new one and copies it out. +============ +*/ + +//0 if same +QCC_type_t *QCC_PR_NewType (char *name, int basictype); +int typecmp(QCC_type_t *a, QCC_type_t *b) +{ + if (a == b) + return 0; + if (!a || !b) + return 1; //different (^ and not both null) + + if (a->type != b->type) + return 1; + if (a->num_parms != b->num_parms) + return 1; + + if (a->size != b->size) + return 1; +// if (STRCMP(a->name, b->name)) //This isn't 100% clean. +// return 1; + + if (typecmp(a->aux_type, b->aux_type)) + return 1; + + if (a->param || b->param) + { + a = a->param; + b = b->param; + + while(a || b) + { + if (typecmp(a, b)) + return 1; + + a=a->next; + b=b->next; + } + } + + return 0; +} + +QCC_type_t *QCC_PR_DuplicateType(QCC_type_t *in) +{ + QCC_type_t *out, *op, *ip; + if (!in) + return NULL; + + out = QCC_PR_NewType(in->name, in->type); + out->aux_type = QCC_PR_DuplicateType(in->aux_type); + out->param = QCC_PR_DuplicateType(in->param); + ip = in->param; + op = NULL; + while(ip) + { + if (!op) + out->param = op = QCC_PR_DuplicateType(ip); + else + op = (op->next = QCC_PR_DuplicateType(ip)); + ip = ip->next; + } + out->size = in->size; + out->num_parms = in->num_parms; + out->ofs = in->ofs; + out->name = in->name; + out->parentclass = in->parentclass; + + return out; +} + +char *TypeName(QCC_type_t *type) +{ + static char buffer[2][512]; + static int op; + char *ret; + + + op++; + ret = buffer[op&1]; + if (type->type == ev_field) + { + type = type->aux_type; + *ret++ = '.'; + } + *ret = 0; + + if (type->type == ev_function) + { + strcat(ret, type->aux_type->name); + strcat(ret, " ("); + type = type->param; + while(type) + { + strcat(ret, type->name); + type = type->next; + + if (type) + strcat(ret, ", "); + } + strcat(ret, ")"); + } + else if (type->type == ev_entity && type->parentclass) + { + ret = buffer[op&1]; + *ret = 0; + strcat(ret, "class "); + strcat(ret, type->name); +/* strcat(ret, " {"); + type = type->param; + while(type) + { + strcat(ret, type->name); + type = type->next; + + if (type) + strcat(ret, ", "); + } + strcat(ret, "}"); +*/ + } + else + strcpy(ret, type->name); + + return buffer[op&1]; +} +//#define typecmp(a, b) (a && ((a)->type==(b)->type) && !STRCMP((a)->name, (b)->name)) + +QCC_type_t *QCC_PR_FindType (QCC_type_t *type) +{ + int t; + for (t = 0; t < numtypeinfos; t++) + { +// check = &qcc_typeinfo[t]; + if (typecmp(&qcc_typeinfo[t], type)) + continue; + + +// c2 = check->next; +// n2 = type->next; +// for (i=0 ; n2&&c2 ; i++) +// { +// if (!typecmp((c2), (n2))) +// break; +// c2=c2->next; +// n2=n2->next; +// } + +// if (n2==NULL&&c2==NULL) + { + return &qcc_typeinfo[t]; + } + } +QCC_Error(ERR_INTERNAL, "Error with type"); + + return type; +} +/* +QCC_type_t *QCC_PR_NextSubType(QCC_type_t *type, QCC_type_t *prev) +{ + int p; + if (!prev) + return type->next; + + for (p = prev->num_parms; p; p--) + prev = QCC_PR_NextSubType(prev, NULL); + if (prev->num_parms) + + switch(prev->type) + { + case ev_function: + + } + + return prev->next; +} +*/ + +QCC_type_t *QCC_TypeForName(char *name) +{ + int i; + + for (i = 0; i < numtypeinfos; i++) + { + if (!STRCMP(qcc_typeinfo[i].name, name)) + { + return &qcc_typeinfo[i]; + } + } + + return NULL; +} + +/* +============ +PR_SkipToSemicolon + +For error recovery, also pops out of nested braces +============ +*/ +void QCC_PR_SkipToSemicolon (void) +{ + do + { + if (!pr_bracelevel && QCC_PR_CheckToken (";")) + return; + QCC_PR_Lex (); + } while (pr_token_type != tt_eof); +} + + +/* +============ +PR_ParseType + +Parses a variable type, including field and functions types +============ +*/ +#ifdef MAX_EXTRA_PARMS +char pr_parm_names[MAX_PARMS+MAX_EXTRA_PARMS][MAX_NAME]; +#else +char pr_parm_names[MAX_PARMS][MAX_NAME]; +#endif + +pbool recursivefunctiontype; + +QCC_type_t *QCC_PR_NewType (char *name, int basictype); +//expects a ( to have already been parsed. +QCC_type_t *QCC_PR_ParseFunctionType (int newtype, QCC_type_t *returntype) +{ + QCC_type_t *ftype, *ptype, *nptype; + char *name; + int definenames = !recursivefunctiontype; + + recursivefunctiontype++; + + ftype = QCC_PR_NewType(type_function->name, ev_function); + + ftype->aux_type = returntype; // return type + ftype->num_parms = 0; + ptype = NULL; + + + if (!QCC_PR_CheckToken (")")) + { + if (QCC_PR_CheckToken ("...")) + ftype->num_parms = -1; // variable args + else + do + { + if (ftype->num_parms>=MAX_PARMS+MAX_EXTRA_PARMS) + QCC_PR_ParseError(ERR_TOOMANYTOTALPARAMETERS, "Too many parameters. Sorry. (limit is %i)\n", MAX_PARMS+MAX_EXTRA_PARMS); + + if (QCC_PR_CheckToken ("...")) + { + ftype->num_parms = (ftype->num_parms * -1) - 1; + break; + } + + nptype = QCC_PR_ParseType(true); + + if (nptype->type == ev_void) + break; + if (!ptype) + { + ptype = nptype; + ftype->param = ptype; + } + else + { + ptype->next = nptype; + ptype = ptype->next; + } +// type->name = "FUNC PARAMETER"; + + + if (STRCMP(pr_token, ",") && STRCMP(pr_token, ")")) + { + name = QCC_PR_ParseName (); + if (definenames) + strcpy (pr_parm_names[ftype->num_parms], name); + } + else if (definenames) + strcpy (pr_parm_names[ftype->num_parms], ""); + ftype->num_parms++; + } while (QCC_PR_CheckToken (",")); + + QCC_PR_Expect (")"); + } + recursivefunctiontype--; + if (newtype) + return ftype; + return QCC_PR_FindType (ftype); +} +QCC_type_t *QCC_PR_ParseFunctionTypeReacc (int newtype, QCC_type_t *returntype) +{ + QCC_type_t *ftype, *ptype, *nptype; + char *name; + char argname[64]; + int definenames = !recursivefunctiontype; + + recursivefunctiontype++; + + ftype = QCC_PR_NewType(type_function->name, ev_function); + + ftype->aux_type = returntype; // return type + ftype->num_parms = 0; + ptype = NULL; + + + if (!QCC_PR_CheckToken (")")) + { + if (QCC_PR_CheckToken ("...")) + ftype->num_parms = -1; // variable args + else + do + { + if (ftype->num_parms>=MAX_PARMS+MAX_EXTRA_PARMS) + QCC_PR_ParseError(ERR_TOOMANYTOTALPARAMETERS, "Too many parameters. Sorry. (limit is %i)\n", MAX_PARMS+MAX_EXTRA_PARMS); + + if (QCC_PR_CheckToken ("...")) + { + ftype->num_parms = (ftype->num_parms * -1) - 1; + break; + } + + if (QCC_PR_CheckName("arg")) + { + sprintf(argname, "arg%i", ftype->num_parms); + name = argname; + nptype = QCC_PR_NewType("Variant", ev_variant); + } + else if (QCC_PR_CheckName("vect")) //this can only be of vector sizes, so... + { + sprintf(argname, "arg%i", ftype->num_parms); + name = argname; + nptype = QCC_PR_NewType("Vector", ev_vector); + } + else + { + name = QCC_PR_ParseName(); + QCC_PR_Expect(":"); + nptype = QCC_PR_ParseType(true); + } + + if (nptype->type == ev_void) + break; + if (!ptype) + { + ptype = nptype; + ftype->param = ptype; + } + else + { + ptype->next = nptype; + ptype = ptype->next; + } +// type->name = "FUNC PARAMETER"; + + if (definenames) + strcpy (pr_parm_names[ftype->num_parms], name); + ftype->num_parms++; + } while (QCC_PR_CheckToken (";")); + + QCC_PR_Expect (")"); + } + recursivefunctiontype--; + if (newtype) + return ftype; + return QCC_PR_FindType (ftype); +} +QCC_type_t *QCC_PR_PointerType (QCC_type_t *pointsto) +{ + QCC_type_t *ptype; + char name[128]; + sprintf(name, "*%s", pointsto->name); + ptype = QCC_PR_NewType(name, ev_pointer); + ptype->aux_type = pointsto; + return QCC_PR_FindType (ptype); +} +QCC_type_t *QCC_PR_FieldType (QCC_type_t *pointsto) +{ + QCC_type_t *ptype; + char name[128]; + sprintf(name, "FIELD TYPE(%s)", pointsto->name); + ptype = QCC_PR_NewType(name, ev_field); + ptype->aux_type = pointsto; + ptype->size = ptype->aux_type->size; + return QCC_PR_FindType (ptype); +} + +pbool type_inlinefunction; +QCC_type_t *QCC_PR_ParseType (int newtype) +{ + QCC_type_t *newparm; + QCC_type_t *newt; + QCC_type_t *type; + char *name; + int i; + + type_inlinefunction = false; //doesn't really matter so long as its not from an inline function type + +// int ofs; + + if (QCC_PR_CheckToken ("..")) //so we don't end up with the user specifying '. .vector blah' (hexen2 added the .. token for array ranges) + { + newt = QCC_PR_NewType("FIELD TYPE", ev_field); + newt->aux_type = QCC_PR_ParseType (false); + + newt->size = newt->aux_type->size; + + newt = QCC_PR_FindType (newt); + + type = QCC_PR_NewType("FIELD TYPE", ev_field); + type->aux_type = newt; + + type->size = type->aux_type->size; + + if (newtype) + return type; + return QCC_PR_FindType (type); + } + if (QCC_PR_CheckToken (".")) + { + newt = QCC_PR_NewType("FIELD TYPE", ev_field); + newt->aux_type = QCC_PR_ParseType (false); + + newt->size = newt->aux_type->size; + + if (newtype) + return newt; + return QCC_PR_FindType (newt); + } + + name = QCC_PR_CheakCompConstString(pr_token); + + if (QCC_PR_CheckKeyword (keyword_class, "class")) + { +// int parms; + QCC_type_t *fieldtype; + char membername[2048]; + char *classname = QCC_PR_ParseName(); + int forwarddeclaration; + + newt = 0; + + /* Don't advance the line number yet */ + forwarddeclaration = pr_token[0] == ';'; + + /* Look to see if this type is already defined */ + for(i=0;inum_parms != 0) + QCC_PR_ParseError(ERR_REDECLARATION, "Redeclaration of class %s", classname); + + if (!newt) + newt = QCC_PR_NewType(classname, ev_entity); + + newt->size=type_entity->size; + + type = NULL; + + if (forwarddeclaration) + { + QCC_PR_CheckToken(";"); + return NULL; + } + + + + if (QCC_PR_CheckToken(":")) + { + char *parentname = QCC_PR_ParseName(); + newt->parentclass = QCC_TypeForName(parentname); + if (!newt->parentclass) + QCC_PR_ParseError(ERR_NOTANAME, "Parent class %s was not defined", parentname); + } + else + newt->parentclass = type_entity; + + + QCC_PR_Expect("{"); + if (QCC_PR_CheckToken(",")) + QCC_PR_ParseError(ERR_NOTANAME, "member missing name"); + while (!QCC_PR_CheckToken("}")) + { +// if (QCC_PR_CheckToken(",")) +// type->next = QCC_PR_NewType(type->name, type->type); +// else + newparm = QCC_PR_ParseType(true); + + if (newparm->type == ev_struct || newparm->type == ev_union) //we wouldn't be able to handle it. + QCC_PR_ParseError(ERR_INTERNAL, "Struct or union in class %s", classname); + + if (!QCC_PR_CheckToken(";")) + { + newparm->name = QCC_CopyString(pr_token)+strings; + QCC_PR_Lex(); + if (QCC_PR_CheckToken("[")) + { + type->next->size*=atoi(pr_token); + QCC_PR_Lex(); + QCC_PR_Expect("]"); + } + QCC_PR_CheckToken(";"); + } + else + newparm->name = QCC_CopyString("")+strings; + + sprintf(membername, "%s::"MEMBERFIELDNAME, classname, newparm->name); + fieldtype = QCC_PR_NewType(newparm->name, ev_field); + fieldtype->aux_type = newparm; + fieldtype->size = newparm->size; + QCC_PR_GetDef(fieldtype, membername, pr_scope, 2, 1, false); + + + newparm->ofs = 0;//newt->size; + newt->num_parms++; + + if (type) + type->next = newparm; + else + newt->param = newparm; + + type = newparm; + } + + + QCC_PR_Expect(";"); + return NULL; + } + if (QCC_PR_CheckKeyword (keyword_struct, "struct")) + { + newt = QCC_PR_NewType("struct", ev_struct); + newt->size=0; + QCC_PR_Expect("{"); + + type = NULL; + if (QCC_PR_CheckToken(",")) + QCC_PR_ParseError(ERR_NOTANAME, "element missing name"); + + newparm = NULL; + while (!QCC_PR_CheckToken("}")) + { + if (QCC_PR_CheckToken(",")) + { + if (!newparm) + QCC_PR_ParseError(ERR_NOTANAME, "element missing type"); + newparm = QCC_PR_NewType(newparm->name, newparm->type); + } + else + newparm = QCC_PR_ParseType(true); + + if (!QCC_PR_CheckToken(";")) + { + newparm->name = QCC_CopyString(pr_token)+strings; + QCC_PR_Lex(); + if (QCC_PR_CheckToken("[")) + { + newparm->size*=atoi(pr_token); + QCC_PR_Lex(); + QCC_PR_Expect("]"); + } + QCC_PR_CheckToken(";"); + } + else + newparm->name = QCC_CopyString("")+strings; + newparm->ofs = newt->size; + newt->size += newparm->size; + newt->num_parms++; + + if (type) + type->next = newparm; + else + newt->param = newparm; + type = newparm; + } + return newt; + } + if (QCC_PR_CheckKeyword (keyword_union, "union")) + { + newt = QCC_PR_NewType("union", ev_union); + newt->size=0; + QCC_PR_Expect("{"); + + type = NULL; + if (QCC_PR_CheckToken(",")) + QCC_PR_ParseError(ERR_NOTANAME, "element missing name"); + newparm = NULL; + while (!QCC_PR_CheckToken("}")) + { + if (QCC_PR_CheckToken(",")) + { + if (!newparm) + QCC_PR_ParseError(ERR_NOTANAME, "element missing type"); + newparm = QCC_PR_NewType(newparm->name, newparm->type); + } + else + newparm = QCC_PR_ParseType(true); + if (QCC_PR_CheckToken(";")) + newparm->name = QCC_CopyString("")+strings; + else + { + newparm->name = QCC_CopyString(pr_token)+strings; + QCC_PR_Lex(); + QCC_PR_Expect(";"); + } + newparm->ofs = 0; + if (newparm->size > newt->size) + newt->size = newparm->size; + newt->num_parms++; + + if (type) + type->next = newparm; + else + newt->param = newparm; + type = newparm; + } + return newt; + } + type = NULL; + for (i = 0; i < numtypeinfos; i++) + { + if (!STRCMP(qcc_typeinfo[i].name, name)) + { + type = &qcc_typeinfo[i]; + break; + } + } + + if (i == numtypeinfos) + { + if (!*name) + return NULL; + if (!stricmp("Void", name)) + type = type_void; + else if (!stricmp("Real", name)) + type = type_float; + else if (!stricmp("Vector", name)) + type = type_vector; + else if (!stricmp("Object", name)) + type = type_entity; + else if (!stricmp("String", name)) + type = type_string; + else if (!stricmp("PFunc", name)) + type = type_function; + else + { + QCC_PR_ParseError (ERR_NOTATYPE, "\"%s\" is not a type", name); + type = type_float; // shut up compiler warning + } + } + QCC_PR_Lex (); + + if (QCC_PR_CheckToken ("(")) //this is followed by parameters. Must be a function. + { + type_inlinefunction = true; + return QCC_PR_ParseFunctionType(newtype, type); + } + else + { + if (newtype) + { + type = QCC_PR_DuplicateType(type); + } + + return type; + } +} + +#endif + + + diff --git a/misc/mediasource/extra/fteqcc-src/qccgui.c b/misc/mediasource/extra/fteqcc-src/qccgui.c new file mode 100644 index 00000000..e6fb7bd0 --- /dev/null +++ b/misc/mediasource/extra/fteqcc-src/qccgui.c @@ -0,0 +1,2343 @@ +#include +#include +#include +#include +#include +#include + +#include "qcc.h" +#include "gui.h" + + + + +/* +============== +LoadFile +============== +*/ +unsigned char *QCC_ReadFile (char *fname, void *buffer, int len) +{ + long length; + FILE *f; + f = fopen(fname, "rb"); + if (!f) + return NULL; + length = fread(buffer, 1, len, f); + fclose(f); + + if (length != len) + return NULL; + + return buffer; +} +int QCC_FileSize (char *fname) +{ + long length; + FILE *f; + f = fopen(fname, "rb"); + if (!f) + return -1; + fseek(f, 0, SEEK_END); + length = ftell(f); + fclose(f); + + return length; +} + +pbool QCC_WriteFile (char *name, void *data, int len) +{ + long length; + FILE *f; + f = fopen(name, "wb"); + if (!f) + return false; + length = fwrite(data, 1, len, f); + fclose(f); + + if (length != len) + return false; + + return true; +} + +#undef printf +#undef Sys_Error + +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); +} + + +FILE *logfile; +int logprintf(const char *format, ...) +{ + va_list argptr; + static char string[1024]; + + va_start (argptr, format); +#ifdef _WIN32 + _vsnprintf (string,sizeof(string)-1, format,argptr); +#else + vsnprintf (string,sizeof(string), format,argptr); +#endif + va_end (argptr); + + printf("%s", string); + if (logfile) + fputs(string, logfile); + + return 0; +} + + + + + + + + + + + +#define Edit_Redo(hwndCtl) ((BOOL)(DWORD)SNDMSG((hwndCtl), EM_REDO, 0L, 0L)) + + +#define MAIN_WINDOW_CLASS_NAME "FTEMAINWINDOW" +#define MDI_WINDOW_CLASS_NAME "FTEMDIWINDOW" +#define EDIT_WINDOW_CLASS_NAME "FTEEDITWINDOW" +#define OPTIONS_WINDOW_CLASS_NAME "FTEOPTIONSWINDOW" + +#define EM_GETSCROLLPOS (WM_USER + 221) +#define EM_SETSCROLLPOS (WM_USER + 222) + + + +int GUIprintf(const char *msg, ...); +void GUIPrint(HWND wnd, char *msg); + +char finddef[256]; + +void RunCompiler(char *args); + +HINSTANCE ghInstance; +HMODULE richedit; + +pbool resetprogssrc; //progs.src was changed, reload project info. + + +HWND mainwindow; +HWND mdibox; +HWND outputwindow; +HWND outputbox; +HWND projecttree; +HWND gotodefbox; +HWND gotodefaccept; + +FILE *logfile; + +struct{ + char *text; + HWND hwnd; + int washit; +} buttons[] = { + {"Compile"}, + {"Edit"}, + {"Options"}, + {"Quit"} +}; + +#define ID_COMPILE 0 +#define ID_EDIT 1 +#define ID_OPTIONS 2 +#define ID_QUIT 3 + +#define NUMBUTTONS sizeof(buttons)/sizeof(buttons[0]) + + + +void GUI_DialogPrint(char *title, char *text) +{ + MessageBox(mainwindow, text, title, 0); +} + +HWND CreateAnEditControl(HWND parent) +{ + HWND newc; + + if (!richedit) + richedit = LoadLibrary("RICHED32.DLL"); + + newc=CreateWindowEx(WS_EX_CLIENTEDGE, + richedit?RICHEDIT_CLASS:"EDIT", + "", + WS_CHILD /*| ES_READONLY*/ | WS_VISIBLE | + WS_HSCROLL | WS_VSCROLL | ES_LEFT | ES_WANTRETURN | + ES_MULTILINE | ES_AUTOVSCROLL, + 0, 0, 0, 0, + parent, + NULL, + ghInstance, + NULL); + + if (!newc) + newc=CreateWindowEx(WS_EX_CLIENTEDGE, + richedit?RICHEDIT_CLASS10A:"EDIT", //fall back to the earlier version + "", + WS_CHILD /*| ES_READONLY*/ | WS_VISIBLE | + WS_HSCROLL | WS_VSCROLL | ES_LEFT | ES_WANTRETURN | + ES_MULTILINE | ES_AUTOVSCROLL, + 0, 0, 0, 0, + parent, + NULL, + ghInstance, + NULL); + + if (!newc) + { //you've not got RICHEDIT installed properly, I guess + FreeLibrary(richedit); + richedit = NULL; + newc=CreateWindowEx(WS_EX_CLIENTEDGE, + "EDIT", + "", + WS_CHILD /*| ES_READONLY*/ | WS_VISIBLE | + WS_HSCROLL | WS_VSCROLL | ES_LEFT | ES_WANTRETURN | + ES_MULTILINE | ES_AUTOVSCROLL, + 0, 0, 0, 0, + parent, + NULL, + ghInstance, + NULL); + } + + //go to lucidia console, 10pt + { + CHARFORMAT cf; + memset(&cf, 0, sizeof(cf)); + cf.cbSize = sizeof(cf); + cf.dwMask = CFM_BOLD | CFM_FACE;// | CFM_SIZE; + strcpy(cf.szFaceName, "Lucida Console"); + cf.yHeight = 5; + + SendMessage(newc, EM_SETCHARFORMAT, SCF_ALL, (WPARAM)&cf); + } + + if (richedit) + { + SendMessage(newc, EM_EXLIMITTEXT, 0, 1<<20); + } + + ShowWindow(newc, SW_SHOW); + + return newc; +} + + + + +enum { + IDM_OPENDOCU=32, + IDM_OPENNEW, + IDM_GOTODEF, + IDM_SAVE, + IDM_FIND, + IDM_QUIT, + IDM_UNDO, + IDM_REDO, + IDM_ABOUT, + IDM_HIGHTLIGHT, + IDM_CASCADE, + IDM_TILE_HORIZ, + IDM_TILE_VERT, + + IDI_O_LEVEL0, + IDI_O_LEVEL1, + IDI_O_LEVEL2, + IDI_O_LEVEL3, + IDI_O_DEFAULT, + IDI_O_DEBUG, + IDI_O_CHANGE_PROGS_SRC, + IDI_O_ADDITIONALPARAMETERS, + IDI_O_OPTIMISATION, + IDI_O_COMPILER_FLAG, + IDI_O_USE, + IDI_O_APPLY, + IDI_O_TARGET, + IDI_O_SYNTAX_HIGHLIGHTING, + + IDM_FIRSTCHILD +}; + + +typedef struct editor_s { + char filename[MAX_PATH]; //abs + HWND window; + HWND editpane; + struct editor_s *next; +} editor_t; + +editor_t *editors; + +int EditorSave(editor_t *edit); +void EditFile(char *name, int line); +pbool EditorModified(editor_t *e); +int Rehighlight(editor_t *edit); + +void QueryOpenFile(void) +{ + char filename[MAX_PATH]; + char oldpath[MAX_PATH+10]; + OPENFILENAME ofn; + memset(&ofn, 0, sizeof(ofn)); + ofn.lStructSize = sizeof(ofn); + ofn.hInstance = ghInstance; + ofn.lpstrFile = filename; + ofn.nMaxFile = sizeof(filename)-1; + memset(filename, 0, sizeof(filename)); + GetCurrentDirectory(sizeof(oldpath)-1, oldpath); + if (GetOpenFileName(&ofn)) + EditFile(filename, -1); + SetCurrentDirectory(oldpath); +} + +//IDM_ stuff that needs no active menu +void GenericMenu(WPARAM wParam) +{ + switch(LOWORD(wParam)) + { + case IDM_OPENNEW: + QueryOpenFile(); + break; + + case IDM_ABOUT: + MessageBox(NULL, "FTE QuakeC Compiler\nWritten by Forethough Entertainment.\nBasically that means it was written by Spike.\n\nIt has a few cool features, like a useful IDE.\n\nSupports:\nPrecompiler (with macros)\nArrays\n+= / -= / *= / /= operations.\nSwitch statements\nfor loops\nLots of optimisations.", "About", 0); + break; + + case IDM_CASCADE: + SendMessage(mdibox, WM_MDICASCADE, 0, 0); + break; + case IDM_TILE_HORIZ: + SendMessage(mdibox, WM_MDITILE, MDITILE_HORIZONTAL, 0); + break; + case IDM_TILE_VERT: + SendMessage(mdibox, WM_MDITILE, MDITILE_VERTICAL, 0); + break; + } +} + +void EditorMenu(editor_t *editor, WPARAM wParam) +{ + switch(LOWORD(wParam)) + { + case IDM_OPENDOCU: + { + char buffer[1024]; + int total; + total = SendMessage(editor->editpane, EM_GETSELTEXT, (WPARAM)sizeof(buffer)-1, (LPARAM)buffer); + buffer[total]='\0'; + if (!total) + { + MessageBox(NULL, "There is no name currently selected.", "Whoops", 0); + break; + } + else + EditFile(buffer, -1); + } + break; + case IDM_SAVE: + EditorSave(editor); + break; + case IDM_GOTODEF: + { + char buffer[1024]; + int total; + total = SendMessage(editor->editpane, EM_GETSELTEXT, (WPARAM)sizeof(buffer)-1, (LPARAM)buffer); + buffer[total]='\0'; + if (!total) + { + MessageBox(NULL, "There is no name currently selected.", "Whoops", 0); + break; + } + else + GoToDefinition(buffer); + } + break; + case IDM_HIGHTLIGHT: + Rehighlight(editor); + break; + + case IDM_UNDO: + Edit_Undo(editor->editpane); + break; + case IDM_REDO: + Edit_Redo(editor->editpane); + break; + + default: + GenericMenu(wParam); + break; + } +} + +static LONG CALLBACK EditorWndProc(HWND hWnd,UINT message, + WPARAM wParam,LPARAM lParam) +{ + RECT rect; + HDC hdc; + PAINTSTRUCT ps; + + editor_t *editor; + for (editor = editors; editor; editor = editor->next) + { + if (editor->window == hWnd) + break; + if (editor->window == NULL) + break; //we're actually creating it now. + } + if (!editor) + goto gdefault; + + switch (message) + { + case WM_CLOSE: + case WM_QUIT: + if (EditorModified(editor)) + { + switch (MessageBox(hWnd, "Would you like to save?", editor->filename, MB_YESNOCANCEL)) + { + case IDCANCEL: + return false; + case IDYES: + if (!EditorSave(editor)) + return false; + case IDNO: + default: + break; + } + } + goto gdefault; + case WM_DESTROY: + { + editor_t *e; + if (editor == editors) + { + editors = editor->next; + free(editor); + return 0; + } + for (e = editors; e; e = e->next) + { + if (e->next == editor) + { + e->next = editor->next; + free(editor); + return 0; + } + } + MessageBox(0, "Couldn't destroy file reference", "WARNING", 0); + } + goto gdefault; + case WM_CREATE: + editor->editpane = CreateAnEditControl(hWnd); + /* + editor->editpane=CreateWindowEx(WS_EX_CLIENTEDGE, + richedit?RICHEDIT_CLASS:"EDIT", + "", + WS_CHILD | WS_VISIBLE | + WS_HSCROLL | WS_VSCROLL | ES_LEFT | ES_WANTRETURN | + ES_MULTILINE | ES_AUTOVSCROLL, + 0, 0, 0, 0, + hWnd, + NULL, + ghInstance, + NULL); +*/ + if (richedit) + { + SendMessage(editor->editpane, EM_EXLIMITTEXT, 0, 1<<31); + + SendMessage(editor->editpane, EM_SETUNDOLIMIT, 256, 256); + } + goto gdefault; + case WM_SIZE: + GetClientRect(hWnd, &rect); + SetWindowPos(editor->editpane, NULL, 0, 0, rect.right-rect.left, rect.bottom-rect.top, 0); + goto gdefault; + case WM_PAINT: + hdc=BeginPaint(hWnd,(LPPAINTSTRUCT)&ps); + + EndPaint(hWnd,(LPPAINTSTRUCT)&ps); + return TRUE; + break; + case WM_COMMAND: + if (mdibox) + goto gdefault; + EditorMenu(editor, wParam); + break; + case WM_NOTIFY: + { + NMHDR *nmhdr; + SELCHANGE *sel; + char message[2048]; + nmhdr = (NMHDR *)lParam; + switch(nmhdr->code) + { + case EN_SELCHANGE: + sel = (SELCHANGE *)nmhdr; + sprintf(message, "%s:%i - FTEQCC Editor", editor->filename, 1+Edit_LineFromChar(editor->editpane, sel->chrg.cpMin)); + SetWindowText(editor->window, message); + break; + } + } + default: + gdefault: + if (mdibox) + return DefMDIChildProc(hWnd,message,wParam,lParam); + else + return DefWindowProc(hWnd,message,wParam,lParam); + } + return 0; +} + +#if 1 +static DWORD lastcolour; +int GUIEmitText(HWND wnd, int start, char *text, int len) +{ + int c, cr; + DWORD colour; + CHARFORMAT cf; + + if (!len) + return start; + + c = text[len]; + text[len] = '\0'; + Edit_SetSel(wnd,start,start); + Edit_ReplaceSel(wnd,text); + + if (!strcmp(text, "void")) + colour = RGB(0, 0, 255); + else if (!strcmp(text, "float")) + colour = RGB(0, 0, 255); + else if (!strcmp(text, "vector")) + colour = RGB(0, 0, 255); + else if (!strcmp(text, "entity")) + colour = RGB(0, 0, 255); + else if (!strcmp(text, "local")) + colour = RGB(0, 0, 255); + else if (!strcmp(text, "string")) + colour = RGB(0, 0, 255); + else if (!strcmp(text, "struct")) + colour = RGB(0, 0, 255); + else if (!strcmp(text, "class")) + colour = RGB(0, 0, 255); + else if (!strcmp(text, "union")) + colour = RGB(0, 0, 255); + else if (!strcmp(text, "const")) + colour = RGB(0, 0, 255); + else if (!strcmp(text, "var")) + colour = RGB(0, 0, 255); + else if (!strcmp(text, "nosave")) + colour = RGB(0, 0, 255); + + else if (!strcmp(text, "goto")) + colour = RGB(255, 0, 0); + else if (!strcmp(text, "thinktime")) + colour = RGB(0, 0, 255); + else if (!strcmp(text, "if")) + colour = RGB(0, 0, 255); + else if (!strcmp(text, "else")) + colour = RGB(0, 0, 255); + else if (!strcmp(text, "switch")) + colour = RGB(0, 0, 255); + else if (!strcmp(text, "case")) + colour = RGB(0, 0, 255); + else if (!strcmp(text, "default")) + colour = RGB(0, 0, 255); + else if (!strcmp(text, "break")) + colour = RGB(0, 0, 255); + else if (!strcmp(text, "continue")) + colour = RGB(0, 0, 255); + else if (!strcmp(text, "do")) + colour = RGB(0, 0, 255); + else if (!strcmp(text, "while")) + colour = RGB(0, 0, 255); + else if (!strcmp(text, "for")) + colour = RGB(0, 0, 255); + else if (!strcmp(text, "return")) + colour = RGB(0, 0, 255); + + else if (!strcmp(text, "self")) + colour = RGB(0, 0, 127); + else if (!strcmp(text, "this")) + colour = RGB(0, 0, 127); + else if (!strcmp(text, "other")) + colour = RGB(0, 0, 127); + else if (!strcmp(text, "world")) + colour = RGB(0, 0, 127); + else if (!strcmp(text, "time")) + colour = RGB(0, 0, 127); + + + else if (!strcmp(text, "#define")) + colour = RGB(0, 128, 255); + else if (!strcmp(text, "#ifdef")) + colour = RGB(0, 128, 255); + else if (!strcmp(text, "#ifndef")) + colour = RGB(0, 128, 255); + else if (!strcmp(text, "#else")) + colour = RGB(0, 128, 255); + else if (!strcmp(text, "#endif")) + colour = RGB(0, 128, 255); + else if (!strcmp(text, "#undef")) + colour = RGB(0, 128, 255); + else if (!strcmp(text, "#pragma")) + colour = RGB(0, 128, 255); + else if (!strcmp(text, "#includelist")) + colour = RGB(0, 128, 255); + else if (!strcmp(text, "#endlist")) + colour = RGB(0, 128, 255); + + + else if (*text == '\"') + colour = RGB(128, 0, 0); + + else if (!strncmp(text, "//", 2)) + colour = RGB(0, 127, 0); + else if (!strncmp(text, "/*", 2)) + colour = RGB(0, 127, 0); + else + colour = RGB(0, 0, 0); + + text[len] = c; + + cr = 0; + for (c = 0; c < len; c++) + if (text[c] == '\r') + cr++; + if (cr) + len-=cr; + + if (colour == lastcolour) + return start+len; + + lastcolour = colour; + + Edit_SetSel(wnd,start,start+len); + memset(&cf, 0, sizeof(cf)); + cf.cbSize = sizeof(cf); + cf.dwMask = CFM_COLOR; + cf.crTextColor = colour; + SendMessage(wnd, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf); + Edit_SetSel(wnd,start+len,start+len); + + return start + len; +} +void GUIFormattingPrint(HWND wnd, char *msg) +{ + int len=Edit_GetTextLength(wnd); + char *start; + CHARRANGE chrg; + lastcolour = RGB(0,0,0); + SendMessage(wnd, WM_SETREDRAW, false, 0); + chrg.cpMin = chrg.cpMax = 0; + SendMessage(wnd, EM_EXSETSEL, 0, (LPARAM) &chrg); + + for(start = msg;;) + { + if (!*msg) + break; + else if (*msg == '/' && msg[1] == '/') + { + len = GUIEmitText(wnd, len, start, msg - start); + start = msg; + + msg+=2; + while(*msg && *msg != '\n' && *msg != '\r') + msg++; + } + else if (*msg == '/' && msg[1] == '*') + { + len = GUIEmitText(wnd, len, start, msg - start); + start = msg; + msg+=2; + while(*msg) + { + if (msg[0] == '*' && msg[1] == '/') + { + msg+=2; + break; + } + msg++; + } + } + else if (*msg == '#' || *msg == '_' || (*msg >= 'A' && *msg <= 'Z') || (*msg >= 'a' && *msg <= 'z')) + { + len = GUIEmitText(wnd, len, start, msg - start); + start = msg; + msg++; + while (*msg == '_' || (*msg >= 'A' && *msg <= 'Z') || (*msg >= 'a' && *msg <= 'z' || *msg >= '0' && *msg <= '9')) + msg++; + } + else if (*msg == '\"') + { + len = GUIEmitText(wnd, len, start, msg - start); + start = msg; + msg++; + while(*msg) + { + if (*msg == '\\') + msg++; + else if (*msg == '\"') + { + msg++; + break; + } + + msg++; + } + } +/* else if (*msg <= ' ') + { + while (*msg <= ' ' && *msg) + msg++; + }*/ + else + { + msg++; + continue; + } + + len = GUIEmitText(wnd, len, start, msg - start); + start = msg; + } + len = GUIEmitText(wnd, len, start, msg - start); + start = msg; + SendMessage(wnd, WM_SETREDRAW, true, 0); +} + +int Rehighlight(editor_t *edit) +{ + int len; + char *file; + + CHARRANGE chrg; + POINT scrollpos; + + SendMessage(edit->editpane, EM_SETEVENTMASK, 0, 0); + + SendMessage(edit->editpane, EM_GETSCROLLPOS, 0, (LPARAM)&scrollpos); + SendMessage(edit->editpane, EM_EXGETSEL, 0, (LPARAM) &chrg); + + len = Edit_GetTextLength(edit->editpane); + file = malloc(len+1); + if (!file) + { + MessageBox(NULL, "Save failed - not enough mem", "Error", 0); + return false; + } + Edit_GetText(edit->editpane, file, len); + file[len] = '\0'; + + SetWindowText(edit->editpane,""); + +// GUIPrint(edit->editpane, file); + GUIFormattingPrint(edit->editpane, file); + free(file); + +// Edit_SetSel(edit->editpane, Edit_LineIndex(neweditor->editpane, 0), Edit_LineIndex(neweditor->editpane, 0)); + + InvalidateRect(edit->editpane, NULL, true); + InvalidateRect(edit->window, NULL, true); + + SendMessage(edit->editpane, EM_SETEVENTMASK, 0, ENM_SELCHANGE); + + SendMessage(edit->editpane, EM_SETSCROLLPOS, 0, (LPARAM)&scrollpos); + SendMessage(edit->editpane, EM_EXSETSEL, 0, (LPARAM) &chrg); + + UpdateWindow(edit->editpane); + RedrawWindow(edit->window, NULL, NULL, 0); + + return true; +} +#else +static void chunkcolour(HWND pane, int start, int end, DWORD colour) +{ + CHARFORMAT cf; + if (colour == RGB(0,0,0)) + return; //don't need to + Edit_SetSel(pane,start,end); + memset(&cf, 0, sizeof(cf)); + cf.cbSize = sizeof(cf); + cf.dwMask = CFM_COLOR; + cf.crTextColor = colour; + SendMessage(pane, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf); +} +void GUIFormattingPrint(HWND wnd, char *msg) +{ +} +int Rehighlight(editor_t *edit) +{ + char *file; + int c, last, len; + DWORD color; + CHARRANGE chrg; + POINT scrollpos; + + //Dsiable redraws + SendMessage(edit->editpane, WM_SETREDRAW, false, 0); + + //Don't notify us for a bit.. + SendMessage(edit->editpane, EM_SETEVENTMASK, 0, 0); + + //get state so we can restore scroll positions and things. + SendMessage(edit->editpane, EM_GETSCROLLPOS, 0, (LPARAM)&scrollpos); + SendMessage(edit->editpane, EM_EXGETSEL, 0, (LPARAM) &chrg); + + + len = Edit_GetTextLength(edit->editpane); + file = malloc(len+1); + if (!file) + { + MessageBox(NULL, "Highlight failed - not enough mem", "Error", 0); + return false; + } + Edit_GetText(edit->editpane, file, len); + file[len] = '\0'; + SetWindowText(edit->editpane,file); //this is so that we guarentee that the \rs or whatever that windows insists on inserting don't get in the way + + color = RGB(0,0,0); + for (last = 0, c = 0; c < len; c++) + { + if (file[c] == '/' && file[c+1] == '/') //do special syntax + { + chunkcolour(edit->editpane, last, c, color); + last = c; + + while(file[c] != '\n') + c++; + color = RGB(0, 127, 0); + } + else + { + chunkcolour(edit->editpane, last, c, color); + last = c; + + while(file[c] >= 'a' && file[c] <= 'z' || file[c] >= 'A' && file[c] <= 'Z' || file[c] == '_') + c++; + + color = RGB(rand(), rand(), rand()); + } + } + + free(file); + + //reenable drawing + SendMessage(edit->editpane, WM_SETREDRAW, true, 0); +} +#endif + +void EditFile(char *name, int line) +{ + char title[1024]; + int flen; + char *file; + editor_t *neweditor; + WNDCLASS wndclass; + HMENU menu, menufile, menuhelp, menunavig; + + for (neweditor = editors; neweditor; neweditor = neweditor->next) + { + if (neweditor->window && !strcmp(neweditor->filename, name)) + { + if (line >= 0) + { + Edit_SetSel(neweditor->editpane, Edit_LineIndex(neweditor->editpane, line), Edit_LineIndex(neweditor->editpane, line+1)); + Edit_ScrollCaret(neweditor->editpane); + } + if (mdibox) + SendMessage(mdibox, WM_MDIACTIVATE, (WPARAM)neweditor->window, 0); + SetFocus(neweditor->window); + SetFocus(neweditor->editpane); + return; + } + } + + flen = QCC_FileSize(name); + if (flen == -1) + { + MessageBox(NULL, "File not found.", "Error", 0); + return; + } + + neweditor = malloc(sizeof(editor_t)); + if (!neweditor) + { + MessageBox(NULL, "Low memory", "Error", 0); + return; + } + + neweditor->next = editors; + editors = neweditor; + + strncpy(neweditor->filename, name, sizeof(neweditor->filename)-1); + + if (!mdibox) + { + menu = CreateMenu(); + menufile = CreateMenu(); + menuhelp = CreateMenu(); + menunavig = CreateMenu(); + AppendMenu(menu, MF_POPUP, (UINT)menufile, "&File"); + AppendMenu(menu, MF_POPUP, (UINT)menunavig, "&Navigation"); + AppendMenu(menu, MF_POPUP, (UINT)menuhelp, "&Help"); + AppendMenu(menufile, 0, IDM_OPENNEW, "Open &new file "); + AppendMenu(menufile, 0, IDM_SAVE, "&Save "); + // AppendMenu(menufile, 0, IDM_FIND, "&Find"); + AppendMenu(menufile, 0, IDM_UNDO, "&Undo Ctrl+Z"); + AppendMenu(menufile, 0, IDM_REDO, "&Redo Ctrl+Y"); + AppendMenu(menunavig, 0, IDM_GOTODEF, "Go to definition"); + AppendMenu(menunavig, 0, IDM_OPENDOCU, "Open selected file"); + AppendMenu(menuhelp, 0, IDM_ABOUT, "About"); + AppendMenu(menu, 0, IDM_HIGHTLIGHT, "H&ighlight"); + } + + + + wndclass.style = 0; + wndclass.lpfnWndProc = (WNDPROC)EditorWndProc; + wndclass.cbClsExtra = 0; + wndclass.cbWndExtra = 0; + wndclass.hInstance = ghInstance; + wndclass.hIcon = 0; + wndclass.hCursor = LoadCursor (NULL,IDC_ARROW); + wndclass.hbrBackground = (void *)COLOR_WINDOW; + wndclass.lpszMenuName = 0; + wndclass.lpszClassName = EDIT_WINDOW_CLASS_NAME; + RegisterClass(&wndclass); + + neweditor->window = NULL; + if (mdibox) + { + MDICREATESTRUCT mcs; + + sprintf(title, "%s", name); + + mcs.szClass = EDIT_WINDOW_CLASS_NAME; + mcs.szTitle = name; + mcs.hOwner = ghInstance; + mcs.x = mcs.cx = CW_USEDEFAULT; + mcs.y = mcs.cy = CW_USEDEFAULT; + mcs.style = WS_OVERLAPPEDWINDOW; + mcs.lParam = 0; + + neweditor->window = (HWND) SendMessage (mdibox, WM_MDICREATE, 0, + (LONG) (LPMDICREATESTRUCT) &mcs); + } + else + { + sprintf(title, "%s - FTEEditor", name); + + neweditor->window=CreateWindow(EDIT_WINDOW_CLASS_NAME, title, WS_OVERLAPPEDWINDOW, + 0, 0, 640, 480, NULL, NULL, ghInstance, NULL); + } + + if (!mdibox) + SetMenu(neweditor->window, menu); + + if (!neweditor->window) + { + MessageBox(NULL, "Failed to create editor window", "Error", 0); + return; + } + + flen = QCC_FileSize(name); + file = malloc(flen+1); + QCC_ReadFile(name, file, flen); + file[flen] = 0; + + SendMessage(neweditor->editpane, EM_SETEVENTMASK, 0, 0); + + if (!fl_autohighlight) + { + GUIPrint(neweditor->editpane, file); + } + else + { + GUIFormattingPrint(neweditor->editpane, file); + } + + SendMessage(neweditor->editpane, EM_SETEVENTMASK, 0, ENM_SELCHANGE); + + if (line >= 0) + Edit_SetSel(neweditor->editpane, Edit_LineIndex(neweditor->editpane, line), Edit_LineIndex(neweditor->editpane, line+1)); + else + Edit_SetSel(neweditor->editpane, Edit_LineIndex(neweditor->editpane, 0), Edit_LineIndex(neweditor->editpane, 0)); + + Edit_ScrollCaret(neweditor->editpane); + + ShowWindow(neweditor->window, SW_SHOW); + SetFocus(mainwindow); + SetFocus(neweditor->window); + SetFocus(neweditor->editpane); +} + +int EditorSave(editor_t *edit) +{ + int len; + char *file; + len = Edit_GetTextLength(edit->editpane); + file = malloc(len+1); + if (!file) + { + MessageBox(NULL, "Save failed - not enough mem", "Error", 0); + return false; + } + Edit_GetText(edit->editpane, file, len); + if (!QCC_WriteFile(edit->filename, file, len)) + { + MessageBox(NULL, "Save failed\nCheck path and ReadOnly flags", "Failure", 0); + return false; + } + free(file); + + return true; +} +void EditorsRun(void) +{ +} + + +char *GUIReadFile(char *fname, void *buffer, int blen) +{ + editor_t *e; + for (e = editors; e; e = e->next) + { + if (e->window && !strcmp(e->filename, fname)) + { + int elen = Edit_GetTextLength(e->editpane); + Edit_GetText(e->editpane, buffer, blen); + return buffer; + } + } + + return QCC_ReadFile(fname, buffer, blen); +} + +int GUIFileSize(char *fname) +{ + editor_t *e; + for (e = editors; e; e = e->next) + { + if (e->window && !strcmp(e->filename, fname)) + { + int len = Edit_GetTextLength(e->editpane); + return len; + } + } + return QCC_FileSize(fname); +} + +pbool EditorModified(editor_t *e) +{ + char *buffer; + int elen, flen; + elen = Edit_GetTextLength(e->editpane); + flen = QCC_FileSize(e->filename); + + if (elen != flen) + return true; + + buffer = malloc(elen+flen); + Edit_GetText(e->editpane, buffer, elen); + QCC_ReadFile(e->filename, buffer+elen, flen); + if (memcmp(buffer, buffer+elen, elen)) + { + free(buffer); + return true; + } + free(buffer); + + return false; +} + + + + + + + + + + + + + +HWND optionsmenu; +HWND hexen2item; +HWND nokeywords_coexistitem; +HWND autoprototype_item; +HWND autohighlight_item; +HWND extraparmsitem; +static LONG CALLBACK OptionsWndProc(HWND hWnd,UINT message, + WPARAM wParam,LPARAM lParam) +{ + int i; + switch (message) + { + case WM_DESTROY: + optionsmenu = NULL; + break; + + case WM_COMMAND: + switch(wParam) + { + case IDI_O_USE: + case IDI_O_APPLY: + for (i = 0; optimisations[i].enabled; i++) + { + if (optimisations[i].flags & FLAG_HIDDENINGUI) + continue; + + if (Button_GetCheck(optimisations[i].guiinfo)) + optimisations[i].flags |= FLAG_SETINGUI; + else + optimisations[i].flags &= ~FLAG_SETINGUI; + } + fl_hexen2 = Button_GetCheck(hexen2item); + for (i = 0; compiler_flag[i].enabled; i++) + { + if (compiler_flag[i].flags & FLAG_HIDDENINGUI) + continue; + if (Button_GetCheck(compiler_flag[i].guiinfo)) + compiler_flag[i].flags |= FLAG_SETINGUI; + else + compiler_flag[i].flags &= ~FLAG_SETINGUI; + } + fl_autohighlight = Button_GetCheck(autohighlight_item); + Edit_GetText(extraparmsitem, parameters, sizeof(parameters)-1); + + if (wParam == IDI_O_USE) + buttons[ID_COMPILE].washit = true; + break; + case IDI_O_CHANGE_PROGS_SRC: + { + char *s, *s2; + char filename[MAX_PATH]; + char oldpath[MAX_PATH+10]; + OPENFILENAME ofn; + memset(&ofn, 0, sizeof(ofn)); + ofn.lStructSize = sizeof(ofn); + ofn.hInstance = ghInstance; + ofn.lpstrFile = filename; + ofn.lpstrTitle = "Please find progs.src"; + ofn.nMaxFile = sizeof(filename)-1; + ofn.lpstrFilter = "QuakeC source\0*.src\0All files\0*.*\0"; + memset(filename, 0, sizeof(filename)); + GetCurrentDirectory(sizeof(oldpath)-1, oldpath); + ofn.lpstrInitialDir = oldpath; + if (GetOpenFileName(&ofn)) + { + strcpy(progssrcdir, filename); + for(s = progssrcdir; s; s = s2) + { + s2 = strchr(s+1, '\\'); + if (!s2) + break; + s = s2; + } + if (s) + { + *s = '\0'; + strcpy(progssrcname, s+1); + } + else + strcpy(progssrcname, filename); + + SetCurrentDirectory(progssrcdir); + *progssrcdir = '\0'; + } + resetprogssrc = true; + } + break; + case IDI_O_LEVEL0: + case IDI_O_LEVEL1: + case IDI_O_LEVEL2: + case IDI_O_LEVEL3: + for (i = 0; optimisations[i].enabled; i++) + { + if (optimisations[i].flags & FLAG_HIDDENINGUI) + continue; + + if (optimisations[i].optimisationlevel<=(int)wParam-IDI_O_LEVEL0) + Button_SetCheck(optimisations[i].guiinfo, 1); + else + Button_SetCheck(optimisations[i].guiinfo, 0); + } + break; + case IDI_O_DEBUG: + for (i = 0; optimisations[i].enabled; i++) + { + if (optimisations[i].flags & FLAG_HIDDENINGUI) + continue; + + if (optimisations[i].flags&FLAG_KILLSDEBUGGERS) + Button_SetCheck(optimisations[i].guiinfo, 0); + } + break; + case IDI_O_DEFAULT: + for (i = 0; optimisations[i].enabled; i++) + { + if (optimisations[i].flags & FLAG_HIDDENINGUI) + continue; + + if (optimisations[i].flags & FLAG_ASDEFAULT) + Button_SetCheck(optimisations[i].guiinfo, 1); + else + Button_SetCheck(optimisations[i].guiinfo, 0); + } + break; + } + break; + case WM_HELP: + { + HELPINFO *hi; + hi = (HELPINFO *)lParam; + switch(hi->iCtrlId) + { + case IDI_O_DEFAULT: + MessageBox(hWnd, "Sets the default optimisations", "Help", MB_OK|MB_ICONINFORMATION); + break; + case IDI_O_DEBUG: + MessageBox(hWnd, "Clears all optimisations which can make your progs harder to debug", "Help", MB_OK|MB_ICONINFORMATION); + break; + case IDI_O_LEVEL0: + case IDI_O_LEVEL1: + case IDI_O_LEVEL2: + case IDI_O_LEVEL3: + MessageBox(hWnd, "Sets a specific optimisation level", "Help", MB_OK|MB_ICONINFORMATION); + break; + case IDI_O_CHANGE_PROGS_SRC: + MessageBox(hWnd, "Use this button to change your root source file.\nNote that fteqcc compiles sourcefiles from editors first, rather than saving. This means that changes are saved ONLY when you save them, but means that switching project mid-compile can result in problems.", "Help", MB_OK|MB_ICONINFORMATION); + break; + case IDI_O_ADDITIONALPARAMETERS: + MessageBox(hWnd, "Type in additional commandline parameters here. Use -Dname to define a named precompiler constant before compiling.", "Help", MB_OK|MB_ICONINFORMATION); + break; + case IDI_O_APPLY: + MessageBox(hWnd, "Apply changes shown, but do not recompile yet.", "Help", MB_OK|MB_ICONINFORMATION); + break; + case IDI_O_USE: + MessageBox(hWnd, "Apply changes shown here and recompile.", "Help", MB_OK|MB_ICONINFORMATION); + break; + case IDI_O_OPTIMISATION: + for (i = 0; optimisations[i].enabled; i++) + { + if (optimisations[i].guiinfo == hi->hItemHandle) + { + MessageBox(hWnd, optimisations[i].description, "Help", MB_OK|MB_ICONINFORMATION); + break; + } + } + break; + case IDI_O_COMPILER_FLAG: + for (i = 0; compiler_flag[i].enabled; i++) + { + if (compiler_flag[i].guiinfo == hi->hItemHandle) + { + MessageBox(hWnd, compiler_flag[i].description, "Help", MB_OK|MB_ICONINFORMATION); + break; + } + } + break; + case IDI_O_TARGET: + MessageBox(hWnd, "Click here to compile a hexen2 compatible progs. Note that this uses the -Thexen2. There are other targets available.", "Help", MB_OK|MB_ICONINFORMATION); + break; + case IDI_O_SYNTAX_HIGHLIGHTING: + MessageBox(hWnd, "Should syntax be highlighted automatically when a file is opened?", "Help", MB_OK|MB_ICONINFORMATION); + break; + } + } + break; + default: + return DefWindowProc(hWnd,message,wParam,lParam); + } + return 0; +} +void OptionsDialog(void) +{ + HWND subsection; + RECT r; + WNDCLASS wndclass; + HWND wnd; + int i; + int flagcolums=1; + + int x; + int y; + int my; + int height; + int num; + int cflagsshown; + + if (optionsmenu) + { + BringWindowToTop(optionsmenu); + return; + } + + + memset(&wndclass, 0, sizeof(wndclass)); + wndclass.style = 0; + wndclass.lpfnWndProc = (WNDPROC)OptionsWndProc; + wndclass.cbClsExtra = 0; + wndclass.cbWndExtra = 0; + wndclass.hInstance = ghInstance; + wndclass.hIcon = 0; + wndclass.hCursor = LoadCursor (NULL,IDC_ARROW); + wndclass.hbrBackground = (void *)COLOR_WINDOW; + wndclass.lpszMenuName = 0; + wndclass.lpszClassName = OPTIONS_WINDOW_CLASS_NAME; + RegisterClass(&wndclass); + + height = 0; + for (i = 0; optimisations[i].enabled; i++) + { + if (optimisations[i].flags & FLAG_HIDDENINGUI) + continue; + + height++; + } + + cflagsshown = 2; + for (i = 0; compiler_flag[i].enabled; i++) + { + if (compiler_flag[i].flags & FLAG_HIDDENINGUI) + continue; + + cflagsshown++; + } + + height = (height-1)/2; + + while (cflagsshown > ((480-(88+40))/16)*(flagcolums)) + flagcolums++; + + if (height < (cflagsshown+flagcolums-1)/flagcolums) + height = (cflagsshown+flagcolums-1)/flagcolums; + + r.right = 408 + flagcolums*168; + if (r.right < 640) + r.right = 640; + + height *= 16; + + height += 88+40; + + r.left = GetSystemMetrics(SM_CXSCREEN)/2-320; + r.top = GetSystemMetrics(SM_CYSCREEN)/2-240; + r.bottom = r.top + height; + r.right += r.left; + + + + AdjustWindowRectEx (&r, WS_CAPTION|WS_SYSMENU, FALSE, 0); + + optionsmenu=CreateWindowEx(WS_EX_CONTEXTHELP, OPTIONS_WINDOW_CLASS_NAME, "Options - FTE QuakeC compiler", WS_CAPTION|WS_SYSMENU, + r.left, r.top, r.right-r.left, r.bottom-r.top, NULL, NULL, ghInstance, NULL); + + subsection = CreateWindow("BUTTON", "Optimisations", WS_CHILD|WS_VISIBLE|BS_GROUPBOX, + 0, 0, 400, height-48, optionsmenu, NULL, ghInstance, NULL); + + num = 0; + for (i = 0; optimisations[i].enabled; i++) + { + if (optimisations[i].flags & FLAG_HIDDENINGUI) + { + optimisations[i].guiinfo = NULL; + continue; + } + + optimisations[i].guiinfo = wnd = CreateWindow("BUTTON",optimisations[i].fullname, + WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX, + 8+200*(num&1),16+16*(num/2),200-16,16, + subsection, + (HMENU)IDI_O_OPTIMISATION, + ghInstance, + NULL); + + if (optimisations[i].flags&FLAG_SETINGUI) + Button_SetCheck(wnd, 1); + else + Button_SetCheck(wnd, 0); + + num++; + } + + CreateWindow("BUTTON","O0", + WS_CHILD | WS_VISIBLE, + 8,height-88,64,32, + optionsmenu, + (HMENU)IDI_O_LEVEL0, + ghInstance, + NULL); + CreateWindow("BUTTON","O1", + WS_CHILD | WS_VISIBLE, + 8+64,height-88,64,32, + optionsmenu, + (HMENU)IDI_O_LEVEL1, + ghInstance, + NULL); + CreateWindow("BUTTON","O2", + WS_CHILD | WS_VISIBLE, + 8+64*2,height-88,64,32, + optionsmenu, + (HMENU)IDI_O_LEVEL2, + ghInstance, + NULL); + CreateWindow("BUTTON","O3", + WS_CHILD | WS_VISIBLE, + 8+64*3,height-88,64,32, + optionsmenu, + (HMENU)IDI_O_LEVEL3, + ghInstance, + NULL); + CreateWindow("BUTTON","Debug", + WS_CHILD | WS_VISIBLE, + 8+64*4,height-88,64,32, + optionsmenu, + (HMENU)IDI_O_DEBUG, + ghInstance, + NULL); + CreateWindow("BUTTON","Default", + WS_CHILD | WS_VISIBLE, + 8+64*5,height-88,64,32, + optionsmenu, + (HMENU)IDI_O_DEFAULT, + ghInstance, + NULL); + CreateWindow("BUTTON","Apply", + WS_CHILD | WS_VISIBLE, + 8,height-40,64,32, + optionsmenu, + (HMENU)IDI_O_APPLY, + ghInstance, + NULL); + CreateWindow("BUTTON","Use", + WS_CHILD | WS_VISIBLE, + 8+64,height-40,64,32, + optionsmenu, + (HMENU)IDI_O_USE, + ghInstance, + NULL); + CreateWindow("BUTTON","progs.src", + WS_CHILD | WS_VISIBLE, + 8+64*2,height-40,64,32, + optionsmenu, + (HMENU)IDI_O_CHANGE_PROGS_SRC, + ghInstance, + NULL); + + + + y=4; + hexen2item = wnd = CreateWindow("BUTTON","HexenC", + WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX, + 408,y,200-16,16, + optionsmenu, + (HMENU)IDI_O_TARGET, + ghInstance, + NULL); + y+=16; + if (fl_hexen2) + Button_SetCheck(wnd, 1); + else + Button_SetCheck(wnd, 0); + + autohighlight_item = wnd = CreateWindow("BUTTON","Syntax Highlighting", + WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX, + 408,y,200-16,16, + optionsmenu, + (HMENU)IDI_O_SYNTAX_HIGHLIGHTING, + ghInstance, + NULL); + y+=16; + if (fl_autohighlight) + Button_SetCheck(wnd, 1); + else + Button_SetCheck(wnd, 0); + + x = 408; + my = y; + for (i = 0; compiler_flag[i].enabled; i++) + { + if (compiler_flag[i].flags & FLAG_HIDDENINGUI) + { + compiler_flag[i].guiinfo = NULL; + continue; + } + + if (y > height-(88+40)) + { + y = 4; + x += 168; + } + + compiler_flag[i].guiinfo = wnd = CreateWindow("BUTTON",compiler_flag[i].fullname, + WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX, + x,y,168,16, + optionsmenu, + (HMENU)IDI_O_COMPILER_FLAG, + ghInstance, + NULL); + y+=16; + + if (my < y) + my = y; + + if (compiler_flag[i].flags & FLAG_SETINGUI) + Button_SetCheck(wnd, 1); + else + Button_SetCheck(wnd, 0); + } + + CreateWindow("STATIC","Extra Parameters:", + WS_CHILD | WS_VISIBLE, + 408,my,200-16,16, + optionsmenu, + (HMENU)0, + ghInstance, + NULL); + my+=16; + extraparmsitem = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT",parameters, + WS_CHILD | WS_VISIBLE|ES_LEFT | ES_WANTRETURN | + ES_MULTILINE | ES_AUTOVSCROLL, + 408,my,r.right-r.left - 408 - 8,height-my-4, + optionsmenu, + (HMENU)IDI_O_ADDITIONALPARAMETERS, + ghInstance, + NULL); + + ShowWindow(optionsmenu, SW_SHOWDEFAULT); +} + + + + + + + + + + + +#undef printf + + + +static LONG CALLBACK MainWndProc(HWND hWnd,UINT message, + WPARAM wParam,LPARAM lParam) +{ + int width; + int i; + RECT rect; + HDC hdc; + PAINTSTRUCT ps; + switch (message) + { + case WM_CREATE: + { + CLIENTCREATESTRUCT ccs; + + HMENU rootmenu, windowmenu, m; + rootmenu = CreateMenu(); + + AppendMenu(rootmenu, MF_POPUP, (UINT)(m = CreateMenu()), "&File"); + AppendMenu(m, 0, IDM_OPENNEW, "Open &new file "); + AppendMenu(m, 0, IDM_SAVE, "&Save "); + // AppendMenu(m, 0, IDM_FIND, "&Find"); + AppendMenu(m, 0, IDM_UNDO, "&Undo Ctrl+Z"); + AppendMenu(m, 0, IDM_REDO, "&Redo Ctrl+Y"); + AppendMenu(rootmenu, MF_POPUP, (UINT)(m = CreateMenu()), "&Navigation"); + AppendMenu(m, 0, IDM_GOTODEF, "Go to definition"); + AppendMenu(m, 0, IDM_OPENDOCU, "Open selected file"); + AppendMenu(rootmenu, MF_POPUP, (UINT)(m = windowmenu = CreateMenu()), "&Window"); + AppendMenu(m, 0, IDM_CASCADE, "&Cascade"); + AppendMenu(m, 0, IDM_TILE_HORIZ, "Tile &Horizontally"); + AppendMenu(m, 0, IDM_TILE_VERT, "Tile &Vertically"); + AppendMenu(rootmenu, MF_POPUP, (UINT)(m = CreateMenu()), "&Help"); + AppendMenu(m, 0, IDM_ABOUT, "About"); + + SetMenu(hWnd, rootmenu); + + // Retrieve the handle to the window menu and assign the + // first child window identifier. + + memset(&ccs, 0, sizeof(ccs)); + ccs.hWindowMenu = windowmenu; + ccs.idFirstChild = IDM_FIRSTCHILD; + + // Create the MDI client window. + + mdibox = CreateWindow( "MDICLIENT", (LPCTSTR) NULL, + WS_CHILD | WS_CLIPCHILDREN | WS_VSCROLL | WS_HSCROLL, + 0, 0, 320, 200, hWnd, (HMENU) 0xCAC, ghInstance, (LPSTR) &ccs); + ShowWindow(mdibox, SW_SHOW); + + projecttree = CreateWindow(WC_TREEVIEW, (LPCTSTR) NULL, + WS_CHILD | WS_CLIPCHILDREN | WS_VSCROLL | WS_HSCROLL + | TVS_HASBUTTONS |TVS_LINESATROOT|TVS_HASLINES, + 0, 0, 320, 200, hWnd, (HMENU) 0xCAC, ghInstance, (LPSTR) &ccs); + ShowWindow(projecttree, SW_SHOW); + + if (projecttree) + { + gotodefbox = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", (LPCTSTR) NULL, + WS_CHILD | WS_CLIPCHILDREN, + 0, 0, 320, 200, hWnd, (HMENU) 0xCAC, ghInstance, (LPSTR) NULL); + ShowWindow(gotodefbox, SW_SHOW); + + gotodefaccept = CreateWindowEx(WS_EX_CLIENTEDGE, "BUTTON", "GO", + WS_CHILD | WS_CLIPCHILDREN | BS_DEFPUSHBUTTON, + 0, 0, 320, 200, hWnd, (HMENU) 0x4404, ghInstance, (LPSTR) NULL); + ShowWindow(gotodefaccept, SW_SHOW); + } + } + break; + + case WM_DESTROY: + mainwindow = NULL; + break; + + case WM_SIZE: + GetClientRect(mainwindow, &rect); + if (projecttree) + { + SetWindowPos(projecttree, NULL, 0, 0, 192, rect.bottom-rect.top - 34 - 24, 0); + + SetWindowPos(gotodefbox, NULL, 0, rect.bottom-rect.top - 33 - 24, 160, 24, 0); + SetWindowPos(gotodefaccept, NULL, 160, rect.bottom-rect.top - 33 - 24, 32, 24, 0); + SetWindowPos(mdibox?mdibox:outputbox, NULL, 192, 0, rect.right-rect.left-192, rect.bottom-rect.top - 32, 0); + } + else + SetWindowPos(mdibox?mdibox:outputbox, NULL, 0, 0, rect.right-rect.left, rect.bottom-rect.top - 32, 0); + width = (rect.right-rect.left); + width/=NUMBUTTONS; + for (i = 0; i < NUMBUTTONS; i++) + { + SetWindowPos(buttons[i].hwnd, NULL, width*i, rect.bottom-rect.top - 32, width, 32, 0); + } + break; + case WM_PAINT: + hdc=BeginPaint(hWnd,(LPPAINTSTRUCT)&ps); + + EndPaint(hWnd,(LPPAINTSTRUCT)&ps); + return TRUE; + break; + case WM_COMMAND: + if (wParam == 0x4404) + { + GetWindowText(gotodefbox, finddef, sizeof(finddef)-1); + return true; + } + if (LOWORD(wParam)>0 && LOWORD(wParam) <= NUMBUTTONS) + { + if (LOWORD(wParam)) + buttons[LOWORD(wParam)-1].washit = 1; + break; + } + if (LOWORD(wParam) < IDM_FIRSTCHILD) + { + HWND ew; + editor_t *editor; + + ew = (HWND)SendMessage(mdibox, WM_MDIGETACTIVE, 0, 0); + + for (editor = editors; editor; editor = editor->next) + { + if (editor->window == ew) + break; + } + if (editor) + EditorMenu(editor, wParam); + else + GenericMenu(wParam); + break; + } + break; + case WM_NOTIFY: + if (lParam) + { + NMHDR *nm; + HANDLE item; + TVITEM i; + char filename[256]; + char itemtext[256]; + int oldlen; + int newlen; + nm = (NMHDR*)lParam; + if (nm->hwndFrom == projecttree) + { + switch(nm->code) + { + case NM_DBLCLK: + item = TreeView_GetSelection(projecttree); + i.hItem = item; + i.mask = TVIF_TEXT; + i.pszText = itemtext; + i.cchTextMax = sizeof(itemtext)-1; + if (!TreeView_GetItem(projecttree, &i)) + return 0; + strcpy(filename, i.pszText); + while(item) + { + item = TreeView_GetParent(projecttree, item); + i.hItem = item; + if (!TreeView_GetItem(projecttree, &i)) + break; + if (!TreeView_GetParent(projecttree, item)) + break; + + oldlen = strlen(filename); + newlen = strlen(i.pszText); + memmove(filename+newlen+1, filename, oldlen+1); + filename[newlen] = '/'; + strncpy(filename, i.pszText, newlen); + } + EditFile(filename, -1); + break; + } + } + } + default: + if (mdibox) + return DefFrameProc(hWnd,mdibox,message,wParam,lParam); + else + return DefWindowProc(hWnd,message,wParam,lParam); + } + return 0; +} + +static LONG CALLBACK OutputWindowProc(HWND hWnd,UINT message, + WPARAM wParam,LPARAM lParam) +{ + RECT rect; + switch (message) + { + case WM_DESTROY: + outputwindow = NULL; + outputbox = NULL; + break; + case WM_CREATE: + outputbox = CreateAnEditControl(hWnd); + case WM_SIZE: + GetClientRect(hWnd, &rect); + SetWindowPos(outputbox, NULL, 0, 0, rect.right-rect.left, rect.bottom-rect.top, 0); + + default: + return DefMDIChildProc(hWnd,message,wParam,lParam); + } + return 0; +} + +void GUIPrint(HWND wnd, char *msg) +{ + MSG wmsg; + int len; + static int writing; + + if (writing) + return; + if (!mainwindow) + { + printf("%s", msg); + return; + } + writing=true; + len=Edit_GetTextLength(wnd); +/* if ((unsigned)len>(32767-strlen(msg))) + Edit_SetSel(wnd,0,len); + else*/ + Edit_SetSel(wnd,len,len); + Edit_ReplaceSel(wnd,msg); + + while (PeekMessage (&wmsg, NULL, 0, 0, PM_NOREMOVE)) + { + if (!GetMessage (&wmsg, NULL, 0, 0)) + break; + TranslateMessage (&wmsg); + DispatchMessage (&wmsg); + } + writing=false; +} +int GUIEmitOutputText(HWND wnd, int start, char *text, int len, DWORD colour) +{ + int c, cr; + CHARFORMAT cf; + + if (!len) + return start; + + c = text[len]; + text[len] = '\0'; + Edit_SetSel(wnd,start,start); + Edit_ReplaceSel(wnd,text); + + text[len] = c; + + cr = 0; + for (c = 0; c < len; c++) + if (text[c] == '\r') + cr++; + if (cr) + len-=cr; + + Edit_SetSel(wnd,start,start+len); + memset(&cf, 0, sizeof(cf)); + cf.cbSize = sizeof(cf); + cf.dwMask = CFM_COLOR; + cf.crTextColor = colour; + SendMessage(wnd, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf); + Edit_SetSel(wnd,start+len,start+len); + + return start + len; +} +int outlen; +int GUIprintf(const char *msg, ...) +{ + va_list argptr; + char buf[1024]; + char rn[3] = "\n"; + char *st, *s; + int args; + MSG wmsg; + + DWORD col; + + va_start (argptr,msg); + args = QC_vsnprintf (buf,sizeof(buf)-1, msg,argptr); + va_end (argptr); + + printf("%s", buf); + if (logfile) + fprintf(logfile, "%s", buf); + + if (!*buf) + { + SetWindowText(outputbox,""); + outlen = 0; + return 0; + } + + if (strstr(buf, "warning: ")) + col = RGB(128, 128, 0); + else if (strstr(buf, "error: ")) + col = RGB(255, 0, 0); + else + col = RGB(0, 0, 0); + + s = st = buf; + while(*s) + { + if (*s == '\n') + { + *s = '\0'; + if (*st) + outlen = GUIEmitOutputText(outputbox, outlen, st, strlen(st), col); + outlen = GUIEmitOutputText(outputbox, outlen, rn, 1, col); + st = s+1; + } + + s++; + } + if (*st) + outlen = GUIEmitOutputText(outputbox, outlen, st, strlen(st), col); + + while (PeekMessage (&wmsg, NULL, 0, 0, PM_NOREMOVE)) + { + if (!GetMessage (&wmsg, NULL, 0, 0)) + break; + TranslateMessage (&wmsg); + DispatchMessage (&wmsg); + } +/* + s = st = buf; + while(*s) + { + if (*s == '\n') + { + *s = '\0'; + if (*st) + GUIPrint(outputbox, st); + GUIPrint(outputbox, "\r\n"); + st = s+1; + } + + s++; + } + if (*st) + GUIPrint(outputbox, st); +*/ + return args; +} + +#undef Sys_Error + +void Sys_Error(const char *text, ...); +void RunCompiler(char *args) +{ + char *argv[64]; + int argc; + progexterns_t ext; + progfuncs_t funcs; + + memset(&funcs, 0, sizeof(funcs)); + funcs.parms = &ext; + memset(&ext, 0, sizeof(ext)); + funcs.parms->ReadFile = GUIReadFile; + funcs.parms->FileSize = GUIFileSize; + funcs.parms->WriteFile = QCC_WriteFile; + funcs.parms->printf = GUIprintf; + funcs.parms->Sys_Error = Sys_Error; + GUIprintf(""); + + if (logfile) + fclose(logfile); + if (fl_log) + logfile = fopen("fteqcc.log", "wb"); + else + logfile = NULL; + + argc = GUI_BuildParms(args, argv); + + CompileParams(&funcs, true, argc, argv); + + if (logfile) + fclose(logfile); +} + + +void CreateOutputWindow(void) +{ + WNDCLASS wndclass; + MDICREATESTRUCT mcs; + + if (!mdibox) //should already be created + return; + + if (!outputwindow) + { + wndclass.style = 0; + wndclass.lpfnWndProc = (WNDPROC)OutputWindowProc; + wndclass.cbClsExtra = 0; + wndclass.cbWndExtra = 0; + wndclass.hInstance = ghInstance; + wndclass.hIcon = 0; + wndclass.hCursor = LoadCursor (NULL,IDC_ARROW); + wndclass.hbrBackground = (void *)COLOR_WINDOW; + wndclass.lpszMenuName = 0; + wndclass.lpszClassName = MAIN_WINDOW_CLASS_NAME; + RegisterClass(&wndclass); + + + + mcs.szClass = MAIN_WINDOW_CLASS_NAME; + mcs.szTitle = "Compiler output"; + mcs.hOwner = ghInstance; + mcs.x = mcs.cx = CW_USEDEFAULT; + mcs.y = mcs.cy = CW_USEDEFAULT; + mcs.style = WS_OVERLAPPEDWINDOW; + mcs.lParam = 0; + + outputwindow = (HWND) SendMessage (mdibox, WM_MDICREATE, 0, + (LONG) (LPMDICREATESTRUCT) &mcs); + + ShowWindow(outputwindow, SW_SHOW); + } + + //bring it to the front. + SendMessage(mdibox, WM_MDIACTIVATE, (WPARAM)outputwindow, 0); +} + +//progssrcname should already have been set. +void SetProgsSrc(void) +{ + FILE *f; + + HANDLE rootitem, pi; + TVINSERTSTRUCT item; + TV_ITEM parent; + char parentstring[256]; + memset(&item, 0, sizeof(item)); + memset(&parent, 0, sizeof(parent)); + + if (projecttree) + { + int size; + char *buffer; + char *slash; + + f = fopen (progssrcname, "rb"); + if (!f) + return; + fseek(f, 0, SEEK_END); + size = ftell(f); + fseek(f, 0, SEEK_SET); + buffer = malloc(size+1); + if (!buffer) + { + fclose(f); + return; + } + buffer[size] = '\0'; + fread(buffer, 1, size, f); + fclose(f); + + pr_file_p = QCC_COM_Parse(buffer); + if (*qcc_token == '#') + { + free(buffer); //aaaahhh! newstyle! + return; + } + + pr_file_p = QCC_COM_Parse(pr_file_p); //we dont care about the produced progs.dat + + + item.hParent = TVI_ROOT; + item.hInsertAfter = TVI_SORT; + item.item.pszText = progssrcname; + item.item.mask = TVIF_TEXT; + rootitem = (HANDLE)SendMessage(projecttree,TVM_INSERTITEM,0,(LPARAM)&item); + while(pr_file_p) + { + pi = item.hParent = rootitem; + item.item.pszText = qcc_token; + while(slash = strchr(item.item.pszText, '/')) + { + *slash = '\0'; + item.hParent = TreeView_GetChild(projecttree, item.hParent); + do + { + parent.hItem = item.hParent; + parent.mask = TVIF_TEXT; + parent.pszText = parentstring; + parent.cchTextMax = sizeof(parentstring)-1; + if (TreeView_GetItem(projecttree, &parent)) + { + if (!stricmp(parent.pszText, item.item.pszText)) + break; + } + } while(item.hParent=TreeView_GetNextSibling(projecttree, item.hParent)); + if (!item.hParent) + { //add a directory. + item.hParent = pi; + pi = (HANDLE)SendMessage(projecttree,TVM_INSERTITEM,0,(LPARAM)&item); + item.hParent = pi; + } + else pi = item.hParent; + + item.item.pszText = slash+1; + } + SendMessage(projecttree,TVM_INSERTITEM,0,(LPARAM)&item); + pr_file_p = QCC_COM_Parse(pr_file_p); + } + + free(buffer); + } +} + +int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) +{ + unsigned int i; + WNDCLASS wndclass; + ghInstance= hInstance; + + GUI_SetDefaultOpts(); + + if(strstr(lpCmdLine, "-stdout")) + { + GUI_ParseCommandLine(lpCmdLine); + RunCompiler(lpCmdLine); + return 0; + } + + if (!*lpCmdLine) + { + int len; + FILE *f; + char *s; + + f = fopen("fteqcc.cfg", "rb"); + if (f) + { + fseek(f, 0, SEEK_END); + len = ftell(f); + fseek(f, 0, SEEK_SET); + lpCmdLine = malloc(len+1); + fread(lpCmdLine, 1, len, f); + lpCmdLine[len] = '\0'; + fclose(f); + + while(s = strchr(lpCmdLine, '\r')) + *s = ' '; + while(s = strchr(lpCmdLine, '\n')) + *s = ' '; + } + } + + GUI_ParseCommandLine(lpCmdLine); + + GUI_RevealOptions(); + + if (/*!fl_acc &&*/ !*progssrcname) + { + strcpy(progssrcname, "preprogs.src"); + if (QCC_FileSize(progssrcname)==-1) + strcpy(progssrcname, "progs.src"); + if (QCC_FileSize(progssrcname)==-1) + { + char *s, *s2; + char filename[MAX_PATH]; + char oldpath[MAX_PATH+10]; + OPENFILENAME ofn; + memset(&ofn, 0, sizeof(ofn)); + ofn.lStructSize = sizeof(ofn); + ofn.hInstance = ghInstance; + ofn.lpstrFile = filename; + ofn.lpstrTitle = "Please find progs.src"; + ofn.nMaxFile = sizeof(filename)-1; + ofn.lpstrFilter = "QuakeC source\0*.src\0All files\0*.*\0"; + memset(filename, 0, sizeof(filename)); + GetCurrentDirectory(sizeof(oldpath)-1, oldpath); + ofn.lpstrInitialDir = oldpath; + if (GetOpenFileName(&ofn)) + { + strcpy(progssrcdir, filename); + for(s = progssrcdir; s; s = s2) + { + s2 = strchr(s+1, '\\'); + if (!s2) + break; + s = s2; + } + if (s) + { + *s = '\0'; + strcpy(progssrcname, s+1); + } + else + strcpy(progssrcname, filename); + } + else + { + MessageBox(NULL, "You didn't select a file", "Error", 0); + return 0; + } + SetCurrentDirectory(progssrcdir); + *progssrcdir = '\0'; + } + } + + resetprogssrc = true; + + wndclass.style = 0; + wndclass.lpfnWndProc = (WNDPROC)MainWndProc; + wndclass.cbClsExtra = 0; + wndclass.cbWndExtra = 0; + wndclass.hInstance = ghInstance; + wndclass.hIcon = 0; + wndclass.hCursor = LoadCursor (NULL,IDC_ARROW); + wndclass.hbrBackground = (void *)COLOR_WINDOW; + wndclass.lpszMenuName = 0; + wndclass.lpszClassName = MDI_WINDOW_CLASS_NAME; + RegisterClass(&wndclass); + + mainwindow=CreateWindow(MDI_WINDOW_CLASS_NAME, "FTE QuakeC compiler", WS_OVERLAPPEDWINDOW, + 0, 0, 640, 480, NULL, NULL, ghInstance, NULL); + + if (mdibox) + { + SetWindowText(mainwindow, "FTE QuakeC Development Suite"); + } + + if (!mainwindow) + { + MessageBox(NULL, "Failed to create main window", "Error", 0); + return 0; + } + + InitCommonControls(); +/* + outputbox=CreateWindowEx(WS_EX_CLIENTEDGE, + "EDIT", + "", + WS_CHILD | ES_READONLY | WS_VISIBLE | + WS_VSCROLL | ES_LEFT | ES_WANTRETURN | + ES_MULTILINE | ES_AUTOVSCROLL, + 0, 0, 0, 0, + mainwindow, + NULL, + ghInstance, + NULL); +*/ + + if (!mdibox) + outputbox = CreateAnEditControl(mainwindow); + + for (i = 0; i < NUMBUTTONS; i++) + { + buttons[i].hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, + "BUTTON", + buttons[i].text, + WS_CHILD | WS_VISIBLE, + 0, 0, 5, 5, + mainwindow, + (HMENU)(i+1), + ghInstance, + NULL); + } + + ShowWindow(mainwindow, SW_SHOWDEFAULT); + + if (fl_compileonstart) + { + CreateOutputWindow(); + RunCompiler(lpCmdLine); + } + else + { + if (mdibox) + { + buttons[ID_EDIT].washit = true; + } + else + { + GUIprintf("Welcome to FTE QCC\n"); + GUIprintf("Source file: "); + GUIprintf(progssrcname); + GUIprintf("\n"); + + RunCompiler("-?"); + } + } + + while(mainwindow || editors) + { + MSG msg; + + if (resetprogssrc) + { //this here, with the compiler below, means that we don't run recursivly. + resetprogssrc = false; + SetProgsSrc(); + } + + EditorsRun(); + + while (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) + { + if (!GetMessage (&msg, NULL, 0, 0)) + break; + if (!mdibox || !TranslateMDISysAccel(mdibox, &msg)) + { + TranslateMessage (&msg); + DispatchMessage (&msg); + } + } + + if (mainwindow) + { + i = Edit_GetSel(outputbox); + if ((i>>16) != (i&0xffff) && i != -1) //some text is selected. + { + int bytes; + char line[1024]; + char *colon1, *colon2 = NULL; + + int l1; + int l2; + + l1 = Edit_LineFromChar(outputbox, i&0xffff); + l2 = Edit_LineFromChar(outputbox, (i>>16)&0xffff); + if (l1 == l2) + { + bytes = Edit_GetLine(outputbox, Edit_LineFromChar(outputbox, i&0xffff), line, sizeof(line)-1); + line[bytes] = 0; + + for (colon1 = line+strlen(line)-1; *colon1 <= ' ' && colon1>=line; colon1--) + *colon1 = '\0'; + if (!strncmp(line, "warning: ", 9)) + memmove(line, line+9, sizeof(line)); + colon1=line; + do + { + colon1 = strchr(colon1+1, ':'); + } while (colon1 && colon1[1] == '\\'); + + if (colon1) + { + colon2 = strchr(colon1+1, ':'); + while (colon2 && colon2[1] == '\\') + { + colon2 = strchr(colon2+1, ':'); + } + if (colon2) + { + *colon1 = '\0'; + *colon2 = '\0'; + EditFile(line, atoi(colon1+1)-1); + } + else if (!strncmp(line, "Source file: ", 13)) + EditFile(line+13, -1); + else if (!strncmp(line, "Including: ", 11)) + EditFile(line+11, -1); + } + else if (!strncmp(line, "compiling ", 10)) + EditFile(line+10, -1); + else if (!strncmp(line, "prototyping ", 12)) + EditFile(line+12, -1); + Edit_SetSel(outputbox, i&0xffff, i&0xffff); //deselect it. + } + } + + if (buttons[ID_COMPILE].washit) + { + CreateOutputWindow(); + RunCompiler(parameters); + + buttons[ID_COMPILE].washit = false; + } + if (buttons[ID_EDIT].washit) + { + buttons[ID_EDIT].washit = false; + EditFile(progssrcname, -1); + } + if (buttons[ID_OPTIONS].washit) + { + buttons[ID_OPTIONS].washit = false; + OptionsDialog(); + } + if (buttons[ID_QUIT].washit) + { + buttons[ID_QUIT].washit = false; + DestroyWindow(mainwindow); + } + } + + if (*finddef) + { + GoToDefinition(finddef); + *finddef = '\0'; + } + + Sleep(10); + } + + return 0; +} diff --git a/misc/mediasource/extra/fteqcc-src/qccguistuff.c b/misc/mediasource/extra/fteqcc-src/qccguistuff.c new file mode 100644 index 00000000..b9555312 --- /dev/null +++ b/misc/mediasource/extra/fteqcc-src/qccguistuff.c @@ -0,0 +1,325 @@ +#include "qcc.h" +#include "gui.h" + +//common gui things + +pbool fl_hexen2; +pbool fl_autohighlight; +pbool fl_compileonstart; +pbool fl_showall; +pbool fl_log; + +char parameters[16384]; +char progssrcname[256]; +char progssrcdir[256]; + +int qccpersisthunk = 1; +void GoToDefinition(char *name) +{ + QCC_def_t *def; + QCC_dfunction_t *fnc; + + char *strip; //trim whitespace (for convieniance). + while (*name <= ' ' && *name) + name++; + for (strip = name + strlen(name)-1; *strip; strip++) + { + if (*strip <= ' ') + *strip = '\0'; + else //got some part of a word + break; + } + + if (!globalstable.numbuckets) + { + GUI_DialogPrint("Not found", "You need to compile first."); + return; + } + + + def = QCC_PR_GetDef(NULL, name, NULL, false, 0, false); + + if (def) + { + if (def->type->type == ev_function && def->constant) + { + fnc = &functions[((int *)qcc_pr_globals)[def->ofs]]; + if (fnc->first_statement>=0 && fnc->s_file) + { + EditFile(fnc->s_file+strings, statement_linenums[fnc->first_statement]); + return; + } + } + if (!def->s_file) + GUI_DialogPrint("Not found", "Global definition of var was not specified."); + else + EditFile(def->s_file+strings, def->s_line-1); + } + else + GUI_DialogPrint("Not found", "Global instance of var was not found."); +} + + + + +//this function takes the windows specified commandline and strips out all the options menu items. +void GUI_ParseCommandLine(char *args) +{ + int paramlen=0; + int l, p; + char *next; + while(*args) + { + while (*args <= ' ' && *args) + args++; + + for (next = args; *next>' '; next++) + ; + + strncpy(parameters+paramlen, args, next-args); + parameters[paramlen+next-args] = '\0'; + l = strlen(parameters+paramlen)+1; + + if (!strnicmp(parameters+paramlen, "-O", 2) || !strnicmp(parameters+paramlen, "/O", 2)) + { //strip out all -O + if (parameters[paramlen+2]) + { + if (parameters[paramlen+2] >= '0' && parameters[paramlen+2] <= '3') + { + p = parameters[paramlen+2]-'0'; + for (l = 0; optimisations[l].enabled; l++) + { + if (optimisations[l].optimisationlevel<=p) + optimisations[l].flags |= FLAG_SETINGUI; + else + optimisations[l].flags &= ~FLAG_SETINGUI; + } + } + else if (!strncmp(parameters+paramlen+2, "no-", 3)) + { + if (parameters[paramlen+5]) + { + for (p = 0; optimisations[p].enabled; p++) + if ((*optimisations[p].abbrev && !strcmp(parameters+paramlen+5, optimisations[p].abbrev)) || !strcmp(parameters+paramlen+5, optimisations[p].fullname)) + { + optimisations[p].flags &= ~FLAG_SETINGUI; + break; + } + + if (!optimisations[p].enabled) + { + parameters[paramlen+next-args] = ' '; + paramlen += l; + } + } + } + else + { + for (p = 0; optimisations[p].enabled; p++) + if ((*optimisations[p].abbrev && !strcmp(parameters+paramlen+2, optimisations[p].abbrev)) || !strcmp(parameters+paramlen+2, optimisations[p].fullname)) + { + optimisations[p].flags |= FLAG_SETINGUI; + break; + } + + if (!optimisations[p].enabled) + { + parameters[paramlen+next-args] = ' '; + paramlen += l; + } + } + } + } +/* + else if (!strnicmp(parameters+paramlen, "-Fno-kce", 8) || !strnicmp(parameters+paramlen, "/Fno-kce", 8)) //keywords stuph + { + fl_nokeywords_coexist = true; + } + else if (!strnicmp(parameters+paramlen, "-Fkce", 5) || !strnicmp(parameters+paramlen, "/Fkce", 5)) + { + fl_nokeywords_coexist = false; + } + else if (!strnicmp(parameters+paramlen, "-Facc", 5) || !strnicmp(parameters+paramlen, "/Facc", 5)) + { + fl_acc = true; + } + else if (!strnicmp(parameters+paramlen, "-autoproto", 10) || !strnicmp(parameters+paramlen, "/autoproto", 10)) + { + fl_autoprototype = true; + } +*/ + else if (!strnicmp(parameters+paramlen, "-showall", 8) || !strnicmp(parameters+paramlen, "/showall", 8)) + { + fl_showall = true; + } + else if (!strnicmp(parameters+paramlen, "-ah", 3) || !strnicmp(parameters+paramlen, "/ah", 3)) + { + fl_autohighlight = true; + } + else if (!strnicmp(parameters+paramlen, "-ac", 3) || !strnicmp(parameters+paramlen, "/ac", 3)) + { + fl_compileonstart = true; + } + else if (!strnicmp(parameters+paramlen, "-log", 4) || !strnicmp(parameters+paramlen, "/log", 4)) + { + fl_log = true; + } + else if (!strnicmp(parameters+paramlen, "-srcfile", 8) || !strnicmp(parameters+paramlen, "/srcfile", 8)) + { + while (*next == ' ') + next++; + + l = 0; + while (*next != ' ' && *next) + progssrcname[l++] = *next++; + progssrcname[l] = 0; + } + else if (!strnicmp(parameters+paramlen, "-T", 2) || !strnicmp(parameters+paramlen, "/T", 2)) //the target + { + if (!strnicmp(parameters+paramlen+2, "h2", 2)) + { + fl_hexen2 = true; + } + else + { + fl_hexen2 = false; + parameters[paramlen+next-args] = ' '; + paramlen += l; + } + } + else + { + parameters[paramlen+next-args] = ' '; + paramlen += l; + } + + args=next; + } + if (paramlen) + parameters[paramlen-1] = '\0'; + else + *parameters = '\0'; +} + +void GUI_SetDefaultOpts(void) +{ + int i; + for (i = 0; compiler_flag[i].enabled; i++) //enabled is a pointer + { + if (compiler_flag[i].flags & FLAG_ASDEFAULT) + compiler_flag[i].flags |= FLAG_SETINGUI; + else + compiler_flag[i].flags &= ~FLAG_SETINGUI; + } + for (i = 0; optimisations[i].enabled; i++) //enabled is a pointer + { + if (optimisations[i].flags & FLAG_ASDEFAULT) + optimisations[i].flags |= FLAG_SETINGUI; + else + optimisations[i].flags &= ~FLAG_SETINGUI; + } +} + +void GUI_RevealOptions(void) +{ + int i; + for (i = 0; compiler_flag[i].enabled; i++) //enabled is a pointer + { + if (fl_showall && compiler_flag[i].flags & FLAG_HIDDENINGUI) + compiler_flag[i].flags &= ~FLAG_HIDDENINGUI; + } + for (i = 0; optimisations[i].enabled; i++) //enabled is a pointer + { + if (fl_showall && optimisations[i].flags & FLAG_HIDDENINGUI) + optimisations[i].flags &= ~FLAG_HIDDENINGUI; + + if (optimisations[i].flags & FLAG_HIDDENINGUI) //hidden optimisations are disabled as default + optimisations[i].optimisationlevel = 4; + } +} + + + + +int GUI_BuildParms(char *args, char **argv) +{ + static char param[2048]; + int paramlen = 0; + int argc; + char *next; + int i; + + + argc = 1; + argv[0] = "fteqcc"; + + while(*args) + { + while (*args <= ' '&& *args) + args++; + + for (next = args; *next>' '; next++) + ; + strncpy(param+paramlen, args, next-args); + param[paramlen+next-args] = '\0'; + argv[argc++] = param+paramlen; + paramlen += strlen(param+paramlen)+1; + + args=next; + } + + if (fl_hexen2) + { + strcpy(param+paramlen, "-Th2"); + argv[argc++] = param+paramlen; + paramlen += strlen(param+paramlen)+1; + } + + for (i = 0; optimisations[i].enabled; i++) //enabled is a pointer + { + if (optimisations[i].flags & FLAG_SETINGUI) + sprintf(param+paramlen, "-O%s", optimisations[i].abbrev); + else + sprintf(param+paramlen, "-Ono-%s", optimisations[i].abbrev); + argv[argc++] = param+paramlen; + paramlen += strlen(param+paramlen)+1; + } + + for (i = 0; compiler_flag[i].enabled; i++) //enabled is a pointer + { + if (compiler_flag[i].flags & FLAG_SETINGUI) + sprintf(param+paramlen, "-F%s", compiler_flag[i].abbrev); + else + sprintf(param+paramlen, "-Fno-%s", compiler_flag[i].abbrev); + argv[argc++] = param+paramlen; + paramlen += strlen(param+paramlen)+1; + } + + +/* while(*args) + { + while (*args <= ' '&& *args) + args++; + + for (next = args; *next>' '; next++) + ; + strncpy(param+paramlen, args, next-args); + param[paramlen+next-args] = '\0'; + argv[argc++] = param+paramlen; + paramlen += strlen(param+paramlen)+1; + args=next; + }*/ + + if (*progssrcname) + { + argv[argc++] = "-srcfile"; + argv[argc++] = progssrcname; + } + if (*progssrcdir) + { + argv[argc++] = "-src"; + argv[argc++] = progssrcdir; + } + + return argc; +} diff --git a/misc/mediasource/extra/fteqcc-src/qccmain.c b/misc/mediasource/extra/fteqcc-src/qccmain.c new file mode 100644 index 00000000..a410ab08 --- /dev/null +++ b/misc/mediasource/extra/fteqcc-src/qccmain.c @@ -0,0 +1,3478 @@ +#ifndef MINIMAL + +#define PROGSUSED +#include "qcc.h" +int mkdir(const char *path); + +char QCC_copyright[1024]; +int QCC_packid; +char QCC_Packname[5][128]; + +extern QCC_def_t *functemps; //floats/strings/funcs/ents... + +extern int optres_test1; +extern int optres_test2; + +int writeasm; +static pbool pr_werror; +pbool verbose; + + +pbool QCC_PR_SimpleGetToken (void); +void QCC_PR_LexWhitespace (void); + +void *FS_ReadToMem(char *fname, void *membuf, int *len); +void FS_CloseFromMem(void *mem); + +struct qcc_includechunk_s *currentchunk; + +unsigned int MAX_REGS; + +int MAX_STRINGS; +int MAX_GLOBALS; +int MAX_FIELDS; +int MAX_STATEMENTS; +int MAX_FUNCTIONS; +int MAX_CONSTANTS; +int max_temps; + +int *qcc_tempofs; +int tempsstart; +int numtemps; + +#define MAXSOURCEFILESLIST 8 +char sourcefileslist[MAXSOURCEFILESLIST][1024]; +int currentsourcefile; +int numsourcefiles; + +void QCC_PR_ResetErrorScope(void); + +pbool compressoutput; + +pbool newstylesource; +char destfile[1024]; + +float *qcc_pr_globals; +unsigned int numpr_globals; + +char *strings; +int strofs; + +QCC_dstatement_t *statements; +int numstatements; +int *statement_linenums; + +QCC_dfunction_t *functions; +int numfunctions; + +QCC_ddef_t *qcc_globals; +int numglobaldefs; + +QCC_ddef_t *fields; +int numfielddefs; + +//typedef char PATHSTRING[MAX_DATA_PATH]; + +PATHSTRING *precache_sounds; +int *precache_sounds_block; +int numsounds; + +PATHSTRING *precache_textures; +int *precache_textures_block; +int numtextures; + +PATHSTRING *precache_models; +int *precache_models_block; +int nummodels; + +PATHSTRING *precache_files; +int *precache_files_block; +int numfiles; + +extern int numCompilerConstants; +hashtable_t compconstantstable; +hashtable_t globalstable; +hashtable_t localstable; +hashtable_t floatconstdefstable; +hashtable_t stringconstdefstable; +hashtable_t stringconstdefstable_dotranslate; +int dotranslate; +int dotranslate_count; + +pbool qccwarningdisabled[WARN_MAX]; + +qcc_targetformat_t qcc_targetformat; + +pbool bodylessfuncs; + +QCC_type_t *qcc_typeinfo; +int numtypeinfos; +int maxtypeinfos; + + +struct { + char *name; + int index; +} warningnames[] = +{ + {"Q302", WARN_NOTREFERENCED}, +// {"", WARN_NOTREFERENCEDCONST}, +// {"", WARN_CONFLICTINGRETURNS}, + {"Q105", WARN_TOOFEWPARAMS}, + {"Q101", WARN_TOOMANYPARAMS}, +// {"", WARN_UNEXPECTEDPUNCT}, + {"Q106", WARN_ASSIGNMENTTOCONSTANT}, + {"Q203", WARN_MISSINGRETURNVALUE}, + {"Q204", WARN_WRONGRETURNTYPE}, + {"Q205", WARN_POINTLESSSTATEMENT}, + {"Q206", WARN_MISSINGRETURN}, + {"Q207", WARN_DUPLICATEDEFINITION}, + {"Q100", WARN_PRECOMPILERMESSAGE}, +// {"", WARN_STRINGTOOLONG}, +// {"", WARN_BADTARGET}, + {"Q120", WARN_BADPRAGMA}, +// {"", WARN_HANGINGSLASHR}, +// {"", WARN_NOTDEFINED}, +// {"", WARN_SWITCHTYPEMISMATCH}, +// {"", WARN_CONFLICTINGUNIONMEMBER}, +// {"", WARN_KEYWORDDISABLED}, +// {"", WARN_ENUMFLAGS_NOTINTEGER}, +// {"", WARN_ENUMFLAGS_NOTBINARY}, +// {"", WARN_CASEINSENSATIVEFRAMEMACRO}, + {"Q111", WARN_DUPLICATELABEL}, + {"Q201", WARN_ASSIGNMENTINCONDITIONAL}, + {"F300", WARN_DEADCODE}, + {NULL} +}; + +int QCC_WarningForName(char *name) +{ + int i; + for (i = 0; warningnames[i].name; i++) + { + if (!stricmp(name, warningnames[i].name)) + return warningnames[i].index; + } + return -1; +} + +optimisations_t optimisations[] = +{ + //level 0 = no optimisations + //level 1 = size optimisations + //level 2 = speed optimisations + //level 3 = dodgy optimisations. + //level 4 = experimental... + + {&opt_assignments, "t", 1, FLAG_ASDEFAULT, "assignments", "c = a*b is performed in one operation rather than two, and can cause older decompilers to fail."}, + {&opt_shortenifnots, "i", 1, FLAG_ASDEFAULT, "shortenifs", "if (!a) was traditionally compiled in two statements. This optimisation does it in one, but can cause some decompilers to get confused."}, + {&opt_nonvec_parms, "p", 1, FLAG_ASDEFAULT, "nonvec_parms", "In the original qcc, function parameters were specified as a vector store even for floats. This fixes that."}, + {&opt_constant_names, "c", 2, FLAG_KILLSDEBUGGERS, "constant_names", "This optimisation strips out the names of constants (but not strings) from your progs, resulting in smaller files. It makes decompilers leave out names or fabricate numerical ones."}, + {&opt_constant_names_strings, "cs", 3, FLAG_KILLSDEBUGGERS, "constant_names_strings", "This optimisation strips out the names of string constants from your progs. However, this can break addons, so don't use it in those cases."}, + {&opt_dupconstdefs, "d", 1, FLAG_ASDEFAULT, "dupconstdefs", "This will merge definitions of constants which are the same value. Pay extra attention to assignment to constant warnings."}, + {&opt_noduplicatestrings, "s", 1, 0, "noduplicatestrings", "This will compact the string table that is stored in the progs. It will be considerably smaller with this."}, + {&opt_locals, "l", 1, FLAG_KILLSDEBUGGERS, "locals", "Strips out local names and definitions. This makes it REALLY hard to decompile"}, + {&opt_function_names, "n", 1, FLAG_KILLSDEBUGGERS, "function_names", "This strips out the names of functions which are never called. Doesn't make much of an impact though."}, + {&opt_filenames, "f", 1, FLAG_KILLSDEBUGGERS, "filenames", "This strips out the filenames of the progs. This can confuse the really old decompilers, but is nothing to the more recent ones."}, + {&opt_unreferenced, "u", 1, FLAG_ASDEFAULT, "unreferenced", "Removes the entries of unreferenced variables. Doesn't make a difference in well maintained code."}, + {&opt_overlaptemps, "r", 1, FLAG_ASDEFAULT, "overlaptemps", "Optimises the pr_globals count by overlapping temporaries. In QC, every multiplication, division or operation in general produces a temporary variable. This optimisation prevents excess, and in the case of Hexen2's gamecode, reduces the count by 50k. This is the most important optimisation, ever."}, + {&opt_constantarithmatic, "a", 1, FLAG_ASDEFAULT, "constantarithmatic", "5*6 actually emits an operation into the progs. This prevents that happening, effectivly making the compiler see 30"}, + {&opt_precache_file, "pf", 2, 0, "precache_file", "Strip out stuff wasted used in function calls and strings to the precache_file builtin (which is actually a stub in quake)."}, + {&opt_return_only, "ro", 3, FLAG_KILLSDEBUGGERS, "return_only", "Functions ending in a return statement do not need a done statement at the end of the function. This can confuse some decompilers, making functions appear larger than they were."}, + {&opt_compound_jumps, "cj", 3, FLAG_KILLSDEBUGGERS, "compound_jumps", "This optimisation plays an effect mostly with nested if/else statements, instead of jumping to an unconditional jump statement, it'll jump to the final destination instead. This will bewilder decompilers."}, +// {&opt_comexprremoval, "cer", 4, 0, "expression_removal", "Eliminate common sub-expressions"}, //this would be too hard... + {&opt_stripfunctions, "sf", 3, 0, "strip_functions", "Strips out the 'defs' of functions that were only ever called directly. This does not affect saved games."}, + {&opt_locals_marshalling, "lm", 4, FLAG_KILLSDEBUGGERS, "locals_marshalling", "Store all locals in one section of the pr_globals. Vastly reducing it. This effectivly does the job of overlaptemps. It's been noticed as buggy by a few, however, and the curcumstances where it causes problems are not yet known."}, + {&opt_vectorcalls, "vc", 4, FLAG_KILLSDEBUGGERS, "vectorcalls", "Where a function is called with just a vector, this causes the function call to store three floats instead of one vector. This can save a good number of pr_globals where those vectors contain many duplicate coordinates but do not match entirly."}, + {NULL} +}; + +#define defaultkeyword FLAG_HIDDENINGUI|FLAG_ASDEFAULT|FLAG_MIDCOMPILE +#define nondefaultkeyword FLAG_HIDDENINGUI|0|FLAG_MIDCOMPILE +//global to store useage to, flags, codename, human-readable name, help text +compiler_flag_t compiler_flag[] = { + //keywords + {&keyword_asm, defaultkeyword, "asm", "Keyword: asm", "Disables the 'asm' keyword. Use the writeasm flag to see an example of the asm."}, + {&keyword_break, defaultkeyword, "break", "Keyword: break", "Disables the 'break' keyword."}, + {&keyword_case, defaultkeyword, "case", "Keyword: case", "Disables the 'case' keyword."}, + {&keyword_class, defaultkeyword, "class", "Keyword: class", "Disables the 'class' keyword."}, + {&keyword_const, defaultkeyword, "const", "Keyword: const", "Disables the 'const' keyword."}, + {&keyword_continue, defaultkeyword, "continue", "Keyword: continue", "Disables the 'continue' keyword."}, + {&keyword_default, defaultkeyword, "default", "Keyword: default", "Disables the 'default' keyword."}, + {&keyword_entity, defaultkeyword, "entity", "Keyword: entity", "Disables the 'entity' keyword."}, + {&keyword_enum, defaultkeyword, "enum", "Keyword: enum", "Disables the 'enum' keyword."}, //kinda like in c, but typedef not supported. + {&keyword_enumflags, defaultkeyword, "enumflags", "Keyword: enumflags", "Disables the 'enumflags' keyword."}, //like enum, but doubles instead of adds 1. + {&keyword_extern, defaultkeyword, "extern", "Keyword: extern", "Disables the 'extern' keyword. Use only on functions inside addons."}, //function is external, don't error or warn if the body was not found + {&keyword_float, defaultkeyword, "float", "Keyword: float", "Disables the 'float' keyword. (Disables the float keyword without 'local' preceeding it)"}, + {&keyword_for, defaultkeyword, "for", "Keyword: for", "Disables the 'for' keyword. Syntax: for(assignment; while; increment) {codeblock;}"}, + {&keyword_goto, defaultkeyword, "goto", "Keyword: goto", "Disables the 'goto' keyword."}, + {&keyword_int, defaultkeyword, "int", "Keyword: int", "Disables the 'int' keyword."}, + {&keyword_integer, defaultkeyword, "integer", "Keyword: integer", "Disables the 'integer' keyword."}, + {&keyword_noref, defaultkeyword, "noref", "Keyword: noref", "Disables the 'noref' keyword."}, //nowhere else references this, don't strip it. + {&keyword_nosave, defaultkeyword, "nosave", "Keyword: nosave", "Disables the 'nosave' keyword."}, //don't write the def to the output. + {&keyword_shared, defaultkeyword, "shared", "Keyword: shared", "Disables the 'shared' keyword."}, //mark global to be copied over when progs changes (part of FTE_MULTIPROGS) + {&keyword_state, nondefaultkeyword,"state", "Keyword: state", "Disables the 'state' keyword."}, + {&keyword_string, defaultkeyword, "string", "Keyword: string", "Disables the 'string' keyword."}, + {&keyword_struct, defaultkeyword, "struct", "Keyword: struct", "Disables the 'struct' keyword."}, + {&keyword_switch, defaultkeyword, "switch", "Keyword: switch", "Disables the 'switch' keyword."}, + {&keyword_thinktime, nondefaultkeyword,"thinktime", "Keyword: thinktime", "Disables the 'thinktime' keyword which is used in HexenC"}, + {&keyword_typedef, defaultkeyword, "typedef", "Keyword: typedef", "Disables the 'typedef' keyword."}, //fixme + {&keyword_union, defaultkeyword, "union", "Keyword: union", "Disables the 'union' keyword."}, //you surly know what a union is! + {&keyword_var, defaultkeyword, "var", "Keyword: var", "Disables the 'var' keyword."}, + {&keyword_vector, defaultkeyword, "vector", "Keyword: vector", "Disables the 'vector' keyword."}, + + + //options + {&keywords_coexist, FLAG_ASDEFAULT, "kce", "Keywords Coexist", "If you want keywords to NOT be disabled when they a variable by the same name is defined, check here."}, + {&output_parms, 0, "parms", "Define offset parms", "if PARM0 PARM1 etc should be defined by the compiler. These are useful if you make use of the asm keyword for function calls, or you wish to create your own variable arguments. This is an easy way to break decompilers."}, //controls weather to define PARMx for the parms (note - this can screw over some decompilers) + {&autoprototype, 0, "autoproto", "Automatic Prototyping","Causes compilation to take two passes instead of one. The first pass, only the definitions are read. The second pass actually compiles your code. This means you never have to remember to prototype functions again."}, //so you no longer need to prototype functions and things in advance. + {&writeasm, 0, "wasm", "Dump Assembler", "Writes out a qc.asm which contains all your functions but in assembler. This is a great way to look for bugs in fteqcc, but can also be used to see exactly what your functions turn into, and thus how to optimise statements better."}, //spit out a qc.asm file, containing an assembler dump of the ENTIRE progs. (Doesn't include initialisation of constants) + {&flag_ifstring, FLAG_MIDCOMPILE,"ifstring", "if(string) fix", "Causes if(string) to behave identically to if(string!="") This is most useful with addons of course, but also has adverse effects with FRIK_FILE's fgets, where it becomes impossible to determin the end of the file. In such a case, you can still use asm {IF string 2;RETURN} to detect eof and leave the function."}, //correction for if(string) no-ifstring to get the standard behaviour. + {&flag_iffloat, FLAG_MIDCOMPILE,"iffloat","if(-0.0) fix","Fixes certain floating point logic."}, + {&flag_acc, 0, "acc", "Reacc support", "Reacc is a pascall like compiler. It was released before the Quake source was released. This flag has a few effects. It sorts all qc files in the current directory into alphabetical order to compile them. It also allows Reacc global/field distinctions, as well as allows ¦ as EOF. Whilst case insensativity and lax type checking are supported by reacc, they are seperate compiler flags in fteqcc."}, //reacc like behaviour of src files. + {&flag_caseinsensative, 0, "caseinsens", "Case insensativity", "Causes fteqcc to become case insensative whilst compiling names. It's generally not advised to use this as it compiles a little more slowly and provides little benefit. However, it is required for full reacc support."}, //symbols will be matched to an insensative case if the specified case doesn't exist. This should b usable for any mod + {&flag_laxcasts, FLAG_MIDCOMPILE,"lax", "Lax type checks", "Disables many errors (generating warnings instead) when function calls or operations refer to two normally incompatible types. This is required for reacc support, and can also allow certain (evil) mods to compile that were originally written for frikqcc."}, //Allow lax casting. This'll produce loadsa warnings of course. But allows compilation of certain dodgy code. + {&flag_hashonly, FLAG_MIDCOMPILE,"hashonly", "Hash-only constants", "Allows use of only #constant for precompiler constants, allows certain preqcc using mods to compile"}, + {&opt_logicops, FLAG_MIDCOMPILE,"lo", "Logic ops", "This changes the behaviour of your code. It generates additional if operations to early-out in if statements. With this flag, the line if (0 && somefunction()) will never call the function. It can thus be considered an optimisation. However, due to the change of behaviour, it is not considered so by fteqcc. Note that due to inprecisions with floats, this flag can cause runaway loop errors within the player walk and run functions (without iffloat also enabled). This code is advised:\nplayer_stand1:\n if (self.velocity_x || self.velocity_y)\nplayer_run\n if (!(self.velocity_x || self.velocity_y))"}, + {&flag_msvcstyle, FLAG_MIDCOMPILE,"msvcstyle", "MSVC-style errors", "Generates warning and error messages in a format that msvc understands, to facilitate ide integration."}, + {&flag_fasttrackarrays, FLAG_MIDCOMPILE|FLAG_ASDEFAULT,"fastarrays","fast arrays where possible", "Generates extra instructions inside array handling functions to detect engine and use extension opcodes only in supporting engines.\nAdds a global which is set by the engine if the engine supports the extra opcodes. Note that this applies to all arrays or none."}, + {&flag_assume_integer, FLAG_MIDCOMPILE,"assumeint", "Assume Integers", "Numerical constants are assumed to be integers, instead of floats."}, + {&pr_subscopedlocals, FLAG_MIDCOMPILE,"subscope", "Subscoped locals", "Allow locals to only be valid in the block they are defined in (like in C)."}, + {NULL} +}; + +struct { + qcc_targetformat_t target; + char *name; +} targets[] = { + {QCF_STANDARD, "standard"}, + {QCF_STANDARD, "q1"}, + {QCF_STANDARD, "quakec"}, + {QCF_HEXEN2, "hexen2"}, + {QCF_HEXEN2, "h2"}, + {QCF_KK7, "kkqwsv"}, + {QCF_KK7, "kk7"}, + {QCF_KK7, "bigprogs"}, + {QCF_KK7, "version7"}, + {QCF_KK7, "kkqwsv"}, + {QCF_FTE, "fte"}, + {QCF_DARKPLACES,"darkplaces"}, + {QCF_DARKPLACES,"dp"}, + {0, NULL} +}; + +/* +================= +BspModels + +Runs qbsp and light on all of the models with a .bsp extension +================= +*/ +int QCC_CheckParm (char *check); + +void QCC_BspModels (void) +{ + int p; + char *gamedir; + int i; + char *m; + char cmd[1024]; + char name[256]; + + p = QCC_CheckParm ("-bspmodels"); + if (!p) + return; + if (p == myargc-1) + QCC_Error (ERR_BADPARMS, "-bspmodels must preceed a game directory"); + gamedir = myargv[p+1]; + + for (i=0 ; istrings ; s--) + if (!strcmp(s, str)) + return s-strings; + + old = strofs; + strcpy (strings+strofs, str); + strofs += strlen(str)+1; + return old; +} + +void QCC_PrintStrings (void) +{ + int i, l, j; + + for (i=0 ; is_file, strings + d->s_name, d->first_statement, d->parm_start); + for (j=0 ; jnumparms ; j++) + printf ("%i ",d->parm_size[j]); + printf (")\n"); + } +}*/ + +void QCC_PrintFields (void) +{ + int i; + QCC_ddef_t *d; + + for (i=0 ; iofs, d->type, strings + d->s_name); + } +} + +void QCC_PrintGlobals (void) +{ + int i; + QCC_ddef_t *d; + + for (i=0 ; iofs, d->type, strings + d->s_name); + } +} + +int encode(int len, int method, char *in, int handle); +int WriteSourceFiles(int h, dprograms_t *progs, pbool sourceaswell) +{ + includeddatafile_t *idf; + qcc_cachedsourcefile_t *f; + int num=0; + int ofs; + + /* + for (f = qcc_sourcefile; f ; f=f->next) + { + if (f->type == FT_CODE && !sourceaswell) + continue; + + SafeWrite(h, f->filename, strlen(f->filename)+1); + i = PRLittleLong(f->size); + SafeWrite(h, &i, sizeof(int)); + + i = PRLittleLong(encrpytmode); + SafeWrite(h, &i, sizeof(int)); + + if (encrpytmode) + for (i = 0; i < f->size; i++) + f->file[i] ^= 0xA5; + + SafeWrite(h, f->file, f->size); + }*/ + + for (f = qcc_sourcefile,num=0; f ; f=f->next) + { + if (f->type == FT_CODE && !sourceaswell) + continue; + + num++; + } + if (!num) + return 0; + idf = qccHunkAlloc(sizeof(includeddatafile_t)*num); + for (f = qcc_sourcefile,num=0; f ; f=f->next) + { + if (f->type == FT_CODE && !sourceaswell) + continue; + + strcpy(idf[num].filename, f->filename); + idf[num].size = f->size; +#ifdef AVAIL_ZLIB + idf[num].compmethod = 2; +#else + idf[num].compmethod = 1; +#endif + idf[num].ofs = SafeSeek(h, 0, SEEK_CUR); + idf[num].compsize = QC_encode(progfuncs, f->size, idf[num].compmethod, f->file, h); + num++; + } + + ofs = SafeSeek(h, 0, SEEK_CUR); + SafeWrite(h, &num, sizeof(int)); + SafeWrite(h, idf, sizeof(includeddatafile_t)*num); + + qcc_sourcefile = NULL; + + return ofs; +} + +void QCC_InitData (void) +{ + static char parmname[12][MAX_PARMS]; + static temp_t ret_temp; + int i; + + qcc_sourcefile = NULL; + + numstatements = 1; + strofs = 1; + numfunctions = 1; + numglobaldefs = 1; + numfielddefs = 1; + + memset(&ret_temp, 0, sizeof(ret_temp)); + + def_ret.ofs = OFS_RETURN; + def_ret.name = "return"; + def_ret.temp = &ret_temp; + def_ret.constant = false; + def_ret.type = NULL; + ret_temp.ofs = def_ret.ofs; + ret_temp.scope = NULL; + ret_temp.size = 3; + ret_temp.next = NULL; + for (i=0 ; inext) + { + if (d->type->type == ev_function && !d->scope)// function parms are ok + { + if (d->initialized != 1 && d->references>0) + { + SafeWrite(handle, d->name, strlen(d->name)+1); + ret++; + } + } + } + + return ret; +} + +//marshalled locals remaps all the functions to use the range MAX_REGS onwards for the offset to their locals. +//this function remaps all the locals back into the function. +void QCC_UnmarshalLocals(void) +{ + QCC_def_t *def; + unsigned int ofs; + unsigned int maxo; + int i; + + ofs = numpr_globals; + maxo = ofs; + + for (def = pr.def_head.next ; def ; def = def->next) + { + if (def->ofs >= MAX_REGS) //unmap defs. + { + def->ofs = def->ofs + ofs - MAX_REGS; + if (maxo < def->ofs) + maxo = def->ofs; + } + } + + for (i = 0; i < numfunctions; i++) + { + if (functions[i].parm_start == MAX_REGS) + functions[i].parm_start = ofs; + } + + QCC_RemapOffsets(0, numstatements, MAX_REGS, MAX_REGS + maxo-numpr_globals + 3, ofs); + + numpr_globals = maxo+3; + if (numpr_globals > MAX_REGS) + QCC_Error(ERR_TOOMANYGLOBALS, "Too many globals are in use to unmarshal all locals"); + + if (maxo-ofs) + printf("Total of %i marshalled globals\n", maxo-ofs); +} + +CompilerConstant_t *QCC_PR_CheckCompConstDefined(char *def); +pbool QCC_WriteData (int crc) +{ + char element[MAX_NAME]; + QCC_def_t *def, *comp_x, *comp_y, *comp_z; + QCC_ddef_t *dd; + dprograms_t progs; + int h; + int i, len; + pbool debugtarget = false; + pbool types = false; + int outputsize = 16; + + if (numstatements==1 && numfunctions==1 && numglobaldefs==1 && numfielddefs==1) + { + printf("nothing to write\n"); + return false; + } + + progs.blockscompressed=0; + + if (numstatements > MAX_STATEMENTS) + QCC_Error(ERR_TOOMANYSTATEMENTS, "Too many statements - %i\nAdd \"MAX_STATEMENTS\" \"%i\" to qcc.cfg", numstatements, (numstatements+32768)&~32767); + + if (strofs > MAX_STRINGS) + QCC_Error(ERR_TOOMANYSTRINGS, "Too many strings - %i\nAdd \"MAX_STRINGS\" \"%i\" to qcc.cfg", strofs, (strofs+32768)&~32767); + + QCC_UnmarshalLocals(); + + switch (qcc_targetformat) + { + case QCF_HEXEN2: + case QCF_STANDARD: + if (bodylessfuncs) + printf("Warning: There are some functions without bodies.\n"); + + if (numpr_globals > 65530 ) + { + printf("Forcing target to FTE32 due to numpr_globals\n"); + outputsize = 32; + } + else if (qcc_targetformat == QCF_HEXEN2) + { + printf("Progs execution requires a Hexen2 compatible engine\n"); + break; + } + else + { + if (numpr_globals >= 32768) //not much of a different format. Rewrite output to get it working on original executors? + printf("An enhanced executor will be required (FTE/QF/KK)\n"); + else + printf("Progs should run on any Quake executor\n"); + break; + } + //intentional + qcc_targetformat = QCF_FTE; + case QCF_FTEDEBUG: + case QCF_FTE: + case QCF_DARKPLACES: + if (qcc_targetformat == QCF_FTEDEBUG) + debugtarget = true; + + if (numpr_globals > 65530) + { + printf("Using 32 bit target due to numpr_globals\n"); + outputsize = 32; + } + + if (qcc_targetformat == QCF_DARKPLACES) + compressoutput = 0; + + + //compression of blocks? + if (compressoutput) progs.blockscompressed |=1; //statements + if (compressoutput) progs.blockscompressed |=2; //defs + if (compressoutput) progs.blockscompressed |=4; //fields + if (compressoutput) progs.blockscompressed |=8; //functions + if (compressoutput) progs.blockscompressed |=16; //strings + if (compressoutput) progs.blockscompressed |=32; //globals + if (compressoutput) progs.blockscompressed |=64; //line numbers + if (compressoutput) progs.blockscompressed |=128; //types + //include a type block? + types = debugtarget;//!!QCC_PR_CheckCompConstDefined("TYPES"); //useful for debugging and saving (maybe, anyway...). + + if (verbose) + { + if (qcc_targetformat == QCF_DARKPLACES) + printf("DarkPlaces or FTE will be required\n"); + else + printf("An FTE executor will be required\n"); + } + break; + case QCF_KK7: + if (bodylessfuncs) + printf("Warning: There are some functions without bodies.\n"); + if (numpr_globals > 65530) + printf("Warning: Saving is not supported. Ensure all engine read fields and globals are defined early on.\n"); + + printf("A KK compatible executor will be required (FTE/KK)\n"); + break; + } + + //part of how compilation works. This def is always present, and never used. + def = QCC_PR_GetDef(NULL, "end_sys_globals", NULL, false, 0, false); + if (def) + def->references++; + + def = QCC_PR_GetDef(NULL, "end_sys_fields", NULL, false, 0, false); + if (def) + def->references++; + + for (def = pr.def_head.next ; def ; def = def->next) + { + if (def->type->type == ev_vector || (def->type->type == ev_field && def->type->aux_type->type == ev_vector)) + { //do the references, so we don't get loadsa not referenced VEC_HULL_MINS_x + sprintf(element, "%s_x", def->name); + comp_x = QCC_PR_GetDef(NULL, element, def->scope, false, 0, false); + sprintf(element, "%s_y", def->name); + comp_y = QCC_PR_GetDef(NULL, element, def->scope, false, 0, false); + sprintf(element, "%s_z", def->name); + comp_z = QCC_PR_GetDef(NULL, element, def->scope, false, 0, false); + + h = def->references; + if (comp_x && comp_y && comp_z) + { + h += comp_x->references; + h += comp_y->references; + h += comp_z->references; + + if (!def->references) + if (!comp_x->references || !comp_y->references || !comp_z->references) //one of these vars is useless... + h=0; + + def->references = h; + + + if (!h) + h = 1; + if (comp_x) + comp_x->references = h; + if (comp_y) + comp_y->references = h; + if (comp_z) + comp_z->references = h; + } + } + if (def->references<=0) + { + if (def->constant) + QCC_PR_Warning(WARN_NOTREFERENCEDCONST, strings + def->s_file, def->s_line, "%s no references", def->name); + else + QCC_PR_Warning(WARN_NOTREFERENCED, strings + def->s_file, def->s_line, "%s no references", def->name); + + if (opt_unreferenced && def->type->type != ev_field) + { + optres_unreferenced++; + continue; + } + } + + if (def->type->type == ev_function) + { + if (opt_function_names && functions[G_FUNCTION(def->ofs)].first_statement<0) + { + optres_function_names++; + def->name = ""; + } + if (!def->timescalled) + { + if (def->references<=1) + QCC_PR_Warning(WARN_DEADCODE, strings + def->s_file, def->s_line, "%s is never directly called or referenced (spawn function or dead code)", def->name); +// else +// QCC_PR_Warning(WARN_DEADCODE, strings + def->s_file, def->s_line, "%s is never directly called", def->name); + } + if (opt_stripfunctions && def->timescalled >= def->references-1) //make sure it's not copied into a different var. + { //if it ever does self.think then it could be needed for saves. + optres_stripfunctions++; //if it's only ever called explicitly, the engine doesn't need to know. + continue; + } + +// df = &functions[numfunctions]; +// numfunctions++; + + } + else if (def->type->type == ev_field)// && !def->constant) + { + dd = &fields[numfielddefs]; + numfielddefs++; + dd->type = def->type->aux_type->type; + dd->s_name = QCC_CopyString (def->name); + dd->ofs = G_INT(def->ofs); + } + else if ((def->scope||def->constant) && (def->type->type != ev_string || opt_constant_names_strings)) + { + if (opt_constant_names) + { + if (def->type->type == ev_string) + optres_constant_names_strings += strlen(def->name); + else + optres_constant_names += strlen(def->name); + continue; + } + } + +// if (!def->saved && def->type->type != ev_string) +// continue; + dd = &qcc_globals[numglobaldefs]; + numglobaldefs++; + + if (types) + dd->type = def->type-qcc_typeinfo; + else + dd->type = def->type->type; +#ifdef DEF_SAVEGLOBAL + if ( def->saved && ((!def->initialized || def->type->type == ev_function) +// && def->type->type != ev_function + && def->type->type != ev_field + && def->scope == NULL)) + { + dd->type |= DEF_SAVEGLOBAL; + } +#endif + if (def->shared) + dd->type |= DEF_SHARED; + + if (opt_locals && (def->scope || !strcmp(def->name, "IMMEDIATE"))) + { + dd->s_name = 0; + optres_locals += strlen(def->name); + } + else + dd->s_name = QCC_CopyString (def->name); + dd->ofs = def->ofs; + } + + for (i = 0; i < numglobaldefs; i++) + { + dd = &qcc_globals[i]; + if (!(dd->type & DEF_SAVEGLOBAL)) //only warn about saved ones. + continue; + + for (h = 0; h < numglobaldefs; h++) + { + if (i == h) + continue; + if (dd->ofs == qcc_globals[h].ofs) + { + if (dd->type != qcc_globals[h].type) + { + if (dd->type != ev_vector && qcc_globals[h].type != ev_float) + QCC_PR_Warning(0, NULL, 0, "Mismatched union global types (%s and %s)", strings+dd->s_name, strings+qcc_globals[h].s_name); + } + //remove the saveglobal flag on the duplicate globals. + qcc_globals[h].type &= ~DEF_SAVEGLOBAL; + } + } + } + for (i = 1; i < numfielddefs; i++) + { + dd = &fields[i]; + + if (dd->type == ev_vector) //just ignore vectors. + continue; + + for (h = 1; h < numfielddefs; h++) + { + if (i == h) + continue; + if (dd->ofs == fields[h].ofs) + { + if (dd->type != fields[h].type) + { + if (fields[h].type != ev_vector) + { + QCC_PR_Warning(0, NULL, 0, "Mismatched union field types (%s and %s)", strings+dd->s_name, strings+fields[h].s_name); + } + } + } + } + } + + if (numglobaldefs > MAX_GLOBALS) + QCC_Error(ERR_TOOMANYGLOBALS, "Too many globals - %i\nAdd \"MAX_GLOBALS\" \"%i\" to qcc.cfg", numglobaldefs, (numglobaldefs+32768)&~32767); + + + for (i = 0; i < nummodels; i++) + { + if (!precache_models_used[i]) + QCC_PR_Warning(WARN_EXTRAPRECACHE, NULL, 0, "Model %s was precached but not directly used", precache_models[i]); + else if (!precache_models_block[i]) + QCC_PR_Warning(WARN_NOTPRECACHED, NULL, 0, "Model %s was used but not precached", precache_models[i]); + } +//PrintStrings (); +//PrintFunctions (); +//PrintFields (); +//PrintGlobals (); +strofs = (strofs+3)&~3; + + if (verbose) + { + printf ("%6i strofs (of %i)\n", strofs, MAX_STRINGS); + printf ("%6i numstatements (of %i)\n", numstatements, MAX_STATEMENTS); + printf ("%6i numfunctions (of %i)\n", numfunctions, MAX_FUNCTIONS); + printf ("%6i numglobaldefs (of %i)\n", numglobaldefs, MAX_GLOBALS); + printf ("%6i numfielddefs (%i unique) (of %i)\n", numfielddefs, pr.size_fields, MAX_FIELDS); + printf ("%6i numpr_globals (of %i)\n", numpr_globals, MAX_REGS); + } + + if (!*destfile) + strcpy(destfile, "progs.dat"); + if (verbose) + printf("Writing %s\n", destfile); + h = SafeOpenWrite (destfile, 2*1024*1024); + SafeWrite (h, &progs, sizeof(progs)); + SafeWrite (h, "\r\n\r\n", 4); + SafeWrite (h, QCC_copyright, strlen(QCC_copyright)+1); + SafeWrite (h, "\r\n\r\n", 4); + while(SafeSeek (h, 0, SEEK_CUR) & 3)//this is a lame way to do it + { + SafeWrite (h, "\0", 1); + } + + progs.ofs_strings = SafeSeek (h, 0, SEEK_CUR); + progs.numstrings = strofs; + + if (progs.blockscompressed&16) + { + SafeWrite (h, &len, sizeof(int)); //save for later + len = QC_encode(progfuncs, strofs*sizeof(char), 2, (char *)strings, h); //write + i = SafeSeek (h, 0, SEEK_CUR); + SafeSeek(h, progs.ofs_strings, SEEK_SET);//seek back + len = PRLittleLong(len); + SafeWrite (h, &len, sizeof(int)); //write size. + SafeSeek(h, i, SEEK_SET); + } + else + SafeWrite (h, strings, strofs); + + progs.ofs_statements = SafeSeek (h, 0, SEEK_CUR); + progs.numstatements = numstatements; + + if (qcc_targetformat == QCF_HEXEN2) + { + for (i=0 ; i= OP_CALL1 && statements[i].op <= OP_CALL8) + QCC_Error(ERR_BADTARGETSWITCH, "Target switching produced incompatible instructions"); + else if (statements[i].op >= OP_CALL1H && statements[i].op <= OP_CALL8H) + statements[i].op = statements[i].op - OP_CALL1H + OP_CALL1; + } + } + + for (i=0 ; iMAX_PARMS)?MAX_PARMS:functions[i].numparms); + functions[i].locals = PRLittleLong (functions[i].locals); + } + + if (progs.blockscompressed&8) + { + SafeWrite (h, &len, sizeof(int)); //save for later + len = QC_encode(progfuncs, numfunctions*sizeof(QCC_dfunction_t), 2, (char *)functions, h); //write + i = SafeSeek (h, 0, SEEK_CUR); + SafeSeek(h, progs.ofs_functions, SEEK_SET);//seek back + len = PRLittleLong(len); + SafeWrite (h, &len, sizeof(int)); //write size. + SafeSeek(h, i, SEEK_SET); + } + else + SafeWrite (h, functions, numfunctions*sizeof(QCC_dfunction_t)); + + switch(outputsize) + { + case 32: + progs.ofs_globaldefs = SafeSeek (h, 0, SEEK_CUR); + progs.numglobaldefs = numglobaldefs; + for (i=0 ; i 60) + { + *s++ = '.'; + *s++ = '.'; + *s++ = '.'; + break; + } + } + *s++ = '"'; + *s++ = 0; + return buf; +} + + + +QCC_def_t *QCC_PR_DefForFieldOfs (gofs_t ofs) +{ + QCC_def_t *d; + + for (d=pr.def_head.next ; d ; d=d->next) + { + if (d->type->type != ev_field) + continue; + if (*((unsigned int *)&qcc_pr_globals[d->ofs]) == ofs) + return d; + } + QCC_Error (ERR_NOTDEFINED, "PR_DefForFieldOfs: couldn't find %i",ofs); + return NULL; +} + +/* +============ +PR_ValueString + +Returns a string describing *data in a type specific manner +============= +*/ +char *QCC_PR_ValueString (etype_t type, void *val) +{ + static char line[256]; + QCC_def_t *def; + QCC_dfunction_t *f; + + switch (type) + { + case ev_string: + sprintf (line, "%s", QCC_PR_String(strings + *(int *)val)); + break; + case ev_entity: + sprintf (line, "entity %i", *(int *)val); + break; + case ev_function: + f = functions + *(int *)val; + if (!f) + sprintf (line, "undefined function"); + else + sprintf (line, "%s()", strings + f->s_name); + break; + case ev_field: + def = QCC_PR_DefForFieldOfs ( *(int *)val ); + sprintf (line, ".%s", def->name); + break; + case ev_void: + sprintf (line, "void"); + break; + case ev_float: + sprintf (line, "%5.1f", *(float *)val); + break; + case ev_integer: + sprintf (line, "%i", *(int *)val); + break; + case ev_vector: + sprintf (line, "'%5.1f %5.1f %5.1f'", ((float *)val)[0], ((float *)val)[1], ((float *)val)[2]); + break; + case ev_pointer: + sprintf (line, "pointer"); + break; + default: + sprintf (line, "bad type %i", type); + break; + } + + return line; +} + +/* +============ +PR_GlobalString + +Returns a string with a description and the contents of a global, +padded to 20 field width +============ +*/ +/*char *QCC_PR_GlobalStringNoContents (gofs_t ofs) +{ + int i; + QCC_def_t *def; + void *val; + static char line[128]; + + val = (void *)&qcc_pr_globals[ofs]; + def = pr_global_defs[ofs]; + if (!def) +// Error ("PR_GlobalString: no def for %i", ofs); + sprintf (line,"%i(?""?""?)", ofs); + else + sprintf (line,"%i(%s)", ofs, def->name); + + i = strlen(line); + for ( ; i<16 ; i++) + strcat (line," "); + strcat (line," "); + + return line; +} + +char *QCC_PR_GlobalString (gofs_t ofs) +{ + char *s; + int i; + QCC_def_t *def; + void *val; + static char line[128]; + + val = (void *)&qcc_pr_globals[ofs]; + def = pr_global_defs[ofs]; + if (!def) + return QCC_PR_GlobalStringNoContents(ofs); + if (def->initialized && def->type->type != ev_function) + { + s = QCC_PR_ValueString (def->type->type, &qcc_pr_globals[ofs]); + sprintf (line,"%i(%s)", ofs, s); + } + else + sprintf (line,"%i(%s)", ofs, def->name); + + i = strlen(line); + for ( ; i<16 ; i++) + strcat (line," "); + strcat (line," "); + + return line; +}*/ + +/* +============ +PR_PrintOfs +============ +*/ +/*void QCC_PR_PrintOfs (gofs_t ofs) +{ + printf ("%s\n",QCC_PR_GlobalString(ofs)); +}*/ + +/* +================= +PR_PrintStatement +================= +*/ +/*void QCC_PR_PrintStatement (QCC_dstatement_t *s) +{ + int i; + + printf ("%4i : %4i : %s ", (int)(s - statements), statement_linenums[s-statements], pr_opcodes[s->op].opname); + i = strlen(pr_opcodes[s->op].opname); + for ( ; i<10 ; i++) + printf (" "); + + if (s->op == OP_IF || s->op == OP_IFNOT) + printf ("%sbranch %i",QCC_PR_GlobalString(s->a),s->b); + else if (s->op == OP_GOTO) + { + printf ("branch %i",s->a); + } + else if ( (unsigned)(s->op - OP_STORE_F) < 6) + { + printf ("%s",QCC_PR_GlobalString(s->a)); + printf ("%s", QCC_PR_GlobalStringNoContents(s->b)); + } + else + { + if (s->a) + printf ("%s",QCC_PR_GlobalString(s->a)); + if (s->b) + printf ("%s",QCC_PR_GlobalString(s->b)); + if (s->c) + printf ("%s", QCC_PR_GlobalStringNoContents(s->c)); + } + printf ("\n"); +}*/ + + +/* +============ +PR_PrintDefs +============ +*/ +/*void QCC_PR_PrintDefs (void) +{ + QCC_def_t *d; + + for (d=pr.def_head.next ; d ; d=d->next) + QCC_PR_PrintOfs (d->ofs); +}*/ + +QCC_type_t *QCC_PR_NewType (char *name, int basictype) +{ + if (numtypeinfos>= maxtypeinfos) + QCC_Error(ERR_TOOMANYTYPES, "Too many types"); + memset(&qcc_typeinfo[numtypeinfos], 0, sizeof(QCC_type_t)); + qcc_typeinfo[numtypeinfos].type = basictype; + qcc_typeinfo[numtypeinfos].name = name; + qcc_typeinfo[numtypeinfos].num_parms = 0; + qcc_typeinfo[numtypeinfos].param = NULL; + qcc_typeinfo[numtypeinfos].size = type_size[basictype]; + + + numtypeinfos++; + + return &qcc_typeinfo[numtypeinfos-1]; +} + +/* +============== +PR_BeginCompilation + +called before compiling a batch of files, clears the pr struct +============== +*/ +void QCC_PR_BeginCompilation (void *memory, int memsize) +{ + extern int recursivefunctiontype; + extern struct freeoffset_s *freeofs; + int i; + char name[16]; + + pr.memory = memory; + pr.max_memory = memsize; + + pr.def_tail = &pr.def_head; + + QCC_PR_ResetErrorScope(); + pr_scope = NULL; + +/* numpr_globals = RESERVED_OFS; + + for (i=0 ; iaux_type = type_float; + type_pointer->aux_type = QCC_PR_NewType("pointeraux", ev_float); + + type_function->aux_type = type_void; + + //type_field->aux_type = type_float; + + if (keyword_integer) + type_integer = QCC_PR_NewType("integer", ev_integer); + if (keyword_int) + type_integer = QCC_PR_NewType("int", ev_integer); + + + + if (output_parms) + { //this tends to confuse the brains out of decompilers. :) + numpr_globals = 1; + QCC_PR_GetDef(type_vector, "RETURN", NULL, true, 1, false)->references++; + for (i = 0; i < MAX_PARMS; i++) + { + sprintf(name, "PARM%i", i); + QCC_PR_GetDef(type_vector, name, NULL, true, 1, false)->references++; + } + } + else + { + numpr_globals = RESERVED_OFS; +// for (i=0 ; inext = NULL; + pr_error_count = 0; + pr_warning_count = 0; + recursivefunctiontype = 0; + + freeofs = NULL; +} + +/* +============== +PR_FinishCompilation + +called after all files are compiled to check for errors +Returns false if errors were detected. +============== +*/ +int QCC_PR_FinishCompilation (void) +{ + QCC_def_t *d; + int errors; + + errors = false; + +// check to make sure all functions prototyped have code + for (d=pr.def_head.next ; d ; d=d->next) + { + if (d->type->type == ev_function && !d->scope)// function parms are ok + { +// f = G_FUNCTION(d->ofs); +// if (!f || (!f->code && !f->builtin) ) + if (d->initialized==0) + { + if (!strncmp(d->name, "ArrayGet*", 9)) + { + QCC_PR_EmitArrayGetFunction(d, d->name+9); + pr_scope = NULL; + } + else if (!strncmp(d->name, "ArraySet*", 9)) + { + QCC_PR_EmitArraySetFunction(d, d->name+9); + pr_scope = NULL; + } + else if (!strncmp(d->name, "Class*", 6)) + { + QCC_PR_EmitClassFromFunction(d, d->name+6); + pr_scope = NULL; + } + else + { + QCC_PR_ParseErrorPrintDef(ERR_NOFUNC, d, "function %s was not defined",d->name); + bodylessfuncs = true; + errors = true; + } +// errors = true; + } + else if (d->initialized==2) + bodylessfuncs = true; + } + } + pr_scope = NULL; + + return !errors; +} + +//============================================================================= + +// FIXME: byte swap? + +// this is a 16 bit, non-reflected CRC using the polynomial 0x1021 +// and the initial and final xor values shown below... in other words, the +// CCITT standard CRC used by XMODEM + + +#define CRC_INIT_VALUE 0xffff +#define CRC_XOR_VALUE 0x0000 + +static unsigned short QCC_crctable[256] = +{ + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, + 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, + 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, + 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, + 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, + 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, + 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, + 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, + 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, + 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, + 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, + 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, + 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, + 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, + 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, + 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, + 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, + 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, + 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, + 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, + 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 +}; + +void QCC_CRC_Init(unsigned short *crcvalue) +{ + *crcvalue = CRC_INIT_VALUE; +} + +void QCC_CRC_ProcessByte(unsigned short *crcvalue, qbyte data) +{ + *crcvalue = (*crcvalue << 8) ^ QCC_crctable[(*crcvalue >> 8) ^ data]; +} + +unsigned short QCC_CRC_Value(unsigned short crcvalue) +{ + return crcvalue ^ CRC_XOR_VALUE; +} +//============================================================================= + + +/* +============ +PR_WriteProgdefs + +Writes the global and entity structures out +Returns a crc of the header, to be stored in the progs file for comparison +at load time. +============ +*/ +/* +char *Sva(char *msg, ...) +{ + va_list l; + static char buf[1024]; + + va_start(l, msg); + QC_vsnprintf (buf,sizeof(buf)-1, msg, l); + va_end(l); + + return buf; +} +*/ + +#define PROGDEFS_MAX_SIZE 16384 +//write (to file buf) and add to the crc +static void Add(char *p, unsigned short *crc, char *file) +{ + char *s; + int i = strlen(file); + if (i + strlen(p)+1 >= PROGDEFS_MAX_SIZE) + return; + for(s=p;*s;s++,i++) + { + QCC_CRC_ProcessByte(crc, *s); + file[i] = *s; + } + file[i]='\0'; +} +#define ADD(p) Add(p, &crc, file) +//#define ADD(p) {char *s;int i = strlen(p);for(s=p;*s;s++,i++){QCC_CRC_ProcessByte(&crc, *s);file[i] = *s;}file[i]='\0';} + +static void Add3(char *p, unsigned short *crc, char *file) +{ + char *s; + for(s=p;*s;s++) + QCC_CRC_ProcessByte(crc, *s); +} +#define ADD3(p) Add3(p, &crc, file) + +unsigned short QCC_PR_WriteProgdefs (char *filename) +{ +#define ADD2(p) strncat(file, p, PROGDEFS_MAX_SIZE-1 - strlen(file)) //no crc (later changes) + char file[PROGDEFS_MAX_SIZE]; + QCC_def_t *d; + int f; + unsigned short crc; +// int c; + + file[0] = '\0'; + + QCC_CRC_Init (&crc); + +// print global vars until the first field is defined + + //ADD: crc and dump + //ADD2: dump but don't crc + //ADD3: crc but don't dump + + ADD("\n/* "); + if (qcc_targetformat == QCF_HEXEN2) + ADD3("generated by hcc, do not modify"); + else + ADD3("file generated by qcc, do not modify"); + ADD2("File generated by FTEQCC, relevent for engine modding only, the generated crc must be the same as your engine expects."); + ADD(" */\n\ntypedef struct"); + ADD2(" globalvars_s"); + ADD(qcva("\n{")); + ADD2("\tint pad;\n" + "\tint ofs_return[3];\n" //makes it easier with the get globals func + "\tint ofs_parm0[3];\n" + "\tint ofs_parm1[3];\n" + "\tint ofs_parm2[3];\n" + "\tint ofs_parm3[3];\n" + "\tint ofs_parm4[3];\n" + "\tint ofs_parm5[3];\n" + "\tint ofs_parm6[3];\n" + "\tint ofs_parm7[3];\n"); + ADD3(qcva("\tint\tpad[%i];\n", RESERVED_OFS)); + for (d=pr.def_head.next ; d ; d=d->next) + { + if (!strcmp (d->name, "end_sys_globals")) + break; + if (d->ofstype->type) + { + case ev_float: + ADD(qcva("\tfloat\t%s;\n",d->name)); + break; + case ev_vector: + ADD(qcva("\tvec3_t\t%s;\n",d->name)); + d=d->next->next->next; // skip the elements + break; + case ev_string: + ADD(qcva("\tstring_t\t%s;\n",d->name)); + break; + case ev_function: + ADD(qcva("\tfunc_t\t%s;\n",d->name)); + break; + case ev_entity: + ADD(qcva("\tint\t%s;\n",d->name)); + break; + case ev_integer: + ADD(qcva("\tint\t%s;\n",d->name)); + break; + default: + ADD(qcva("\tint\t%s;\n",d->name)); + break; + } + } + ADD("} globalvars_t;\n\n"); + +// print all fields + ADD("typedef struct"); + ADD2(" entvars_s"); + ADD("\n{\n"); + for (d=pr.def_head.next ; d ; d=d->next) + { + if (!strcmp (d->name, "end_sys_fields")) + break; + + if (d->type->type != ev_field) + continue; + + switch (d->type->aux_type->type) + { + case ev_float: + ADD(qcva("\tfloat\t%s;\n",d->name)); + break; + case ev_vector: + ADD(qcva("\tvec3_t\t%s;\n",d->name)); + d=d->next->next->next; // skip the elements + break; + case ev_string: + ADD(qcva("\tstring_t\t%s;\n",d->name)); + break; + case ev_function: + ADD(qcva("\tfunc_t\t%s;\n",d->name)); + break; + case ev_entity: + ADD(qcva("\tint\t%s;\n",d->name)); + break; + case ev_integer: + ADD(qcva("\tint\t%s;\n",d->name)); + break; + default: + ADD(qcva("\tint\t%s;\n",d->name)); + break; + } + } + ADD("} entvars_t;\n\n"); + + ///temp + ADD2("//with this the crc isn't needed for fields.\n#ifdef FIELDSSTRUCT\nstruct fieldvars_s {\n\tint ofs;\n\tint type;\n\tchar *name;\n} fieldvars[] = {\n"); + f=0; + for (d=pr.def_head.next ; d ; d=d->next) + { + if (!strcmp (d->name, "end_sys_fields")) + break; + + if (d->type->type != ev_field) + continue; + if (f) + ADD2(",\n"); + ADD2(qcva("\t{%i,\t%i,\t\"%s\"}",G_INT(d->ofs), d->type->aux_type->type, d->name)); + f = 1; + } + ADD2("\n};\n#endif\n\n"); + //end temp + + ADD2(qcva("#define PROGHEADER_CRC %i\n", crc)); + + if (QCC_CheckParm("-progdefs")) + { + printf ("writing %s\n", filename); + f = SafeOpenWrite(filename, 16384); + SafeWrite(f, file, strlen(file)); + SafeClose(f); + } + + + if (ForcedCRC) + crc = ForcedCRC; + + switch (crc) + { + case 12923: //#pragma sourcefile usage + break; + case 54730: + if (verbose) + printf("Recognised progs as QuakeWorld\n"); + break; + case 5927: + if (verbose) + printf("Recognised progs as NetQuake server gamecode\n"); + break; + + case 26940: + if (verbose) + printf("Recognised progs as Quake pre-release...\n"); + break; + + case 38488: + if (verbose) + printf("Recognised progs as original Hexen2\n"); + break; + case 26905: + if (verbose) + printf("Recognised progs as Hexen2 Mission Pack\n"); + break; + case 14046: + if (verbose) + printf("Recognised progs as Hexen2 (demo)\n"); + break; + + case 22390: //EXT_CSQC_1 + if (verbose) + printf("Recognised progs as an EXT_CSQC_1 module\n"); + break; + case 17105: + case 32199: //outdated ext_csqc + printf("Recognised progs as outdated CSQC module\n"); + break; + case 52195: + printf("Recognised progs as outdated CSQC module\n"); + break; + case 10020: + if (verbose) + printf("Recognised progs as a DP/FTE Menu module\n"); + break; + + case 32401: + printf("Warning: please update your tenebrae system defs.\n"); + break; + default: + printf("Warning: progs CRC not recognised from quake nor clones\n"); + break; + } + + + return crc; +} + + +/*void QCC_PrintFunction (char *name) +{ + int i; + QCC_dstatement_t *ds; + QCC_dfunction_t *df; + + for (i=0 ; ifirst_statement; + while (1) + { + QCC_PR_PrintStatement (ds); + if (!ds->op) + break; + ds++; + } +}*/ +/* +void QCC_PrintOfs(unsigned int ofs) +{ + int i; + bool printfunc; + QCC_dstatement_t *ds; + QCC_dfunction_t *df; + + for (i=0 ; ifirst_statement; + printfunc = false; + while (1) + { + if (!ds->op) + break; + if (ds->a == ofs || ds->b == ofs || ds->c == ofs) + { + QCC_PR_PrintStatement (ds); + printfunc = true; + } + ds++; + } + if (printfunc) + { + QCC_PrintFunction(strings + functions[i].s_name); + printf(" \n \n"); + } + } + +} +*/ +/* +============================================================================== + +DIRECTORY COPYING / PACKFILE CREATION + +============================================================================== +*/ + +typedef struct +{ + char name[56]; + int filepos, filelen; +} packfile_t; + +typedef struct +{ + char id[4]; + int dirofs; + int dirlen; +} packheader_t; + +packfile_t pfiles[4096], *pf; +int packhandle; +int packbytes; + + +/* +============ +CreatePath +============ +*/ +void QCC_CreatePath (char *path) +{ + /* + char *ofs; + + for (ofs = path+1 ; *ofs ; ofs++) + { + if (*ofs == '/') + { // create the directory + *ofs = 0; +#ifdef QCC + mkdir(path); +#else + QCC_mkdir (path); +#endif + *ofs = '/'; + } + } + */ +} + + +/* +=========== +PackFile + +Copy a file into the pak file +=========== +*/ +void QCC_PackFile (char *src, char *name) +{ + int remaining; +#if 1 + char *f; +#else + int in; + int count; + char buf[4096]; +#endif + + + if ( (qbyte *)pf - (qbyte *)pfiles > sizeof(pfiles) ) + QCC_Error (ERR_TOOMANYPAKFILES, "Too many files in pak file"); + +#if 1 + f = FS_ReadToMem(src, NULL, &remaining); + if (!f) + { + printf ("%64s : %7s\n", name, ""); +// QCC_Error("Failed to open file %s", src); + return; + } + + pf->filepos = PRLittleLong (SafeSeek (packhandle, 0, SEEK_CUR)); + pf->filelen = PRLittleLong (remaining); + strcpy (pf->name, name); + printf ("%64s : %7i\n", pf->name, remaining); + + packbytes += remaining; + + SafeWrite (packhandle, f, remaining); + + FS_CloseFromMem(f); +#else + in = SafeOpenRead (src); + remaining = filelength (in); + + pf->filepos = PRLittleLong (lseek (packhandle, 0, SEEK_CUR)); + pf->filelen = PRLittleLong (remaining); + strcpy (pf->name, name); + printf ("%64s : %7i\n", pf->name, remaining); + + packbytes += remaining; + + while (remaining) + { + if (remaining < sizeof(buf)) + count = remaining; + else + count = sizeof(buf); + SafeRead (in, buf, count); + SafeWrite (packhandle, buf, count); + remaining -= count; + } + + close (in); +#endif + pf++; +} + + +/* +=========== +CopyFile + +Copies a file, creating any directories needed +=========== +*/ +void QCC_CopyFile (char *src, char *dest) +{ + /* + int in, out; + int remaining, count; + char buf[4096]; + + print ("%s to %s\n", src, dest); + + in = SafeOpenRead (src); + remaining = filelength (in); + + QCC_CreatePath (dest); + out = SafeOpenWrite (dest, remaining+10); + + while (remaining) + { + if (remaining < sizeof(buf)) + count = remaining; + else + count = sizeof(buf); + SafeRead (in, buf, count); + SafeWrite (out, buf, count); + remaining -= count; + } + + close (in); + SafeClose (out); + */ +} + + +/* +=========== +CopyFiles +=========== +*/ + +void _QCC_CopyFiles (int blocknum, int copytype, char *srcdir, char *destdir) +{ + int i; + int dirlen; + unsigned short crc; + packheader_t header; + char name[1024]; + char srcfile[1024], destfile[1024]; + + packbytes = 0; + + if (copytype == 2) + { + pf = pfiles; + packhandle = SafeOpenWrite (destdir, 1024*1024); + SafeWrite (packhandle, &header, sizeof(header)); + } + + for (i=0 ; i 0) + printf ("%3i unique precache_sounds\n", numsounds); + if (nummodels > 0) + printf ("%3i unique precache_models\n", nummodels); + if (numtextures > 0) + printf ("%3i unique precache_textures\n", numtextures); + if (numfiles > 0) + printf ("%3i unique precache_files\n", numfiles); + } + + p = QCC_CheckParm ("-copy"); + if (p && p < myargc-2) + { // create a new directory tree + + strcpy (srcdir, myargv[p+1]); + strcpy (destdir, myargv[p+2]); + if (srcdir[strlen(srcdir)-1] != '/') + strcat (srcdir, "/"); + if (destdir[strlen(destdir)-1] != '/') + strcat (destdir, "/"); + + _QCC_CopyFiles(0, 1, srcdir, destdir); + return; + } + + for ( p = 0; p < 5; p++) + { + s = QCC_Packname[p]; + if (!*s) + continue; + strcpy(destdir, s); + strcpy(srcdir, ""); + _QCC_CopyFiles(p+1, 2, srcdir, destdir); + } + return; + /* + + blocknum = 1; + p = QCC_CheckParm ("-pak2"); + if (p && p = sizeof(cnst->value)) + QCC_Error(ERR_PRECOMPILERCONSTANTTOOLONG, "Compiler constant value is too long\n"); + strncpy(cnst->value, val, sizeof(cnst->value)-1); + cnst->value[sizeof(cnst->value)-1] = '\0'; + } + } + + //optimisations. + else if ( !strnicmp(myargv[i], "-O", 2) || !strnicmp(myargv[i], "/O", 2) ) + { + p = 0; + if (myargv[i][2] >= '0' && myargv[i][2] <= '3') + { + } + else if (!strnicmp(myargv[i]+2, "no-", 3)) + { + if (myargv[i][5]) + { + for (p = 0; optimisations[p].enabled; p++) + { + if ((*optimisations[p].abbrev && !stricmp(myargv[i]+5, optimisations[p].abbrev)) || !stricmp(myargv[i]+5, optimisations[p].fullname)) + { + *optimisations[p].enabled = false; + break; + } + } + } + } + else + { + if (myargv[i][2]) + for (p = 0; optimisations[p].enabled; p++) + if ((*optimisations[p].abbrev && !stricmp(myargv[i]+2, optimisations[p].abbrev)) || !stricmp(myargv[i]+2, optimisations[p].fullname)) + { + *optimisations[p].enabled = true; + break; + } + } + if (!optimisations[p].enabled) + QCC_PR_Warning(0, NULL, WARN_BADPARAMS, "Unrecognised optimisation parameter (%s)", myargv[i]); + } + + else if ( !strnicmp(myargv[i], "-K", 2) || !strnicmp(myargv[i], "/K", 2) ) + { + p = 0; + if (!strnicmp(myargv[i]+2, "no-", 3)) + { + for (p = 0; compiler_flag[p].enabled; p++) + if (!stricmp(myargv[i]+5, compiler_flag[p].abbrev)) + { + *compiler_flag[p].enabled = false; + break; + } + } + else + { + for (p = 0; compiler_flag[p].enabled; p++) + if (!stricmp(myargv[i]+2, compiler_flag[p].abbrev)) + { + *compiler_flag[p].enabled = true; + break; + } + } + + if (!compiler_flag[p].enabled) + QCC_PR_Warning(0, NULL, WARN_BADPARAMS, "Unrecognised keyword parameter (%s)", myargv[i]); + } + else if ( !strnicmp(myargv[i], "-F", 2) || !strnicmp(myargv[i], "/F", 2) ) + { + p = 0; + if (!strnicmp(myargv[i]+2, "no-", 3)) + { + for (p = 0; compiler_flag[p].enabled; p++) + if (!stricmp(myargv[i]+5, compiler_flag[p].abbrev)) + { + *compiler_flag[p].enabled = false; + break; + } + } + else + { + for (p = 0; compiler_flag[p].enabled; p++) + if (!stricmp(myargv[i]+2, compiler_flag[p].abbrev)) + { + *compiler_flag[p].enabled = true; + break; + } + } + + if (!compiler_flag[p].enabled) + QCC_PR_Warning(0, NULL, WARN_BADPARAMS, "Unrecognised flag parameter (%s)", myargv[i]); + } + + + else if ( !strncmp(myargv[i], "-T", 2) || !strncmp(myargv[i], "/T", 2) ) + { + p = 0; + for (p = 0; targets[p].name; p++) + if (!stricmp(myargv[i]+2, targets[p].name)) + { + qcc_targetformat = targets[p].target; + break; + } + + if (!targets[p].name) + QCC_PR_Warning(0, NULL, WARN_BADPARAMS, "Unrecognised target parameter (%s)", myargv[i]); + } + + else if ( !strnicmp(myargv[i], "-W", 2) || !strnicmp(myargv[i], "/W", 2) ) + { + if (!stricmp(myargv[i]+2, "all")) + memset(qccwarningdisabled, 0, sizeof(qccwarningdisabled)); + else if (!stricmp(myargv[i]+2, "none")) + memset(qccwarningdisabled, 1, sizeof(qccwarningdisabled)); + else if(!stricmp(myargv[i]+2, "error")) + pr_werror = true; + else if (!stricmp(myargv[i]+2, "no-mundane")) + { //disable mundane performance/efficiency/blah warnings that don't affect code. + qccwarningdisabled[WARN_SAMENAMEASGLOBAL] = true; + qccwarningdisabled[WARN_DUPLICATEDEFINITION] = true; + qccwarningdisabled[WARN_CONSTANTCOMPARISON] = true; + qccwarningdisabled[WARN_ASSIGNMENTINCONDITIONAL] = true; + qccwarningdisabled[WARN_DEADCODE] = true; + qccwarningdisabled[WARN_NOTREFERENCEDCONST] = true; + qccwarningdisabled[WARN_NOTREFERENCED] = true; + qccwarningdisabled[WARN_POINTLESSSTATEMENT] = true; + qccwarningdisabled[WARN_ASSIGNMENTTOCONSTANTFUNC] = true; + qccwarningdisabled[WARN_BADPRAGMA] = true; //C specs say that these should be ignored. We're close enough to C that I consider that a valid statement. + qccwarningdisabled[WARN_IDENTICALPRECOMPILER] = true; + qccwarningdisabled[WARN_UNDEFNOTDEFINED] = true; + qccwarningdisabled[WARN_FIXEDRETURNVALUECONFLICT] = true; + qccwarningdisabled[WARN_EXTRAPRECACHE] = true; + qccwarningdisabled[WARN_CORRECTEDRETURNTYPE] = true; + } + else + { + p = 0; + if (!strnicmp(myargv[i]+2, "no-", 3)) + { + for (p = 0; warningnames[p].name; p++) + if (!strcmp(myargv[i]+5, warningnames[p].name)) + { + qccwarningdisabled[warningnames[p].index] = true; + break; + } + } + else + { + for (p = 0; warningnames[p].name; p++) + if (!stricmp(myargv[i]+2, warningnames[p].name)) + { + qccwarningdisabled[warningnames[p].index] = false; + break; + } + } + + if (!warningnames[p].name) + QCC_PR_Warning(0, NULL, WARN_BADPARAMS, "Unrecognised warning parameter (%s)", myargv[i]); + } + } + } +} + +/* +============ +main +============ +*/ + +char *qccmsrc; +char *qccmsrc2; +char qccmfilename[1024]; +char qccmprogsdat[1024]; +char qccmsourcedir[1024]; + +void QCC_FinishCompile(void); + + +void SetEndian(void); + + + +void QCC_SetDefaultProperties (void) +{ + int level; + int i; + + Hash_InitTable(&compconstantstable, MAX_CONSTANTS, qccHunkAlloc(Hash_BytesForBuckets(MAX_CONSTANTS))); + + ForcedCRC = 0; + defaultstatic = 0; + + QCC_PR_DefineName("FTEQCC"); + + if (QCC_CheckParm("/Oz")) + { + qcc_targetformat = QCF_FTE; + QCC_PR_DefineName("OP_COMP_STATEMENTS"); + QCC_PR_DefineName("OP_COMP_DEFS"); + QCC_PR_DefineName("OP_COMP_FIELDS"); + QCC_PR_DefineName("OP_COMP_FUNCTIONS"); + QCC_PR_DefineName("OP_COMP_STRINGS"); + QCC_PR_DefineName("OP_COMP_GLOBALS"); + QCC_PR_DefineName("OP_COMP_LINES"); + QCC_PR_DefineName("OP_COMP_TYPES"); + } + + if (QCC_CheckParm("/O0") || QCC_CheckParm("-O0")) + level = 0; + else if (QCC_CheckParm("/O1") || QCC_CheckParm("-O1")) + level = 1; + else if (QCC_CheckParm("/O2") || QCC_CheckParm("-O2")) + level = 2; + else if (QCC_CheckParm("/O3") || QCC_CheckParm("-O3")) + level = 3; + else + level = -1; + + if (level == -1) + { + for (i = 0; optimisations[i].enabled; i++) + { + if (optimisations[i].flags & FLAG_ASDEFAULT) + *optimisations[i].enabled = true; + else + *optimisations[i].enabled = false; + } + } + else + { + for (i = 0; optimisations[i].enabled; i++) + { + if (level >= optimisations[i].optimisationlevel) + *optimisations[i].enabled = true; + else + *optimisations[i].enabled = false; + } + } + + if (QCC_CheckParm ("-h2")) + qcc_targetformat = QCF_HEXEN2; + else if (QCC_CheckParm ("-fte")) + qcc_targetformat = QCF_FTE; + else if (QCC_CheckParm ("-dp")) + qcc_targetformat = QCF_DARKPLACES; + else + qcc_targetformat = QCF_STANDARD; + + + //enable all warnings + memset(qccwarningdisabled, 0, sizeof(qccwarningdisabled)); + + //play with default warnings. + qccwarningdisabled[WARN_NOTREFERENCEDCONST] = true; + qccwarningdisabled[WARN_MACROINSTRING] = true; +// qccwarningdisabled[WARN_ASSIGNMENTTOCONSTANT] = true; + qccwarningdisabled[WARN_FIXEDRETURNVALUECONFLICT] = true; + qccwarningdisabled[WARN_EXTRAPRECACHE] = true; + qccwarningdisabled[WARN_DEADCODE] = true; + qccwarningdisabled[WARN_INEFFICIENTPLUSPLUS] = true; + qccwarningdisabled[WARN_FTE_SPECIFIC] = true; + qccwarningdisabled[WARN_EXTENSION_USED] = true; + qccwarningdisabled[WARN_IFSTRING_USED] = true; + + + + if (QCC_CheckParm("-nowarn") || QCC_CheckParm("-Wnone")) + memset(qccwarningdisabled, 1, sizeof(qccwarningdisabled)); + if (QCC_CheckParm("-Wall")) + memset(qccwarningdisabled, 0, sizeof(qccwarningdisabled)); + + if (QCC_CheckParm("-h2")) + qccwarningdisabled[WARN_CASEINSENSATIVEFRAMEMACRO] = true; + + //Check the command line + QCC_PR_CommandLinePrecompilerOptions(); + + + if (qcc_targetformat == QCF_HEXEN2) //force on the thinktime keyword if hexen2 progs. + keyword_thinktime = true; + + if (QCC_CheckParm("/Debug")) //disable any debug optimisations + { + for (i = 0; optimisations[i].enabled; i++) + { + if (optimisations[i].flags & FLAG_KILLSDEBUGGERS) + *optimisations[i].enabled = false; + } + } +} + +//builds a list of files, pretends that they came from a progs.src +int QCC_FindQCFiles() +{ +#ifdef _WIN32 + WIN32_FIND_DATA fd; + HANDLE h; +#endif + + int numfiles = 0, i, j; + char *filelist[256], *temp; + + + qccmsrc = qccHunkAlloc(8192); + strcat(qccmsrc, "progs.dat\n");//"#pragma PROGS_DAT progs.dat\n"); + +#ifdef _WIN32 + h = FindFirstFile("*.qc", &fd); + if (h == INVALID_HANDLE_VALUE) + return 0; + + do + { + filelist[numfiles] = qccHunkAlloc (strlen(fd.cFileName)+1); + strcpy(filelist[numfiles], fd.cFileName); + numfiles++; + } while(FindNextFile(h, &fd)!=0); + FindClose(h); +#else + printf("-Facc is not supported on this platform. Please make a progs.src file instead\n"); +#endif + + //Sort alphabetically. + //bubble. :( + + for (i = 0; i < numfiles-1; i++) + { + for (j = i+1; j < numfiles; j++) + { + if (stricmp(filelist[i], filelist[j]) > 0) + { + temp = filelist[j]; + filelist[j] = filelist[i]; + filelist[i] = temp; + } + } + } + for (i = 0; i < numfiles; i++) + { + strcat(qccmsrc, filelist[i]); + strcat(qccmsrc, "\n"); +// strcat(qccmsrc, "#include \""); +// strcat(qccmsrc, filelist[i]); +// strcat(qccmsrc, "\"\n"); + } + + return numfiles; +} + +int qcc_compileactive = false; +extern int accglobalsblock; +char *originalqccmsrc; //for autoprototype. +void QCC_main (int argc, char **argv) //as part of the quake engine +{ + extern int pr_bracelevel; + + int p; + +#ifndef QCCONLY + char destfile2[1024], *s2; +#endif + char *s; + + SetEndian(); + + myargc = argc; + myargv = argv; + + qcc_compileactive = true; + + pHash_Get = &Hash_Get; + pHash_GetNext = &Hash_GetNext; + pHash_Add = &Hash_Add; + + MAX_REGS = 65536; + MAX_STRINGS = 1000000; + MAX_GLOBALS = 32768; + MAX_FIELDS = 2048; + MAX_STATEMENTS = 0x80000; + MAX_FUNCTIONS = 16384; + maxtypeinfos = 16384; + MAX_CONSTANTS = 2048; + + compressoutput = 0; + + p = externs->FileSize("qcc.cfg"); + if (p < 0) + p = externs->FileSize("src/qcc.cfg"); + if (p>0) + { + s = qccHunkAlloc(p+1); + s = externs->ReadFile("qcc.cfg", s, p); + + while(1) + { + s = QCC_COM_Parse(s); + if (!strcmp(qcc_token, "MAX_REGS")) + { + s = QCC_COM_Parse(s); + MAX_REGS = atoi(qcc_token); + } else if (!strcmp(qcc_token, "MAX_STRINGS")) { + s = QCC_COM_Parse(s); + MAX_STRINGS = atoi(qcc_token); + } else if (!strcmp(qcc_token, "MAX_GLOBALS")) { + s = QCC_COM_Parse(s); + MAX_GLOBALS = atoi(qcc_token); + } else if (!strcmp(qcc_token, "MAX_FIELDS")) { + s = QCC_COM_Parse(s); + MAX_FIELDS = atoi(qcc_token); + } else if (!strcmp(qcc_token, "MAX_STATEMENTS")) { + s = QCC_COM_Parse(s); + MAX_STATEMENTS = atoi(qcc_token); + } else if (!strcmp(qcc_token, "MAX_FUNCTIONS")) { + s = QCC_COM_Parse(s); + MAX_FUNCTIONS = atoi(qcc_token); + } else if (!strcmp(qcc_token, "MAX_TYPES")) { + s = QCC_COM_Parse(s); + maxtypeinfos = atoi(qcc_token); + } else if (!strcmp(qcc_token, "MAX_TEMPS")) { + s = QCC_COM_Parse(s); + max_temps = atoi(qcc_token); + } else if (!strcmp(qcc_token, "CONSTANTS")) { + s = QCC_COM_Parse(s); + MAX_CONSTANTS = atoi(qcc_token); + } + else if (!s) + break; + else + printf("Bad token in qcc.cfg file\n"); + } + } + /* don't try to be clever + else if (p < 0) + { + s = qccHunkAlloc(8192); + sprintf(s, "MAX_REGS\t%i\r\nMAX_STRINGS\t%i\r\nMAX_GLOBALS\t%i\r\nMAX_FIELDS\t%i\r\nMAX_STATEMENTS\t%i\r\nMAX_FUNCTIONS\t%i\r\nMAX_TYPES\t%i\r\n", + MAX_REGS, MAX_STRINGS, MAX_GLOBALS, MAX_FIELDS, MAX_STATEMENTS, MAX_FUNCTIONS, maxtypeinfos); + externs->WriteFile("qcc.cfg", s, strlen(s)); + } + */ + + strcpy(QCC_copyright, "This file was created with ForeThought's modified QuakeC compiler\nThanks to ID Software"); + for (p = 0; p < 5; p++) + strcpy(QCC_Packname[p], ""); + + for (p = 0; compiler_flag[p].enabled; p++) + { + *compiler_flag[p].enabled = compiler_flag[p].flags & FLAG_ASDEFAULT; + } + pr_werror = false; + + QCC_SetDefaultProperties(); + + optres_shortenifnots = 0; + optres_overlaptemps = 0; + optres_noduplicatestrings = 0; + optres_constantarithmatic = 0; + optres_nonvec_parms = 0; + optres_constant_names = 0; + optres_constant_names_strings = 0; + optres_precache_file = 0; + optres_filenames = 0; + optres_assignments = 0; + optres_unreferenced = 0; + optres_function_names = 0; + optres_locals = 0; + optres_dupconstdefs = 0; + optres_return_only = 0; + optres_compound_jumps = 0; +// optres_comexprremoval = 0; + optres_stripfunctions = 0; + optres_locals_marshalling = 0; + optres_logicops = 0; + + optres_test1 = 0; + optres_test2 = 0; + + accglobalsblock = 0; + + + numtemps = 0; + + functemps=NULL; + + strings = (void *)qccHunkAlloc(sizeof(char) * MAX_STRINGS); + strofs = 1; + + statements = (void *)qccHunkAlloc(sizeof(QCC_dstatement_t) * MAX_STATEMENTS); + numstatements = 0; + statement_linenums = (void *)qccHunkAlloc(sizeof(int) * MAX_STATEMENTS); + + functions = (void *)qccHunkAlloc(sizeof(QCC_dfunction_t) * MAX_FUNCTIONS); + numfunctions=0; + + pr_bracelevel = 0; + + qcc_pr_globals = (void *)qccHunkAlloc(sizeof(float) * MAX_REGS); + numpr_globals=0; + + Hash_InitTable(&globalstable, MAX_REGS, qccHunkAlloc(Hash_BytesForBuckets(MAX_REGS))); + Hash_InitTable(&localstable, MAX_REGS, qccHunkAlloc(Hash_BytesForBuckets(MAX_REGS))); + Hash_InitTable(&floatconstdefstable, MAX_REGS+1, qccHunkAlloc(Hash_BytesForBuckets(MAX_REGS+1))); + Hash_InitTable(&stringconstdefstable, MAX_REGS, qccHunkAlloc(Hash_BytesForBuckets(MAX_REGS))); + Hash_InitTable(&stringconstdefstable_dotranslate, MAX_REGS, qccHunkAlloc(Hash_BytesForBuckets(MAX_REGS))); + dotranslate=0; + dotranslate_count=0; + +// pr_global_defs = (QCC_def_t **)qccHunkAlloc(sizeof(QCC_def_t *) * MAX_REGS); + + qcc_globals = (void *)qccHunkAlloc(sizeof(QCC_ddef_t) * MAX_GLOBALS); + numglobaldefs=0; + + fields = (void *)qccHunkAlloc(sizeof(QCC_ddef_t) * MAX_FIELDS); + numfielddefs=0; + +memset(pr_immediate_string, 0, sizeof(pr_immediate_string)); + + precache_sounds = (void *)qccHunkAlloc(sizeof(char)*MAX_DATA_PATH*MAX_SOUNDS); + precache_sounds_block = (void *)qccHunkAlloc(sizeof(int)*MAX_SOUNDS); + precache_sounds_used = (void *)qccHunkAlloc(sizeof(int)*MAX_SOUNDS); + numsounds=0; + + precache_textures = (void *)qccHunkAlloc(sizeof(char)*MAX_DATA_PATH*MAX_TEXTURES); + precache_textures_block = (void *)qccHunkAlloc(sizeof(int)*MAX_TEXTURES); + numtextures=0; + + precache_models = (void *)qccHunkAlloc(sizeof(char)*MAX_DATA_PATH*MAX_MODELS); + precache_models_block = (void *)qccHunkAlloc(sizeof(int)*MAX_MODELS); + precache_models_used = (void *)qccHunkAlloc(sizeof(int)*MAX_MODELS); + nummodels=0; + + precache_files = (void *)qccHunkAlloc(sizeof(char)*MAX_DATA_PATH*MAX_FILES); + precache_files_block = (void *)qccHunkAlloc(sizeof(int)*MAX_FILES); + numfiles = 0; + + qcc_typeinfo = (void *)qccHunkAlloc(sizeof(QCC_type_t)*maxtypeinfos); + numtypeinfos = 0; + + qcc_tempofs = qccHunkAlloc(sizeof(int) * max_temps); + tempsstart = 0; + + bodylessfuncs=0; + + memset(&pr, 0, sizeof(pr)); +#ifdef MAX_EXTRA_PARMS + memset(&extra_parms, 0, sizeof(extra_parms)); +#endif + + if ( QCC_CheckParm ("/?") || QCC_CheckParm ("?") || QCC_CheckParm ("-?") || QCC_CheckParm ("-help") || QCC_CheckParm ("--help")) + { + printf ("qcc looks for progs.src in the current directory.\n"); + printf ("to look in a different directory: qcc -src \n"); +// printf ("to build a clean data tree: qcc -copy \n"); +// printf ("to build a clean pak file: qcc -pak \n"); +// printf ("to bsp all bmodels: qcc -bspmodels \n"); + printf ("-Fwasm causes FTEQCC to dump all asm to qc.asm\n"); + printf ("-O0 to disable optimisations\n"); + printf ("-O1 to optimise for size\n"); + printf ("-O2 to optimise more - some behaviours may change\n"); + printf ("-O3 to optimise lots - experimental or non-future-proof\n"); + printf ("-Oname to enable an optimisation\n"); + printf ("-Ono-name to disable optimisations\n"); + printf ("-Kkeyword to activate keyword\n"); + printf ("-Kno-keyword to disable keyword\n"); + printf ("-Wall to give a stupid number of warnings\n"); + printf ("-Ttarget to set a output format\n"); + printf ("-Fautoproto to enable automatic prototyping\n"); + printf ("-Fsubscope to enable subscopes\n"); + + qcc_compileactive = false; + return; + } + + if (flag_caseinsensative) + { + printf("Compiling without case sensativity\n"); + pHash_Get = &Hash_GetInsensative; + pHash_GetNext = &Hash_GetNextInsensative; + pHash_Add = &Hash_AddInsensative; + } + + + if (opt_locals_marshalling) + printf("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\nLocals marshalling might be buggy. Use with caution\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"); + + p = QCC_CheckParm ("-src"); + if (p && p < argc-1 ) + { + strcpy (qccmsourcedir, argv[p+1]); + strcat (qccmsourcedir, "/"); + printf ("Source directory: %s\n", qccmsourcedir); + } + else + *qccmsourcedir = '\0'; + + QCC_InitData (); + + QCC_PR_BeginCompilation ((void *)qccHunkAlloc (0x100000), 0x100000); + + if (flag_acc) + { + if (!QCC_FindQCFiles()) + QCC_Error (ERR_COULDNTOPENFILE, "Couldn't open file for asm output."); + } + else + { + if (!numsourcefiles) + { + p = QCC_CheckParm ("-qc"); + if (!p || p >= argc-1 || argv[p+1][0] == '-') + p = QCC_CheckParm ("-srcfile"); + if (p && p < argc-1 ) + sprintf (qccmprogsdat, "%s%s", qccmsourcedir, argv[p+1]); + else + { //look for a preprogs.src... :o) + sprintf (qccmprogsdat, "%spreprogs.src", qccmsourcedir); + if (externs->FileSize(qccmprogsdat) <= 0) + sprintf (qccmprogsdat, "%sprogs.src", qccmsourcedir); + } + + numsourcefiles = 0; + strcpy(sourcefileslist[numsourcefiles++], qccmprogsdat); + currentsourcefile = 0; + } + else if (currentsourcefile == numsourcefiles) + { + //no more. + qcc_compileactive = false; + numsourcefiles = 0; + currentsourcefile = 0; + return; + } + + if (currentsourcefile) + printf("-------------------------------------\n"); + + strcpy(qccmprogsdat, sourcefileslist[currentsourcefile++]); + + printf ("Source file: %s\n", qccmprogsdat); + + if (QCC_LoadFile (qccmprogsdat, (void *)&qccmsrc) == -1) + { + return; + } + } + +#ifdef WRITEASM + if (writeasm) + { + asmfile = fopen("qc.asm", "wb"); + if (!asmfile) + QCC_Error (ERR_INTERNAL, "Couldn't open file for asm output."); + } +#endif + + newstylesource = false; + while(*qccmsrc && *qccmsrc < ' ') + qccmsrc++; + pr_file_p = QCC_COM_Parse(qccmsrc); + + if (QCC_CheckParm ("-qc")) + { + strcpy(destfile, qccmprogsdat); + StripExtension(destfile); + strcat(destfile, ".qco"); + + p = QCC_CheckParm ("-o"); + if (!p || p >= argc-1 || argv[p+1][0] == '-') + if (p && p < argc-1 ) + sprintf (destfile, "%s%s", qccmsourcedir, argv[p+1]); + goto newstyle; + } + + if (*qcc_token == '#') + { + void StartNewStyleCompile(void); +newstyle: + newstylesource = true; + StartNewStyleCompile(); + return; + } + + pr_file_p = qccmsrc; + QCC_PR_LexWhitespace(); + qccmsrc = pr_file_p; + + s = qccmsrc; + pr_file_p = qccmsrc; + QCC_PR_SimpleGetToken (); + strcpy(qcc_token, pr_token); + qccmsrc = pr_file_p; + + if (!qccmsrc) + QCC_Error (ERR_NOOUTPUT, "No destination filename. qcc -help for info."); + strcpy (destfile, qcc_token); + +#ifndef QCCONLY + p=0; + s2 = strcpy(destfile2, destfile); + if (!strncmp(s2, "./", 2)) + s2+=2; + else + { + while(!strncmp(s2, "../", 3)) + { + s2+=3; + p++; + } + } + strcpy(qccmfilename, qccmsourcedir); + for (s=qccmfilename+strlen(qccmfilename);p && s>=qccmfilename; s--) + { + if (*s == '/' || *s == '\\') + { + *(s+1) = '\0'; + p--; + } + } + sprintf(destfile, "%s", s2); + + while (p>0) + { + memmove(destfile+3, destfile, strlen(destfile)+1); + destfile[0] = '.'; + destfile[1] = '.'; + destfile[2] = '/'; + p--; + } +#endif + + printf ("outputfile: %s\n", destfile); + + pr_dumpasm = false; + + currentchunk = NULL; + + originalqccmsrc = qccmsrc; +} + +void new_QCC_ContinueCompile(void); +//called between exe frames - won't loose net connection (is the theory)... +void QCC_ContinueCompile(void) +{ + char *s, *s2; + currentchunk = NULL; + if (!qcc_compileactive) + //HEY! + return; + + if (newstylesource) + { + new_QCC_ContinueCompile(); + return; + } + + qccmsrc = QCC_COM_Parse(qccmsrc); + if (!qccmsrc) + { + if (autoprototype) + { + qccmsrc = originalqccmsrc; + QCC_SetDefaultProperties(); + autoprototype = false; + return; + } + QCC_FinishCompile(); + + PostCompile(); + if (!PreCompile()) + return; + QCC_main(myargc, myargv); + return; + } + s = qcc_token; + + strcpy (qccmfilename, qccmsourcedir); + while(1) + { + if (!strncmp(s, "..\\", 3)) + { + s2 = qccmfilename + strlen(qccmfilename)-2; + while (s2>=qccmfilename) + { + if (*s2 == '/' || *s2 == '\\') + { + s2[1] = '\0'; + break; + } + s2--; + } + s+=3; + continue; + } + if (!strncmp(s, ".\\", 2)) + { + s+=2; + continue; + } + + break; + } + strcat (qccmfilename, s); + if (autoprototype) + printf ("prototyping %s\n", qccmfilename); + else + { + printf ("compiling %s\n", qccmfilename); + } + QCC_LoadFile (qccmfilename, (void *)&qccmsrc2); + + if (!QCC_PR_CompileFile (qccmsrc2, qccmfilename) ) + QCC_Error (ERR_PARSEERRORS, "Errors have occured\n"); +} +void QCC_FinishCompile(void) +{ + pbool donesomething; + int crc; +// int p; + currentchunk = NULL; + + if (setjmp(pr_parse_abort)) + QCC_Error(ERR_INTERNAL, ""); + + if (!QCC_PR_FinishCompilation ()) + { + QCC_Error (ERR_PARSEERRORS, "compilation errors"); + } + +/* p = QCC_CheckParm ("-asm"); + if (p) + { + for (p++ ; p MAX_ERRORS) + return; + if (setjmp(pr_parse_abort)) + return; + + QCC_PR_SkipToSemicolon (); + if (pr_token_type == tt_eof) + return; + } + + + QCC_PR_ClearGrabMacros (); // clear the frame macros + + compilingfile = qccmprogsdat; + + pr_file_p = qccmsrc; + s_file = s_file2 = QCC_CopyString (compilingfile); + + pr_source_line = 0; + + QCC_PR_NewLine (false); + + QCC_PR_Lex (); // read first token +} +void new_QCC_ContinueCompile(void) +{ + if (setjmp(pr_parse_abort)) + { +// if (pr_error_count != 0) + { + QCC_Error (ERR_PARSEERRORS, "Errors have occured"); + return; + } + QCC_PR_SkipToSemicolon (); + if (pr_token_type == tt_eof) + return; + } + + if (pr_token_type == tt_eof) + { + if (pr_error_count) + QCC_Error (ERR_PARSEERRORS, "Errors have occured"); + QCC_FinishCompile(); + + PostCompile(); + if (!PreCompile()) + return; + QCC_main(myargc, myargv); + return; + } + + pr_scope = NULL; // outside all functions + + QCC_PR_ParseDefs (NULL); +} + +/*void new_QCC_ContinueCompile(void) +{ + char *s, *s2; + if (!qcc_compileactive) + //HEY! + return; + +// compile all the files + + qccmsrc = QCC_COM_Parse(qccmsrc); + if (!qccmsrc) + { + QCC_FinishCompile(); + return; + } + s = qcc_token; + + strcpy (qccmfilename, qccmsourcedir); + while(1) + { + if (!strncmp(s, "..\\", 3)) + { + s2 = qccmfilename + strlen(qccmfilename)-2; + while (s2>=qccmfilename) + { + if (*s2 == '/' || *s2 == '\\') + { + s2[1] = '\0'; + break; + } + s2--; + } + s+=3; + continue; + } + if (!strncmp(s, ".\\", 2)) + { + s+=2; + continue; + } + + break; + } +// strcat (qccmfilename, s); +// printf ("compiling %s\n", qccmfilename); +// QCC_LoadFile (qccmfilename, (void *)&qccmsrc2); + +// if (!new_QCC_PR_CompileFile (qccmsrc2, qccmfilename) ) +// QCC_Error ("Errors have occured\n"); + + + { + + if (!pr.memory) + QCC_Error ("PR_CompileFile: Didn't clear"); + + QCC_PR_ClearGrabMacros (); // clear the frame macros + + compilingfile = filename; + + pr_file_p = qccmsrc2; + s_file = QCC_CopyString (filename); + + pr_source_line = 0; + + QCC_PR_NewLine (); + + QCC_PR_Lex (); // read first token + + while (pr_token_type != tt_eof) + { + if (setjmp(pr_parse_abort)) + { + if (++pr_error_count > MAX_ERRORS) + return false; + QCC_PR_SkipToSemicolon (); + if (pr_token_type == tt_eof) + return false; + } + + pr_scope = NULL; // outside all functions + + QCC_PR_ParseDefs (); + } + } + return (pr_error_count == 0); + +}*/ + + +#endif diff --git a/misc/mediasource/extra/fteqcc-src/qcctui.c b/misc/mediasource/extra/fteqcc-src/qcctui.c new file mode 100644 index 00000000..7f33e913 --- /dev/null +++ b/misc/mediasource/extra/fteqcc-src/qcctui.c @@ -0,0 +1,117 @@ +#include "qcc.h" + +#include +#include + +/* +============== +LoadFile +============== +*/ +unsigned char *QCC_ReadFile (char *fname, void *buffer, int len) +{ + long length; + FILE *f; + f = fopen(fname, "rb"); + if (!f) + return NULL; + length = fread(buffer, 1, len, f); + fclose(f); + + if (length != len) + return NULL; + + return buffer; +} +int QCC_FileSize (char *fname) +{ + long length; + FILE *f; + f = fopen(fname, "rb"); + if (!f) + return -1; + fseek(f, 0, SEEK_END); + length = ftell(f); + fclose(f); + + return length; +} + +pbool QCC_WriteFile (char *name, void *data, int len) +{ + long length; + FILE *f; + f = fopen(name, "wb"); + if (!f) + return false; + length = fwrite(data, 1, len, f); + fclose(f); + + if (length != len) + return false; + + return true; +} + +#undef printf +#undef Sys_Error + +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); +} + + +FILE *logfile; +int logprintf(const char *format, ...) +{ + va_list argptr; + static char string[1024]; + + va_start (argptr, format); +#ifdef _WIN32 + _vsnprintf (string,sizeof(string)-1, format,argptr); +#else + vsnprintf (string,sizeof(string), format,argptr); +#endif + va_end (argptr); + + printf("%s", string); + if (logfile) + fputs(string, logfile); + + return 0; +} + +int main (int argc, char **argv) +{ + int sucess; + progexterns_t ext; + progfuncs_t funcs; + progfuncs = &funcs; + memset(&funcs, 0, sizeof(funcs)); + funcs.parms = &ext; + memset(&ext, 0, sizeof(progexterns_t)); + funcs.parms->ReadFile = QCC_ReadFile; + funcs.parms->FileSize = QCC_FileSize; + funcs.parms->WriteFile = QCC_WriteFile; + funcs.parms->printf = logprintf; + funcs.parms->Sys_Error = Sys_Error; + logfile = fopen("fteqcc.log", "wt"); + sucess = CompileParams(&funcs, true, argc, argv); + qccClearHunk(); + if (logfile) + fclose(logfile); + +#ifdef _WIN32 +// fgetc(stdin); //wait for keypress +#endif + return !sucess; +} diff --git a/misc/mediasource/extra/fteqcc-src/qcd.h b/misc/mediasource/extra/fteqcc-src/qcd.h new file mode 100644 index 00000000..fd7817eb --- /dev/null +++ b/misc/mediasource/extra/fteqcc-src/qcd.h @@ -0,0 +1,7 @@ +pbool QC_decodeMethodSupported(int method); +char *QC_decode(progfuncs_t *progfuncs, int complen, int len, int method, char *info, char *buffer); +int QC_encode(progfuncs_t *progfuncs, int len, int method, char *in, int handle); + +char *filefromprogs(progfuncs_t *progfuncs, progsnum_t prnum, char *fname, int *size, char *buffer); +char *filefromnewprogs(progfuncs_t *progfuncs, char *prname, char *fname, int *size, char *buffer);//fixme - remove parm 1 + diff --git a/misc/mediasource/extra/fteqcc-src/qcd_main.c b/misc/mediasource/extra/fteqcc-src/qcd_main.c new file mode 100644 index 00000000..f98e5336 --- /dev/null +++ b/misc/mediasource/extra/fteqcc-src/qcd_main.c @@ -0,0 +1,228 @@ +#include "progsint.h" +//#include "qcc.h" + +//#define AVAIL_ZLIB + +#ifdef AVAIL_ZLIB +#ifdef _WIN32 +#define ZEXPORT VARGS +#include "../libs/zlib.h" + +# pragma comment (lib, "../libs/zlib.lib") +#else +#include +#endif +#endif + +pbool QC_decodeMethodSupported(int method) +{ + if (method == 0) + return true; + if (method == 1) + return true; + if (method == 2) + { +#ifdef AVAIL_ZLIB + return false; +#endif + } + return false; +} + +char *QC_decode(progfuncs_t *progfuncs, int complen, int len, int method, char *info, char *buffer) +{ + int i; + if (method == 0) //copy + { + if (complen != len) Sys_Error("lengths do not match"); + memcpy(buffer, info, len); + } + else if (method == 1) //xor encryption + { + if (complen != len) Sys_Error("lengths do not match"); + for (i = 0; i < len; i++) + buffer[i] = info[i] ^ 0xA5; + } + else if (method == 2) //compression (ZLIB) + { +#ifdef AVAIL_ZLIB + z_stream strm = { + info, + complen, + 0, + + buffer, + len, + 0, + + NULL, + NULL, + + NULL, + NULL, + NULL, + + Z_BINARY, + 0, + 0 + }; + + inflateInit(&strm); + if (Z_STREAM_END != inflate(&strm, Z_FINISH)) //decompress it in one go. + Sys_Error("Failed block decompression\n"); + inflateEnd(&strm); +#endif + } + //add your decryption/decompression routine here. + else + Sys_Error("Bad file encryption routine\n"); + + + return buffer; +} + +#ifndef MINIMAL +void SafeWrite(int hand, void *buf, long count); +int SafeSeek(int hand, int ofs, int mode); +//we are allowed to trash our input here. +int QC_encode(progfuncs_t *progfuncs, int len, int method, char *in, int handle) +{ + int i; + if (method == 0) //copy + { + SafeWrite(handle, in, len); + return len; + } + else if (method == 1) //xor encryption + { + for (i = 0; i < len; i++) + in[i] = in[i] ^ 0xA5; + SafeWrite(handle, in, len); + return len; + } + else if (method == 2) //compression (ZLIB) + { +#ifdef AVAIL_ZLIB + char out[8192]; + + z_stream strm = { + in, + len, + 0, + + out, + sizeof(out), + 0, + + NULL, + NULL, + + NULL, + NULL, + NULL, + + Z_BINARY, + 0, + 0 + }; + i=0; + + deflateInit(&strm, Z_BEST_COMPRESSION); + while(deflate(&strm, Z_FINISH) == Z_OK) + { + SafeWrite(handle, out, sizeof(out) - strm.avail_out); //compress in chunks of 8192. Saves having to allocate a huge-mega-big buffer + i+=sizeof(out) - strm.avail_out; + strm.next_out = out; + strm.avail_out = sizeof(out); + } + SafeWrite(handle, out, sizeof(out) - strm.avail_out); + i+=sizeof(out) - strm.avail_out; + deflateEnd(&strm); + return i; +#endif + Sys_Error("ZLIB compression not supported in this build"); + return 0; + } + //add your compression/decryption routine here. + else + { + Sys_Error("Wierd method"); + return 0; + } +} +#endif + +char *filefromprogs(progfuncs_t *progfuncs, progsnum_t prnum, char *fname, int *size, char *buffer) +{ + int num; + includeddatafile_t *s; + if (!pr_progstate[prnum].progs) + return NULL; + if (pr_progstate[prnum].progs->version != PROG_EXTENDEDVERSION) + return NULL; + if (!pr_progstate[prnum].progs->secondaryversion != PROG_SECONDARYVERSION16 && + !pr_progstate[prnum].progs->secondaryversion != PROG_SECONDARYVERSION32) + return NULL; + + num = *(int*)((char *)pr_progstate[prnum].progs + pr_progstate[prnum].progs->ofsfiles); + s = (includeddatafile_t *)((char *)pr_progstate[prnum].progs + pr_progstate[prnum].progs->ofsfiles+4); + while(num>0) + { + if (!strcmp(s->filename, fname)) + { + if (size) + *size = s->size; + if (!buffer) + return (char *)0xffffffff; + return QC_decode(progfuncs, s->compsize, s->size, s->compmethod, (char *)pr_progstate[prnum].progs+s->ofs, buffer); + } + + s++; + num--; + } + + if (size) + *size = 0; + return NULL; +} + +/* +char *filefromnewprogs(progfuncs_t *progfuncs, char *prname, char *fname, int *size, char *buffer) +{ + int num; + includeddatafile_t *s; + progstate_t progs; + if (!PR_ReallyLoadProgs(progfuncs, prname, -1, &progs, false)) + { + if (size) + *size = 0; + return NULL; + } + + if (progs.progs->version < PROG_EXTENDEDVERSION) + return NULL; + if (!progs.progs->ofsfiles) + return NULL; + + num = *(int*)((char *)progs.progs + progs.progs->ofsfiles); + s = (includeddatafile_t *)((char *)progs.progs + progs.progs->ofsfiles+4); + while(num>0) + { + if (!strcmp(s->filename, fname)) + { + if (size) + *size = s->size; + if (!buffer) + return (char *)0xffffffff; + return QC_decode(progfuncs, s->compsize, s->size, s->compmethod, (char *)progs.progs+s->ofs, buffer); + } + + s++; + num--; + } + + if (size) + *size = 0; + return NULL; +} +*/ diff --git a/misc/mediasource/extra/fteqcc-src/qcdecomp.c b/misc/mediasource/extra/fteqcc-src/qcdecomp.c new file mode 100644 index 00000000..d1fcabf5 --- /dev/null +++ b/misc/mediasource/extra/fteqcc-src/qcdecomp.c @@ -0,0 +1,986 @@ +#ifndef MINIMAL + +#include "progsint.h" +#include "setjmp.h" + +#define MAX_PARMS 8 + +typedef struct QCC_type_s +{ + etype_t type; + + struct QCC_type_s *next; +// function types are more complex + struct QCC_type_s *aux_type; // return type or field type + int num_parms; // -1 = variable args +// struct QCC_type_s *parm_types[MAX_PARMS]; // only [num_parms] allocated + + int ofs; //inside a structure. + int size; + char *name; + +} QCC_type_t; + + +extern QCC_type_t *qcc_typeinfo; +extern int numtypeinfos; +extern int maxtypeinfos; +extern QCC_type_t *type_void;// = {ev_void/*, &def_void*/}; +extern QCC_type_t *type_string;// = {ev_string/*, &def_string*/}; +extern QCC_type_t *type_float;// = {ev_float/*, &def_float*/}; +extern QCC_type_t *type_vector;// = {ev_vector/*, &def_vector*/}; +extern QCC_type_t *type_entity;// = {ev_entity/*, &def_entity*/}; +extern QCC_type_t *type_field;// = {ev_field/*, &def_field*/}; +extern QCC_type_t *type_function;// = {ev_function/*, &def_function*/,NULL,&type_void}; +// type_function is a void() function used for state defs +extern QCC_type_t *type_pointer;// = {ev_pointer/*, &def_pointer*/}; +extern QCC_type_t *type_integer;// = {ev_integer/*, &def_integer*/}; + +extern QCC_type_t *type_floatfield;// = {ev_field/*, &def_field*/, NULL, &type_float}; +QCC_type_t *QCC_PR_NewType (char *name, int basictype); + + +jmp_buf decompilestatementfailure; + +#if 0 +pbool Decompile(progfuncs_t *progfuncs, char *fname) +{ + return false; +} +#else + +QCC_type_t **ofstype; +qbyte *ofsflags; + +int SafeOpenWrite (char *filename, int maxsize); +void SafeWrite(int hand, void *buf, long count); +int SafeSeek(int hand, int ofs, int mode); +void SafeClose(int hand); +void VARGS writes(int hand, char *msg, ...) +{ + va_list va; + char buf[4192]; + + va_start(va, msg); + Q_vsnprintf (buf,sizeof(buf)-1, msg, va); + va_end(va); + + SafeWrite(hand, buf, strlen(buf)); +}; + +char *PR_UglyValueString (etype_t type, eval_t *val); +ddef16_t *ED_GlobalAtOfs16 (progfuncs_t *progfuncs, int ofs); +char *VarAtOfs(progfuncs_t *progfuncs, int ofs) +{ + static char buf [4192]; + ddef16_t *def; + int typen; + + if (ofsflags[ofs]&8) + def = ED_GlobalAtOfs16(progfuncs, ofs); + else + def = NULL; + if (!def) + { + if (ofsflags[ofs]&3) + { + if (ofstype[ofs]) + sprintf(buf, "_v_%s_%i", ofstype[ofs]->name, ofs); + else + sprintf(buf, "_v_%i", ofs); + } + else + { + if (ofstype[ofs]) + { + typen = ofstype[ofs]->type; + goto evaluateimmediate; + } + else + sprintf(buf, "_c_%i", ofs); + } + return buf; + } + if (!def->s_name[progfuncs->stringtable] || !strcmp(progfuncs->stringtable+def->s_name, "IMMEDIATE")) + { + if (current_progstate->types) + typen = current_progstate->types[def->type & ~DEF_SHARED].type; + else + typen = def->type & ~(DEF_SHARED|DEF_SAVEGLOBAL); + +evaluateimmediate: +// return PR_UglyValueString(def->type, (eval_t *)¤t_progstate->globals[def->ofs]); + switch(typen) + { + case ev_float: + sprintf(buf, "%f", G_FLOAT(ofs)); + return buf; + case ev_vector: + sprintf(buf, "\'%f %f %f\'", G_FLOAT(ofs), G_FLOAT(ofs+1), G_FLOAT(ofs+2)); + return buf; + case ev_string: + { + char *s, *s2; + s = buf; + *s++ = '\"'; + s2 = pr_strings+G_INT(ofs); + + + if (s2) + while(*s2) + { + if (*s2 == '\n') + { + *s++ = '\\'; + *s++ = 'n'; + s2++; + } + else if (*s2 == '\"') + { + *s++ = '\\'; + *s++ = '\"'; + s2++; + } + else if (*s2 == '\t') + { + *s++ = '\\'; + *s++ = 't'; + s2++; + } + else + *s++=*s2++; + } + *s++ = '\"'; + *s++ = '\0'; + } + return buf; + case ev_pointer: + sprintf(buf, "_c_pointer_%i", ofs); + return buf; + default: + sprintf(buf, "_c_%i", ofs); + return buf; + } + } + return def->s_name+progfuncs->stringtable; +} + + +int file; + +int ImmediateReadLater(progfuncs_t *progfuncs, progstate_t *progs, unsigned int ofs, int firstst) +{ + dstatement16_t *st; + if (ofsflags[ofs] & 8) + return false; //this is a global/local/pramater, not a temp + if (!(ofsflags[ofs] & 3)) + return false; //this is a constant. + for (st = &((dstatement16_t*)progs->statements)[firstst]; ; st++,firstst++) + { //if written, return false, if read, return true. + if (st->op >= OP_CALL0 && st->op <= OP_CALL8) + { + if (ofs == OFS_RETURN) + return false; + if (ofs < OFS_PARM0 + 3*((unsigned int)st->op - OP_CALL0)) + return true; + } + else if (pr_opcodes[st->op].associative == ASSOC_RIGHT) + { + if (ofs == st->b) + return false; + if (ofs == st->a) + return true; + } + else + { + if (st->a == ofs) + return true; + if (st->b == ofs) + return true; + if (st->c == ofs) + return false; + } + + if (st->op == OP_DONE || st->op == OP_RETURN) //we missed our chance. (return/done ends any code coherancy). + return false; + } + return false; +} +int ProductReadLater(progfuncs_t *progfuncs, progstate_t *progs, int stnum) +{ + dstatement16_t *st; + st = &((dstatement16_t*)progs->statements)[stnum]; + if (pr_opcodes[st->op].priority == -1) + { + if (st->op >= OP_CALL0 && st->op <= OP_CALL7) + return ImmediateReadLater(progfuncs, progs, OFS_RETURN, stnum+1); + return false;//these don't have products... + } + + if (pr_opcodes[st->op].associative == ASSOC_RIGHT) + return ImmediateReadLater(progfuncs, progs, st->b, stnum+1); + else + return ImmediateReadLater(progfuncs, progs, st->c, stnum+1); +} + +void WriteStatementProducingOfs(progfuncs_t *progfuncs, progstate_t *progs, int lastnum, int firstpossible, int ofs) //recursive, works backwards +{ + int i; + dstatement16_t *st; + ddef16_t *def; + if (ofs == 0) + longjmp(decompilestatementfailure, 1); + for (; lastnum >= firstpossible; lastnum--) + { + st = &((dstatement16_t*)progs->statements)[lastnum]; + if (st->op >= OP_CALL0 && st->op < OP_CALL7) + { + if (ofs != OFS_RETURN) + continue; + WriteStatementProducingOfs(progfuncs, progs, lastnum-1, firstpossible, st->a); + writes(file, "("); + for (i = 0; i < st->op - OP_CALL0; i++) + { + WriteStatementProducingOfs(progfuncs, progs, lastnum-1, firstpossible, OFS_PARM0 + i*3); + if (i != st->op - OP_CALL0-1) + writes(file, ", "); + } + writes(file, ")"); + return; + } + else if (pr_opcodes[st->op].associative == ASSOC_RIGHT) + { + if (st->b != ofs) + continue; + if (!ImmediateReadLater(progfuncs, progs, st->b, lastnum+1)) + { + writes(file, "("); + WriteStatementProducingOfs(progfuncs, progs, lastnum-1, firstpossible, st->b); + writes(file, " "); + writes(file, pr_opcodes[st->op].name); + writes(file, " "); + WriteStatementProducingOfs(progfuncs, progs, lastnum-1, firstpossible, st->a); + writes(file, ")"); + return; + } + WriteStatementProducingOfs(progfuncs, progs, lastnum-1, firstpossible, st->a); + return; + } + else + { + if (st->c != ofs) + continue; + + if (!ImmediateReadLater(progfuncs, progs, st->c, lastnum+1)) + { + WriteStatementProducingOfs(progfuncs, progs, lastnum-1, firstpossible, st->c); + writes(file, " = "); + } + writes(file, "("); + WriteStatementProducingOfs(progfuncs, progs, lastnum-1, firstpossible, st->a); + + if (!strcmp(pr_opcodes[st->op].name, ".")) + writes(file, pr_opcodes[st->op].name); //extra spaces around .s are ugly. + else + { + writes(file, " "); + writes(file, pr_opcodes[st->op].name); + writes(file, " "); + } + WriteStatementProducingOfs(progfuncs, progs, lastnum-1, firstpossible, st->b); + writes(file, ")"); + return; + } + } + + def = ED_GlobalAtOfs16(progfuncs, ofs); + if (def) + { + if (!strcmp(def->s_name+progfuncs->stringtable, "IMMEDIATE")) + writes(file, "%s", VarAtOfs(progfuncs, ofs)); + else + writes(file, "%s", progfuncs->stringtable+def->s_name); + } + else + writes(file, "%s", VarAtOfs(progfuncs, ofs)); +// longjmp(decompilestatementfailure, 1); +} + +int WriteStatement(progfuncs_t *progfuncs, progstate_t *progs, int stnum, int firstpossible) +{ + int count, skip; + dstatement16_t *st; + st = &((dstatement16_t*)progs->statements)[stnum]; + switch(st->op) + { + case OP_IFNOT: + count = (signed short)st->b; + writes(file, "if ("); + WriteStatementProducingOfs(progfuncs, progs, stnum, firstpossible, st->a); + writes(file, ")\r\n"); + writes(file, "{\r\n"); + firstpossible = stnum+1; + count--; + stnum++; + while(count) + { + if (ProductReadLater(progfuncs, progs, stnum)) + { + count--; + stnum++; + continue; + } + skip = WriteStatement(progfuncs, progs, stnum, firstpossible); + count-=skip; + stnum+=skip; + } + writes(file, "}\r\n"); + st = &((dstatement16_t*)progs->statements)[stnum]; + if (st->op == OP_GOTO) + { + count = (signed short)st->b; + count--; + stnum++; + + writes(file, "else\r\n"); + writes(file, "{\r\n"); + while(count) + { + if (ProductReadLater(progfuncs, progs, stnum)) + { + count--; + stnum++; + continue; + } + skip = WriteStatement(progfuncs, progs, stnum, firstpossible); + count-=skip; + stnum+=skip; + } + writes(file, "}\r\n"); + } + break; + case OP_IF: + longjmp(decompilestatementfailure, 1); + break; + case OP_GOTO: + longjmp(decompilestatementfailure, 1); + break; + case OP_RETURN: + case OP_DONE: + if (st->a) + WriteStatementProducingOfs(progfuncs, progs, stnum-1, firstpossible, st->a); + break; + case OP_CALL0: + case OP_CALL1: + case OP_CALL2: + case OP_CALL3: + case OP_CALL4: + case OP_CALL5: + case OP_CALL6: + case OP_CALL7: + WriteStatementProducingOfs(progfuncs, progs, stnum, firstpossible, OFS_RETURN); + writes(file, ";\r\n"); + break; + default: + if (pr_opcodes[st->op].associative == ASSOC_RIGHT) + WriteStatementProducingOfs(progfuncs, progs, stnum, firstpossible, st->b); + else + WriteStatementProducingOfs(progfuncs, progs, stnum, firstpossible, st->c); + writes(file, ";\r\n"); + break; + } + + return 1; +} + +void WriteAsmStatements(progfuncs_t *progfuncs, progstate_t *progs, int num, int f, char *functionname) +{ + int stn = progs->functions[num].first_statement; + QCC_opcode_t *op; + dstatement16_t *st = NULL; + eval_t *v; + + ddef16_t *def; + int ofs,i; + + int fileofs; + + if (!functionname && stn<0) + { + //we wrote this one... + return; + } + + if (stn>=0) + { + for (stn = progs->functions[num].first_statement; stn < (signed int)pr_progs->numstatements; stn++) + { + st = &((dstatement16_t*)progs->statements)[stn]; + if (st->op == OP_DONE || st->op == OP_RETURN) + { + if (!st->a) + writes(f, "void("); + else if (ofstype[st->a]) + { + writes(f, "%s", ofstype[st->a]->name); + writes(f, "("); + } + else + writes(f, "function("); + break; + } + } + st=NULL; + stn = progs->functions[num].first_statement; + } + else + writes(f, "function("); + for (ofs = progs->functions[num].parm_start, i = 0; i < progs->functions[num].numparms; i++, ofs+=progs->functions[num].parm_size[i]) + { + ofsflags[ofs] |= 4; + + def = ED_GlobalAtOfs16(progfuncs, ofs); + if (def && stn>=0) + { + if (st) + writes(f, ", "); + st = (void *)0xffff; + + if (!def->s_name[progfuncs->stringtable]) + { + char mem[64]; + sprintf(mem, "_p_%i", def->ofs); + def->s_name = (char*)malloc(strlen(mem)+1)-progfuncs->stringtable; + strcpy(def->s_name+progfuncs->stringtable, mem); + } + + if (current_progstate->types) + writes(f, "%s %s", current_progstate->types[def->type&~(DEF_SHARED|DEF_SAVEGLOBAL)].name, def->s_name); + else + switch(def->type&~(DEF_SHARED|DEF_SAVEGLOBAL)) + { + case ev_string: + writes(f, "%s %s", "string", progfuncs->stringtable+def->s_name); + break; + case ev_float: + writes(f, "%s %s", "float", progfuncs->stringtable+def->s_name); + break; + case ev_entity: + writes(f, "%s %s", "entity", progfuncs->stringtable+def->s_name); + break; + case ev_vector: + writes(f, "%s %s", "vector", progfuncs->stringtable+def->s_name); + break; + default: + writes(f, "%s %s", "randomtype", progfuncs->stringtable+def->s_name); + break; + } + } + } + for (ofs = progs->functions[num].parm_start+progs->functions[num].numparms, i = progs->functions[num].numparms; i < progs->functions[num].locals; i++, ofs+=1) + ofsflags[ofs] |= 4; + + if (!progfuncs->stringtable[progs->functions[num].s_name]) + { + char mem[64]; + if (!functionname) + { + sprintf(mem, "_bi_%i", num); + progs->functions[num].s_name = (char*)malloc(strlen(mem)+1)-progfuncs->stringtable; + strcpy(progs->functions[num].s_name+progfuncs->stringtable, mem); + } + else + { + progs->functions[num].s_name = (char*)malloc(strlen(functionname)+1)-progfuncs->stringtable; + strcpy(progs->functions[num].s_name+progfuncs->stringtable, functionname); + } + } + + writes(f, ") %s", progfuncs->stringtable+progs->functions[num].s_name); + + if (stn < 0) + { + stn*=-1; + writes(f, " = #%i;\r\n", stn); +/* + for (ofs = progs->functions[num].parm_start, i = 0; i < progs->functions[num].numparms; i++, ofs+=progs->functions[num].parm_size[i]) + { + def = ED_GlobalAtOfs16(progfuncs, ofs); + if (def) + { + def->ofs = 0xffff; + + if (progs->types) + { + if (progs->types[def->type & ~(DEF_SHARED|DEF_SAVEGLOBAL)].type == ev_vector) + { + def = ED_GlobalAtOfs16(progfuncs, ofs); + def->ofs = 0xffff; + def = ED_GlobalAtOfs16(progfuncs, ofs+1); + def->ofs = 0xffff; + def = ED_GlobalAtOfs16(progfuncs, ofs+2); + def->ofs = 0xffff; + } + } + else if ((def->type & (~(DEF_SHARED|DEF_SAVEGLOBAL))) == ev_vector) + { + def = ED_GlobalAtOfs16(progfuncs, ofs); + def->ofs = 0xffff; + def = ED_GlobalAtOfs16(progfuncs, ofs+1); + def->ofs = 0xffff; + def = ED_GlobalAtOfs16(progfuncs, ofs+2); + def->ofs = 0xffff; + } + } + } + */ + return; + } + + if (functionname) //parsing defs + { + writes(f, ";\r\n"); + return; + } + + fileofs = SafeSeek(f, 0, SEEK_CUR); + if (setjmp(decompilestatementfailure)) + { + writes(f, "*/\r\n"); + // SafeSeek(f, fileofs, SEEK_SET); + writes(f, " = asm {\r\n"); + + stn = progs->functions[num].first_statement; + for (ofs = progs->functions[num].parm_start+progs->functions[num].numparms, i = progs->functions[num].numparms; i < progs->functions[num].locals; i++, ofs+=1) + { + def = ED_GlobalAtOfs16(progfuncs, ofs); + if (def) + { + v = (eval_t *)&((int *)progs->globals)[def->ofs]; + if (current_progstate->types) + writes(f, "\tlocal %s %s;\r\n", current_progstate->types[def->type&~(DEF_SHARED|DEF_SAVEGLOBAL)].name, def->s_name); + else + { + if (!progfuncs->stringtable[def->s_name]) + { + char mem[64]; + sprintf(mem, "_l_%i", def->ofs); + def->s_name = (char*)malloc(strlen(mem)+1)-progfuncs->stringtable; + strcpy(def->s_name+progfuncs->stringtable, mem); + } + + switch(def->type&~(DEF_SHARED|DEF_SAVEGLOBAL)) + { + case ev_string: + writes(f, "\tlocal %s %s;\r\n", "string", progfuncs->stringtable+def->s_name); + break; + case ev_float: + writes(f, "\tlocal %s %s;\r\n", "float", progfuncs->stringtable+def->s_name); + break; + case ev_entity: + writes(f, "\tlocal %s %s;\r\n", "entity", progfuncs->stringtable+def->s_name); + break; + case ev_vector: + if (v->_vector[0] || v->_vector[1] || v->_vector[2]) + writes(f, "\tlocal vector %s = '%f %f %f';\r\n", progfuncs->stringtable+def->s_name, v->_vector[0], v->_vector[1], v->_vector[2]); + else + writes(f, "\tlocal %s %s;\r\n", "vector", progfuncs->stringtable+def->s_name); + ofs+=2; //skip floats; + break; + default: + writes(f, "\tlocal %s %s;\r\n", "randomtype", progfuncs->stringtable+def->s_name); + break; + } + } + } + } + + while(1) + { + st = &((dstatement16_t*)progs->statements)[stn]; + if (!st->op) //end of function statement! + break; + op = &pr_opcodes[st->op]; + writes(f, "\t%s", op->opname); + + if (op->priority==-1&&op->associative==ASSOC_RIGHT) //last param is a goto + { + if (op->type_b == &type_void) + { + if (st->a) + writes(f, " %i", (signed short)st->a); + } + else if (op->type_c == &type_void) + { + if (st->a) + writes(f, " %s", VarAtOfs(progfuncs, st->a)); + if (st->b) + writes(f, " %i", (signed short)st->b); + } + else + { + if (st->a) + writes(f, " %s", VarAtOfs(progfuncs, st->a)); + if (st->b) + writes(f, " %s", VarAtOfs(progfuncs, st->b)); + if (st->c) //rightness means it uses a as c + writes(f, " %i", (signed short)st->c); + } + } + else + { + if (st->a) + { + if (op->type_a == NULL) + writes(f, " %i", (signed short)st->a); + else + writes(f, " %s", VarAtOfs(progfuncs, st->a)); + } + if (st->b) + { + if (op->type_b == NULL) + writes(f, " %i", (signed short)st->b); + else + writes(f, " %s", VarAtOfs(progfuncs, st->b)); + } + if (st->c && op->associative != ASSOC_RIGHT) //rightness means it uses a as c + { + if (op->type_c == NULL) + writes(f, " %i", (signed short)st->c); + else + writes(f, " %s", VarAtOfs(progfuncs, st->c)); + } + } + + writes(f, ";\r\n"); + + stn++; + } + } + else + { + if (!strcmp(progfuncs->stringtable+progs->functions[num].s_name, "SUB_Remove")) + file = 0; + file = f; + + writes(f, "/*\r\n"); + + writes(f, " =\r\n{\r\n"); + + for (ofs = progs->functions[num].parm_start+progs->functions[num].numparms, i = progs->functions[num].numparms; i < progs->functions[num].locals; i++, ofs+=1) + { + def = ED_GlobalAtOfs16(progfuncs, ofs); + if (def) + { + v = (eval_t *)&((int *)progs->globals)[def->ofs]; + if (current_progstate->types) + writes(f, "\tlocal %s %s;\r\n", current_progstate->types[def->type&~(DEF_SHARED|DEF_SAVEGLOBAL)].name, def->s_name); + else + { + if (!def->s_name[progfuncs->stringtable]) + { + char mem[64]; + sprintf(mem, "_l_%i", def->ofs); + def->s_name = (char*)malloc(strlen(mem)+1)-progfuncs->stringtable; + strcpy(def->s_name+progfuncs->stringtable, mem); + } + + switch(def->type&~(DEF_SHARED|DEF_SAVEGLOBAL)) + { + case ev_string: + writes(f, "\tlocal %s %s;\r\n", "string", progfuncs->stringtable+def->s_name); + break; + case ev_float: + writes(f, "\tlocal %s %s;\r\n", "float", progfuncs->stringtable+def->s_name); + break; + case ev_entity: + writes(f, "\tlocal %s %s;\r\n", "entity", progfuncs->stringtable+def->s_name); + break; + case ev_vector: + if (v->_vector[0] || v->_vector[1] || v->_vector[2]) + writes(f, "\tlocal vector %s = '%f %f %f';\r\n", def->s_name, v->_vector[0], v->_vector[1], v->_vector[2]); + else + writes(f, "\tlocal %s %s;\r\n", "vector",progfuncs->stringtable+def->s_name); + ofs+=2; //skip floats; + break; + default: + writes(f, "\tlocal %s %s;\r\n", "randomtype", progfuncs->stringtable+def->s_name); + break; + } + } + } + } + + + for (stn = progs->functions[num].first_statement; stn < (signed int)pr_progs->numstatements; stn++) + { + if (ProductReadLater(progfuncs, progs, stn)) + continue; + + st = &((dstatement16_t*)progs->statements)[stn]; + if (!st->op) + break; + WriteStatement(progfuncs, progs, stn, progs->functions[num].first_statement); + } + + longjmp(decompilestatementfailure, 1); + } + writes(f, "};\r\n"); +} + +void FigureOutTypes(progfuncs_t *progfuncs) +{ + ddef16_t *def; + QCC_opcode_t *op; + unsigned int i,p; + dstatement16_t *st; + + int parmofs[8]; + + ofstype = realloc(ofstype, sizeof(*ofstype)*65535); + ofsflags = realloc(ofsflags, sizeof(*ofsflags)*65535); + + maxtypeinfos=256; + qcc_typeinfo = (void *)realloc(qcc_typeinfo, sizeof(QCC_type_t)*maxtypeinfos); + numtypeinfos = 0; + + memset(ofstype, 0, sizeof(*ofstype)*65535); + memset(ofsflags, 0, sizeof(*ofsflags)*65535); + + type_void = QCC_PR_NewType("void", ev_void); + type_string = QCC_PR_NewType("string", ev_string); + type_float = QCC_PR_NewType("float", ev_float); + type_vector = QCC_PR_NewType("vector", ev_vector); + type_entity = QCC_PR_NewType("entity", ev_entity); + type_field = QCC_PR_NewType("field", ev_field); + type_function = QCC_PR_NewType("function", ev_function); + type_pointer = QCC_PR_NewType("pointer", ev_pointer); + type_integer = QCC_PR_NewType("integer", ev_integer); + +// type_variant = QCC_PR_NewType("__variant", ev_variant); + + type_floatfield = QCC_PR_NewType("fieldfloat", ev_field); + type_floatfield->aux_type = type_float; + type_pointer->aux_type = QCC_PR_NewType("pointeraux", ev_float); + + type_function->aux_type = type_void; + + for (i = 0,st = pr_statements16; i < pr_progs->numstatements; i++,st++) + { + op = &pr_opcodes[st->op]; + if (st->op >= OP_CALL1 && st->op <= OP_CALL8) + { + for (p = 0; p < (unsigned int)st->op-OP_CALL0; p++) + { + ofstype[parmofs[p]] = ofstype[OFS_PARM0+p*3]; + } + } + else if (op->associative == ASSOC_RIGHT) + { //assignment + ofsflags[st->b] |= 1; + if (st->b >= OFS_PARM0 && st->b < RESERVED_OFS) + parmofs[(st->b-OFS_PARM0)/3] = st->a; + +// if (st->op != OP_STORE_F || st->b>RESERVED_OFS) //optimising compilers fix the OP_STORE_V, it's the storef that becomes meaningless (this is the only time that we need this sort of info anyway) + { + if (op->type_c && op->type_c != &type_void) + ofstype[st->a] = *op->type_c; + if (op->type_b && op->type_b != &type_void) + ofstype[st->b] = *op->type_b; + } + } + else if (op->type_c) + { + ofsflags[st->c] |= 2; + + if (st->c >= OFS_PARM0 && st->b < RESERVED_OFS) //too complicated + parmofs[(st->b-OFS_PARM0)/3] = 0; + +// if (st->op != OP_STORE_F || st->b>RESERVED_OFS) //optimising compilers fix the OP_STORE_V, it's the storef that becomes meaningless (this is the only time that we need this sort of info anyway) + { + if (op->type_a && op->type_a != &type_void) + ofstype[st->a] = *op->type_a; + if (op->type_b && op->type_b != &type_void) + ofstype[st->b] = *op->type_b; + if (op->type_c && op->type_c != &type_void) + ofstype[st->c] = *op->type_c; + } + } + } + + + for (i=0 ; inumglobaldefs ; i++) + { + def = &pr_globaldefs16[i]; + ofsflags[def->ofs] |= 8; + switch(def->type) + { + case ev_float: + ofstype[def->ofs] = type_float; + break; + case ev_string: + ofstype[def->ofs] = type_string; + break; + case ev_vector: + ofstype[def->ofs] = type_vector; + break; + default: + break; + } + } +} + +pbool Decompile(progfuncs_t *progfuncs, char *fname) +{ + extern progfuncs_t *qccprogfuncs; + unsigned int i; + unsigned int fld=0; + eval_t *v; +// char *filename; + int f, type; + + progstate_t progs, *op; + + qccprogfuncs = progfuncs; + op=current_progstate; + + if (!PR_ReallyLoadProgs(progfuncs, fname, -1, &progs, false)) + { + return false; + } + + f=SafeOpenWrite("qcdtest/defs.qc", 1024*512); + + writes(f, "//Decompiled code can contain little type info.\r\n#define NOWARNINGS\r\n"); + + FigureOutTypes(progfuncs); + + for (i = 1; i < progs.progs->numglobaldefs; i++) + { + if (!strcmp(progfuncs->stringtable+pr_globaldefs16[i].s_name, "IMMEDIATE")) + continue; + + if (ofsflags[pr_globaldefs16[i].ofs] & 4) + continue; //this is a local. + + if (current_progstate->types) + type = progs.types[pr_globaldefs16[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL)].type; + else + type = pr_globaldefs16[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL); + v = (eval_t *)&((int *)progs.globals)[pr_globaldefs16[i].ofs]; + + if (!progfuncs->stringtable[pr_globaldefs16[i].s_name]) + { + char mem[64]; + if (ofsflags[pr_globaldefs16[i].ofs] & 3) + { + ofsflags[pr_globaldefs16[i].ofs] &= ~8; + continue; //this is a constant... + } + + sprintf(mem, "_g_%i", pr_globaldefs16[i].ofs); + pr_globaldefs16[i].s_name = (char*)malloc(strlen(mem)+1)-progfuncs->stringtable; + strcpy(pr_globaldefs16[i].s_name+progfuncs->stringtable, mem); + } + + switch(type) + { + case ev_void: + writes(f, "void %s;\r\n", progfuncs->stringtable+pr_globaldefs16[i].s_name); + break; + case ev_string: + if (v->string && *(pr_strings+v->_int)) + writes(f, "string %s = \"%s\";\r\n", progfuncs->stringtable+pr_globaldefs16[i].s_name, pr_strings+v->_int); + else + writes(f, "string %s;\r\n", pr_globaldefs16[i].s_name); + break; + case ev_float: + if (v->_float) + writes(f, "float %s = %f;\r\n", progfuncs->stringtable+pr_globaldefs16[i].s_name, v->_float); + else + writes(f, "float %s;\r\n", progfuncs->stringtable+pr_globaldefs16[i].s_name); + break; + case ev_vector: + if (v->_vector[0] || v->_vector[1] || v->_vector[2]) + writes(f, "vector %s = '%f %f %f';\r\n", progfuncs->stringtable+pr_globaldefs16[i].s_name, v->_vector[0], v->_vector[1], v->_vector[2]); + else + writes(f, "vector %s;\r\n", progfuncs->stringtable+pr_globaldefs16[i].s_name); + i+=3;//skip the floats + break; + case ev_entity: + writes(f, "entity %s;\r\n", progfuncs->stringtable+pr_globaldefs16[i].s_name); + break; + case ev_field: +//wierd + fld++; + if (!v->_int) + writes(f, "var "); + switch(pr_fielddefs16[fld].type) + { + case ev_string: + writes(f, ".string %s;", progfuncs->stringtable+pr_globaldefs16[i].s_name); + break; + + case ev_float: + writes(f, ".float %s;", progfuncs->stringtable+pr_globaldefs16[i].s_name); + break; + + case ev_vector: + writes(f, ".float %s;", progfuncs->stringtable+pr_globaldefs16[i].s_name); + break; + + case ev_entity: + writes(f, ".float %s;", progfuncs->stringtable+pr_globaldefs16[i].s_name); + break; + + case ev_function: + writes(f, ".void() %s;", progfuncs->stringtable+pr_globaldefs16[i].s_name); + break; + + default: + writes(f, "field %s;", progfuncs->stringtable+pr_globaldefs16[i].s_name); + break; + } + if (v->_int) + writes(f, "/* %i */", v->_int); + writes(f, "\r\n"); + break; + + case ev_function: +//wierd + WriteAsmStatements(progfuncs, &progs, ((int *)progs.globals)[pr_globaldefs16[i].ofs], f, pr_globaldefs16[i].s_name+progfuncs->stringtable); + break; + + case ev_pointer: + writes(f, "pointer %s;\r\n", progfuncs->stringtable+pr_globaldefs16[i].s_name); + break; + case ev_integer: + writes(f, "integer %s;\r\n", progfuncs->stringtable+pr_globaldefs16[i].s_name); + break; + + case ev_union: + writes(f, "union %s;\r\n", progfuncs->stringtable+pr_globaldefs16[i].s_name); + break; + case ev_struct: + writes(f, "struct %s;\r\n", progfuncs->stringtable+pr_globaldefs16[i].s_name); + break; + default: + break; + + } + } + + for (i = 0; i < progs.progs->numfunctions; i++) + { + WriteAsmStatements(progfuncs, &progs, i, f, NULL); + } + + SafeClose(f); + + current_progstate=op; + + return true; +} +#endif + +#endif diff --git a/misc/mediasource/extra/fteqcc-src/readme.txt b/misc/mediasource/extra/fteqcc-src/readme.txt new file mode 100644 index 00000000..a254729a --- /dev/null +++ b/misc/mediasource/extra/fteqcc-src/readme.txt @@ -0,0 +1,50 @@ +Readme for the FTE QCLib + +This library is a library for running QuakeC gamecode. It does not provide any builtins itself. + +Features: + * Multiple library instances, enabling server qc, client qc, and menu qc. There is no maximum instance limit other than memory. + + * Addons, for running multiple progs in any individual instance. + + * Field reassignment, allowing a single engine to support multiple subtly different QC APIs. Also makes additional fields easier. + + * Step-by-step debugging. Requires a text editor of some form, however. A printout of the current line is also useful of course. + + * 64bit support. All strings, globals, and fields are allocated in a consecutive addressable section of memory. This also allows pointers and secure access (not implemented yet, but should be relativly easy bar builtins, which are your responsability). + + * Multiple 'threads'. The library allows a builtin to make a duplicate of the current execution state, or to wipe the current state. This allows sleep commands and fork commands. How handy. + + * Integrated QC compiler. FTEQCC comes as part of qclib. By setting up an interface with a specific value, you can cause it to always run, or run only if it detects a source change. + + * Support for different sorts of progs. Namly Hexen2's, kkqwsv's bigprogs, and FTE's extended format with extra opcodes and possibly fully 32bit offsets. The use of kkqwsv's progs is not recommended - this might be removed at some point. + + + + +Quirks: + * don't use multiple instances of fteqcc at the same time. Compilation will fail. + * 64bit support requires all strings to be allocated by qclib itself, achivable via a method call. Compatability requires a certain ammount of caution. + * a fair number of methods are obsolete. + * An overuse of pointers in the API. There are some macros which you can use to hide some of the dereferences. + * kkqwsv progs are not reliable. Do not try saving the game. Avoid letting your users know of support. + + * Builtin structures are different from original quake. You'll need to convert the arguments to qclib style. This change was required for both multiple instances as well as addon support. It should be straightforward enough. + * Entity fields are accessed via a pointer from the edict_t structure. This was required to place entity fields within the 64bit accessable section. Changing a . to a -> is not a major issue though. However, there are a lot. do a find and replace of ->v. to ->v-> + * FTE's entities are numbers not pointers. This fact is not made into a big feature as it's kinda incompatable with standard quake. Please do not use numbers directly to refer to ents but instead use the EDICT_TO_PROGS macro which will give protection. This is consistant with standard quake. + + +Basic usage: + * refer to test.c for a sample on how to set up the library. + * refer to progslib.h for the things that I've forgotten to mention. + * Call the InitProgs function to get a handle to the instance. It takes a parameter which should be set up with some fields. You'll require ReadFile, FileSize, Abort and printf for basic execution. + * Call the configure function to say how much memory to use, and how many progs/addons to support. + * Load your progs via LoadProgs. Use a crc of 0 to use any. Otherwise progs will be rejected if it doesn't match. Give it a list of progs-specific builtins too. :) + * Before calling the spawn builtin, call the InitEnts method. It's parameter stating how many maximum entities to spawn. Using a really large quantity is not much of an issue, as they are allocated as required. + * Before calling InitEnts, you can tell the VM which fields your engine uses (state all basic ones or none). This will place the entity fields in the same order as your engine expects for entvars_t. + * Obtain pointers to globals, or just use the globals structure directly. + * Call the ExecuteProgram method to start execution. + * Call the FindFunction method to find a function to run in the first place. + * Call the 'globals' method to retrieve a pointer to the globals (you should always use PR_CURRENT here). Set the parameters with the G_INT/G_FLOAT macros and friends. Use OFS_PARM0 - OFS_PARM7 to set params before calling or read inside a builtin. Use OFS_RETURN to read the return value. These macros are hard coded to use a 'pr_globals' symbol, so avoid renaming builtin parameter names. + * Ask me on IRC when it all starts keeling over. + * These are the C files that form qclib: pr_edict.c pr_exec.c pr_multi.c initlib.c qcc_pr_comp.c qcc_pr_lex.c qccmain.c qcc_cmdlib.c comprout.c hash.c qcd_main.c qcdecomp.c diff --git a/misc/mediasource/extra/fteqcc-src/test.c b/misc/mediasource/extra/fteqcc-src/test.c new file mode 100644 index 00000000..8875d2eb --- /dev/null +++ b/misc/mediasource/extra/fteqcc-src/test.c @@ -0,0 +1,221 @@ +//This is basically a sample program. +//It deomnstrates the code required to get qclib up and running. +//This code does not demonstrate entities, however. +//It does demonstrate the built in qc compiler, and does demonstrate a globals-only progs interface. +//It also demonstrates basic builtin(s). + + + +#include "progtype.h" +#include "progslib.h" + +#include +#include +#include +#include + + + + +//builtins and builtin management. +void PF_prints (progfuncs_t *prinst, struct globalvars_s *gvars) +{ + char *s; + s = prinst->VarString(prinst, 0); + + printf("%s", s); +} + +void PF_printv (progfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + printf("%f %f %f\n", G_FLOAT(OFS_PARM0+0), G_FLOAT(OFS_PARM0+1), G_FLOAT(OFS_PARM0+2)); +} + +void PF_printf (progfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + printf("%f\n", G_FLOAT(OFS_PARM0)); +} + + +void PF_bad (progfuncs_t *prinst, struct globalvars_s *gvars) +{ + printf("bad builtin\n"); +} + +builtin_t builtins[] = { + PF_bad, + PF_prints, + PF_printv, + PF_printf +}; + + + + +//Called when the qc library has some sort of serious error. +void Sys_Abort(char *s, ...) +{ //quake handles this with a longjmp. + va_list ap; + va_start(ap, s); + vprintf(s, ap); + va_end(ap); + exit(1); +} +//Called when the library has something to say. +//Kinda required for the compiler... +//Not really that useful for the normal vm. +int Sys_Printf(char *s, ...) +{ //look up quake's va function to find out how to deal with variable arguments properly. + return printf("%s", s); +} + +#include +//copy file into buffer. note that the buffer will have been sized to fit the file (obtained via FileSize) +unsigned char *Sys_ReadFile (char *fname, void *buffer, int buflen) +{ + int len; + FILE *f; + if (!strncmp(fname, "src/", 4)) + fname+=4; //skip the src part + f = fopen(fname, "rb"); + if (!f) + return NULL; + fseek(f, 0, SEEK_END); + len = ftell(f); + if (buflen < len) + return NULL; + fseek(f, 0, SEEK_SET); + fread(buffer, 1, len, f); + fclose(f); + return buffer; +} +//Finds the size of a file. +int Sys_FileSize (char *fname) +{ + int len; + FILE *f; + if (!strncmp(fname, "src/", 4)) + fname+=4; //skip the src part + f = fopen(fname, "rb"); + if (!f) + return -1; + fseek(f, 0, SEEK_END); + len = ftell(f); + fclose(f); + return len; +} +//Writes a file. +pbool Sys_WriteFile (char *fname, void *data, int len) +{ + FILE *f; + f = fopen(fname, "wb"); + if (!f) + return 0; + fwrite(data, 1, len, f); + fclose(f); + return 1; +} + +void runtest(char *progsname) +{ + progfuncs_t *pf; + func_t func; + progsnum_t pn; + + progparms_t ext; + memset(&ext, 0, sizeof(ext)); + + ext.progsversion = PROGSTRUCT_VERSION; + ext.ReadFile = Sys_ReadFile; + ext.FileSize= Sys_FileSize; + ext.Abort = Sys_Abort; + ext.printf = printf; + + ext.numglobalbuiltins = sizeof(builtins)/sizeof(builtins[0]); + ext.globalbuiltins = builtins; + + pf = InitProgs(&ext); + pf->Configure(pf, 1024*1024, 1); //memory quantity of 1mb. Maximum progs loadable into the instance of 1 +//If you support multiple progs types, you should tell the VM the offsets here, via RegisterFieldVar + pn = pf->LoadProgs(pf, progsname, 0, NULL, 0); //load the progs, don't care about the crc, and use those builtins. + if (pn < 0) + printf("test: Failed to load progs \"%s\"\n", progsname); + else + { +//allocate qc-acessable strings here for 64bit cpus. (allocate via AddString, tempstringbase is a holding area not used by the actual vm) +//you can call functions before InitEnts if you want. it's not really advised for anything except naming additional progs. This sample only allows one max. + + pf->InitEnts(pf, 10); //Now we know how many fields required, we can say how many maximum ents we want to allow. 10 in this case. This can be huge without too many problems. + +//now it's safe to ED_Alloc. + + func = pf->FindFunction(pf, "main", PR_ANY); //find the function 'main' in the first progs that has it. + if (!func) + printf("Couldn't find function\n"); + else + pf->ExecuteProgram(pf, func); //call the function + } + CloseProgs(pf); +} + + +//Run a compiler and nothing else. +//Note that this could be done with an autocompile of PR_COMPILEALWAYS. +void compile(int argc, char **argv) +{ + progfuncs_t *pf; + + progparms_t ext; + + if (0) + { + char *testsrcfile = //newstyle progs.src must start with a #. + //it's newstyle to avoid using multiple source files. + "#pragma PROGS_DAT \"testprogs.dat\"\r\n" + "//INTERMEDIATE FILE - EDIT TEST.C INSTEAD\r\n" + "\r\n" + "void(...) print = #1;\r\n" + "void() main =\r\n" + "{\r\n" + " print(\"hello world\\n\");\r\n" + "};\r\n"; + + //so that the file exists. We could insert it via the callbacks instead + Sys_WriteFile("progs.src", testsrcfile, strlen(testsrcfile)); + } + + memset(&ext, 0, sizeof(ext)); + ext.progsversion = PROGSTRUCT_VERSION; + ext.ReadFile = Sys_ReadFile; + ext.FileSize= Sys_FileSize; + ext.WriteFile= Sys_WriteFile; + ext.Abort = Sys_Abort; + ext.printf = printf; + + pf = InitProgs(&ext); + if (pf->StartCompile) + { + if (pf->StartCompile(pf, argc, argv)) + { + while(pf->ContinueCompile(pf) == 1) + ; + } + } + else + printf("no compiler in this qcvm build\n"); + CloseProgs(pf); +} + +int main(int argc, char **argv) +{ + if (argc < 2) + { + printf("Invalid arguments!\nPlease run as, for example:\n%s testprogs.dat -srcfile progs.src\nThe first argument is the name of the progs.dat to run, the remaining arguments are the qcc args to use", argv[0]); + return 0; + } + + compile(argc-1, argv+1); + runtest(argv[1]); + + return 0; +}