/* Copyright (C) 1999-2006 Id Software, Inc. and contributors. For a list of contributors, see the accompanying CONTRIBUTORS file. This file is part of GtkRadiant. GtkRadiant is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. GtkRadiant is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "eclass_def.h" #include "iscriplib.h" #include "ifilesystem.h" #include "iarchive.h" #include "eclasslib.h" #include "stream/stringstream.h" #include "stream/textfilestream.h" #include "modulesystem/moduleregistry.h" #include "os/path.h" const char* EClass_GetExtension() { return "def"; } void Eclass_ScanFile (EntityClassCollector& collector, const char *filename); #include "modulesystem/singletonmodule.h" class EntityClassDefDependencies : public GlobalShaderCacheModuleRef, public GlobalScripLibModuleRef { }; class EclassDefAPI { EntityClassScanner m_eclassdef; public: typedef EntityClassScanner Type; STRING_CONSTANT(Name, "def"); EclassDefAPI() { m_eclassdef.scanFile = &Eclass_ScanFile; m_eclassdef.getExtension = &EClass_GetExtension; } EntityClassScanner* getTable() { return &m_eclassdef; } }; typedef SingletonModule EclassDefModule; typedef Static StaticEclassDefModule; StaticRegisterModule staticRegisterEclassDef(StaticEclassDefModule::instance()); #include "string/string.h" #include char com_token[1024]; bool com_eof; /* ============== COM_Parse Parse a token out of a string ============== */ const char *COM_Parse (const char *data) { int c; int len; len = 0; com_token[0] = 0; if (!data) return 0; // skip whitespace skipwhite: while ( (c = *data) <= ' ') { if (c == 0) { com_eof = true; return 0; // 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=='\"') { com_token[len] = 0; return data; } com_token[len] = c; len++; } while (1); } // parse single characters if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':') { com_token[len] = c; len++; com_token[len] = 0; return data+1; } // parse a regular word do { com_token[len] = c; data++; len++; c = *data; if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':') break; } while (c>32); com_token[len] = 0; return data; } const char* Get_COM_Token() { return com_token; } const char *debugname; void setSpecialLoad(EntityClass *e, const char* pWhat, CopiedString& p) { // Hydra: removed some amazingly bad cstring usage, whoever wrote that // needs to be taken out and shot. const char *pText = 0; const char *where = 0; where = strstr(e->comments(),pWhat); if (!where) return; pText = where + strlen(pWhat); if (*pText == '\"') pText++; where = strchr(pText,'\"'); if (where) { p = StringRange(pText, where); } else { p = pText; } } #include "eclasslib.h" /* the classname, color triple, and bounding box are parsed out of comments A ? size means take the exact brush size. / *QUAKED (0 0 0) ? / *QUAKED (0 0 0) (-8 -8 -8) (8 8 8) Flag names can follow the size description: / *QUAKED func_door (0 .5 .8) ? START_OPEN STONE_SOUND DOOR_DONT_LINK GOLD_KEY SILVER_KEY */ EntityClass *Eclass_InitFromText (const char *text) { EntityClass* e = Eclass_Alloc(); e->free = &Eclass_Free; // grab the name text = COM_Parse (text); e->m_name = Get_COM_Token(); debugname = e->name(); { // grab the color, reformat as texture name int r = sscanf (text," (%f %f %f)", &e->color[0], &e->color[1], &e->color[2]); if (r != 3) return e; eclass_capture_state(e); } while (*text != ')') { if (!*text) { return 0; } text++; } text++; // get the size text = COM_Parse (text); if (Get_COM_Token()[0] == '(') { // parse the size as two vectors e->fixedsize = true; int r = sscanf (text,"%f %f %f) (%f %f %f)", &e->mins[0], &e->mins[1], &e->mins[2], &e->maxs[0], &e->maxs[1], &e->maxs[2]); if (r != 6) { return 0; } for (int i=0 ; i<2 ; i++) { while (*text != ')') { if (!*text) { return 0; } text++; } text++; } } char parms[256]; // get the flags { // copy to the first /n char* p = parms; while (*text && *text != '\n') *p++ = *text++; *p = 0; text++; } { // any remaining words are parm flags const char* p = parms; for (std::size_t i=0 ; iflagnames[i], Get_COM_Token()); } } e->m_comments = text; setSpecialLoad(e, "model=", e->m_modelpath); StringOutputStream buffer(string_length(e->m_modelpath.c_str())); buffer << PathCleaned(e->m_modelpath.c_str()); e->m_modelpath = buffer.c_str(); if(!e->fixedsize) { EntityClass_insertAttribute(*e, "angle", EntityClassAttribute("direction", "Direction", "0")); } else { EntityClass_insertAttribute(*e, "angle", EntityClassAttribute("angle", "Yaw Angle", "0")); } EntityClass_insertAttribute(*e, "model", EntityClassAttribute("model", "Model")); EntityClass_insertAttribute(*e, "noise", EntityClassAttribute("sound", "Sound")); return e; } void Eclass_ScanFile (EntityClassCollector& collector, const char *filename) { EntityClass *e; TextFileInputStream inputFile(filename); if(inputFile.failed()) { globalErrorStream() << "ScanFile: " << filename << " not found\n"; return; } globalOutputStream() << "ScanFile: " << filename << "\n"; enum EParserState { eParseDefault, eParseSolidus, eParseComment, eParseQuakeED, eParseEntityClass, eParseEntityClassEnd, } state = eParseDefault; const char* quakeEd = "QUAKED"; const char* p = 0; StringBuffer buffer; SingleCharacterInputStream bufferedInput(inputFile); for(;;) { char c; if(!bufferedInput.readChar(c)) { break; } switch(state) { case eParseDefault: if(c == '/') { state = eParseSolidus; } break; case eParseSolidus: if(c == '/') { state = eParseComment; } else if(c == '*') { p = quakeEd; state = eParseQuakeED; } break; case eParseComment: if(c == '\n') { state = eParseDefault; } break; case eParseQuakeED: if(c == *p) { if(*(++p) == '\0') { state = eParseEntityClass; } } else { state = eParseDefault; } break; case eParseEntityClass: if(c == '*') { state = eParseEntityClassEnd; } else { buffer.push_back(c); } break; case eParseEntityClassEnd: if(c == '/') { e = Eclass_InitFromText(buffer.c_str()); state = eParseDefault; if (e) collector.insert(e); else globalErrorStream() << "Error parsing: " << debugname << " in " << filename << "\n"; buffer.clear(); state = eParseDefault; } else { buffer.push_back('*'); buffer.push_back(c); state = eParseEntityClass; } break; } } }