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 screenshot*.png
*.o *.o
*.a
*~ *~
data data
CMakeFiles CMakeFiles
@ -13,4 +14,8 @@ build*
plugins.cfg plugins.cfg
openmw.cfg openmw.cfg
Doxygen Doxygen
.thumbnails
resources
mwcompiler
mwinterpreter
clientconsole

4
.gitmodules vendored

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

@ -6,62 +6,176 @@ cmake_minimum_required(VERSION 2.6)
# Add path for CMake scripts # Add path for CMake scripts
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/) set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/)
# Local files # source directory: apps
set(BSA bsa/bsa_archive.cpp bsa/bsa_file.cpp) set(GAME
set(BSA_HEADER bsa/bsa_archive.hpp bsa/bsa_file.hpp) apps/openmw/main.cpp
source_group(bsa FILES ${BSA} ${BSA_HEADER}) 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(GAMEINPUT)
set(NIF_HEADER nif/controlled.hpp nif/effect.hpp nif/nif_types.hpp nif/record.hpp set(GAMEINPUT_HEADER
nif/controller.hpp nif/extra.hpp nif/node.hpp nif/record_ptr.hpp apps/openmw/mwinput/inputmanager.hpp)
nif/data.hpp nif/nif_file.hpp nif/property.hpp) source_group(apps\\openmw\\mwinput FILES ${GAMEINPUT} ${GAMEINPUT_HEADER})
source_group(nif FILES ${NIF} ${NIF_HEADER})
set(NIFOGRE nifogre/ogre_nif_loader.cpp) set(GAMESCRIPT
set(NIFOGRE_HEADER nifogre/ogre_nif_loader.hpp) apps/openmw/mwscript/scriptmanager.cpp
source_group(nifogre FILES ${NIFOGRE} ${NIFOGRE_HEADER}) 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(GAMESOUND
set(TOOLS_HEADER tools/fileops.hpp tools/slice_array.hpp tools/stringops.hpp) apps/openmw/mwsound/soundmanager.cpp
source_group(tools FILES ${TOOLS} ${TOOLS_HEADER}) 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) set(GAMEWORLD
source_group(mangle_vfs FILES ${MANGLE_VFS}) 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(APPS ${GAME} ${GAMEREND} ${GAMEINPUT} ${GAMESCRIPT} ${GAMESOUND} ${GAMEWORLD})
set(INPUT_HEADER input/oismanager.hpp input/listener.hpp input/func_binder.hpp input/dispatch_map.hpp input/dispatcher.hpp input/poller.hpp) set(APPS_HEADER ${GAME_HEADER} ${GAMEREND_HEADER} ${GAMEINPUT_HEADER} ${GAMESCRIPT_HEADER}
source_group(input FILES ${INPUT} ${INPUT_HEADER}) ${GAMESOUND_HEADER} ${GAMEWORLD_HEADER})
set(GAME game/main.cpp game/engine.cpp) # source directory: components
set(GAME_HEADER game/mwinput/inputmanager.hpp game/engine.hpp)
source_group(game FILES ${GAME} ${GAME_HEADER}) 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(INPUT
set(ESM_STORE_HEADER esm_store/cell_store.hpp esm_store/reclists.hpp esm_store/store.hpp) components/engine/input/oismanager.cpp)
source_group(esm_store FILES ${ESM_STORE} ${ESM_STORE_HEADER}) set(INPUT_HEADER
components/engine/input/oismanager.hpp
set(GAMEREND game/mwrender/mwscene.cpp game/mwrender/cell.cpp game/mwrender/interior.cpp) components/engine/input/listener.hpp
set(GAMEREND_HEADER game/mwrender/cell.hpp game/mwrender/mwscene.hpp components/engine/input/func_binder.hpp
game/mwrender/interior.hpp game/mwrender/playerpos.hpp) components/engine/input/dispatch_map.hpp
source_group(game_renderer FILES ${GAMEREND} ${GAMEREND_HEADER}) components/engine/input/dispatcher.hpp
components/engine/input/poller.hpp)
set(ESM_HEADER esm/defs.hpp esm/loadcell.hpp esm/loadfact.hpp esm/loadltex.hpp source_group(components\\engine\\input FILES ${INPUT} ${INPUT_HEADER})
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(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 # Platform specific
if (WIN32) if (WIN32)
set(PLATFORM_INCLUDE_DIR "platform") set(PLATFORM_INCLUDE_DIR "platform")
@ -76,9 +190,12 @@ find_package(Boost REQUIRED COMPONENTS system filesystem program_options)
find_package(OIS REQUIRED) find_package(OIS REQUIRED)
include_directories("." include_directories("."
${OGRE_INCLUDE_DIR} ${OIS_INCLUDE_DIR} ${Boost_INCLUDE_DIR} ${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}) link_directories(${Boost_LIBRARY_DIRS} ${OGRE_LIB_DIR})
add_subdirectory( extern/caelum )
# Specify build paths # Specify build paths
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${OpenMW_BINARY_DIR}") set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${OpenMW_BINARY_DIR}")
@ -103,6 +220,11 @@ if (APPLE)
) )
endif (APPLE) endif (APPLE)
# Compiler settings
if (CMAKE_COMPILER_IS_GNUCC)
add_definitions (-Wall)
endif (CMAKE_COMPILER_IS_GNUCC)
# Main executable # Main executable
add_executable(openmw add_executable(openmw
${BSA} ${BSA_HEADER} ${BSA} ${BSA_HEADER}
@ -115,6 +237,10 @@ add_executable(openmw
${GAME} ${GAME}
${ESM_STORE} ${ESM_STORE_HEADER} ${ESM_STORE} ${ESM_STORE_HEADER}
${GAMEREND} ${GAMEREND_HEADER} ${GAMEREND} ${GAMEREND_HEADER}
MACOSX_BUNDLE
${COMPONENTS} ${COMPONENTS_HEADER}
${OPENMW_LIBS} ${OPENMW_LIBS_HEADER}
${APPS} ${APPS_HEADER}
${ESM_HEADER} ${ESM_HEADER}
${APPLE_BUNDLE_RESOURCES} ${APPLE_BUNDLE_RESOURCES}
) )
@ -122,13 +248,17 @@ add_executable(openmw
target_link_libraries(openmw target_link_libraries(openmw
${OGRE_LIBRARIES} ${OGRE_LIBRARIES}
${OIS_LIBRARIES} ${OIS_LIBRARIES}
${Boost_LIBRARIES}) ${Boost_LIBRARIES}
caelum)
if (APPLE) if (APPLE)
find_library(CARBON_FRAMEWORK Carbon) find_library(CARBON_FRAMEWORK Carbon)
target_link_libraries(openmw ${CARBON_FRAMEWORK}) target_link_libraries(openmw ${CARBON_FRAMEWORK})
endif (APPLE) endif (APPLE)
# Other apps and tools
add_subdirectory( apps/clientconsole )
# Apple bundling # Apple bundling
if (APPLE) if (APPLE)
set(MISC_FILES set(MISC_FILES
@ -155,3 +285,14 @@ set(CMAKE_EXE_LINKER_FLAGS "-arch i386")
set(CMAKE_CXX_FLAGS "-arch i386") set(CMAKE_CXX_FLAGS "-arch i386")
endif (APPLE) 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 # directories like "/usr/src/myproject". Separate the files or directories
# with spaces. # with spaces.
INPUT = .. INPUT = ..\apps
..\components
..\libs
..\docs
# This tag can be used to specify the character encoding of the source files # 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 # 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) 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"); "Syntax: openmw <options>\nAllowed options");
desc.add_options() desc.add_options()
("help", "print help message") ("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") "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") "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") "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 #if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
std::string configFilePath(macBundlePath() + "/Contents/MacOS/openmw.cfg"); 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"); std::ifstream configFile ("openmw.cfg");
#endif #endif
boost::program_options::parsed_options valid_opts = bpo::parsed_options valid_opts = bpo::command_line_parser(argc, argv).options(desc).allow_unregistered().run();
boost::program_options::command_line_parser(argc, argv).options(desc).allow_unregistered().run();
boost::program_options::store(valid_opts, variables); bpo::store(valid_opts, variables);
boost::program_options::notify(variables); bpo::notify(variables);
if (configFile.is_open()) if (configFile.is_open())
boost::program_options::store ( bpo::store ( bpo::parse_config_file (configFile, desc), variables);
boost::program_options::parse_config_file (configFile, desc), variables);
if (variables.count ("help")) 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.setDataDir (variables["data"].as<std::string>());
engine.setCell (variables["start"].as<std::string>()); engine.setCell (variables["start"].as<std::string>());
engine.addMaster (variables["master"].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; return true;
} }

@ -1,12 +1,12 @@
#ifndef _MWINPUT_MWINPUTMANAGER_H #ifndef _MWINPUT_MWINPUTMANAGER_H
#define _MWINPUT_MWINPUTMANAGER_H #define _MWINPUT_MWINPUTMANAGER_H
#include "input/listener.hpp" #include "components/engine/input/listener.hpp"
#include "input/dispatcher.hpp" #include "components/engine/input/dispatcher.hpp"
#include "input/poller.hpp" #include "components/engine/input/poller.hpp"
#include "boost/bind.hpp" #include "boost/bind.hpp"
#include "game/mwrender/playerpos.hpp" #include "apps/openmw/mwrender/playerpos.hpp"
#include "platform/strings.h" #include "libs/platform/strings.h"
namespace MWInput namespace MWInput
{ {
@ -47,7 +47,7 @@ namespace MWInput
{ {
// Find the first unused filename. // Find the first unused filename.
// //
char buf[50]; char buf[50];
do do
{ {
snprintf(buf, 50, "screenshot%03d.png", shotCount++); snprintf(buf, 50, "screenshot%03d.png", shotCount++);
@ -58,10 +58,10 @@ namespace MWInput
public: public:
MWInputManager(Render::OgreRenderer &_ogre, MWInputManager(Render::OgreRenderer &_ogre,
MWRender::PlayerPos &_player) MWRender::PlayerPos &_player, bool debug)
: disp(A_LAST), : disp(A_LAST),
ogre(_ogre), ogre(_ogre),
input(_ogre), input(_ogre, debug),
poller(input), poller(input),
listener(_ogre, input, disp), listener(_ogre, input, disp),
player(_player), 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 <cassert>
#include "esm_store/cell_store.hpp"
using namespace MWRender; using namespace MWRender;
template<typename T> template<typename T>
void insertObj(CellRender& cellRender, const T& liveRef) void insertObj(CellRenderImp& cellRender, T& liveRef)
{ {
assert (liveRef.base != NULL); assert (liveRef.base != NULL);
const std::string &model = liveRef.base->model; const std::string &model = liveRef.base->model;
@ -15,21 +13,21 @@ void insertObj(CellRender& cellRender, const T& liveRef)
{ {
cellRender.insertBegin (liveRef.ref); cellRender.insertBegin (liveRef.ref);
cellRender.insertMesh ("meshes\\" + model); cellRender.insertMesh ("meshes\\" + model);
cellRender.insertEnd(); liveRef.mData.setHandle (cellRender.insertEnd());
} }
} }
template<> template<>
void insertObj(CellRender& cellRender, const ESMS::LiveCellRef<ESM::Light>& liveRef) void insertObj(CellRenderImp& cellRender, ESMS::LiveCellRef<ESM::Light, MWWorld::RefData>& liveRef)
{ {
assert (liveRef.base != NULL); assert (liveRef.base != NULL);
const std::string &model = liveRef.base->model; const std::string &model = liveRef.base->model;
if(!model.empty()) if(!model.empty())
{ {
cellRender.insertBegin (liveRef.ref); cellRender.insertBegin (liveRef.ref);
cellRender.insertMesh ("meshes\\" + model); cellRender.insertMesh ("meshes\\" + model);
// Extract the color and convert to floating point // Extract the color and convert to floating point
const int color = liveRef.base->data.color; const int color = liveRef.base->data.color;
const float r = ((color >> 0) & 0xFF) / 255.0f; 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 b = ((color >> 16) & 0xFF) / 255.0f;
const float radius = float(liveRef.base->data.radius); const float radius = float(liveRef.base->data.radius);
cellRender.insertLight(r, g, b, radius); cellRender.insertLight(r, g, b, radius);
cellRender.insertEnd(); liveRef.mData.setHandle (cellRender.insertEnd());
} }
} }
template<typename T> 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++) it != cellRefList.list.end(); it++)
{ {
insertObj (cellRender, *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 // Loop through all references in the cell
insertCellRefList (*this, cell.activators); insertCellRefList (*this, cell.activators);

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

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

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

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

@ -15,7 +15,7 @@ namespace MWRender
public: public:
PlayerPos(Ogre::Camera *cam) : 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. // Set the player position. Uses Morrowind coordinates.
void setPos(float _x, float _y, float _z) 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 <OgreArchiveFactory.h>
#include <OgreArchiveManager.h> #include <OgreArchiveManager.h>
#include "bsa_file.hpp" #include "bsa_file.hpp"
#include <mangle/stream/clients/ogre_datastream.hpp> #include <libs/mangle/stream/clients/ogre_datastream.hpp>
using namespace Ogre; using namespace Ogre;
using namespace Mangle::Stream; using namespace Mangle::Stream;
@ -137,7 +137,7 @@ public:
return name; return name;
} }
Archive *createInstance( const String& name ) Archive *createInstance( const String& name )
{ {
return new BSAArchive(name); return new BSAArchive(name);
} }

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

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