// // Author: Michael Cameron // Email: chronokun@hotmail.com // #include "libraries.h" #include "v8mapparser.h" using namespace std; #ifndef _MSC_VER /* str*_s functions are microsoft only extensions of C string manip. mingw is ok with strtok_r, but is not ok with strcpy_r additionally, old versions of mingw may not be ok with strtok_r at all. */ inline char *strtok_s(char* s, const char* delm, char** context) { return strtok_r(s, delm, context); } inline char *strcpy_s(char *dest, const char *src) { return strcpy(dest, src); } #endif #define KEYWORD_GLOBAL "\tglobal" #define KEYWORD_PREFAB "\tprefab" #define KEYWORD_ENTITY "\tentity" #define KEYWORD_BRUSH "\tbrush" #define KEYWORD_VERTICES "\tvertices" #define KEYWORD_FACES "\tfaces" const bool CMapParser::LoadMap(const char* _kpcFileName) { std::ifstream InFile; EParserState eState = PARSERSTATE_UNKNOWN; InFile.open(_kpcFileName, std::ios::in); if(InFile.is_open()) { std::string Line; bool bAdvance = true; bool bGoing = true; while(bGoing) { if(bAdvance) { if(!std::getline(InFile, Line)) { bGoing = false; } } bAdvance = true; if(eState == PARSERSTATE_UNKNOWN) { if(Line.find(KEYWORD_ENTITY) != string::npos) { eState = PARSERSTATE_ENTITY; continue; } else if (Line.find(KEYWORD_PREFAB) != string::npos || Line.find(KEYWORD_GLOBAL) != string::npos) { // prefab and global share an indentation level // both encapsulate similar objects eState = PARSERSTATE_PREFAB; continue; } else { cout << "Warning: PARSERSTATE_UNKNOWN and no cases met." << endl; } } else if (eState == PARSERSTATE_PREFAB) { eState = this->ParsePrefab(Line); } else if(eState == PARSERSTATE_ENTITY) { eState = this->ParseEntity(Line); } else if(eState == PARSERSTATE_WORLDSPAWN) { eState = this->ParseWorldSpawn(Line); } else if(eState == PARSERSTATE_BRUSH) { eState = this->ParseBrush(Line); //bAdvance = false; } else if(eState == PARSERSTATE_VERTEX) { eState = this->ParseVertex(Line); if(eState != PARSERSTATE_VERTEX) { bAdvance = false; } } else if(eState == PARSERSTATE_FACE) { eState = this->ParseFace(Line); if(eState != PARSERSTATE_FACE) { bAdvance = false; } } } InFile.close(); } else { return(false); } return(true); } EParserState CMapParser::ParsePrefab(const std::string _Line) { // this is going with the idea that its possible to correctly parse the map file // if prefabs are simply broken and de-indented. // typically entities are listed first, then brushes, but just in case. if (_Line.find(KEYWORD_ENTITY) != string::npos) { return PARSERSTATE_ENTITY; } else { return PARSERSTATE_BRUSH; } } EParserState CMapParser::ParseEntity(const std::string _Line) { const size_t kszLineSize = 256; char pcLine[kszLineSize]; const char* kpcDelim = " "; char* pcToken; char* pcContext; strcpy_s(pcLine, _Line.c_str()); pcToken = strtok_s(pcLine, kpcDelim, &pcContext); if (_Line.find(KEYWORD_BRUSH) != string::npos) { m_WorldSpawn.m_Brushes.push_back(TBrush()); return PARSERSTATE_BRUSH; } while(pcToken != nullptr) { if(strcmp(pcToken, "\ttype") == 0) { pcToken = strtok_s(nullptr, kpcDelim, &pcContext); if(strcmp(pcToken, "WorldSpawn") == 0) { return(PARSERSTATE_WORLDSPAWN); } else { return(PARSERSTATE_UNKNOWN); } } else { return(PARSERSTATE_ENTITY); } } return(PARSERSTATE_UNKNOWN); } EParserState CMapParser::ParseWorldSpawn(const std::string _Line) { if(_Line.find(KEYWORD_BRUSH) != string::npos) { m_WorldSpawn.m_Brushes.push_back(TBrush()); return(PARSERSTATE_BRUSH); } return(PARSERSTATE_WORLDSPAWN); } EParserState CMapParser::ParseBrush(const std::string _Line) { if(_Line.find(KEYWORD_BRUSH) != string::npos) { m_WorldSpawn.m_Brushes.push_back(TBrush()); return(PARSERSTATE_BRUSH); } if(_Line.find(KEYWORD_VERTICES) != string::npos) { return(PARSERSTATE_VERTEX); } else if(_Line.find(KEYWORD_FACES) != string::npos) { return(PARSERSTATE_FACE); } return(PARSERSTATE_BRUSH); } EParserState CMapParser::ParseVertex(const std::string _Line) { const size_t kszLineSize = 2048; char pcLine[kszLineSize]; const char* kpcDelim = " \t"; char* pcToken; char* pcContext; strcpy_s(pcLine, _Line.c_str()); pcToken = strtok_s(pcLine, kpcDelim, &pcContext); Eigen::Vector3f Vert; int iTokenNum = 0; while(pcToken != NULL) { if(std::isdigit(pcToken[0], std::locale()) || pcToken[0] == '-') { double dValue = std::stod(pcToken); if(iTokenNum == 0) { Vert[X] = (float)dValue; } else if(iTokenNum == 1) { Vert[Y] = (float)dValue; } else if(iTokenNum == 2) { Vert[Z] = (float)dValue; } iTokenNum++; } else { return(PARSERSTATE_BRUSH); } pcToken = strtok_s(NULL, kpcDelim, &pcContext); } m_WorldSpawn.m_Brushes[m_WorldSpawn.m_Brushes.size()-1].m_Vertices.push_back(Vert); return(PARSERSTATE_VERTEX); } EParserState CMapParser::ParseFace(const std::string _Line) { const size_t kszLineSize = 2048; char pcLine[kszLineSize]; const char* kpcDelim = " \t"; char* pcToken; char* pcContext; strcpy_s(pcLine, _Line.c_str()); pcToken = strtok_s(pcLine, kpcDelim, &pcContext); int iTokenNum = 0; std::string material; std::vector Indices; while(pcToken != nullptr) { if(iTokenNum < 5) { if(std::isdigit(pcToken[0], std::locale()) || pcToken[0] == '-') { double dValue = std::stod(pcToken); } else { return(PARSERSTATE_BRUSH); } } else if (iTokenNum == 9) { // this should be '0x' something, which wasn't in V6. ; } else { if(std::isdigit(pcToken[0], std::locale()) || pcToken[0] == '-') { int iValue = std::stoi(pcToken); Indices.push_back(iValue); } else { material = pcToken; } } iTokenNum++; pcToken = strtok_s(nullptr, kpcDelim, &pcContext); } TFace Face; Face.m_Indices = Indices; Face.m_Material = material; m_WorldSpawn.m_Brushes[m_WorldSpawn.m_Brushes.size()-1].m_Faces.push_back(Face); return(PARSERSTATE_FACE); }