Merge branch 'master' of git://github.com/korslund/openmw

Conflicts:
	CMakeLists.txt
	apps/openmw/main.cpp
	game/engine.cpp
actorid
Ardekantur 15 years ago
commit ee6c933156

7
.gitignore vendored

@ -1,5 +1,6 @@
screenshot*.png
*.o
*.a
*~
data
CMakeFiles
@ -13,4 +14,8 @@ build*
plugins.cfg
openmw.cfg
Doxygen
.thumbnails
resources
mwcompiler
mwinterpreter
clientconsole

4
.gitmodules vendored

@ -1,3 +1,3 @@
[submodule "mangle"]
path = mangle
[submodule "libs/mangle"]
path = libs/mangle
url = git://github.com/korslund/mangle.git

@ -6,62 +6,176 @@ cmake_minimum_required(VERSION 2.6)
# Add path for CMake scripts
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/)
# Local files
# source directory: apps
set(BSA bsa/bsa_archive.cpp bsa/bsa_file.cpp)
set(BSA_HEADER bsa/bsa_archive.hpp bsa/bsa_file.hpp)
source_group(bsa FILES ${BSA} ${BSA_HEADER})
set(GAME
apps/openmw/main.cpp
apps/openmw/engine.cpp)
set(GAME_HEADER
apps/openmw/engine.hpp)
source_group(game FILES ${GAME} ${GAME_HEADER})
set(GAMEREND
apps/openmw/mwrender/mwscene.cpp
apps/openmw/mwrender/cellimp.cpp
apps/openmw/mwrender/interior.cpp
apps/openmw/mwrender/sky.cpp)
set(GAMEREND_HEADER
apps/openmw/mwrender/cell.hpp
apps/openmw/mwrender/cellimp.hpp
apps/openmw/mwrender/mwscene.hpp
apps/openmw/mwrender/interior.hpp
apps/openmw/mwrender/playerpos.hpp
apps/openmw/mwrender/sky.hpp)
source_group(apps\\openmw\\mwrender FILES ${GAMEREND} ${GAMEREND_HEADER})
set(NIF nif/nif_file.cpp)
set(NIF_HEADER nif/controlled.hpp nif/effect.hpp nif/nif_types.hpp nif/record.hpp
nif/controller.hpp nif/extra.hpp nif/node.hpp nif/record_ptr.hpp
nif/data.hpp nif/nif_file.hpp nif/property.hpp)
source_group(nif FILES ${NIF} ${NIF_HEADER})
# set(GAMEINPUT)
set(GAMEINPUT_HEADER
apps/openmw/mwinput/inputmanager.hpp)
source_group(apps\\openmw\\mwinput FILES ${GAMEINPUT} ${GAMEINPUT_HEADER})
set(NIFOGRE nifogre/ogre_nif_loader.cpp)
set(NIFOGRE_HEADER nifogre/ogre_nif_loader.hpp)
source_group(nifogre FILES ${NIFOGRE} ${NIFOGRE_HEADER})
set(GAMESCRIPT
apps/openmw/mwscript/scriptmanager.cpp
apps/openmw/mwscript/compilercontext.cpp
apps/openmw/mwscript/interpretercontext.cpp
apps/openmw/mwscript/cellextensions.cpp
apps/openmw/mwscript/extensions.cpp
apps/openmw/mwscript/globalscripts.cpp
)
set(GAMESCRIPT_HEADER
apps/openmw/mwscript/locals.hpp
apps/openmw/mwscript/scriptmanager.hpp
apps/openmw/mwscript/compilercontext.hpp
apps/openmw/mwscript/interpretercontext.hpp
apps/openmw/mwscript/cellextensions.hpp
apps/openmw/mwscript/extensions.hpp
apps/openmw/mwscript/globalscripts.hpp
)
source_group(apps\\openmw\\mwscript FILES ${GAMESCRIPT} ${GAMESCRIPT_HEADER})
set(TOOLS tools/stringops.cpp tools/fileops.cpp)
set(TOOLS_HEADER tools/fileops.hpp tools/slice_array.hpp tools/stringops.hpp)
source_group(tools FILES ${TOOLS} ${TOOLS_HEADER})
set(GAMESOUND
apps/openmw/mwsound/soundmanager.cpp
apps/openmw/mwsound/extensions.cpp)
set(GAMESOUND_HEADER
apps/openmw/mwsound/soundmanager.hpp
apps/openmw/mwsound/extensions.hpp)
source_group(apps\\openmw\\mwsound FILES ${GAMESOUND} ${GAMESOUND_HEADER})
set(MANGLE_VFS mangle/vfs/servers/ogre_vfs.cpp)
source_group(mangle_vfs FILES ${MANGLE_VFS})
set(GAMEWORLD
apps/openmw/mwworld/world.cpp)
set(GAMEWORLD_HEADER
apps/openmw/mwworld/refdata.hpp
apps/openmw/mwworld/world.hpp
apps/openmw/mwworld/ptr.hpp
apps/openmw/mwworld/environment.hpp
)
source_group(apps\\openmw\\mwworld FILES ${GAMEWORLD} ${GAMEWORLD_HEADER})
set(OGRE ogre/renderer.cpp)
set(OGRE_HEADER ogre/renderer.hpp)
source_group(ogre FILES ${OGRE} ${OGRE_HEADER})
set(INPUT input/oismanager.cpp)
set(INPUT_HEADER input/oismanager.hpp input/listener.hpp input/func_binder.hpp input/dispatch_map.hpp input/dispatcher.hpp input/poller.hpp)
source_group(input FILES ${INPUT} ${INPUT_HEADER})
set(APPS ${GAME} ${GAMEREND} ${GAMEINPUT} ${GAMESCRIPT} ${GAMESOUND} ${GAMEWORLD})
set(APPS_HEADER ${GAME_HEADER} ${GAMEREND_HEADER} ${GAMEINPUT_HEADER} ${GAMESCRIPT_HEADER}
${GAMESOUND_HEADER} ${GAMEWORLD_HEADER})
set(GAME game/main.cpp game/engine.cpp)
set(GAME_HEADER game/mwinput/inputmanager.hpp game/engine.hpp)
source_group(game FILES ${GAME} ${GAME_HEADER})
# source directory: components
set(BSA
components/bsa/bsa_archive.cpp
components/bsa/bsa_file.cpp)
set(BSA_HEADER
components/bsa/bsa_archive.hpp
components/bsa/bsa_file.hpp)
source_group(components\\bsa FILES ${BSA} ${BSA_HEADER})
set(NIF
components/nif/nif_file.cpp)
set(NIF_HEADER
components/nif/controlled.hpp
components/nif/effect.hpp
components/nif/nif_types.hpp
components/nif/record.hpp
components/nif/controller.hpp
components/nif/extra.hpp
components/nif/node.hpp
components/nif/record_ptr.hpp
components/nif/data.hpp
components/nif/nif_file.hpp
components/nif/property.hpp)
source_group(components\\nif FILES ${NIF} ${NIF_HEADER})
set(NIFOGRE
components/nifogre/ogre_nif_loader.cpp)
set(NIFOGRE_HEADER
components/nifogre/ogre_nif_loader.hpp)
source_group(components\\nifogre FILES ${NIFOGRE} ${NIFOGRE_HEADER})
set(ESM_STORE
components/esm_store/store.cpp)
set(ESM_STORE_HEADER
components/esm_store/cell_store.hpp
components/esm_store/reclists.hpp
components/esm_store/store.hpp)
source_group(components\\esm_store FILES ${ESM_STORE} ${ESM_STORE_HEADER})
file(GLOB ESM_HEADER components/esm/*.hpp)
source_group(components\\esm FILES ${ESM_HEADER})
set(OGRE
components/engine/ogre/renderer.cpp)
set(OGRE_HEADER
components/engine/ogre/renderer.hpp)
source_group(components\\engine\\ogre FILES ${OGRE} ${OGRE_HEADER})
set(ESM_STORE esm_store/store.cpp esm_store/cell_store.cpp)
set(ESM_STORE_HEADER esm_store/cell_store.hpp esm_store/reclists.hpp esm_store/store.hpp)
source_group(esm_store FILES ${ESM_STORE} ${ESM_STORE_HEADER})
set(GAMEREND game/mwrender/mwscene.cpp game/mwrender/cell.cpp game/mwrender/interior.cpp)
set(GAMEREND_HEADER game/mwrender/cell.hpp game/mwrender/mwscene.hpp
game/mwrender/interior.hpp game/mwrender/playerpos.hpp)
source_group(game_renderer FILES ${GAMEREND} ${GAMEREND_HEADER})
set(ESM_HEADER esm/defs.hpp esm/loadcell.hpp esm/loadfact.hpp esm/loadltex.hpp
esm/loadskil.hpp
esm/esm_reader.hpp esm/loadclas.hpp esm/loadglob.hpp esm/loadmgef.hpp esm/loadsndg.hpp
esm/loadacti.hpp esm/loadclot.hpp esm/loadgmst.hpp esm/loadmisc.hpp esm/loadsoun.hpp
esm/loadalch.hpp esm/loadcont.hpp esm/loadinfo.hpp esm/loadnpcc.hpp esm/loadspel.hpp
esm/loadappa.hpp esm/loadcrea.hpp esm/loadingr.hpp esm/loadnpc.hpp esm/loadsscr.hpp
esm/loadarmo.hpp esm/loadcrec.hpp esm/loadland.hpp esm/loadpgrd.hpp esm/loadstat.hpp
esm/loadbody.hpp esm/loaddial.hpp esm/loadlevlist.hpp esm/loadrace.hpp esm/loadweap.hpp
esm/loadbook.hpp esm/loaddoor.hpp esm/loadligh.hpp esm/loadregn.hpp esm/records.hpp
esm/loadbsgn.hpp esm/loadench.hpp esm/loadlocks.hpp esm/loadscpt.hpp)
source_group(esm_header FILES ${ESM_HEADER})
set(INPUT
components/engine/input/oismanager.cpp)
set(INPUT_HEADER
components/engine/input/oismanager.hpp
components/engine/input/listener.hpp
components/engine/input/func_binder.hpp
components/engine/input/dispatch_map.hpp
components/engine/input/dispatcher.hpp
components/engine/input/poller.hpp)
source_group(components\\engine\\input FILES ${INPUT} ${INPUT_HEADER})
set(COMMANDSERVER
components/commandserver/command.hpp
components/commandserver/server.hpp
components/commandserver/server.cpp)
source_group(components\\commandserver FILES ${COMMANDSERVER})
set(MISC
components/misc/stringops.cpp
components/misc/fileops.cpp)
set(MISC_HEADER
components/misc/fileops.hpp
components/misc/slice_array.hpp
components/misc/stringops.hpp
components/misc/tsdeque.hpp)
source_group(components\\misc FILES ${MISC} ${MISC_HEADER})
file(GLOB COMPILER components/compiler/*.cpp)
file(GLOB COMPILER_HEADER components/compiler/*.hpp)
source_group(components\\compiler FILES ${COMPILER} ${COMPILER_HEADER})
file(GLOB INTERPRETER components/interpreter/*.cpp)
file(GLOB INTERPRETER_HEADER components/interpreter/*.hpp)
source_group(components\\interpreter FILES ${INTERPRETER} ${INTERPRETER_HEADER})
set(COMPONENTS ${BSA} ${NIF} ${NIFOGRE} ${ESM_STORE} ${OGRE} ${INPUT} ${MISC}
${COMMANDSERVER}
${COMPILER}
${INTERPRETER})
set(COMPONENTS_HEADER ${BSA_HEADER} ${NIF_HEADER} ${NIFOGRE_HEADER} ${ESM_STORE_HEADER}
${ESM_HEADER} ${OGRE_HEADER} ${INPUT_HEADER} ${MISC_HEADER} ${COMPILER_HEADER}
${INTERPRETER_HEADER})
# source directory: libs
set(MANGLE_VFS libs/mangle/vfs/servers/ogre_vfs.cpp)
source_group(libs\\mangle_vfs FILES ${MANGLE_VFS})
set(OPENMW_LIBS ${MANGLE_VFS})
set(OPENMW_LIBS_HEADER)
# Platform specific
if (WIN32)
set(PLATFORM_INCLUDE_DIR "platform")
@ -76,9 +190,12 @@ find_package(Boost REQUIRED COMPONENTS system filesystem program_options)
find_package(OIS REQUIRED)
include_directories("."
${OGRE_INCLUDE_DIR} ${OIS_INCLUDE_DIR} ${Boost_INCLUDE_DIR}
${PLATFORM_INCLUDE_DIR})
${PLATFORM_INCLUDE_DIR}
${CMAKE_HOME_DIRECTORY}/extern/caelum/include)
link_directories(${Boost_LIBRARY_DIRS} ${OGRE_LIB_DIR})
add_subdirectory( extern/caelum )
# Specify build paths
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${OpenMW_BINARY_DIR}")
@ -103,6 +220,11 @@ if (APPLE)
)
endif (APPLE)
# Compiler settings
if (CMAKE_COMPILER_IS_GNUCC)
add_definitions (-Wall)
endif (CMAKE_COMPILER_IS_GNUCC)
# Main executable
add_executable(openmw
${BSA} ${BSA_HEADER}
@ -115,6 +237,10 @@ add_executable(openmw
${GAME}
${ESM_STORE} ${ESM_STORE_HEADER}
${GAMEREND} ${GAMEREND_HEADER}
MACOSX_BUNDLE
${COMPONENTS} ${COMPONENTS_HEADER}
${OPENMW_LIBS} ${OPENMW_LIBS_HEADER}
${APPS} ${APPS_HEADER}
${ESM_HEADER}
${APPLE_BUNDLE_RESOURCES}
)
@ -122,13 +248,17 @@ add_executable(openmw
target_link_libraries(openmw
${OGRE_LIBRARIES}
${OIS_LIBRARIES}
${Boost_LIBRARIES})
${Boost_LIBRARIES}
caelum)
if (APPLE)
find_library(CARBON_FRAMEWORK Carbon)
target_link_libraries(openmw ${CARBON_FRAMEWORK})
endif (APPLE)
# Other apps and tools
add_subdirectory( apps/clientconsole )
# Apple bundling
if (APPLE)
set(MISC_FILES
@ -155,3 +285,14 @@ set(CMAKE_EXE_LINKER_FLAGS "-arch i386")
set(CMAKE_CXX_FLAGS "-arch i386")
endif (APPLE)
# Tools
option(BUILD_MWCOMPILER "build standalone Morrowind script compiler" ON)
if (BUILD_MWCOMPILER)
add_subdirectory( apps/mwcompiler )
endif()
option(BUILD_MWINTERPRETER "build standalone Morrowind script code interpreter" ON)
if (BUILD_MWINTERPRETER)
add_subdirectory( apps/mwinterpreter )
endif()

@ -573,7 +573,10 @@ WARN_LOGFILE =
# directories like "/usr/src/myproject". Separate the files or directories
# with spaces.
INPUT = ..
INPUT = ..\apps
..\components
..\libs
..\docs
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is

@ -0,0 +1,5 @@
project(clientconsole)
find_package(Boost REQUIRED COMPONENTS system)
link_directories(${Boost_LIBRARY_DIRS})
add_executable(clientconsole client.cpp)
target_link_libraries(clientconsole ${Boost_LIBRARIES})

@ -0,0 +1,142 @@
#include <iostream>
#include <boost/array.hpp>
#include <boost/asio.hpp>
#include <libs/mangle/tools/str_exception.hpp>
using boost::asio::ip::tcp;
#ifdef WIN32
#pragma warning( disable : 4966 )
#endif
class Client
{
protected:
struct Header
{
char magic[4];
boost::uint32_t dataLength;
};
boost::asio::io_service mIOService;
tcp::socket* mpSocket;
public:
bool connect(const char* port)
{
tcp::resolver resolver(mIOService);
tcp::resolver::query query("localhost", port);
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
tcp::resolver::iterator end;
mpSocket = new tcp::socket(mIOService);
boost::system::error_code error = boost::asio::error::host_not_found;
while (error && endpoint_iterator != end)
{
mpSocket->close();
mpSocket->connect(*endpoint_iterator++, error);
}
return (error) ? false : true;
}
void disconnect()
{
mpSocket->close();
mIOService.stop();
}
bool send (const char* msg)
{
const size_t slen = strlen(msg);
const size_t plen = sizeof(Header) + slen + 1;
std::vector<char> packet(plen);
Header* pHeader = reinterpret_cast<Header*>(&packet[0]);
strncpy(pHeader->magic, "OMW0", 4);
pHeader->dataLength = slen + 1; // Include the null terminator
strncpy(&packet[8], msg, pHeader->dataLength);
boost::system::error_code ec;
boost::asio::write(*mpSocket, boost::asio::buffer(packet),
boost::asio::transfer_all(), ec);
if (ec)
std::cout << "Error: " << ec.message() << std::endl;
return !ec;
}
bool receive (std::string& reply)
{
Header header;
boost::system::error_code error;
mpSocket->read_some(boost::asio::buffer(&header, sizeof(Header)), error);
if (error != boost::asio::error::eof)
{
if (strncmp(header.magic, "OMW0", 4) == 0)
{
std::vector<char> msg;
msg.resize(header.dataLength);
boost::system::error_code error;
mpSocket->read_some(boost::asio::buffer(&msg[0], header.dataLength), error);
if (!error)
{
reply = &msg[0];
return true;
}
}
else
throw str_exception("Unexpected header!");
}
return false;
}
};
int main(int argc, char* argv[])
{
std::cout << "OpenMW client console" << std::endl;
std::cout << "=====================" << std::endl;
std::cout << "Type 'quit' to exit." << std::endl;
std::cout << "Connecting...";
Client client;
if (client.connect("27917"))
{
std::cout << "success." << std::endl;
bool bDone = false;
do
{
std::cout << "Client> ";
std::string buffer;
std::getline(std::cin, buffer);
if (buffer == "quit")
bDone = true;
else
{
if (client.send(buffer.c_str()))
{
std::string reply;
if (client.receive(reply))
std::cout << "Server: " << reply << std::endl;
else
bDone = true;
}
else
bDone = true;
}
} while (!bDone);
client.disconnect();
}
else
std::cout << "failed." << std::endl;
return 0;
}

@ -0,0 +1,9 @@
project(MWCompiler)
set(TOOLS_MWCOMPILER ${COMPILER}
main.cpp
context.cpp
context.hpp)
add_executable(mwcompiler ${TOOLS_MWCOMPILER})

@ -0,0 +1,16 @@
#include "context.hpp"
namespace SACompiler
{
bool Context::canDeclareLocals() const
{
return true;
}
char Context::getGlobalType (const std::string& name) const
{
return ' ';
}
}

@ -0,0 +1,21 @@
#ifndef MWCOMPILER_CONTEXT_H_INCLUDED
#define MWCOMPILER_CONTEXT_H_INCLUDED
#include <components/compiler/context.hpp>
namespace SACompiler
{
class Context : public Compiler::Context
{
public:
virtual bool canDeclareLocals() const;
///< Is the compiler allowed to declare local variables?
virtual char getGlobalType (const std::string& name) const;
///< 'l: long, 's': short, 'f': float, ' ': does not exist.
};
}
#endif

@ -0,0 +1,78 @@
// Stand-alone MW-script compiler
#include <exception>
#include <iostream>
#include <fstream>
#include <vector>
#include <components/interpreter/types.hpp>
#include <components/compiler/streamerrorhandler.hpp>
#include <components/compiler/scanner.hpp>
#include <components/compiler/fileparser.hpp>
#include <components/compiler/exception.hpp>
#include "context.hpp"
int main (int argc, char **argv)
{
try
{
SACompiler::Context context;
Compiler::StreamErrorHandler errorHandler (std::cout);
Compiler::FileParser parser (errorHandler, context);
std::string filename = argc>1 ? argv[1] : "test.mwscript";
try
{
std::ifstream file (filename.c_str());
if (!file.is_open())
{
std::cout << "can't open script file: " << filename << std::endl;
return 1;
}
Compiler::Scanner scanner (errorHandler, file);
scanner.scan (parser);
}
catch (const Compiler::SourceException&)
{
// ignore exception (problem has already been reported to the user)
}
if (errorHandler.countErrors() || errorHandler.countWarnings())
{
std::cout
<< errorHandler.countErrors() << " error(s), "
<< errorHandler.countWarnings() << " warning(s)" << std::endl
<< std::endl;
}
if (errorHandler.isGood())
{
std::vector<Interpreter::Type_Code> code;
parser.getCode (code);
std::ofstream codeFile ((filename + ".code").c_str());
codeFile.write (reinterpret_cast<const char *> (&code[0]),
code.size()*sizeof (Interpreter::Type_Code));
std::ofstream localFile ((filename + ".locals").c_str());
parser.getLocals().write (localFile);
return 0;
}
return 1;
}
catch (const std::exception &e)
{
std::cout << "\nERROR: " << e.what() << std::endl;
return 1;
}
}

@ -0,0 +1,9 @@
project(MWInterpreter)
set(TOOLS_MWINTERPRETER
${INTERPRETER}
main.cpp
context.cpp
context.hpp)
add_executable(mwinterpreter ${TOOLS_MWINTERPRETER})

@ -0,0 +1,137 @@
#include "context.hpp"
#include <cassert>
#include <fstream>
#include <stdexcept>
#include <iostream>
namespace SAInterpreter
{
Context::Context (const std::string& filename)
{
std::ifstream file (filename.c_str());
if (!file.is_open())
throw std::runtime_error ("can't open locals file: " + filename);
std::size_t shortSize, longSize, floatSize;
file >> shortSize >> longSize >> floatSize;
mShorts.resize (shortSize, 0);
mLongs.resize (longSize, 0);
mFloats.resize (floatSize, 0.0);
std::size_t size = shortSize + longSize + floatSize;
mNames.resize (size);
for (std::size_t i=0; i<size; ++i)
file >> mNames[i];
}
int Context::getLocalShort (int index) const
{
assert (index>=0);
return mShorts.at (index);
}
int Context::getLocalLong (int index) const
{
assert (index>=0);
return mLongs.at (index);
}
float Context::getLocalFloat (int index) const
{
assert (index>=0);
return mFloats.at (index);
}
void Context::setLocalShort (int index, int value)
{
assert (index>=0);
mShorts.at (index) = value;
}
void Context::setLocalLong (int index, int value)
{
assert (index>=0);
mLongs.at (index) = value;
}
void Context::setLocalFloat (int index, float value)
{
assert (index>=0);
mFloats.at (index) = value;
}
void Context::messageBox (const std::string& message,
const std::vector<std::string>& buttons)
{
std::cout << "message box: " << message << std::endl;
for (std::size_t i=0; i<buttons.size(); ++i)
std::cout << " button " << i << ": " << buttons[i] << std::endl;
}
bool Context::menuMode()
{
return false;
}
int Context::getGlobalShort (const std::string& name) const
{
return 0;
}
int Context::getGlobalLong (const std::string& name) const
{
return 0;
}
float Context::getGlobalFloat (const std::string& name) const
{
return 0;
}
void Context::setGlobalShort (const std::string& name, int value) {}
void Context::setGlobalLong (const std::string& name, int value) {}
void Context::setGlobalFloat (const std::string& name, float value) {}
bool Context::isScriptRunning (const std::string& name)
{
return false;
}
void Context::startScript (const std::string& name) {}
void Context::stopScript (const std::string& name) {}
void Context::report()
{
std::size_t i = 0;
std::cout << "local shorts:" << std::endl;
for (std::vector<Interpreter::Type_Short>::const_iterator iter (mShorts.begin());
iter!=mShorts.end(); ++iter)
std::cout << mNames[i++] << ": " << *iter << std::endl;
std::cout << "local longs:" << std::endl;
for (std::vector<Interpreter::Type_Integer>::const_iterator iter (mLongs.begin());
iter!=mLongs.end(); ++iter)
std::cout << mNames[i++] << ": " << *iter << std::endl;
std::cout << "local floats:" << std::endl;
for (std::vector<Interpreter::Type_Float>::const_iterator iter (mFloats.begin());
iter!=mFloats.end(); ++iter)
std::cout << mNames[i++] << ": " << *iter << std::endl;
}
}

@ -0,0 +1,66 @@
#ifndef SAINTERPRETER_CONTEXT_H_INCLUDED
#define SAINTERPRETER_CONTEXT_H_INCLUDED
#include <string>
#include <vector>
#include <components/interpreter/context.hpp>
#include <components/interpreter/types.hpp>
namespace SAInterpreter
{
class Context : public Interpreter::Context
{
std::vector<Interpreter::Type_Short> mShorts;
std::vector<Interpreter::Type_Integer> mLongs;
std::vector<Interpreter::Type_Float> mFloats;
std::vector<std::string> mNames;
public:
Context (const std::string& filename);
///< Create context from file
/// \note A context for an integreted interpreter will typically not
/// configure at construction, but will offer a separate function.
virtual int getLocalShort (int index) const;
virtual int getLocalLong (int index) const;
virtual float getLocalFloat (int index) const;
virtual void setLocalShort (int index, int value);
virtual void setLocalLong (int index, int value);
virtual void setLocalFloat (int index, float value);
virtual void messageBox (const std::string& message,
const std::vector<std::string>& buttons);
virtual bool menuMode();
virtual int getGlobalShort (const std::string& name) const;
virtual int getGlobalLong (const std::string& name) const;
virtual float getGlobalFloat (const std::string& name) const;
virtual void setGlobalShort (const std::string& name, int value);
virtual void setGlobalLong (const std::string& name, int value);
virtual void setGlobalFloat (const std::string& name, float value);
virtual bool isScriptRunning (const std::string& name);
virtual void startScript (const std::string& name);
virtual void stopScript (const std::string& name);
void report();
///< Write state to std::cout
};
}
#endif

@ -0,0 +1,57 @@
// Stand-alone MW-script code interpreter
#include <exception>
#include <fstream>
#include <iostream>
#include <vector>
#include <components/interpreter/interpreter.hpp>
#include <components/interpreter/context.hpp>
#include <components/interpreter/types.hpp>
#include <components/interpreter/installopcodes.hpp>
#include "context.hpp"
int main (int argc, char **argv)
{
try
{
std::string filename = argc>1 ? argv[1] : "test.mwscript";
std::string localfilename = filename + ".locals";
SAInterpreter::Context context (localfilename);
Interpreter::Interpreter interpreter (context);
Interpreter::installOpcodes (interpreter);
std::string codefilename = filename + ".code";
std::ifstream codefile (codefilename.c_str());
if (!codefile.is_open())
{
std::cout << "can't open code file: " << codefilename << std::endl;
return 1;
}
std::vector<Interpreter::Type_Code> code (4);
codefile.read (reinterpret_cast<char *> (&code[0]), 4 * sizeof (Interpreter::Type_Code));
unsigned int size = code[0] + code[1] + code[2] + code[3];
code.resize (4+size);
codefile.read (reinterpret_cast<char *> (&code[4]), size * sizeof (Interpreter::Type_Code));
interpreter.run (&code[0], size+4);
context.report();
}
catch (const std::exception &e)
{
std::cout << "\nERROR: " << e.what() << std::endl;
return 1;
}
}

@ -0,0 +1,217 @@
#include "engine.hpp"
#include <cassert>
#include <iostream>
#include <components/misc/fileops.hpp>
#include <components/bsa/bsa_archive.hpp>
#include "mwinput/inputmanager.hpp"
#include "mwscript/scriptmanager.hpp"
#include "mwscript/compilercontext.hpp"
#include "mwscript/interpretercontext.hpp"
#include "mwscript/extensions.hpp"
#include "mwscript/globalscripts.hpp"
#include "mwsound/soundmanager.hpp"
#include "mwworld/world.hpp"
#include "mwworld/ptr.hpp"
#include "mwworld/environment.hpp"
void OMW::Engine::executeLocalScripts()
{
for (MWWorld::World::ScriptList::const_iterator iter (
mEnvironment.mWorld->getLocalScripts().begin());
iter!=mEnvironment.mWorld->getLocalScripts().end(); ++iter)
{
MWScript::InterpreterContext interpreterContext (mEnvironment,
&iter->second.getRefData().getLocals(), MWWorld::Ptr (iter->second));
mScriptManager->run (iter->first, interpreterContext);
}
}
bool OMW::Engine::frameStarted(const Ogre::FrameEvent& evt)
{
// console
processCommands();
// local scripts
executeLocalScripts();
// global scripts
mEnvironment.mGlobalScripts->run (mEnvironment);
return true;
}
void OMW::Engine::processCommands()
{
Command cmd;
while (mCommandQueue.try_pop_front(cmd))
{
///\todo Add actual processing of the received command strings
std::cout << "Command: '" << cmd.mCommand << "'" << std::endl;
///\todo Replace with real output. For now, echo back the string in uppercase
std::string reply(cmd.mCommand);
std::transform(reply.begin(), reply.end(), reply.begin(), toupper);
cmd.mReplyFunction(reply);
}
}
OMW::Engine::Engine()
: mDebug (false), mVerboseScripts (false), mNewGame (false), mScriptManager (0),
mScriptContext (0)
{
mspCommandServer.reset(
new OMW::CommandServer::Server(&mCommandQueue, kCommandServerPort));
}
OMW::Engine::~Engine()
{
// mspCommandServer->stop();
delete mEnvironment.mWorld;
delete mEnvironment.mSoundManager;
delete mEnvironment.mGlobalScripts;
delete mScriptManager;
delete mScriptContext;
}
// Load all BSA files in data directory.
void OMW::Engine::loadBSA()
{
boost::filesystem::directory_iterator end;
for (boost::filesystem::directory_iterator iter (mDataDir); iter!=end; ++iter)
{
if (boost::filesystem::extension (iter->path())==".bsa")
{
std::cout << "Adding " << iter->path().string() << std::endl;
addBSA(iter->path().file_string());
}
}
}
// add resources directory
// \note This function works recursively.
void OMW::Engine::addResourcesDirectory (const boost::filesystem::path& path)
{
mOgre.getRoot()->addResourceLocation (path.file_string(), "FileSystem",
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, true);
}
// Set data dir
void OMW::Engine::setDataDir (const boost::filesystem::path& dataDir)
{
mDataDir = boost::filesystem::system_complete (dataDir);
}
// Set start cell name (only interiors for now)
void OMW::Engine::setCell (const std::string& cellName)
{
mCellName = cellName;
}
// Set master file (esm)
// - If the given name does not have an extension, ".esm" is added automatically
// - Currently OpenMW only supports one master at the same time.
void OMW::Engine::addMaster (const std::string& master)
{
assert (mMaster.empty());
mMaster = master;
// Append .esm if not already there
std::string::size_type sep = mMaster.find_last_of (".");
if (sep == std::string::npos)
{
mMaster += ".esm";
}
}
void OMW::Engine::enableDebugMode()
{
mDebug = true;
}
void OMW::Engine::enableVerboseScripts()
{
mVerboseScripts = true;
}
void OMW::Engine::setNewGame()
{
mNewGame = true;
}
// Initialise and enter main loop.
void OMW::Engine::go()
{
assert (!mEnvironment.mWorld);
assert (!mDataDir.empty());
assert (!mCellName.empty());
assert (!mMaster.empty());
std::cout << "Hello, fellow traveler!\n";
std::cout << "Your data directory for today is: " << mDataDir << "\n";
std::cout << "Initializing OGRE\n";
const char* plugCfg = "plugins.cfg";
mOgre.configure(!isFile("ogre.cfg"), plugCfg, false);
addResourcesDirectory (mDataDir / "Meshes");
addResourcesDirectory (mDataDir / "Textures");
// Create the window
mOgre.createWindow("OpenMW");
loadBSA();
// Create the world
mEnvironment.mWorld = new MWWorld::World (mOgre, mDataDir, mMaster, mCellName, mNewGame);
mEnvironment.mSoundManager = new MWSound::SoundManager;
MWScript::registerExtensions (mExtensions);
mScriptContext = new MWScript::CompilerContext (MWScript::CompilerContext::Type_Full,
mEnvironment);
mScriptContext->setExtensions (&mExtensions);
mScriptManager = new MWScript::ScriptManager (mEnvironment.mWorld->getStore(), mVerboseScripts,
*mScriptContext);
mEnvironment.mGlobalScripts = new MWScript::GlobalScripts (mEnvironment.mWorld->getStore(),
*mScriptManager);
std::cout << "Setting up input system\n";
// Sets up the input system
MWInput::MWInputManager input(mOgre, mEnvironment.mWorld->getPlayerPos(), mDebug);
// Launch the console server
std::cout << "Starting command server on port " << kCommandServerPort << std::endl;
mspCommandServer->start();
std::cout << "\nStart! Press Q/ESC or close window to exit.\n";
mOgre.getRoot()->addFrameListener (this);
// Start the main rendering loop
mOgre.start();
std::cout << "\nThat's all for now!\n";
}

@ -0,0 +1,112 @@
#ifndef ENGINE_H
#define ENGINE_H
#include <string>
#include <boost/filesystem.hpp>
#include <OgreFrameListener.h>
#include <components/engine/ogre/renderer.hpp>
#include <components/misc/tsdeque.hpp>
#include <components/commandserver/server.hpp>
#include <components/commandserver/command.hpp>
#include <components/compiler/extensions.hpp>
#include "mwworld/environment.hpp"
namespace Compiler
{
class Context;
}
namespace MWScript
{
class ScriptManager;
}
namespace MWSound
{
class SoundManager;
}
namespace MWWorld
{
class World;
}
namespace OMW
{
/// \brief Main engine class, that brings together all the components of OpenMW
class Engine : private Ogre::FrameListener
{
enum { kCommandServerPort = 27917 };
boost::filesystem::path mDataDir;
Render::OgreRenderer mOgre;
std::string mCellName;
std::string mMaster;
bool mDebug;
bool mVerboseScripts;
bool mNewGame;
TsDeque<OMW::Command> mCommandQueue;
std::auto_ptr<OMW::CommandServer::Server> mspCommandServer;
MWWorld::Environment mEnvironment;
MWScript::ScriptManager *mScriptManager;
Compiler::Extensions mExtensions;
Compiler::Context *mScriptContext;
// not implemented
Engine (const Engine&);
Engine& operator= (const Engine&);
/// add resources directory
/// \note This function works recursively.
void addResourcesDirectory (const boost::filesystem::path& path);
/// Load all BSA files in data directory.
void loadBSA();
void executeLocalScripts();
virtual bool frameStarted(const Ogre::FrameEvent& evt);
/// Process pending commands
void processCommands();
public:
Engine();
~Engine();
/// Set data dir
void setDataDir (const boost::filesystem::path& dataDir);
/// Set start cell name (only interiors for now)
void setCell (const std::string& cellName);
/// Set master file (esm)
/// - If the given name does not have an extension, ".esm" is added automatically
/// - Currently OpenMW only supports one master at the same time.
void addMaster (const std::string& master);
/// Enable debug mode:
/// - non-exclusive input
void enableDebugMode();
/// Enable verbose script output
void enableVerboseScripts();
/// Start as a new game.
void setNewGame();
/// Initialise and enter main loop.
void go();
};
}
#endif

@ -16,20 +16,26 @@ using namespace std;
bool parseOptions (int argc, char**argv, OMW::Engine& engine)
{
boost::program_options::options_description desc (
// Create a local alias for brevity
namespace bpo = boost::program_options;
bpo::options_description desc (
"Syntax: openmw <options>\nAllowed options");
desc.add_options()
("help", "print help message")
("data", boost::program_options::value<std::string>()->default_value ("data"),
("data", bpo::value<std::string>()->default_value ("data"),
"set data directory")
("start", boost::program_options::value<std::string>()->default_value ("Beshara"),
("start", bpo::value<std::string>()->default_value ("Beshara"),
"set initial cell (only interior cells supported at the moment")
("master", boost::program_options::value<std::string>()->default_value ("Morrowind"),
("master", bpo::value<std::string>()->default_value ("Morrowind"),
"master file")
( "debug", "debug mode" )
( "script-verbose", "verbose script output" )
( "new-game", "activate char gen/new game mechanics" )
;
boost::program_options::variables_map variables;
bpo::variables_map variables;
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
std::string configFilePath(macBundlePath() + "/Contents/MacOS/openmw.cfg");
@ -38,15 +44,13 @@ bool parseOptions (int argc, char**argv, OMW::Engine& engine)
std::ifstream configFile ("openmw.cfg");
#endif
boost::program_options::parsed_options valid_opts =
boost::program_options::command_line_parser(argc, argv).options(desc).allow_unregistered().run();
bpo::parsed_options valid_opts = bpo::command_line_parser(argc, argv).options(desc).allow_unregistered().run();
boost::program_options::store(valid_opts, variables);
boost::program_options::notify(variables);
bpo::store(valid_opts, variables);
bpo::notify(variables);
if (configFile.is_open())
boost::program_options::store (
boost::program_options::parse_config_file (configFile, desc), variables);
bpo::store ( bpo::parse_config_file (configFile, desc), variables);
if (variables.count ("help"))
{
@ -57,7 +61,16 @@ bool parseOptions (int argc, char**argv, OMW::Engine& engine)
engine.setDataDir (variables["data"].as<std::string>());
engine.setCell (variables["start"].as<std::string>());
engine.addMaster (variables["master"].as<std::string>());
if (variables.count ("debug"))
engine.enableDebugMode();
if (variables.count ("script-verbose"))
engine.enableVerboseScripts();
if (variables.count ("new-game"))
engine.setNewGame();
return true;
}

@ -1,12 +1,12 @@
#ifndef _MWINPUT_MWINPUTMANAGER_H
#define _MWINPUT_MWINPUTMANAGER_H
#include "input/listener.hpp"
#include "input/dispatcher.hpp"
#include "input/poller.hpp"
#include "components/engine/input/listener.hpp"
#include "components/engine/input/dispatcher.hpp"
#include "components/engine/input/poller.hpp"
#include "boost/bind.hpp"
#include "game/mwrender/playerpos.hpp"
#include "platform/strings.h"
#include "apps/openmw/mwrender/playerpos.hpp"
#include "libs/platform/strings.h"
namespace MWInput
{
@ -47,7 +47,7 @@ namespace MWInput
{
// Find the first unused filename.
//
char buf[50];
char buf[50];
do
{
snprintf(buf, 50, "screenshot%03d.png", shotCount++);
@ -58,10 +58,10 @@ namespace MWInput
public:
MWInputManager(Render::OgreRenderer &_ogre,
MWRender::PlayerPos &_player)
MWRender::PlayerPos &_player, bool debug)
: disp(A_LAST),
ogre(_ogre),
input(_ogre),
input(_ogre, debug),
poller(input),
listener(_ogre, input, disp),
player(_player),

@ -0,0 +1,22 @@
#ifndef GAME_RENDER_CELL_H
#define GAME_RENDER_CELL_H
namespace MWRender
{
class CellRender
{
public:
virtual ~CellRender() {};
/// Make the cell visible. Load the cell if necessary.
virtual void show() = 0;
/// Remove the cell from rendering, but don't remove it from
/// memory.
virtual void hide() = 0;
};
}
#endif

@ -1,13 +1,11 @@
#include "cell.hpp"
#include "cellimp.hpp"
#include <cassert>
#include "esm_store/cell_store.hpp"
using namespace MWRender;
template<typename T>
void insertObj(CellRender& cellRender, const T& liveRef)
template<typename T>
void insertObj(CellRenderImp& cellRender, T& liveRef)
{
assert (liveRef.base != NULL);
const std::string &model = liveRef.base->model;
@ -15,21 +13,21 @@ void insertObj(CellRender& cellRender, const T& liveRef)
{
cellRender.insertBegin (liveRef.ref);
cellRender.insertMesh ("meshes\\" + model);
cellRender.insertEnd();
liveRef.mData.setHandle (cellRender.insertEnd());
}
}
template<>
void insertObj(CellRender& cellRender, const ESMS::LiveCellRef<ESM::Light>& liveRef)
template<>
void insertObj(CellRenderImp& cellRender, ESMS::LiveCellRef<ESM::Light, MWWorld::RefData>& liveRef)
{
assert (liveRef.base != NULL);
const std::string &model = liveRef.base->model;
if(!model.empty())
{
cellRender.insertBegin (liveRef.ref);
cellRender.insertMesh ("meshes\\" + model);
// Extract the color and convert to floating point
const int color = liveRef.base->data.color;
const float r = ((color >> 0) & 0xFF) / 255.0f;
@ -37,22 +35,22 @@ void insertObj(CellRender& cellRender, const ESMS::LiveCellRef<ESM::Light>& live
const float b = ((color >> 16) & 0xFF) / 255.0f;
const float radius = float(liveRef.base->data.radius);
cellRender.insertLight(r, g, b, radius);
cellRender.insertEnd();
}
liveRef.mData.setHandle (cellRender.insertEnd());
}
}
template<typename T>
void insertCellRefList (CellRender& cellRender, const T& cellRefList)
void insertCellRefList (CellRenderImp& cellRender, T& cellRefList)
{
for(typename T::List::const_iterator it = cellRefList.list.begin();
for(typename T::List::iterator it = cellRefList.list.begin();
it != cellRefList.list.end(); it++)
{
insertObj (cellRender, *it);
}
}
}
}
void CellRender::insertCell(const ESMS::CellStore &cell)
void CellRenderImp::insertCell(ESMS::CellStore<MWWorld::RefData> &cell)
{
// Loop through all references in the cell
insertCellRefList (*this, cell.activators);

@ -1,31 +1,30 @@
#ifndef _GAME_RENDER_CELL_H
#define _GAME_RENDER_CELL_H
#ifndef _GAME_RENDER_CELLIMP_H
#define _GAME_RENDER_CELLIMP_H
#include <string>
#include "components/esm_store/cell_store.hpp"
#include "../mwworld/refdata.hpp"
namespace ESM
{
class CellRef;
}
namespace ESMS
{
class CellStore;
}
namespace MWRender
{
/// Base class for cell render, that implements inserting references into a cell in a
/// cell type- and render-engine-independent way.
class CellRender
class CellRenderImp
{
public:
CellRender() {}
virtual ~CellRender() {}
CellRenderImp() {}
virtual ~CellRenderImp() {}
/// start inserting a new reference.
virtual void insertBegin (const ESM::CellRef &ref) = 0;
virtual void insertBegin (ESM::CellRef &ref) = 0;
/// insert a mesh related to the most recent insertBegin call.
virtual void insertMesh(const std::string &mesh) = 0;
@ -36,7 +35,8 @@ namespace MWRender
/// finish inserting a new reference and return a handle to it.
virtual std::string insertEnd() = 0;
void insertCell(const ESMS::CellStore &cell);
void insertCell(ESMS::CellStore<MWWorld::RefData> &cell);
};
}

@ -3,7 +3,7 @@
#include <OgreEntity.h>
#include <OgreLight.h>
#include "nifogre/ogre_nif_loader.hpp"
#include "components/nifogre/ogre_nif_loader.hpp"
#include "mwscene.hpp"
using namespace MWRender;
@ -11,7 +11,7 @@ using namespace Ogre;
using namespace ESMS;
bool InteriorCellRender::lightConst = false;
float InteriorCellRender::lightConstValue = 0.0;
float InteriorCellRender::lightConstValue = 0.0f;
bool InteriorCellRender::lightLinear = true;
int InteriorCellRender::lightLinearMethod = 1;
@ -27,10 +27,10 @@ bool InteriorCellRender::lightOutQuadInLin = false;
// start inserting a new reference.
void InteriorCellRender::insertBegin (const ESM::CellRef &ref)
void InteriorCellRender::insertBegin (ESM::CellRef &ref)
{
assert (!insert);
// Create and place scene node for this object
insert = base->createChildSceneNode();
@ -59,7 +59,7 @@ void InteriorCellRender::insertBegin (const ESM::CellRef &ref)
void InteriorCellRender::insertMesh(const std::string &mesh)
{
assert (insert);
NIFLoader::load(mesh);
MovableObject *ent = scene.getMgr()->createEntity(mesh);
insert->attachObject(ent);
@ -74,7 +74,7 @@ void InteriorCellRender::insertLight(float r, float g, float b, float radius)
light->setDiffuseColour (r, g, b);
float cval=0.0f, lval=0.0f, qval=0.0f;
if(lightConst)
cval = lightConstValue;
if(!lightOutQuadInLin)
@ -95,9 +95,9 @@ void InteriorCellRender::insertLight(float r, float g, float b, float radius)
// Do quadratic or linear, depending if we're in an exterior or interior
// cell, respectively. Ignore lightLinear and lightQuadratic.
}
light->setAttenuation(10*radius, cval, lval, qval);
insert->attachObject(light);
}
@ -108,19 +108,19 @@ std::string InteriorCellRender::insertEnd()
assert (insert);
std::string handle = insert->getName();
insert = 0;
return handle;
}
}
// configure lighting according to cell
void InteriorCellRender::configureAmbient()
{
ambientColor.setAsABGR (cell.cell->ambi.ambient);
setAmbientMode();
// Create a "sun" that shines light downwards. It doesn't look
// completely right, but leave it for now.
Ogre::Light *light = scene.getMgr()->createLight();
@ -130,7 +130,7 @@ void InteriorCellRender::configureAmbient()
light->setType(Ogre::Light::LT_DIRECTIONAL);
light->setDirection(0,-1,0);
}
// configure fog according to cell
void InteriorCellRender::configureFog()
{
@ -144,28 +144,28 @@ void InteriorCellRender::configureFog()
scene.getCamera()->setFarClipDistance (high + 10);
scene.getViewport()->setBackgroundColour (color);
}
void InteriorCellRender::setAmbientMode()
{
switch (ambientMode)
{
case 0:
scene.getMgr()->setAmbientLight(ambientColor);
break;
case 1:
scene.getMgr()->setAmbientLight(0.7f*ambientColor + 0.3f*ColourValue(1,1,1));
break;
case 2:
scene.getMgr()->setAmbientLight(ColourValue(1,1,1));
break;
}
}
void InteriorCellRender::show()
{
// If already loaded, just make the cell visible.
@ -208,14 +208,14 @@ void InteriorCellRender::toggleLight()
ambientMode = 0;
else
++ambientMode;
switch (ambientMode)
{
case 0: std::cout << "Setting lights to normal\n"; break;
case 1: std::cout << "Turning the lights up\n"; break;
case 2: std::cout << "Turning the lights to full\n"; break;
}
setAmbientMode();
}

@ -2,7 +2,8 @@
#define _GAME_RENDER_INTERIOR_H
#include "cell.hpp"
#include "esm_store/cell_store.hpp"
#include "cellimp.hpp"
#include "components/esm_store/cell_store.hpp"
#include "OgreColourValue.h"
@ -14,7 +15,7 @@ namespace Ogre
namespace MWRender
{
class MWScene;
/**
This class is responsible for inserting meshes and other
rendering objects from the given cell into the given rendering
@ -22,10 +23,10 @@ namespace MWRender
TODO FIXME: Doesn't do full cleanup yet.
*/
class InteriorCellRender : private CellRender
class InteriorCellRender : public CellRender, private CellRenderImp
{
static bool lightConst;
static float lightConstValue;
@ -40,58 +41,58 @@ namespace MWRender
static float lightQuadraticRadiusMult;
static bool lightOutQuadInLin;
const ESMS::CellStore &cell;
ESMS::CellStore<MWWorld::RefData> &cell;
MWScene &scene;
/// The scene node that contains all objects belonging to this
/// cell.
Ogre::SceneNode *base;
Ogre::SceneNode *insert;
// 0 normal, 1 more bright, 2 max
int ambientMode;
Ogre::ColourValue ambientColor;
/// start inserting a new reference.
virtual void insertBegin (const ESM::CellRef &ref);
virtual void insertBegin (ESM::CellRef &ref);
/// insert a mesh related to the most recent insertBegin call.
virtual void insertMesh(const std::string &mesh);
/// insert a light related to the most recent insertBegin call.
virtual void insertLight(float r, float g, float b, float radius);
/// finish inserting a new reference and return a handle to it.
virtual std::string insertEnd();
/// configure lighting according to cell
void configureAmbient();
void configureAmbient();
/// configure fog according to cell
void configureFog();
void setAmbientMode();
public:
InteriorCellRender(const ESMS::CellStore &_cell, MWScene &_scene)
InteriorCellRender(ESMS::CellStore<MWWorld::RefData> &_cell, MWScene &_scene)
: cell(_cell), scene(_scene), base(NULL), insert(NULL), ambientMode (0) {}
virtual ~InteriorCellRender() { destroy(); }
/// Make the cell visible. Load the cell if necessary.
void show();
virtual void show();
/// Remove the cell from rendering, but don't remove it from
/// memory.
void hide();
/// Destroy all rendering objects connected with this cell.
void destroy();
void destroy(); // comment by Zini: shouldn't this go into the destructor?
/// Switch through lighting modes.
void toggleLight();
};

@ -23,9 +23,8 @@ MWScene::MWScene(Render::OgreRenderer &_rend)
// Create the camera
camera = sceneMgr->createCamera("PlayerCam");
camera->setNearClipDistance(5);
// Create one viewport, entire window
vp = window->addViewport(camera);

@ -1,7 +1,7 @@
#ifndef _GAME_RENDER_MWSCENE_H
#define _GAME_RENDER_MWSCENE_H
#include "ogre/renderer.hpp"
#include "components/engine/ogre/renderer.hpp"
namespace Ogre
{

@ -15,7 +15,7 @@ namespace MWRender
public:
PlayerPos(Ogre::Camera *cam) :
camera(cam), x(0), y(0), z(0) {}
x(0), y(0), z(0), camera(cam) {}
// Set the player position. Uses Morrowind coordinates.
void setPos(float _x, float _y, float _z)

@ -0,0 +1,93 @@
#include "sky.hpp"
#include "Caelum.h"
namespace MWRender
{
//
// Implements a Caelum sky with default settings.
//
// Note: this is intended as a temporary solution to provide some form of
// sky rendering. This code will obviously need significant tailoring to
// support fidelity with Morrowind's rendering. Before doing major work
// on this class, more research should be done to determine whether
// Caelum or another plug-in such as SkyX would be best for the long-term.
//
class CaelumManager : public SkyManager
{
protected:
Caelum::CaelumSystem* mpCaelumSystem;
public:
CaelumManager (Ogre::RenderWindow* pRenderWindow,
Ogre::Camera* pCamera);
virtual ~CaelumManager ();
};
CaelumManager::CaelumManager (Ogre::RenderWindow* pRenderWindow,
Ogre::Camera* pCamera)
: mpCaelumSystem (NULL)
{
using namespace Ogre;
using namespace Caelum;
assert(pCamera);
assert(pRenderWindow);
// Load the Caelum resources
//
ResourceGroupManager::getSingleton().addResourceLocation("resources/caelum", "FileSystem", "Caelum");
ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
// Load the Caelum resources
//
Ogre::SceneManager* pScene = pCamera->getSceneManager();
Caelum::CaelumSystem::CaelumComponent componentMask = CaelumSystem::CAELUM_COMPONENTS_DEFAULT;
mpCaelumSystem = new Caelum::CaelumSystem (Root::getSingletonPtr(), pScene, componentMask);
// Set time acceleration.
mpCaelumSystem->getUniversalClock()->setTimeScale(128);
// Disable fog since OpenMW is handling OGRE fog elsewhere
mpCaelumSystem->setManageSceneFog(false);
// Change the camera far distance to make sure the sky is not clipped
pCamera->setFarClipDistance(50000);
// Register Caelum as an OGRE listener
pRenderWindow->addListener(mpCaelumSystem);
Root::getSingletonPtr()->addFrameListener(mpCaelumSystem);
}
CaelumManager::~CaelumManager()
{
if (mpCaelumSystem)
mpCaelumSystem->shutdown (false);
}
/// Creates and connects the sky rendering component to OGRE.
///
/// \return NULL on failure.
///
SkyManager* SkyManager::create (Ogre::RenderWindow* pRenderWindow,
Ogre::Camera* pCamera)
{
SkyManager* pSkyManager = NULL;
try
{
pSkyManager = new CaelumManager(pRenderWindow, pCamera);
}
catch (Ogre::Exception& e)
{
std::cout << "\nOGRE Exception when attempting to add sky: "
<< e.getFullDescription().c_str() << std::endl;
}
catch (std::exception& e)
{
std::cout << "\nException when attempting to add sky: "
<< e.what() << std::endl;
}
return pSkyManager;
}
}

@ -0,0 +1,24 @@
#ifndef _GAME_RENDER_SKY_H
#define _GAME_RENDER_SKY_H
namespace Ogre
{
class RenderWindow;
class Camera;
}
namespace MWRender
{
///
/// Interface for the sky rendering system
///
class SkyManager
{
public:
static SkyManager* create (Ogre::RenderWindow* pRenderWindow,
Ogre::Camera* pCamera);
virtual ~SkyManager() {}
};
}
#endif // _GAME_RENDER_SKY_H

@ -0,0 +1,44 @@
#include "cellextensions.hpp"
#include <components/compiler/extensions.hpp>
#include <components/interpreter/interpreter.hpp>
#include <components/interpreter/runtime.hpp>
#include <components/interpreter/opcodes.hpp>
#include "../mwworld/world.hpp"
#include "interpretercontext.hpp"
namespace MWScript
{
namespace Cell
{
class OpCellChanged : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
InterpreterContext& context
= static_cast<InterpreterContext&> (runtime.getContext());
runtime.push (context.getWorld().hasCellChanged() ? 1 : 0);
}
};
const int opcodeCellChanged = 0x2000000;
void registerExtensions (Compiler::Extensions& extensions)
{
extensions.registerFunction ("cellchanged", 'l', "", opcodeCellChanged);
}
void installOpcodes (Interpreter::Interpreter& interpreter)
{
interpreter.installSegment5 (opcodeCellChanged, new OpCellChanged);
}
}
}

