//======================================================================= // Copyright XashXT Group 2007 © // qcc_utils.c - qcc common tools //======================================================================= #include "vprogs.h" #include "const.h" void Hash_InitTable(hashtable_t *table, int numbucks) { table->numbuckets = numbucks; table->bucket = (bucket_t **)Qalloc(BytesForBuckets(numbucks)); } uint Hash_Key(char *name, int modulus) { //FIXME: optimize. uint key; for (key = 0; *name; name++) key += ((key<<3) + (key>>28) + *name); return (key%modulus); } void *Hash_Get(hashtable_t *table, char *name) { uint bucknum = Hash_Key(name, table->numbuckets); bucket_t *buck; buck = table->bucket[bucknum]; while(buck) { if (!STRCMP(name, buck->keystring)) return buck->data; buck = buck->next; } return NULL; } void *Hash_GetKey(hashtable_t *table, int key) { uint bucknum = key%table->numbuckets; bucket_t *buck; buck = table->bucket[bucknum]; while(buck) { if ((int)buck->keystring == key) return buck->data; buck = buck->next; } return NULL; } void *Hash_GetNext(hashtable_t *table, char *name, void *old) { uint bucknum = Hash_Key(name, table->numbuckets); bucket_t *buck; buck = table->bucket[bucknum]; while(buck) { if (!STRCMP(name, buck->keystring)) { 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->keystring)) return buck->data; buck = buck->next; } return NULL; } void *Hash_Add(hashtable_t *table, char *name, void *data, bucket_t *buck) { uint bucknum = Hash_Key(name, table->numbuckets); buck->data = data; buck->keystring = 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) { uint bucknum = key%table->numbuckets; buck->data = data; buck->keystring = (char*)key; buck->next = table->bucket[bucknum]; table->bucket[bucknum] = buck; return buck; } void Hash_Remove(hashtable_t *table, char *name) { uint bucknum = Hash_Key(name, table->numbuckets); bucket_t *buck; buck = table->bucket[bucknum]; if (!STRCMP(name, buck->keystring)) { table->bucket[bucknum] = buck->next; return; } while(buck->next) { if (!STRCMP(name, buck->next->keystring)) { buck->next = buck->next->next; return; } buck = buck->next; } } void Hash_RemoveData(hashtable_t *table, char *name, void *data) { uint bucknum = Hash_Key(name, table->numbuckets); bucket_t *buck; buck = table->bucket[bucknum]; if (buck->data == data) { if (!STRCMP(name, buck->keystring)) { table->bucket[bucknum] = buck->next; return; } } while(buck->next) { if (buck->next->data == data) { if (!STRCMP(name, buck->next->keystring)) { buck->next = buck->next->next; return; } } buck = buck->next; } } void Hash_RemoveKey(hashtable_t *table, int key) { uint bucknum = key%table->numbuckets; bucket_t *buck; buck = table->bucket[bucknum]; if ((int)buck->keystring == key) { table->bucket[bucknum] = buck->next; return; } while(buck->next) { if ((int)buck->next->keystring == key) { buck->next = buck->next->next; return; } buck = buck->next; } } /* ================ PR_decode ================ */ int PR_decode(int complen, int len, int method, char *src, char **dst) { int i; char *buffer = *dst; switch(method) { case CMP_NONE: if (complen != len) { MsgDev(D_WARN, "PR_decode: complen[%d] != len[%d]\n", complen, len); return false; } Mem_Copy(buffer, src, len); break; case CMP_LZSS: if (complen != len) { MsgDev(D_WARN, "PR_decode: complen[%d] != len[%d]\n", complen, len); return false; } for (i = 0; i < len; i++) buffer[i] = src[i] ^ 0xA5; break; case CMP_ZLIB: VFS_Unpack( src, complen, dst, len ); break; default: MsgDev(D_WARN, "PR_decode: invalid method\n"); return false; } return true; } /* ================ PR_encode ================ */ int PR_encode(int len, int method, char *src, file_t *h) { int i, pos; vfile_t *vf; switch(method) { case CMP_NONE: FS_Write(h, src, len); return len; case CMP_LZSS: for (i = 0; i < len; i++) src[i] = src[i] ^ 0xA5; FS_Write(h, src, len); return len; case CMP_ZLIB: vf = VFS_Open( h, "wz" ); pos = FS_Tell(h); // member ofs VFS_Write( vf, src, len ); VFS_Close( vf ); return FS_Tell(h) - pos; default: MsgDev(D_WARN, "PR_encode: invalid method\n"); return false; } } // CopyString returns an offset from the string heap int PR_CopyString (char *str, bool noduplicate ) { int old; char *s; if (noduplicate) { if (!str || !*str) return 0; for (s = strings; s < strings + strofs; s++) if (!com.strcmp(s, str)) return s - strings; } old = strofs; com.strcpy (strings + strofs, str); strofs += com.strlen(str)+1; return old; } /* ================ PR_WriteSourceFiles include sources into progs.dat for fake decompile if it needs ================ */ bool PR_WriteSourceFiles( file_t *h, dprograms_t *progs, bool sourceaswell ) { dsource_t *idf; cachedsourcefile_t *f; int num = 0; int ofs; for (f = sourcefile, num = 0; f; f = f->next) { if (f->type == FT_CODE && !sourceaswell) continue; num++; } if( !num ) { progs->ofssources = progs->numsources = 0; return false; } idf = Qalloc(sizeof(dsource_t) * num); for( f = sourcefile, num = 0; f; f = f->next ) { if( f->type == FT_CODE && !sourceaswell ) continue; com.strncpy( idf[num].name, f->filename, 64 ); idf[num].size = f->size; idf[num].compression = CMP_ZLIB; idf[num].filepos = FS_Tell( h ); idf[num].disksize = PR_encode(f->size, idf[num].compression, f->file, h ); num++; } ofs = FS_Tell( h ); FS_Write( h, idf, sizeof(dsource_t) * num ); sourcefile = NULL; // update header progs->ofssources = ofs; progs->numsources = num; return true; } int PR_WriteBodylessFuncs (file_t *handle) { def_t *d; int ret = 0; for (d = pr.def_head.next; d; d = d->next) { // function parms are ok if (d->type->type == ev_function && !d->scope) { if (d->initialized != 1 && d->references > 0) { FS_Write(handle, d->name, com.strlen(d->name)+1); ret++; } } } return ret; } /* ==================== AddBuffer copy string into buffer and process crc ==================== */ #define NEED_CRC 1 // calcaulate crc #define NEED_CPY 2 // copy string #define NEED_ALL NEED_CRC|NEED_CPY #define ADD1(p) AddBuffer(p, &crc, file, NEED_ALL) #define ADD2(p) AddBuffer(p, &crc, file, NEED_CPY) #define ADD3(p) AddBuffer(p, &crc, file, NEED_CRC) _inline void AddBuffer(char *p, word *crc, char *file, byte flags ) { char *s; int i = com.strlen(file); if (i + com.strlen(p) + 1 >= PROGDEFS_MAX_SIZE) return; for(s = p; *s; s++, i++) { if(flags & NEED_CRC) CRC_ProcessByte(crc, *s); if(flags & NEED_CPY) file[i] = *s; } if(flags & NEED_CPY) file[i] = '\0'; } /* ==================== PR_WriteProgdefs write advanced progdefs.h into disk ==================== */ word PR_WriteProgdefs( void ) { char file[PROGDEFS_MAX_SIZE]; string header_name, lwr_header; char lwr_prefix[8], upr_prefix[8]; char array_size[8]; // g-cont: mega name! int f = 0, k = 1; string path; def_t *d; word crc; CRC_Init( &crc ); Mem_Set( file, 0, PROGDEFS_MAX_SIZE ); FS_FileBase( progsoutname, header_name ); com.strupr( header_name, header_name ); // as part of define name com.strlwr( header_name, lwr_header ); // as part of head comment // convert progsoutname to prefix // using special case for "server", because "se" prefix looks ugly if(!com.stricmp( header_name, "server" )) com.strncpy( lwr_prefix, "sv", 8 ); else com.strnlwr( header_name, lwr_prefix, 3 ); com.strnupr( lwr_prefix, upr_prefix, 3 ); ADD1("//=======================================================================\n"); ADD1("// Copyright XashXT Group 2008 ©\n"); ADD2(va("// %s_edict.h - %s prvm edict\n", lwr_prefix, lwr_header )); ADD1("//=======================================================================\n"); ADD2(va("#ifndef %s_EDICT_H\n#define %s_EDICT_H\n", upr_prefix, upr_prefix )); ADD1("\nstruct"); ADD2(va(" %s_globalvars_s", lwr_prefix )); ADD1(va("\n{")); ADD2(va("\n")); ADD1(va("\tint\tpad[%i];\n", RESERVED_OFS)); // write globalvars_t for( d = pr.def_head.next; d; d = d->next ) { if( !com.strcmp (d->name, "end_sys_globals")) break; if( d->ofs < RESERVED_OFS ) continue; if( com.strchr( d->name, '[' )) continue; // skip arrays if( d->arraysize > 1 ) com.snprintf( array_size, 8, "[%i]", d->arraysize ); else array_size[0] = '\0'; // clear array switch( d->type->type ) { case ev_float: ADD1(va("\tfloat\t%s%s;\n",d->name, array_size)); break; case ev_vector: ADD1(va("\tvec3_t\t%s%s;\n",d->name, array_size)); d = d->next->next->next; // skip the elements break; case ev_string: ADD1(va("\tstring_t\t%s%s;\n",d->name, array_size)); break; case ev_function: ADD1(va("\tfunc_t\t%s%s;\n",d->name, array_size)); break; case ev_entity: ADD1(va("\tint\t%s%s;\n",d->name, array_size)); break; case ev_integer: ADD1(va("\tint\t%s%s;\n",d->name, array_size)); break; default: ADD1(va("\tint\t%s%s;\n",d->name, array_size)); break; } } ADD1("};\n\n"); // write entvars_t ADD1("struct"); ADD2(va(" %s_entvars_s", lwr_prefix )); ADD1("\n{\n"); // write entvars_t for( d = pr.def_head.next; d; d = d->next ) { if( !com.strcmp (d->name, "end_sys_fields")) break; if( d->type->type != ev_field ) continue; if( com.strchr( d->name, '[' )) continue; // skip arrays if( d->arraysize > 1 ) com.snprintf( array_size, 8, "[%i]", d->arraysize ); else array_size[0] = '\0'; // clear array switch( d->type->aux_type->type ) { case ev_float: ADD1(va("\tfloat\t%s%s;\n",d->name, array_size )); break; case ev_vector: ADD1(va("\tvec3_t\t%s%s;\n",d->name, array_size)); d = d->next->next->next; // skip the elements break; case ev_string: ADD1(va("\tstring_t\t%s%s;\n",d->name, array_size)); break; case ev_function: ADD1(va("\tfunc_t\t%s%s;\n",d->name, array_size)); break; case ev_entity: ADD1(va("\tint\t%s%s;\n",d->name, array_size)); break; case ev_integer: ADD1(va("\tint\t%s%s;\n",d->name, array_size)); break; default: ADD1(va("\tint\t%s%s;\n",d->name, array_size)); break; } } ADD1("};\n\n"); ADD2(va("#define PROG_CRC_%s\t\t%i\n", header_name, crc )); ADD2(va("\n#endif//%s_EDICT_H", upr_prefix )); if( ForcedCRC ) crc = ForcedCRC; // potentially backdoor if( host_instance == HOST_NORMAL || host_instance == HOST_DEDICATED ) { // make sure what progs file will be placed into right directory com.snprintf( progsoutname, MAX_SYSPATH, "%s/%s.dat", GI->vprogs_dir, header_name ); } switch( crc ) { case 8505: PR_Message("Xash3D unmodified server.dat\n"); if(!com.strcmp(progsoutname, "unknown.dat")) com.strcpy(progsoutname, "server.dat"); break; case 3720: PR_Message("Xash3D unmodified client.dat\n"); if(!com.strcmp(progsoutname, "unknown.dat")) com.strcpy(progsoutname, "client.dat"); break; case 2158: PR_Message("Xash3D unmodified uimenu.dat\n"); if(!com.strcmp(progsoutname, "unknown.dat")) com.strcpy(progsoutname, "uimenu.dat"); break; default: PR_Message("Custom progs crc %d\n", crc ); if(!com.strcmp(progsoutname, "unknown.dat")) com.strcpy(progsoutname, "progs.dat"); if( host_instance != HOST_QCCLIB ) break; com.snprintf( path, MAX_STRING, "../%s_edict.h", lwr_prefix ); PR_Message( "writing %s\n", path ); // auto-determine FS_WriteFile( path, file, com.strlen(file)); break; } return crc; } void PR_WriteDAT( void ) { ddef_t *dd; file_t *f; dprograms_t progs; // header char element[MAX_NAME]; int i, crc, progsize, num_ref; def_t *def, *comp_x, *comp_y, *comp_z; crc = PR_WriteProgdefs(); // write progdefs.h progs.flags = 0; if(numstatements > MAX_STATEMENTS) PR_ParseError(ERR_INTERNAL, "Too many statements - %i\n", numstatements ); if(strofs > MAX_STRINGS) PR_ParseError(ERR_INTERNAL, "Too many strings - %i\n", strofs ); PR_UnmarshalLocals(); if(opt_compstrings) progs.flags |= COMP_STRINGS; if(opt_compfunctions) { progs.flags |= COMP_FUNCTIONS; progs.flags |= COMP_STATEMENTS; } if(opt_compress_other) { progs.flags |= COMP_DEFS; progs.flags |= COMP_FIELDS; progs.flags |= COMP_GLOBALS; } // compression of blocks? if( opt_writelinenums ) progs.flags |= COMP_LINENUMS; if( opt_writetypes ) progs.flags |= COMP_TYPES; // part of how compilation works. This def is always present, and never used. def = PR_GetDef(NULL, "end_sys_globals", NULL, false, 0); if(def) def->references++; def = PR_GetDef(NULL, "end_sys_fields", NULL, false, 0); 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 com.sprintf(element, "%s_x", def->name); comp_x = PR_GetDef(NULL, element, def->scope, false, 0); com.sprintf(element, "%s_y", def->name); comp_y = PR_GetDef(NULL, element, def->scope, false, 0); com.sprintf(element, "%s_z", def->name); comp_z = PR_GetDef(NULL, element, def->scope, false, 0); num_ref = def->references; if (comp_x && comp_y && comp_z) { num_ref += comp_x->references; num_ref += comp_y->references; num_ref += comp_z->references; if (!def->references) { if (!comp_x->references || !comp_y->references || !comp_z->references) num_ref = 0; // one of these vars is useless... } def->references = num_ref; if (!num_ref) num_ref = 1; if (comp_x) comp_x->references = num_ref; if (comp_y) comp_y->references = num_ref; if (comp_z) comp_z->references = num_ref; } } if (def->references <= 0) { if(def->local) PR_Warning(WARN_NOTREFERENCED, strings + def->s_file, def->s_line, "'%s' : unreferenced local variable", def->name); if (opt_unreferenced && def->type->type != ev_field) continue; } if (def->type->type == ev_function) { if( opt_function_names && functions[G_FUNCTION(def->ofs)].first_statement < 0 ) { def->name = ""; } if( !def->timescalled ) { if( def->references <= 1 ) 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); } 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. // if it's only ever called explicitly, the engine doesn't need to know. continue; } } else if( def->type->type == ev_field ) { dd = &fields[numfielddefs]; dd->type = def->type->aux_type->type; dd->s_name = PR_CopyString( def->name, opt_noduplicatestrings ); dd->ofs = G_INT(def->ofs); numfielddefs++; } else if ((def->scope||def->constant) && (def->type->type != ev_string || opt_constant_names_strings)) { if (opt_constant_names) continue; } dd = &qcc_globals[numglobaldefs]; numglobaldefs++; if (opt_writetypes) dd->type = def->type-qcc_typeinfo; else dd->type = def->type->type; if ( def->saved && ((!def->initialized || def->type->type == ev_function) && def->type->type != ev_field && def->scope == NULL)) dd->type |= DEF_SAVEGLOBAL; if (def->shared) dd->type |= DEF_SHARED; if (opt_locals && (def->scope || !com.strcmp(def->name, "IMMEDIATE"))) { dd->s_name = 0; } else dd->s_name = PR_CopyString (def->name, opt_noduplicatestrings ); dd->ofs = def->ofs; } if(numglobaldefs > MAX_GLOBALS) PR_ParseError(ERR_INTERNAL, "Too many globals - %i\n", numglobaldefs ); strofs = (strofs + 3) & ~3; PR_Message("Linking...\n"); if( host_instance == HOST_QCCLIB ) { // don't flood into engine console MsgDev(D_INFO, "%6i strofs (of %i)\n", strofs, MAX_STRINGS); MsgDev(D_INFO, "%6i numstatements (of %i)\n", numstatements, MAX_STATEMENTS); MsgDev(D_INFO, "%6i numfunctions (of %i)\n", numfunctions, MAX_FUNCTIONS); MsgDev(D_INFO, "%6i numglobaldefs (of %i)\n", numglobaldefs, MAX_GLOBALS); MsgDev(D_INFO, "%6i numfielddefs (%i unique) (of %i)\n", numfielddefs, pr.size_fields, MAX_FIELDS); MsgDev(D_INFO, "%6i numpr_globals (of %i)\n", numpr_globals, MAX_REGS); } f = FS_Open( progsoutname, "wb" ); if( !f ) PR_ParseError( ERR_INTERNAL, "Couldn't open file %s", progsoutname ); FS_Write(f, &progs, sizeof(progs)); FS_Write(f, "\r\n\r\n", 4); FS_Write(f, v_copyright, com.strlen(v_copyright) + 1); FS_Write(f, "\r\n\r\n", 4); progs.ofs_strings = FS_Tell(f); progs.numstrings = strofs; PR_WriteBlock(f, progs.ofs_strings, strings, strofs, progs.flags & COMP_STRINGS); progs.ofs_statements = FS_Tell(f); progs.numstatements = numstatements; for (i = 0; i < numstatements; i++) { statements[i].op = LittleLong(statements[i].op); statements[i].a = LittleLong(statements[i].a); statements[i].b = LittleLong(statements[i].b); statements[i].c = LittleLong(statements[i].c); } PR_WriteBlock(f, progs.ofs_statements, statements, progs.numstatements*sizeof(dstatement_t), progs.flags & COMP_STATEMENTS); progs.ofs_functions = FS_Tell(f); progs.numfunctions = numfunctions; for (i = 0; i < numfunctions; i++) { functions[i].first_statement = LittleLong (functions[i].first_statement); functions[i].parm_start = LittleLong (functions[i].parm_start); functions[i].s_name = LittleLong (functions[i].s_name); functions[i].s_file = LittleLong (functions[i].s_file); functions[i].numparms = LittleLong ((functions[i].numparms > MAX_PARMS) ? MAX_PARMS : functions[i].numparms); functions[i].locals = LittleLong (functions[i].locals); } PR_WriteBlock(f, progs.ofs_functions, functions, progs.numfunctions*sizeof(dfunction_t), progs.flags & COMP_FUNCTIONS); progs.ofs_globaldefs = FS_Tell(f); progs.numglobaldefs = numglobaldefs; for (i = 0; i < numglobaldefs; i++) { qcc_globals[i].type = LittleLong(qcc_globals[i].type); qcc_globals[i].ofs = LittleLong(qcc_globals[i].ofs); qcc_globals[i].s_name = LittleLong(qcc_globals[i].s_name); } PR_WriteBlock(f, progs.ofs_globaldefs, qcc_globals, progs.numglobaldefs*sizeof(ddef_t), progs.flags & COMP_DEFS); progs.ofs_fielddefs = FS_Tell(f); progs.numfielddefs = numfielddefs; for (i = 0; i < numfielddefs; i++) { fields[i].type = LittleLong(fields[i].type); fields[i].ofs = LittleLong(fields[i].ofs); fields[i].s_name = LittleLong(fields[i].s_name); } PR_WriteBlock(f, progs.ofs_fielddefs, fields, progs.numfielddefs*sizeof(ddef_t), progs.flags & COMP_FIELDS); progs.ofs_globals = FS_Tell(f); progs.numglobals = numpr_globals; for (i = 0; (uint) i < numpr_globals; i++) ((int *)pr_globals)[i] = LittleLong (((int *)pr_globals)[i]); PR_WriteBlock(f, progs.ofs_globals, pr_globals, numpr_globals*4, progs.flags & COMP_GLOBALS); if(opt_writetypes) { for (i = 0; i < numtypeinfos; i++) { if (qcc_typeinfo[i].aux_type) qcc_typeinfo[i].aux_type = (type_t*)(qcc_typeinfo[i].aux_type - qcc_typeinfo); if (qcc_typeinfo[i].next) qcc_typeinfo[i].next = (type_t*)(qcc_typeinfo[i].next - qcc_typeinfo); qcc_typeinfo[i].name = (char *)PR_CopyString(qcc_typeinfo[i].name, true ); } } progs.ident = VPROGSHEADER32; progs.version = VPROGS_VERSION; progs.ofssources = 0; progs.ofslinenums = 0; progs.ident = 0; progs.ofsbodylessfuncs = 0; progs.numbodylessfuncs = 0; progs.ofs_types = 0; progs.numtypes = 0; progs.ofsbodylessfuncs = FS_Tell(f); progs.numbodylessfuncs = PR_WriteBodylessFuncs(f); if( opt_writelinenums ) { progs.ofslinenums = FS_Tell(f); PR_WriteBlock(f, progs.ofslinenums, statement_linenums, numstatements*sizeof(int), progs.flags & COMP_LINENUMS); } if( opt_writetypes ) { progs.ofs_types = FS_Tell(f); progs.numtypes = numtypeinfos; PR_WriteBlock(f, progs.ofs_types, qcc_typeinfo, progs.numtypes*sizeof(type_t), progs.flags & COMP_TYPES); } PR_WriteSourceFiles( f, &progs, opt_writesources ); progsize = ((FS_Tell(f)+3) & ~3); progs.entityfields = pr.size_fields; progs.crc = crc; // byte swap the header and write it out for (i = 0; i < sizeof(progs)/4; i++)((int *)&progs)[i] = LittleLong (((int *)&progs)[i]); FS_Seek(f, 0, SEEK_SET); FS_Write(f, &progs, sizeof(progs)); if( asmfile ) FS_Close(asmfile); if( host_instance == HOST_QCCLIB ) MsgDev(D_INFO, "Writing %s, total size %i bytes\n", progsoutname, progsize ); FS_Close( f ); } /* ======================= PR_UnmarshalLocals marshalled locals remaps all the functions to use the range MAX_REGS onwards for the offset to thier locals. this function remaps all the locals back into the function. ======================= */ void PR_UnmarshalLocals( void ) { def_t *def; uint ofs; uint 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; } PR_RemapOffsets(0, numstatements, MAX_REGS, MAX_REGS + maxo-numpr_globals + 3, ofs); numpr_globals = maxo+3; if (numpr_globals > MAX_REGS) PR_ParseError(ERR_INTERNAL, "Too many globals are in use to unmarshal all locals"); if (maxo-ofs) PR_Message("Total of %i marshalled globals\n", maxo-ofs); } byte *PR_LoadFile( char *filename, bool crash, int type ) { int length; cachedsourcefile_t *newfile; string fullname; byte *file; char *path; // NOTE: in-game we can't use ../pathes, but root directory always // ahead over ../pathes, so translate path from // ../common/svc_user.h to source/common/svc_user.h if( host_instance == HOST_NORMAL || host_instance == HOST_DEDICATED ) { if((path = com.strstr( filename, ".." ))) { path += 2; // skip .. com.snprintf( fullname, MAX_STRING, "%s%s", GI->source_dir, path ); } else com.snprintf( fullname, MAX_STRING, "%s/%s/%s", GI->source_dir, sourcedir, filename ); } else com.strncpy( fullname, filename, MAX_STRING ); file = FS_LoadFile( fullname, &length ); if( !file || !length ) { if( crash ) PR_ParseError( ERR_INTERNAL, "Couldn't open file %s", fullname ); else return NULL; } newfile = (cachedsourcefile_t*)Qalloc(sizeof(cachedsourcefile_t)); newfile->next = sourcefile; sourcefile = newfile; // make chain com.strcpy(sourcefile->filename, fullname ); sourcefile->file = file; sourcefile->type = type; sourcefile->size = length; return sourcefile->file; } bool PR_Include( char *filename ) { char *newfile; char fname[512]; char *opr_file_p; string_t os_file, os_file2; int opr_source_line; char *ocompilingfile; includechunk_t *oldcurrentchunk; ocompilingfile = compilingfile; os_file = s_file; os_file2 = s_file2; opr_source_line = pr_source_line; opr_file_p = pr_file_p; oldcurrentchunk = currentchunk; com.strcpy(fname, filename); newfile = QCC_LoadFile( fname, true ); currentchunk = NULL; pr_file_p = newfile; 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; return true; } void PR_WriteBlock(file_t *f, fs_offset_t pos, const void *data, size_t blocksize, bool compress) { vfile_t *h; int len = 0; if (compress) { h = VFS_Open( f, "wz" ); VFS_Write( h, data, blocksize ); // write into buffer FS_Write(f, &len, sizeof(int)); // first four bytes it's a compressed filesize f = VFS_Close(h); // deflate, then write into disk len = LittleLong(FS_Tell(f) - pos); // calculate complength FS_Seek(f, pos, SEEK_SET); // seek back to start block... FS_Write(f, &len, sizeof(int)); // ... and write real complength value. FS_Seek(f, 0, SEEK_END); // return } else FS_Write(f, data, blocksize); // just write data } byte *PR_CreateProgsSRC( void ) { search_t *qc = FS_Search( "*", true ); const char *datname = "unknown.dat\n"; // outname will be set by PR_WriteProgdefs byte *newprogs_src = NULL; char searchmask[8][16]; string headers[2]; // contains filename with struct description bool have_entvars = 0; bool have_globals = 0; int i, k, j = 0; // hardcoded table! don't change! com.strcpy( searchmask[0], "qh" ); // quakec header com.strcpy( searchmask[1], "h" ); // c-style header com.strcpy( searchmask[2], "qc" ); // quakec sources com.strcpy( searchmask[3], "c" ); // c-style sources if(!qc) { PR_ParseError(ERR_INTERNAL, "Couldn't open file progs.src" ); return NULL; } Mem_Set( headers, '/0', MAX_STRING * 2 ); for( i = 0; i < qc->numfilenames; i++ ) { // search by mask for( k = 0; k < 8; k++) { // skip blank mask if(!com.strlen( searchmask[k] )) continue; if(!com.stricmp( searchmask[k], FS_FileExtension( qc->filenames[i] ))) // validate ext { script_t *source = Com_OpenScript( qc->filenames[i], NULL, 0 ); token_t token; if( source ) { while( Com_ReadToken( source, SC_ALLOW_NEWLINES, &token )) { // parse all sources for "end_sys_globals" if( !com.strcmp( token.string, "end_sys_globals" )) { com.strncpy( headers[0], qc->filenames[i], MAX_STRING ); have_globals = true; } else if( !com.strcmp( token.string, "end_sys_fields" )) { com.strncpy( headers[1], qc->filenames[i], MAX_STRING ); have_entvars = true; } if( have_globals && have_entvars ) { Com_CloseScript( source ); goto buildnewlist; // end of parsing } } Com_CloseScript( source ); } } } } // globals and locals not declared PR_ParseError( ERR_INTERNAL, "Couldn't open file progs.src" ); return NULL; buildnewlist: newprogs_src = Qrealloc( newprogs_src, j + com.strlen( datname ) + 1 ); // outfile name Mem_Copy( newprogs_src + j, (char *)datname, com.strlen( datname )); j += com.strlen(datname) + 1; // null term // file contains "sys_globals" and possible "sys_fields" newprogs_src = Qrealloc(newprogs_src, j + com.strlen(headers[0]) + 2); // first file com.strncat(newprogs_src, va("%s\n", headers[0]), com.strlen(headers[0]) + 1); j += com.strlen(headers[0]) + 2; //null term if(STRCMP( headers[0], headers[1] )) { // file contains sys_fields description newprogs_src = Qrealloc( newprogs_src, j + com.strlen( headers[1] ) + 2 ); // second file (option) com.strncat( newprogs_src, va("%s\n", headers[1]), com.strlen(headers[1]) + 1); j += com.strlen(headers[1]) + 2; //null term } // add headers for( i = 0; i < qc->numfilenames; i++ ) { for( k = 0; k < 2; k++) { // skip blank mask if(!com.strlen(searchmask[k])) continue; if(!com.stricmp(searchmask[k], FS_FileExtension(qc->filenames[i]))) // validate ext { if(!com.strcmp(qc->filenames[i], headers[0]) || !com.strcmp(qc->filenames[i], headers[1])) break; //we already have it, just skip newprogs_src = Qrealloc( newprogs_src, j + com.strlen(qc->filenames[i]) + 2); com.strncat(newprogs_src, va("%s\n", qc->filenames[i]), com.strlen(qc->filenames[i]) + 1); j += com.strlen(qc->filenames[i]) + 2; } } } // add other sources for(i = 0; i < qc->numfilenames; i++) { for( k = 2; k < 8; k++) { // skip blank mask if(!com.strlen(searchmask[k])) continue; if(!com.stricmp(searchmask[k], FS_FileExtension(qc->filenames[i]))) // validate ext { if(!com.strcmp(qc->filenames[i], headers[0]) || !com.strcmp(qc->filenames[i], headers[1])) break; //we already have it, just skip newprogs_src = Qrealloc( newprogs_src, j + com.strlen(qc->filenames[i]) + 2); com.strncat(newprogs_src, va("%s\n", qc->filenames[i]), com.strlen(qc->filenames[i]) + 1); j += com.strlen(qc->filenames[i]) + 2; } } } Mem_Free( qc ); // free search FS_WriteFile( "progs.src", newprogs_src, j ); return newprogs_src; }