celestia/src/celengine/cmdparser.cpp

418 lines
12 KiB
C++

// cmdparser.cpp
//
// Parse Celestia command files and turn them into CommandSequences.
//
// Copyright (C) 2001 Chris Laurel <claurel@shatters.net>
//
// This program 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.
#include <algorithm>
#include "celestia.h"
// Ugh . . . the C++ standard says that stringstream should be in
// sstream, but the GNU C++ compiler uses strstream instead.
#ifdef HAVE_SSTREAM
#include <sstream>
#else
#include <strstream>
#endif // HAVE_SSTREAM
#include "celutil/util.h"
#include "celutil/debug.h"
#include "celmath/mathlib.h"
#include "astro.h"
#include "cmdparser.h"
using namespace std;
static int parseRenderFlags(string);
static int parseLabelFlags(string);
CommandParser::CommandParser(istream& in)
{
tokenizer = new Tokenizer(&in);
parser = new Parser(tokenizer);
}
CommandParser::CommandParser(Tokenizer& tok)
{
tokenizer = &tok;
parser = new Parser(tokenizer);
}
CommandParser::~CommandParser()
{
delete parser;
delete tokenizer;
}
CommandSequence* CommandParser::parse()
{
CommandSequence* seq = new CommandSequence();
if (tokenizer->nextToken() != Tokenizer::TokenBeginGroup)
{
error("'{' expected at start of script.");
delete seq;
return NULL;
}
Tokenizer::TokenType ttype = tokenizer->nextToken();
while (ttype != Tokenizer::TokenEnd && ttype != Tokenizer::TokenEndGroup)
{
tokenizer->pushBack();
Command* cmd = parseCommand();
if (cmd == NULL)
{
for (CommandSequence::const_iterator iter = seq->begin();
iter != seq->end();
iter++)
{
delete *iter;
}
delete seq;
return NULL;
}
else
{
seq->insert(seq->end(), cmd);
}
ttype = tokenizer->nextToken();
}
if (ttype != Tokenizer::TokenEndGroup)
{
error("Missing '}' at end of script.");
for_each(seq->begin(), seq->end(), deleteFunc<Command*>());;
delete seq;
return NULL;
}
return seq;
}
const vector<string>* CommandParser::getErrors() const
{
return &errorList;
}
void CommandParser::error(const string errMsg)
{
errorList.insert(errorList.end(), errMsg);
}
Command* CommandParser::parseCommand()
{
if (tokenizer->nextToken() != Tokenizer::TokenName)
{
error("Invalid command name");
return NULL;
}
string commandName = tokenizer->getStringValue();
Value* paramListValue = parser->readValue();
if (paramListValue == NULL || paramListValue->getType() != Value::HashType)
{
error("Bad parameter list");
return NULL;
}
Hash* paramList = paramListValue->getHash();
Command* cmd = NULL;
if (commandName == "wait")
{
double duration = 1.0;
paramList->getNumber("duration", duration);
cmd = new CommandWait(duration);
}
else if (commandName == "select")
{
string object;
paramList->getString("object", object);
cmd = new CommandSelect(object);
}
else if (commandName == "goto")
{
double t = 1.0;
paramList->getNumber("time", t);
double distance = 5.0;
paramList->getNumber("distance", distance);
astro::CoordinateSystem upFrame = astro::ObserverLocal;
string frameString;
if (paramList->getString("upframe", frameString))
{
if (compareIgnoringCase(frameString, "observer") == 0)
upFrame = astro::ObserverLocal;
else if (compareIgnoringCase(frameString, "geographic") == 0)
upFrame = astro::Geographic;
else if (compareIgnoringCase(frameString, "equatorial") == 0)
upFrame = astro::Equatorial;
else if (compareIgnoringCase(frameString, "ecliptical") == 0)
upFrame = astro::Ecliptical;
else if (compareIgnoringCase(frameString, "universal") == 0)
upFrame = astro::Universal;
}
Vec3d up(0, 1, 0);
paramList->getVector("up", up);
cmd = new CommandGoto(t,
distance,
Vec3f((float) up.x, (float) up.y, (float) up.z),
upFrame);
}
else if (commandName == "gotolonglat")
{
double t = 1.0;
paramList->getNumber("time", t);
double distance = 5.0;
paramList->getNumber("distance", distance);
Vec3d up(0, 1, 0);
paramList->getVector("up", up);
double longitude;
paramList->getNumber("longitude", longitude);
double latitude;
paramList->getNumber("latitude", latitude);
cmd = new CommandGotoLongLat(t,
distance,
(float) degToRad(longitude),
(float) degToRad(latitude),
Vec3f((float) up.x, (float) up.y, (float) up.z));
}
else if (commandName == "center")
{
double t = 1.0;
paramList->getNumber("time", t);
cmd = new CommandCenter(t);
}
else if (commandName == "follow")
{
cmd = new CommandFollow();
}
else if (commandName == "synchronous")
{
cmd = new CommandSynchronous();
}
else if (commandName == "cancel")
{
cmd = new CommandCancel();
}
else if (commandName == "print")
{
string text;
paramList->getString("text", text);
cmd = new CommandPrint(text);
}
else if (commandName == "cls")
{
cmd = new CommandClearScreen();
}
else if (commandName == "time")
{
double jd = 2451545;
paramList->getNumber("jd", jd);
cmd = new CommandSetTime(jd);
}
else if (commandName == "timerate")
{
double rate = 1.0;
paramList->getNumber("rate", rate);
cmd = new CommandSetTimeRate(rate);
}
else if (commandName == "changedistance")
{
double rate = 0.0;
double duration = 1.0;
paramList->getNumber("rate", rate);
paramList->getNumber("duration", duration);
cmd = new CommandChangeDistance(duration, rate);
}
else if (commandName == "orbit")
{
double rate = 0.0;
double duration = 1.0;
Vec3d axis;
paramList->getNumber("duration", duration);
paramList->getNumber("rate", rate);
paramList->getVector("axis", axis);
cmd = new CommandOrbit(duration,
Vec3f((float) axis.x, (float) axis.y, (float) axis.z),
(float) degToRad(rate));
}
else if (commandName == "rotate")
{
double rate = 0.0;
double duration = 1.0;
Vec3d axis;
paramList->getNumber("duration", duration);
paramList->getNumber("rate", rate);
paramList->getVector("axis", axis);
cmd = new CommandRotate(duration,
Vec3f((float) axis.x, (float) axis.y, (float) axis.z),
(float) degToRad(rate));
}
else if (commandName == "move")
{
Vec3d velocity;
double duration;
paramList->getNumber("duration", duration);
paramList->getVector("velocity", velocity);
cmd = new CommandMove(duration, velocity * astro::kilometersToLightYears(1.0));
}
else if (commandName == "setposition")
{
Vec3d base, offset;
paramList->getVector("base", base);
paramList->getVector("offset", offset);
cmd = new CommandSetPosition(astro::universalPosition(Point3d(offset.x, offset.y, offset.z),
Point3f((float) base.x, (float) base.y, (float) base.z)));
}
else if (commandName == "setorientation")
{
Vec3d axis;
double angle;
paramList->getNumber("angle", angle);
paramList->getVector("axis", axis);
cmd = new CommandSetOrientation(Vec3f((float) axis.x, (float) axis.y, (float) axis.z),
(float) degToRad(angle));
}
else if (commandName == "renderflags")
{
int setFlags = 0;
int clearFlags = 0;
string s;
if (paramList->getString("set", s))
setFlags = parseRenderFlags(s);
if (paramList->getString("clear", s))
clearFlags = parseRenderFlags(s);
cmd = new CommandRenderFlags(setFlags, clearFlags);
}
else if (commandName == "labels")
{
int setFlags = 0;
int clearFlags = 0;
string s;
if (paramList->getString("set", s))
setFlags = parseLabelFlags(s);
if (paramList->getString("clear", s))
clearFlags = parseLabelFlags(s);
cmd = new CommandLabels(setFlags, clearFlags);
}
else if (commandName == "setvisibilitylimit")
{
double mag = 6.0;
paramList->getNumber("magnitude", mag);
cmd = new CommandSetVisibilityLimit(mag);
}
else
{
error("Unknown command name '" + commandName + "'");
cmd = NULL;
}
delete paramListValue;
return cmd;
}
int parseRenderFlags(string s)
{
#ifdef HAVE_SSTREAM
istringstream in(s);
#else
istrstream in(s.c_str());
#endif
Tokenizer tokenizer(&in);
int flags = 0;
Tokenizer::TokenType ttype = tokenizer.nextToken();
while (ttype != Tokenizer::TokenEnd)
{
if (ttype == Tokenizer::TokenName)
{
string name = tokenizer.getNameValue();
if (compareIgnoringCase(name, "orbits") == 0)
flags |= Renderer::ShowOrbits;
else if (compareIgnoringCase(name, "cloudmaps") == 0)
flags |= Renderer::ShowCloudMaps;
else if (compareIgnoringCase(name, "constellations") == 0)
flags |= Renderer::ShowDiagrams;
else if (compareIgnoringCase(name, "galaxies") == 0)
flags |= Renderer::ShowGalaxies;
else if (compareIgnoringCase(name, "planets") == 0)
flags |= Renderer::ShowPlanets;
else if (compareIgnoringCase(name, "stars") == 0)
flags |= Renderer::ShowStars;
ttype = tokenizer.nextToken();
if (ttype == Tokenizer::TokenBar)
ttype = tokenizer.nextToken();
}
else
{
DPRINTF("Error parsing render flags\n");
return 0;
}
}
return flags;
}
int parseLabelFlags(string s)
{
#ifdef HAVE_SSTREAM
istringstream in(s);
#else
istrstream in(s.c_str());
#endif
Tokenizer tokenizer(&in);
int flags = 0;
Tokenizer::TokenType ttype = tokenizer.nextToken();
while (ttype != Tokenizer::TokenEnd)
{
if (ttype == Tokenizer::TokenName)
{
string name = tokenizer.getNameValue();
if (compareIgnoringCase(name, "planets") == 0)
flags |= Renderer::MajorPlanetLabels;
else if (compareIgnoringCase(name, "minorplanets") == 0)
flags |= Renderer::MinorPlanetLabels;
else if (compareIgnoringCase(name, "constellations") == 0)
flags |= Renderer::ConstellationLabels;
else if (compareIgnoringCase(name, "stars") == 0)
flags |= Renderer::StarLabels;
ttype = tokenizer.nextToken();
if (ttype == Tokenizer::TokenBar)
ttype = tokenizer.nextToken();
}
else
{
DPRINTF("Error parsing label flags\n");
return 0;
}
}
return flags;
}