@ -0,0 +1,27 @@
#ifndef GAME_SCRIPT_CELLEXTENSIONS_H
#define GAME_SCRIPT_CELLEXTENSIONS_H
namespace Compiler
{
class Extensions;
}
namespace Interpreter
{
class Interpreter;
}
namespace MWScript
{
/// \brief cell-related script functionality
namespace Cell
{
void registerExtensions (Compiler::Extensions& extensions);
void installOpcodes (Interpreter::Interpreter& interpreter);
}
}
#endif

@ -0,0 +1,35 @@
#include "compilercontext.hpp"
#include "../mwworld/environment.hpp"
#include "../mwworld/world.hpp"
namespace MWScript
{
CompilerContext::CompilerContext (Type type, const MWWorld::Environment& environment)
: mType (type), mEnvironment (environment)
{}
bool CompilerContext::canDeclareLocals() const
{
return mType==Type_Full;
}
char CompilerContext::getGlobalType (const std::string& name) const
{
if (const ESM::Global *global = mEnvironment.mWorld->getStore().globals.find (name))
{
switch (global->type)
{
case ESM::VT_Short: return 's';
case ESM::VT_Int: return 'l';
case ESM::VT_Float: return 'f';
default: return ' ';
}
}
return ' ';
}
}

@ -0,0 +1,43 @@
#ifndef GAME_SCRIPT_COMPILERCONTEXT_H
#define GAME_SCRIPT_COMPILERCONTEXT_H
#include <components/compiler/context.hpp>
namespace MWWorld
{
class Environment;
}
namespace MWScript
{
class CompilerContext : public Compiler::Context
{
public:
enum Type
{
Type_Full, // global, local, targetted
Type_Dialgoue,
Type_Console
};
private:
Type mType;
const MWWorld::Environment& mEnvironment;
public:
CompilerContext (Type type, const MWWorld::Environment& environment);
/// Is the compiler allowed to declare local variables?
virtual bool canDeclareLocals() const;
/// 'l: long, 's': short, 'f': float, ' ': does not exist.
virtual char getGlobalType (const std::string& name) const;
};
}
#endif

