diff --git a/.gitignore b/.gitignore index 646185021..30ddac5ba 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ screenshot*.png *.o +*.a *~ data CMakeFiles @@ -13,4 +14,8 @@ build* plugins.cfg openmw.cfg Doxygen - +.thumbnails +resources +mwcompiler +mwinterpreter +clientconsole diff --git a/.gitmodules b/.gitmodules index 5cf974020..1e2f38738 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ -[submodule "mangle"] - path = mangle +[submodule "libs/mangle"] + path = libs/mangle url = git://github.com/korslund/mangle.git diff --git a/CMakeLists.txt b/CMakeLists.txt index ca8c61d13..436824a15 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,62 +6,176 @@ cmake_minimum_required(VERSION 2.6) # Add path for CMake scripts set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/) -# Local files +# source directory: apps -set(BSA bsa/bsa_archive.cpp bsa/bsa_file.cpp) -set(BSA_HEADER bsa/bsa_archive.hpp bsa/bsa_file.hpp) -source_group(bsa FILES ${BSA} ${BSA_HEADER}) +set(GAME + apps/openmw/main.cpp + apps/openmw/engine.cpp) +set(GAME_HEADER + apps/openmw/engine.hpp) +source_group(game FILES ${GAME} ${GAME_HEADER}) + +set(GAMEREND + apps/openmw/mwrender/mwscene.cpp + apps/openmw/mwrender/cellimp.cpp + apps/openmw/mwrender/interior.cpp + apps/openmw/mwrender/sky.cpp) +set(GAMEREND_HEADER + apps/openmw/mwrender/cell.hpp + apps/openmw/mwrender/cellimp.hpp + apps/openmw/mwrender/mwscene.hpp + apps/openmw/mwrender/interior.hpp + apps/openmw/mwrender/playerpos.hpp + apps/openmw/mwrender/sky.hpp) +source_group(apps\\openmw\\mwrender FILES ${GAMEREND} ${GAMEREND_HEADER}) -set(NIF nif/nif_file.cpp) -set(NIF_HEADER nif/controlled.hpp nif/effect.hpp nif/nif_types.hpp nif/record.hpp - nif/controller.hpp nif/extra.hpp nif/node.hpp nif/record_ptr.hpp - nif/data.hpp nif/nif_file.hpp nif/property.hpp) -source_group(nif FILES ${NIF} ${NIF_HEADER}) +# set(GAMEINPUT) +set(GAMEINPUT_HEADER + apps/openmw/mwinput/inputmanager.hpp) +source_group(apps\\openmw\\mwinput FILES ${GAMEINPUT} ${GAMEINPUT_HEADER}) -set(NIFOGRE nifogre/ogre_nif_loader.cpp) -set(NIFOGRE_HEADER nifogre/ogre_nif_loader.hpp) -source_group(nifogre FILES ${NIFOGRE} ${NIFOGRE_HEADER}) +set(GAMESCRIPT + apps/openmw/mwscript/scriptmanager.cpp + apps/openmw/mwscript/compilercontext.cpp + apps/openmw/mwscript/interpretercontext.cpp + apps/openmw/mwscript/cellextensions.cpp + apps/openmw/mwscript/extensions.cpp + apps/openmw/mwscript/globalscripts.cpp + ) +set(GAMESCRIPT_HEADER + apps/openmw/mwscript/locals.hpp + apps/openmw/mwscript/scriptmanager.hpp + apps/openmw/mwscript/compilercontext.hpp + apps/openmw/mwscript/interpretercontext.hpp + apps/openmw/mwscript/cellextensions.hpp + apps/openmw/mwscript/extensions.hpp + apps/openmw/mwscript/globalscripts.hpp + ) +source_group(apps\\openmw\\mwscript FILES ${GAMESCRIPT} ${GAMESCRIPT_HEADER}) -set(TOOLS tools/stringops.cpp tools/fileops.cpp) -set(TOOLS_HEADER tools/fileops.hpp tools/slice_array.hpp tools/stringops.hpp) -source_group(tools FILES ${TOOLS} ${TOOLS_HEADER}) +set(GAMESOUND + apps/openmw/mwsound/soundmanager.cpp + apps/openmw/mwsound/extensions.cpp) +set(GAMESOUND_HEADER + apps/openmw/mwsound/soundmanager.hpp + apps/openmw/mwsound/extensions.hpp) +source_group(apps\\openmw\\mwsound FILES ${GAMESOUND} ${GAMESOUND_HEADER}) -set(MANGLE_VFS mangle/vfs/servers/ogre_vfs.cpp) -source_group(mangle_vfs FILES ${MANGLE_VFS}) +set(GAMEWORLD + apps/openmw/mwworld/world.cpp) +set(GAMEWORLD_HEADER + apps/openmw/mwworld/refdata.hpp + apps/openmw/mwworld/world.hpp + apps/openmw/mwworld/ptr.hpp + apps/openmw/mwworld/environment.hpp + ) +source_group(apps\\openmw\\mwworld FILES ${GAMEWORLD} ${GAMEWORLD_HEADER}) -set(OGRE ogre/renderer.cpp) -set(OGRE_HEADER ogre/renderer.hpp) -source_group(ogre FILES ${OGRE} ${OGRE_HEADER}) -set(INPUT input/oismanager.cpp) -set(INPUT_HEADER input/oismanager.hpp input/listener.hpp input/func_binder.hpp input/dispatch_map.hpp input/dispatcher.hpp input/poller.hpp) -source_group(input FILES ${INPUT} ${INPUT_HEADER}) +set(APPS ${GAME} ${GAMEREND} ${GAMEINPUT} ${GAMESCRIPT} ${GAMESOUND} ${GAMEWORLD}) +set(APPS_HEADER ${GAME_HEADER} ${GAMEREND_HEADER} ${GAMEINPUT_HEADER} ${GAMESCRIPT_HEADER} + ${GAMESOUND_HEADER} ${GAMEWORLD_HEADER}) -set(GAME game/main.cpp game/engine.cpp) -set(GAME_HEADER game/mwinput/inputmanager.hpp game/engine.hpp) -source_group(game FILES ${GAME} ${GAME_HEADER}) +# source directory: components + +set(BSA + components/bsa/bsa_archive.cpp + components/bsa/bsa_file.cpp) +set(BSA_HEADER + components/bsa/bsa_archive.hpp + components/bsa/bsa_file.hpp) +source_group(components\\bsa FILES ${BSA} ${BSA_HEADER}) + +set(NIF + components/nif/nif_file.cpp) +set(NIF_HEADER + components/nif/controlled.hpp + components/nif/effect.hpp + components/nif/nif_types.hpp + components/nif/record.hpp + components/nif/controller.hpp + components/nif/extra.hpp + components/nif/node.hpp + components/nif/record_ptr.hpp + components/nif/data.hpp + components/nif/nif_file.hpp + components/nif/property.hpp) +source_group(components\\nif FILES ${NIF} ${NIF_HEADER}) + +set(NIFOGRE + components/nifogre/ogre_nif_loader.cpp) +set(NIFOGRE_HEADER + components/nifogre/ogre_nif_loader.hpp) +source_group(components\\nifogre FILES ${NIFOGRE} ${NIFOGRE_HEADER}) + +set(ESM_STORE + components/esm_store/store.cpp) +set(ESM_STORE_HEADER + components/esm_store/cell_store.hpp + components/esm_store/reclists.hpp + components/esm_store/store.hpp) +source_group(components\\esm_store FILES ${ESM_STORE} ${ESM_STORE_HEADER}) + +file(GLOB ESM_HEADER components/esm/*.hpp) +source_group(components\\esm FILES ${ESM_HEADER}) + +set(OGRE + components/engine/ogre/renderer.cpp) +set(OGRE_HEADER + components/engine/ogre/renderer.hpp) +source_group(components\\engine\\ogre FILES ${OGRE} ${OGRE_HEADER}) -set(ESM_STORE esm_store/store.cpp esm_store/cell_store.cpp) -set(ESM_STORE_HEADER esm_store/cell_store.hpp esm_store/reclists.hpp esm_store/store.hpp) -source_group(esm_store FILES ${ESM_STORE} ${ESM_STORE_HEADER}) - -set(GAMEREND game/mwrender/mwscene.cpp game/mwrender/cell.cpp game/mwrender/interior.cpp) -set(GAMEREND_HEADER game/mwrender/cell.hpp game/mwrender/mwscene.hpp - game/mwrender/interior.hpp game/mwrender/playerpos.hpp) -source_group(game_renderer FILES ${GAMEREND} ${GAMEREND_HEADER}) - -set(ESM_HEADER esm/defs.hpp esm/loadcell.hpp esm/loadfact.hpp esm/loadltex.hpp - esm/loadskil.hpp - esm/esm_reader.hpp esm/loadclas.hpp esm/loadglob.hpp esm/loadmgef.hpp esm/loadsndg.hpp - esm/loadacti.hpp esm/loadclot.hpp esm/loadgmst.hpp esm/loadmisc.hpp esm/loadsoun.hpp - esm/loadalch.hpp esm/loadcont.hpp esm/loadinfo.hpp esm/loadnpcc.hpp esm/loadspel.hpp - esm/loadappa.hpp esm/loadcrea.hpp esm/loadingr.hpp esm/loadnpc.hpp esm/loadsscr.hpp - esm/loadarmo.hpp esm/loadcrec.hpp esm/loadland.hpp esm/loadpgrd.hpp esm/loadstat.hpp - esm/loadbody.hpp esm/loaddial.hpp esm/loadlevlist.hpp esm/loadrace.hpp esm/loadweap.hpp - esm/loadbook.hpp esm/loaddoor.hpp esm/loadligh.hpp esm/loadregn.hpp esm/records.hpp - esm/loadbsgn.hpp esm/loadench.hpp esm/loadlocks.hpp esm/loadscpt.hpp) -source_group(esm_header FILES ${ESM_HEADER}) +set(INPUT + components/engine/input/oismanager.cpp) +set(INPUT_HEADER + components/engine/input/oismanager.hpp + components/engine/input/listener.hpp + components/engine/input/func_binder.hpp + components/engine/input/dispatch_map.hpp + components/engine/input/dispatcher.hpp + components/engine/input/poller.hpp) +source_group(components\\engine\\input FILES ${INPUT} ${INPUT_HEADER}) +set(COMMANDSERVER + components/commandserver/command.hpp + components/commandserver/server.hpp + components/commandserver/server.cpp) +source_group(components\\commandserver FILES ${COMMANDSERVER}) + +set(MISC + components/misc/stringops.cpp + components/misc/fileops.cpp) +set(MISC_HEADER + components/misc/fileops.hpp + components/misc/slice_array.hpp + components/misc/stringops.hpp + components/misc/tsdeque.hpp) +source_group(components\\misc FILES ${MISC} ${MISC_HEADER}) + +file(GLOB COMPILER components/compiler/*.cpp) +file(GLOB COMPILER_HEADER components/compiler/*.hpp) +source_group(components\\compiler FILES ${COMPILER} ${COMPILER_HEADER}) + +file(GLOB INTERPRETER components/interpreter/*.cpp) +file(GLOB INTERPRETER_HEADER components/interpreter/*.hpp) +source_group(components\\interpreter FILES ${INTERPRETER} ${INTERPRETER_HEADER}) + +set(COMPONENTS ${BSA} ${NIF} ${NIFOGRE} ${ESM_STORE} ${OGRE} ${INPUT} ${MISC} + ${COMMANDSERVER} + ${COMPILER} + ${INTERPRETER}) +set(COMPONENTS_HEADER ${BSA_HEADER} ${NIF_HEADER} ${NIFOGRE_HEADER} ${ESM_STORE_HEADER} + ${ESM_HEADER} ${OGRE_HEADER} ${INPUT_HEADER} ${MISC_HEADER} ${COMPILER_HEADER} + ${INTERPRETER_HEADER}) + +# source directory: libs + +set(MANGLE_VFS libs/mangle/vfs/servers/ogre_vfs.cpp) +source_group(libs\\mangle_vfs FILES ${MANGLE_VFS}) + +set(OPENMW_LIBS ${MANGLE_VFS}) +set(OPENMW_LIBS_HEADER) + # Platform specific if (WIN32) set(PLATFORM_INCLUDE_DIR "platform") @@ -76,9 +190,12 @@ find_package(Boost REQUIRED COMPONENTS system filesystem program_options) find_package(OIS REQUIRED) include_directories("." ${OGRE_INCLUDE_DIR} ${OIS_INCLUDE_DIR} ${Boost_INCLUDE_DIR} - ${PLATFORM_INCLUDE_DIR}) + ${PLATFORM_INCLUDE_DIR} + ${CMAKE_HOME_DIRECTORY}/extern/caelum/include) link_directories(${Boost_LIBRARY_DIRS} ${OGRE_LIB_DIR}) +add_subdirectory( extern/caelum ) + # Specify build paths set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${OpenMW_BINARY_DIR}") @@ -103,6 +220,11 @@ if (APPLE) ) endif (APPLE) +# Compiler settings +if (CMAKE_COMPILER_IS_GNUCC) + add_definitions (-Wall) +endif (CMAKE_COMPILER_IS_GNUCC) + # Main executable add_executable(openmw ${BSA} ${BSA_HEADER} @@ -115,6 +237,10 @@ add_executable(openmw ${GAME} ${ESM_STORE} ${ESM_STORE_HEADER} ${GAMEREND} ${GAMEREND_HEADER} + MACOSX_BUNDLE + ${COMPONENTS} ${COMPONENTS_HEADER} + ${OPENMW_LIBS} ${OPENMW_LIBS_HEADER} + ${APPS} ${APPS_HEADER} ${ESM_HEADER} ${APPLE_BUNDLE_RESOURCES} ) @@ -122,13 +248,17 @@ add_executable(openmw target_link_libraries(openmw ${OGRE_LIBRARIES} ${OIS_LIBRARIES} - ${Boost_LIBRARIES}) + ${Boost_LIBRARIES} + caelum) if (APPLE) find_library(CARBON_FRAMEWORK Carbon) target_link_libraries(openmw ${CARBON_FRAMEWORK}) endif (APPLE) +# Other apps and tools +add_subdirectory( apps/clientconsole ) + # Apple bundling if (APPLE) set(MISC_FILES @@ -155,3 +285,14 @@ set(CMAKE_EXE_LINKER_FLAGS "-arch i386") set(CMAKE_CXX_FLAGS "-arch i386") endif (APPLE) + +# Tools +option(BUILD_MWCOMPILER "build standalone Morrowind script compiler" ON) +if (BUILD_MWCOMPILER) + add_subdirectory( apps/mwcompiler ) +endif() + +option(BUILD_MWINTERPRETER "build standalone Morrowind script code interpreter" ON) +if (BUILD_MWINTERPRETER) + add_subdirectory( apps/mwinterpreter ) +endif() diff --git a/Docs/Doxyfile b/Docs/Doxyfile index f1f5fda07..5adeea871 100644 --- a/Docs/Doxyfile +++ b/Docs/Doxyfile @@ -573,7 +573,10 @@ WARN_LOGFILE = # directories like "/usr/src/myproject". Separate the files or directories # with spaces. -INPUT = .. +INPUT = ..\apps + ..\components + ..\libs + ..\docs # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is diff --git a/apps/clientconsole/CMakeLists.txt b/apps/clientconsole/CMakeLists.txt new file mode 100644 index 000000000..f73610863 --- /dev/null +++ b/apps/clientconsole/CMakeLists.txt @@ -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}) diff --git a/apps/clientconsole/client.cpp b/apps/clientconsole/client.cpp new file mode 100755 index 000000000..6d3e0ccc4 --- /dev/null +++ b/apps/clientconsole/client.cpp @@ -0,0 +1,142 @@ +#include +#include +#include + +#include + +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 packet(plen); + Header* pHeader = reinterpret_cast(&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 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; +} diff --git a/apps/mwcompiler/CMakeLists.txt b/apps/mwcompiler/CMakeLists.txt new file mode 100644 index 000000000..b2243f255 --- /dev/null +++ b/apps/mwcompiler/CMakeLists.txt @@ -0,0 +1,9 @@ +project(MWCompiler) + +set(TOOLS_MWCOMPILER ${COMPILER} + main.cpp + context.cpp + context.hpp) + +add_executable(mwcompiler ${TOOLS_MWCOMPILER}) + diff --git a/apps/mwcompiler/context.cpp b/apps/mwcompiler/context.cpp new file mode 100644 index 000000000..895e2c62a --- /dev/null +++ b/apps/mwcompiler/context.cpp @@ -0,0 +1,16 @@ + +#include "context.hpp" + +namespace SACompiler +{ + bool Context::canDeclareLocals() const + { + return true; + } + + char Context::getGlobalType (const std::string& name) const + { + return ' '; + } +} + diff --git a/apps/mwcompiler/context.hpp b/apps/mwcompiler/context.hpp new file mode 100644 index 000000000..be06bbca8 --- /dev/null +++ b/apps/mwcompiler/context.hpp @@ -0,0 +1,21 @@ +#ifndef MWCOMPILER_CONTEXT_H_INCLUDED +#define MWCOMPILER_CONTEXT_H_INCLUDED + +#include + +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 + diff --git a/apps/mwcompiler/main.cpp b/apps/mwcompiler/main.cpp new file mode 100644 index 000000000..a19a50ab3 --- /dev/null +++ b/apps/mwcompiler/main.cpp @@ -0,0 +1,78 @@ +// Stand-alone MW-script compiler + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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 code; + parser.getCode (code); + + std::ofstream codeFile ((filename + ".code").c_str()); + + codeFile.write (reinterpret_cast (&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; + } +} + diff --git a/apps/mwinterpreter/CMakeLists.txt b/apps/mwinterpreter/CMakeLists.txt new file mode 100644 index 000000000..078925657 --- /dev/null +++ b/apps/mwinterpreter/CMakeLists.txt @@ -0,0 +1,9 @@ +project(MWInterpreter) + +set(TOOLS_MWINTERPRETER + ${INTERPRETER} + main.cpp + context.cpp + context.hpp) + +add_executable(mwinterpreter ${TOOLS_MWINTERPRETER}) diff --git a/apps/mwinterpreter/context.cpp b/apps/mwinterpreter/context.cpp new file mode 100644 index 000000000..9119521fb --- /dev/null +++ b/apps/mwinterpreter/context.cpp @@ -0,0 +1,137 @@ + +#include "context.hpp" + +#include +#include +#include +#include + +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> 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& buttons) + { + std::cout << "message box: " << message << std::endl; + for (std::size_t i=0; i::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::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::const_iterator iter (mFloats.begin()); + iter!=mFloats.end(); ++iter) + std::cout << mNames[i++] << ": " << *iter << std::endl; + + } +} + diff --git a/apps/mwinterpreter/context.hpp b/apps/mwinterpreter/context.hpp new file mode 100644 index 000000000..217b2a1d4 --- /dev/null +++ b/apps/mwinterpreter/context.hpp @@ -0,0 +1,66 @@ +#ifndef SAINTERPRETER_CONTEXT_H_INCLUDED +#define SAINTERPRETER_CONTEXT_H_INCLUDED + +#include +#include + +#include +#include + +namespace SAInterpreter +{ + class Context : public Interpreter::Context + { + std::vector mShorts; + std::vector mLongs; + std::vector mFloats; + std::vector 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& 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 diff --git a/apps/mwinterpreter/main.cpp b/apps/mwinterpreter/main.cpp new file mode 100644 index 000000000..e8f0c8783 --- /dev/null +++ b/apps/mwinterpreter/main.cpp @@ -0,0 +1,57 @@ +// Stand-alone MW-script code interpreter + +#include +#include +#include +#include + +#include +#include +#include +#include + +#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 code (4); + + codefile.read (reinterpret_cast (&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 (&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; + } +} + diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp new file mode 100644 index 000000000..30cae5897 --- /dev/null +++ b/apps/openmw/engine.cpp @@ -0,0 +1,217 @@ +#include "engine.hpp" + +#include + +#include + +#include +#include + +#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"; +} + + diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp new file mode 100644 index 000000000..f0b46c6ab --- /dev/null +++ b/apps/openmw/engine.hpp @@ -0,0 +1,112 @@ +#ifndef ENGINE_H +#define ENGINE_H + +#include + +#include + +#include + +#include +#include +#include +#include +#include + +#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 mCommandQueue; + std::auto_ptr 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 diff --git a/game/main.cpp b/apps/openmw/main.cpp similarity index 59% rename from game/main.cpp rename to apps/openmw/main.cpp index 3cb6b5b38..83de17d7a 100644 --- a/game/main.cpp +++ b/apps/openmw/main.cpp @@ -16,20 +16,26 @@ using namespace std; bool parseOptions (int argc, char**argv, OMW::Engine& engine) { - boost::program_options::options_description desc ( + // Create a local alias for brevity + namespace bpo = boost::program_options; + + bpo::options_description desc ( "Syntax: openmw \nAllowed options"); desc.add_options() ("help", "print help message") - ("data", boost::program_options::value()->default_value ("data"), + ("data", bpo::value()->default_value ("data"), "set data directory") - ("start", boost::program_options::value()->default_value ("Beshara"), + ("start", bpo::value()->default_value ("Beshara"), "set initial cell (only interior cells supported at the moment") - ("master", boost::program_options::value()->default_value ("Morrowind"), + ("master", bpo::value()->default_value ("Morrowind"), "master file") + ( "debug", "debug mode" ) + ( "script-verbose", "verbose script output" ) + ( "new-game", "activate char gen/new game mechanics" ) ; - boost::program_options::variables_map variables; + bpo::variables_map variables; #if OGRE_PLATFORM == OGRE_PLATFORM_APPLE std::string configFilePath(macBundlePath() + "/Contents/MacOS/openmw.cfg"); @@ -38,15 +44,13 @@ bool parseOptions (int argc, char**argv, OMW::Engine& engine) std::ifstream configFile ("openmw.cfg"); #endif - boost::program_options::parsed_options valid_opts = - boost::program_options::command_line_parser(argc, argv).options(desc).allow_unregistered().run(); + bpo::parsed_options valid_opts = bpo::command_line_parser(argc, argv).options(desc).allow_unregistered().run(); - boost::program_options::store(valid_opts, variables); - boost::program_options::notify(variables); + bpo::store(valid_opts, variables); + bpo::notify(variables); if (configFile.is_open()) - boost::program_options::store ( - boost::program_options::parse_config_file (configFile, desc), variables); + bpo::store ( bpo::parse_config_file (configFile, desc), variables); if (variables.count ("help")) { @@ -57,7 +61,16 @@ bool parseOptions (int argc, char**argv, OMW::Engine& engine) engine.setDataDir (variables["data"].as()); engine.setCell (variables["start"].as()); engine.addMaster (variables["master"].as()); + + if (variables.count ("debug")) + engine.enableDebugMode(); + + if (variables.count ("script-verbose")) + engine.enableVerboseScripts(); + if (variables.count ("new-game")) + engine.setNewGame(); + return true; } diff --git a/game/mwinput/inputmanager.hpp b/apps/openmw/mwinput/inputmanager.hpp similarity index 91% rename from game/mwinput/inputmanager.hpp rename to apps/openmw/mwinput/inputmanager.hpp index 5c2c9857f..81cf21dd3 100644 --- a/game/mwinput/inputmanager.hpp +++ b/apps/openmw/mwinput/inputmanager.hpp @@ -1,12 +1,12 @@ #ifndef _MWINPUT_MWINPUTMANAGER_H #define _MWINPUT_MWINPUTMANAGER_H -#include "input/listener.hpp" -#include "input/dispatcher.hpp" -#include "input/poller.hpp" +#include "components/engine/input/listener.hpp" +#include "components/engine/input/dispatcher.hpp" +#include "components/engine/input/poller.hpp" #include "boost/bind.hpp" -#include "game/mwrender/playerpos.hpp" -#include "platform/strings.h" +#include "apps/openmw/mwrender/playerpos.hpp" +#include "libs/platform/strings.h" namespace MWInput { @@ -47,7 +47,7 @@ namespace MWInput { // Find the first unused filename. // - char buf[50]; + char buf[50]; do { snprintf(buf, 50, "screenshot%03d.png", shotCount++); @@ -58,10 +58,10 @@ namespace MWInput public: MWInputManager(Render::OgreRenderer &_ogre, - MWRender::PlayerPos &_player) + MWRender::PlayerPos &_player, bool debug) : disp(A_LAST), ogre(_ogre), - input(_ogre), + input(_ogre, debug), poller(input), listener(_ogre, input, disp), player(_player), diff --git a/game/mwrender/.gitignore b/apps/openmw/mwrender/.gitignore similarity index 100% rename from game/mwrender/.gitignore rename to apps/openmw/mwrender/.gitignore diff --git a/apps/openmw/mwrender/cell.hpp b/apps/openmw/mwrender/cell.hpp new file mode 100644 index 000000000..756ec1cda --- /dev/null +++ b/apps/openmw/mwrender/cell.hpp @@ -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 + diff --git a/game/mwrender/cell.cpp b/apps/openmw/mwrender/cellimp.cpp similarity index 77% rename from game/mwrender/cell.cpp rename to apps/openmw/mwrender/cellimp.cpp index 396e24cf5..7a8de957e 100644 --- a/game/mwrender/cell.cpp +++ b/apps/openmw/mwrender/cellimp.cpp @@ -1,13 +1,11 @@ -#include "cell.hpp" +#include "cellimp.hpp" #include -#include "esm_store/cell_store.hpp" - using namespace MWRender; -template -void insertObj(CellRender& cellRender, const T& liveRef) +template +void insertObj(CellRenderImp& cellRender, T& liveRef) { assert (liveRef.base != NULL); const std::string &model = liveRef.base->model; @@ -15,21 +13,21 @@ void insertObj(CellRender& cellRender, const T& liveRef) { cellRender.insertBegin (liveRef.ref); cellRender.insertMesh ("meshes\\" + model); - cellRender.insertEnd(); + liveRef.mData.setHandle (cellRender.insertEnd()); } } - -template<> -void insertObj(CellRender& cellRender, const ESMS::LiveCellRef& liveRef) + +template<> +void insertObj(CellRenderImp& cellRender, ESMS::LiveCellRef& liveRef) { assert (liveRef.base != NULL); const std::string &model = liveRef.base->model; if(!model.empty()) { cellRender.insertBegin (liveRef.ref); - + cellRender.insertMesh ("meshes\\" + model); - + // Extract the color and convert to floating point const int color = liveRef.base->data.color; const float r = ((color >> 0) & 0xFF) / 255.0f; @@ -37,22 +35,22 @@ void insertObj(CellRender& cellRender, const ESMS::LiveCellRef& live const float b = ((color >> 16) & 0xFF) / 255.0f; const float radius = float(liveRef.base->data.radius); cellRender.insertLight(r, g, b, radius); - - cellRender.insertEnd(); - } + + liveRef.mData.setHandle (cellRender.insertEnd()); + } } template -void insertCellRefList (CellRender& cellRender, const T& cellRefList) +void insertCellRefList (CellRenderImp& cellRender, T& cellRefList) { - for(typename T::List::const_iterator it = cellRefList.list.begin(); + for(typename T::List::iterator it = cellRefList.list.begin(); it != cellRefList.list.end(); it++) { insertObj (cellRender, *it); - } -} + } +} -void CellRender::insertCell(const ESMS::CellStore &cell) +void CellRenderImp::insertCell(ESMS::CellStore &cell) { // Loop through all references in the cell insertCellRefList (*this, cell.activators); diff --git a/game/mwrender/cell.hpp b/apps/openmw/mwrender/cellimp.hpp similarity index 66% rename from game/mwrender/cell.hpp rename to apps/openmw/mwrender/cellimp.hpp index 0ddec0964..88406bb4f 100644 --- a/game/mwrender/cell.hpp +++ b/apps/openmw/mwrender/cellimp.hpp @@ -1,31 +1,30 @@ -#ifndef _GAME_RENDER_CELL_H -#define _GAME_RENDER_CELL_H +#ifndef _GAME_RENDER_CELLIMP_H +#define _GAME_RENDER_CELLIMP_H #include +#include "components/esm_store/cell_store.hpp" + +#include "../mwworld/refdata.hpp" + namespace ESM { class CellRef; } -namespace ESMS -{ - class CellStore; -} - namespace MWRender { /// Base class for cell render, that implements inserting references into a cell in a /// cell type- and render-engine-independent way. - class CellRender + class CellRenderImp { public: - CellRender() {} - virtual ~CellRender() {} + CellRenderImp() {} + virtual ~CellRenderImp() {} /// start inserting a new reference. - virtual void insertBegin (const ESM::CellRef &ref) = 0; + virtual void insertBegin (ESM::CellRef &ref) = 0; /// insert a mesh related to the most recent insertBegin call. virtual void insertMesh(const std::string &mesh) = 0; @@ -36,7 +35,8 @@ namespace MWRender /// finish inserting a new reference and return a handle to it. virtual std::string insertEnd() = 0; - void insertCell(const ESMS::CellStore &cell); + void insertCell(ESMS::CellStore &cell); + }; } diff --git a/game/mwrender/interior.cpp b/apps/openmw/mwrender/interior.cpp similarity index 95% rename from game/mwrender/interior.cpp rename to apps/openmw/mwrender/interior.cpp index 684e7cf63..5cf38a5a1 100644 --- a/game/mwrender/interior.cpp +++ b/apps/openmw/mwrender/interior.cpp @@ -3,7 +3,7 @@ #include #include -#include "nifogre/ogre_nif_loader.hpp" +#include "components/nifogre/ogre_nif_loader.hpp" #include "mwscene.hpp" using namespace MWRender; @@ -11,7 +11,7 @@ using namespace Ogre; using namespace ESMS; bool InteriorCellRender::lightConst = false; -float InteriorCellRender::lightConstValue = 0.0; +float InteriorCellRender::lightConstValue = 0.0f; bool InteriorCellRender::lightLinear = true; int InteriorCellRender::lightLinearMethod = 1; @@ -27,10 +27,10 @@ bool InteriorCellRender::lightOutQuadInLin = false; // start inserting a new reference. -void InteriorCellRender::insertBegin (const ESM::CellRef &ref) +void InteriorCellRender::insertBegin (ESM::CellRef &ref) { assert (!insert); - + // Create and place scene node for this object insert = base->createChildSceneNode(); @@ -59,7 +59,7 @@ void InteriorCellRender::insertBegin (const ESM::CellRef &ref) void InteriorCellRender::insertMesh(const std::string &mesh) { assert (insert); - + NIFLoader::load(mesh); MovableObject *ent = scene.getMgr()->createEntity(mesh); insert->attachObject(ent); @@ -74,7 +74,7 @@ void InteriorCellRender::insertLight(float r, float g, float b, float radius) light->setDiffuseColour (r, g, b); float cval=0.0f, lval=0.0f, qval=0.0f; - + if(lightConst) cval = lightConstValue; if(!lightOutQuadInLin) @@ -95,9 +95,9 @@ void InteriorCellRender::insertLight(float r, float g, float b, float radius) // Do quadratic or linear, depending if we're in an exterior or interior // cell, respectively. Ignore lightLinear and lightQuadratic. } - + light->setAttenuation(10*radius, cval, lval, qval); - + insert->attachObject(light); } @@ -108,19 +108,19 @@ std::string InteriorCellRender::insertEnd() assert (insert); std::string handle = insert->getName(); - + insert = 0; - + return handle; -} - +} + // configure lighting according to cell void InteriorCellRender::configureAmbient() { ambientColor.setAsABGR (cell.cell->ambi.ambient); setAmbientMode(); - + // Create a "sun" that shines light downwards. It doesn't look // completely right, but leave it for now. Ogre::Light *light = scene.getMgr()->createLight(); @@ -130,7 +130,7 @@ void InteriorCellRender::configureAmbient() light->setType(Ogre::Light::LT_DIRECTIONAL); light->setDirection(0,-1,0); } - + // configure fog according to cell void InteriorCellRender::configureFog() { @@ -144,28 +144,28 @@ void InteriorCellRender::configureFog() scene.getCamera()->setFarClipDistance (high + 10); scene.getViewport()->setBackgroundColour (color); } - + void InteriorCellRender::setAmbientMode() { switch (ambientMode) { case 0: - + scene.getMgr()->setAmbientLight(ambientColor); break; - + case 1: scene.getMgr()->setAmbientLight(0.7f*ambientColor + 0.3f*ColourValue(1,1,1)); break; - + case 2: - + scene.getMgr()->setAmbientLight(ColourValue(1,1,1)); break; } } - + void InteriorCellRender::show() { // If already loaded, just make the cell visible. @@ -208,14 +208,14 @@ void InteriorCellRender::toggleLight() ambientMode = 0; else ++ambientMode; - + switch (ambientMode) { case 0: std::cout << "Setting lights to normal\n"; break; case 1: std::cout << "Turning the lights up\n"; break; case 2: std::cout << "Turning the lights to full\n"; break; } - + setAmbientMode(); } diff --git a/game/mwrender/interior.hpp b/apps/openmw/mwrender/interior.hpp similarity index 81% rename from game/mwrender/interior.hpp rename to apps/openmw/mwrender/interior.hpp index da5ba6a21..5eda4eccd 100644 --- a/game/mwrender/interior.hpp +++ b/apps/openmw/mwrender/interior.hpp @@ -2,7 +2,8 @@ #define _GAME_RENDER_INTERIOR_H #include "cell.hpp" -#include "esm_store/cell_store.hpp" +#include "cellimp.hpp" +#include "components/esm_store/cell_store.hpp" #include "OgreColourValue.h" @@ -14,7 +15,7 @@ namespace Ogre namespace MWRender { class MWScene; - + /** This class is responsible for inserting meshes and other rendering objects from the given cell into the given rendering @@ -22,10 +23,10 @@ namespace MWRender TODO FIXME: Doesn't do full cleanup yet. */ - - class InteriorCellRender : private CellRender + + class InteriorCellRender : public CellRender, private CellRenderImp { - + static bool lightConst; static float lightConstValue; @@ -40,58 +41,58 @@ namespace MWRender static float lightQuadraticRadiusMult; static bool lightOutQuadInLin; - - const ESMS::CellStore &cell; + + ESMS::CellStore &cell; MWScene &scene; /// The scene node that contains all objects belonging to this /// cell. Ogre::SceneNode *base; - + Ogre::SceneNode *insert; // 0 normal, 1 more bright, 2 max int ambientMode; - + Ogre::ColourValue ambientColor; /// start inserting a new reference. - virtual void insertBegin (const ESM::CellRef &ref); + virtual void insertBegin (ESM::CellRef &ref); /// insert a mesh related to the most recent insertBegin call. virtual void insertMesh(const std::string &mesh); - + /// insert a light related to the most recent insertBegin call. virtual void insertLight(float r, float g, float b, float radius); - + /// finish inserting a new reference and return a handle to it. virtual std::string insertEnd(); - + /// configure lighting according to cell - void configureAmbient(); - + void configureAmbient(); + /// configure fog according to cell void configureFog(); - + void setAmbientMode(); - + public: - - InteriorCellRender(const ESMS::CellStore &_cell, MWScene &_scene) + + InteriorCellRender(ESMS::CellStore &_cell, MWScene &_scene) : cell(_cell), scene(_scene), base(NULL), insert(NULL), ambientMode (0) {} - + virtual ~InteriorCellRender() { destroy(); } - + /// Make the cell visible. Load the cell if necessary. - void show(); + virtual void show(); /// Remove the cell from rendering, but don't remove it from /// memory. void hide(); /// Destroy all rendering objects connected with this cell. - void destroy(); - + void destroy(); // comment by Zini: shouldn't this go into the destructor? + /// Switch through lighting modes. void toggleLight(); }; diff --git a/game/mwrender/mwscene.cpp b/apps/openmw/mwrender/mwscene.cpp similarity index 99% rename from game/mwrender/mwscene.cpp rename to apps/openmw/mwrender/mwscene.cpp index 960a50b2c..08172f885 100644 --- a/game/mwrender/mwscene.cpp +++ b/apps/openmw/mwrender/mwscene.cpp @@ -23,9 +23,8 @@ MWScene::MWScene(Render::OgreRenderer &_rend) // Create the camera camera = sceneMgr->createCamera("PlayerCam"); - camera->setNearClipDistance(5); - + // Create one viewport, entire window vp = window->addViewport(camera); diff --git a/game/mwrender/mwscene.hpp b/apps/openmw/mwrender/mwscene.hpp similarity index 95% rename from game/mwrender/mwscene.hpp rename to apps/openmw/mwrender/mwscene.hpp index 724d4cb10..bca2b831b 100644 --- a/game/mwrender/mwscene.hpp +++ b/apps/openmw/mwrender/mwscene.hpp @@ -1,7 +1,7 @@ #ifndef _GAME_RENDER_MWSCENE_H #define _GAME_RENDER_MWSCENE_H -#include "ogre/renderer.hpp" +#include "components/engine/ogre/renderer.hpp" namespace Ogre { diff --git a/game/mwrender/playerpos.hpp b/apps/openmw/mwrender/playerpos.hpp similarity index 97% rename from game/mwrender/playerpos.hpp rename to apps/openmw/mwrender/playerpos.hpp index cf1d02092..47175651c 100644 --- a/game/mwrender/playerpos.hpp +++ b/apps/openmw/mwrender/playerpos.hpp @@ -15,7 +15,7 @@ namespace MWRender public: PlayerPos(Ogre::Camera *cam) : - camera(cam), x(0), y(0), z(0) {} + x(0), y(0), z(0), camera(cam) {} // Set the player position. Uses Morrowind coordinates. void setPos(float _x, float _y, float _z) diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp new file mode 100644 index 000000000..901ab405e --- /dev/null +++ b/apps/openmw/mwrender/sky.cpp @@ -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; + } +} diff --git a/apps/openmw/mwrender/sky.hpp b/apps/openmw/mwrender/sky.hpp new file mode 100644 index 000000000..35b8cca74 --- /dev/null +++ b/apps/openmw/mwrender/sky.hpp @@ -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 diff --git a/apps/openmw/mwscript/cellextensions.cpp b/apps/openmw/mwscript/cellextensions.cpp new file mode 100644 index 000000000..73e131a8d --- /dev/null +++ b/apps/openmw/mwscript/cellextensions.cpp @@ -0,0 +1,44 @@ + +#include "cellextensions.hpp" + +#include + +#include +#include +#include + +#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 (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); + } + } +} + diff --git a/apps/openmw/mwscript/cellextensions.hpp b/apps/openmw/mwscript/cellextensions.hpp new file mode 100644 index 000000000..4aef25d1a --- /dev/null +++ b/apps/openmw/mwscript/cellextensions.hpp @@ -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 + + diff --git a/apps/openmw/mwscript/compilercontext.cpp b/apps/openmw/mwscript/compilercontext.cpp new file mode 100644 index 000000000..1e5cdaf4a --- /dev/null +++ b/apps/openmw/mwscript/compilercontext.cpp @@ -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 ' '; + } +} + diff --git a/apps/openmw/mwscript/compilercontext.hpp b/apps/openmw/mwscript/compilercontext.hpp new file mode 100644 index 000000000..41d5f314e --- /dev/null +++ b/apps/openmw/mwscript/compilercontext.hpp @@ -0,0 +1,43 @@ +#ifndef GAME_SCRIPT_COMPILERCONTEXT_H +#define GAME_SCRIPT_COMPILERCONTEXT_H + +#include + +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 + + diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt new file mode 100644 index 000000000..1532cacf1 --- /dev/null +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -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 + diff --git a/apps/openmw/mwscript/extensions.cpp b/apps/openmw/mwscript/extensions.cpp new file mode 100644 index 000000000..fa548770f --- /dev/null +++ b/apps/openmw/mwscript/extensions.cpp @@ -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); + } +} + diff --git a/apps/openmw/mwscript/extensions.hpp b/apps/openmw/mwscript/extensions.hpp new file mode 100644 index 000000000..a38da0340 --- /dev/null +++ b/apps/openmw/mwscript/extensions.hpp @@ -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 diff --git a/apps/openmw/mwscript/globalscripts.cpp b/apps/openmw/mwscript/globalscripts.cpp new file mode 100644 index 000000000..14930b828 --- /dev/null +++ b/apps/openmw/mwscript/globalscripts.cpp @@ -0,0 +1,69 @@ + +#include "globalscripts.hpp" + +#include + +#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::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 >::iterator iter = mScripts.find (name); + + if (iter!=mScripts.end()) + iter->second.first = false; + } + + bool GlobalScripts::isRunning (const std::string& name) const + { + std::map >::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 >::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); + } + } + + } +} + diff --git a/apps/openmw/mwscript/globalscripts.hpp b/apps/openmw/mwscript/globalscripts.hpp new file mode 100644 index 000000000..741968fcd --- /dev/null +++ b/apps/openmw/mwscript/globalscripts.hpp @@ -0,0 +1,45 @@ +#ifndef GAME_SCRIPT_GLOBALSCRIPTS_H +#define GAME_SCRIPT_GLOBALSCRIPTS_H + +#include +#include + +#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 > 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 + diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp new file mode 100644 index 000000000..525aef183 --- /dev/null +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -0,0 +1,155 @@ + +#include "interpretercontext.hpp" + +#include +#include + +#include + +#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& 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 ( + *reinterpret_cast (&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 ( + *reinterpret_cast (&value)); + } + + float InterpreterContext::getGlobalFloat (const std::string& name) const + { + Interpreter::Type_Data value = mEnvironment.mWorld->getGlobalVariable (name); + return *reinterpret_cast (&value); + } + + void InterpreterContext::setGlobalShort (const std::string& name, int value) + { + mEnvironment.mWorld->getGlobalVariable (name) = + *reinterpret_cast (&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 (&value2); + } + + void InterpreterContext::setGlobalFloat (const std::string& name, float value) + { + mEnvironment.mWorld->getGlobalVariable (name) = + *reinterpret_cast (&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; + } +} + diff --git a/apps/openmw/mwscript/interpretercontext.hpp b/apps/openmw/mwscript/interpretercontext.hpp new file mode 100644 index 000000000..f0efae65c --- /dev/null +++ b/apps/openmw/mwscript/interpretercontext.hpp @@ -0,0 +1,81 @@ +#ifndef GAME_SCRIPT_INTERPRETERCONTEXT_H +#define GAME_SCRIPT_INTERPRETERCONTEXT_H + +#include + +#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& 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 + + diff --git a/apps/openmw/mwscript/locals.hpp b/apps/openmw/mwscript/locals.hpp new file mode 100644 index 000000000..e5500158f --- /dev/null +++ b/apps/openmw/mwscript/locals.hpp @@ -0,0 +1,30 @@ +#ifndef GAME_SCRIPT_LOCALS_H +#define GAME_SCRIPT_LOCALS_H + +#include + +#include +#include + +namespace MWScript +{ + struct Locals + { + std::vector mShorts; + std::vector mLongs; + std::vector 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 + diff --git a/apps/openmw/mwscript/scriptmanager.cpp b/apps/openmw/mwscript/scriptmanager.cpp new file mode 100644 index 000000000..7aea55693 --- /dev/null +++ b/apps/openmw/mwscript/scriptmanager.cpp @@ -0,0 +1,127 @@ + +#include "scriptmanager.hpp" + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include + +#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 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 >::iterator iter = + mScripts.find (name); + + if (iter==mScripts.end()) + { + if (!compile (name)) + { + // failed -> ignore script from now on. + std::vector 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); + } +} + diff --git a/apps/openmw/mwscript/scriptmanager.hpp b/apps/openmw/mwscript/scriptmanager.hpp new file mode 100644 index 000000000..17b8cc242 --- /dev/null +++ b/apps/openmw/mwscript/scriptmanager.hpp @@ -0,0 +1,55 @@ +#ifndef GAME_SCRIPT_SCRIPTMANAGER_H +#define GAME_SCRIPT_SCRIPTMANAGER_H + +#include +#include +#include + +#include +#include + +#include + +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 > 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 + diff --git a/apps/openmw/mwsound/extensions.cpp b/apps/openmw/mwsound/extensions.cpp new file mode 100644 index 000000000..4aee9827c --- /dev/null +++ b/apps/openmw/mwsound/extensions.cpp @@ -0,0 +1,236 @@ + +#include "extensions.hpp" + +#include + +#include +#include +#include + +#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 (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 (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 (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 (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 (runtime.getContext()); + + std::string sound = runtime.getStringLiteral (runtime[0]); + runtime.pop(); + + float volume = *reinterpret_cast (&runtime[0]); + runtime.pop(); + + float pitch = *reinterpret_cast (&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 (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 (runtime.getContext()); + + std::string sound = runtime.getStringLiteral (runtime[0]); + runtime.pop(); + + float volume = *reinterpret_cast (&runtime[0]); + runtime.pop(); + + float pitch = *reinterpret_cast (&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 (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 (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); + } +} diff --git a/apps/openmw/mwsound/extensions.hpp b/apps/openmw/mwsound/extensions.hpp new file mode 100644 index 000000000..0e860952a --- /dev/null +++ b/apps/openmw/mwsound/extensions.hpp @@ -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 + diff --git a/apps/openmw/mwsound/soundmanager.cpp b/apps/openmw/mwsound/soundmanager.cpp new file mode 100644 index 000000000..bfe51686b --- /dev/null +++ b/apps/openmw/mwsound/soundmanager.cpp @@ -0,0 +1,71 @@ + +#include "soundmanager.hpp" + +#include // TODO remove this line, once the real code is in place. + +#include + +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::const_iterator iter = + mSounds.find (reference.getRefData().getHandle()); + + if (iter==mSounds.end()) + return false; + + return iter->second==soundId; + } +} + diff --git a/apps/openmw/mwsound/soundmanager.hpp b/apps/openmw/mwsound/soundmanager.hpp new file mode 100644 index 000000000..5a79c0c27 --- /dev/null +++ b/apps/openmw/mwsound/soundmanager.hpp @@ -0,0 +1,68 @@ +#ifndef GAME_SOUND_SOUNDMANAGER_H +#define GAME_SOUND_SOUNDMANAGER_H + +#include +#include + +#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 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 + + diff --git a/apps/openmw/mwworld/environment.hpp b/apps/openmw/mwworld/environment.hpp new file mode 100644 index 000000000..b208d95a2 --- /dev/null +++ b/apps/openmw/mwworld/environment.hpp @@ -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 + diff --git a/apps/openmw/mwworld/ptr.hpp b/apps/openmw/mwworld/ptr.hpp new file mode 100644 index 000000000..11ec4baf7 --- /dev/null +++ b/apps/openmw/mwworld/ptr.hpp @@ -0,0 +1,55 @@ +#ifndef GAME_MWWORLD_PTR_H +#define GAME_MWWORLD_PTR_H + +#include + +#include + +#include + +#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 + Ptr (ESMS::LiveCellRef *liveCellRef) + { + mPtr = liveCellRef; + mCellRef = &liveCellRef->ref; + mRefData = &liveCellRef->mData; + } + + template + ESMS::LiveCellRef *get() const + { + return boost::any_cast*> (mPtr); + } + + ESM::CellRef& getCellRef() const + { + assert (mCellRef); + return *mCellRef; + } + + RefData& getRefData() const + { + assert (mRefData); + return *mRefData; + } + }; +} + +#endif + diff --git a/apps/openmw/mwworld/refdata.hpp b/apps/openmw/mwworld/refdata.hpp new file mode 100644 index 000000000..a7e137b9d --- /dev/null +++ b/apps/openmw/mwworld/refdata.hpp @@ -0,0 +1,54 @@ +#ifndef GAME_MWWORLD_REFDATA_H +#define GAME_MWWORLD_REFDATA_H + +#include + +#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 diff --git a/apps/openmw/mwworld/world.cpp b/apps/openmw/mwworld/world.cpp new file mode 100644 index 000000000..217bff7da --- /dev/null +++ b/apps/openmw/mwworld/world.cpp @@ -0,0 +1,150 @@ + +#include "world.hpp" + +#include + +#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 + void listCellScripts (const ESMS::ESMStore& store, + ESMS::CellRefList& cellRefList, MWWorld::World::ScriptList& scriptList) + { + for (typename ESMS::CellRefList::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& 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::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 (&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::iterator iter = mGlobalVariables.find (name); + + if (iter==mGlobalVariables.end()) + throw std::runtime_error ("unknown global variable: " + name); + + return iter->second; + } +} diff --git a/apps/openmw/mwworld/world.hpp b/apps/openmw/mwworld/world.hpp new file mode 100644 index 000000000..d39c457d2 --- /dev/null +++ b/apps/openmw/mwworld/world.hpp @@ -0,0 +1,83 @@ +#ifndef GAME_MWWORLD_WORLD_H +#define GAME_MWWORLD_WORLD_H + +#include +#include + +#include + +#include + +#include + +#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 > ScriptList; + + private: + + typedef ESMS::CellStore CellStore; + typedef std::map 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 mInteriors; + ScriptList mLocalScripts; + std::map mGlobalVariables; + + // not implemented + World (const World&); + World& operator= (const World&); + + void insertInteriorScripts (ESMS::CellStore& 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 diff --git a/bsa/bsa_archive.cpp b/components/bsa/bsa_archive.cpp similarity index 97% rename from bsa/bsa_archive.cpp rename to components/bsa/bsa_archive.cpp index a025c2507..4691eb546 100644 --- a/bsa/bsa_archive.cpp +++ b/components/bsa/bsa_archive.cpp @@ -27,7 +27,7 @@ #include #include #include "bsa_file.hpp" -#include +#include using namespace Ogre; using namespace Mangle::Stream; @@ -137,7 +137,7 @@ public: return name; } - Archive *createInstance( const String& name ) + Archive *createInstance( const String& name ) { return new BSAArchive(name); } diff --git a/bsa/bsa_archive.hpp b/components/bsa/bsa_archive.hpp similarity index 100% rename from bsa/bsa_archive.hpp rename to components/bsa/bsa_archive.hpp diff --git a/bsa/bsa_file.cpp b/components/bsa/bsa_file.cpp similarity index 95% rename from bsa/bsa_file.cpp rename to components/bsa/bsa_file.cpp index 7a7600ad1..acd63a4bc 100644 --- a/bsa/bsa_file.cpp +++ b/components/bsa/bsa_file.cpp @@ -23,9 +23,9 @@ #include "bsa_file.hpp" -#include -#include -#include +#include +#include +#include #include #include @@ -129,7 +129,7 @@ void BSAFile::readHeader() // Set up the the FileStruct table files.resize(filenum); - for(int i=0;isecond; - assert(res >= 0 && res < files.size()); + assert(res >= 0 && res < static_cast (files.size())); return res; } } diff --git a/bsa/bsa_file.hpp b/components/bsa/bsa_file.hpp similarity index 96% rename from bsa/bsa_file.hpp rename to components/bsa/bsa_file.hpp index 5c19409ab..7ec101b43 100644 --- a/bsa/bsa_file.hpp +++ b/components/bsa/bsa_file.hpp @@ -24,10 +24,9 @@ #ifndef _BSA_FILE_H_ #define _BSA_FILE_H_ -#include - -#include -#include +#include +#include +#include #include #include #include diff --git a/bsa/tests/.gitignore b/components/bsa/tests/.gitignore similarity index 100% rename from bsa/tests/.gitignore rename to components/bsa/tests/.gitignore diff --git a/bsa/tests/Makefile b/components/bsa/tests/Makefile similarity index 100% rename from bsa/tests/Makefile rename to components/bsa/tests/Makefile diff --git a/bsa/tests/bsa_file_test.cpp b/components/bsa/tests/bsa_file_test.cpp similarity index 100% rename from bsa/tests/bsa_file_test.cpp rename to components/bsa/tests/bsa_file_test.cpp diff --git a/bsa/tests/bsatool.cpp b/components/bsa/tests/bsatool.cpp similarity index 100% rename from bsa/tests/bsatool.cpp rename to components/bsa/tests/bsatool.cpp diff --git a/bsa/tests/bsatool.ggo b/components/bsa/tests/bsatool.ggo similarity index 100% rename from bsa/tests/bsatool.ggo rename to components/bsa/tests/bsatool.ggo diff --git a/bsa/tests/bsatool_cmd.c b/components/bsa/tests/bsatool_cmd.c similarity index 100% rename from bsa/tests/bsatool_cmd.c rename to components/bsa/tests/bsatool_cmd.c diff --git a/bsa/tests/bsatool_cmd.h b/components/bsa/tests/bsatool_cmd.h similarity index 100% rename from bsa/tests/bsatool_cmd.h rename to components/bsa/tests/bsatool_cmd.h diff --git a/bsa/tests/ogre_archive_test.cpp b/components/bsa/tests/ogre_archive_test.cpp similarity index 100% rename from bsa/tests/ogre_archive_test.cpp rename to components/bsa/tests/ogre_archive_test.cpp diff --git a/bsa/tests/output/bsa_file_test.out b/components/bsa/tests/output/bsa_file_test.out similarity index 100% rename from bsa/tests/output/bsa_file_test.out rename to components/bsa/tests/output/bsa_file_test.out diff --git a/bsa/tests/output/ogre_archive_test.out b/components/bsa/tests/output/ogre_archive_test.out similarity index 100% rename from bsa/tests/output/ogre_archive_test.out rename to components/bsa/tests/output/ogre_archive_test.out diff --git a/bsa/tests/test.sh b/components/bsa/tests/test.sh similarity index 100% rename from bsa/tests/test.sh rename to components/bsa/tests/test.sh diff --git a/components/commandserver/command.hpp b/components/commandserver/command.hpp new file mode 100755 index 000000000..f58177f6e --- /dev/null +++ b/components/commandserver/command.hpp @@ -0,0 +1,21 @@ +#ifndef COMMANDSERVER_COMMAND_HPP +#define COMMANDSERVER_COMMAND_HPP + +#include + +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 mReplyFunction; + }; +} + +#endif //COMMANDSERVER_COMMAND_HPP diff --git a/components/commandserver/server.cpp b/components/commandserver/server.cpp new file mode 100755 index 000000000..c8bf34bfa --- /dev/null +++ b/components/commandserver/server.cpp @@ -0,0 +1,201 @@ + +#include "server.hpp" + +using boost::asio::ip::tcp; + +#include + +// +// 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 packet(plen); + Header* pHeader = reinterpret_cast(&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 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::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 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; + } + } + +}} diff --git a/components/commandserver/server.hpp b/components/commandserver/server.hpp new file mode 100755 index 000000000..b53e0f23a --- /dev/null +++ b/components/commandserver/server.hpp @@ -0,0 +1,63 @@ +#ifndef CONSOLESERVER_H +#define CONSOLESERVER_H + +#include +#include +#include +#include +#include +#include + +#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 Deque; + + Server (Deque* pCommandQueue, const int port); + + void start(); + void stop(); + + protected: + friend class Detail::Connection; + typedef std::set 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 diff --git a/components/compiler/context.hpp b/components/compiler/context.hpp new file mode 100644 index 000000000..1dfdf6afe --- /dev/null +++ b/components/compiler/context.hpp @@ -0,0 +1,39 @@ +#ifndef COMPILER_CONTEXT_H_INCLUDED +#define COMPILER_CONTEXT_H_INCLUDED + +#include + +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 + diff --git a/components/compiler/controlparser.cpp b/components/compiler/controlparser.cpp new file mode 100644 index 000000000..4715d3214 --- /dev/null +++ b/components/compiler/controlparser.cpp @@ -0,0 +1,250 @@ + +#include "controlparser.hpp" + +#include +#include +#include + +#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 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& 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; + } +} + diff --git a/components/compiler/controlparser.hpp b/components/compiler/controlparser.hpp new file mode 100644 index 000000000..17b3137c7 --- /dev/null +++ b/components/compiler/controlparser.hpp @@ -0,0 +1,70 @@ +#ifndef COMPILER_CONTROLPARSER_H_INCLUDED +#define COMPILER_CONTROLPARSER_H_INCLUDED + +#include + +#include + +#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 Codes; + typedef std::vector > 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& 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 + diff --git a/components/compiler/errorhandler.cpp b/components/compiler/errorhandler.cpp new file mode 100644 index 000000000..ee13c837d --- /dev/null +++ b/components/compiler/errorhandler.cpp @@ -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; + } +} diff --git a/components/compiler/errorhandler.hpp b/components/compiler/errorhandler.hpp new file mode 100644 index 000000000..636eb996a --- /dev/null +++ b/components/compiler/errorhandler.hpp @@ -0,0 +1,68 @@ + +#ifndef COMPILER_ERRORHANDLER_H_INCLUDED +#define COMPILER_ERRORHANDLER_H_INCLUDED + +#include + +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 diff --git a/components/compiler/exception.hpp b/components/compiler/exception.hpp new file mode 100644 index 000000000..979315801 --- /dev/null +++ b/components/compiler/exception.hpp @@ -0,0 +1,32 @@ +#ifndef COMPILER_EXCEPTION_H_INCLUDED +#define COMPILER_EXCEPTION_H_INCLUDED + +#include + +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 diff --git a/components/compiler/exprparser.cpp b/components/compiler/exprparser.cpp new file mode 100644 index 000000000..fd44c5e41 --- /dev/null +++ b/components/compiler/exprparser.cpp @@ -0,0 +1,551 @@ + +#include "exprparser.hpp" + +#include +#include +#include +#include +#include + +#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 (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& 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& code, bool invert) + { + ExprParser parser (getErrorHandler(), getContext(), mLocals, mLiterals, true); + StringParser stringParser (getErrorHandler(), getContext(), mLiterals); + + std::stack > 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 tmp; + stringParser.append (tmp); + + stack.push (tmp); + } + else + stringParser.append (code); + } + else + { + parser.reset(); + scanner.scan (parser); + + std::vector 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& tmp = stack.top(); + + std::copy (tmp.begin(), tmp.end(), std::back_inserter (code)); + + stack.pop(); + } + } +} + diff --git a/components/compiler/exprparser.hpp b/components/compiler/exprparser.hpp new file mode 100644 index 000000000..b8f295b5c --- /dev/null +++ b/components/compiler/exprparser.hpp @@ -0,0 +1,102 @@ +#ifndef COMPILER_EXPRPARSER_H_INCLUDED +#define COMPILER_EXPRPARSER_H_INCLUDED + +#include + +#include + +#include "parser.hpp" +#include "tokenloc.hpp" + +namespace Compiler +{ + class Locals; + class Literals; + + class ExprParser : public Parser + { + Locals& mLocals; + Literals& mLiterals; + std::vector mOperands; + std::vector mOperators; + bool mNextOperand; + TokenLoc mTokenLoc; + std::vector 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& code); + ///< Generate code for parsed expression. + /// \return Type ('l': integer, 'f': float) + + void parseArguments (const std::string& arguments, Scanner& scanner, + std::vector& 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 diff --git a/components/compiler/extensions.cpp b/components/compiler/extensions.cpp new file mode 100644 index 000000000..ff05208de --- /dev/null +++ b/components/compiler/extensions.cpp @@ -0,0 +1,101 @@ + +#include "extensions.hpp" + +#include +#include + +#include "generator.hpp" + +namespace Compiler +{ + Extensions::Extensions() : mNextKeywordIndex (-1) {} + + int Extensions::searchKeyword (const std::string& keyword) const + { + std::map::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::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::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& code) + const + { + std::map::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& code) + const + { + std::map::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)); + } +} diff --git a/components/compiler/extensions.hpp b/components/compiler/extensions.hpp new file mode 100644 index 000000000..f864daf1b --- /dev/null +++ b/components/compiler/extensions.hpp @@ -0,0 +1,75 @@ +#ifndef COMPILER_EXTENSIONS_H_INCLUDED +#define COMPILER_EXTENSINOS_H_INCLUDED + +#include +#include +#include + +#include + +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 mKeywords; + std::map mFunctions; + std::map 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& code) + const; + ///< Append code for function to \a code. + + void generateInstructionCode (int keyword, std::vector& code) + const; + ///< Append code for function to \a code. + }; +} + +#endif + diff --git a/components/compiler/fileparser.cpp b/components/compiler/fileparser.cpp new file mode 100644 index 000000000..f8b3d0e61 --- /dev/null +++ b/components/compiler/fileparser.cpp @@ -0,0 +1,103 @@ +#include "fileparser.hpp" + +#include + +#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& 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(); + } +} + diff --git a/components/compiler/fileparser.hpp b/components/compiler/fileparser.hpp new file mode 100644 index 000000000..13c7fb537 --- /dev/null +++ b/components/compiler/fileparser.hpp @@ -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& 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 diff --git a/components/compiler/generator.cpp b/components/compiler/generator.cpp new file mode 100644 index 000000000..f9893ad9a --- /dev/null +++ b/components/compiler/generator.cpp @@ -0,0 +1,670 @@ + +#include "generator.hpp" + +#include +#include +#include +#include + +#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); + } + } +} + diff --git a/components/compiler/generator.hpp b/components/compiler/generator.hpp new file mode 100644 index 000000000..cebb5b00f --- /dev/null +++ b/components/compiler/generator.hpp @@ -0,0 +1,113 @@ +#ifndef COMPILER_GENERATOR_H_INCLUDED +#define COMPILER_GENERATOR_H_INCLUDED + +#include +#include +#include + +#include + +namespace Compiler +{ + class Literals; + + namespace Generator + { + typedef std::vector 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 + diff --git a/components/compiler/lineparser.cpp b/components/compiler/lineparser.cpp new file mode 100644 index 000000000..8ce37b414 --- /dev/null +++ b/components/compiler/lineparser.cpp @@ -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& 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; iisInstruction (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 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 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(); + } +} + diff --git a/components/compiler/lineparser.hpp b/components/compiler/lineparser.hpp new file mode 100644 index 000000000..63efb60a4 --- /dev/null +++ b/components/compiler/lineparser.hpp @@ -0,0 +1,68 @@ +#ifndef COMPILER_LINEPARSER_H_INCLUDED +#define COMPILER_LINEPARSER_H_INCLUDED + +#include + +#include + +#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& mCode; + State mState; + std::string mName; + char mType; + ExprParser mExprParser; + + public: + + LineParser (ErrorHandler& errorHandler, Context& context, Locals& locals, + Literals& literals, std::vector& 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 diff --git a/components/compiler/literals.cpp b/components/compiler/literals.cpp new file mode 100644 index 000000000..626b03afb --- /dev/null +++ b/components/compiler/literals.cpp @@ -0,0 +1,94 @@ + +#include "literals.hpp" + +#include + +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::const_iterator iter (mStrings.begin()); + iter!=mStrings.end(); ++iter) + size += static_cast (iter->size()) + 1; + + if (size % 4) // padding + size += 4 - size % 4; + + return size; + } + + void Literals::append (std::vector& code) const + { + for (std::vector::const_iterator iter (mIntegers.begin()); + iter!=mIntegers.end(); ++iter) + code.push_back (*reinterpret_cast (&*iter)); + + for (std::vector::const_iterator iter (mFloats.begin()); + iter!=mFloats.end(); ++iter) + code.push_back (*reinterpret_cast (&*iter)); + + int stringBlockSize = getStringSize(); + int size = static_cast (code.size()); + + code.resize (size+stringBlockSize/4); + + int offset = 0; + + for (std::vector::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 (&code[size]) + offset); + offset += stringSize; + } + } + + int Literals::addInteger (Interpreter::Type_Integer value) + { + int index = static_cast (mIntegers.size()); + + mIntegers.push_back (value); + + return index; + } + + int Literals::addFloat (Interpreter::Type_Float value) + { + int index = static_cast (mFloats.size()); + + mFloats.push_back (value); + + return index; + } + + int Literals::addString (const std::string& value) + { + int index = static_cast (mStrings.size()); + + mStrings.push_back (value); + + return index; + } + + void Literals::clear() + { + mIntegers.clear(); + mFloats.clear(); + mStrings.clear(); + } +} + diff --git a/components/compiler/literals.hpp b/components/compiler/literals.hpp new file mode 100644 index 000000000..b18c86479 --- /dev/null +++ b/components/compiler/literals.hpp @@ -0,0 +1,49 @@ +#ifndef COMPILER_LITERALS_H_INCLUDED +#define COMPILER_LITERALS_H_INCLUDED + +#include +#include + +#include + +namespace Compiler +{ + /// \brief Literal values. + + class Literals + { + std::vector mIntegers; + std::vector mFloats; + std::vector 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& 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 + diff --git a/components/compiler/locals.cpp b/components/compiler/locals.cpp new file mode 100644 index 000000000..d93e73849 --- /dev/null +++ b/components/compiler/locals.cpp @@ -0,0 +1,110 @@ + +#include "locals.hpp" + +#include +#include +#include +#include +#include + +namespace Compiler +{ + const std::vector& 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& collection = get (type); + + std::vector::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& 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 (localFile, " ")); + std::copy (get ('l').begin(), get ('l').end(), + std::ostream_iterator (localFile, " ")); + std::copy (get ('f').begin(), get ('f').end(), + std::ostream_iterator (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(); + } +} + diff --git a/components/compiler/locals.hpp b/components/compiler/locals.hpp new file mode 100644 index 000000000..fdd0a6b29 --- /dev/null +++ b/components/compiler/locals.hpp @@ -0,0 +1,45 @@ +#ifndef COMPILER_LOCALS_H_INCLUDED +#define COMPILER_LOCALS_H_INCLUDED + +#include +#include +#include + +namespace Compiler +{ + /// \brief Local variable declarations + + class Locals + { + std::vector mShorts; + std::vector mLongs; + std::vector mFloats; + + const std::vector& get (char type) const; + + int searchIndex (char type, const std::string& name) const; + + bool search (char type, const std::string& name) const; + + std::vector& 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 diff --git a/components/compiler/output.cpp b/components/compiler/output.cpp new file mode 100644 index 000000000..46e04b8dc --- /dev/null +++ b/components/compiler/output.cpp @@ -0,0 +1,74 @@ + +#include "output.hpp" + +#include +#include +#include + +#include "locals.hpp" + +namespace Compiler +{ + Output::Output (Locals& locals) : mLocals (locals) {} + + void Output::getCode (std::vector& code) const + { + code.clear(); + + // header + code.push_back (static_cast (mCode.size())); + + assert (mLiterals.getIntegerSize()%4==0); + code.push_back (static_cast (mLiterals.getIntegerSize()/4)); + + assert (mLiterals.getFloatSize()%4==0); + code.push_back (static_cast (mLiterals.getFloatSize()/4)); + + assert (mLiterals.getStringSize()%4==0); + code.push_back (static_cast (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& Output::getCode() const + { + return mCode; + } + + const Locals& Output::getLocals() const + { + return mLocals; + } + + Literals& Output::getLiterals() + { + return mLiterals; + } + + std::vector& Output::getCode() + { + return mCode; + } + + Locals& Output::getLocals() + { + return mLocals; + } + + void Output::clear() + { + mLiterals.clear(); + mCode.clear(); + mLocals.clear(); + } +} + diff --git a/components/compiler/output.hpp b/components/compiler/output.hpp new file mode 100644 index 000000000..37b88cee9 --- /dev/null +++ b/components/compiler/output.hpp @@ -0,0 +1,44 @@ +#ifndef COMPILER_OUTPUT_H_INCLUDED +#define COMPILER_OUTPUT_H_INCLUDED + +#include "literals.hpp" + +#include + +#include + +namespace Compiler +{ + class Locals; + + class Output + { + Literals mLiterals; + std::vector mCode; + Locals& mLocals; + + public: + + Output (Locals& locals); + + void getCode (std::vector& code) const; + ///< store generated code in \æ code. + + const Literals& getLiterals() const; + + const Locals& getLocals() const; + + const std::vector& getCode() const; + + Literals& getLiterals(); + + std::vector& getCode(); + + Locals& getLocals(); + + void clear(); + }; +} + +#endif + diff --git a/components/compiler/parser.cpp b/components/compiler/parser.cpp new file mode 100644 index 000000000..ff5bf33ee --- /dev/null +++ b/components/compiler/parser.cpp @@ -0,0 +1,140 @@ + +#include "parser.hpp" + +#include +#include +#include + +#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(); + } +} + diff --git a/components/compiler/parser.hpp b/components/compiler/parser.hpp new file mode 100644 index 000000000..a55f5a024 --- /dev/null +++ b/components/compiler/parser.hpp @@ -0,0 +1,90 @@ +#ifndef COMPILER_PARSER_H_INCLUDED +#define COMPILER_PARSER_H_INCLUDED + +#include + +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 diff --git a/components/compiler/scanner.cpp b/components/compiler/scanner.cpp new file mode 100644 index 000000000..092d8074a --- /dev/null +++ b/components/compiler/scanner.cpp @@ -0,0 +1,513 @@ + +#include "scanner.hpp" + +#include +#include +#include +#include + +#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 = ""; + + 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; + } +} + diff --git a/components/compiler/scanner.hpp b/components/compiler/scanner.hpp new file mode 100644 index 000000000..88e070673 --- /dev/null +++ b/components/compiler/scanner.hpp @@ -0,0 +1,119 @@ +#ifndef COMPILER_SCANNER_H_INCLUDED +#define COMPILER_SCANNER_H_INCLUDED + +#include +#include + +#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 diff --git a/components/compiler/scriptparser.cpp b/components/compiler/scriptparser.cpp new file mode 100644 index 000000000..ce142847f --- /dev/null +++ b/components/compiler/scriptparser.cpp @@ -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& 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(); + } +} + diff --git a/components/compiler/scriptparser.hpp b/components/compiler/scriptparser.hpp new file mode 100644 index 000000000..bb4809dab --- /dev/null +++ b/components/compiler/scriptparser.hpp @@ -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& 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 + diff --git a/components/compiler/skipparser.cpp b/components/compiler/skipparser.cpp new file mode 100644 index 000000000..2d09b63ba --- /dev/null +++ b/components/compiler/skipparser.cpp @@ -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; + } +} + diff --git a/components/compiler/skipparser.hpp b/components/compiler/skipparser.hpp new file mode 100644 index 000000000..17f1c8d98 --- /dev/null +++ b/components/compiler/skipparser.hpp @@ -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 + diff --git a/components/compiler/streamerrorhandler.cpp b/components/compiler/streamerrorhandler.cpp new file mode 100644 index 000000000..a1d12b708 --- /dev/null +++ b/components/compiler/streamerrorhandler.cpp @@ -0,0 +1,38 @@ + +#include "streamerrorhandler.hpp" + +#include "tokenloc.hpp" + +namespace Compiler +{ + // Report error to the user. + + void StreamErrorHandler::report (const std::string& message, const TokenLoc& loc, + Type type) + { + if (type==ErrorMessage) + mStream << "error "; + else + mStream << "warning "; + + mStream + << "line " << loc.mLine << ", column " << loc.mColumn + << " (" << loc.mLiteral << ")" << std::endl + << " " << message << std::endl; + } + + // Report a file related error + + void StreamErrorHandler::report (const std::string& message, Type type) + { + if (type==ErrorMessage) + mStream << "error "; + else + mStream << "warning "; + + mStream + << "file:" << std::endl + << " " << message << std::endl; + } + + StreamErrorHandler::StreamErrorHandler (std::ostream& ErrorStream) : mStream (ErrorStream) {}} diff --git a/components/compiler/streamerrorhandler.hpp b/components/compiler/streamerrorhandler.hpp new file mode 100644 index 000000000..ce4d4c6c6 --- /dev/null +++ b/components/compiler/streamerrorhandler.hpp @@ -0,0 +1,37 @@ + +#ifndef COMPILER_STREAMERRORHANDLER_H_INCLUDED +#define COMPILER_STREAMERRORHANDLER_H_INCLUDED + +#include + +#include "errorhandler.hpp" + +namespace Compiler +{ + /// \brief Error handler implementation: Write errors into stream + + class StreamErrorHandler : public ErrorHandler + { + std::ostream& mStream; + + // not implemented + + StreamErrorHandler (const StreamErrorHandler&); + StreamErrorHandler& operator= (const StreamErrorHandler&); + + virtual void report (const std::string& message, const TokenLoc& loc, Type type); + ///< Report error to the user. + + virtual void report (const std::string& message, Type type); + ///< Report a file related error + + public: + + // constructors + + StreamErrorHandler (std::ostream& ErrorStream); + ///< constructor + }; +} + +#endif diff --git a/components/compiler/stringparser.cpp b/components/compiler/stringparser.cpp new file mode 100644 index 000000000..0e2e38be5 --- /dev/null +++ b/components/compiler/stringparser.cpp @@ -0,0 +1,62 @@ + +#include "stringparser.hpp" + +#include +#include + +#include "scanner.hpp" +#include "generator.hpp" + +namespace Compiler +{ + StringParser::StringParser (ErrorHandler& errorHandler, Context& context, Literals& literals) + : Parser (errorHandler, context), mLiterals (literals), mState (StartState), mSmashCase (false) + { + + } + + bool StringParser::parseName (const std::string& name, const TokenLoc& loc, + Scanner& scanner) + { + if (mState==StartState || mState==CommaState) + { + if (mSmashCase) + Generator::pushString (mCode, mLiterals, toLower (name)); + else + Generator::pushString (mCode, mLiterals, name); + + return false; + } + + return Parser::parseName (name, loc, scanner); + } + + bool StringParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner) + { + if (code==Scanner::S_comma && mState==StartState) + { + mState = CommaState; + return true; + } + + return Parser::parseSpecial (code, loc, scanner); + } + + void StringParser::append (std::vector& code) + { + std::copy (mCode.begin(), mCode.end(), std::back_inserter (code)); + } + + void StringParser::reset() + { + mState = StartState; + mCode.clear(); + mSmashCase = false; + } + + void StringParser::smashCase() + { + mSmashCase = false; + } +} + diff --git a/components/compiler/stringparser.hpp b/components/compiler/stringparser.hpp new file mode 100644 index 000000000..f692c650b --- /dev/null +++ b/components/compiler/stringparser.hpp @@ -0,0 +1,50 @@ +#ifndef COMPILER_STRINGPARSER_H_INCLUDED +#define COMPILER_STRINGPARSER_H_INCLUDED + +#include + +#include + +#include "parser.hpp" + +namespace Compiler +{ + class Literals; + + class StringParser : public Parser + { + enum State + { + StartState, CommaState + }; + + Literals& mLiterals; + State mState; + std::vector mCode; + bool mSmashCase; + + public: + + StringParser (ErrorHandler& errorHandler, Context& context, Literals& literals); + + virtual bool parseName (const std::string& name, const TokenLoc& loc, + Scanner& scanner); + ///< Handle a name 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 append (std::vector& code); + ///< Append code for parsed string. + + void smashCase(); + ///< Transform all scanned strings to lower case + + void reset(); + ///< Reset parser to clean state (this includes the smashCase function). + }; +} + +#endif diff --git a/components/compiler/tokenloc.hpp b/components/compiler/tokenloc.hpp new file mode 100644 index 000000000..62b5cdee5 --- /dev/null +++ b/components/compiler/tokenloc.hpp @@ -0,0 +1,20 @@ +#ifndef COMPILER_TOKENLOC_H_INCLUDED +#define COMPILER_TOKENLOC_H_INCLUDED + +#include + +namespace Compiler +{ + /// \brief Location of a token in a source file + + struct TokenLoc + { + int mColumn; + int mLine; + std::string mLiteral; + + TokenLoc() : mColumn (0), mLine (0), mLiteral ("") {} + }; +} + +#endif // TOKENLOC_H_INCLUDED diff --git a/input/dispatch_map.hpp b/components/engine/input/dispatch_map.hpp similarity index 100% rename from input/dispatch_map.hpp rename to components/engine/input/dispatch_map.hpp diff --git a/input/dispatcher.hpp b/components/engine/input/dispatcher.hpp similarity index 100% rename from input/dispatcher.hpp rename to components/engine/input/dispatcher.hpp diff --git a/input/func_binder.hpp b/components/engine/input/func_binder.hpp similarity index 100% rename from input/func_binder.hpp rename to components/engine/input/func_binder.hpp diff --git a/input/listener.hpp b/components/engine/input/listener.hpp similarity index 96% rename from input/listener.hpp rename to components/engine/input/listener.hpp index 5b9d4fb49..ecc786f22 100644 --- a/input/listener.hpp +++ b/components/engine/input/listener.hpp @@ -2,7 +2,7 @@ #define _INPUT_LISTENER_H #include "oismanager.hpp" -#include "ogre/renderer.hpp" +#include "components/engine/ogre/renderer.hpp" #include "dispatcher.hpp" #include @@ -17,7 +17,7 @@ namespace Input InputListener(Render::OgreRenderer &rend, Input::OISManager &input, const Input::Dispatcher &_disp) - : doExit(false), disp(_disp) + : disp(_disp), doExit(false) { // Set up component pointers mWindow = rend.getWindow(); diff --git a/input/oismanager.cpp b/components/engine/input/oismanager.cpp similarity index 97% rename from input/oismanager.cpp rename to components/engine/input/oismanager.cpp index 92495f967..5f872a29d 100644 --- a/input/oismanager.cpp +++ b/components/engine/input/oismanager.cpp @@ -16,7 +16,7 @@ using namespace OIS; using namespace std; -OISManager::OISManager(Render::OgreRenderer &rend) +OISManager::OISManager(Render::OgreRenderer &rend, bool debug) { RenderWindow *window = rend.getWindow(); assert(window); @@ -32,7 +32,7 @@ OISManager::OISManager(Render::OgreRenderer &rend) // Non-exclusive mouse and keyboard input in debug mode. Debug mode // isn't implemented yet though. - if(false) + if(debug) { #if defined OIS_WIN32_PLATFORM pl.insert(std::make_pair(std::string("w32_mouse"), diff --git a/input/oismanager.hpp b/components/engine/input/oismanager.hpp similarity index 69% rename from input/oismanager.hpp rename to components/engine/input/oismanager.hpp index 8e687782e..05d0ac622 100644 --- a/input/oismanager.hpp +++ b/components/engine/input/oismanager.hpp @@ -1,7 +1,7 @@ #ifndef _INPUT_OISMANAGER_H #define _INPUT_OISMANAGER_H -#include "ogre/renderer.hpp" +#include "components/engine/ogre/renderer.hpp" #include namespace Input @@ -12,7 +12,7 @@ namespace Input OIS::Mouse *mouse; OIS::Keyboard *keyboard; - OISManager(Render::OgreRenderer &rend); + OISManager(Render::OgreRenderer &rend, bool debug); ~OISManager(); }; } diff --git a/input/poller.hpp b/components/engine/input/poller.hpp similarity index 100% rename from input/poller.hpp rename to components/engine/input/poller.hpp diff --git a/input/tests/.gitignore b/components/engine/input/tests/.gitignore similarity index 100% rename from input/tests/.gitignore rename to components/engine/input/tests/.gitignore diff --git a/input/tests/Makefile b/components/engine/input/tests/Makefile similarity index 100% rename from input/tests/Makefile rename to components/engine/input/tests/Makefile diff --git a/input/tests/dispatch_map_test.cpp b/components/engine/input/tests/dispatch_map_test.cpp similarity index 100% rename from input/tests/dispatch_map_test.cpp rename to components/engine/input/tests/dispatch_map_test.cpp diff --git a/input/tests/funcbind_test.cpp b/components/engine/input/tests/funcbind_test.cpp similarity index 100% rename from input/tests/funcbind_test.cpp rename to components/engine/input/tests/funcbind_test.cpp diff --git a/input/tests/output/dispatch_map_test.out b/components/engine/input/tests/output/dispatch_map_test.out similarity index 100% rename from input/tests/output/dispatch_map_test.out rename to components/engine/input/tests/output/dispatch_map_test.out diff --git a/input/tests/output/funcbind_test.out b/components/engine/input/tests/output/funcbind_test.out similarity index 100% rename from input/tests/output/funcbind_test.out rename to components/engine/input/tests/output/funcbind_test.out diff --git a/input/tests/test.sh b/components/engine/input/tests/test.sh similarity index 100% rename from input/tests/test.sh rename to components/engine/input/tests/test.sh diff --git a/ogre/.gitignore b/components/engine/ogre/.gitignore similarity index 100% rename from ogre/.gitignore rename to components/engine/ogre/.gitignore diff --git a/ogre/renderer.cpp b/components/engine/ogre/renderer.cpp similarity index 100% rename from ogre/renderer.cpp rename to components/engine/ogre/renderer.cpp diff --git a/ogre/renderer.hpp b/components/engine/ogre/renderer.hpp similarity index 100% rename from ogre/renderer.hpp rename to components/engine/ogre/renderer.hpp diff --git a/esm/.gitignore b/components/esm/.gitignore similarity index 100% rename from esm/.gitignore rename to components/esm/.gitignore diff --git a/esm/defs.hpp b/components/esm/defs.hpp similarity index 100% rename from esm/defs.hpp rename to components/esm/defs.hpp diff --git a/esm/esm_reader.hpp b/components/esm/esm_reader.hpp similarity index 97% rename from esm/esm_reader.hpp rename to components/esm/esm_reader.hpp index 52f67fea0..7db21323f 100644 --- a/esm/esm_reader.hpp +++ b/components/esm/esm_reader.hpp @@ -2,17 +2,17 @@ #define _ESM_READER_H #include -#include +#include #include #include #include #include #include -#include -#include -#include -#include "tools/stringops.hpp" +#include +#include +#include +#include #ifdef __APPLE__ // need our own implementation of strnlen @@ -217,7 +217,7 @@ public: /// Raw opening. Opens the file and sets everything up but doesn't /// parse the header. void openRaw(Mangle::Stream::StreamPtr _esm, const std::string &name) - { + { close(); esm = _esm; c.filename = name; @@ -394,7 +394,7 @@ public: void getHExact(void*p, int size) { getSubHeader(); - if(size != c.leftSub) + if(size !=static_cast (c.leftSub)) fail("getHExact() size mismatch"); getExact(p,size); } @@ -480,7 +480,7 @@ public: void skipHSubSize(int size) { skipHSub(); - if(c.leftSub != size) + if(static_cast (c.leftSub) != size) fail("skipHSubSize() mismatch"); } @@ -508,7 +508,7 @@ public: void getSubHeaderIs(int size) { getSubHeader(); - if(size != c.leftSub) + if(size != static_cast (c.leftSub)) fail("getSubHeaderIs(): Sub header mismatch"); } diff --git a/esm/loadacti.hpp b/components/esm/loadacti.hpp similarity index 100% rename from esm/loadacti.hpp rename to components/esm/loadacti.hpp diff --git a/esm/loadalch.hpp b/components/esm/loadalch.hpp similarity index 100% rename from esm/loadalch.hpp rename to components/esm/loadalch.hpp diff --git a/esm/loadappa.hpp b/components/esm/loadappa.hpp similarity index 100% rename from esm/loadappa.hpp rename to components/esm/loadappa.hpp diff --git a/esm/loadarmo.hpp b/components/esm/loadarmo.hpp similarity index 100% rename from esm/loadarmo.hpp rename to components/esm/loadarmo.hpp diff --git a/esm/loadbody.hpp b/components/esm/loadbody.hpp similarity index 100% rename from esm/loadbody.hpp rename to components/esm/loadbody.hpp diff --git a/esm/loadbook.hpp b/components/esm/loadbook.hpp similarity index 100% rename from esm/loadbook.hpp rename to components/esm/loadbook.hpp diff --git a/esm/loadbsgn.hpp b/components/esm/loadbsgn.hpp similarity index 100% rename from esm/loadbsgn.hpp rename to components/esm/loadbsgn.hpp diff --git a/esm/loadcell.hpp b/components/esm/loadcell.hpp similarity index 100% rename from esm/loadcell.hpp rename to components/esm/loadcell.hpp diff --git a/esm/loadclas.hpp b/components/esm/loadclas.hpp similarity index 100% rename from esm/loadclas.hpp rename to components/esm/loadclas.hpp diff --git a/esm/loadclot.hpp b/components/esm/loadclot.hpp similarity index 100% rename from esm/loadclot.hpp rename to components/esm/loadclot.hpp diff --git a/esm/loadcont.hpp b/components/esm/loadcont.hpp similarity index 100% rename from esm/loadcont.hpp rename to components/esm/loadcont.hpp diff --git a/esm/loadcrea.hpp b/components/esm/loadcrea.hpp similarity index 100% rename from esm/loadcrea.hpp rename to components/esm/loadcrea.hpp diff --git a/esm/loadcrec.hpp b/components/esm/loadcrec.hpp similarity index 100% rename from esm/loadcrec.hpp rename to components/esm/loadcrec.hpp diff --git a/esm/loaddial.hpp b/components/esm/loaddial.hpp similarity index 100% rename from esm/loaddial.hpp rename to components/esm/loaddial.hpp diff --git a/esm/loaddoor.hpp b/components/esm/loaddoor.hpp similarity index 100% rename from esm/loaddoor.hpp rename to components/esm/loaddoor.hpp diff --git a/esm/loadench.hpp b/components/esm/loadench.hpp similarity index 100% rename from esm/loadench.hpp rename to components/esm/loadench.hpp diff --git a/esm/loadfact.hpp b/components/esm/loadfact.hpp similarity index 100% rename from esm/loadfact.hpp rename to components/esm/loadfact.hpp diff --git a/esm/loadglob.hpp b/components/esm/loadglob.hpp similarity index 76% rename from esm/loadglob.hpp rename to components/esm/loadglob.hpp index ca1675097..9de3e1a46 100644 --- a/esm/loadglob.hpp +++ b/components/esm/loadglob.hpp @@ -11,7 +11,7 @@ namespace ESM { struct Global { - float value; + unsigned value; VarType type; void load(ESMReader &esm) @@ -24,8 +24,7 @@ struct Global else esm.fail("Illegal global variable type " + tmp); type = t; - // The value looks like a float in many cases, and like an integer - // in others (for the s type at least.) Figure it out. + // Note: Both floats and longs are represented as floats. esm.getHNT(value, "FLTV"); } }; diff --git a/esm/loadgmst.hpp b/components/esm/loadgmst.hpp similarity index 100% rename from esm/loadgmst.hpp rename to components/esm/loadgmst.hpp diff --git a/esm/loadinfo.hpp b/components/esm/loadinfo.hpp similarity index 100% rename from esm/loadinfo.hpp rename to components/esm/loadinfo.hpp diff --git a/esm/loadingr.hpp b/components/esm/loadingr.hpp similarity index 100% rename from esm/loadingr.hpp rename to components/esm/loadingr.hpp diff --git a/esm/loadland.hpp b/components/esm/loadland.hpp similarity index 100% rename from esm/loadland.hpp rename to components/esm/loadland.hpp diff --git a/esm/loadlevlist.hpp b/components/esm/loadlevlist.hpp similarity index 100% rename from esm/loadlevlist.hpp rename to components/esm/loadlevlist.hpp diff --git a/esm/loadligh.hpp b/components/esm/loadligh.hpp similarity index 96% rename from esm/loadligh.hpp rename to components/esm/loadligh.hpp index 30b4fe774..d78bc5767 100644 --- a/esm/loadligh.hpp +++ b/components/esm/loadligh.hpp @@ -44,6 +44,7 @@ struct Light model = esm.getHNString("MODL"); name = esm.getHNOString("FNAM"); icon = esm.getHNOString("ITEX"); + assert(sizeof(data) == 24); esm.getHNT(data, "LHDT", 24); script = esm.getHNOString("SCRI"); sound = esm.getHNOString("SNAM"); diff --git a/esm/loadlocks.hpp b/components/esm/loadlocks.hpp similarity index 100% rename from esm/loadlocks.hpp rename to components/esm/loadlocks.hpp diff --git a/esm/loadltex.hpp b/components/esm/loadltex.hpp similarity index 100% rename from esm/loadltex.hpp rename to components/esm/loadltex.hpp diff --git a/esm/loadmgef.hpp b/components/esm/loadmgef.hpp similarity index 100% rename from esm/loadmgef.hpp rename to components/esm/loadmgef.hpp diff --git a/esm/loadmisc.hpp b/components/esm/loadmisc.hpp similarity index 100% rename from esm/loadmisc.hpp rename to components/esm/loadmisc.hpp diff --git a/esm/loadnpc.hpp b/components/esm/loadnpc.hpp similarity index 100% rename from esm/loadnpc.hpp rename to components/esm/loadnpc.hpp diff --git a/esm/loadnpcc.hpp b/components/esm/loadnpcc.hpp similarity index 100% rename from esm/loadnpcc.hpp rename to components/esm/loadnpcc.hpp diff --git a/esm/loadpgrd.hpp b/components/esm/loadpgrd.hpp similarity index 100% rename from esm/loadpgrd.hpp rename to components/esm/loadpgrd.hpp diff --git a/esm/loadrace.hpp b/components/esm/loadrace.hpp similarity index 100% rename from esm/loadrace.hpp rename to components/esm/loadrace.hpp diff --git a/esm/loadregn.hpp b/components/esm/loadregn.hpp similarity index 100% rename from esm/loadregn.hpp rename to components/esm/loadregn.hpp diff --git a/esm/loadscpt.hpp b/components/esm/loadscpt.hpp similarity index 100% rename from esm/loadscpt.hpp rename to components/esm/loadscpt.hpp diff --git a/esm/loadskil.hpp b/components/esm/loadskil.hpp similarity index 100% rename from esm/loadskil.hpp rename to components/esm/loadskil.hpp diff --git a/esm/loadsndg.hpp b/components/esm/loadsndg.hpp similarity index 100% rename from esm/loadsndg.hpp rename to components/esm/loadsndg.hpp diff --git a/esm/loadsoun.hpp b/components/esm/loadsoun.hpp similarity index 100% rename from esm/loadsoun.hpp rename to components/esm/loadsoun.hpp diff --git a/esm/loadspel.hpp b/components/esm/loadspel.hpp similarity index 100% rename from esm/loadspel.hpp rename to components/esm/loadspel.hpp diff --git a/esm/loadsscr.hpp b/components/esm/loadsscr.hpp similarity index 100% rename from esm/loadsscr.hpp rename to components/esm/loadsscr.hpp diff --git a/esm/loadstat.hpp b/components/esm/loadstat.hpp similarity index 100% rename from esm/loadstat.hpp rename to components/esm/loadstat.hpp diff --git a/esm/loadweap.hpp b/components/esm/loadweap.hpp similarity index 100% rename from esm/loadweap.hpp rename to components/esm/loadweap.hpp diff --git a/esm/records.hpp b/components/esm/records.hpp similarity index 100% rename from esm/records.hpp rename to components/esm/records.hpp diff --git a/esm/tests/.gitignore b/components/esm/tests/.gitignore similarity index 100% rename from esm/tests/.gitignore rename to components/esm/tests/.gitignore diff --git a/esm/tests/Makefile b/components/esm/tests/Makefile similarity index 100% rename from esm/tests/Makefile rename to components/esm/tests/Makefile diff --git a/esm/tests/esmtool.cpp b/components/esm/tests/esmtool.cpp similarity index 100% rename from esm/tests/esmtool.cpp rename to components/esm/tests/esmtool.cpp diff --git a/esm/tests/esmtool.ggo b/components/esm/tests/esmtool.ggo similarity index 100% rename from esm/tests/esmtool.ggo rename to components/esm/tests/esmtool.ggo diff --git a/esm/tests/esmtool_cmd.c b/components/esm/tests/esmtool_cmd.c similarity index 100% rename from esm/tests/esmtool_cmd.c rename to components/esm/tests/esmtool_cmd.c diff --git a/esm/tests/esmtool_cmd.h b/components/esm/tests/esmtool_cmd.h similarity index 100% rename from esm/tests/esmtool_cmd.h rename to components/esm/tests/esmtool_cmd.h diff --git a/components/esm_store/cell_store.hpp b/components/esm_store/cell_store.hpp new file mode 100644 index 000000000..90cd5d1a6 --- /dev/null +++ b/components/esm_store/cell_store.hpp @@ -0,0 +1,174 @@ +#ifndef _GAME_CELL_STORE_H +#define _GAME_CELL_STORE_H + +/* + Cell storage. + + Used to load, look up and store all references in a single cell. + + Depends on esm/loadcell.hpp (loading from ESM) and esm_store.hpp + (looking up references.) Neither of these modules depend on us. + */ + +#include "store.hpp" +#include "components/esm/records.hpp" +#include "components/esm/loadcell.hpp" +#include +#include + +#include +#include "libs/mangle/tools/str_exception.hpp" + +namespace ESMS +{ + using namespace ESM; + + /// A reference to one object (of any type) in a cell. + template + struct LiveCellRef + { + // The object that this instance is based on. + const X* base; + + /* Information about this instance, such as 3D location and + rotation and individual type-dependent data. + */ + CellRef ref; + + /// runtime-data + D mData; + }; + + /// A list of cell references + template + struct CellRefList + { + typedef LiveCellRef LiveRef; + typedef std::list List; + List list; + + // Search for the given reference in the given reclist from + // ESMStore. Insert the reference into the list if a match is + // found. If not, throw an exception. + template + void find(CellRef &ref, const Y& recList) + { + const X* obj = recList.find(ref.refID); + if(obj == NULL) + throw str_exception("Error resolving cell reference " + ref.refID); + + LiveRef lr; + lr.ref = ref; + lr.base = obj; + + list.push_back(lr); + } + }; + + /// A storage struct for one single cell reference. + template + class CellStore + { + public: + CellStore() : cell (0) {} + + const ESM::Cell *cell; + + // Lists for each individual object type + CellRefList activators; + CellRefList potions; + CellRefList appas; + CellRefList armors; + CellRefList books; + CellRefList clothes; + CellRefList containers; + CellRefList creatures; + CellRefList doors; + CellRefList ingreds; + CellRefList creatureLists; + CellRefList itemLists; + CellRefList lights; + CellRefList lockpicks; + CellRefList miscItems; + CellRefList npcs; + CellRefList probes; + CellRefList repairs; + CellRefList statics; + CellRefList weapons; + + /** Look up and load an interior cell from the given ESM data + storage. */ + void loadInt(const std::string &name, const ESMStore &store, ESMReader &esm) + { + std::cout << "loading cell '" << name << "'\n"; + + cell = store.cells.findInt(name); + + if(cell == NULL) + throw str_exception("Cell not found - " + name); + + loadRefs(store, esm); + } + + /** Ditto for exterior cell. */ + void loadExt(int X, int Y, const ESMStore &store, ESMReader &esm) + { + + } + + private: + void loadRefs(const ESMStore &store, ESMReader &esm) + { + assert (cell); + + // Reopen the ESM reader and seek to the right position. + cell->restore(esm); + + CellRef ref; + + // Get each reference in turn + while(cell->getNextRef(esm, ref)) + { + int rec = store.find(ref.refID); + + /* We can optimize this further by storing the pointer to the + record itself in store.all, so that we don't need to look it + up again here. However, never optimize. There are infinite + opportunities to do that later. + */ + switch(rec) + { + case REC_ACTI: activators.find(ref, store.activators); break; + case REC_ALCH: potions.find(ref, store.potions); break; + case REC_APPA: appas.find(ref, store.appas); break; + case REC_ARMO: armors.find(ref, store.armors); break; + case REC_BOOK: books.find(ref, store.books); break; + case REC_CLOT: clothes.find(ref, store.clothes); break; + case REC_CONT: containers.find(ref, store.containers); break; + case REC_CREA: creatures.find(ref, store.creatures); break; + case REC_DOOR: doors.find(ref, store.doors); break; + case REC_INGR: ingreds.find(ref, store.ingreds); break; + case REC_LEVC: creatureLists.find(ref, store.creatureLists); break; + case REC_LEVI: itemLists.find(ref, store.itemLists); break; + case REC_LIGH: lights.find(ref, store.lights); break; + case REC_LOCK: lockpicks.find(ref, store.lockpicks); break; + case REC_MISC: miscItems.find(ref, store.miscItems); break; + case REC_NPC_: npcs.find(ref, store.npcs); break; + case REC_PROB: probes.find(ref, store.probes); break; + case REC_REPA: repairs.find(ref, store.repairs); break; + case REC_STAT: statics.find(ref, store.statics); break; + case REC_WEAP: weapons.find(ref, store.weapons); break; + + case 0: std::cout << "Cell reference " + ref.refID + " not found!\n"; break; + default: + assert(0); + } + } + + std::cout << "Statics in cell: " << statics.list.size() << std::endl; + } + + }; +} + +#endif diff --git a/esm_store/reclists.hpp b/components/esm_store/reclists.hpp similarity index 68% rename from esm_store/reclists.hpp rename to components/esm_store/reclists.hpp index 6fd6e9f72..83181b687 100644 --- a/esm_store/reclists.hpp +++ b/components/esm_store/reclists.hpp @@ -1,9 +1,11 @@ #ifndef _GAME_ESM_RECLISTS_H #define _GAME_ESM_RECLISTS_H -#include "esm/records.hpp" +#include "components/esm/records.hpp" #include #include +#include +#include #include namespace ESMS @@ -14,6 +16,16 @@ namespace ESMS { virtual void load(ESMReader &esm, const std::string &id) = 0; virtual int getSize() = 0; + + static std::string toLower (const std::string& name) + { + std::string lowerCase; + + std::transform (name.begin(), name.end(), std::back_inserter (lowerCase), + (int(*)(int)) std::tolower); + + return lowerCase; + } }; typedef std::map RecListList; @@ -28,15 +40,17 @@ namespace ESMS // Load one object of this type void load(ESMReader &esm, const std::string &id) { - list[id].load(esm); + std::string id2 = toLower (id); + list[id2].load(esm); } // Find the given object ID, or return NULL if not found. const X* find(const std::string &id) const { - if(list.find(id) == list.end()) + std::string id2 = toLower (id); + if(list.find(id2) == list.end()) return NULL; - return &list.find(id)->second; + return &list.find(id2)->second; } int getSize() { return list.size(); } @@ -54,7 +68,8 @@ namespace ESMS void load(ESMReader &esm, const std::string &id) { - X& ref = list[id]; + std::string id2 = toLower (id); + X& ref = list[id2]; ref.id = id; ref.load(esm); @@ -65,6 +80,8 @@ namespace ESMS // Cells aren't simply indexed by name. Exterior cells are treated // separately. + // TODO: case handling (cell names are case-insensitive, but they are also showen to the + // player, so we can't simply smash case. struct CellList : RecList { // Total cell count. Used for statistics. @@ -131,6 +148,36 @@ namespace ESMS } } }; + + template + struct ScriptListT : RecList + { + typedef std::map MapType; + + MapType list; + + // Load one object of this type + void load(ESMReader &esm, const std::string &id) + { + X ref; + ref.load (esm); + + std::string realId = toLower (ref.data.name.toString()); + + std::swap (list[realId], ref); + } + + // Find the given object ID, or return NULL if not found. + const X* find(const std::string &id) const + { + std::string id2 = toLower (id); + if(list.find(id2) == list.end()) + return NULL; + return &list.find(id2)->second; + } + + int getSize() { return list.size(); } + }; /* We need special lists for: diff --git a/esm_store/store.cpp b/components/esm_store/store.cpp similarity index 100% rename from esm_store/store.cpp rename to components/esm_store/store.cpp diff --git a/esm_store/store.hpp b/components/esm_store/store.hpp similarity index 97% rename from esm_store/store.hpp rename to components/esm_store/store.hpp index 2271f4238..7c4971ac4 100644 --- a/esm_store/store.hpp +++ b/components/esm_store/store.hpp @@ -13,7 +13,7 @@ maintainability. */ -#include "esm/records.hpp" +#include "components/esm/records.hpp" #include "reclists.hpp" namespace ESMS @@ -73,7 +73,7 @@ namespace ESMS //RecListT lands; //RecListT landTexts; //RecListT magicEffects; - //RecListT