Skip to content
Browse files

Added Level File class & parsing for level files

  • Loading branch information...
1 parent 94ea492 commit a838f06b31e00afa116918524ce1b73ddb346d0b @mrwonko committed
Showing with 305 additions and 0 deletions.
  1. +2 −0 CMakeLists.txt
  2. +74 −0 include/LevelFormat.hpp
  3. +227 −0 src/LevelFormat.cpp
  4. +2 −0 src/main.cpp
View
2 CMakeLists.txt
@@ -76,8 +76,10 @@ endif(${CMAKE_BUILD_TYPE} MATCHES Debug)
include_directories("include")
set(SRC_FILES
"src/main.cpp"
+ "src/LevelFormat.cpp"
)
set(INCLUDE_FILES
+ "include/LevelFormat.hpp"
)
set(ALL_FILES ${SRC_FILES} ${INCLUDE_FILES})
View
74 include/LevelFormat.hpp
@@ -0,0 +1,74 @@
+#ifndef HFPS_LEVEL_FORMAT_HPP
+#define HFPS_LEVEL_FORMAT_HPP
+
+#include <string>
+#include <map>
+#include <list>
+
+struct LevelFile
+{
+ static const unsigned int VERSION = 0;
+ static const unsigned int MAX_PATH_LENGTH = 64;
+ static const char IDENT[8];
+
+ struct Header
+ {
+ char ident[8]; //"HFPSLVL\0" - Hydra FPS Level
+ unsigned int version;
+ unsigned int numSurfaces;
+ };
+
+ struct Vertex
+ {
+ float coordinates[3];
+ float uv[2];
+ };
+
+ struct Triangle
+ {
+ unsigned short indices[3];
+ };
+
+ struct Surface
+ {
+ Surface() :
+ vertices(NULL),
+ triangles(NULL)
+ {}
+
+ ~Surface()
+ {
+ delete[] vertices;
+ delete[] triangles;
+ }
+
+ enum SurfaceFlags
+ {
+ sfSolid = 1 << 0, // Whether this surface is solid, as far as the Physics Engine is concerned
+ };
+
+ char texture[MAX_PATH_LENGTH]; // NULL-Terminated string containing texture name
+ int flags; // Combination of Surface Flags
+ unsigned short numVertices;
+ unsigned short numTriangles;
+ Vertex* vertices; // array with numVertices elements
+ Triangle* triangles; // array with numTriangles elements
+ };
+
+ struct Entity
+ {
+ std::string classname;
+ std::map<std::string, std::string> properties;
+ };
+
+ Header header;
+ Surface* surfaces; //array with header.numSurfaces elements
+ std::list<Entity> entities; //the rest of the file are entities in plain text - { "key" "value" "key" "value" ... } { "key" "value" ... } ...
+
+ const bool loadFromFile(const std::string& filename);
+
+ LevelFile();
+ ~LevelFile();
+};
+
+#endif
View
227 src/LevelFormat.cpp
@@ -0,0 +1,227 @@
+#include <LevelFormat.hpp>
+#include <fstream>
+#include <iostream>
+#include <algorithm>
+
+// "HFPSLVL\0"
+const char LevelFile::IDENT[8] = { 'H', 'F', 'P', 'S', 'L', 'V', 'L', '\0' };
+
+namespace
+{
+ template<typename T, std::size_t len> const std::size_t getArrayLength(const T(&)[len])
+ {
+ return len;
+ }
+}
+
+LevelFile::LevelFile():
+ surfaces(NULL)
+{
+}
+
+LevelFile::~LevelFile()
+{
+ delete[] surfaces;
+}
+
+const bool LevelFile::loadFromFile(const std::string& filename)
+{
+ // Open for binary reading
+ std::ifstream file(filename.c_str(), std::ios::binary|std::ios::in);
+ if(file.fail())
+ {
+ std::cerr << "Could not open " << filename << "!" << std::endl;
+ return false;
+ }
+
+ // Read header
+ file.read(reinterpret_cast<char*>(&header), sizeof(Header));
+ if(file.fail())
+ {
+ std::cerr << filename << " is no valid level file, contains no header!" << std::endl;
+ return false;
+ }
+ for(std::size_t i = 0; i < getArrayLength(header.ident); ++i)
+ {
+ if(header.ident[i] != IDENT[i])
+ {
+ std::cerr << filename << " is no valid level file, ident does not match!" << std::endl;
+ return false;
+ }
+ }
+ if(header.version != VERSION)
+ {
+ std::cerr << "level file " << filename << " has the wrong version (" << header.version << " should be " << VERSION << ")" << std::endl;
+ return false;
+ }
+
+ // Read surfaces
+ surfaces = new Surface[header.numSurfaces];
+ for(Surface* curSurf = surfaces; curSurf < surfaces + header.numSurfaces; ++curSurf)
+ {
+ file.read(reinterpret_cast<char*>(curSurf), sizeof(Surface) - sizeof(curSurf->vertices) - sizeof(curSurf->triangles));
+ if(file.fail())
+ {
+ delete[] surfaces;
+ std::cerr << "Error reading from level file " << filename << "!" << std::endl;
+ return false;
+ }
+ curSurf->vertices = new Vertex[curSurf->numVertices];
+ curSurf->triangles = new Triangle[curSurf->numTriangles];
+ file.read(reinterpret_cast<char*>(&curSurf->vertices), curSurf->numVertices * sizeof(Vertex));
+ file.read(reinterpret_cast<char*>(&curSurf->triangles), curSurf->numTriangles * sizeof(Triangle));
+ if(file.fail())
+ {
+ delete[] surfaces;
+ std::cerr << "Error reading from level file " << filename << "!" << std::endl;
+ return false;
+ }
+ }
+
+ // Read entities (unknown number)
+ enum State
+ {
+ sStart,
+ sInEntity,
+ sInKey,
+ sPastKey,
+ sInValue
+ };
+ State curState = sStart;
+ Entity curEntity;
+ std::string curKey;
+ std::string curValue;
+ while(true)
+ {
+ char c;
+ file.get(c);
+ // could not read?
+ if(file.fail())
+ {
+ if(file.eof() && curState == sStart)
+ {
+ return true;
+ }
+ else
+ {
+ std::cerr << "Error reading entities from level file " << filename << "!" << std::endl;
+ return false;
+ }
+ }
+ switch(curState)
+ {
+ case sStart:
+ switch(c)
+ {
+ case ' ':
+ case '\t':
+ case '\n':
+ // ignore whitespaces
+ break;
+ case '{':
+ curState = sInEntity;
+ break;
+ default:
+ std::cerr << "Invalid Syntax in Entity part of level file " << filename << "!" << std::endl;
+ return false;
+ }
+ break;
+
+ case sInEntity:
+ switch(c)
+ {
+ case ' ':
+ case '\t':
+ case '\n':
+ //ignore whitespaces
+ break;
+ case '"':
+ curState = sInKey;
+ curKey.clear();
+ break;
+ case '}':
+ if(curEntity.classname.empty())
+ {
+ std::cout << "Warning: Entity without classname in level file " << filename << "!" << std::endl;
+ }
+ else
+ {
+ entities.push_back(curEntity);
+ }
+ curState = sStart;
+ break;
+ default:
+ std::cerr << "Invalid Syntax in Entity part of level file " << filename << "!" << std::endl;
+ return false;
+ }
+ break;
+
+ case sInKey:
+ switch(c)
+ {
+ case '"':
+ curState = sPastKey;
+ break;
+ default:
+ curKey += c;
+ break;
+ }
+ break;
+
+ case sPastKey:
+ switch(c)
+ {
+ case ' ':
+ case '\t':
+ case '\n':
+ //ignore whitespaces
+ break;
+ case '"':
+ curState = sInValue;
+ curValue.clear();
+ break;
+ default:
+ std::cerr << "Invalid Syntax in Entity part of level file " << filename << "!" << std::endl;
+ return false;
+ }
+ break;
+
+ case sInValue:
+ switch(c)
+ {
+ case '"':
+ curState = sInEntity;
+ std::transform(curKey.begin(), curKey.end(), curKey.begin(), tolower);
+ if(curKey == "classname")
+ {
+ if(curEntity.classname.empty())
+ {
+ curEntity.classname = curValue;
+ }
+ else
+ {
+ std::cout<<"Warning: duplicate key \"classname\" in Entity in level file " << filename << "!" << std::endl;
+ }
+ }
+ else
+ {
+ if(curEntity.properties.find(curKey) == curEntity.properties.end())
+ {
+ curEntity.properties[curKey] = curValue;
+ }
+ else
+ {
+ std::cout<<"Warning: duplicate key \"" << curKey << "\" in Entity in level file " << filename << "!" << std::endl;
+ }
+ }
+ break;
+ default:
+ curValue += c;
+ break;
+ }
+ break;
+ }
+ std::string buf;
+
+ }
+}
View
2 src/main.cpp
@@ -4,6 +4,8 @@
#include <iostream>
#include <cassert>
+#include <LevelFormat.hpp> //temporary, to see if it compiles
+
// Making sure Sixense exits properly, even when exceptions happen. (Not sure about exit() though...)
class SixenseAutoExit
{

0 comments on commit a838f06

Please sign in to comment.
Something went wrong with that request. Please try again.