@ -0,0 +1,37 @@
OpenMW Extensions:
Segment 0:
(not implemented yet)
opcodes 0x20-0x3f unused
Segment 1:
(not implemented yet)
opcodes 0x20-0x3f unused
Segment 2:
(not implemented yet)
opcodes 0x200-0x3ff unused
Segment 3:
(not implemented yet)
opcodes 0x200-0x3ff unused
Segment 4:
(not implemented yet)
opcodes 0x200-0x3ff unused
Segment 5:
op 2000000: CellChanged
op 2000001: Say
op 2000002: SayDone
op 2000003: StreamMusic
op 2000004: PlaySound
op 2000005: PlaySoundVP
op 2000006: PlaySound3D
op 2000007: PlaySound3DVP
op 2000008: PlayLoopSound3D
op 2000009: PlayLoopSound3DVP
op 200000a: StopSound
op 200000b: GetSoundPlaying
opcodes 0x200000c-0x3ffffff unused

@ -0,0 +1,16 @@
#include "extensions.hpp"
#include "../mwsound/extensions.hpp"
#include "cellextensions.hpp"
namespace MWScript
{
void registerExtensions (Compiler::Extensions& extensions)
{
Cell::registerExtensions (extensions);
MWSound::registerExtensions (extensions);
}
}

@ -0,0 +1,14 @@
#ifndef GAME_SCRIPT_EXTENSIONS_H
#define GAME_SCRIPT_EXTENSIONS_H
namespace Compiler
{
class Extensions;
}
namespace MWScript
{
void registerExtensions (Compiler::Extensions& extensions);
}
#endif

@ -0,0 +1,69 @@
#include "globalscripts.hpp"
#include <cassert>
#include "interpretercontext.hpp"
#include "scriptmanager.hpp"
namespace MWScript
{
GlobalScripts::GlobalScripts (const ESMS::ESMStore& store, ScriptManager& scriptManager)
: mStore (store), mScriptManager (scriptManager)
{
addScript ("Main");
for (ESMS::RecListT<ESM::StartScript>::MapType::const_iterator iter
(store.startScripts.list.begin());
iter != store.startScripts.list.end(); ++iter)
addScript (iter->second.script);
}
void GlobalScripts::addScript (const std::string& name)
{
if (mScripts.find (name)==mScripts.end())
if (const ESM::Script *script = mStore.scripts.find (name))
{
Locals locals;
locals.configure (*script);
mScripts.insert (std::make_pair (name, std::make_pair (true, locals)));
}
}
void GlobalScripts::removeScript (const std::string& name)
{
std::map<std::string, std::pair<bool, Locals> >::iterator iter = mScripts.find (name);
if (iter!=mScripts.end())
iter->second.first = false;
}
bool GlobalScripts::isRunning (const std::string& name) const
{
std::map<std::string, std::pair<bool, Locals> >::const_iterator iter =
mScripts.find (name);
if (iter==mScripts.end())
return false;
return iter->second.first;
}
void GlobalScripts::run (MWWorld::Environment& environment)
{
for (std::map<std::string, std::pair<bool, Locals> >::iterator iter (mScripts.begin());
iter!=mScripts.end(); ++iter)
{
if (iter->second.first)
{
MWScript::InterpreterContext interpreterContext (environment,
&iter->second.second, MWWorld::Ptr());
mScriptManager.run (iter->first, interpreterContext);
}
}
}
}

@ -0,0 +1,45 @@
#ifndef GAME_SCRIPT_GLOBALSCRIPTS_H
#define GAME_SCRIPT_GLOBALSCRIPTS_H
#include <string>
#include <map>
#include "locals.hpp"
namespace ESMS
{
struct ESMStore;
}
namespace MWWorld
{
class Environment;
}
namespace MWScript
{
class ScriptManager;
class GlobalScripts
{
const ESMS::ESMStore& mStore;
ScriptManager& mScriptManager;
std::map<std::string, std::pair<bool, Locals> > mScripts; // running, local variables
public:
GlobalScripts (const ESMS::ESMStore& store, ScriptManager& scriptManager);
void addScript (const std::string& name);
void removeScript (const std::string& name);
bool isRunning (const std::string& name) const;
void run (MWWorld::Environment& environment);
///< run all active global scripts
};
}
#endif

@ -0,0 +1,155 @@
#include "interpretercontext.hpp"
#include <stdexcept>
#include <iostream>
#include <components/interpreter/types.hpp>
#include "../mwworld/world.hpp"
#include "locals.hpp"
#include "globalscripts.hpp"
namespace MWScript
{
InterpreterContext::InterpreterContext (MWWorld::Environment& environment,
MWScript::Locals *locals, MWWorld::Ptr reference)
: mEnvironment (environment), mLocals (locals), mReference (reference)
{}
int InterpreterContext::getLocalShort (int index) const
{
if (!mLocals)
throw std::runtime_error ("local variables not available in this context");
return mLocals->mShorts.at (index);
}
int InterpreterContext::getLocalLong (int index) const
{
if (!mLocals)
throw std::runtime_error ("local variables not available in this context");
return mLocals->mLongs.at (index);
}
float InterpreterContext::getLocalFloat (int index) const
{
if (!mLocals)
throw std::runtime_error ("local variables not available in this context");
return mLocals->mFloats.at (index);
}
void InterpreterContext::setLocalShort (int index, int value)
{
if (!mLocals)
throw std::runtime_error ("local variables not available in this context");
mLocals->mShorts.at (index) = value;
}
void InterpreterContext::setLocalLong (int index, int value)
{
if (!mLocals)
throw std::runtime_error ("local variables not available in this context");
mLocals->mLongs.at (index) = value;
}
void InterpreterContext::setLocalFloat (int index, float value)
{
if (!mLocals)
throw std::runtime_error ("local variables not available in this context");
mLocals->mFloats.at (index) = value;
}
void InterpreterContext::messageBox (const std::string& message,
const std::vector<std::string>& buttons)
{
std::cout << "message box: " << message << std::endl;
if (!buttons.empty())
std::cerr << "error: message box buttons not supported" << std::endl;
}
bool InterpreterContext::menuMode()
{
return false;
}
int InterpreterContext::getGlobalShort (const std::string& name) const
{
Interpreter::Type_Data value = mEnvironment.mWorld->getGlobalVariable (name);
return static_cast<Interpreter::Type_Short> (
*reinterpret_cast<Interpreter::Type_Integer *> (&value));
}
int InterpreterContext::getGlobalLong (const std::string& name) const
{
// a global long is internally a float.
Interpreter::Type_Data value = mEnvironment.mWorld->getGlobalVariable (name);
return static_cast<Interpreter::Type_Integer> (
*reinterpret_cast<Interpreter::Type_Float *> (&value));
}
float InterpreterContext::getGlobalFloat (const std::string& name) const
{
Interpreter::Type_Data value = mEnvironment.mWorld->getGlobalVariable (name);
return *reinterpret_cast<Interpreter::Type_Float *> (&value);
}
void InterpreterContext::setGlobalShort (const std::string& name, int value)
{
mEnvironment.mWorld->getGlobalVariable (name) =
*reinterpret_cast<Interpreter::Type_Data *> (&value);
}
void InterpreterContext::setGlobalLong (const std::string& name, int value)
{
// a global long is internally a float.
float value2 = value;
mEnvironment.mWorld->getGlobalVariable (name) =
*reinterpret_cast<Interpreter::Type_Data *> (&value2);
}
void InterpreterContext::setGlobalFloat (const std::string& name, float value)
{
mEnvironment.mWorld->getGlobalVariable (name) =
*reinterpret_cast<Interpreter::Type_Data *> (&value);
}
bool InterpreterContext::isScriptRunning (const std::string& name)
{
return mEnvironment.mGlobalScripts->isRunning (name);
}
void InterpreterContext::startScript (const std::string& name)
{
mEnvironment.mGlobalScripts->addScript (name);
}
void InterpreterContext::stopScript (const std::string& name)
{
mEnvironment.mGlobalScripts->removeScript (name);
}
MWWorld::World& InterpreterContext::getWorld()
{
return *mEnvironment.mWorld;
}
MWSound::SoundManager& InterpreterContext::getSoundManager()
{
return *mEnvironment.mSoundManager;
}
MWWorld::Ptr InterpreterContext::getReference()
{
return mReference;
}
}

@ -0,0 +1,81 @@
#ifndef GAME_SCRIPT_INTERPRETERCONTEXT_H
#define GAME_SCRIPT_INTERPRETERCONTEXT_H
#include <components/interpreter/context.hpp>
#include "../mwworld/ptr.hpp"
#include "../mwworld/environment.hpp"
namespace MWWorld
{
class World;
}
namespace MWSound
{
class SoundManager;
}
namespace MWScript
{
struct Locals;
class InterpreterContext : public Interpreter::Context
{
MWWorld::Environment& mEnvironment;
Locals *mLocals;
MWWorld::Ptr mReference;
public:
InterpreterContext (MWWorld::Environment& environment,
MWScript::Locals *locals, MWWorld::Ptr reference);
///< The ownership of \a locals is not transferred. 0-pointer allowed.
virtual int getLocalShort (int index) const;
virtual int getLocalLong (int index) const;
virtual float getLocalFloat (int index) const;
virtual void setLocalShort (int index, int value);
virtual void setLocalLong (int index, int value);
virtual void setLocalFloat (int index, float value);
virtual void messageBox (const std::string& message,
const std::vector<std::string>& buttons);
virtual bool menuMode();
virtual int getGlobalShort (const std::string& name) const;
virtual int getGlobalLong (const std::string& name) const;
virtual float getGlobalFloat (const std::string& name) const;
virtual void setGlobalShort (const std::string& name, int value);
virtual void setGlobalLong (const std::string& name, int value);
virtual void setGlobalFloat (const std::string& name, float value);
virtual bool isScriptRunning (const std::string& name);
virtual void startScript (const std::string& name);
virtual void stopScript (const std::string& name);
MWWorld::World& getWorld();
MWSound::SoundManager& getSoundManager();
MWWorld::Ptr getReference();
///< Reference, that the script is running from (can be empty)
};
}
#endif

@ -0,0 +1,30 @@
#ifndef GAME_SCRIPT_LOCALS_H
#define GAME_SCRIPT_LOCALS_H
#include <vector>
#include <components/esm/loadscpt.hpp>
#include <components/interpreter/types.hpp>
namespace MWScript
{
struct Locals
{
std::vector<Interpreter::Type_Short> mShorts;
std::vector<Interpreter::Type_Integer> mLongs;
std::vector<Interpreter::Type_Float> mFloats;
void configure (const ESM::Script& script)
{
mShorts.clear();
mShorts.resize (script.data.numShorts, 0);
mLongs.clear();
mLongs.resize (script.data.numLongs, 0);
mFloats.clear();
mFloats.resize (script.data.numFloats, 0);
}
};
}
#endif

