1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
|
#include "Quake3MapReader.h"
#include "itextstream.h"
#include "ieclass.h"
#include "igame.h"
#include "ientity.h"
#include "string/string.h"
#include "i18n.h"
#include <fmt/format.h>
#include "primitiveparsers/BrushDef.h"
#include "primitiveparsers/BrushDef3.h"
#include "primitiveparsers/PatchDef2.h"
#include "primitiveparsers/PatchDef3.h"
namespace map {
Quake3MapReader::Quake3MapReader(IMapImportFilter& importFilter) :
_importFilter(importFilter),
_entityCount(0),
_primitiveCount(0)
{}
void Quake3MapReader::readFromStream(std::istream& stream)
{
// Call the virtual method to initialise the primitve parser map (if not done yet)
initPrimitiveParsers();
// The tokeniser used to split the stream into pieces
parser::BasicDefTokeniser<std::istream> tok(stream);
// Read each entity in the map, until EOF is reached
while (tok.hasMoreTokens())
{
// Create an entity node by parsing from the stream. If there is an
// exception, display it and return
try
{
parseEntity(tok);
}
catch (FailureException& e)
{
std::string text = fmt::format(_("Failed parsing entity {0:d}:\n{1}"), _entityCount, e.what());
// Re-throw with more text
throw FailureException(text);
}
_entityCount++;
}
// EOF reached, success
}
void Quake3MapReader::initPrimitiveParsers()
{
if (_primitiveParsers.empty())
{
addPrimitiveParser(std::make_shared<BrushDefParser>());
addPrimitiveParser(std::make_shared<PatchDef2ParserQ3>());
addPrimitiveParser(std::make_shared<LegacyBrushDefParser>());
}
}
void Quake3MapReader::addPrimitiveParser(const PrimitiveParserPtr& parser)
{
_primitiveParsers.insert(PrimitiveParsers::value_type(parser->getKeyword(), parser));
}
void Quake3MapReader::parsePrimitive(parser::DefTokeniser& tok, const scene::INodePtr& parentEntity)
{
_primitiveCount++;
std::string primitiveKeyword = tok.peek();
// Get a parser for this keyword
PrimitiveParsers::const_iterator p = _primitiveParsers.find(primitiveKeyword);
if (p == _primitiveParsers.end())
{
throw FailureException("Unknown primitive type: " + primitiveKeyword);
}
const PrimitiveParserPtr& parser = p->second;
// All primitive formats except for the legacy brushDef format have a proper keyword
// which should be parsed away before reading in the tokens.
if (primitiveKeyword != "(")
{
tok.nextToken();
}
// Try to parse the primitive, throwing exception if failed
try
{
scene::INodePtr primitive = parser->parse(tok);
if (!primitive)
{
std::string text = fmt::format(_("Primitive #{0:d}: parse error"), _primitiveCount);
throw FailureException(text);
}
// Now add the primitive as a child of the entity
_importFilter.addPrimitiveToEntity(primitive, parentEntity);
}
catch (parser::ParseException& e)
{
// Translate ParseExceptions to FailureExceptions
std::string text = fmt::format(_("Primitive #{0:d}: parse exception {1}"), _primitiveCount, e.what());
throw FailureException(text);
}
}
scene::INodePtr Quake3MapReader::createEntity(const EntityKeyValues& keyValues)
{
// Get the classname from the EntityKeyValues
EntityKeyValues::const_iterator found = keyValues.find("classname");
if (found == keyValues.end())
{
throw FailureException("Quake3MapReader::createEntity(): could not find classname.");
}
// Otherwise create the entity and add all of the properties
std::string className = found->second;
IEntityClassPtr classPtr = GlobalEntityClassManager().findClass(className);
if (classPtr == NULL)
{
rError() << "[mapdoom3]: Could not find entity class: "
<< className << std::endl;
// greebo: EntityClass not found, insert a brush-based one
classPtr = GlobalEntityClassManager().findOrInsert(className, true);
}
// Create the actual entity node
IEntityNodePtr node(GlobalEntityModule().createEntity(classPtr));
for (EntityKeyValues::const_iterator i = keyValues.begin();
i != keyValues.end();
++i)
{
node->getEntity().setKeyValue(i->first, i->second);
}
return node;
}
void Quake3MapReader::parseEntity(parser::DefTokeniser& tok)
{
// Map of keyvalues for this entity
EntityKeyValues keyValues;
// The actual entity. This is initially null, and will be created when
// primitives start or the end of the entity is reached
scene::INodePtr entity;
// Start parsing, first token must be an open brace
tok.assertNextToken("{");
std::string token = tok.nextToken();
// Reset the primitive counter, we're starting a new entity
_primitiveCount = 0;
while (true)
{
// Token must be either a key, a "{" to indicate the start of a
// primitive, or a "}" to indicate the end of the entity
if (token == "{") // PRIMITIVE
{
// Create the entity right now, if not yet done
if (entity == NULL)
{
entity = createEntity(keyValues);
}
// Parse the primitive block, and pass the parent entity
parsePrimitive(tok, entity);
}
else if (token == "}") // END OF ENTITY
{
// Create the entity if necessary and return it
if (entity == NULL)
{
entity = createEntity(keyValues);
}
break;
}
else // KEY
{
std::string value = tok.nextToken();
// Sanity check (invalid number of tokens will get us out of sync)
if (value == "{" || value == "}")
{
std::string text = fmt::format(_("Parsed invalid value '{0}' for key '{1}'"), value, token);
throw FailureException(text);
}
// Otherwise add the keyvalue pair to our map
keyValues.insert(EntityKeyValues::value_type(token, value));
}
// Get the next token
token = tok.nextToken();
}
// Insert the entity
_importFilter.addEntity(entity);
}
} // namespace map
|