@ -0,0 +1,127 @@
#include "scriptmanager.hpp"
#include <cassert>
#include <iostream>
#include <sstream>
#include <exception>
#include <components/esm/loadscpt.hpp>
#include <components/esm_store/store.hpp>
#include <components/compiler/scanner.hpp>
#include <components/compiler/context.hpp>
#include <components/interpreter/installopcodes.hpp>
#include <components/interpreter/interpreter.hpp>
#include "../mwsound/extensions.hpp"
#include "cellextensions.hpp"
namespace MWScript
{
ScriptManager::ScriptManager (const ESMS::ESMStore& store, bool verbose,
Compiler::Context& compilerContext)
: mErrorHandler (std::cerr), mStore (store), mVerbose (verbose),
mCompilerContext (compilerContext), mParser (mErrorHandler, mCompilerContext)
{}
bool ScriptManager::compile (const std::string& name)
{
mParser.reset();
mErrorHandler.reset();
bool Success = true;
if (const ESM::Script *script = mStore.scripts.find (name))
{
if (mVerbose)
std::cout << "compiling script: " << name << std::endl;
try
{
std::istringstream input (script->scriptText);
Compiler::Scanner scanner (mErrorHandler, input, mCompilerContext.getExtensions());
scanner.scan (mParser);
if (!mErrorHandler.isGood())
Success = false;
}
catch (const std::exception& error)
{
Success = false;
}
if (!Success && mVerbose)
{
std::cerr
<< "compiling failed: " << name << std::endl
<< script->scriptText
<< std::endl << std::endl;
}
if (Success)
{
std::vector<Interpreter::Type_Code> code;
mParser.getCode (code);
mScripts.insert (std::make_pair (name, code));
// TODO sanity check on generated locals
return true;
}
}
return false;
}
void ScriptManager::run (const std::string& name, Interpreter::Context& interpreterContext)
{
// compile script
std::map<std::string, std::vector<Interpreter::Type_Code> >::iterator iter =
mScripts.find (name);
if (iter==mScripts.end())
{
if (!compile (name))
{
// failed -> ignore script from now on.
std::vector<Interpreter::Type_Code> empty;
mScripts.insert (std::make_pair (name, empty));
return;
}
iter = mScripts.find (name);
assert (iter!=mScripts.end());
}
// execute script
if (!iter->second.empty())
try
{
Interpreter::Interpreter interpreter (interpreterContext);
installOpcodes (interpreter);
interpreter.run (&iter->second[0], iter->second.size());
}
catch (const std::exception& e)
{
std::cerr << "exeution of script " << name << " failed." << std::endl;
if (mVerbose)
std::cerr << "(" << e.what() << ")" << std::endl;
iter->second.clear(); // don't execute again.
}
}
void ScriptManager::installOpcodes (Interpreter::Interpreter& interpreter)
{
Interpreter::installOpcodes (interpreter);
Cell::installOpcodes (interpreter);
MWSound::installOpcodes (interpreter);
}
}

@ -0,0 +1,55 @@
#ifndef GAME_SCRIPT_SCRIPTMANAGER_H
#define GAME_SCRIPT_SCRIPTMANAGER_H
#include <map>
#include <vector>
#include <string>
#include <components/compiler/streamerrorhandler.hpp>
#include <components/compiler/fileparser.hpp>
#include <components/interpreter/types.hpp>
namespace ESMS
{
struct ESMStore;
}
namespace Compiler
{
class Context;
}
namespace Interpreter
{
class Context;
class Interpreter;
}
namespace MWScript
{
class ScriptManager
{
Compiler::StreamErrorHandler mErrorHandler;
const ESMS::ESMStore& mStore;
bool mVerbose;
Compiler::Context& mCompilerContext;
Compiler::FileParser mParser;
std::map<std::string, std::vector<Interpreter::Type_Code> > mScripts;
bool compile (const std::string& name);
public:
ScriptManager (const ESMS::ESMStore& store, bool verbose,
Compiler::Context& compilerContext);
void run (const std::string& name, Interpreter::Context& interpreterContext);
static void installOpcodes (Interpreter::Interpreter& interpreter);
};
};
#endif

@ -0,0 +1,236 @@
#include "extensions.hpp"
#include <components/compiler/extensions.hpp>
#include <components/interpreter/interpreter.hpp>
#include <components/interpreter/runtime.hpp>
#include <components/interpreter/opcodes.hpp>
#include "../mwscript/interpretercontext.hpp"
#include "../mwworld/world.hpp"
#include "soundmanager.hpp"
namespace MWSound
{
namespace Script
{
class OpSay : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
MWScript::InterpreterContext& context
= static_cast<MWScript::InterpreterContext&> (runtime.getContext());
std::string file = runtime.getStringLiteral (runtime[0]);
runtime.pop();
std::string text = runtime.getStringLiteral (runtime[0]);
runtime.pop();
context.getSoundManager().say (context.getReference(), file, text,
context);
}
};
class OpSayDone : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
MWScript::InterpreterContext& context
= static_cast<MWScript::InterpreterContext&> (runtime.getContext());
runtime.push (context.getSoundManager().sayDone (context.getReference(),
context));
}
};
class OpStreamMusic : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
MWScript::InterpreterContext& context
= static_cast<MWScript::InterpreterContext&> (runtime.getContext());
std::string sound = runtime.getStringLiteral (runtime[0]);
runtime.pop();
context.getSoundManager().streamMusic (sound, context);
}
};
class OpPlaySound : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
MWScript::InterpreterContext& context
= static_cast<MWScript::InterpreterContext&> (runtime.getContext());
std::string sound = runtime.getStringLiteral (runtime[0]);
runtime.pop();
context.getSoundManager().playSound (sound, 1.0, 1.0, context);
}
};
class OpPlaySoundVP : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
MWScript::InterpreterContext& context
= static_cast<MWScript::InterpreterContext&> (runtime.getContext());
std::string sound = runtime.getStringLiteral (runtime[0]);
runtime.pop();
float volume = *reinterpret_cast<float *> (&runtime[0]);
runtime.pop();
float pitch = *reinterpret_cast<float *> (&runtime[0]);
runtime.pop();
context.getSoundManager().playSound (sound, volume, pitch, context);
}
};
class OpPlaySound3D : public Interpreter::Opcode0
{
bool mLoop;
public:
OpPlaySound3D (bool loop) : mLoop (loop) {}
virtual void execute (Interpreter::Runtime& runtime)
{
MWScript::InterpreterContext& context
= static_cast<MWScript::InterpreterContext&> (runtime.getContext());
std::string sound = runtime.getStringLiteral (runtime[0]);
runtime.pop();
context.getSoundManager().playSound3D (context.getReference(), sound,
1.0, 1.0, mLoop, context);
}
};
class OpPlaySoundVP3D : public Interpreter::Opcode0
{
bool mLoop;
public:
OpPlaySoundVP3D (bool loop) : mLoop (loop) {}
virtual void execute (Interpreter::Runtime& runtime)
{
MWScript::InterpreterContext& context
= static_cast<MWScript::InterpreterContext&> (runtime.getContext());
std::string sound = runtime.getStringLiteral (runtime[0]);
runtime.pop();
float volume = *reinterpret_cast<float *> (&runtime[0]);
runtime.pop();
float pitch = *reinterpret_cast<float *> (&runtime[0]);
runtime.pop();
context.getSoundManager().playSound3D (context.getReference(), sound, volume,
pitch, mLoop, context);
}
};
class OpStopSound : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
MWScript::InterpreterContext& context
= static_cast<MWScript::InterpreterContext&> (runtime.getContext());
std::string sound = runtime.getStringLiteral (runtime[0]);
runtime.pop();
context.getSoundManager().stopSound3D (context.getReference(), sound, context);
}
};
class OpGetSoundPlaying : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
MWScript::InterpreterContext& context
= static_cast<MWScript::InterpreterContext&> (runtime.getContext());
int index = runtime[0];
runtime.pop();
runtime.push (context.getSoundManager().getSoundPlaying (
context.getReference(), runtime.getStringLiteral (index), context));
}
};
const int opcodeSay = 0x2000001;
const int opcodeSayDone = 0x2000002;
const int opcodeStreamMusic = 0x2000003;
const int opcodePlaySound = 0x2000004;
const int opcodePlaySoundVP = 0x2000005;
const int opcodePlaySound3D = 0x2000006;
const int opcodePlaySound3DVP = 0x2000007;
const int opcodePlayLoopSound3D = 0x2000008;
const int opcodePlayLoopSound3DVP = 0x2000009;
const int opcodeStopSound = 0x200000a;
const int opcodeGetSoundPlaying = 0x200000b;
}
void registerExtensions (Compiler::Extensions& extensions)
{
extensions.registerInstruction ("say", "SS", Script::opcodeSay);
extensions.registerFunction ("saydone", 'l', "", Script::opcodeSayDone);
extensions.registerInstruction ("streammusic", "S", Script::opcodeStreamMusic);
extensions.registerInstruction ("playsound", "c", Script::opcodePlaySound);
extensions.registerInstruction ("playsoundvp", "cff", Script::opcodePlaySoundVP);
extensions.registerInstruction ("playsound3d", "c", Script::opcodePlaySound3D);
extensions.registerInstruction ("playsound3dvp", "cff", Script::opcodePlaySound3DVP);
extensions.registerInstruction ("playloopsound3d", "c", Script::opcodePlayLoopSound3D);
extensions.registerInstruction ("playloopsound3dvp", "cff",
Script::opcodePlayLoopSound3DVP);
extensions.registerInstruction ("stopsound", "c", Script::opcodeStopSound);
extensions.registerFunction ("getsoundplaying", 'l', "c", Script::opcodeGetSoundPlaying);
}
void installOpcodes (Interpreter::Interpreter& interpreter)
{
interpreter.installSegment5 (Script::opcodeSay, new Script::OpSay);
interpreter.installSegment5 (Script::opcodeSayDone, new Script::OpSayDone);
interpreter.installSegment5 (Script::opcodeStreamMusic, new Script::OpStreamMusic);
interpreter.installSegment5 (Script::opcodePlaySound, new Script::OpPlaySound);
interpreter.installSegment5 (Script::opcodePlaySoundVP, new Script::OpPlaySoundVP);
interpreter.installSegment5 (Script::opcodePlaySound3D, new Script::OpPlaySound3D (false));
interpreter.installSegment5 (Script::opcodePlaySound3DVP,
new Script::OpPlaySoundVP3D (false));
interpreter.installSegment5 (Script::opcodePlayLoopSound3D,
new Script::OpPlaySound3D (true));
interpreter.installSegment5 (Script::opcodePlayLoopSound3DVP,
new Script::OpPlaySoundVP3D (true));
interpreter.installSegment5 (Script::opcodeStopSound, new Script::OpStopSound);
interpreter.installSegment5 (Script::opcodeGetSoundPlaying, new Script::OpGetSoundPlaying);
}
}

@ -0,0 +1,24 @@
#ifndef GAME_SOUND_EXTENSIONS_H
#define GAME_SOUND_EXTENSIONS_H
namespace Compiler
{
class Extensions;
}
namespace Interpreter
{
class Interpreter;
}
namespace MWSound
{
// Script-extensions related to sound
void registerExtensions (Compiler::Extensions& extensions);
void installOpcodes (Interpreter::Interpreter& interpreter);
}
#endif

@ -0,0 +1,71 @@
#include "soundmanager.hpp"
#include <iostream> // TODO remove this line, once the real code is in place.
#include <components/interpreter/context.hpp>
namespace MWSound
{
void SoundManager::say (MWWorld::Ptr reference, const std::string& filename,
const std::string& text, Interpreter::Context& context)
{
std::cout << "sound effect: " << reference.getRefData().getHandle() << " is speaking" << std::endl;
context.messageBox (text);
}
bool SoundManager::sayDone (MWWorld::Ptr reference, Interpreter::Context& context) const
{
return false;
}
void SoundManager::streamMusic (const std::string& filename, Interpreter::Context& context)
{
std::cout << "sound effect: playing music" << filename << std::endl;
}
void SoundManager::playSound (const std::string& soundId, float volume, float pitch,
Interpreter::Context& context)
{
std::cout
<< "sound effect: playing sound " << soundId
<< " at volume " << volume << ", at pitch " << pitch
<< std::endl;
}
void SoundManager::playSound3D (MWWorld::Ptr reference, const std::string& soundId,
float volume, float pitch, bool loop, Interpreter::Context& context)
{
std::cout
<< "sound effect: playing sound " << soundId
<< " from " << reference.getRefData().getHandle()
<< " at volume " << volume << ", at pitch " << pitch
<< std::endl;
mSounds[reference.getRefData().getHandle()] = soundId;
}
void SoundManager::stopSound3D (MWWorld::Ptr reference, const std::string& soundId,
Interpreter::Context& context)
{
std::cout
<< "sound effect : stop playing sound " << soundId
<< " from " << reference.getRefData().getHandle() << std::endl;
mSounds[reference.getRefData().getHandle()] = "";
}
bool SoundManager::getSoundPlaying (MWWorld::Ptr reference, const std::string& soundId,
Interpreter::Context& context) const
{
std::map<std::string, std::string>::const_iterator iter =
mSounds.find (reference.getRefData().getHandle());
if (iter==mSounds.end())
return false;
return iter->second==soundId;
}
}

@ -0,0 +1,68 @@
#ifndef GAME_SOUND_SOUNDMANAGER_H
#define GAME_SOUND_SOUNDMANAGER_H
#include <string>
#include <map>
#include "../mwworld/ptr.hpp"
namespace Interpreter
{
class Context;
}
namespace MWSound
{
// Note to the sound implementor (can be removed once the implementation is complete):
//
// - the dummy implementation allows only one sound effect per object at a time. I am
// not sure, if that is what Morrowind does. Beyond the dummy code in this class the script
// system does not make any assumption about the number of sound effects.
//
// - all text-output (error messages and such) must be directed through the
// context.messageBox interface.
//
// - the -> script syntax is not implemented yet ( script instructions of the type
// npc_x -> say "file", "text"
// aren't working)
class SoundManager
{
std::map<std::string, std::string> mSounds; // object, sound (for testing only)
public:
void say (MWWorld::Ptr reference, const std::string& filename,
const std::string& text, Interpreter::Context& context);
///< Make an actor say some text.
/// \param filename name of a sound file in "Sound/Vo/" in the data directory.
/// \param text Subtitle
bool sayDone (MWWorld::Ptr reference, Interpreter::Context& context) const;
///< Is actor not speaking?
void streamMusic (const std::string& filename, Interpreter::Context& context);
///< Play a soundifle
/// \param filename name of a sound file in "Music/" in the data directory.
void playSound (const std::string& soundId, float volume, float pitch,
Interpreter::Context& context);
///< Play a sound, independently of 3D-position
void playSound3D (MWWorld::Ptr reference, const std::string& soundId,
float volume, float pitch, bool loop, Interpreter::Context& context);
///< Play a sound from an object
void stopSound3D (MWWorld::Ptr reference, const std::string& soundId,
Interpreter::Context& context);
///< Stop the given object from playing the given sound.
bool getSoundPlaying (MWWorld::Ptr reference, const std::string& soundId,
Interpreter::Context& context) const;
///< Is the given sound currently playing on the given object?
};
}
#endif

@ -0,0 +1,30 @@
#ifndef GAME_WORLD_INVIRONMENT_H
#define GAME_WORLD_INVIRONMENT_H
namespace MWSound
{
class SoundManager;
}
namespace MWScript
{
class GlobalScripts;
}
namespace MWWorld
{
class World;
///< Collection of script-accessable sub-systems
struct Environment
{
Environment() : mWorld (0), mSoundManager (0), mGlobalScripts (0) {}
World *mWorld;
MWSound::SoundManager *mSoundManager;
MWScript::GlobalScripts *mGlobalScripts;
};
}
#endif

@ -0,0 +1,55 @@
#ifndef GAME_MWWORLD_PTR_H
#define GAME_MWWORLD_PTR_H
#include <cassert>
#include <boost/any.hpp>
#include <components/esm_store/cell_store.hpp>
#include "refdata.hpp"
namespace MWWorld
{
/// \brief Pointer to a LiveCellRef
class Ptr
{
boost::any mPtr;
ESM::CellRef *mCellRef;
RefData *mRefData;
public:
Ptr() : mCellRef (0), mRefData (0) {}
template<typename T>
Ptr (ESMS::LiveCellRef<T, RefData> *liveCellRef)
{
mPtr = liveCellRef;
mCellRef = &liveCellRef->ref;
mRefData = &liveCellRef->mData;
}
template<typename T>
ESMS::LiveCellRef<T, RefData> *get() const
{
return boost::any_cast<const ESMS::LiveCellRef<T, RefData>*> (mPtr);
}
ESM::CellRef& getCellRef() const
{
assert (mCellRef);
return *mCellRef;
}
RefData& getRefData() const
{
assert (mRefData);
return *mRefData;
}
};
}
#endif

@ -0,0 +1,54 @@
#ifndef GAME_MWWORLD_REFDATA_H
#define GAME_MWWORLD_REFDATA_H
#include <string>
#include "../mwscript/locals.hpp"
namespace ESM
{
class Script;
}
namespace MWWorld
{
class RefData
{
std::string mHandle;
MWScript::Locals mLocals; // if we find the overhead of heaving a locals
// object in the refdata of refs without a script,
// we can make this a pointer later.
bool mHasLocals;
public:
RefData() : mHasLocals (false) {}
std::string getHandle()
{
return mHandle;
}
void setLocals (const ESM::Script& script)
{
if (!mHasLocals)
{
mLocals.configure (script);
mHasLocals = true;
}
}
void setHandle (const std::string& handle)
{
mHandle = handle;
}
MWScript::Locals& getLocals()
{
return mLocals;
}
};
}
#endif

@ -0,0 +1,150 @@
#include "world.hpp"
#include <iostream>
#include "components/bsa/bsa_archive.hpp"
#include "components/engine/ogre/renderer.hpp"
#include "apps/openmw/mwrender/sky.hpp"
#include "apps/openmw/mwrender/interior.hpp"
namespace
{
template<typename T>
void listCellScripts (const ESMS::ESMStore& store,
ESMS::CellRefList<T, MWWorld::RefData>& cellRefList, MWWorld::World::ScriptList& scriptList)
{
for (typename ESMS::CellRefList<T, MWWorld::RefData>::List::iterator iter (
cellRefList.list.begin());
iter!=cellRefList.list.end(); ++iter)
{
if (!iter->base->script.empty())
{
if (const ESM::Script *script = store.scripts.find (iter->base->script))
{
iter->mData.setLocals (*script);
scriptList.push_back (
std::make_pair (iter->base->script, MWWorld::Ptr (&*iter)));
}
}
}
}
}
namespace MWWorld
{
void World::insertInteriorScripts (ESMS::CellStore<RefData>& cell)
{
listCellScripts (mStore, cell.activators, mLocalScripts);
listCellScripts (mStore, cell.potions, mLocalScripts);
listCellScripts (mStore, cell.appas, mLocalScripts);
listCellScripts (mStore, cell.armors, mLocalScripts);
listCellScripts (mStore, cell.books, mLocalScripts);
listCellScripts (mStore, cell.clothes, mLocalScripts);
listCellScripts (mStore, cell.containers, mLocalScripts);
listCellScripts (mStore, cell.creatures, mLocalScripts);
listCellScripts (mStore, cell.doors, mLocalScripts);
listCellScripts (mStore, cell.ingreds, mLocalScripts);
listCellScripts (mStore, cell.lights, mLocalScripts);
listCellScripts (mStore, cell.lockpicks, mLocalScripts);
listCellScripts (mStore, cell.miscItems, mLocalScripts);
listCellScripts (mStore, cell.npcs, mLocalScripts);
listCellScripts (mStore, cell.probes, mLocalScripts);
listCellScripts (mStore, cell.repairs, mLocalScripts);
listCellScripts (mStore, cell.weapons, mLocalScripts);
}
World::World (Render::OgreRenderer& renderer, const boost::filesystem::path& dataDir,
const std::string& master, const std::string& startCell, bool newGame)
: mSkyManager (0), mScene (renderer), mPlayerPos (mScene.getCamera())
{
boost::filesystem::path masterPath (dataDir);
masterPath /= master;
std::cout << "Loading ESM " << masterPath.string() << "\n";
// This parses the ESM file and loads a sample cell
mEsm.open (masterPath.file_string());
mStore.load (mEsm);
mInteriors[startCell].loadInt (startCell, mStore, mEsm);
insertInteriorScripts (mInteriors[startCell]);
// global variables
for (ESMS::RecListT<ESM::Global>::MapType::const_iterator iter
(mStore.globals.list.begin());
iter != mStore.globals.list.end(); ++iter)
mGlobalVariables.insert (std::make_pair (iter->first, iter->second.value));
if (newGame)
{
// set new game mark
float newGameState = 1;
mGlobalVariables["chargenstate"] =
*reinterpret_cast<Interpreter::Type_Data *> (&newGameState);
}
std::cout << "\nSetting up cell rendering\n";
// This connects the cell data with the rendering scene.
mActiveCells.insert (std::make_pair (&mInteriors[startCell],
new MWRender::InteriorCellRender (mInteriors[startCell], mScene)));
// Load the cell and insert it into the renderer
for (CellRenderCollection::iterator iter (mActiveCells.begin());
iter!=mActiveCells.end(); ++iter)
iter->second->show();
// Optionally enable the sky
// if (mEnableSky)
// mpSkyManager = MWRender::SkyManager::create(renderer.getWindow(), mScene.getCamera());
}
World::~World()
{
for (CellRenderCollection::iterator iter (mActiveCells.begin());
iter!=mActiveCells.end(); ++iter)
delete iter->second;
for (CellRenderCollection::iterator iter (mBufferedCells.begin());
iter!=mBufferedCells.end(); ++iter)
delete iter->second;
delete mSkyManager;
}
MWRender::PlayerPos& World::getPlayerPos()
{
return mPlayerPos;
}
ESMS::ESMStore& World::getStore()
{
return mStore;
}
const World::ScriptList& World::getLocalScripts() const
{
return mLocalScripts;
}
bool World::hasCellChanged() const
{
// Cell change not implemented yet.
return false;
}
Interpreter::Type_Data& World::getGlobalVariable (const std::string& name)
{
std::map<std::string, Interpreter::Type_Data>::iterator iter = mGlobalVariables.find (name);
if (iter==mGlobalVariables.end())
throw std::runtime_error ("unknown global variable: " + name);
return iter->second;
}
}

@ -0,0 +1,83 @@
#ifndef GAME_MWWORLD_WORLD_H
#define GAME_MWWORLD_WORLD_H
#include <vector>
#include <map>
#include <boost/filesystem.hpp>
#include <components/esm_store/cell_store.hpp>
#include <components/interpreter/types.hpp>
#include "../mwrender/playerpos.hpp"
#include "../mwrender/mwscene.hpp"
#include "refdata.hpp"
#include "ptr.hpp"
namespace Render
{
class OgreRenderer;
}
namespace MWRender
{
class SkyManager;
class CellRender;
}
namespace MWWorld
{
/// \brief The game world and its visual representation
class World
{
public:
typedef std::vector<std::pair<std::string, Ptr> > ScriptList;
private:
typedef ESMS::CellStore<RefData> CellStore;
typedef std::map<CellStore *, MWRender::CellRender *> CellRenderCollection;
MWRender::SkyManager* mSkyManager;
MWRender::MWScene mScene;
MWRender::PlayerPos mPlayerPos;
CellRenderCollection mActiveCells;
CellRenderCollection mBufferedCells; // loaded, but not active (buffering not implementd yet)
ESM::ESMReader mEsm;
ESMS::ESMStore mStore;
std::map<std::string, CellStore> mInteriors;
ScriptList mLocalScripts;
std::map<std::string, Interpreter::Type_Data> mGlobalVariables;
// not implemented
World (const World&);
World& operator= (const World&);
void insertInteriorScripts (ESMS::CellStore<RefData>& cell);
public:
World (Render::OgreRenderer& renderer, const boost::filesystem::path& master,
const std::string& dataDir, const std::string& startCell, bool newGame);
~World();
MWRender::PlayerPos& getPlayerPos();
ESMS::ESMStore& getStore();
const ScriptList& getLocalScripts() const;
///< Names and local variable state of all local scripts in active cells.
bool hasCellChanged() const;
///< Has the player moved to a different cell, since the last frame?
Interpreter::Type_Data& getGlobalVariable (const std::string& name);
};
}
#endif

@ -27,7 +27,7 @@
#include <OgreArchiveFactory.h>
#include <OgreArchiveManager.h>
#include "bsa_file.hpp"
#include <mangle/stream/clients/ogre_datastream.hpp>
#include <libs/mangle/stream/clients/ogre_datastream.hpp>
using namespace Ogre;
using namespace Mangle::Stream;
@ -137,7 +137,7 @@ public:
return name;
}
Archive *createInstance( const String& name )
Archive *createInstance( const String& name )
{
return new BSAArchive(name);
}

@ -23,9 +23,9 @@
#include "bsa_file.hpp"
#include <mangle/stream/servers/file_stream.hpp>
#include <mangle/stream/filters/slice_stream.hpp>
#include <mangle/tools/str_exception.hpp>
#include <libs/mangle/stream/servers/file_stream.hpp>
#include <libs/mangle/stream/filters/slice_stream.hpp>
#include <libs/mangle/tools/str_exception.hpp>
#include <stdlib.h>
#include <assert.h>
@ -129,7 +129,7 @@ void BSAFile::readHeader()
// Set up the the FileStruct table
files.resize(filenum);
for(int i=0;i<filenum;i++)
for(size_t i=0;i<filenum;i++)
{
FileStruct &fs = files[i];
fs.fileSize = offsets[i*2];
@ -156,7 +156,7 @@ int BSAFile::getIndex(const char *str)
else
{
int res = it->second;
assert(res >= 0 && res < files.size());
assert(res >= 0 && res < static_cast<int> (files.size()));
return res;
}
}

@ -24,10 +24,9 @@
#ifndef _BSA_FILE_H_
#define _BSA_FILE_H_
#include <mangle/stream/stream.hpp>
#include <stdint.h>
#include <strings.h>
#include <libs/mangle/stream/stream.hpp>
#include <libs/platform/stdint.h>
#include <libs/platform/strings.h>
#include <string>
#include <vector>
#include <map>

@ -0,0 +1,21 @@
#ifndef COMMANDSERVER_COMMAND_HPP
#define COMMANDSERVER_COMMAND_HPP
#include <boost/function.hpp>
namespace OMW
{
///
/// A Command is currently defined as a string input that, when processed,
/// will generate a string output. The string output is passed to the
/// mReplyFunction as soon as the command has been processed.
///
class Command
{
public:
std::string mCommand;
boost::function1<void, std::string> mReplyFunction;
};
}
#endif //COMMANDSERVER_COMMAND_HPP

@ -0,0 +1,201 @@
#include "server.hpp"
using boost::asio::ip::tcp;
#include <libs/mangle/tools/str_exception.hpp>
//
// Namespace for containing implementation details that the
// rest of OpenMW doesn't need to worry about
//
namespace OMW { namespace CommandServer { namespace Detail {
struct Header
{
char magic[4];
size_t dataLength;
} header;
///
/// Tracks an active connection to the CommandServer
///
class Connection
{
public:
Connection (boost::asio::io_service& io_service, Server* pServer);
void start();
void stop();
tcp::socket& socket();
void reply (std::string s);
protected:
void handle ();
tcp::socket mSocket;
Server* mpServer;
boost::thread* mpThread;
};
Connection::Connection (boost::asio::io_service& io_service, Server* pServer)
: mSocket (io_service)
, mpServer (pServer)
{
}
void Connection::start()
{
mpThread = new boost::thread(boost::bind(&Connection::handle, this));
}
///
/// Stops and disconnects the connection
///
void Connection::stop()
{
mSocket.close();
mpThread->join();
}
tcp::socket& Connection::socket()
{
return mSocket;
}
void Connection::reply (std::string reply)
{
const size_t plen = sizeof(Header) + reply.length() + 1;
std::vector<char> packet(plen);
Header* pHeader = reinterpret_cast<Header*>(&packet[0]);
strncpy(pHeader->magic, "OMW0", 4);
pHeader->dataLength = reply.length() + 1; // Include the null terminator
strncpy(&packet[8], reply.c_str(), pHeader->dataLength);
boost::system::error_code ec;
boost::asio::write(mSocket, boost::asio::buffer(packet),
boost::asio::transfer_all(), ec);
if (ec)
std::cout << "Error: " << ec.message() << std::endl;
}
void Connection::handle ()
{
bool bDone = false;
while (!bDone)
{
// Read the header
boost::system::error_code error;
mSocket.read_some(boost::asio::buffer(&header, sizeof(Header)), error);
if (error != boost::asio::error::eof)
{
if (strncmp(header.magic, "OMW0", 4) == 0)
{
std::vector<char> msg;
msg.resize(header.dataLength);
boost::system::error_code error;
mSocket.read_some(boost::asio::buffer(&msg[0], header.dataLength), error);
if (!error)
mpServer->postCommand(this, &msg[0]);
else
bDone = true;
}
else
throw str_exception("Unexpected header!");
}
else
bDone = true;
}
mpServer->removeConnection(this);
}
}}}
namespace OMW { namespace CommandServer {
using namespace Detail;
Server::Server (Deque* pCommandQueue, const int port)
: mAcceptor (mIOService, tcp::endpoint(tcp::v4(), port))
, mbStopping (false)
, mpCommandQueue (pCommandQueue)
{
}
void Server::start()
{
mIOService.run();
mpThread = new boost::thread(boost::bind(&Server::threadMain, this));
}
void Server::stop()
{
// (1) Stop accepting new connections
// (2) Wait for the listener thread to finish
mAcceptor.close();
mpThread->join();
// Now that no new connections are possible, close any existing
// open connections
{
boost::mutex::scoped_lock lock(mConnectionsMutex);
mbStopping = true;
for (ConnectionSet::iterator it = mConnections.begin();
it != mConnections.end();
++it)
{
(*it)->stop();
}
}
}
void Server::removeConnection (Connection* ptr)
{
// If the server is shutting down (rather the client closing the
// connection), don't remove the connection from the list: that
// would corrupt the iterator the server is using to shutdown all
// clients.
if (!mbStopping)
{
boost::mutex::scoped_lock lock(mConnectionsMutex);
std::set<Connection*>::iterator it = mConnections.find(ptr);
if (it != mConnections.end())
mConnections.erase(it);
}
delete ptr;
}
void Server::postCommand (Connection* pConnection, const char* s)
{
Command cmd;
cmd.mCommand = s;
cmd.mReplyFunction = std::bind1st(std::mem_fun(&Connection::reply), pConnection);
mpCommandQueue->push_back(cmd);
}
void Server::threadMain()
{
// Loop until accept() fails, which will cause the break statement to be hit
while (true)
{
std::auto_ptr<Connection> spConnection(new Connection(mAcceptor.io_service(), this));
boost::system::error_code ec;
mAcceptor.accept(spConnection->socket(), ec);
if (!ec)
{
boost::mutex::scoped_lock lock(mConnectionsMutex);
mConnections.insert(spConnection.get());
spConnection->start();
spConnection.release();
}
else
break;
}
}
}}

@ -0,0 +1,63 @@
#ifndef CONSOLESERVER_H
#define CONSOLESERVER_H
#include <iostream>
#include <string>
#include <deque>
#include <set>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include "components/misc/tsdeque.hpp"
#include "components/commandserver/command.hpp"
namespace OMW { namespace CommandServer
{
//
// Forward Declarations
//
namespace Detail
{
class Connection;
}
//
// Server that opens a port to listen for string commands which will be
// put into the deque provided in the Server constructor.
//
class Server
{
public:
typedef TsDeque<Command> Deque;
Server (Deque* pCommandQueue, const int port);
void start();
void stop();
protected:
friend class Detail::Connection;
typedef std::set<Detail::Connection*> ConnectionSet;
void removeConnection (Detail::Connection* ptr);
void postCommand (Detail::Connection*, const char* s);
void threadMain();
// Objects used to set up the listening server
boost::asio::io_service mIOService;
boost::asio::ip::tcp::acceptor mAcceptor;
boost::thread* mpThread;
bool mbStopping;
// Track active connections
ConnectionSet mConnections;
mutable boost::mutex mConnectionsMutex;
// Pointer to command queue
Deque* mpCommandQueue;
};
}}
#endif // CONSOLESERVER_H

@ -0,0 +1,39 @@
#ifndef COMPILER_CONTEXT_H_INCLUDED
#define COMPILER_CONTEXT_H_INCLUDED
#include <string>
namespace Compiler
{
class Extensions;
class Context
{
const Extensions *mExtensions;
public:
Context() : mExtensions (0) {}
virtual ~Context() {}
virtual bool canDeclareLocals() const = 0;
///< Is the compiler allowed to declare local variables?
void setExtensions (const Extensions *extensions = 0)
{
mExtensions = extensions;
}
const Extensions *getExtensions() const
{
return mExtensions;
}
virtual char getGlobalType (const std::string& name) const = 0;
///< 'l: long, 's': short, 'f': float, ' ': does not exist.
};
}
#endif

@ -0,0 +1,250 @@
#include "controlparser.hpp"
#include <algorithm>
#include <iterator>
#include <stdexcept>
#include "scanner.hpp"
#include "generator.hpp"
namespace Compiler
{
bool ControlParser::parseIfBody (int keyword, const TokenLoc& loc, Scanner& scanner)
{
if (keyword==Scanner::K_endif || keyword==Scanner::K_elseif ||
keyword==Scanner::K_else)
{
std::pair<Codes, Codes> entry;
if (mState!=IfElseBodyState)
mExprParser.append (entry.first);
std::copy (mCodeBlock.begin(), mCodeBlock.end(),
std::back_inserter (entry.second));
mIfCode.push_back (entry);
mCodeBlock.clear();
if (keyword==Scanner::K_endif)
{
// store code for if-cascade
Codes codes;
for (IfCodes::reverse_iterator iter (mIfCode.rbegin());
iter!=mIfCode.rend(); ++iter)
{
Codes block;
if (iter!=mIfCode.rbegin())
Generator::jump (iter->second, codes.size()+1);
if (!iter->first.empty())
{
// if or elseif
std::copy (iter->first.begin(), iter->first.end(),
std::back_inserter (block));
Generator::jumpOnZero (block, iter->second.size()+1);
}
std::copy (iter->second.begin(), iter->second.end(),
std::back_inserter (block));
std::swap (codes, block);
std::copy (block.begin(), block.end(), std::back_inserter (codes));
}
std::copy (codes.begin(), codes.end(), std::back_inserter (mCode));
mIfCode.clear();
mState = IfEndifState;
}
else if (keyword==Scanner::K_elseif)
{
mExprParser.reset();
scanner.scan (mExprParser);
mState = IfElseifEndState;
}
else if (keyword==Scanner::K_else)
{
mState = IfElseEndState;
}
return true;
}
else if (keyword==Scanner::K_if || keyword==Scanner::K_while)
{
// nested
ControlParser parser (getErrorHandler(), getContext(), mLocals, mLiterals);
if (parser.parseKeyword (keyword, loc, scanner))
scanner.scan (parser);
parser.appendCode (mCodeBlock);
return true;
}
else
{
mLineParser.reset();
if (mLineParser.parseKeyword (keyword, loc, scanner))
scanner.scan (mLineParser);
return true;
}
return false;
}
bool ControlParser::parseWhileBody (int keyword, const TokenLoc& loc, Scanner& scanner)
{
if (keyword==Scanner::K_endwhile)
{
Codes loop;
Codes expr;
mExprParser.append (expr);
Generator::jump (loop, -mCodeBlock.size()-expr.size());
std::copy (expr.begin(), expr.end(), std::back_inserter (mCode));
Codes skip;
Generator::jumpOnZero (skip, mCodeBlock.size()+loop.size()+1);
std::copy (skip.begin(), skip.end(), std::back_inserter (mCode));
std::copy (mCodeBlock.begin(), mCodeBlock.end(), std::back_inserter (mCode));
Codes loop2;
Generator::jump (loop2, -mCodeBlock.size()-expr.size()-skip.size());
if (loop.size()!=loop2.size())
throw std::logic_error (
"internal compiler error: failed to generate a while loop");
std::copy (loop2.begin(), loop2.end(), std::back_inserter (mCode));
mState = WhileEndwhileState;
return true;
}
else if (keyword==Scanner::K_if || keyword==Scanner::K_while)
{
// nested
ControlParser parser (getErrorHandler(), getContext(), mLocals, mLiterals);
if (parser.parseKeyword (keyword, loc, scanner))
scanner.scan (parser);
parser.appendCode (mCodeBlock);
return true;
}
else
{
mLineParser.reset();
if (mLineParser.parseKeyword (keyword, loc, scanner))
scanner.scan (mLineParser);
return true;
}
return false;
}
ControlParser::ControlParser (ErrorHandler& errorHandler, Context& context, Locals& locals,
Literals& literals)
: Parser (errorHandler, context), mLocals (locals), mLiterals (literals),
mLineParser (errorHandler, context, locals, literals, mCodeBlock),
mExprParser (errorHandler, context, locals, literals),
mState (StartState)
{
}
void ControlParser::appendCode (std::vector<Interpreter::Type_Code>& code) const
{
std::copy (mCode.begin(), mCode.end(), std::back_inserter (code));
}
bool ControlParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner)
{
if (mState==StartState)
{
if (keyword==Scanner::K_if)
{
mExprParser.reset();
scanner.scan (mExprParser);
mState = IfEndState;
return true;
}
else if (keyword==Scanner::K_while)
{
mExprParser.reset();
scanner.scan (mExprParser);
mState = WhileEndState;
return true;
}
}
else if (mState==IfBodyState || mState==IfElseifBodyState || mState==IfElseBodyState)
{
if (parseIfBody (keyword, loc, scanner))
return true;
}
else if (mState==WhileBodyState)
{
if ( parseWhileBody (keyword, loc, scanner))
return true;
}
return Parser::parseKeyword (keyword, loc, scanner);
}
bool ControlParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner)
{
if (code==Scanner::S_newline)
{
switch (mState)
{
case IfEndState: mState = IfBodyState; return true;
case IfElseifEndState: mState = IfElseifBodyState; return true;
case IfElseEndState: mState = IfElseBodyState; return true;
case WhileEndState: mState = WhileBodyState; return true;
case IfBodyState:
case IfElseifBodyState:
case IfElseBodyState:
case WhileBodyState:
return true; // empty line
case IfEndifState:
case WhileEndwhileState:
return false;
default: ;
}
}
return Parser::parseSpecial (code, loc, scanner);
}
void ControlParser::reset()
{
mCode.clear();
mCodeBlock.clear();
mIfCode.clear();
mState = StartState;
}
}

@ -0,0 +1,70 @@
#ifndef COMPILER_CONTROLPARSER_H_INCLUDED
#define COMPILER_CONTROLPARSER_H_INCLUDED
#include <vector>
#include <components/interpreter/types.hpp>
#include "parser.hpp"
#include "exprparser.hpp"
#include "lineparser.hpp"
namespace Compiler
{
class Locals;
class Literals;
// Control structure parser
class ControlParser : public Parser
{
enum State
{
StartState,
IfEndState, IfBodyState,
IfElseifEndState, IfElseifBodyState,
IfElseEndState, IfElseBodyState,
IfEndifState,
WhileEndState, WhileBodyState,
WhileEndwhileState
};
typedef std::vector<Interpreter::Type_Code> Codes;
typedef std::vector<std::pair<Codes, Codes> > IfCodes;
Locals& mLocals;
Literals& mLiterals;
Codes mCode;
Codes mCodeBlock;
IfCodes mIfCode; // condition, body
LineParser mLineParser;
ExprParser mExprParser;
State mState;
bool parseIfBody (int keyword, const TokenLoc& loc, Scanner& scanner);
bool parseWhileBody (int keyword, const TokenLoc& loc, Scanner& scanner);
public:
ControlParser (ErrorHandler& errorHandler, Context& context, Locals& locals,
Literals& literals);
void appendCode (std::vector<Interpreter::Type_Code>& code) const;
///< store generated code in \æ code.
virtual bool parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner);
///< Handle a keyword token.
/// \return fetch another token?
virtual bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner);
///< Handle a special character token.
/// \return fetch another token?
void reset();
///< Reset parser to clean state.
};
}
#endif

@ -0,0 +1,65 @@
#include "errorhandler.hpp"
namespace Compiler
{
// constructor
ErrorHandler::ErrorHandler() : mWarnings (0), mErrors (0) {}
// destructor
ErrorHandler::~ErrorHandler() {}
// Was compiling successful?
bool ErrorHandler::isGood() const
{
return mErrors==0;
}
// Return number of errors
int ErrorHandler::countErrors() const
{
return mErrors;
}
// Return number of warnings
int ErrorHandler::countWarnings() const
{
return mWarnings;
}
// Generate a warning message.
void ErrorHandler::warning (const std::string& message, const TokenLoc& loc)
{
++mWarnings;
report (message, loc, WarningMessage);
}
// Generate an error message.
void ErrorHandler::error (const std::string& message, const TokenLoc& loc)
{
++mErrors;
report (message, loc, ErrorMessage);
}
// Generate an error message for an unexpected EOF.
void ErrorHandler::endOfFile()
{
++mErrors;
report ("unexpected end of file", ErrorMessage);
}
// Remove all previous error/warning events
void ErrorHandler::reset()
{
mErrors = mWarnings = 0;
}
}

@ -0,0 +1,68 @@
#ifndef COMPILER_ERRORHANDLER_H_INCLUDED
#define COMPILER_ERRORHANDLER_H_INCLUDED
#include <string>
namespace Compiler
{
struct TokenLoc;
/// \brief Error handling
///
/// This class collects errors and provides an interface for reporting them to the user.
class ErrorHandler
{
int mWarnings;
int mErrors;
protected:
enum Type
{
WarningMessage, ErrorMessage
};
private:
// mutators
virtual void report (const std::string& message, const TokenLoc& loc, Type type) = 0;
///< Report error to the user.
virtual void report (const std::string& message, Type type) = 0;
///< Report a file related error
public:
ErrorHandler();
///< constructor
virtual ~ErrorHandler();
///< destructor
bool isGood() const;
///< Was compiling successful?
int countErrors() const;
///< Return number of errors
int countWarnings() const;
///< Return number of warnings
void warning (const std::string& message, const TokenLoc& loc);
///< Generate a warning message.
void error (const std::string& message, const TokenLoc& loc);
///< Generate an error message.
void endOfFile();
///< Generate an error message for an unexpected EOF.
virtual void reset();
///< Remove all previous error/warning events
};
}
#endif

@ -0,0 +1,32 @@
#ifndef COMPILER_EXCEPTION_H_INCLUDED
#define COMPILER_EXCEPTION_H_INCLUDED
#include <exception>
namespace Compiler
{
/// \brief Exception: Error while parsing the source
class SourceException : public std::exception
{
virtual const char *what() const throw() { return "compile error";}
///< Return error message
};
/// \brief Exception: File error
class FileException : public SourceException
{
virtual const char *what() const throw() { return "can't read file"; }
///< Return error message
};
/// \brief Exception: EOF condition encountered
class EOFException : public SourceException
{ virtual const char *what() const throw() { return "end of file"; }
///< Return error message
};
}
#endif

@ -0,0 +1,551 @@
#include "exprparser.hpp"
#include <stdexcept>
#include <cassert>
#include <algorithm>
#include <stack>
#include <iterator>
#include "generator.hpp"
#include "scanner.hpp"
#include "errorhandler.hpp"
#include "locals.hpp"
#include "stringparser.hpp"
#include "extensions.hpp"
#include "context.hpp"
namespace Compiler
{
int ExprParser::getPriority (char op) const
{
switch (op)
{
case '(':
return 0;
case 'e': // ==
case 'n': // !=
case 'l': // <
case 'L': // <=
case 'g': // <
case 'G': // >=
return 1;
case '+':
case '-':
return 2;
case '*':
case '/':
return 3;
case 'm':
return 4;
}
return 0;
}
char ExprParser::getOperandType (int Index) const
{
assert (!mOperands.empty());
assert (Index>=0);
assert (Index<static_cast<int> (mOperands.size()));
return mOperands[mOperands.size()-1-Index];
}
char ExprParser::getOperator() const
{
assert (!mOperators.empty());
return mOperators[mOperators.size()-1];
}
bool ExprParser::isOpen() const
{
return std::find (mOperators.begin(), mOperators.end(), '(')!=mOperators.end();
}
void ExprParser::popOperator()
{
assert (!mOperators.empty());
mOperators.resize (mOperators.size()-1);
}
void ExprParser::popOperand()
{
assert (!mOperands.empty());
mOperands.resize (mOperands.size()-1);
}
void ExprParser::replaceBinaryOperands()
{
char t1 = getOperandType (1);
char t2 = getOperandType();
popOperand();
popOperand();
if (t1==t2)
mOperands.push_back (t1);
else if (t1=='f' || t2=='f')
mOperands.push_back ('f');
else
std::logic_error ("failed to determine result operand type");
}
void ExprParser::pop()
{
char op = getOperator();
switch (op)
{
case 'm':
Generator::negate (mCode, getOperandType());
popOperator();
break;
case '+':
Generator::add (mCode, getOperandType (1), getOperandType());
popOperator();
replaceBinaryOperands();
break;
case '-':
Generator::sub (mCode, getOperandType (1), getOperandType());
popOperator();
replaceBinaryOperands();
break;
case '*':
Generator::mul (mCode, getOperandType (1), getOperandType());
popOperator();
replaceBinaryOperands();
break;
case '/':
Generator::div (mCode, getOperandType (1), getOperandType());
popOperator();
replaceBinaryOperands();
break;
case 'e':
case 'n':
case 'l':
case 'L':
case 'g':
case 'G':
Generator::compare (mCode, op, getOperandType (1), getOperandType());
popOperator();
popOperand();
popOperand();
mOperands.push_back ('l');
break;
default:
throw std::logic_error ("unknown operator");
}
}
void ExprParser::pushIntegerLiteral (int value)
{
mNextOperand = false;
mOperands.push_back ('l');
Generator::pushInt (mCode, mLiterals, value);
}
void ExprParser::pushFloatLiteral (float value)
{
mNextOperand = false;
mOperands.push_back ('f');
Generator::pushFloat (mCode, mLiterals, value);
}
void ExprParser::pushBinaryOperator (char c)
{
while (!mOperators.empty() && getPriority (getOperator())>=getPriority (c))
pop();
mOperators.push_back (c);
mNextOperand = true;
}
void ExprParser::close()
{
while (getOperator()!='(')
pop();
popOperator();
}
void ExprParser::parseArguments (const std::string& arguments, Scanner& scanner)
{
parseArguments (arguments, scanner, mCode);
}
ExprParser::ExprParser (ErrorHandler& errorHandler, Context& context, Locals& locals,
Literals& literals, bool argument)
: Parser (errorHandler, context), mLocals (locals), mLiterals (literals),
mNextOperand (true), mFirst (true), mArgument (argument)
{}
bool ExprParser::parseInt (int value, const TokenLoc& loc, Scanner& scanner)
{
mFirst = false;
if (mNextOperand)
{
pushIntegerLiteral (value);
mTokenLoc = loc;
return true;
}
else
{
// no comma was used between arguments
scanner.putbackInt (value, loc);
return false;
}
}
bool ExprParser::parseFloat (float value, const TokenLoc& loc, Scanner& scanner)
{
mFirst = false;
if (mNextOperand)
{
pushFloatLiteral (value);
mTokenLoc = loc;
return true;
}
else
{
// no comma was used between arguments
scanner.putbackFloat (value, loc);
return false;
}
}
bool ExprParser::parseName (const std::string& name, const TokenLoc& loc,
Scanner& scanner)
{
mFirst = false;
if (mNextOperand)
{
std::string name2 = toLower (name);
char type = mLocals.getType (name2);
if (type!=' ')
{
Generator::fetchLocal (mCode, type, mLocals.getIndex (name2));
mNextOperand = false;
mOperands.push_back (type=='f' ? 'f' : 'l');
return true;
}
type = getContext().getGlobalType (name2);
if (type!=' ')
{
Generator::fetchGlobal (mCode, mLiterals, type, name2);
mNextOperand = false;
mOperands.push_back (type=='f' ? 'f' : 'l');
return true;
}
}
else
{
// no comma was used between arguments
scanner.putbackName (name, loc);
return false;
}
return Parser::parseName (name, loc, scanner);
}
bool ExprParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner)
{
mFirst = false;
if (mNextOperand)
{
if (keyword==Scanner::K_getsquareroot)
{
mTokenLoc = loc;
parseArguments ("f", scanner);
Generator::squareRoot (mCode);
mOperands.push_back ('f');
mNextOperand = false;
return true;
}
else if (keyword==Scanner::K_menumode)
{
mTokenLoc = loc;
Generator::menuMode (mCode);
mOperands.push_back ('l');
mNextOperand = false;
return true;
}
else if (keyword==Scanner::K_random)
{
mTokenLoc = loc;
parseArguments ("l", scanner);
Generator::random (mCode);
mOperands.push_back ('l');
mNextOperand = false;
return true;
}
else if (keyword==Scanner::K_scriptrunning)
{
mTokenLoc = loc;
parseArguments ("c", scanner);
Generator::scriptRunning (mCode);
mOperands.push_back ('l');
mNextOperand = false;
return true;
}
else
{
// check for custom extensions
if (const Extensions *extensions = getContext().getExtensions())
{
char returnType;
std::string argumentType;
if (extensions->isFunction (keyword, returnType, argumentType))
{
mTokenLoc = loc;
parseArguments (argumentType, scanner);
extensions->generateFunctionCode (keyword, mCode);
mOperands.push_back (returnType);
mNextOperand = false;
return true;
}
}
}
}
else
{
// no comma was used between arguments
scanner.putbackKeyword (keyword, loc);
return false;
}
return Parser::parseKeyword (keyword, loc, scanner);
}
bool ExprParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner)
{
if (code==Scanner::S_comma)
{
mTokenLoc = loc;
if (mFirst)
{
// leading comma
mFirst = false;
return true;
}
// end marker
scanner.putbackSpecial (code, loc);
return false;
}
mFirst = false;
if (code==Scanner::S_newline)
{
// end marker
mTokenLoc = loc;
scanner.putbackSpecial (code, loc);
return false;
}
if (code==Scanner::S_minus && mNextOperand)
{
// unary
mOperators.push_back ('m');
mTokenLoc = loc;
return true;
}
if (code==Scanner::S_open)
{
if (mNextOperand)
{
mOperators.push_back ('(');
mTokenLoc = loc;
return true;
}
else
{
// no comma was used between arguments
scanner.putbackKeyword (code, loc);
return false;
}
}
if (code==Scanner::S_close && !mNextOperand)
{
if (isOpen())
{
close();
return true;
}
mTokenLoc = loc;
scanner.putbackSpecial (code, loc);
return false;
}
if (!mNextOperand)
{
mTokenLoc = loc;
char c = 0; // comparison
switch (code)
{
case Scanner::S_plus: pushBinaryOperator ('+'); return true;
case Scanner::S_minus: pushBinaryOperator ('-'); return true;
case Scanner::S_mult: pushBinaryOperator ('*'); return true;
case Scanner::S_div: pushBinaryOperator ('/'); return true;
case Scanner::S_cmpEQ: c = 'e'; break;
case Scanner::S_cmpNE: c = 'n'; break;
case Scanner::S_cmpLT: c = 'l'; break;
case Scanner::S_cmpLE: c = 'L'; break;
case Scanner::S_cmpGT: c = 'g'; break;
case Scanner::S_cmpGE: c = 'G'; break;
}
if (c)
{
if (mArgument && !isOpen())
{
// expression ends here
// Thank you Morrowind for this rotten syntax :(
scanner.putbackSpecial (code, loc);
return false;
}
pushBinaryOperator (c);
return true;
}
}
return Parser::parseSpecial (code, loc, scanner);
}
void ExprParser::reset()
{
mOperands.clear();
mOperators.clear();
mNextOperand = true;
mCode.clear();
mFirst = true;
}
char ExprParser::append (std::vector<Interpreter::Type_Code>& code)
{
if (mOperands.empty() && mOperators.empty())
{
getErrorHandler().error ("missing expression", mTokenLoc);
return 'l';
}
if (mNextOperand || mOperands.empty())
{
getErrorHandler().error ("syntax error in expression", mTokenLoc);
return 'l';
}
while (!mOperators.empty())
pop();
std::copy (mCode.begin(), mCode.end(), std::back_inserter (code));
assert (mOperands.size()==1);
return mOperands[0];
}
void ExprParser::parseArguments (const std::string& arguments, Scanner& scanner,
std::vector<Interpreter::Type_Code>& code, bool invert)
{
ExprParser parser (getErrorHandler(), getContext(), mLocals, mLiterals, true);
StringParser stringParser (getErrorHandler(), getContext(), mLiterals);
std::stack<std::vector<Interpreter::Type_Code> > stack;
for (std::string::const_iterator iter (arguments.begin()); iter!=arguments.end();
++iter)
{
if (*iter=='S' || *iter=='c')
{
stringParser.reset();
if (*iter=='c') stringParser.smashCase();
scanner.scan (stringParser);
if (invert)
{
std::vector<Interpreter::Type_Code> tmp;
stringParser.append (tmp);
stack.push (tmp);
}
else
stringParser.append (code);
}
else
{
parser.reset();
scanner.scan (parser);
std::vector<Interpreter::Type_Code> tmp;
char type = parser.append (tmp);
if (type!=*iter)
Generator::convert (tmp, type, *iter);
if (invert)
stack.push (tmp);
else
std::copy (tmp.begin(), tmp.end(), std::back_inserter (code));
}
}
while (!stack.empty())
{
std::vector<Interpreter::Type_Code>& tmp = stack.top();
std::copy (tmp.begin(), tmp.end(), std::back_inserter (code));
stack.pop();
}
}
}

@ -0,0 +1,102 @@
#ifndef COMPILER_EXPRPARSER_H_INCLUDED
#define COMPILER_EXPRPARSER_H_INCLUDED
#include <vector>
#include <components/interpreter/types.hpp>
#include "parser.hpp"
#include "tokenloc.hpp"
namespace Compiler
{
class Locals;
class Literals;
class ExprParser : public Parser
{
Locals& mLocals;
Literals& mLiterals;
std::vector<char> mOperands;
std::vector<char> mOperators;
bool mNextOperand;
TokenLoc mTokenLoc;
std::vector<Interpreter::Type_Code> mCode;
bool mFirst;
bool mArgument;
int getPriority (char op) const;
char getOperandType (int Index = 0) const;
char getOperator() const;
bool isOpen() const;
void popOperator();
void popOperand();
void replaceBinaryOperands();
void pop();
void pushIntegerLiteral (int value);
void pushFloatLiteral (float value);
void pushBinaryOperator (char c);
void close();
void parseArguments (const std::string& arguments, Scanner& scanner);
public:
ExprParser (ErrorHandler& errorHandler, Context& context, Locals& locals,
Literals& literals, bool argument = false);
///< constructor
/// \param argument Parser is used to parse function- or instruction-
/// arguments (this influences the precedence rules).
char getType() const;
///< Return type of parsed expression ('l' integer, 'f' float)
virtual bool parseInt (int value, const TokenLoc& loc, Scanner& scanner);
///< Handle an int token.
/// \return fetch another token?
virtual bool parseFloat (float value, const TokenLoc& loc, Scanner& scanner);
///< Handle a float token.
/// \return fetch another token?
virtual bool parseName (const std::string& name, const TokenLoc& loc,
Scanner& scanner);
///< Handle a name token.
/// \return fetch another token?
virtual bool parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner);
///< Handle a keyword token.
/// \return fetch another token?
virtual bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner);
///< Handle a special character token.
/// \return fetch another token?
void reset();
///< Reset parser to clean state.
char append (std::vector<Interpreter::Type_Code>& code);
///< Generate code for parsed expression.
/// \return Type ('l': integer, 'f': float)
void parseArguments (const std::string& arguments, Scanner& scanner,
std::vector<Interpreter::Type_Code>& code, bool invert = false);
///< Parse sequence of arguments specified by \a arguments.
/// \param arguments Each character represents one arguments ('l': integer,
/// 'f': float, 'S': string, 'c': string (case smashed))
/// \param invert Store arguments in reverted order.
};
}
#endif

@ -0,0 +1,101 @@
#include "extensions.hpp"
#include <cassert>
#include <stdexcept>
#include "generator.hpp"
namespace Compiler
{
Extensions::Extensions() : mNextKeywordIndex (-1) {}
int Extensions::searchKeyword (const std::string& keyword) const
{
std::map<std::string, int>::const_iterator iter = mKeywords.find (keyword);
if (iter==mKeywords.end())
return 0;
return iter->second;
}
bool Extensions::isFunction (int keyword, char& returnType, std::string& argumentType) const
{
std::map<int, Function>::const_iterator iter = mFunctions.find (keyword);
if (iter==mFunctions.end())
return false;
returnType = iter->second.mReturn;
argumentType = iter->second.mArguments;
return true;
}
bool Extensions::isInstruction (int keyword, std::string& argumentType) const
{
std::map<int, Instruction>::const_iterator iter = mInstructions.find (keyword);
if (iter==mInstructions.end())
return false;
argumentType = iter->second.mArguments;
return true;
}
void Extensions::registerFunction (const std::string& keyword, char returnType,
const std::string& argumentType, int segment5code)
{
assert (segment5code>=33554432 && segment5code<=67108863);
int code = mNextKeywordIndex--;
mKeywords.insert (std::make_pair (keyword, code));
Function function;
function.mReturn = returnType;
function.mArguments = argumentType;
function.mCode = segment5code;
mFunctions.insert (std::make_pair (code, function));
}
void Extensions::registerInstruction (const std::string& keyword,
const std::string& argumentType, int segment5code)
{
assert (segment5code>=33554432 && segment5code<=67108863);
int code = mNextKeywordIndex--;
mKeywords.insert (std::make_pair (keyword, code));
Instruction instruction;
instruction.mArguments = argumentType;
instruction.mCode = segment5code;
mInstructions.insert (std::make_pair (code, instruction));
}
void Extensions::generateFunctionCode (int keyword, std::vector<Interpreter::Type_Code>& code)
const
{
std::map<int, Function>::const_iterator iter = mFunctions.find (keyword);
if (iter==mFunctions.end())
throw std::logic_error ("unknown custom function keyword");
code.push_back (Generator::segment5 (iter->second.mCode));
}
void Extensions::generateInstructionCode (int keyword,
std::vector<Interpreter::Type_Code>& code)
const
{
std::map<int, Instruction>::const_iterator iter = mInstructions.find (keyword);
if (iter==mInstructions.end())
throw std::logic_error ("unknown custom instruction keyword");
code.push_back (Generator::segment5 (iter->second.mCode));
}
}

@ -0,0 +1,75 @@
#ifndef COMPILER_EXTENSIONS_H_INCLUDED
#define COMPILER_EXTENSINOS_H_INCLUDED
#include <string>
#include <map>
#include <vector>
#include <components/interpreter/types.hpp>
namespace Compiler
{
/// \brief Collection of compiler extensions
class Extensions
{
struct Function
{
char mReturn;
std::string mArguments;
int mCode;
};
struct Instruction
{
std::string mArguments;
int mCode;
};
int mNextKeywordIndex;
std::map<std::string, int> mKeywords;
std::map<int, Function> mFunctions;
std::map<int, Instruction> mInstructions;
public:
Extensions();
int searchKeyword (const std::string& keyword) const;
///< Return extension keyword code, that is assigned to the string \a keyword.
/// - if no match is found 0 is returned.
/// - keyword must be all lower case.
bool isFunction (int keyword, char& returnType, std::string& argumentType) const;
///< Is this keyword registered with a function? If yes, return return and argument
/// types.
bool isInstruction (int keyword, std::string& argumentType) const;
///< Is this keyword registered with a function? If yes, return argument types.
void registerFunction (const std::string& keyword, char returnType,
const std::string& argumentType, int segment5code);
///< Register a custom function
/// - keyword must be all lower case.
/// - keyword must be unique
/// \note Currently only segment 5 opcodes are supported.
void registerInstruction (const std::string& keyword,
const std::string& argumentType, int segment5code);
///< Register a custom instruction
/// - keyword must be all lower case.
/// - keyword must be unique
/// \note Currently only segment 5 opcodes are supported.
void generateFunctionCode (int keyword, std::vector<Interpreter::Type_Code>& code)
const;
///< Append code for function to \a code.
void generateInstructionCode (int keyword, std::vector<Interpreter::Type_Code>& code)
const;
///< Append code for function to \a code.
};
}
#endif

@ -0,0 +1,103 @@
#include "fileparser.hpp"
#include <iostream>
#include "tokenloc.hpp"
#include "scanner.hpp"
namespace Compiler
{
FileParser::FileParser (ErrorHandler& errorHandler, Context& context)
: Parser (errorHandler, context),
mScriptParser (errorHandler, context, mLocals, true),
mState (BeginState)
{}
std::string FileParser::getName() const
{
return mName;
}
void FileParser::getCode (std::vector<Interpreter::Type_Code>& code) const
{
mScriptParser.getCode (code);
}
const Locals& FileParser::getLocals() const
{
return mLocals;
}
bool FileParser::parseName (const std::string& name, const TokenLoc& loc,
Scanner& scanner)
{
if (mState==NameState)
{
mName = name;
mState = BeginCompleteState;
return true;
}
if (mState==EndNameState)
{
// optional repeated name after end statement
if (mName!=name)
reportWarning ("Names for script " + mName + " do not match", loc);
mState = EndCompleteState;
return true;
}
return Parser::parseName (name, loc, scanner);
}
bool FileParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner)
{
if (mState==BeginState && keyword==Scanner::K_begin)
{
mState = NameState;
return true;
}
return Parser::parseKeyword (keyword, loc, scanner);
}
bool FileParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner)
{
if (code==Scanner::S_newline)
{
if (mState==BeginCompleteState)
{
// parse the script body
mScriptParser.reset();
scanner.scan (mScriptParser);
mState = EndNameState;
return true;
}
if (mState==EndCompleteState || mState==EndNameState)
{
// we are done here -> ignore the rest of the script
return false;
}
}
return Parser::parseSpecial (code, loc, scanner);
}
void FileParser::parseEOF (Scanner& scanner)
{
if (mState!=EndNameState && mState!=EndCompleteState)
Parser::parseEOF (scanner);
}
void FileParser::reset()
{
mState = BeginState;
mName.clear();
mScriptParser.reset();
}
}

@ -0,0 +1,60 @@
#ifndef COMPILER_FILEPARSER_H_INCLUDED
#define COMPILER_FILEPARSER_H_INCLUDED
#include "parser.hpp"
#include "scriptparser.hpp"
#include "locals.hpp"
#include "literals.hpp"
namespace Compiler
{
// Top-level parser, to be used for global scripts, local scripts and targeted scripts
class FileParser : public Parser
{
enum State
{
BeginState, NameState, BeginCompleteState, EndNameState,
EndCompleteState
};
ScriptParser mScriptParser;
State mState;
std::string mName;
Locals mLocals;
public:
FileParser (ErrorHandler& errorHandler, Context& context);
std::string getName() const;
///< Return script name.
void getCode (std::vector<Interpreter::Type_Code>& code) const;
///< store generated code in \æ code.
const Locals& getLocals() const;
///< get local variable declarations.
virtual bool parseName (const std::string& name, const TokenLoc& loc,
Scanner& scanner);
///< Handle a name token.
/// \return fetch another token?
virtual bool parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner);
///< Handle a keyword token.
/// \return fetch another token?
virtual bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner);
///< Handle a special character token.
/// \return fetch another token?
virtual void parseEOF (Scanner& scanner);
///< Handle EOF token.
void reset();
///< Reset parser to clean state.
};
}
#endif

@ -0,0 +1,670 @@
#include "generator.hpp"
#include <cassert>
#include <algorithm>
#include <iterator>
#include <stdexcept>
#include "literals.hpp"
namespace
{
void opPushInt (Compiler::Generator::CodeContainer& code, int value)
{
code.push_back (Compiler::Generator::segment0 (0, value));
}
void opFetchIntLiteral (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (4));
}
void opFetchFloatLiteral (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (5));
}
void opIntToFloat (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (3));
}
void opFloatToInt (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (6));
}
void opStoreLocalShort (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (0));
}
void opStoreLocalLong (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (1));
}
void opStoreLocalFloat (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (2));
}
void opNegateInt (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (7));
}
void opNegateFloat (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (8));
}
void opAddInt (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (9));
}
void opAddFloat (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (10));
}
void opSubInt (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (11));
}
void opSubFloat (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (12));
}
void opMulInt (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (13));
}
void opMulFloat (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (14));
}
void opDivInt (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (15));
}
void opDivFloat (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (16));
}
void opIntToFloat1 (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (17));
}
void opFloatToInt1 (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (18));
}
void opSquareRoot (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (19));
}
void opReturn (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (20));
}
void opMessageBox (Compiler::Generator::CodeContainer& code, int buttons)
{
code.push_back (Compiler::Generator::segment3 (0, buttons));
}
void opFetchLocalShort (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (21));
}
void opFetchLocalLong (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (22));
}
void opFetchLocalFloat (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (23));
}
void opJumpForward (Compiler::Generator::CodeContainer& code, int offset)
{
code.push_back (Compiler::Generator::segment0 (1, offset));
}
void opJumpBackward (Compiler::Generator::CodeContainer& code, int offset)
{
code.push_back (Compiler::Generator::segment0 (2, offset));
}
void opSkipOnZero (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (24));
}
void opSkipOnNonZero (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (25));
}
void opEqualInt (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (26));
}
void opNonEqualInt (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (27));
}
void opLessThanInt (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (28));
}
void opLessOrEqualInt (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (29));
}
void opGreaterThanInt (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (30));
}
void opGreaterOrEqualInt (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (31));
}
void opEqualFloat (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (32));
}
void opNonEqualFloat (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (33));
}
void opLessThanFloat (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (34));
}
void opLessOrEqualFloat (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (35));
}
void opGreaterThanFloat (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (36));
}
void opGreaterOrEqualFloat (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (37));
}
void opMenuMode (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (38));
}
void opStoreGlobalShort (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (39));
}
void opStoreGlobalLong (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (40));
}
void opStoreGlobalFloat (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (41));
}
void opFetchGlobalShort (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (42));
}
void opFetchGlobalLong (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (43));
}
void opFetchGlobalFloat (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (44));
}
void opRandom (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (45));
}
void opScriptRunning (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (46));
}
void opStartScript (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (47));
}
void opStopScript (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (48));
}
}
namespace Compiler
{
namespace Generator
{
void pushInt (CodeContainer& code, Literals& literals, int value)
{
int index = literals.addInteger (value);
opPushInt (code, index);
opFetchIntLiteral (code);
}
void pushFloat (CodeContainer& code, Literals& literals, float value)
{
int index = literals.addFloat (value);
opPushInt (code, index);
opFetchFloatLiteral (code);
}
void pushString (CodeContainer& code, Literals& literals, const std::string& value)
{
int index = literals.addString (value);
opPushInt (code, index);
}
void assignToLocal (CodeContainer& code, char localType,
int localIndex, const CodeContainer& value, char valueType)
{
opPushInt (code, localIndex);
std::copy (value.begin(), value.end(), std::back_inserter (code));
if (localType!=valueType)
{
if (localType=='f' && valueType=='l')
{
opIntToFloat (code);
}
else if ((localType=='l' || localType=='s') && valueType=='f')
{
opFloatToInt (code);
}
}
switch (localType)
{
case 'f':
opStoreLocalFloat (code);
break;
case 's':
opStoreLocalShort (code);
break;
case 'l':
opStoreLocalLong (code);
break;
default:
assert (0);
}
}
void negate (CodeContainer& code, char valueType)
{
switch (valueType)
{
case 'l':
opNegateInt (code);
break;
case 'f':
opNegateFloat (code);
break;
default:
assert (0);
}
}
void add (CodeContainer& code, char valueType1, char valueType2)
{
if (valueType1=='l' && valueType2=='l')
{
opAddInt (code);
}
else
{
if (valueType1=='l')
opIntToFloat1 (code);
if (valueType2=='l')
opIntToFloat (code);
opAddFloat (code);
}
}
void sub (CodeContainer& code, char valueType1, char valueType2)
{
if (valueType1=='l' && valueType2=='l')
{
opSubInt (code);
}
else
{
if (valueType1=='l')
opIntToFloat1 (code);
if (valueType2=='l')
opIntToFloat (code);
opSubFloat (code);
}
}
void mul (CodeContainer& code, char valueType1, char valueType2)
{
if (valueType1=='l' && valueType2=='l')
{
opMulInt (code);
}
else
{
if (valueType1=='l')
opIntToFloat1 (code);
if (valueType2=='l')
opIntToFloat (code);
opMulFloat (code);
}
}
void div (CodeContainer& code, char valueType1, char valueType2)
{
if (valueType1=='l' && valueType2=='l')
{
opDivInt (code);
}
else
{
if (valueType1=='l')
opIntToFloat1 (code);
if (valueType2=='l')
opIntToFloat (code);
opDivFloat (code);
}
}
void convert (CodeContainer& code, char fromType, char toType)
{
if (fromType!=toType)
{
if (fromType=='f' && toType=='l')
opFloatToInt (code);
else if (fromType=='l' && toType=='f')
opIntToFloat (code);
else
throw std::logic_error ("illegal type conversion");
}
}
void squareRoot (CodeContainer& code)
{
opSquareRoot (code);
}
void exit (CodeContainer& code)
{
opReturn (code);
}
void message (CodeContainer& code, Literals& literals, const std::string& message,
int buttons)
{
assert (buttons==0);
int index = literals.addString (message);
opPushInt (code, index);
opMessageBox (code, buttons);
}
void fetchLocal (CodeContainer& code, char localType, int localIndex)
{
opPushInt (code, localIndex);
switch (localType)
{
case 'f':
opFetchLocalFloat (code);
break;
case 's':
opFetchLocalShort (code);
break;
case 'l':
opFetchLocalLong (code);
break;
default:
assert (0);
}
}
void jump (CodeContainer& code, int offset)
{
if (offset>0)
opJumpForward (code, offset);
else if (offset<0)
opJumpBackward (code, -offset);
else
throw std::logic_error ("inifite loop");
}
void jumpOnZero (CodeContainer& code, int offset)
{
opSkipOnNonZero (code);
if (offset<0)
--offset; // compensate for skip instruction
jump (code, offset);
}
void jumpOnNonZero (CodeContainer& code, int offset)
{
opSkipOnZero (code);
if (offset<0)
--offset; // compensate for skip instruction
jump (code, offset);
}
void compare (CodeContainer& code, char op, char valueType1, char valueType2)
{
if (valueType1=='l' && valueType2=='l')
{
switch (op)
{
case 'e': opEqualInt (code); break;
case 'n': opNonEqualInt (code); break;
case 'l': opLessThanInt (code); break;
case 'L': opLessOrEqualInt (code); break;
case 'g': opGreaterThanInt (code); break;
case 'G': opGreaterOrEqualInt (code); break;
default:
assert (0);
}
}
else
{
if (valueType1=='l')
opIntToFloat1 (code);
if (valueType2=='l')
opIntToFloat (code);
switch (op)
{
case 'e': opEqualFloat (code); break;
case 'n': opNonEqualFloat (code); break;
case 'l': opLessThanFloat (code); break;
case 'L': opLessOrEqualFloat (code); break;
case 'g': opGreaterThanFloat (code); break;
case 'G': opGreaterOrEqualFloat (code); break;
default:
assert (0);
}
}
}
void menuMode (CodeContainer& code)
{
opMenuMode (code);
}
void assignToGlobal (CodeContainer& code, Literals& literals, char localType,
const std::string& name, const CodeContainer& value, char valueType)
{
int index = literals.addString (name);
opPushInt (code, index);
std::copy (value.begin(), value.end(), std::back_inserter (code));
if (localType!=valueType)
{
if (localType=='f' && valueType=='l')
{
opIntToFloat (code);
}
else if ((localType=='l' || localType=='s') && valueType=='f')
{
opFloatToInt (code);
}
}
switch (localType)
{
case 'f':
opStoreGlobalFloat (code);
break;
case 's':
opStoreGlobalShort (code);
break;
case 'l':
opStoreGlobalLong (code);
break;
default:
assert (0);
}
}
void fetchGlobal (CodeContainer& code, Literals& literals, char localType,
const std::string& name)
{
int index = literals.addString (name);
opPushInt (code, index);
switch (localType)
{
case 'f':
opFetchGlobalFloat (code);
break;
case 's':
opFetchGlobalShort (code);
break;
case 'l':
opFetchGlobalLong (code);
break;
default:
assert (0);
}
}
void random (CodeContainer& code)
{
opRandom (code);
}
void scriptRunning (CodeContainer& code)
{
opScriptRunning (code);
}
void startScript (CodeContainer& code)
{
opStartScript (code);
}
void stopScript (CodeContainer& code)
{
opStopScript (code);
}
}
}

@ -0,0 +1,113 @@
#ifndef COMPILER_GENERATOR_H_INCLUDED
#define COMPILER_GENERATOR_H_INCLUDED
#include <vector>
#include <string>
#include <cassert>
#include <components/interpreter/types.hpp>
namespace Compiler
{
class Literals;
namespace Generator
{
typedef std::vector<Interpreter::Type_Code> CodeContainer;
inline Interpreter::Type_Code segment0 (unsigned int c, unsigned int arg0)
{
assert (c<64);
return (c<<24) | (arg0 & 0xffffff);
}
inline Interpreter::Type_Code segment1 (unsigned int c, unsigned int arg0,
unsigned int arg1)
{
assert (c<64);
return 0x40000000 | (c<<24) | ((arg0 & 0xfff)<<12) | (arg1 & 0xfff);
}
inline Interpreter::Type_Code segment2 (unsigned int c, unsigned int arg0)
{
assert (c<1024);
return 0x80000000 | (c<<20) | (arg0 & 0xfffff);
}
inline Interpreter::Type_Code segment3 (unsigned int c, unsigned int arg0)
{
assert (c<1024);
return 0xc0000000 | (c<<20) | (arg0 & 0xffff);
}
inline Interpreter::Type_Code segment4 (unsigned int c, unsigned int arg0,
unsigned int arg1)
{
assert (c<1024);
return 0xc4000000 | (c<<16) | ((arg0 & 0xff)<<8) | (arg1 & 0xff);
}
inline Interpreter::Type_Code segment5 (unsigned int c)
{
assert (c<67108864);
return 0xc8000000 | c;
}
void pushInt (CodeContainer& code, Literals& literals, int value);
void pushFloat (CodeContainer& code, Literals& literals, float value);
void pushString (CodeContainer& code, Literals& literals, const std::string& value);
void assignToLocal (CodeContainer& code, char localType,
int localIndex, const CodeContainer& value, char valueType);
void negate (CodeContainer& code, char valueType);
void add (CodeContainer& code, char valueType1, char valueType2);
void sub (CodeContainer& code, char valueType1, char valueType2);
void mul (CodeContainer& code, char valueType1, char valueType2);
void div (CodeContainer& code, char valueType1, char valueType2);
void convert (CodeContainer& code, char fromType, char toType);
void squareRoot (CodeContainer& code);
void exit (CodeContainer& code);
void message (CodeContainer& code, Literals& literals, const std::string& message,
int buttons);
void fetchLocal (CodeContainer& code, char localType, int localIndex);
void jump (CodeContainer& code, int offset);
void jumpOnZero (CodeContainer& code, int offset);
void jumpOnNonZero (CodeContainer& code, int offset);
void compare (CodeContainer& code, char op, char valueType1, char valueType2);
void menuMode (CodeContainer& code);
void assignToGlobal (CodeContainer& code, Literals& literals, char localType,
const std::string& name, const CodeContainer& value, char valueType);
void fetchGlobal (CodeContainer& code, Literals& literals, char localType,
const std::string& name);
void random (CodeContainer& code);
void scriptRunning (CodeContainer& code);
void startScript (CodeContainer& code);
void stopScript (CodeContainer& code);
}
}
#endif

@ -0,0 +1,230 @@
#include "lineparser.hpp"
#include "scanner.hpp"
#include "context.hpp"
#include "errorhandler.hpp"
#include "skipparser.hpp"
#include "locals.hpp"
#include "generator.hpp"
#include "extensions.hpp"
namespace Compiler
{
LineParser::LineParser (ErrorHandler& errorHandler, Context& context, Locals& locals,
Literals& literals, std::vector<Interpreter::Type_Code>& code)
: Parser (errorHandler, context), mLocals (locals), mLiterals (literals), mCode (code),
mState (BeginState), mExprParser (errorHandler, context, locals, literals)
{}
bool LineParser::parseInt (int value, const TokenLoc& loc, Scanner& scanner)
{
return Parser::parseInt (value, loc, scanner);
}
bool LineParser::parseFloat (float value, const TokenLoc& loc, Scanner& scanner)
{
return Parser::parseFloat (value, loc, scanner);
}
bool LineParser::parseName (const std::string& name, const TokenLoc& loc,
Scanner& scanner)
{
if (mState==ShortState || mState==LongState || mState==FloatState)
{
if (!getContext().canDeclareLocals())
{
getErrorHandler().error ("local variables can't be declared in this context", loc);
SkipParser skip (getErrorHandler(), getContext());
scanner.scan (skip);
return false;
}
std::string name2 = toLower (name);
char type = mLocals.getType (name2);
if (type!=' ')
{
getErrorHandler().error ("can't re-declare local variable", loc);
SkipParser skip (getErrorHandler(), getContext());
scanner.scan (skip);
return false;
}
mLocals.declare (mState==ShortState ? 's' : (mState==LongState ? 'l' : 'f'),
name2);
mState = EndState;
return true;
}
if (mState==SetState)
{
std::string name2 = toLower (name);
// local variable?
char type = mLocals.getType (name2);
if (type!=' ')
{
mName = name2;
mState = SetLocalVarState;
return true;
}
type = getContext().getGlobalType (name2);
if (type!=' ')
{
mName = name2;
mType = type;
mState = SetGlobalVarState;
return true;
}
getErrorHandler().error ("unknown variable", loc);
SkipParser skip (getErrorHandler(), getContext());
scanner.scan (skip);
return false;
}
if (mState==MessageState || mState==MessageCommaState)
{
std::string arguments;
for (std::size_t i=0; i<name.size(); ++i)
{
if (name[i]=='%')
{
++i;
if (i<name.size())
{
if (name[i]=='G' || name[i]=='g')
{
arguments += "l";
}
else if (name[i]=='S' || name[i]=='s')
{
arguments += 'S';
}
else if (name[i]=='.' || name[i]=='f')
{
arguments += 'f';
}
}
}
}
if (!arguments.empty())
{
mExprParser.reset();
mExprParser.parseArguments (arguments, scanner, mCode, true);
}
Generator::message (mCode, mLiterals, name, 0);
mState = EndState;
return false;
}
return Parser::parseName (name, loc, scanner);
}
bool LineParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner)
{
if (mState==BeginState)
{
switch (keyword)
{
case Scanner::K_short: mState = ShortState; return true;
case Scanner::K_long: mState = LongState; return true;
case Scanner::K_float: mState = FloatState; return true;
case Scanner::K_set: mState = SetState; return true;
case Scanner::K_messagebox: mState = MessageState; return true;
case Scanner::K_return:
Generator::exit (mCode);
mState = EndState;
return true;
case Scanner::K_startscript:
mExprParser.parseArguments ("c", scanner, mCode, true);
Generator::startScript (mCode);
mState = EndState;
return true;
case Scanner::K_stopscript:
mExprParser.parseArguments ("c", scanner, mCode, true);
Generator::stopScript (mCode);
mState = EndState;
return true;
}
// check for custom extensions
if (const Extensions *extensions = getContext().getExtensions())
{
std::string argumentType;
if (extensions->isInstruction (keyword, argumentType))
{
mExprParser.parseArguments (argumentType, scanner, mCode, true);
extensions->generateInstructionCode (keyword, mCode);
mState = EndState;
return true;
}
}
}
else if (mState==SetLocalVarState && keyword==Scanner::K_to)
{
mExprParser.reset();
scanner.scan (mExprParser);
std::vector<Interpreter::Type_Code> code;
char type = mExprParser.append (code);
Generator::assignToLocal (mCode, mLocals.getType (mName),
mLocals.getIndex (mName), code, type);
mState = EndState;
return true;
}
else if (mState==SetGlobalVarState && keyword==Scanner::K_to)
{
mExprParser.reset();
scanner.scan (mExprParser);
std::vector<Interpreter::Type_Code> code;
char type = mExprParser.append (code);
Generator::assignToGlobal (mCode, mLiterals, mType, mName, code, type);
mState = EndState;
return true;
}
return Parser::parseKeyword (keyword, loc, scanner);
}
bool LineParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner)
{
if (code==Scanner::S_newline && mState==EndState)
return false;
if (code==Scanner::S_comma && mState==MessageState)
{
mState = MessageCommaState;
return true;
}
return Parser::parseSpecial (code, loc, scanner);
}
void LineParser::reset()
{
mState = BeginState;
mName.clear();
}
}

@ -0,0 +1,68 @@
#ifndef COMPILER_LINEPARSER_H_INCLUDED
#define COMPILER_LINEPARSER_H_INCLUDED
#include <vector>
#include <components/interpreter/types.hpp>
#include "parser.hpp"
#include "exprparser.hpp"
namespace Compiler
{
class Locals;
class Literals;
/// \brief Line parser, to be used in console scripts and as part of ScriptParser
class LineParser : public Parser
{
enum State
{
BeginState,
ShortState, LongState, FloatState,
SetState, SetLocalVarState, SetGlobalVarState,
MessageState, MessageCommaState,
EndState
};
Locals& mLocals;
Literals& mLiterals;
std::vector<Interpreter::Type_Code>& mCode;
State mState;
std::string mName;
char mType;
ExprParser mExprParser;
public:
LineParser (ErrorHandler& errorHandler, Context& context, Locals& locals,
Literals& literals, std::vector<Interpreter::Type_Code>& code);
virtual bool parseInt (int value, const TokenLoc& loc, Scanner& scanner);
///< Handle an int token.
/// \return fetch another token?
virtual bool parseFloat (float value, const TokenLoc& loc, Scanner& scanner);
///< Handle a float token.
/// \return fetch another token?
virtual bool parseName (const std::string& name, const TokenLoc& loc,
Scanner& scanner);
///< Handle a name token.
/// \return fetch another token?
virtual bool parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner);
///< Handle a keyword token.
/// \return fetch another token?
virtual bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner);
///< Handle a special character token.
/// \return fetch another token?
void reset();
///< Reset parser to clean state.
};
}
#endif

@ -0,0 +1,94 @@
#include "literals.hpp"
#include <algorithm>
namespace Compiler
{
int Literals::getIntegerSize() const
{
return mIntegers.size() * sizeof (Interpreter::Type_Integer);
}
int Literals::getFloatSize() const
{
return mFloats.size() * sizeof (Interpreter::Type_Float);
}
int Literals::getStringSize() const
{
int size = 0;
for (std::vector<std::string>::const_iterator iter (mStrings.begin());
iter!=mStrings.end(); ++iter)
size += static_cast<int> (iter->size()) + 1;
if (size % 4) // padding
size += 4 - size % 4;
return size;
}
void Literals::append (std::vector<Interpreter::Type_Code>& code) const
{
for (std::vector<Interpreter::Type_Integer>::const_iterator iter (mIntegers.begin());
iter!=mIntegers.end(); ++iter)
code.push_back (*reinterpret_cast<const Interpreter::Type_Code *> (&*iter));
for (std::vector<Interpreter::Type_Float>::const_iterator iter (mFloats.begin());
iter!=mFloats.end(); ++iter)
code.push_back (*reinterpret_cast<const Interpreter::Type_Code *> (&*iter));
int stringBlockSize = getStringSize();
int size = static_cast<int> (code.size());
code.resize (size+stringBlockSize/4);
int offset = 0;
for (std::vector<std::string>::const_iterator iter (mStrings.begin());
iter!=mStrings.end(); ++iter)
{
int stringSize = iter->size()+1;
std::copy (iter->c_str(), iter->c_str()+stringSize,
reinterpret_cast<char *> (&code[size]) + offset);
offset += stringSize;
}
}
int Literals::addInteger (Interpreter::Type_Integer value)
{
int index = static_cast<int> (mIntegers.size());
mIntegers.push_back (value);
return index;
}
int Literals::addFloat (Interpreter::Type_Float value)
{
int index = static_cast<int> (mFloats.size());
mFloats.push_back (value);
return index;
}
int Literals::addString (const std::string& value)
{
int index = static_cast<int> (mStrings.size());
mStrings.push_back (value);
return index;
}
void Literals::clear()
{
mIntegers.clear();
mFloats.clear();
mStrings.clear();
}
}

@ -0,0 +1,49 @@
#ifndef COMPILER_LITERALS_H_INCLUDED
#define COMPILER_LITERALS_H_INCLUDED
#include <string>
#include <vector>
#include <components/interpreter/types.hpp>
namespace Compiler
{
/// \brief Literal values.
class Literals
{
std::vector<Interpreter::Type_Integer> mIntegers;
std::vector<Interpreter::Type_Float> mFloats;
std::vector<std::string> mStrings;
public:
int getIntegerSize() const;
///< Return size of integer block (in bytes).
int getFloatSize() const;
///< Return size of float block (in bytes).
int getStringSize() const;
///< Return size of string block (in bytes).
void append (std::vector<Interpreter::Type_Code>& code) const;
///< Apepnd literal blocks to code.
/// \note code blocks will be padded for 32-bit alignment.
int addInteger (Interpreter::Type_Integer value);
///< add integer liternal and return index.
int addFloat (Interpreter::Type_Float value);
///< add float literal and return value.
int addString (const std::string& value);
///< add string literal and return value.
void clear();
///< remove all literals.
};
}
#endif

@ -0,0 +1,110 @@
#include "locals.hpp"
#include <cassert>
#include <stdexcept>
#include <algorithm>
#include <ostream>
#include <iterator>
namespace Compiler
{
const std::vector<std::string>& Locals::get (char type) const
{
switch (type)
{
case 's': return mShorts;
case 'l': return mLongs;
case 'f': return mFloats;
}
throw std::logic_error ("unknown variable type");
}
int Locals::searchIndex (char type, const std::string& name) const
{
const std::vector<std::string>& collection = get (type);
std::vector<std::string>::const_iterator iter =
std::find (collection.begin(), collection.end(), name);
if (iter==collection.end())
return -1;
return iter-collection.begin();
}
bool Locals::search (char type, const std::string& name) const
{
return searchIndex (type, name)!=-1;
}
std::vector<std::string>& Locals::get (char type)
{
switch (type)
{
case 's': return mShorts;
case 'l': return mLongs;
case 'f': return mFloats;
}
throw std::logic_error ("unknown variable type");
}
char Locals::getType (const std::string& name) const
{
if (search ('s', name))
return 's';
if (search ('l', name))
return 'l';
if (search ('f', name))
return 'f';
return ' ';
}
int Locals::getIndex (const std::string& name) const
{
int index = searchIndex ('s', name);
if (index!=-1)
return index;
index = searchIndex ('l', name);
if (index!=-1)
return index;
return searchIndex ('f', name);
}
void Locals::write (std::ostream& localFile) const
{
localFile
<< get ('s').size() << ' '
<< get ('l').size() << ' '
<< get ('f').size() << std::endl;
std::copy (get ('s').begin(), get ('s').end(),
std::ostream_iterator<std::string> (localFile, " "));
std::copy (get ('l').begin(), get ('l').end(),
std::ostream_iterator<std::string> (localFile, " "));
std::copy (get ('f').begin(), get ('f').end(),
std::ostream_iterator<std::string> (localFile, " "));
}
void Locals::declare (char type, const std::string& name)
{
get (type).push_back (name);
}
void Locals::clear()
{
get ('s').clear();
get ('l').clear();
get ('f').clear();
}
}

@ -0,0 +1,45 @@
#ifndef COMPILER_LOCALS_H_INCLUDED
#define COMPILER_LOCALS_H_INCLUDED
#include <vector>
#include <string>
#include <iosfwd>
namespace Compiler
{
/// \brief Local variable declarations
class Locals
{
std::vector<std::string> mShorts;
std::vector<std::string> mLongs;
std::vector<std::string> mFloats;
const std::vector<std::string>& get (char type) const;
int searchIndex (char type, const std::string& name) const;
bool search (char type, const std::string& name) const;
std::vector<std::string>& get (char type);
public:
char getType (const std::string& name) const;
///< 's': short, 'l': long, 'f': float, ' ': does not exist.
int getIndex (const std::string& name) const;
///< return index for local variable \a name (-1: does not exist).
void write (std::ostream& localFile) const;
///< write declarations to file.
void declare (char type, const std::string& name);
///< declares a variable.
void clear();
///< remove all declarations.
};
}
#endif

@ -0,0 +1,74 @@
#include "output.hpp"
#include <cassert>
#include <algorithm>
#include <iterator>
#include "locals.hpp"
namespace Compiler
{
Output::Output (Locals& locals) : mLocals (locals) {}
void Output::getCode (std::vector<Interpreter::Type_Code>& code) const
{
code.clear();
// header
code.push_back (static_cast<Interpreter::Type_Code> (mCode.size()));
assert (mLiterals.getIntegerSize()%4==0);
code.push_back (static_cast<Interpreter::Type_Code> (mLiterals.getIntegerSize()/4));
assert (mLiterals.getFloatSize()%4==0);
code.push_back (static_cast<Interpreter::Type_Code> (mLiterals.getFloatSize()/4));
assert (mLiterals.getStringSize()%4==0);
code.push_back (static_cast<Interpreter::Type_Code> (mLiterals.getStringSize()/4));
// code
std::copy (mCode.begin(), mCode.end(), std::back_inserter (code));
// literals
mLiterals.append (code);
}
const Literals& Output::getLiterals() const
{
return mLiterals;
}
const std::vector<Interpreter::Type_Code>& Output::getCode() const
{
return mCode;
}
const Locals& Output::getLocals() const
{
return mLocals;
}
Literals& Output::getLiterals()
{
return mLiterals;
}
std::vector<Interpreter::Type_Code>& Output::getCode()
{
return mCode;
}
Locals& Output::getLocals()
{
return mLocals;
}
void Output::clear()
{
mLiterals.clear();
mCode.clear();
mLocals.clear();
}
}

@ -0,0 +1,44 @@
#ifndef COMPILER_OUTPUT_H_INCLUDED
#define COMPILER_OUTPUT_H_INCLUDED
#include "literals.hpp"
#include <vector>
#include <components/interpreter/types.hpp>
namespace Compiler
{
class Locals;
class Output
{
Literals mLiterals;
std::vector<Interpreter::Type_Code> mCode;
Locals& mLocals;
public:
Output (Locals& locals);
void getCode (std::vector<Interpreter::Type_Code>& code) const;
///< store generated code in \æ code.
const Literals& getLiterals() const;
const Locals& getLocals() const;
const std::vector<Interpreter::Type_Code>& getCode() const;
Literals& getLiterals();
std::vector<Interpreter::Type_Code>& getCode();
Locals& getLocals();
void clear();
};
}
#endif

@ -0,0 +1,140 @@
#include "parser.hpp"
#include <cctype>
#include <algorithm>
#include <iterator>
#include "errorhandler.hpp"
#include "exception.hpp"
namespace Compiler
{
// Report the error and throw an exception.
void Parser::reportSeriousError (const std::string& message, const TokenLoc& loc)
{
mErrorHandler.error (message, loc);
throw SourceException();
}
// Report the error
void Parser::reportError (const std::string& message, const TokenLoc& loc)
{
mErrorHandler.error (message, loc);
}
// Report the warning without throwing an exception.
void Parser::reportWarning (const std::string& message, const TokenLoc& loc)
{
mErrorHandler.warning (message, loc);
}
// Report an unexpected EOF condition.
void Parser::reportEOF()
{
mErrorHandler.endOfFile();
throw EOFException();
}
// Return error handler
ErrorHandler& Parser::getErrorHandler()
{
return mErrorHandler;
}
// Return context
Context& Parser::getContext()
{
return mContext;
}
std::string Parser::toLower (const std::string& name)
{
std::string lowerCase;
std::transform (name.begin(), name.end(), std::back_inserter (lowerCase),
(int(*)(int)) std::tolower);
return lowerCase;
}
Parser::Parser (ErrorHandler& errorHandler, Context& context)
: mErrorHandler (errorHandler), mContext (context)
{}
// destructor
Parser::~Parser() {}
// Handle an int token.
// \return fetch another token?
//
// - Default-implementation: Report an error.
bool Parser::parseInt (int value, const TokenLoc& loc, Scanner& scanner)
{
reportSeriousError ("Unexpected numeric value", loc);
return false;
}
// Handle a float token.
// \return fetch another token?
//
// - Default-implementation: Report an error.
bool Parser::parseFloat (float value, const TokenLoc& loc, Scanner& scanner)
{
reportSeriousError ("Unexpected floating point value", loc);
return false;
}
// Handle a name token.
// \return fetch another token?
//
// - Default-implementation: Report an error.
bool Parser::parseName (const std::string& name, const TokenLoc& loc,
Scanner& scanner)
{
reportSeriousError ("Unexpected name", loc);
return false;
}
// Handle a keyword token.
// \return fetch another token?
//
// - Default-implementation: Report an error.
bool Parser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner)
{
reportSeriousError ("Unexpected keyword", loc);
return false;
}
// Handle a special character token.
// \return fetch another token?
//
// - Default-implementation: Report an error.
bool Parser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner)
{
reportSeriousError ("Unexpected special token", loc);
return false;
}
// Handle an EOF token.
//
// - Default-implementation: Report an error.
void Parser::parseEOF (Scanner& scanner)
{
reportEOF();
}
}

@ -0,0 +1,90 @@
#ifndef COMPILER_PARSER_H_INCLUDED
#define COMPILER_PARSER_H_INCLUDED
#include <string>
namespace Compiler
{
class Scanner;
struct TokenLoc;
class ErrorHandler;
class Context;
/// \brief Parser base class
///
/// This class defines a callback-parser.
class Parser
{
ErrorHandler& mErrorHandler;
Context& mContext;
protected:
void reportSeriousError (const std::string& message, const TokenLoc& loc);
///< Report the error and throw a exception.
void reportError (const std::string& message, const TokenLoc& loc);
///< Report the error
void reportWarning (const std::string& message, const TokenLoc& loc);
///< Report the warning without throwing an exception.
void reportEOF();
///< Report an unexpected EOF condition.
ErrorHandler& getErrorHandler();
///< Return error handler
Context& getContext();
///< Return context
static std::string toLower (const std::string& name);
public:
Parser (ErrorHandler& errorHandler, Context& context);
///< constructor
virtual ~Parser();
///< destructor
virtual bool parseInt (int value, const TokenLoc& loc, Scanner& scanner);
///< Handle an int token.
/// \return fetch another token?
///
/// - Default-implementation: Report an error.
virtual bool parseFloat (float value, const TokenLoc& loc, Scanner& scanner);
///< Handle a float token.
/// \return fetch another token?
///
/// - Default-implementation: Report an error.
virtual bool parseName (const std::string& name, const TokenLoc& loc,
Scanner& scanner);
///< Handle a name token.
/// \return fetch another token?
///
/// - Default-implementation: Report an error.
virtual bool parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner);
///< Handle a keyword token.
/// \return fetch another token?
///
/// - Default-implementation: Report an error.
virtual bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner);
///< Handle a special character token.
/// \return fetch another token?
///
/// - Default-implementation: Report an error.
virtual void parseEOF (Scanner& scanner);
///< Handle EOF token.
///
/// - Default-implementation: Report an error.
};
}
#endif

@ -0,0 +1,513 @@
#include "scanner.hpp"
#include <cctype>
#include <sstream>
#include <algorithm>
#include <iterator>
#include "exception.hpp"
#include "errorhandler.hpp"
#include "parser.hpp"
#include "extensions.hpp"
namespace Compiler
{
bool Scanner::get (char& c)
{
mStream.get (c);
if (!mStream.good())
return false;
mPrevLoc =mLoc;
if (c=='\n')
{
mLoc.mColumn = 0;
++mLoc.mLine;
mLoc.mLiteral.clear();
}
else
{
++mLoc.mColumn;
mLoc.mLiteral += c;
}
return true;
}
void Scanner::putback (char c)
{
mStream.putback (c);
mLoc = mPrevLoc;
}
bool Scanner::scanToken (Parser& parser)
{
switch (mPutback)
{
case Putback_Special:
mPutback = Putback_None;
return parser.parseSpecial (mPutbackCode, mPutbackLoc, *this);
case Putback_Integer:
mPutback = Putback_None;
return parser.parseInt (mPutbackInteger, mPutbackLoc, *this);
case Putback_Float:
mPutback = Putback_None;
return parser.parseFloat (mPutbackFloat, mPutbackLoc, *this);
case Putback_Name:
mPutback = Putback_None;
return parser.parseName (mPutbackName, mPutbackLoc, *this);
case Putback_Keyword:
mPutback = Putback_None;
return parser.parseKeyword (mPutbackCode, mPutbackLoc, *this);
case Putback_None:
break;
}
char c;
if (!get (c))
{
parser.parseEOF (*this);
return false;
}
else if (c==';')
{
while (get (c))
{
if (c=='\n')
{
putback (c);
break;
}
}
mLoc.mLiteral.clear();
return true;
}
else if (isWhitespace (c))
{
mLoc.mLiteral.clear();
return true;
}
else if (std::isdigit (c))
{
bool cont = false;
if (scanInt (c, parser, cont))
{
mLoc.mLiteral.clear();
return cont;
}
}
else if (std::isalpha (c) || c=='_' || c=='"')
{
bool cont = false;
if (scanName (c, parser, cont))
{
mLoc.mLiteral.clear();
return cont;
}
}
else if (c==13) // linux compatibility hack
{
return true;
}
else
{
bool cont = false;
if (scanSpecial (c, parser, cont))
{
mLoc.mLiteral.clear();
return cont;
}
}
TokenLoc loc (mLoc);
mLoc.mLiteral.clear();
mErrorHandler.error ("syntax error", loc);
throw SourceException();
}
bool Scanner::scanInt (char c, Parser& parser, bool& cont)
{
std::string value;
value += c;
bool empty = false;
bool error = false;
while (get (c))
{
if (std::isdigit (c))
{
value += c;
empty = false;
}
else if (std::isalpha (c) || c=='_')
error = true;
else if (c=='.' && !error)
{
return scanFloat (value, parser, cont);
}
else
{
putback (c);
break;
}
}
if (empty || error)
return false;
TokenLoc loc (mLoc);
mLoc.mLiteral.clear();
std::istringstream stream (value);
int intValue = 0;
stream >> intValue;
cont = parser.parseInt (intValue, loc, *this);
return true;
}
bool Scanner::scanFloat (const std::string& intValue, Parser& parser, bool& cont)
{
std::string value = intValue + ".";
char c;
bool empty = intValue.empty() || intValue=="-";
bool error = false;
while (get (c))
{
if (std::isdigit (c))
{
value += c;
empty = false;
}
else if (std::isalpha (c) || c=='_')
error = true;
else
{
putback (c);
break;
}
}
if (empty || error)
return false;
TokenLoc loc (mLoc);
mLoc.mLiteral.clear();
std::istringstream stream (value);
float floatValue = 0;
stream >> floatValue;
cont = parser.parseFloat (floatValue, loc, *this);
return true;
}
bool Scanner::scanName (char c, Parser& parser, bool& cont)
{
static const char *keywords[] =
{
"begin", "end",
"short", "long", "float",
"if", "endif", "else", "elseif",
"while", "endwhile",
"return",
"messagebox",
"set", "to",
"getsquareroot",
"menumode",
"random",
"startscript", "stopscript", "scriptrunning",
0
};
std::string name;
if (!scanName (c, name))
return false;
TokenLoc loc (mLoc);
mLoc.mLiteral.clear();
if (name.size()>=2 && name[0]=='"' && name[name.size()-1]=='"')
{
name = name.substr (1, name.size()-2);
cont = parser.parseName (name, loc, *this);
return true;
}
int i = 0;
std::string lowerCase;
lowerCase.reserve (name.size());
std::transform (name.begin(), name.end(), std::back_inserter (lowerCase),
(int(*)(int)) std::tolower);
for (; keywords[i]; ++i)
if (lowerCase==keywords[i])
break;
if (keywords[i])
{
cont = parser.parseKeyword (i, loc, *this);
return true;
}
if (mExtensions)
{
if (int keyword = mExtensions->searchKeyword (lowerCase))
{
cont = parser.parseKeyword (keyword, loc, *this);
return true;
}
}
cont = parser.parseName (name, loc, *this);
return true;
}
bool Scanner::scanName (char c, std::string& name)
{
bool first = false;
bool error = false;
name.clear();
putback (c);
while (get (c))
{
if (!name.empty() && name[0]=='"')
{
if (c=='"')
{
name += c;
break;
}
else if (c=='\\')
{
if (!get (c))
{
mErrorHandler.error ("incomplete escape sequence", mLoc);
break;
}
}
else if (c=='\n')
{
mErrorHandler.error ("incomplete string or name", mLoc);
break;
}
}
else if (!(c=='"' && name.empty()))
{
if (!(std::isalpha (c) || std::isdigit (c) || c=='_'))
{
putback (c);
break;
}
if (first && std::isdigit (c))
error = true;
}
name += c;
first = false;
}
return !error;
}
bool Scanner::scanSpecial (char c, Parser& parser, bool& cont)
{
int special = -1;
if (c=='\n')
special = S_newline;
else if (c=='(')
special = S_open;
else if (c==')')
special = S_close;
else if (c=='=')
{
if (get (c))
{
if (c=='=')
special = S_cmpEQ;
else
{
putback (c);
return false;
}
}
else
{
putback (c);
return false;
}
}
else if (c=='!')
{
if (get (c))
{
if (c=='=')
special = S_cmpNE;
else
{
putback (c);
return false;
}
}
else
return false;
}
else if (c=='-')
{
if (get (c))
{
if (c=='>')
special = S_ref;
else
{
putback (c);
special = S_minus;
}
}
else
special = S_minus;
}
else if (c=='<')
{
if (get (c))
{
if (c=='=')
special = S_cmpLE;
else
{
putback (c);
special = S_cmpLT;
}
}
else
special = S_cmpLT;
}
else if (c=='>')
{
if (get (c))
{
if (c=='=')
special = S_cmpGE;
else
{
putback (c);
special = S_cmpGT;
}
}
else
special = S_cmpGT;
}
else if (c==',')
special = S_comma;
else if (c=='+')
special = S_plus;
else if (c=='*')
special = S_mult;
else if (c=='/')
special = S_div;
else
return false;
if (special==S_newline)
mLoc.mLiteral = "<newline>";
TokenLoc loc (mLoc);
mLoc.mLiteral.clear();
cont = parser.parseSpecial (special, loc, *this);
return true;
}
bool Scanner::isWhitespace (char c)
{
return c==' ' || c=='\t';
}
// constructor
Scanner::Scanner (ErrorHandler& errorHandler, std::istream& inputStream,
const Extensions *extensions)
: mErrorHandler (errorHandler), mStream (inputStream), mExtensions (extensions),
mPutback (Putback_None)
{
}
void Scanner::scan (Parser& parser)
{
while (scanToken (parser));
}
void Scanner::putbackSpecial (int code, const TokenLoc& loc)
{
mPutback = Putback_Special;
mPutbackCode = code;
mPutbackLoc = loc;
}
void Scanner::putbackInt (int value, const TokenLoc& loc)
{
mPutback = Putback_Integer;
mPutbackInteger = value;
mPutbackLoc = loc;
}
void Scanner::putbackFloat (float value, const TokenLoc& loc)
{
mPutback = Putback_Float;
mPutbackFloat = value;
mPutbackLoc = loc;
}
void Scanner::putbackName (const std::string& name, const TokenLoc& loc)
{
mPutback = Putback_Name;
mPutbackName = name;
mPutbackLoc = loc;
}
void Scanner::putbackKeyword (int keyword, const TokenLoc& loc)
{
mPutback = Putback_Keyword;
mPutbackCode = keyword;
mPutbackLoc = loc;
}
}

@ -0,0 +1,119 @@
#ifndef COMPILER_SCANNER_H_INCLUDED
#define COMPILER_SCANNER_H_INCLUDED
#include <string>
#include <iosfwd>
#include "tokenloc.hpp"
namespace Compiler
{
class ErrorHandler;
class Parser;
class Extensions;
/// \brief Scanner
///
/// This class translate a char-stream to a token stream (delivered via
/// parser-callbacks).
class Scanner
{
enum putback_type
{
Putback_None, Putback_Special, Putback_Integer, Putback_Float,
Putback_Name, Putback_Keyword
};
ErrorHandler& mErrorHandler;
TokenLoc mLoc;
TokenLoc mPrevLoc;
std::istream& mStream;
const Extensions *mExtensions;
putback_type mPutback;
int mPutbackCode;
int mPutbackInteger;
float mPutbackFloat;
std::string mPutbackName;
TokenLoc mPutbackLoc;
public:
enum keyword
{
K_begin, K_end,
K_short, K_long, K_float,
K_if, K_endif, K_else, K_elseif,
K_while, K_endwhile,
K_return,
K_messagebox,
K_set, K_to,
K_getsquareroot,
K_menumode,
K_random,
K_startscript, K_stopscript, K_scriptrunning
};
enum special
{
S_newline,
S_open, S_close,
S_cmpEQ, S_cmpNE, S_cmpLT, S_cmpLE, S_cmpGT, S_cmpGE,
S_plus, S_minus, S_mult, S_div,
S_comma,
S_ref
};
private:
// not implemented
Scanner (const Scanner&);
Scanner& operator= (const Scanner&);
bool get (char& c);
void putback (char c);
bool scanToken (Parser& parser);
bool scanInt (char c, Parser& parser, bool& cont);
bool scanFloat (const std::string& intValue, Parser& parser, bool& cont);
bool scanName (char c, Parser& parser, bool& cont);
bool scanName (char c, std::string& name);
bool scanSpecial (char c, Parser& parser, bool& cont);
static bool isWhitespace (char c);
public:
Scanner (ErrorHandler& errorHandler, std::istream& inputStream,
const Extensions *extensions = 0);
///< constructor
void scan (Parser& parser);
///< Scan a token and deliver it to the parser.
void putbackSpecial (int code, const TokenLoc& loc);
///< put back a special token
void putbackInt (int value, const TokenLoc& loc);
///< put back an integer token
void putbackFloat (float value, const TokenLoc& loc);
///< put back a float token
void putbackName (const std::string& name, const TokenLoc& loc);
///< put back a name toekn
void putbackKeyword (int keyword, const TokenLoc& loc);
///< put back a keyword token
};
}
#endif

@ -0,0 +1,80 @@
#include "scriptparser.hpp"
#include "scanner.hpp"
namespace Compiler
{
ScriptParser::ScriptParser (ErrorHandler& errorHandler, Context& context,
Locals& locals, bool end)
: Parser (errorHandler, context), mOutput (locals),
mLineParser (errorHandler, context, locals, mOutput.getLiterals(), mOutput.getCode()),
mControlParser (errorHandler, context, locals, mOutput.getLiterals()),
mEnd (end)
{}
void ScriptParser::getCode (std::vector<Interpreter::Type_Code>& code) const
{
mOutput.getCode (code);
}
bool ScriptParser::parseName (const std::string& name, const TokenLoc& loc,
Scanner& scanner)
{
mLineParser.reset();
if (mLineParser.parseName (name, loc, scanner))
scanner.scan (mLineParser);
return true;
}
bool ScriptParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner)
{
if (keyword==Scanner::K_while || keyword==Scanner::K_if)
{
mControlParser.reset();
if (mControlParser.parseKeyword (keyword, loc, scanner))
scanner.scan (mControlParser);
mControlParser.appendCode (mOutput.getCode());
return true;
}
if (keyword==Scanner::K_end && mEnd)
{
return false;
}
mLineParser.reset();
if (mLineParser.parseKeyword (keyword, loc, scanner))
scanner.scan (mLineParser);
return true;
}
bool ScriptParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner)
{
if (code==Scanner::S_newline) // empty line
return true;
mLineParser.reset();
if (mLineParser.parseSpecial (code, loc, scanner))
scanner.scan (mLineParser);
return true;
}
void ScriptParser::parseEOF (Scanner& scanner)
{
if (mEnd)
Parser::parseEOF (scanner);
}
void ScriptParser::reset()
{
mLineParser.reset();
mOutput.clear();
}
}

@ -0,0 +1,54 @@
#ifndef COMPILER_SCRIPTPARSER_H_INCLUDED
#define COMPILER_SCRIPTPARSER_H_INCLUDED
#include "parser.hpp"
#include "lineparser.hpp"
#include "controlparser.hpp"
#include "output.hpp"
namespace Compiler
{
class Locals;
// Script parser, to be used in dialogue scripts and as part of FileParser
class ScriptParser : public Parser
{
Output mOutput;
LineParser mLineParser;
ControlParser mControlParser;
bool mEnd;
public:
/// \param end of script is marked by end keyword.
ScriptParser (ErrorHandler& errorHandler, Context& context, Locals& locals,
bool end = false);
void getCode (std::vector<Interpreter::Type_Code>& code) const;
///< store generated code in \æ code.
virtual bool parseName (const std::string& name, const TokenLoc& loc,
Scanner& scanner);
///< Handle a name token.
/// \return fetch another token?
virtual bool parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner);
///< Handle a keyword token.
/// \return fetch another token?
virtual bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner);
///< Handle a special character token.
/// \return fetch another token?
virtual void parseEOF (Scanner& scanner);
///< Handle EOF token.
void reset();
///< Reset parser to clean state.
};
}
#endif

@ -0,0 +1,41 @@
#include "skipparser.hpp"
#include "scanner.hpp"
namespace Compiler
{
SkipParser::SkipParser (ErrorHandler& errorHandler, Context& context)
: Parser (errorHandler, context)
{}
bool SkipParser::parseInt (int value, const TokenLoc& loc, Scanner& scanner)
{
return true;
}
bool SkipParser::parseFloat (float value, const TokenLoc& loc, Scanner& scanner)
{
return true;
}
bool SkipParser::parseName (const std::string& name, const TokenLoc& loc,
Scanner& scanner)
{
return true;
}
bool SkipParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner)
{
return true;
}
bool SkipParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner)
{
if (code==Scanner::S_newline)
return false;
return true;
}
}

@ -0,0 +1,42 @@
#ifndef COMPILER_SKIPPARSER_H_INCLUDED
#define COMPILER_SKIPPARSER_H_INCLUDED
#include "parser.hpp"
namespace Compiler
{
// \brief Skip parser for skipping a line
//
// This parser is mainly intended for skipping the rest of a faulty line.
class SkipParser : public Parser
{
public:
SkipParser (ErrorHandler& errorHandler, Context& context);
virtual bool parseInt (int value, const TokenLoc& loc, Scanner& scanner);
///< Handle an int token.
/// \return fetch another token?
virtual bool parseFloat (float value, const TokenLoc& loc, Scanner& scanner);
///< Handle a float token.
/// \return fetch another token?
virtual bool parseName (const std::string& name, const TokenLoc& loc,
Scanner& scanner);
///< Handle a name token.
/// \return fetch another token?
virtual bool parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner);
///< Handle a keyword token.
/// \return fetch another token?
virtual bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner);
///< Handle a special character token.
/// \return fetch another token?
};
}
#endif

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save