mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-04-29 21:37:57 +03:00
Imported Upstream version 0.26.0
This commit is contained in:
commit
9a2b6c69b6
1398 changed files with 212217 additions and 0 deletions
102
components/CMakeLists.txt
Normal file
102
components/CMakeLists.txt
Normal file
|
@ -0,0 +1,102 @@
|
|||
project (Components)
|
||||
|
||||
# source files
|
||||
|
||||
add_component_dir (settings
|
||||
settings
|
||||
)
|
||||
|
||||
add_component_dir (nifoverrides
|
||||
nifoverrides
|
||||
)
|
||||
|
||||
add_component_dir (bsa
|
||||
bsa_archive bsa_file
|
||||
)
|
||||
|
||||
add_component_dir (nif
|
||||
controlled effect niftypes record controller extra node record_ptr data niffile property
|
||||
)
|
||||
|
||||
add_component_dir (nifogre
|
||||
ogrenifloader skeleton material mesh
|
||||
)
|
||||
|
||||
add_component_dir (nifbullet
|
||||
bulletnifloader
|
||||
)
|
||||
|
||||
add_component_dir (to_utf8
|
||||
to_utf8
|
||||
)
|
||||
|
||||
add_component_dir (file_finder
|
||||
file_finder filename_less search
|
||||
)
|
||||
|
||||
add_component_dir (esm
|
||||
attr defs esmcommon esmreader esmwriter loadacti loadalch loadappa loadarmo loadbody loadbook loadbsgn loadcell
|
||||
loadclas loadclot loadcont loadcrea loadcrec loaddial loaddoor loadench loadfact loadglob loadgmst
|
||||
loadinfo loadingr loadland loadlevlist loadligh loadlock loadprob loadrepa loadltex loadmgef loadmisc loadnpcc
|
||||
loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat
|
||||
loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter
|
||||
)
|
||||
|
||||
add_component_dir (misc
|
||||
slice_array stringops
|
||||
)
|
||||
|
||||
add_component_dir (files
|
||||
linuxpath windowspath macospath fixedpath multidircollection collections fileops configurationmanager
|
||||
filelibrary ogreplugin constrainedfiledatastream lowlevelfile
|
||||
)
|
||||
|
||||
add_component_dir (compiler
|
||||
context controlparser errorhandler exception exprparser extensions fileparser generator
|
||||
lineparser literals locals output parser scanner scriptparser skipparser streamerrorhandler
|
||||
stringparser tokenloc nullerrorhandler opcodes extensions0
|
||||
)
|
||||
|
||||
add_component_dir (interpreter
|
||||
context controlopcodes genericopcodes installopcodes interpreter localopcodes mathopcodes
|
||||
miscopcodes opcodes runtime scriptopcodes spatialopcodes types defines
|
||||
)
|
||||
|
||||
add_component_dir (translation
|
||||
translation
|
||||
)
|
||||
|
||||
add_component_dir (terrain
|
||||
quadtreenode chunk world storage material
|
||||
)
|
||||
|
||||
add_component_dir (loadinglistener
|
||||
loadinglistener
|
||||
)
|
||||
|
||||
find_package(Qt4 COMPONENTS QtCore QtGui)
|
||||
|
||||
if(QT_QTGUI_LIBRARY AND QT_QTCORE_LIBRARY)
|
||||
add_component_qt_dir (fileorderlist
|
||||
model/modelitem model/datafilesmodel model/pluginsproxymodel model/esm/esmfile
|
||||
utils/profilescombobox utils/comboboxlineedit utils/lineedit utils/naturalsort
|
||||
)
|
||||
|
||||
include(${QT_USE_FILE})
|
||||
QT4_WRAP_CPP(MOC_SRCS ${COMPONENT_MOC_FILES})
|
||||
endif(QT_QTGUI_LIBRARY AND QT_QTCORE_LIBRARY)
|
||||
|
||||
include_directories(${BULLET_INCLUDE_DIRS})
|
||||
|
||||
add_library(components STATIC ${COMPONENT_FILES} ${MOC_SRCS})
|
||||
|
||||
target_link_libraries(components ${Boost_LIBRARIES} ${OGRE_LIBRARIES})
|
||||
|
||||
# Fix for not visible pthreads functions for linker with glibc 2.15
|
||||
if (UNIX AND NOT APPLE)
|
||||
target_link_libraries(components ${CMAKE_THREAD_LIBS_INIT})
|
||||
endif()
|
||||
|
||||
|
||||
# Make the variable accessible for other subdirectories
|
||||
set(COMPONENT_FILES ${COMPONENT_FILES} PARENT_SCOPE)
|
382
components/bsa/bsa_archive.cpp
Normal file
382
components/bsa/bsa_archive.cpp
Normal file
|
@ -0,0 +1,382 @@
|
|||
/*
|
||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
||||
Copyright (C) 2008-2010 Nicolay Korslund
|
||||
Email: < korslund@gmail.com >
|
||||
WWW: http://openmw.sourceforge.net/
|
||||
|
||||
This file (cpp_bsaarchive.cpp) is part of the OpenMW package.
|
||||
|
||||
OpenMW is distributed as free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License
|
||||
version 3, as published by the Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
version 3 along with this program. If not, see
|
||||
http://www.gnu.org/licenses/ .
|
||||
|
||||
*/
|
||||
|
||||
#include "bsa_archive.hpp"
|
||||
|
||||
#include <OgreFileSystem.h>
|
||||
#include <OgreArchive.h>
|
||||
#include <OgreArchiveFactory.h>
|
||||
#include <OgreArchiveManager.h>
|
||||
#include "bsa_file.hpp"
|
||||
|
||||
#include "../files/constrainedfiledatastream.hpp"
|
||||
|
||||
using namespace Ogre;
|
||||
|
||||
static bool fsstrict = false;
|
||||
|
||||
static char strict_normalize_char(char ch)
|
||||
{
|
||||
return ch == '\\' ? '/' : ch;
|
||||
}
|
||||
|
||||
static char nonstrict_normalize_char(char ch)
|
||||
{
|
||||
return ch == '\\' ? '/' : std::tolower(ch);
|
||||
}
|
||||
|
||||
template<typename T1, typename T2>
|
||||
static std::string normalize_path(T1 begin, T2 end)
|
||||
{
|
||||
std::string normalized;
|
||||
normalized.reserve(std::distance(begin, end));
|
||||
char (*normalize_char)(char) = fsstrict ? &strict_normalize_char : &nonstrict_normalize_char;
|
||||
std::transform(begin, end, std::back_inserter(normalized), normalize_char);
|
||||
return normalized;
|
||||
}
|
||||
|
||||
/// An OGRE Archive wrapping a BSAFile archive
|
||||
class DirArchive: public Ogre::Archive
|
||||
{
|
||||
typedef std::map <std::string, std::string> index;
|
||||
|
||||
index mIndex;
|
||||
|
||||
index::const_iterator lookup_filename (std::string const & filename) const
|
||||
{
|
||||
std::string normalized = normalize_path (filename.begin (), filename.end ());
|
||||
return mIndex.find (normalized);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
DirArchive(const String& name)
|
||||
: Archive(name, "Dir")
|
||||
{
|
||||
typedef boost::filesystem::recursive_directory_iterator directory_iterator;
|
||||
|
||||
directory_iterator end;
|
||||
|
||||
size_t prefix = name.size ();
|
||||
|
||||
if (name.size () > 0 && name [prefix - 1] != '\\' && name [prefix - 1] != '/')
|
||||
++prefix;
|
||||
|
||||
for (directory_iterator i (name); i != end; ++i)
|
||||
{
|
||||
if(boost::filesystem::is_directory (*i))
|
||||
continue;
|
||||
|
||||
std::string proper = i->path ().string ();
|
||||
|
||||
std::string searchable = normalize_path (proper.begin () + prefix, proper.end ());
|
||||
|
||||
mIndex.insert (std::make_pair (searchable, proper));
|
||||
}
|
||||
}
|
||||
|
||||
bool isCaseSensitive() const { return fsstrict; }
|
||||
|
||||
// The archive is loaded in the constructor, and never unloaded.
|
||||
void load() {}
|
||||
void unload() {}
|
||||
|
||||
DataStreamPtr open(const String& filename, bool readonly = true) const
|
||||
{
|
||||
index::const_iterator i = lookup_filename (filename);
|
||||
|
||||
if (i == mIndex.end ())
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "The file '" << filename << "' could not be found.";
|
||||
throw std::runtime_error (os.str ());
|
||||
}
|
||||
|
||||
return openConstrainedFileDataStream (i->second.c_str ());
|
||||
}
|
||||
|
||||
StringVectorPtr list(bool recursive = true, bool dirs = false)
|
||||
{
|
||||
return find ("*", recursive, dirs);
|
||||
}
|
||||
|
||||
FileInfoListPtr listFileInfo(bool recursive = true, bool dirs = false)
|
||||
{
|
||||
return findFileInfo ("*", recursive, dirs);
|
||||
}
|
||||
|
||||
StringVectorPtr find(const String& pattern, bool recursive = true,
|
||||
bool dirs = false)
|
||||
{
|
||||
std::string normalizedPattern = normalize_path(pattern.begin(), pattern.end());
|
||||
StringVectorPtr ptr = StringVectorPtr(new StringVector());
|
||||
for(index::const_iterator iter = mIndex.begin();iter != mIndex.end();++iter)
|
||||
{
|
||||
if(Ogre::StringUtil::match(iter->first, normalizedPattern) ||
|
||||
(recursive && Ogre::StringUtil::match(iter->first, "*/"+normalizedPattern)))
|
||||
ptr->push_back(iter->first);
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
bool exists(const String& filename)
|
||||
{
|
||||
return lookup_filename(filename) != mIndex.end ();
|
||||
}
|
||||
|
||||
time_t getModifiedTime(const String&) { return 0; }
|
||||
|
||||
FileInfoListPtr findFileInfo(const String& pattern, bool recursive = true,
|
||||
bool dirs = false) const
|
||||
{
|
||||
std::string normalizedPattern = normalize_path(pattern.begin(), pattern.end());
|
||||
FileInfoListPtr ptr = FileInfoListPtr(new FileInfoList());
|
||||
|
||||
index::const_iterator i = mIndex.find(normalizedPattern);
|
||||
if(i != mIndex.end())
|
||||
{
|
||||
std::string::size_type pt = i->first.rfind('/');
|
||||
if(pt == std::string::npos)
|
||||
pt = 0;
|
||||
|
||||
FileInfo fi;
|
||||
fi.archive = const_cast<DirArchive*>(this);
|
||||
fi.path = i->first.substr(0, pt);
|
||||
fi.filename = i->first.substr((i->first[pt]=='/') ? pt+1 : pt);
|
||||
fi.compressedSize = fi.uncompressedSize = 0;
|
||||
|
||||
ptr->push_back(fi);
|
||||
}
|
||||
else
|
||||
{
|
||||
for(index::const_iterator iter = mIndex.begin();iter != mIndex.end();++iter)
|
||||
{
|
||||
if(Ogre::StringUtil::match(iter->first, normalizedPattern) ||
|
||||
(recursive && Ogre::StringUtil::match(iter->first, "*/"+normalizedPattern)))
|
||||
{
|
||||
std::string::size_type pt = iter->first.rfind('/');
|
||||
if(pt == std::string::npos)
|
||||
pt = 0;
|
||||
|
||||
FileInfo fi;
|
||||
fi.archive = const_cast<DirArchive*>(this);
|
||||
fi.path = iter->first.substr(0, pt);
|
||||
fi.filename = iter->first.substr((iter->first[pt]=='/') ? pt+1 : pt);
|
||||
fi.compressedSize = fi.uncompressedSize = 0;
|
||||
|
||||
ptr->push_back(fi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
};
|
||||
|
||||
class BSAArchive : public Archive
|
||||
{
|
||||
Bsa::BSAFile arc;
|
||||
|
||||
static const char *extractFilename(const Bsa::BSAFile::FileStruct &entry)
|
||||
{
|
||||
return entry.name;
|
||||
}
|
||||
|
||||
public:
|
||||
BSAArchive(const String& name)
|
||||
: Archive(name, "BSA")
|
||||
{ arc.open(name); }
|
||||
|
||||
bool isCaseSensitive() const { return false; }
|
||||
|
||||
// The archive is loaded in the constructor, and never unloaded.
|
||||
void load() {}
|
||||
void unload() {}
|
||||
|
||||
DataStreamPtr open(const String& filename, bool readonly = true) const
|
||||
{
|
||||
// Get a non-const reference to arc. This is a hack and it's all
|
||||
// OGRE's fault. You should NOT expect an open() command not to
|
||||
// have any side effects on the archive, and hence this function
|
||||
// should not have been declared const in the first place.
|
||||
Bsa::BSAFile *narc = const_cast<Bsa::BSAFile*>(&arc);
|
||||
|
||||
// Open the file
|
||||
return narc->getFile(filename.c_str());
|
||||
}
|
||||
|
||||
bool exists(const String& filename) {
|
||||
return arc.exists(filename.c_str());
|
||||
}
|
||||
|
||||
time_t getModifiedTime(const String&) { return 0; }
|
||||
|
||||
// This is never called as far as I can see.
|
||||
StringVectorPtr list(bool recursive = true, bool dirs = false)
|
||||
{
|
||||
return find ("*", recursive, dirs);
|
||||
}
|
||||
|
||||
// Also never called.
|
||||
FileInfoListPtr listFileInfo(bool recursive = true, bool dirs = false)
|
||||
{
|
||||
return findFileInfo ("*", recursive, dirs);
|
||||
}
|
||||
|
||||
StringVectorPtr find(const String& pattern, bool recursive = true,
|
||||
bool dirs = false)
|
||||
{
|
||||
std::string normalizedPattern = normalize_path(pattern.begin(), pattern.end());
|
||||
const Bsa::BSAFile::FileList &filelist = arc.getList();
|
||||
StringVectorPtr ptr = StringVectorPtr(new StringVector());
|
||||
for(Bsa::BSAFile::FileList::const_iterator iter = filelist.begin();iter != filelist.end();++iter)
|
||||
{
|
||||
std::string ent = normalize_path(iter->name, iter->name+std::strlen(iter->name));
|
||||
if(Ogre::StringUtil::match(ent, normalizedPattern) ||
|
||||
(recursive && Ogre::StringUtil::match(ent, "*/"+normalizedPattern)))
|
||||
ptr->push_back(iter->name);
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
FileInfoListPtr findFileInfo(const String& pattern, bool recursive = true,
|
||||
bool dirs = false) const
|
||||
{
|
||||
std::string normalizedPattern = normalize_path(pattern.begin(), pattern.end());
|
||||
FileInfoListPtr ptr = FileInfoListPtr(new FileInfoList());
|
||||
const Bsa::BSAFile::FileList &filelist = arc.getList();
|
||||
|
||||
for(Bsa::BSAFile::FileList::const_iterator iter = filelist.begin();iter != filelist.end();++iter)
|
||||
{
|
||||
std::string ent = normalize_path(iter->name, iter->name+std::strlen(iter->name));
|
||||
if(Ogre::StringUtil::match(ent, normalizedPattern) ||
|
||||
(recursive && Ogre::StringUtil::match(ent, "*/"+normalizedPattern)))
|
||||
{
|
||||
std::string::size_type pt = ent.rfind('/');
|
||||
if(pt == std::string::npos)
|
||||
pt = 0;
|
||||
|
||||
FileInfo fi;
|
||||
fi.archive = const_cast<BSAArchive*>(this);
|
||||
fi.path = std::string(iter->name, pt);
|
||||
fi.filename = std::string(iter->name + ((ent[pt]=='/') ? pt+1 : pt));
|
||||
fi.compressedSize = fi.uncompressedSize = iter->fileSize;
|
||||
|
||||
ptr->push_back(fi);
|
||||
}
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
};
|
||||
|
||||
// An archive factory for BSA archives
|
||||
class BSAArchiveFactory : public ArchiveFactory
|
||||
{
|
||||
public:
|
||||
const String& getType() const
|
||||
{
|
||||
static String name = "BSA";
|
||||
return name;
|
||||
}
|
||||
|
||||
Archive *createInstance( const String& name )
|
||||
{
|
||||
return new BSAArchive(name);
|
||||
}
|
||||
|
||||
virtual Archive* createInstance(const String& name, bool readOnly)
|
||||
{
|
||||
return new BSAArchive(name);
|
||||
}
|
||||
|
||||
void destroyInstance( Archive* arch) { delete arch; }
|
||||
};
|
||||
|
||||
class DirArchiveFactory : public ArchiveFactory
|
||||
{
|
||||
public:
|
||||
const String& getType() const
|
||||
{
|
||||
static String name = "Dir";
|
||||
return name;
|
||||
}
|
||||
|
||||
Archive *createInstance( const String& name )
|
||||
{
|
||||
return new DirArchive(name);
|
||||
}
|
||||
|
||||
virtual Archive* createInstance(const String& name, bool readOnly)
|
||||
{
|
||||
return new DirArchive(name);
|
||||
}
|
||||
|
||||
void destroyInstance( Archive* arch) { delete arch; }
|
||||
};
|
||||
|
||||
|
||||
static bool init = false;
|
||||
static bool init2 = false;
|
||||
|
||||
static void insertBSAFactory()
|
||||
{
|
||||
if(!init)
|
||||
{
|
||||
ArchiveManager::getSingleton().addArchiveFactory( new BSAArchiveFactory );
|
||||
init = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void insertDirFactory()
|
||||
{
|
||||
if(!init2)
|
||||
{
|
||||
ArchiveManager::getSingleton().addArchiveFactory( new DirArchiveFactory );
|
||||
init2 = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
namespace Bsa
|
||||
{
|
||||
|
||||
// The function below is the only publicly exposed part of this file
|
||||
|
||||
void addBSA(const std::string& name, const std::string& group)
|
||||
{
|
||||
insertBSAFactory();
|
||||
ResourceGroupManager::getSingleton().
|
||||
addResourceLocation(name, "BSA", group, true);
|
||||
}
|
||||
|
||||
void addDir(const std::string& name, const bool& fs, const std::string& group)
|
||||
{
|
||||
fsstrict = fs;
|
||||
insertDirFactory();
|
||||
|
||||
ResourceGroupManager::getSingleton().
|
||||
addResourceLocation(name, "Dir", group, true);
|
||||
}
|
||||
|
||||
}
|
42
components/bsa/bsa_archive.hpp
Normal file
42
components/bsa/bsa_archive.hpp
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
||||
Copyright (C) 2008-2010 Nicolay Korslund
|
||||
Email: < korslund@gmail.com >
|
||||
WWW: http://openmw.sourceforge.net/
|
||||
|
||||
This file (cpp_bsaarchive.h) is part of the OpenMW package.
|
||||
|
||||
OpenMW is distributed as free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License
|
||||
version 3, as published by the Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
version 3 along with this program. If not, see
|
||||
http://www.gnu.org/licenses/ .
|
||||
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <algorithm>
|
||||
|
||||
#ifndef BSA_BSA_ARCHIVE_H
|
||||
#define BSA_BSA_ARCHIVE_H
|
||||
|
||||
namespace Bsa
|
||||
{
|
||||
|
||||
/// Add the given BSA file as an input archive in the Ogre resource
|
||||
/// system.
|
||||
void addBSA(const std::string& file, const std::string& group="General");
|
||||
void addDir(const std::string& file, const bool& fs, const std::string& group="General");
|
||||
|
||||
}
|
||||
|
||||
#endif
|
176
components/bsa/bsa_file.cpp
Normal file
176
components/bsa/bsa_file.cpp
Normal file
|
@ -0,0 +1,176 @@
|
|||
/*
|
||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
||||
Copyright (C) 2008-2010 Nicolay Korslund
|
||||
Email: < korslund@gmail.com >
|
||||
WWW: http://openmw.sourceforge.net/
|
||||
|
||||
This file (bsa_file.cpp) is part of the OpenMW package.
|
||||
|
||||
OpenMW is distributed as free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License
|
||||
version 3, as published by the Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
version 3 along with this program. If not, see
|
||||
http://www.gnu.org/licenses/ .
|
||||
|
||||
*/
|
||||
|
||||
#include "bsa_file.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include "../files/constrainedfiledatastream.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace Bsa;
|
||||
|
||||
|
||||
/// Error handling
|
||||
void BSAFile::fail(const string &msg)
|
||||
{
|
||||
throw std::runtime_error("BSA Error: " + msg + "\nArchive: " + filename);
|
||||
}
|
||||
|
||||
/// Read header information from the input source
|
||||
void BSAFile::readHeader()
|
||||
{
|
||||
/*
|
||||
* The layout of a BSA archive is as follows:
|
||||
*
|
||||
* - 12 bytes header, contains 3 ints:
|
||||
* id number - equal to 0x100
|
||||
* dirsize - size of the directory block (see below)
|
||||
* numfiles - number of files
|
||||
*
|
||||
* ---------- start of directory block -----------
|
||||
*
|
||||
* - 8 bytes*numfiles, each record contains:
|
||||
* fileSize
|
||||
* offset into data buffer (see below)
|
||||
*
|
||||
* - 4 bytes*numfiles, each record is an offset into the following name buffer
|
||||
*
|
||||
* - name buffer, indexed by the previous table, each string is
|
||||
* null-terminated. Size is (dirsize - 12*numfiles).
|
||||
*
|
||||
* ---------- end of directory block -------------
|
||||
*
|
||||
* - 8*filenum - hash table block, we currently ignore this
|
||||
*
|
||||
* ----------- start of data buffer --------------
|
||||
*
|
||||
* - The rest of the archive is file data, indexed by the
|
||||
* offsets in the directory block. The offsets start at 0 at
|
||||
* the beginning of this buffer.
|
||||
*
|
||||
*/
|
||||
assert(!isLoaded);
|
||||
|
||||
std::ifstream input(filename.c_str(), std::ios_base::binary);
|
||||
|
||||
// Total archive size
|
||||
size_t fsize = 0;
|
||||
if(input.seekg(0, std::ios_base::end))
|
||||
{
|
||||
fsize = input.tellg();
|
||||
input.seekg(0);
|
||||
}
|
||||
|
||||
if(fsize < 12)
|
||||
fail("File too small to be a valid BSA archive");
|
||||
|
||||
// Get essential header numbers
|
||||
size_t dirsize, filenum;
|
||||
{
|
||||
// First 12 bytes
|
||||
uint32_t head[3];
|
||||
|
||||
input.read(reinterpret_cast<char*>(head), 12);
|
||||
|
||||
if(head[0] != 0x100)
|
||||
fail("Unrecognized BSA header");
|
||||
|
||||
// Total number of bytes used in size/offset-table + filename
|
||||
// sections.
|
||||
dirsize = head[1];
|
||||
|
||||
// Number of files
|
||||
filenum = head[2];
|
||||
}
|
||||
|
||||
// Each file must take up at least 21 bytes of data in the bsa. So
|
||||
// if files*21 overflows the file size then we are guaranteed that
|
||||
// the archive is corrupt.
|
||||
if((filenum*21 > fsize -12) || (dirsize+8*filenum > fsize -12) )
|
||||
fail("Directory information larger than entire archive");
|
||||
|
||||
// Read the offset info into a temporary buffer
|
||||
vector<uint32_t> offsets(3*filenum);
|
||||
input.read(reinterpret_cast<char*>(&offsets[0]), 12*filenum);
|
||||
|
||||
// Read the string table
|
||||
stringBuf.resize(dirsize-12*filenum);
|
||||
input.read(&stringBuf[0], stringBuf.size());
|
||||
|
||||
// Check our position
|
||||
assert(input.tellg() == std::streampos(12+dirsize));
|
||||
|
||||
// Calculate the offset of the data buffer. All file offsets are
|
||||
// relative to this. 12 header bytes + directory + hash table
|
||||
// (skipped)
|
||||
size_t fileDataOffset = 12 + dirsize + 8*filenum;
|
||||
|
||||
// Set up the the FileStruct table
|
||||
files.resize(filenum);
|
||||
for(size_t i=0;i<filenum;i++)
|
||||
{
|
||||
FileStruct &fs = files[i];
|
||||
fs.fileSize = offsets[i*2];
|
||||
fs.offset = offsets[i*2+1] + fileDataOffset;
|
||||
fs.name = &stringBuf[offsets[2*filenum+i]];
|
||||
|
||||
if(fs.offset + fs.fileSize > fsize)
|
||||
fail("Archive contains offsets outside itself");
|
||||
|
||||
// Add the file name to the lookup
|
||||
lookup[fs.name] = i;
|
||||
}
|
||||
|
||||
isLoaded = true;
|
||||
}
|
||||
|
||||
/// Get the index of a given file name, or -1 if not found
|
||||
int BSAFile::getIndex(const char *str) const
|
||||
{
|
||||
Lookup::const_iterator it = lookup.find(str);
|
||||
if(it == lookup.end())
|
||||
return -1;
|
||||
|
||||
int res = it->second;
|
||||
assert(res >= 0 && (size_t)res < files.size());
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Open an archive file.
|
||||
void BSAFile::open(const string &file)
|
||||
{
|
||||
filename = file;
|
||||
readHeader();
|
||||
}
|
||||
|
||||
Ogre::DataStreamPtr BSAFile::getFile(const char *file)
|
||||
{
|
||||
assert(file);
|
||||
int i = getIndex(file);
|
||||
if(i == -1)
|
||||
fail("File not found: " + string(file));
|
||||
|
||||
const FileStruct &fs = files[i];
|
||||
return openConstrainedFileDataStream (filename.c_str (), fs.offset, fs.fileSize);
|
||||
}
|
128
components/bsa/bsa_file.hpp
Normal file
128
components/bsa/bsa_file.hpp
Normal file
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
||||
Copyright (C) 2008-2010 Nicolay Korslund
|
||||
Email: < korslund@gmail.com >
|
||||
WWW: http://openmw.sourceforge.net/
|
||||
|
||||
This file (bsa_file.h) is part of the OpenMW package.
|
||||
|
||||
OpenMW is distributed as free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License
|
||||
version 3, as published by the Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
version 3 along with this program. If not, see
|
||||
http://www.gnu.org/licenses/ .
|
||||
|
||||
*/
|
||||
|
||||
#ifndef BSA_BSA_FILE_H
|
||||
#define BSA_BSA_FILE_H
|
||||
|
||||
#include <libs/platform/stdint.h>
|
||||
#include <libs/platform/strings.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include <OgreDataStream.h>
|
||||
|
||||
|
||||
namespace Bsa
|
||||
{
|
||||
|
||||
/**
|
||||
This class is used to read "Bethesda Archive Files", or BSAs.
|
||||
*/
|
||||
class BSAFile
|
||||
{
|
||||
public:
|
||||
/// Represents one file entry in the archive
|
||||
struct FileStruct
|
||||
{
|
||||
// File size and offset in file. We store the offset from the
|
||||
// beginning of the file, not the offset into the data buffer
|
||||
// (which is what is stored in the archive.)
|
||||
uint32_t fileSize, offset;
|
||||
|
||||
// Zero-terminated file name
|
||||
const char *name;
|
||||
};
|
||||
typedef std::vector<FileStruct> FileList;
|
||||
|
||||
private:
|
||||
/// Table of files in this archive
|
||||
FileList files;
|
||||
|
||||
/// Filename string buffer
|
||||
std::vector<char> stringBuf;
|
||||
|
||||
/// True when an archive has been loaded
|
||||
bool isLoaded;
|
||||
|
||||
/// Used for error messages
|
||||
std::string filename;
|
||||
|
||||
/// Case insensitive string comparison
|
||||
struct iltstr
|
||||
{
|
||||
bool operator()(const char *s1, const char *s2) const
|
||||
{ return strcasecmp(s1,s2) < 0; }
|
||||
};
|
||||
|
||||
/** A map used for fast file name lookup. The value is the index into
|
||||
the files[] vector above. The iltstr ensures that file name
|
||||
checks are case insensitive.
|
||||
*/
|
||||
typedef std::map<const char*, int, iltstr> Lookup;
|
||||
Lookup lookup;
|
||||
|
||||
/// Error handling
|
||||
void fail(const std::string &msg);
|
||||
|
||||
/// Read header information from the input source
|
||||
void readHeader();
|
||||
|
||||
/// Get the index of a given file name, or -1 if not found
|
||||
int getIndex(const char *str) const;
|
||||
|
||||
public:
|
||||
/* -----------------------------------
|
||||
* BSA management methods
|
||||
* -----------------------------------
|
||||
*/
|
||||
|
||||
BSAFile()
|
||||
: isLoaded(false)
|
||||
{ }
|
||||
|
||||
/// Open an archive file.
|
||||
void open(const std::string &file);
|
||||
|
||||
/* -----------------------------------
|
||||
* Archive file routines
|
||||
* -----------------------------------
|
||||
*/
|
||||
|
||||
/// Check if a file exists
|
||||
bool exists(const char *file) const
|
||||
{ return getIndex(file) != -1; }
|
||||
|
||||
/** Open a file contained in the archive. Throws an exception if the
|
||||
file doesn't exist.
|
||||
*/
|
||||
Ogre::DataStreamPtr getFile(const char *file);
|
||||
|
||||
/// Get a list of all files
|
||||
const FileList &getList() const
|
||||
{ return files; }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
15
components/bsa/tests/Makefile
Normal file
15
components/bsa/tests/Makefile
Normal file
|
@ -0,0 +1,15 @@
|
|||
GCC=g++
|
||||
|
||||
all: bsa_file_test ogre_archive_test
|
||||
|
||||
I_OGRE=$(shell pkg-config --cflags OGRE)
|
||||
L_OGRE=$(shell pkg-config --libs OGRE)
|
||||
|
||||
bsa_file_test: bsa_file_test.cpp ../bsa_file.cpp
|
||||
$(GCC) $^ -o $@
|
||||
|
||||
ogre_archive_test: ogre_archive_test.cpp ../bsa_file.cpp ../bsa_archive.cpp
|
||||
$(GCC) $^ -o $@ $(I_OGRE) $(L_OGRE)
|
||||
|
||||
clean:
|
||||
rm *_test
|
44
components/bsa/tests/bsa_file_test.cpp
Normal file
44
components/bsa/tests/bsa_file_test.cpp
Normal file
|
@ -0,0 +1,44 @@
|
|||
#include "../bsa_file.hpp"
|
||||
|
||||
/*
|
||||
Test of the BSAFile class
|
||||
|
||||
This test requires that data/Morrowind.bsa exists in the root
|
||||
directory of OpenMW.
|
||||
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace Bsa;
|
||||
|
||||
BSAFile bsa;
|
||||
|
||||
void find(const char* file)
|
||||
{
|
||||
cout << "Does file '" << file << "' exist?\n ";
|
||||
if(bsa.exists(file))
|
||||
{
|
||||
cout << "Yes.\n ";
|
||||
cout << bsa.getFile(file)->size() << " bytes\n";
|
||||
}
|
||||
else cout << "No.\n";
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
cout << "Reading Morrowind.bsa\n";
|
||||
bsa.open("../../data/Morrowind.bsa");
|
||||
|
||||
const BSAFile::FileList &files = bsa.getList();
|
||||
|
||||
cout << "First 10 files in archive:\n";
|
||||
for(int i=0; i<10; i++)
|
||||
cout << " " << files[i].name
|
||||
<< " (" << files[i].fileSize << " bytes @"
|
||||
<< files[i].offset << ")\n";
|
||||
|
||||
find("meshes\\r\\xnetch_betty.nif");
|
||||
find("humdrum");
|
||||
}
|
34
components/bsa/tests/ogre_archive_test.cpp
Normal file
34
components/bsa/tests/ogre_archive_test.cpp
Normal file
|
@ -0,0 +1,34 @@
|
|||
#include <Ogre.h>
|
||||
#include <iostream>
|
||||
|
||||
// This is a test of the BSA archive handler for OGRE.
|
||||
|
||||
#include "../bsa_archive.hpp"
|
||||
|
||||
using namespace Ogre;
|
||||
using namespace std;
|
||||
|
||||
int main()
|
||||
{
|
||||
// Disable Ogre logging
|
||||
new LogManager;
|
||||
Log *log = LogManager::getSingleton().createLog("");
|
||||
log->setDebugOutputEnabled(false);
|
||||
|
||||
// Set up Root
|
||||
Root *root = new Root("","","");
|
||||
|
||||
// Add the BSA
|
||||
Bsa::addBSA("../../data/Morrowind.bsa");
|
||||
|
||||
// Pick a sample file
|
||||
String tex = "textures\\tx_natural_cavern_wall13.dds";
|
||||
cout << "Opening file: " << tex << endl;
|
||||
|
||||
// Get it from the resource system
|
||||
DataStreamPtr data = ResourceGroupManager::getSingleton().openResource(tex, "General");
|
||||
|
||||
cout << "Size: " << data->size() << endl;
|
||||
|
||||
return 0;
|
||||
}
|
17
components/bsa/tests/output/bsa_file_test.out
Normal file
17
components/bsa/tests/output/bsa_file_test.out
Normal file
|
@ -0,0 +1,17 @@
|
|||
Reading Morrowind.bsa
|
||||
First 10 files in archive:
|
||||
meshes\m\probe_journeyman_01.nif (6276 bytes @126646052)
|
||||
textures\menu_rightbuttonup_top.dds (256 bytes @218530052)
|
||||
textures\menu_rightbuttonup_right.dds (256 bytes @218529796)
|
||||
textures\menu_rightbuttonup_left.dds (256 bytes @218529540)
|
||||
textures\menu_rightbuttondown_top.dds (256 bytes @218528196)
|
||||
meshes\b\b_n_redguard_f_skins.nif (41766 bytes @17809778)
|
||||
meshes\b\b_n_redguard_m_skins.nif (41950 bytes @18103107)
|
||||
meshes\b\b_n_redguard_f_wrist.nif (2355 bytes @17858132)
|
||||
meshes\b\b_n_redguard_m_foot.nif (4141 bytes @17862081)
|
||||
meshes\b\b_n_redguard_m_knee.nif (2085 bytes @18098101)
|
||||
Does file 'meshes\r\xnetch_betty.nif' exist?
|
||||
Yes.
|
||||
53714 bytes
|
||||
Does file 'humdrum' exist?
|
||||
No.
|
2
components/bsa/tests/output/ogre_archive_test.out
Normal file
2
components/bsa/tests/output/ogre_archive_test.out
Normal file
|
@ -0,0 +1,2 @@
|
|||
Opening file: textures\tx_natural_cavern_wall13.dds
|
||||
Size: 43808
|
18
components/bsa/tests/test.sh
Executable file
18
components/bsa/tests/test.sh
Executable file
|
@ -0,0 +1,18 @@
|
|||
#!/bin/bash
|
||||
|
||||
make || exit
|
||||
|
||||
mkdir -p output
|
||||
|
||||
PROGS=*_test
|
||||
|
||||
for a in $PROGS; do
|
||||
if [ -f "output/$a.out" ]; then
|
||||
echo "Running $a:"
|
||||
./$a | diff output/$a.out -
|
||||
else
|
||||
echo "Creating $a.out"
|
||||
./$a > "output/$a.out"
|
||||
git add "output/$a.out"
|
||||
fi
|
||||
done
|
44
components/compiler/context.hpp
Normal file
44
components/compiler/context.hpp
Normal file
|
@ -0,0 +1,44 @@
|
|||
#ifndef COMPILER_CONTEXT_H_INCLUDED
|
||||
#define COMPILER_CONTEXT_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
class Extensions;
|
||||
|
||||
class Context
|
||||
{
|
||||
const Extensions *mExtensions;
|
||||
|
||||
public:
|
||||
|
||||
Context() : mExtensions (0) {}
|
||||
|
||||
virtual ~Context() {}
|
||||
|
||||
virtual bool canDeclareLocals() const = 0;
|
||||
///< Is the compiler allowed to declare local variables?
|
||||
|
||||
void setExtensions (const Extensions *extensions = 0)
|
||||
{
|
||||
mExtensions = extensions;
|
||||
}
|
||||
|
||||
const Extensions *getExtensions() const
|
||||
{
|
||||
return mExtensions;
|
||||
}
|
||||
|
||||
virtual char getGlobalType (const std::string& name) const = 0;
|
||||
///< 'l: long, 's': short, 'f': float, ' ': does not exist.
|
||||
|
||||
virtual char getMemberType (const std::string& name, const std::string& id) const = 0;
|
||||
///< 'l: long, 's': short, 'f': float, ' ': does not exist.
|
||||
|
||||
virtual bool isId (const std::string& name) const = 0;
|
||||
///< Does \a name match an ID, that can be referenced?
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
260
components/compiler/controlparser.cpp
Normal file
260
components/compiler/controlparser.cpp
Normal file
|
@ -0,0 +1,260 @@
|
|||
|
||||
#include "controlparser.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "scanner.hpp"
|
||||
#include "generator.hpp"
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
bool ControlParser::parseIfBody (int keyword, const TokenLoc& loc, Scanner& scanner)
|
||||
{
|
||||
if (keyword==Scanner::K_endif || keyword==Scanner::K_elseif ||
|
||||
keyword==Scanner::K_else)
|
||||
{
|
||||
std::pair<Codes, Codes> entry;
|
||||
|
||||
if (mState!=IfElseBodyState)
|
||||
mExprParser.append (entry.first);
|
||||
|
||||
std::copy (mCodeBlock.begin(), mCodeBlock.end(),
|
||||
std::back_inserter (entry.second));
|
||||
|
||||
mIfCode.push_back (entry);
|
||||
|
||||
mCodeBlock.clear();
|
||||
|
||||
if (keyword==Scanner::K_endif)
|
||||
{
|
||||
// store code for if-cascade
|
||||
Codes codes;
|
||||
|
||||
for (IfCodes::reverse_iterator iter (mIfCode.rbegin());
|
||||
iter!=mIfCode.rend(); ++iter)
|
||||
{
|
||||
Codes block;
|
||||
|
||||
if (iter!=mIfCode.rbegin())
|
||||
Generator::jump (iter->second, codes.size()+1);
|
||||
|
||||
if (!iter->first.empty())
|
||||
{
|
||||
// if or elseif
|
||||
std::copy (iter->first.begin(), iter->first.end(),
|
||||
std::back_inserter (block));
|
||||
Generator::jumpOnZero (block, iter->second.size()+1);
|
||||
}
|
||||
|
||||
std::copy (iter->second.begin(), iter->second.end(),
|
||||
std::back_inserter (block));
|
||||
|
||||
std::swap (codes, block);
|
||||
|
||||
std::copy (block.begin(), block.end(), std::back_inserter (codes));
|
||||
}
|
||||
|
||||
std::copy (codes.begin(), codes.end(), std::back_inserter (mCode));
|
||||
|
||||
mIfCode.clear();
|
||||
mState = IfEndifState;
|
||||
}
|
||||
else if (keyword==Scanner::K_elseif)
|
||||
{
|
||||
mExprParser.reset();
|
||||
scanner.scan (mExprParser);
|
||||
|
||||
mState = IfElseifEndState;
|
||||
}
|
||||
else if (keyword==Scanner::K_else)
|
||||
{
|
||||
mState = IfElseEndState;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (keyword==Scanner::K_if || keyword==Scanner::K_while)
|
||||
{
|
||||
// nested
|
||||
ControlParser parser (getErrorHandler(), getContext(), mLocals, mLiterals);
|
||||
|
||||
if (parser.parseKeyword (keyword, loc, scanner))
|
||||
scanner.scan (parser);
|
||||
|
||||
parser.appendCode (mCodeBlock);
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
mLineParser.reset();
|
||||
if (mLineParser.parseKeyword (keyword, loc, scanner))
|
||||
scanner.scan (mLineParser);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
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, -static_cast<int> (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, -static_cast<int> (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;
|
||||
}
|
||||
}
|
||||
|
||||
ControlParser::ControlParser (ErrorHandler& errorHandler, Context& context, Locals& locals,
|
||||
Literals& literals)
|
||||
: Parser (errorHandler, context), mLocals (locals), mLiterals (literals),
|
||||
mLineParser (errorHandler, context, locals, literals, mCodeBlock),
|
||||
mExprParser (errorHandler, context, locals, literals),
|
||||
mState (StartState)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ControlParser::appendCode (std::vector<Interpreter::Type_Code>& code) const
|
||||
{
|
||||
std::copy (mCode.begin(), mCode.end(), std::back_inserter (code));
|
||||
}
|
||||
|
||||
bool ControlParser::parseName (const std::string& name, const TokenLoc& loc, Scanner& scanner)
|
||||
{
|
||||
if (mState==IfBodyState || mState==IfElseifBodyState || mState==IfElseBodyState ||
|
||||
mState==WhileBodyState)
|
||||
{
|
||||
scanner.putbackName (name, loc);
|
||||
mLineParser.reset();
|
||||
scanner.scan (mLineParser);
|
||||
return true;
|
||||
}
|
||||
|
||||
return Parser::parseName (name, loc, scanner);
|
||||
}
|
||||
|
||||
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;
|
||||
Parser::reset();
|
||||
}
|
||||
}
|
74
components/compiler/controlparser.hpp
Normal file
74
components/compiler/controlparser.hpp
Normal file
|
@ -0,0 +1,74 @@
|
|||
#ifndef COMPILER_CONTROLPARSER_H_INCLUDED
|
||||
#define COMPILER_CONTROLPARSER_H_INCLUDED
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <components/interpreter/types.hpp>
|
||||
|
||||
#include "parser.hpp"
|
||||
#include "exprparser.hpp"
|
||||
#include "lineparser.hpp"
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
class Locals;
|
||||
class Literals;
|
||||
|
||||
// Control structure parser
|
||||
|
||||
class ControlParser : public Parser
|
||||
{
|
||||
enum State
|
||||
{
|
||||
StartState,
|
||||
IfEndState, IfBodyState,
|
||||
IfElseifEndState, IfElseifBodyState,
|
||||
IfElseEndState, IfElseBodyState,
|
||||
IfEndifState,
|
||||
WhileEndState, WhileBodyState,
|
||||
WhileEndwhileState
|
||||
};
|
||||
|
||||
typedef std::vector<Interpreter::Type_Code> Codes;
|
||||
typedef std::vector<std::pair<Codes, Codes> > IfCodes;
|
||||
|
||||
Locals& mLocals;
|
||||
Literals& mLiterals;
|
||||
Codes mCode;
|
||||
Codes mCodeBlock;
|
||||
IfCodes mIfCode; // condition, body
|
||||
LineParser mLineParser;
|
||||
ExprParser mExprParser;
|
||||
State mState;
|
||||
|
||||
bool parseIfBody (int keyword, const TokenLoc& loc, Scanner& scanner);
|
||||
|
||||
bool parseWhileBody (int keyword, const TokenLoc& loc, Scanner& scanner);
|
||||
|
||||
public:
|
||||
|
||||
ControlParser (ErrorHandler& errorHandler, Context& context, Locals& locals,
|
||||
Literals& literals);
|
||||
|
||||
void appendCode (std::vector<Interpreter::Type_Code>& code) const;
|
||||
///< store generated code in \æ code.
|
||||
|
||||
virtual bool 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
|
65
components/compiler/errorhandler.cpp
Normal file
65
components/compiler/errorhandler.cpp
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
68
components/compiler/errorhandler.hpp
Normal file
68
components/compiler/errorhandler.hpp
Normal file
|
@ -0,0 +1,68 @@
|
|||
|
||||
#ifndef COMPILER_ERRORHANDLER_H_INCLUDED
|
||||
#define COMPILER_ERRORHANDLER_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
struct TokenLoc;
|
||||
|
||||
/// \brief Error handling
|
||||
///
|
||||
/// This class collects errors and provides an interface for reporting them to the user.
|
||||
|
||||
class ErrorHandler
|
||||
{
|
||||
int mWarnings;
|
||||
int mErrors;
|
||||
|
||||
protected:
|
||||
|
||||
enum Type
|
||||
{
|
||||
WarningMessage, ErrorMessage
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
// mutators
|
||||
|
||||
virtual void report (const std::string& message, const TokenLoc& loc, Type type) = 0;
|
||||
///< Report error to the user.
|
||||
|
||||
virtual void report (const std::string& message, Type type) = 0;
|
||||
///< Report a file related error
|
||||
|
||||
public:
|
||||
|
||||
ErrorHandler();
|
||||
///< constructor
|
||||
|
||||
virtual ~ErrorHandler();
|
||||
///< destructor
|
||||
|
||||
bool isGood() const;
|
||||
///< Was compiling successful?
|
||||
|
||||
int countErrors() const;
|
||||
///< Return number of errors
|
||||
|
||||
int countWarnings() const;
|
||||
///< Return number of warnings
|
||||
|
||||
void warning (const std::string& message, const TokenLoc& loc);
|
||||
///< Generate a warning message.
|
||||
|
||||
void error (const std::string& message, const TokenLoc& loc);
|
||||
///< Generate an error message.
|
||||
|
||||
void endOfFile();
|
||||
///< Generate an error message for an unexpected EOF.
|
||||
|
||||
virtual void reset();
|
||||
///< Remove all previous error/warning events
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
39
components/compiler/exception.hpp
Normal file
39
components/compiler/exception.hpp
Normal file
|
@ -0,0 +1,39 @@
|
|||
#ifndef COMPILER_EXCEPTION_H_INCLUDED
|
||||
#define COMPILER_EXCEPTION_H_INCLUDED
|
||||
|
||||
#include <exception>
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
/// \brief Exception: Error while parsing the source
|
||||
|
||||
class SourceException : public std::exception
|
||||
{
|
||||
public:
|
||||
|
||||
virtual const char *what() const throw() { return "compile error";}
|
||||
///< Return error message
|
||||
};
|
||||
|
||||
/// \brief Exception: File error
|
||||
|
||||
class FileException : public SourceException
|
||||
{
|
||||
public:
|
||||
|
||||
virtual const char *what() const throw() { return "can't read file"; }
|
||||
///< Return error message
|
||||
};
|
||||
|
||||
/// \brief Exception: EOF condition encountered
|
||||
|
||||
class EOFException : public SourceException
|
||||
{
|
||||
public:
|
||||
|
||||
virtual const char *what() const throw() { return "end of file"; }
|
||||
///< Return error message
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
756
components/compiler/exprparser.cpp
Normal file
756
components/compiler/exprparser.cpp
Normal file
|
@ -0,0 +1,756 @@
|
|||
|
||||
#include "exprparser.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
#include <stack>
|
||||
#include <iterator>
|
||||
|
||||
#include "generator.hpp"
|
||||
#include "scanner.hpp"
|
||||
#include "errorhandler.hpp"
|
||||
#include "locals.hpp"
|
||||
#include "stringparser.hpp"
|
||||
#include "extensions.hpp"
|
||||
#include "context.hpp"
|
||||
#include <components/misc/stringops.hpp>
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
int ExprParser::getPriority (char op) const
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
case '(':
|
||||
|
||||
return 0;
|
||||
|
||||
case 'e': // ==
|
||||
case 'n': // !=
|
||||
case 'l': // <
|
||||
case 'L': // <=
|
||||
case 'g': // <
|
||||
case 'G': // >=
|
||||
|
||||
return 1;
|
||||
|
||||
case '+':
|
||||
case '-':
|
||||
|
||||
return 2;
|
||||
|
||||
case '*':
|
||||
case '/':
|
||||
|
||||
return 3;
|
||||
|
||||
case 'm':
|
||||
|
||||
return 4;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char ExprParser::getOperandType (int Index) const
|
||||
{
|
||||
assert (!mOperands.empty());
|
||||
assert (Index>=0);
|
||||
assert (Index<static_cast<int> (mOperands.size()));
|
||||
return mOperands[mOperands.size()-1-Index];
|
||||
}
|
||||
|
||||
char ExprParser::getOperator() const
|
||||
{
|
||||
assert (!mOperators.empty());
|
||||
return mOperators[mOperators.size()-1];
|
||||
}
|
||||
|
||||
bool ExprParser::isOpen() const
|
||||
{
|
||||
return std::find (mOperators.begin(), mOperators.end(), '(')!=mOperators.end();
|
||||
}
|
||||
|
||||
void ExprParser::popOperator()
|
||||
{
|
||||
assert (!mOperators.empty());
|
||||
mOperators.resize (mOperators.size()-1);
|
||||
}
|
||||
|
||||
void ExprParser::popOperand()
|
||||
{
|
||||
assert (!mOperands.empty());
|
||||
mOperands.resize (mOperands.size()-1);
|
||||
}
|
||||
|
||||
void ExprParser::replaceBinaryOperands()
|
||||
{
|
||||
char t1 = getOperandType (1);
|
||||
char t2 = getOperandType();
|
||||
|
||||
popOperand();
|
||||
popOperand();
|
||||
|
||||
if (t1==t2)
|
||||
mOperands.push_back (t1);
|
||||
else if (t1=='f' || t2=='f')
|
||||
mOperands.push_back ('f');
|
||||
else
|
||||
std::logic_error ("failed to determine result operand type");
|
||||
}
|
||||
|
||||
void ExprParser::pop()
|
||||
{
|
||||
char op = getOperator();
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case 'm':
|
||||
|
||||
Generator::negate (mCode, getOperandType());
|
||||
popOperator();
|
||||
break;
|
||||
|
||||
case '+':
|
||||
|
||||
Generator::add (mCode, getOperandType (1), getOperandType());
|
||||
popOperator();
|
||||
replaceBinaryOperands();
|
||||
break;
|
||||
|
||||
case '-':
|
||||
|
||||
Generator::sub (mCode, getOperandType (1), getOperandType());
|
||||
popOperator();
|
||||
replaceBinaryOperands();
|
||||
break;
|
||||
|
||||
case '*':
|
||||
|
||||
Generator::mul (mCode, getOperandType (1), getOperandType());
|
||||
popOperator();
|
||||
replaceBinaryOperands();
|
||||
break;
|
||||
|
||||
case '/':
|
||||
|
||||
Generator::div (mCode, getOperandType (1), getOperandType());
|
||||
popOperator();
|
||||
replaceBinaryOperands();
|
||||
break;
|
||||
|
||||
case 'e':
|
||||
case 'n':
|
||||
case 'l':
|
||||
case 'L':
|
||||
case 'g':
|
||||
case 'G':
|
||||
|
||||
Generator::compare (mCode, op, getOperandType (1), getOperandType());
|
||||
popOperator();
|
||||
popOperand();
|
||||
popOperand();
|
||||
mOperands.push_back ('l');
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
throw std::logic_error ("unknown operator");
|
||||
}
|
||||
}
|
||||
|
||||
void ExprParser::pushIntegerLiteral (int value)
|
||||
{
|
||||
mNextOperand = false;
|
||||
mOperands.push_back ('l');
|
||||
Generator::pushInt (mCode, mLiterals, value);
|
||||
}
|
||||
|
||||
void ExprParser::pushFloatLiteral (float value)
|
||||
{
|
||||
mNextOperand = false;
|
||||
mOperands.push_back ('f');
|
||||
Generator::pushFloat (mCode, mLiterals, value);
|
||||
}
|
||||
|
||||
void ExprParser::pushBinaryOperator (char c)
|
||||
{
|
||||
while (!mOperators.empty() && getPriority (getOperator())>=getPriority (c))
|
||||
pop();
|
||||
|
||||
mOperators.push_back (c);
|
||||
mNextOperand = true;
|
||||
}
|
||||
|
||||
void ExprParser::close()
|
||||
{
|
||||
while (getOperator()!='(')
|
||||
pop();
|
||||
|
||||
popOperator();
|
||||
}
|
||||
|
||||
int ExprParser::parseArguments (const std::string& arguments, Scanner& scanner)
|
||||
{
|
||||
return parseArguments (arguments, scanner, mCode);
|
||||
}
|
||||
|
||||
bool ExprParser::handleMemberAccess (const std::string& name)
|
||||
{
|
||||
mMemberOp = false;
|
||||
|
||||
std::string name2 = Misc::StringUtils::lowerCase (name);
|
||||
std::string id = Misc::StringUtils::lowerCase (mExplicit);
|
||||
|
||||
char type = getContext().getMemberType (name2, id);
|
||||
|
||||
if (type!=' ')
|
||||
{
|
||||
Generator::fetchMember (mCode, mLiterals, type, name2, id);
|
||||
mNextOperand = false;
|
||||
mExplicit.clear();
|
||||
mOperands.push_back (type=='f' ? 'f' : 'l');
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
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), mRefOp (false), mMemberOp (false)
|
||||
{}
|
||||
|
||||
bool ExprParser::parseInt (int value, const TokenLoc& loc, Scanner& scanner)
|
||||
{
|
||||
if (!mExplicit.empty())
|
||||
return Parser::parseInt (value, loc, scanner);
|
||||
|
||||
mFirst = false;
|
||||
|
||||
if (mNextOperand)
|
||||
{
|
||||
start();
|
||||
|
||||
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)
|
||||
{
|
||||
if (!mExplicit.empty())
|
||||
return Parser::parseFloat (value, loc, scanner);
|
||||
|
||||
mFirst = false;
|
||||
|
||||
if (mNextOperand)
|
||||
{
|
||||
start();
|
||||
|
||||
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)
|
||||
{
|
||||
if (!mExplicit.empty())
|
||||
{
|
||||
if (mMemberOp && handleMemberAccess (name))
|
||||
return true;
|
||||
|
||||
return Parser::parseName (name, loc, scanner);
|
||||
}
|
||||
|
||||
mFirst = false;
|
||||
|
||||
if (mNextOperand)
|
||||
{
|
||||
start();
|
||||
|
||||
std::string name2 = Misc::StringUtils::lowerCase (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;
|
||||
}
|
||||
|
||||
if (mExplicit.empty() && getContext().isId (name2))
|
||||
{
|
||||
mExplicit = name2;
|
||||
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 (!mExplicit.empty())
|
||||
{
|
||||
if (mRefOp && mNextOperand)
|
||||
{
|
||||
if (keyword==Scanner::K_getdisabled)
|
||||
{
|
||||
start();
|
||||
|
||||
mTokenLoc = loc;
|
||||
|
||||
Generator::getDisabled (mCode, mLiterals, mExplicit);
|
||||
mOperands.push_back ('l');
|
||||
mExplicit.clear();
|
||||
mRefOp = false;
|
||||
|
||||
mNextOperand = false;
|
||||
return true;
|
||||
}
|
||||
else if (keyword==Scanner::K_getdistance)
|
||||
{
|
||||
start();
|
||||
|
||||
mTokenLoc = loc;
|
||||
parseArguments ("c", scanner);
|
||||
|
||||
Generator::getDistance (mCode, mLiterals, mExplicit);
|
||||
mOperands.push_back ('f');
|
||||
mExplicit.clear();
|
||||
mRefOp = false;
|
||||
|
||||
mNextOperand = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// check for custom extensions
|
||||
if (const Extensions *extensions = getContext().getExtensions())
|
||||
{
|
||||
char returnType;
|
||||
std::string argumentType;
|
||||
|
||||
if (extensions->isFunction (keyword, returnType, argumentType, true))
|
||||
{
|
||||
start();
|
||||
|
||||
mTokenLoc = loc;
|
||||
int optionals = parseArguments (argumentType, scanner);
|
||||
|
||||
extensions->generateFunctionCode (keyword, mCode, mLiterals, mExplicit,
|
||||
optionals);
|
||||
mOperands.push_back (returnType);
|
||||
mExplicit.clear();
|
||||
mRefOp = false;
|
||||
|
||||
mNextOperand = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Parser::parseKeyword (keyword, loc, scanner);
|
||||
}
|
||||
|
||||
if (mNextOperand)
|
||||
{
|
||||
if (keyword==Scanner::K_getsquareroot)
|
||||
{
|
||||
start();
|
||||
|
||||
mTokenLoc = loc;
|
||||
parseArguments ("f", scanner);
|
||||
|
||||
Generator::squareRoot (mCode);
|
||||
mOperands.push_back ('f');
|
||||
|
||||
mNextOperand = false;
|
||||
return true;
|
||||
}
|
||||
else if (keyword==Scanner::K_menumode)
|
||||
{
|
||||
start();
|
||||
|
||||
mTokenLoc = loc;
|
||||
|
||||
Generator::menuMode (mCode);
|
||||
mOperands.push_back ('l');
|
||||
|
||||
mNextOperand = false;
|
||||
return true;
|
||||
}
|
||||
else if (keyword==Scanner::K_random)
|
||||
{
|
||||
start();
|
||||
|
||||
mTokenLoc = loc;
|
||||
parseArguments ("l", scanner);
|
||||
|
||||
Generator::random (mCode);
|
||||
mOperands.push_back ('l');
|
||||
|
||||
mNextOperand = false;
|
||||
return true;
|
||||
}
|
||||
else if (keyword==Scanner::K_scriptrunning)
|
||||
{
|
||||
start();
|
||||
|
||||
mTokenLoc = loc;
|
||||
parseArguments ("c", scanner);
|
||||
|
||||
Generator::scriptRunning (mCode);
|
||||
mOperands.push_back ('l');
|
||||
|
||||
mNextOperand = false;
|
||||
return true;
|
||||
}
|
||||
else if (keyword==Scanner::K_getdistance)
|
||||
{
|
||||
start();
|
||||
|
||||
mTokenLoc = loc;
|
||||
parseArguments ("c", scanner);
|
||||
|
||||
Generator::getDistance (mCode, mLiterals, "");
|
||||
mOperands.push_back ('f');
|
||||
|
||||
mNextOperand = false;
|
||||
return true;
|
||||
}
|
||||
else if (keyword==Scanner::K_getsecondspassed)
|
||||
{
|
||||
start();
|
||||
|
||||
mTokenLoc = loc;
|
||||
|
||||
Generator::getSecondsPassed (mCode);
|
||||
mOperands.push_back ('f');
|
||||
|
||||
mNextOperand = false;
|
||||
return true;
|
||||
}
|
||||
else if (keyword==Scanner::K_getdisabled)
|
||||
{
|
||||
start();
|
||||
|
||||
mTokenLoc = loc;
|
||||
|
||||
Generator::getDisabled (mCode, mLiterals, "");
|
||||
mOperands.push_back ('l');
|
||||
|
||||
mNextOperand = false;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// check for custom extensions
|
||||
if (const Extensions *extensions = getContext().getExtensions())
|
||||
{
|
||||
start();
|
||||
|
||||
char returnType;
|
||||
std::string argumentType;
|
||||
|
||||
if (extensions->isFunction (keyword, returnType, argumentType, false))
|
||||
{
|
||||
mTokenLoc = loc;
|
||||
int optionals = parseArguments (argumentType, scanner);
|
||||
|
||||
extensions->generateFunctionCode (keyword, mCode, mLiterals, "", optionals);
|
||||
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 (!mExplicit.empty())
|
||||
{
|
||||
if (!mRefOp && code==Scanner::S_ref)
|
||||
{
|
||||
mRefOp = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!mMemberOp && code==Scanner::S_member)
|
||||
{
|
||||
mMemberOp = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return Parser::parseSpecial (code, loc, 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: c = '+'; break;
|
||||
case Scanner::S_minus: c = '-'; break;
|
||||
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;
|
||||
mExplicit.clear();
|
||||
mRefOp = false;
|
||||
mMemberOp = false;
|
||||
Parser::reset();
|
||||
}
|
||||
|
||||
char ExprParser::append (std::vector<Interpreter::Type_Code>& code)
|
||||
{
|
||||
if (mOperands.empty() && mOperators.empty())
|
||||
{
|
||||
getErrorHandler().error ("missing expression", mTokenLoc);
|
||||
return 'l';
|
||||
}
|
||||
|
||||
if (mNextOperand || mOperands.empty())
|
||||
{
|
||||
getErrorHandler().error ("syntax error in expression", mTokenLoc);
|
||||
return 'l';
|
||||
}
|
||||
|
||||
while (!mOperators.empty())
|
||||
pop();
|
||||
|
||||
std::copy (mCode.begin(), mCode.end(), std::back_inserter (code));
|
||||
|
||||
assert (mOperands.size()==1);
|
||||
return mOperands[0];
|
||||
}
|
||||
|
||||
int ExprParser::parseArguments (const std::string& arguments, Scanner& scanner,
|
||||
std::vector<Interpreter::Type_Code>& code, bool invert)
|
||||
{
|
||||
bool optional = false;
|
||||
int optionalCount = 0;
|
||||
|
||||
ExprParser parser (getErrorHandler(), getContext(), mLocals, mLiterals, true);
|
||||
StringParser stringParser (getErrorHandler(), getContext(), mLiterals);
|
||||
|
||||
std::stack<std::vector<Interpreter::Type_Code> > stack;
|
||||
|
||||
for (std::string::const_iterator iter (arguments.begin()); iter!=arguments.end();
|
||||
++iter)
|
||||
{
|
||||
if (*iter=='/')
|
||||
{
|
||||
optional = true;
|
||||
}
|
||||
else if (*iter=='S' || *iter=='c')
|
||||
{
|
||||
stringParser.reset();
|
||||
|
||||
if (optional)
|
||||
stringParser.setOptional (true);
|
||||
|
||||
if (*iter=='c') stringParser.smashCase();
|
||||
scanner.scan (stringParser);
|
||||
|
||||
if (optional && stringParser.isEmpty())
|
||||
break;
|
||||
|
||||
if (invert)
|
||||
{
|
||||
std::vector<Interpreter::Type_Code> tmp;
|
||||
stringParser.append (tmp);
|
||||
|
||||
stack.push (tmp);
|
||||
}
|
||||
else
|
||||
stringParser.append (code);
|
||||
|
||||
if (optional)
|
||||
++optionalCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
parser.reset();
|
||||
|
||||
if (optional)
|
||||
parser.setOptional (true);
|
||||
|
||||
scanner.scan (parser);
|
||||
|
||||
if (optional && parser.isEmpty())
|
||||
break;
|
||||
|
||||
std::vector<Interpreter::Type_Code> tmp;
|
||||
|
||||
char type = parser.append (tmp);
|
||||
|
||||
if (type!=*iter)
|
||||
Generator::convert (tmp, type, *iter);
|
||||
|
||||
if (invert)
|
||||
stack.push (tmp);
|
||||
else
|
||||
std::copy (tmp.begin(), tmp.end(), std::back_inserter (code));
|
||||
|
||||
if (optional)
|
||||
++optionalCount;
|
||||
}
|
||||
}
|
||||
|
||||
while (!stack.empty())
|
||||
{
|
||||
std::vector<Interpreter::Type_Code>& tmp = stack.top();
|
||||
|
||||
std::copy (tmp.begin(), tmp.end(), std::back_inserter (code));
|
||||
|
||||
stack.pop();
|
||||
}
|
||||
|
||||
return optionalCount;
|
||||
}
|
||||
}
|
109
components/compiler/exprparser.hpp
Normal file
109
components/compiler/exprparser.hpp
Normal file
|
@ -0,0 +1,109 @@
|
|||
#ifndef COMPILER_EXPRPARSER_H_INCLUDED
|
||||
#define COMPILER_EXPRPARSER_H_INCLUDED
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <components/interpreter/types.hpp>
|
||||
|
||||
#include "parser.hpp"
|
||||
#include "tokenloc.hpp"
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
class Locals;
|
||||
class Literals;
|
||||
|
||||
class ExprParser : public Parser
|
||||
{
|
||||
Locals& mLocals;
|
||||
Literals& mLiterals;
|
||||
std::vector<char> mOperands;
|
||||
std::vector<char> mOperators;
|
||||
bool mNextOperand;
|
||||
TokenLoc mTokenLoc;
|
||||
std::vector<Interpreter::Type_Code> mCode;
|
||||
bool mFirst;
|
||||
bool mArgument;
|
||||
std::string mExplicit;
|
||||
bool mRefOp;
|
||||
bool mMemberOp;
|
||||
|
||||
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();
|
||||
|
||||
int parseArguments (const std::string& arguments, Scanner& scanner);
|
||||
|
||||
bool handleMemberAccess (const std::string& name);
|
||||
|
||||
public:
|
||||
|
||||
ExprParser (ErrorHandler& errorHandler, Context& context, Locals& locals,
|
||||
Literals& literals, bool argument = false);
|
||||
///< constructor
|
||||
/// \param argument Parser is used to parse function- or instruction-
|
||||
/// arguments (this influences the precedence rules).
|
||||
|
||||
char getType() const;
|
||||
///< Return type of parsed expression ('l' integer, 'f' float)
|
||||
|
||||
virtual bool parseInt (int value, const TokenLoc& loc, Scanner& scanner);
|
||||
///< Handle an int token.
|
||||
/// \return fetch another token?
|
||||
|
||||
virtual bool parseFloat (float value, const TokenLoc& loc, Scanner& scanner);
|
||||
///< Handle a float token.
|
||||
/// \return fetch another token?
|
||||
|
||||
virtual bool parseName (const std::string& name, const TokenLoc& loc,
|
||||
Scanner& scanner);
|
||||
///< Handle a name token.
|
||||
/// \return fetch another token?
|
||||
|
||||
virtual bool parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner);
|
||||
///< Handle a keyword token.
|
||||
/// \return fetch another token?
|
||||
|
||||
virtual bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner);
|
||||
///< Handle a special character token.
|
||||
/// \return fetch another token?
|
||||
|
||||
void reset();
|
||||
///< Reset parser to clean state.
|
||||
|
||||
char append (std::vector<Interpreter::Type_Code>& code);
|
||||
///< Generate code for parsed expression.
|
||||
/// \return Type ('l': integer, 'f': float)
|
||||
|
||||
int parseArguments (const std::string& arguments, Scanner& scanner,
|
||||
std::vector<Interpreter::Type_Code>& code, bool invert = false);
|
||||
///< Parse sequence of arguments specified by \a arguments.
|
||||
/// \param arguments Each character represents one arguments ('l': integer,
|
||||
/// 'f': float, 'S': string, 'c': string (case smashed), '/': following arguments are
|
||||
/// optional)
|
||||
/// \param invert Store arguments in reverted order.
|
||||
/// \return number of optional arguments
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
217
components/compiler/extensions.cpp
Normal file
217
components/compiler/extensions.cpp
Normal file
|
@ -0,0 +1,217 @@
|
|||
|
||||
#include "extensions.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "generator.hpp"
|
||||
#include "literals.hpp"
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
Extensions::Extensions() : mNextKeywordIndex (-1) {}
|
||||
|
||||
int Extensions::searchKeyword (const std::string& keyword) const
|
||||
{
|
||||
std::map<std::string, int>::const_iterator iter = mKeywords.find (keyword);
|
||||
|
||||
if (iter==mKeywords.end())
|
||||
return 0;
|
||||
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
bool Extensions::isFunction (int keyword, char& returnType, std::string& argumentType,
|
||||
bool explicitReference) const
|
||||
{
|
||||
std::map<int, Function>::const_iterator iter = mFunctions.find (keyword);
|
||||
|
||||
if (iter==mFunctions.end())
|
||||
return false;
|
||||
|
||||
if (explicitReference && iter->second.mCodeExplicit==-1)
|
||||
return false;
|
||||
|
||||
returnType = iter->second.mReturn;
|
||||
argumentType = iter->second.mArguments;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Extensions::isInstruction (int keyword, std::string& argumentType,
|
||||
bool explicitReference) const
|
||||
{
|
||||
std::map<int, Instruction>::const_iterator iter = mInstructions.find (keyword);
|
||||
|
||||
if (iter==mInstructions.end())
|
||||
return false;
|
||||
|
||||
if (explicitReference && iter->second.mCodeExplicit==-1)
|
||||
return false;
|
||||
|
||||
argumentType = iter->second.mArguments;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Extensions::registerFunction (const std::string& keyword, char returnType,
|
||||
const std::string& argumentType, int code, int codeExplicit)
|
||||
{
|
||||
Function function;
|
||||
|
||||
if (argumentType.find ('/')==std::string::npos)
|
||||
{
|
||||
function.mSegment = 5;
|
||||
assert (code>=33554432 && code<=67108863);
|
||||
assert (codeExplicit==-1 || (codeExplicit>=33554432 && codeExplicit<=67108863));
|
||||
}
|
||||
else
|
||||
{
|
||||
function.mSegment = 3;
|
||||
assert (code>=0x20000 && code<=0x2ffff);
|
||||
assert (codeExplicit==-1 || (codeExplicit>=0x20000 && codeExplicit<=0x2ffff));
|
||||
}
|
||||
|
||||
int keywordIndex = mNextKeywordIndex--;
|
||||
|
||||
mKeywords.insert (std::make_pair (keyword, keywordIndex));
|
||||
|
||||
function.mReturn = returnType;
|
||||
function.mArguments = argumentType;
|
||||
function.mCode = code;
|
||||
function.mCodeExplicit = codeExplicit;
|
||||
|
||||
mFunctions.insert (std::make_pair (keywordIndex, function));
|
||||
}
|
||||
|
||||
void Extensions::registerInstruction (const std::string& keyword,
|
||||
const std::string& argumentType, int code, int codeExplicit)
|
||||
{
|
||||
Instruction instruction;
|
||||
|
||||
if (argumentType.find ('/')==std::string::npos)
|
||||
{
|
||||
instruction.mSegment = 5;
|
||||
assert (code>=33554432 && code<=67108863);
|
||||
assert (codeExplicit==-1 || (codeExplicit>=33554432 && codeExplicit<=67108863));
|
||||
}
|
||||
else
|
||||
{
|
||||
instruction.mSegment = 3;
|
||||
assert (code>=0x20000 && code<=0x2ffff);
|
||||
assert (codeExplicit==-1 || (codeExplicit>=0x20000 && codeExplicit<=0x2ffff));
|
||||
}
|
||||
|
||||
int keywordIndex = mNextKeywordIndex--;
|
||||
|
||||
mKeywords.insert (std::make_pair (keyword, keywordIndex));
|
||||
|
||||
instruction.mArguments = argumentType;
|
||||
instruction.mCode = code;
|
||||
instruction.mCodeExplicit = codeExplicit;
|
||||
|
||||
mInstructions.insert (std::make_pair (keywordIndex, instruction));
|
||||
}
|
||||
|
||||
void Extensions::generateFunctionCode (int keyword, std::vector<Interpreter::Type_Code>& code,
|
||||
Literals& literals, const std::string& id, int optionalArguments) const
|
||||
{
|
||||
assert (optionalArguments>=0);
|
||||
|
||||
std::map<int, Function>::const_iterator iter = mFunctions.find (keyword);
|
||||
|
||||
if (iter==mFunctions.end())
|
||||
throw std::logic_error ("unknown custom function keyword");
|
||||
|
||||
if (optionalArguments && iter->second.mSegment!=3)
|
||||
throw std::logic_error ("functions with optional arguments must be placed into segment 3");
|
||||
|
||||
if (!id.empty())
|
||||
{
|
||||
if (iter->second.mCodeExplicit==-1)
|
||||
throw std::logic_error ("explicit references not supported");
|
||||
|
||||
int index = literals.addString (id);
|
||||
Generator::pushInt (code, literals, index);
|
||||
}
|
||||
|
||||
switch (iter->second.mSegment)
|
||||
{
|
||||
case 3:
|
||||
|
||||
if (optionalArguments>=256)
|
||||
throw std::logic_error ("number of optional arguments is too large for segment 3");
|
||||
|
||||
code.push_back (Generator::segment3 (
|
||||
id.empty() ? iter->second.mCode : iter->second.mCodeExplicit,
|
||||
optionalArguments));
|
||||
|
||||
break;
|
||||
|
||||
case 5:
|
||||
|
||||
code.push_back (Generator::segment5 (
|
||||
id.empty() ? iter->second.mCode : iter->second.mCodeExplicit));
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
throw std::logic_error ("unsupported code segment");
|
||||
}
|
||||
}
|
||||
|
||||
void Extensions::generateInstructionCode (int keyword,
|
||||
std::vector<Interpreter::Type_Code>& code, Literals& literals, const std::string& id,
|
||||
int optionalArguments) const
|
||||
{
|
||||
assert (optionalArguments>=0);
|
||||
|
||||
std::map<int, Instruction>::const_iterator iter = mInstructions.find (keyword);
|
||||
|
||||
if (iter==mInstructions.end())
|
||||
throw std::logic_error ("unknown custom instruction keyword");
|
||||
|
||||
if (optionalArguments && iter->second.mSegment!=3)
|
||||
throw std::logic_error ("instructions with optional arguments must be placed into segment 3");
|
||||
|
||||
if (!id.empty())
|
||||
{
|
||||
if (iter->second.mCodeExplicit==-1)
|
||||
throw std::logic_error ("explicit references not supported");
|
||||
|
||||
int index = literals.addString (id);
|
||||
Generator::pushInt (code, literals, index);
|
||||
}
|
||||
|
||||
switch (iter->second.mSegment)
|
||||
{
|
||||
case 3:
|
||||
|
||||
if (optionalArguments>=256)
|
||||
throw std::logic_error ("number of optional arguments is too large for segment 3");
|
||||
|
||||
code.push_back (Generator::segment3 (
|
||||
id.empty() ? iter->second.mCode : iter->second.mCodeExplicit,
|
||||
optionalArguments));
|
||||
|
||||
break;
|
||||
|
||||
case 5:
|
||||
|
||||
code.push_back (Generator::segment5 (
|
||||
id.empty() ? iter->second.mCode : iter->second.mCodeExplicit));
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
throw std::logic_error ("unsupported code segment");
|
||||
}
|
||||
}
|
||||
|
||||
void Extensions::listKeywords (std::vector<std::string>& keywords) const
|
||||
{
|
||||
for (std::map<std::string, int>::const_iterator iter (mKeywords.begin());
|
||||
iter!=mKeywords.end(); ++iter)
|
||||
keywords.push_back (iter->first);
|
||||
}
|
||||
}
|
87
components/compiler/extensions.hpp
Normal file
87
components/compiler/extensions.hpp
Normal file
|
@ -0,0 +1,87 @@
|
|||
#ifndef COMPILER_EXTENSIONS_H_INCLUDED
|
||||
#define COMPILER_EXTENSINOS_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include <components/interpreter/types.hpp>
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
class Literals;
|
||||
|
||||
/// \brief Collection of compiler extensions
|
||||
|
||||
class Extensions
|
||||
{
|
||||
struct Function
|
||||
{
|
||||
char mReturn;
|
||||
std::string mArguments;
|
||||
int mCode;
|
||||
int mCodeExplicit;
|
||||
int mSegment;
|
||||
};
|
||||
|
||||
struct Instruction
|
||||
{
|
||||
std::string mArguments;
|
||||
int mCode;
|
||||
int mCodeExplicit;
|
||||
int mSegment;
|
||||
};
|
||||
|
||||
int mNextKeywordIndex;
|
||||
std::map<std::string, int> mKeywords;
|
||||
std::map<int, Function> mFunctions;
|
||||
std::map<int, Instruction> mInstructions;
|
||||
|
||||
public:
|
||||
|
||||
Extensions();
|
||||
|
||||
int searchKeyword (const std::string& keyword) const;
|
||||
///< Return extension keyword code, that is assigned to the string \a keyword.
|
||||
/// - if no match is found 0 is returned.
|
||||
/// - keyword must be all lower case.
|
||||
|
||||
bool isFunction (int keyword, char& returnType, std::string& argumentType,
|
||||
bool explicitReference) const;
|
||||
///< Is this keyword registered with a function? If yes, return return and argument
|
||||
/// types.
|
||||
|
||||
bool isInstruction (int keyword, std::string& argumentType,
|
||||
bool explicitReference) 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 code, int codeExplicit = -1);
|
||||
///< Register a custom function
|
||||
/// - keyword must be all lower case.
|
||||
/// - keyword must be unique
|
||||
/// - if explicit references are not supported, segment5codeExplicit must be set to -1
|
||||
/// \note Currently only segment 3 and segment 5 opcodes are supported.
|
||||
|
||||
void registerInstruction (const std::string& keyword,
|
||||
const std::string& argumentType, int code, int codeExplicit = -1);
|
||||
///< Register a custom instruction
|
||||
/// - keyword must be all lower case.
|
||||
/// - keyword must be unique
|
||||
/// - if explicit references are not supported, segment5codeExplicit must be set to -1
|
||||
/// \note Currently only segment 3 and segment 5 opcodes are supported.
|
||||
|
||||
void generateFunctionCode (int keyword, std::vector<Interpreter::Type_Code>& code,
|
||||
Literals& literals, const std::string& id, int optionalArguments) const;
|
||||
///< Append code for function to \a code.
|
||||
|
||||
void generateInstructionCode (int keyword, std::vector<Interpreter::Type_Code>& code,
|
||||
Literals& literals, const std::string& id, int optionalArguments) const;
|
||||
///< Append code for function to \a code.
|
||||
|
||||
void listKeywords (std::vector<std::string>& keywords) const;
|
||||
///< Append all known keywords to \æ kaywords.
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
467
components/compiler/extensions0.cpp
Normal file
467
components/compiler/extensions0.cpp
Normal file
|
@ -0,0 +1,467 @@
|
|||
#include "extensions0.hpp"
|
||||
|
||||
#include "opcodes.hpp"
|
||||
#include "extensions.hpp"
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
void registerExtensions (Extensions& extensions, bool consoleOnly)
|
||||
{
|
||||
Ai::registerExtensions (extensions);
|
||||
Animation::registerExtensions (extensions);
|
||||
Cell::registerExtensions (extensions);
|
||||
Container::registerExtensions (extensions);
|
||||
Control::registerExtensions (extensions);
|
||||
Dialogue::registerExtensions (extensions);
|
||||
Gui::registerExtensions (extensions);
|
||||
Misc::registerExtensions (extensions);
|
||||
Sky::registerExtensions (extensions);
|
||||
Sound::registerExtensions (extensions);
|
||||
Stats::registerExtensions (extensions);
|
||||
Transformation::registerExtensions (extensions);
|
||||
|
||||
if (consoleOnly)
|
||||
{
|
||||
Console::registerExtensions (extensions);
|
||||
User::registerExtensions (extensions);
|
||||
}
|
||||
}
|
||||
|
||||
namespace Ai
|
||||
{
|
||||
void registerExtensions (Extensions& extensions)
|
||||
{
|
||||
extensions.registerInstruction ("aiactivate", "c/l", opcodeAIActivate,
|
||||
opcodeAIActivateExplicit);
|
||||
extensions.registerInstruction ("aitravel", "fff/l", opcodeAiTravel,
|
||||
opcodeAiTravelExplicit);
|
||||
extensions.registerInstruction ("aiescort", "cffff/l", opcodeAiEscort,
|
||||
opcodeAiEscortExplicit);
|
||||
extensions.registerInstruction ("aiescortcell", "ccffff/l", opcodeAiEscortCell,
|
||||
opcodeAiEscortCellExplicit);
|
||||
extensions.registerInstruction ("aiwander", "fff/llllllllll", opcodeAiWander,
|
||||
opcodeAiWanderExplicit);
|
||||
extensions.registerInstruction ("aifollow", "cffff/l", opcodeAiFollow,
|
||||
opcodeAiFollowExplicit);
|
||||
extensions.registerInstruction ("aifollowcell", "ccffff/l", opcodeAiFollowCell,
|
||||
opcodeAiFollowCellExplicit);
|
||||
extensions.registerFunction ("getaipackagedone", 'l', "", opcodeGetAiPackageDone,
|
||||
opcodeGetAiPackageDoneExplicit);
|
||||
extensions.registerFunction ("getcurrentaipackage", 'l', "", opcodeGetCurrentAiPackage,
|
||||
opcodeGetAiPackageDoneExplicit);
|
||||
extensions.registerFunction ("getdetected", 'l', "c", opcodeGetDetected,
|
||||
opcodeGetDetectedExplicit);
|
||||
extensions.registerInstruction ("sethello", "l", opcodeSetHello, opcodeSetHelloExplicit);
|
||||
extensions.registerInstruction ("setfight", "l", opcodeSetFight, opcodeSetFightExplicit);
|
||||
extensions.registerInstruction ("setflee", "l", opcodeSetFlee, opcodeSetFleeExplicit);
|
||||
extensions.registerInstruction ("setalarm", "l", opcodeSetAlarm, opcodeSetAlarmExplicit);
|
||||
extensions.registerInstruction ("modhello", "l", opcodeModHello, opcodeModHelloExplicit);
|
||||
extensions.registerInstruction ("modfight", "l", opcodeModFight, opcodeModFightExplicit);
|
||||
extensions.registerInstruction ("modflee", "l", opcodeModFlee, opcodeModFleeExplicit);
|
||||
extensions.registerInstruction ("modalarm", "l", opcodeModAlarm, opcodeModAlarmExplicit);
|
||||
extensions.registerFunction ("gethello", 'l', "", opcodeGetHello, opcodeGetHelloExplicit);
|
||||
extensions.registerFunction ("getfight", 'l', "", opcodeGetFight, opcodeGetFightExplicit);
|
||||
extensions.registerFunction ("getflee", 'l', "", opcodeGetFlee, opcodeGetFleeExplicit);
|
||||
extensions.registerFunction ("getalarm", 'l', "", opcodeGetAlarm, opcodeGetAlarmExplicit);
|
||||
}
|
||||
}
|
||||
|
||||
namespace Animation
|
||||
{
|
||||
void registerExtensions (Extensions& extensions)
|
||||
{
|
||||
extensions.registerInstruction ("skipanim", "", opcodeSkipAnim, opcodeSkipAnimExplicit);
|
||||
extensions.registerInstruction ("playgroup", "c/l", opcodePlayAnim, opcodePlayAnimExplicit);
|
||||
extensions.registerInstruction ("loopgroup", "cl/l", opcodeLoopAnim, opcodeLoopAnimExplicit);
|
||||
}
|
||||
}
|
||||
|
||||
namespace Cell
|
||||
{
|
||||
void registerExtensions (Extensions& extensions)
|
||||
{
|
||||
extensions.registerFunction ("cellchanged", 'l', "", opcodeCellChanged);
|
||||
extensions.registerInstruction ("coc", "S", opcodeCOC);
|
||||
extensions.registerInstruction ("centeroncell", "S", opcodeCOC);
|
||||
extensions.registerInstruction ("coe", "ll", opcodeCOE);
|
||||
extensions.registerInstruction ("centeronexterior", "ll", opcodeCOE);
|
||||
extensions.registerInstruction ("setwaterlevel", "f", opcodeSetWaterLevel);
|
||||
extensions.registerInstruction ("modwaterlevel", "f", opcodeModWaterLevel);
|
||||
extensions.registerFunction ("getinterior", 'l', "", opcodeGetInterior);
|
||||
extensions.registerFunction ("getpccell", 'l', "c", opcodeGetPCCell);
|
||||
extensions.registerFunction ("getwaterlevel", 'f', "", opcodeGetWaterLevel);
|
||||
}
|
||||
}
|
||||
|
||||
namespace Console
|
||||
{
|
||||
void registerExtensions (Extensions& extensions)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
namespace Container
|
||||
{
|
||||
void registerExtensions (Extensions& extensions)
|
||||
{
|
||||
extensions.registerInstruction ("additem", "cl", opcodeAddItem, opcodeAddItemExplicit);
|
||||
extensions.registerFunction ("getitemcount", 'l', "c", opcodeGetItemCount,
|
||||
opcodeGetItemCountExplicit);
|
||||
extensions.registerInstruction ("removeitem", "cl", opcodeRemoveItem,
|
||||
opcodeRemoveItemExplicit);
|
||||
extensions.registerInstruction ("equip", "c", opcodeEquip, opcodeEquipExplicit);
|
||||
extensions.registerFunction ("getarmortype", 'l', "l", opcodeGetArmorType, opcodeGetArmorTypeExplicit);
|
||||
extensions.registerFunction ("hasitemequipped", 'l', "c", opcodeHasItemEquipped, opcodeHasItemEquippedExplicit);
|
||||
extensions.registerFunction ("hassoulgem", 'l', "c", opcodeHasSoulGem, opcodeHasSoulGemExplicit);
|
||||
extensions.registerFunction ("getweapontype", 'l', "", opcodeGetWeaponType, opcodeGetWeaponTypeExplicit);
|
||||
}
|
||||
}
|
||||
|
||||
namespace Control
|
||||
{
|
||||
void registerExtensions (Extensions& extensions)
|
||||
{
|
||||
std::string enable ("enable");
|
||||
std::string disable ("disable");
|
||||
|
||||
for (int i=0; i<numberOfControls; ++i)
|
||||
{
|
||||
extensions.registerInstruction (enable + controls[i], "", opcodeEnable+i);
|
||||
extensions.registerInstruction (disable + controls[i], "", opcodeDisable+i);
|
||||
extensions.registerFunction (std::string("get") + controls[i] + std::string("disabled"), 'l', "", opcodeGetDisabled+i);
|
||||
}
|
||||
|
||||
extensions.registerInstruction ("togglecollision", "", opcodeToggleCollision);
|
||||
extensions.registerInstruction ("tcl", "", opcodeToggleCollision);
|
||||
|
||||
extensions.registerInstruction ("clearforcerun", "", opcodeClearForceRun,
|
||||
opcodeClearForceRunExplicit);
|
||||
extensions.registerInstruction ("forcerun", "", opcodeForceRun,
|
||||
opcodeForceRunExplicit);
|
||||
|
||||
extensions.registerInstruction ("clearforcesneak", "", opcodeClearForceSneak,
|
||||
opcodeClearForceSneakExplicit);
|
||||
extensions.registerInstruction ("forcesneak", "", opcodeForceSneak,
|
||||
opcodeForceSneakExplicit);
|
||||
extensions.registerFunction ("getpcrunning", 'l', "", opcodeGetPcRunning);
|
||||
extensions.registerFunction ("getpcsneaking", 'l', "", opcodeGetPcSneaking);
|
||||
extensions.registerFunction ("getforcerun", 'l', "", opcodeGetForceRun, opcodeGetForceRunExplicit);
|
||||
extensions.registerFunction ("getforcesneak", 'l', "", opcodeGetForceSneak, opcodeGetForceSneakExplicit);
|
||||
}
|
||||
}
|
||||
|
||||
namespace Dialogue
|
||||
{
|
||||
void registerExtensions (Extensions& extensions)
|
||||
{
|
||||
extensions.registerInstruction ("journal", "cl", opcodeJournal);
|
||||
extensions.registerInstruction ("setjournalindex", "cl", opcodeSetJournalIndex);
|
||||
extensions.registerFunction ("getjournalindex", 'l', "c", opcodeGetJournalIndex);
|
||||
extensions.registerInstruction ("addtopic", "S" , opcodeAddTopic);
|
||||
extensions.registerInstruction ("choice", "/SlSlSlSlSlSlSlSlSlSlSlSlSlSlSlSl", opcodeChoice);
|
||||
extensions.registerInstruction("forcegreeting","",opcodeForceGreeting);
|
||||
extensions.registerInstruction("forcegreeting","",opcodeForceGreeting,
|
||||
opcodeForceGreetingExplicit);
|
||||
extensions.registerInstruction("goodbye", "", opcodeGoodbye);
|
||||
extensions.registerInstruction("setreputation", "l", opcodeSetReputation,
|
||||
opcodeSetReputationExplicit);
|
||||
extensions.registerInstruction("modreputation", "l", opcodeModReputation,
|
||||
opcodeModReputationExplicit);
|
||||
extensions.registerFunction("getreputation", 'l', "", opcodeGetReputation,
|
||||
opcodeGetReputationExplicit);
|
||||
extensions.registerFunction("samefaction", 'l', "", opcodeSameFaction,
|
||||
opcodeSameFactionExplicit);
|
||||
}
|
||||
}
|
||||
|
||||
namespace Gui
|
||||
{
|
||||
void registerExtensions (Extensions& extensions)
|
||||
{
|
||||
extensions.registerInstruction ("enablebirthmenu", "", opcodeEnableBirthMenu);
|
||||
extensions.registerInstruction ("enableclassmenu", "", opcodeEnableClassMenu);
|
||||
extensions.registerInstruction ("enablenamemenu", "", opcodeEnableNameMenu);
|
||||
extensions.registerInstruction ("enableracemenu", "", opcodeEnableRaceMenu);
|
||||
extensions.registerInstruction ("enablestatreviewmenu", "",
|
||||
opcodeEnableStatsReviewMenu);
|
||||
|
||||
extensions.registerInstruction ("enableinventorymenu", "", opcodeEnableInventoryMenu);
|
||||
extensions.registerInstruction ("enablemagicmenu", "", opcodeEnableMagicMenu);
|
||||
extensions.registerInstruction ("enablemapmenu", "", opcodeEnableMapMenu);
|
||||
extensions.registerInstruction ("enablestatsmenu", "", opcodeEnableStatsMenu);
|
||||
|
||||
extensions.registerInstruction ("enablerest", "", opcodeEnableRest);
|
||||
extensions.registerInstruction ("enablelevelupmenu", "", opcodeEnableRest);
|
||||
|
||||
extensions.registerInstruction ("showrestmenu", "", opcodeShowRestMenu);
|
||||
|
||||
extensions.registerFunction ("getbuttonpressed", 'l', "", opcodeGetButtonPressed);
|
||||
|
||||
extensions.registerInstruction ("togglefogofwar", "", opcodeToggleFogOfWar);
|
||||
extensions.registerInstruction ("tfow", "", opcodeToggleFogOfWar);
|
||||
|
||||
extensions.registerInstruction ("togglefullhelp", "", opcodeToggleFullHelp);
|
||||
extensions.registerInstruction ("tfh", "", opcodeToggleFullHelp);
|
||||
|
||||
extensions.registerInstruction ("showmap", "S", opcodeShowMap);
|
||||
extensions.registerInstruction ("fillmap", "", opcodeFillMap);
|
||||
}
|
||||
}
|
||||
|
||||
namespace Misc
|
||||
{
|
||||
void registerExtensions (Extensions& extensions)
|
||||
{
|
||||
extensions.registerFunction ("xbox", 'l', "", opcodeXBox);
|
||||
extensions.registerFunction ("onactivate", 'l', "", opcodeOnActivate);
|
||||
extensions.registerInstruction ("activate", "", opcodeActivate);
|
||||
extensions.registerInstruction ("lock", "/l", opcodeLock, opcodeLockExplicit);
|
||||
extensions.registerInstruction ("unlock", "", opcodeUnlock, opcodeUnlockExplicit);
|
||||
extensions.registerInstruction ("togglecollisionboxes", "", opcodeToggleCollisionBoxes);
|
||||
extensions.registerInstruction ("togglecollisiongrid", "", opcodeToggleCollisionDebug);
|
||||
extensions.registerInstruction ("tcb", "", opcodeToggleCollisionBoxes);
|
||||
extensions.registerInstruction ("tcg", "", opcodeToggleCollisionDebug);
|
||||
extensions.registerInstruction ("twf", "", opcodeToggleWireframe);
|
||||
extensions.registerInstruction ("togglewireframe", "", opcodeToggleWireframe);
|
||||
extensions.registerInstruction ("fadein", "f", opcodeFadeIn);
|
||||
extensions.registerInstruction ("fadeout", "f", opcodeFadeOut);
|
||||
extensions.registerInstruction ("fadeto", "ff", opcodeFadeTo);
|
||||
extensions.registerInstruction ("togglewater", "", opcodeToggleWater);
|
||||
extensions.registerInstruction ("twa", "", opcodeToggleWater);
|
||||
extensions.registerInstruction ("togglepathgrid", "", opcodeTogglePathgrid);
|
||||
extensions.registerInstruction ("tpg", "", opcodeTogglePathgrid);
|
||||
extensions.registerInstruction ("dontsaveobject", "", opcodeDontSaveObject);
|
||||
extensions.registerInstruction ("togglevanitymode", "", opcodeToggleVanityMode);
|
||||
extensions.registerInstruction ("tvm", "", opcodeToggleVanityMode);
|
||||
extensions.registerFunction ("getpcsleep", 'l', "", opcodeGetPcSleep);
|
||||
extensions.registerInstruction ("wakeuppc", "", opcodeWakeUpPc);
|
||||
extensions.registerInstruction ("playbink", "Sl", opcodePlayBink);
|
||||
extensions.registerFunction ("getlocked", 'l', "", opcodeGetLocked, opcodeGetLockedExplicit);
|
||||
extensions.registerFunction ("geteffect", 'l', "S", opcodeGetEffect, opcodeGetEffectExplicit);
|
||||
extensions.registerInstruction ("addsoulgem", "cc", opcodeAddSoulGem, opcodeAddSoulGemExplicit);
|
||||
extensions.registerInstruction ("removesoulgem", "c", opcodeRemoveSoulGem, opcodeRemoveSoulGemExplicit);
|
||||
extensions.registerInstruction ("drop", "cl", opcodeDrop, opcodeDropExplicit);
|
||||
extensions.registerInstruction ("dropsoulgem", "c", opcodeDropSoulGem, opcodeDropSoulGemExplicit);
|
||||
extensions.registerFunction ("getattacked", 'l', "", opcodeGetAttacked, opcodeGetAttackedExplicit);
|
||||
extensions.registerFunction ("getweapondrawn", 'l', "", opcodeGetWeaponDrawn, opcodeGetWeaponDrawnExplicit);
|
||||
extensions.registerFunction ("getspelleffects", 'l', "c", opcodeGetSpellEffects, opcodeGetSpellEffectsExplicit);
|
||||
extensions.registerFunction ("getcurrenttime", 'f', "", opcodeGetCurrentTime);
|
||||
extensions.registerInstruction ("setdelete", "l", opcodeSetDelete, opcodeSetDeleteExplicit);
|
||||
extensions.registerFunction ("getsquareroot", 'f', "f", opcodeGetSquareRoot);
|
||||
extensions.registerInstruction ("fall", "", opcodeFall, opcodeFallExplicit);
|
||||
extensions.registerFunction ("getstandingpc", 'l', "", opcodeGetStandingPc, opcodeGetStandingPcExplicit);
|
||||
extensions.registerFunction ("getstandingactor", 'l', "", opcodeGetStandingActor, opcodeGetStandingActorExplicit);
|
||||
extensions.registerFunction ("getwindspeed", 'f', "", opcodeGetWindSpeed);
|
||||
extensions.registerFunction ("hitonme", 'l', "S", opcodeHitOnMe, opcodeHitOnMeExplicit);
|
||||
extensions.registerInstruction ("disableteleporting", "", opcodeDisableTeleporting);
|
||||
extensions.registerInstruction ("enableteleporting", "", opcodeEnableTeleporting);
|
||||
extensions.registerInstruction ("showvars", "", opcodeShowVars, opcodeShowVarsExplicit);
|
||||
extensions.registerInstruction ("sv", "", opcodeShowVars, opcodeShowVarsExplicit);
|
||||
}
|
||||
}
|
||||
|
||||
namespace Sky
|
||||
{
|
||||
void registerExtensions (Extensions& extensions)
|
||||
{
|
||||
extensions.registerInstruction ("togglesky", "", opcodeToggleSky);
|
||||
extensions.registerInstruction ("ts", "", opcodeToggleSky);
|
||||
extensions.registerInstruction ("turnmoonwhite", "", opcodeTurnMoonWhite);
|
||||
extensions.registerInstruction ("turnmoonred", "", opcodeTurnMoonRed);
|
||||
extensions.registerInstruction ("changeweather", "Sl", opcodeChangeWeather);
|
||||
extensions.registerFunction ("getmasserphase", 'l', "", opcodeGetMasserPhase);
|
||||
extensions.registerFunction ("getsecundaphase", 'l', "", opcodeGetSecundaPhase);
|
||||
extensions.registerFunction ("getcurrentweather", 'l', "", opcodeGetCurrentWeather);
|
||||
extensions.registerInstruction ("modregion", "S/llllllllll", opcodeModRegion);
|
||||
}
|
||||
}
|
||||
|
||||
namespace Sound
|
||||
{
|
||||
void registerExtensions (Extensions& extensions)
|
||||
{
|
||||
extensions.registerInstruction ("say", "SS", opcodeSay, opcodeSayExplicit);
|
||||
extensions.registerFunction ("saydone", 'l', "", opcodeSayDone, opcodeSayDoneExplicit);
|
||||
extensions.registerInstruction ("streammusic", "S", opcodeStreamMusic);
|
||||
extensions.registerInstruction ("playsound", "c", opcodePlaySound);
|
||||
extensions.registerInstruction ("playsoundvp", "cff", opcodePlaySoundVP);
|
||||
extensions.registerInstruction ("playsound3d", "c", opcodePlaySound3D,
|
||||
opcodePlaySound3DExplicit);
|
||||
extensions.registerInstruction ("playsound3dvp", "cff", opcodePlaySound3DVP,
|
||||
opcodePlaySound3DVPExplicit);
|
||||
extensions.registerInstruction ("playloopsound3d", "c", opcodePlayLoopSound3D,
|
||||
opcodePlayLoopSound3DExplicit);
|
||||
extensions.registerInstruction ("playloopsound3dvp", "cff", opcodePlayLoopSound3DVP,
|
||||
opcodePlayLoopSound3DVPExplicit);
|
||||
extensions.registerInstruction ("stopsound", "c", opcodeStopSound,
|
||||
opcodeStopSoundExplicit);
|
||||
extensions.registerFunction ("getsoundplaying", 'l', "c", opcodeGetSoundPlaying,
|
||||
opcodeGetSoundPlayingExplicit);
|
||||
}
|
||||
}
|
||||
|
||||
namespace Stats
|
||||
{
|
||||
void registerExtensions (Extensions& extensions)
|
||||
{
|
||||
static const char *attributes[numberOfAttributes] =
|
||||
{
|
||||
"strength", "intelligence", "willpower", "agility", "speed", "endurance",
|
||||
"personality", "luck"
|
||||
};
|
||||
|
||||
static const char *dynamics[numberOfDynamics] =
|
||||
{
|
||||
"health", "magicka", "fatigue"
|
||||
};
|
||||
|
||||
static const char *skills[numberOfSkills] =
|
||||
{
|
||||
"block", "armorer", "mediumarmor", "heavyarmor", "bluntweapon",
|
||||
"longblade", "axe", "spear", "athletics", "enchant", "destruction",
|
||||
"alteration", "illusion", "conjuration", "mysticism",
|
||||
"restoration", "alchemy", "unarmored", "security", "sneak",
|
||||
"acrobatics", "lightarmor", "shortblade", "marksman",
|
||||
"mercantile", "speechcraft", "handtohand"
|
||||
};
|
||||
|
||||
std::string get ("get");
|
||||
std::string set ("set");
|
||||
std::string mod ("mod");
|
||||
std::string modCurrent ("modcurrent");
|
||||
std::string getRatio ("getratio");
|
||||
|
||||
for (int i=0; i<numberOfAttributes; ++i)
|
||||
{
|
||||
extensions.registerFunction (get + attributes[i], 'l', "",
|
||||
opcodeGetAttribute+i, opcodeGetAttributeExplicit+i);
|
||||
|
||||
extensions.registerInstruction (set + attributes[i], "l",
|
||||
opcodeSetAttribute+i, opcodeSetAttributeExplicit+i);
|
||||
|
||||
extensions.registerInstruction (mod + attributes[i], "l",
|
||||
opcodeModAttribute+i, opcodeModAttributeExplicit+i);
|
||||
}
|
||||
|
||||
for (int i=0; i<numberOfDynamics; ++i)
|
||||
{
|
||||
extensions.registerFunction (get + dynamics[i], 'f', "",
|
||||
opcodeGetDynamic+i, opcodeGetDynamicExplicit+i);
|
||||
|
||||
extensions.registerInstruction (set + dynamics[i], "f",
|
||||
opcodeSetDynamic+i, opcodeSetDynamicExplicit+i);
|
||||
|
||||
extensions.registerInstruction (mod + dynamics[i], "f",
|
||||
opcodeModDynamic+i, opcodeModDynamicExplicit+i);
|
||||
|
||||
extensions.registerInstruction (modCurrent + dynamics[i], "f",
|
||||
opcodeModCurrentDynamic+i, opcodeModCurrentDynamicExplicit+i);
|
||||
|
||||
extensions.registerFunction (get + dynamics[i] + getRatio, 'f', "",
|
||||
opcodeGetDynamicGetRatio+i, opcodeGetDynamicGetRatioExplicit+i);
|
||||
}
|
||||
|
||||
for (int i=0; i<numberOfSkills; ++i)
|
||||
{
|
||||
extensions.registerFunction (get + skills[i], 'l', "",
|
||||
opcodeGetSkill+i, opcodeGetSkillExplicit+i);
|
||||
|
||||
extensions.registerInstruction (set + skills[i], "l",
|
||||
opcodeSetSkill+i, opcodeSetSkillExplicit+i);
|
||||
|
||||
extensions.registerInstruction (mod + skills[i], "l",
|
||||
opcodeModSkill+i, opcodeModSkillExplicit+i);
|
||||
}
|
||||
|
||||
extensions.registerFunction ("getpccrimelevel", 'f', "", opcodeGetPCCrimeLevel);
|
||||
extensions.registerInstruction ("setpccrimelevel", "f", opcodeSetPCCrimeLevel);
|
||||
extensions.registerInstruction ("modpccrimelevel", "f", opcodeModPCCrimeLevel);
|
||||
|
||||
extensions.registerInstruction ("addspell", "c", opcodeAddSpell, opcodeAddSpellExplicit);
|
||||
extensions.registerInstruction ("removespell", "c", opcodeRemoveSpell,
|
||||
opcodeRemoveSpellExplicit);
|
||||
extensions.registerFunction ("getspell", 'l', "c", opcodeGetSpell, opcodeGetSpellExplicit);
|
||||
|
||||
extensions.registerInstruction("pcraiserank","/S",opcodePCRaiseRank);
|
||||
extensions.registerInstruction("pclowerrank","/S",opcodePCLowerRank);
|
||||
extensions.registerInstruction("pcjoinfaction","/S",opcodePCJoinFaction);
|
||||
extensions.registerInstruction ("moddisposition","l",opcodeModDisposition,
|
||||
opcodeModDispositionExplicit);
|
||||
extensions.registerInstruction ("setdisposition","l",opcodeSetDisposition,
|
||||
opcodeSetDispositionExplicit);
|
||||
extensions.registerFunction ("getdisposition",'l', "",opcodeGetDisposition,
|
||||
opcodeGetDispositionExplicit);
|
||||
extensions.registerFunction("getpcrank",'l',"/S",opcodeGetPCRank,opcodeGetPCRankExplicit);
|
||||
|
||||
extensions.registerInstruction("setlevel", "l", opcodeSetLevel, opcodeSetLevelExplicit);
|
||||
extensions.registerFunction("getlevel", 'l', "", opcodeGetLevel, opcodeGetLevelExplicit);
|
||||
|
||||
extensions.registerFunction ("getdeadcount", 'l', "c", opcodeGetDeadCount);
|
||||
|
||||
extensions.registerFunction ("getpcfacrep", 'l', "/c", opcodeGetPCFacRep, opcodeGetPCFacRepExplicit);
|
||||
extensions.registerInstruction ("setpcfacrep", "l/c", opcodeSetPCFacRep, opcodeSetPCFacRepExplicit);
|
||||
extensions.registerInstruction ("modpcfacrep", "l/c", opcodeModPCFacRep, opcodeModPCFacRepExplicit);
|
||||
|
||||
extensions.registerFunction ("getcommondisease", 'l', "", opcodeGetCommonDisease,
|
||||
opcodeGetCommonDiseaseExplicit);
|
||||
extensions.registerFunction ("getblightdisease", 'l', "", opcodeGetBlightDisease,
|
||||
opcodeGetBlightDiseaseExplicit);
|
||||
|
||||
extensions.registerFunction ("getrace", 'l', "c", opcodeGetRace,
|
||||
opcodeGetRaceExplicit);
|
||||
extensions.registerFunction ("getwerewolfkills", 'f', "", opcodeGetWerewolfKills);
|
||||
extensions.registerFunction ("pcexpelled", 'l', "/S", opcodePcExpelled, opcodePcExpelledExplicit);
|
||||
extensions.registerInstruction ("pcexpell", "/S", opcodePcExpell, opcodePcExpellExplicit);
|
||||
extensions.registerInstruction ("pcclearexpelled", "/S", opcodePcClearExpelled, opcodePcClearExpelledExplicit);
|
||||
extensions.registerInstruction ("raiserank", "", opcodeRaiseRank, opcodeRaiseRankExplicit);
|
||||
extensions.registerInstruction ("lowerrank", "", opcodeLowerRank, opcodeLowerRankExplicit);
|
||||
|
||||
extensions.registerFunction ("ondeath", 'l', "", opcodeOnDeath, opcodeOnDeathExplicit);
|
||||
|
||||
extensions.registerFunction ("iswerewolf", 'l', "", opcodeIsWerewolf, opcodeIsWerewolfExplicit);
|
||||
|
||||
extensions.registerInstruction("becomewerewolf", "", opcodeBecomeWerewolf, opcodeBecomeWerewolfExplicit);
|
||||
extensions.registerInstruction("undowerewolf", "", opcodeUndoWerewolf, opcodeUndoWerewolfExplicit);
|
||||
extensions.registerInstruction("setwerewolfacrobatics", "", opcodeSetWerewolfAcrobatics, opcodeSetWerewolfAcrobaticsExplicit);
|
||||
}
|
||||
}
|
||||
|
||||
namespace Transformation
|
||||
{
|
||||
void registerExtensions (Extensions& extensions)
|
||||
{
|
||||
extensions.registerInstruction("setscale","f",opcodeSetScale,opcodeSetScaleExplicit);
|
||||
extensions.registerFunction("getscale",'f',"",opcodeGetScale,opcodeGetScaleExplicit);
|
||||
extensions.registerInstruction("setangle","cf",opcodeSetAngle,opcodeSetAngleExplicit);
|
||||
extensions.registerFunction("getangle",'f',"c",opcodeGetAngle,opcodeGetAngleExplicit);
|
||||
extensions.registerInstruction("setpos","cf",opcodeSetPos,opcodeSetPosExplicit);
|
||||
extensions.registerFunction("getpos",'f',"c",opcodeGetPos,opcodeGetPosExplicit);
|
||||
extensions.registerFunction("getstartingpos",'f',"c",opcodeGetStartingPos,opcodeGetStartingPosExplicit);
|
||||
extensions.registerInstruction("position","ffff",opcodePosition,opcodePositionExplicit);
|
||||
extensions.registerInstruction("positioncell","ffffc",opcodePositionCell,opcodePositionCellExplicit);
|
||||
extensions.registerInstruction("placeitemcell","ccffff",opcodePlaceItemCell);
|
||||
extensions.registerInstruction("placeitem","cffff",opcodePlaceItem);
|
||||
extensions.registerInstruction("placeatpc","clfl",opcodePlaceAtPc);
|
||||
extensions.registerInstruction("placeatme","clfl",opcodePlaceAtMe,opcodePlaceAtMeExplicit);
|
||||
extensions.registerInstruction("modscale","f",opcodeModScale,opcodeModScaleExplicit);
|
||||
extensions.registerInstruction("rotate","cf",opcodeRotate,opcodeRotateExplicit);
|
||||
extensions.registerInstruction("rotateworld","cf",opcodeRotateWorld,opcodeRotateWorldExplicit);
|
||||
extensions.registerInstruction("setatstart","",opcodeSetAtStart,opcodeSetAtStartExplicit);
|
||||
extensions.registerInstruction("move","cf",opcodeMove,opcodeMoveExplicit);
|
||||
extensions.registerInstruction("moveworld","cf",opcodeMoveWorld,opcodeMoveWorldExplicit);
|
||||
extensions.registerFunction("getstartingangle",'f',"c",opcodeGetStartingAngle,opcodeGetStartingAngleExplicit);
|
||||
}
|
||||
}
|
||||
|
||||
namespace User
|
||||
{
|
||||
void registerExtensions (Extensions& extensions)
|
||||
{
|
||||
extensions.registerInstruction ("user1", "", opcodeUser1);
|
||||
extensions.registerInstruction ("user2", "", opcodeUser2);
|
||||
extensions.registerInstruction ("user3", "", opcodeUser3, opcodeUser3);
|
||||
extensions.registerInstruction ("user4", "", opcodeUser4, opcodeUser4);
|
||||
}
|
||||
}
|
||||
}
|
81
components/compiler/extensions0.hpp
Normal file
81
components/compiler/extensions0.hpp
Normal file
|
@ -0,0 +1,81 @@
|
|||
#ifndef COMPILER_EXTENSIONS0_H
|
||||
#define COMPILER_EXTENSIONS0_H
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
class Extensions;
|
||||
|
||||
void registerExtensions (Extensions& extensions, bool consoleOnly = false);
|
||||
|
||||
namespace Ai
|
||||
{
|
||||
void registerExtensions (Extensions& extensions);
|
||||
}
|
||||
|
||||
namespace Animation
|
||||
{
|
||||
void registerExtensions (Extensions& extensions);
|
||||
}
|
||||
|
||||
namespace Cell
|
||||
{
|
||||
void registerExtensions (Extensions& extensions);
|
||||
}
|
||||
|
||||
namespace Console
|
||||
{
|
||||
void registerExtensions (Extensions& extensions);
|
||||
}
|
||||
|
||||
namespace Container
|
||||
{
|
||||
void registerExtensions (Extensions& extensions);
|
||||
}
|
||||
|
||||
namespace Control
|
||||
{
|
||||
void registerExtensions (Extensions& extensions);
|
||||
}
|
||||
|
||||
namespace Dialogue
|
||||
{
|
||||
void registerExtensions (Extensions& extensions);
|
||||
}
|
||||
|
||||
namespace Gui
|
||||
{
|
||||
void registerExtensions (Extensions& extensions);
|
||||
}
|
||||
|
||||
namespace Misc
|
||||
{
|
||||
void registerExtensions (Extensions& extensions);
|
||||
}
|
||||
|
||||
namespace Sky
|
||||
{
|
||||
void registerExtensions (Extensions& extensions);
|
||||
}
|
||||
|
||||
namespace Sound
|
||||
{
|
||||
void registerExtensions (Extensions& extensions);
|
||||
}
|
||||
|
||||
namespace Stats
|
||||
{
|
||||
void registerExtensions (Extensions& extensions);
|
||||
}
|
||||
|
||||
namespace Transformation
|
||||
{
|
||||
void registerExtensions (Extensions& extensions);
|
||||
}
|
||||
|
||||
namespace User
|
||||
{
|
||||
void registerExtensions (Extensions& extensions);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
134
components/compiler/fileparser.cpp
Normal file
134
components/compiler/fileparser.cpp
Normal file
|
@ -0,0 +1,134 @@
|
|||
#include "fileparser.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "tokenloc.hpp"
|
||||
#include "scanner.hpp"
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
FileParser::FileParser (ErrorHandler& errorHandler, Context& context)
|
||||
: Parser (errorHandler, context),
|
||||
mScriptParser (errorHandler, context, mLocals, true),
|
||||
mState (BeginState)
|
||||
{}
|
||||
|
||||
std::string FileParser::getName() const
|
||||
{
|
||||
return mName;
|
||||
}
|
||||
|
||||
void FileParser::getCode (std::vector<Interpreter::Type_Code>& code) const
|
||||
{
|
||||
mScriptParser.getCode (code);
|
||||
}
|
||||
|
||||
const Locals& FileParser::getLocals() const
|
||||
{
|
||||
return mLocals;
|
||||
}
|
||||
|
||||
bool FileParser::parseName (const std::string& name, const TokenLoc& loc,
|
||||
Scanner& scanner)
|
||||
{
|
||||
if (mState==NameState)
|
||||
{
|
||||
mName = name;
|
||||
mState = BeginCompleteState;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mState==EndNameState)
|
||||
{
|
||||
// optional repeated name after end statement
|
||||
if (mName!=name)
|
||||
reportWarning ("Names for script " + mName + " do not match", loc);
|
||||
|
||||
mState = EndCompleteState;
|
||||
return false; // we are stopping here, because there might be more garbage on the end line,
|
||||
// that we must ignore.
|
||||
//
|
||||
/// \todo allow this workaround to be disabled for newer scripts
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (mState==NameState)
|
||||
{
|
||||
// keywords can be used as script names too. Thank you Morrowind for another
|
||||
// syntactic perversity :(
|
||||
mName = loc.mLiteral;
|
||||
mState = BeginCompleteState;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mState==EndNameState)
|
||||
{
|
||||
// optional repeated name after end statement
|
||||
if (mName!=loc.mLiteral)
|
||||
reportWarning ("Names for script " + mName + " do not match", loc);
|
||||
|
||||
mState = EndCompleteState;
|
||||
return false; // we are stopping here, because there might be more garbage on the end line,
|
||||
// that we must ignore.
|
||||
//
|
||||
/// \todo allow this workaround to be disabled for newer scripts
|
||||
}
|
||||
|
||||
return Parser::parseKeyword (keyword, loc, scanner);
|
||||
}
|
||||
|
||||
bool FileParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner)
|
||||
{
|
||||
if (code==Scanner::S_newline)
|
||||
{
|
||||
if (mState==BeginState)
|
||||
{
|
||||
// ignore empty lines
|
||||
return true;
|
||||
}
|
||||
|
||||
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();
|
||||
Parser::reset();
|
||||
}
|
||||
}
|
60
components/compiler/fileparser.hpp
Normal file
60
components/compiler/fileparser.hpp
Normal file
|
@ -0,0 +1,60 @@
|
|||
#ifndef COMPILER_FILEPARSER_H_INCLUDED
|
||||
#define COMPILER_FILEPARSER_H_INCLUDED
|
||||
|
||||
#include "parser.hpp"
|
||||
#include "scriptparser.hpp"
|
||||
#include "locals.hpp"
|
||||
#include "literals.hpp"
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
// Top-level parser, to be used for global scripts, local scripts and targeted scripts
|
||||
|
||||
class FileParser : public Parser
|
||||
{
|
||||
enum State
|
||||
{
|
||||
BeginState, NameState, BeginCompleteState, EndNameState,
|
||||
EndCompleteState
|
||||
};
|
||||
|
||||
ScriptParser mScriptParser;
|
||||
State mState;
|
||||
std::string mName;
|
||||
Locals mLocals;
|
||||
|
||||
public:
|
||||
|
||||
FileParser (ErrorHandler& errorHandler, Context& context);
|
||||
|
||||
std::string getName() const;
|
||||
///< Return script name.
|
||||
|
||||
void getCode (std::vector<Interpreter::Type_Code>& code) const;
|
||||
///< store generated code in \æ code.
|
||||
|
||||
const Locals& getLocals() const;
|
||||
///< get local variable declarations.
|
||||
|
||||
virtual bool parseName (const std::string& name, const TokenLoc& loc,
|
||||
Scanner& scanner);
|
||||
///< Handle a name token.
|
||||
/// \return fetch another token?
|
||||
|
||||
virtual bool parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner);
|
||||
///< Handle a keyword token.
|
||||
/// \return fetch another token?
|
||||
|
||||
virtual bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner);
|
||||
///< Handle a special character token.
|
||||
/// \return fetch another token?
|
||||
|
||||
virtual void parseEOF (Scanner& scanner);
|
||||
///< Handle EOF token.
|
||||
|
||||
void reset();
|
||||
///< Reset parser to clean state.
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
903
components/compiler/generator.cpp
Normal file
903
components/compiler/generator.cpp
Normal file
|
@ -0,0 +1,903 @@
|
|||
|
||||
#include "generator.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "literals.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
void opPushInt (Compiler::Generator::CodeContainer& code, int value)
|
||||
{
|
||||
code.push_back (Compiler::Generator::segment0 (0, value));
|
||||
}
|
||||
|
||||
void opFetchIntLiteral (Compiler::Generator::CodeContainer& code)
|
||||
{
|
||||
code.push_back (Compiler::Generator::segment5 (4));
|
||||
}
|
||||
|
||||
void opFetchFloatLiteral (Compiler::Generator::CodeContainer& code)
|
||||
{
|
||||
code.push_back (Compiler::Generator::segment5 (5));
|
||||
}
|
||||
|
||||
void opIntToFloat (Compiler::Generator::CodeContainer& code)
|
||||
{
|
||||
code.push_back (Compiler::Generator::segment5 (3));
|
||||
}
|
||||
|
||||
void opFloatToInt (Compiler::Generator::CodeContainer& code)
|
||||
{
|
||||
code.push_back (Compiler::Generator::segment5 (6));
|
||||
}
|
||||
|
||||
void opStoreLocalShort (Compiler::Generator::CodeContainer& code)
|
||||
{
|
||||
code.push_back (Compiler::Generator::segment5 (0));
|
||||
}
|
||||
|
||||
void opStoreLocalLong (Compiler::Generator::CodeContainer& code)
|
||||
{
|
||||
code.push_back (Compiler::Generator::segment5 (1));
|
||||
}
|
||||
|
||||
void opStoreLocalFloat (Compiler::Generator::CodeContainer& code)
|
||||
{
|
||||
code.push_back (Compiler::Generator::segment5 (2));
|
||||
}
|
||||
|
||||
void opNegateInt (Compiler::Generator::CodeContainer& code)
|
||||
{
|
||||
code.push_back (Compiler::Generator::segment5 (7));
|
||||
}
|
||||
|
||||
void opNegateFloat (Compiler::Generator::CodeContainer& code)
|
||||
{
|
||||
code.push_back (Compiler::Generator::segment5 (8));
|
||||
}
|
||||
|
||||
void opAddInt (Compiler::Generator::CodeContainer& code)
|
||||
{
|
||||
code.push_back (Compiler::Generator::segment5 (9));
|
||||
}
|
||||
|
||||
void opAddFloat (Compiler::Generator::CodeContainer& code)
|
||||
{
|
||||
code.push_back (Compiler::Generator::segment5 (10));
|
||||
}
|
||||
|
||||
void opSubInt (Compiler::Generator::CodeContainer& code)
|
||||
{
|
||||
code.push_back (Compiler::Generator::segment5 (11));
|
||||
}
|
||||
|
||||
void opSubFloat (Compiler::Generator::CodeContainer& code)
|
||||
{
|
||||
code.push_back (Compiler::Generator::segment5 (12));
|
||||
}
|
||||
|
||||
void opMulInt (Compiler::Generator::CodeContainer& code)
|
||||
{
|
||||
code.push_back (Compiler::Generator::segment5 (13));
|
||||
}
|
||||
|
||||
void opMulFloat (Compiler::Generator::CodeContainer& code)
|
||||
{
|
||||
code.push_back (Compiler::Generator::segment5 (14));
|
||||
}
|
||||
|
||||
void opDivInt (Compiler::Generator::CodeContainer& code)
|
||||
{
|
||||
code.push_back (Compiler::Generator::segment5 (15));
|
||||
}
|
||||
|
||||
void opDivFloat (Compiler::Generator::CodeContainer& code)
|
||||
{
|
||||
code.push_back (Compiler::Generator::segment5 (16));
|
||||
}
|
||||
|
||||
void opIntToFloat1 (Compiler::Generator::CodeContainer& code)
|
||||
{
|
||||
code.push_back (Compiler::Generator::segment5 (17));
|
||||
}
|
||||
|
||||
void opFloatToInt1 (Compiler::Generator::CodeContainer& code)
|
||||
{
|
||||
code.push_back (Compiler::Generator::segment5 (18));
|
||||
}
|
||||
|
||||
void opSquareRoot (Compiler::Generator::CodeContainer& code)
|
||||
{
|
||||
code.push_back (Compiler::Generator::segment5 (19));
|
||||
}
|
||||
|
||||
void opReturn (Compiler::Generator::CodeContainer& code)
|
||||
{
|
||||
code.push_back (Compiler::Generator::segment5 (20));
|
||||
}
|
||||
|
||||
void opMessageBox (Compiler::Generator::CodeContainer& code, int buttons)
|
||||
{
|
||||
code.push_back (Compiler::Generator::segment3 (0, buttons));
|
||||
}
|
||||
|
||||
void opReport (Compiler::Generator::CodeContainer& code)
|
||||
{
|
||||
code.push_back (Compiler::Generator::segment5 (58));
|
||||
}
|
||||
|
||||
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 opStoreMemberShort (Compiler::Generator::CodeContainer& code)
|
||||
{
|
||||
code.push_back (Compiler::Generator::segment5 (59));
|
||||
}
|
||||
|
||||
void opStoreMemberLong (Compiler::Generator::CodeContainer& code)
|
||||
{
|
||||
code.push_back (Compiler::Generator::segment5 (60));
|
||||
}
|
||||
|
||||
void opStoreMemberFloat (Compiler::Generator::CodeContainer& code)
|
||||
{
|
||||
code.push_back (Compiler::Generator::segment5 (61));
|
||||
}
|
||||
|
||||
void opFetchMemberShort (Compiler::Generator::CodeContainer& code)
|
||||
{
|
||||
code.push_back (Compiler::Generator::segment5 (62));
|
||||
}
|
||||
|
||||
void opFetchMemberLong (Compiler::Generator::CodeContainer& code)
|
||||
{
|
||||
code.push_back (Compiler::Generator::segment5 (63));
|
||||
}
|
||||
|
||||
void opFetchMemberFloat (Compiler::Generator::CodeContainer& code)
|
||||
{
|
||||
code.push_back (Compiler::Generator::segment5 (64));
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
void opGetDistance (Compiler::Generator::CodeContainer& code)
|
||||
{
|
||||
code.push_back (Compiler::Generator::segment5 (49));
|
||||
}
|
||||
|
||||
void opGetSecondsPassed (Compiler::Generator::CodeContainer& code)
|
||||
{
|
||||
code.push_back (Compiler::Generator::segment5 (50));
|
||||
}
|
||||
|
||||
void opEnable (Compiler::Generator::CodeContainer& code)
|
||||
{
|
||||
code.push_back (Compiler::Generator::segment5 (51));
|
||||
}
|
||||
|
||||
void opDisable (Compiler::Generator::CodeContainer& code)
|
||||
{
|
||||
code.push_back (Compiler::Generator::segment5 (52));
|
||||
}
|
||||
|
||||
void opGetDisabled (Compiler::Generator::CodeContainer& code)
|
||||
{
|
||||
code.push_back (Compiler::Generator::segment5 (53));
|
||||
}
|
||||
|
||||
void opEnableExplicit (Compiler::Generator::CodeContainer& code)
|
||||
{
|
||||
code.push_back (Compiler::Generator::segment5 (54));
|
||||
}
|
||||
|
||||
void opDisableExplicit (Compiler::Generator::CodeContainer& code)
|
||||
{
|
||||
code.push_back (Compiler::Generator::segment5 (55));
|
||||
}
|
||||
|
||||
void opGetDisabledExplicit (Compiler::Generator::CodeContainer& code)
|
||||
{
|
||||
code.push_back (Compiler::Generator::segment5 (56));
|
||||
}
|
||||
|
||||
void opGetDistanceExplicit (Compiler::Generator::CodeContainer& code)
|
||||
{
|
||||
code.push_back (Compiler::Generator::segment5 (57));
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (buttons>=256)
|
||||
throw std::runtime_error ("A message box can't have more than 255 buttons");
|
||||
|
||||
int index = literals.addString (message);
|
||||
|
||||
opPushInt (code, index);
|
||||
opMessageBox (code, buttons);
|
||||
}
|
||||
|
||||
void report (CodeContainer& code, Literals& literals, const std::string& message)
|
||||
{
|
||||
int index = literals.addString (message);
|
||||
|
||||
opPushInt (code, index);
|
||||
opReport (code);
|
||||
}
|
||||
|
||||
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' || valueType=='s'))
|
||||
{
|
||||
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 assignToMember (CodeContainer& code, Literals& literals, char localType,
|
||||
const std::string& name, const std::string& id, const CodeContainer& value, char valueType)
|
||||
{
|
||||
int index = literals.addString (name);
|
||||
|
||||
opPushInt (code, index);
|
||||
|
||||
index = literals.addString (id);
|
||||
|
||||
opPushInt (code, index);
|
||||
|
||||
std::copy (value.begin(), value.end(), std::back_inserter (code));
|
||||
|
||||
if (localType!=valueType)
|
||||
{
|
||||
if (localType=='f' && (valueType=='l' || valueType=='s'))
|
||||
{
|
||||
opIntToFloat (code);
|
||||
}
|
||||
else if ((localType=='l' || localType=='s') && valueType=='f')
|
||||
{
|
||||
opFloatToInt (code);
|
||||
}
|
||||
}
|
||||
|
||||
switch (localType)
|
||||
{
|
||||
case 'f':
|
||||
|
||||
opStoreMemberFloat (code);
|
||||
break;
|
||||
|
||||
case 's':
|
||||
|
||||
opStoreMemberShort (code);
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
|
||||
opStoreMemberLong (code);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
assert (0);
|
||||
}
|
||||
}
|
||||
|
||||
void fetchMember (CodeContainer& code, Literals& literals, char localType,
|
||||
const std::string& name, const std::string& id)
|
||||
{
|
||||
int index = literals.addString (name);
|
||||
|
||||
opPushInt (code, index);
|
||||
|
||||
index = literals.addString (id);
|
||||
|
||||
opPushInt (code, index);
|
||||
|
||||
switch (localType)
|
||||
{
|
||||
case 'f':
|
||||
|
||||
opFetchMemberFloat (code);
|
||||
break;
|
||||
|
||||
case 's':
|
||||
|
||||
opFetchMemberShort (code);
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
|
||||
opFetchMemberLong (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);
|
||||
}
|
||||
|
||||
void getDistance (CodeContainer& code, Literals& literals, const std::string& id)
|
||||
{
|
||||
if (id.empty())
|
||||
{
|
||||
opGetDistance (code);
|
||||
}
|
||||
else
|
||||
{
|
||||
int index = literals.addString (id);
|
||||
opPushInt (code, index);
|
||||
opGetDistanceExplicit (code);
|
||||
}
|
||||
}
|
||||
|
||||
void getSecondsPassed (CodeContainer& code)
|
||||
{
|
||||
opGetSecondsPassed (code);
|
||||
}
|
||||
|
||||
void getDisabled (CodeContainer& code, Literals& literals, const std::string& id)
|
||||
{
|
||||
if (id.empty())
|
||||
{
|
||||
opGetDisabled (code);
|
||||
}
|
||||
else
|
||||
{
|
||||
int index = literals.addString (id);
|
||||
opPushInt (code, index);
|
||||
opGetDisabledExplicit (code);
|
||||
}
|
||||
}
|
||||
|
||||
void enable (CodeContainer& code, Literals& literals, const std::string& id)
|
||||
{
|
||||
if (id.empty())
|
||||
{
|
||||
opEnable (code);
|
||||
}
|
||||
else
|
||||
{
|
||||
int index = literals.addString (id);
|
||||
opPushInt (code, index);
|
||||
opEnableExplicit (code);
|
||||
}
|
||||
}
|
||||
|
||||
void disable (CodeContainer& code, Literals& literals, const std::string& id)
|
||||
{
|
||||
if (id.empty())
|
||||
{
|
||||
opDisable (code);
|
||||
}
|
||||
else
|
||||
{
|
||||
int index = literals.addString (id);
|
||||
opPushInt (code, index);
|
||||
opDisableExplicit (code);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
130
components/compiler/generator.hpp
Normal file
130
components/compiler/generator.hpp
Normal file
|
@ -0,0 +1,130 @@
|
|||
#ifndef COMPILER_GENERATOR_H_INCLUDED
|
||||
#define COMPILER_GENERATOR_H_INCLUDED
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <cassert>
|
||||
|
||||
#include <components/interpreter/types.hpp>
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
class Literals;
|
||||
|
||||
namespace Generator
|
||||
{
|
||||
typedef std::vector<Interpreter::Type_Code> CodeContainer;
|
||||
|
||||
inline Interpreter::Type_Code segment0 (unsigned int c, unsigned int arg0)
|
||||
{
|
||||
assert (c<64);
|
||||
return (c<<24) | (arg0 & 0xffffff);
|
||||
}
|
||||
|
||||
inline Interpreter::Type_Code segment1 (unsigned int c, unsigned int arg0,
|
||||
unsigned int arg1)
|
||||
{
|
||||
assert (c<64);
|
||||
return 0x40000000 | (c<<24) | ((arg0 & 0xfff)<<12) | (arg1 & 0xfff);
|
||||
}
|
||||
|
||||
inline Interpreter::Type_Code segment2 (unsigned int c, unsigned int arg0)
|
||||
{
|
||||
assert (c<1024);
|
||||
return 0x80000000 | (c<<20) | (arg0 & 0xfffff);
|
||||
}
|
||||
|
||||
inline Interpreter::Type_Code segment3 (unsigned int c, unsigned int arg0)
|
||||
{
|
||||
assert (c<262144);
|
||||
return 0xc0000000 | (c<<8) | (arg0 & 0xff);
|
||||
}
|
||||
|
||||
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 report (CodeContainer& code, Literals& literals, const std::string& message);
|
||||
|
||||
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 assignToMember (CodeContainer& code, Literals& literals, char memberType,
|
||||
const std::string& name, const std::string& id, const CodeContainer& value, char valueType);
|
||||
|
||||
void fetchMember (CodeContainer& code, Literals& literals, char memberType,
|
||||
const std::string& name, const std::string& id);
|
||||
|
||||
void random (CodeContainer& code);
|
||||
|
||||
void scriptRunning (CodeContainer& code);
|
||||
|
||||
void startScript (CodeContainer& code);
|
||||
|
||||
void stopScript (CodeContainer& code);
|
||||
|
||||
void getDistance (CodeContainer& code, Literals& literals, const std::string& id);
|
||||
|
||||
void getSecondsPassed (CodeContainer& code);
|
||||
|
||||
void getDisabled (CodeContainer& code, Literals& literals, const std::string& id);
|
||||
|
||||
void enable (CodeContainer& code, Literals& literals, const std::string& id);
|
||||
|
||||
void disable (CodeContainer& code, Literals& literals, const std::string& id);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
451
components/compiler/lineparser.cpp
Normal file
451
components/compiler/lineparser.cpp
Normal file
|
@ -0,0 +1,451 @@
|
|||
|
||||
#include "lineparser.hpp"
|
||||
|
||||
#include "scanner.hpp"
|
||||
#include "context.hpp"
|
||||
#include "errorhandler.hpp"
|
||||
#include "skipparser.hpp"
|
||||
#include "locals.hpp"
|
||||
#include "generator.hpp"
|
||||
#include "extensions.hpp"
|
||||
#include <components/misc/stringops.hpp>
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
void LineParser::parseExpression (Scanner& scanner, const TokenLoc& loc)
|
||||
{
|
||||
mExprParser.reset();
|
||||
|
||||
if (!mExplicit.empty())
|
||||
{
|
||||
mExprParser.parseName (mExplicit, loc, scanner);
|
||||
if (mState==MemberState)
|
||||
mExprParser.parseSpecial (Scanner::S_member, loc, scanner);
|
||||
else
|
||||
mExprParser.parseSpecial (Scanner::S_ref, loc, scanner);
|
||||
}
|
||||
|
||||
scanner.scan (mExprParser);
|
||||
|
||||
char type = mExprParser.append (mCode);
|
||||
mState = EndState;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case 'l':
|
||||
|
||||
Generator::report (mCode, mLiterals, "%g");
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
|
||||
Generator::report (mCode, mLiterals, "%f");
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
throw std::runtime_error ("unknown expression result type");
|
||||
}
|
||||
}
|
||||
|
||||
LineParser::LineParser (ErrorHandler& errorHandler, Context& context, Locals& locals,
|
||||
Literals& literals, std::vector<Interpreter::Type_Code>& code, bool allowExpression)
|
||||
: Parser (errorHandler, context), mLocals (locals), mLiterals (literals), mCode (code),
|
||||
mState (BeginState), mExprParser (errorHandler, context, locals, literals),
|
||||
mAllowExpression (allowExpression), mButtons(0), mType(0)
|
||||
{}
|
||||
|
||||
bool LineParser::parseInt (int value, const TokenLoc& loc, Scanner& scanner)
|
||||
{
|
||||
if (mAllowExpression && mState==BeginState)
|
||||
{
|
||||
scanner.putbackInt (value, loc);
|
||||
parseExpression (scanner, loc);
|
||||
return true;
|
||||
}
|
||||
|
||||
return Parser::parseInt (value, loc, scanner);
|
||||
}
|
||||
|
||||
bool LineParser::parseFloat (float value, const TokenLoc& loc, Scanner& scanner)
|
||||
{
|
||||
if (mAllowExpression && mState==BeginState)
|
||||
{
|
||||
scanner.putbackFloat (value, loc);
|
||||
parseExpression (scanner, loc);
|
||||
return true;
|
||||
}
|
||||
|
||||
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 = Misc::StringUtils::lowerCase (name);
|
||||
|
||||
char type = mLocals.getType (name2);
|
||||
|
||||
if (type!=' ')
|
||||
{
|
||||
/// \todo add option to make re-declared local variables an error
|
||||
getErrorHandler().warning ("can't re-declare local variable", loc);
|
||||
SkipParser skip (getErrorHandler(), getContext());
|
||||
scanner.scan (skip);
|
||||
mState = EndState;
|
||||
return true;
|
||||
}
|
||||
|
||||
mLocals.declare (mState==ShortState ? 's' : (mState==LongState ? 'l' : 'f'),
|
||||
name2);
|
||||
|
||||
mState = EndState;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mState==SetState)
|
||||
{
|
||||
std::string name2 = Misc::StringUtils::lowerCase (name);
|
||||
mName = name2;
|
||||
|
||||
// local variable?
|
||||
char type = mLocals.getType (name2);
|
||||
if (type!=' ')
|
||||
{
|
||||
mType = type;
|
||||
mState = SetLocalVarState;
|
||||
return true;
|
||||
}
|
||||
|
||||
type = getContext().getGlobalType (name2);
|
||||
if (type!=' ')
|
||||
{
|
||||
mType = type;
|
||||
mState = SetGlobalVarState;
|
||||
return true;
|
||||
}
|
||||
|
||||
mState = SetPotentialMemberVarState;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mState==SetMemberVarState)
|
||||
{
|
||||
mMemberName = name;
|
||||
char type = getContext().getMemberType (mMemberName, mName);
|
||||
|
||||
if (type!=' ')
|
||||
{
|
||||
mState = SetMemberVarState2;
|
||||
mType = type;
|
||||
return true;
|
||||
}
|
||||
|
||||
getErrorHandler().error ("unknown variable", loc);
|
||||
SkipParser skip (getErrorHandler(), getContext());
|
||||
scanner.scan (skip);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mState==MessageState || mState==MessageCommaState)
|
||||
{
|
||||
std::string arguments;
|
||||
|
||||
for (std::size_t i=0; i<name.size(); ++i)
|
||||
{
|
||||
if (name[i]=='%')
|
||||
{
|
||||
++i;
|
||||
if (i<name.size())
|
||||
{
|
||||
if (name[i]=='G' || name[i]=='g')
|
||||
{
|
||||
arguments += "l";
|
||||
}
|
||||
else if (name[i]=='S' || name[i]=='s')
|
||||
{
|
||||
arguments += 'S';
|
||||
}
|
||||
else if (name[i]=='.' || name[i]=='f')
|
||||
{
|
||||
arguments += 'f';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!arguments.empty())
|
||||
{
|
||||
mExprParser.reset();
|
||||
mExprParser.parseArguments (arguments, scanner, mCode, true);
|
||||
}
|
||||
|
||||
mName = name;
|
||||
mButtons = 0;
|
||||
|
||||
mState = MessageButtonState;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mState==MessageButtonState || mState==MessageButtonCommaState)
|
||||
{
|
||||
Generator::pushString (mCode, mLiterals, name);
|
||||
mState = MessageButtonState;
|
||||
++mButtons;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mState==BeginState && getContext().isId (name))
|
||||
{
|
||||
mState = PotentialExplicitState;
|
||||
mExplicit = Misc::StringUtils::lowerCase (name);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mState==BeginState && mAllowExpression)
|
||||
{
|
||||
std::string name2 = Misc::StringUtils::lowerCase (name);
|
||||
|
||||
char type = mLocals.getType (name2);
|
||||
|
||||
if (type!=' ')
|
||||
{
|
||||
scanner.putbackName (name, loc);
|
||||
parseExpression (scanner, loc);
|
||||
return true;
|
||||
}
|
||||
|
||||
type = getContext().getGlobalType (name2);
|
||||
|
||||
if (type!=' ')
|
||||
{
|
||||
scanner.putbackName (name, loc);
|
||||
parseExpression (scanner, loc);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return Parser::parseName (name, loc, scanner);
|
||||
}
|
||||
|
||||
bool LineParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner)
|
||||
{
|
||||
if (mState==BeginState || mState==ExplicitState)
|
||||
{
|
||||
switch (keyword)
|
||||
{
|
||||
case Scanner::K_enable:
|
||||
|
||||
Generator::enable (mCode, mLiterals, mExplicit);
|
||||
mState = EndState;
|
||||
return true;
|
||||
|
||||
case Scanner::K_disable:
|
||||
|
||||
Generator::disable (mCode, mLiterals, mExplicit);
|
||||
mState = EndState;
|
||||
return true;
|
||||
}
|
||||
|
||||
// check for custom extensions
|
||||
if (const Extensions *extensions = getContext().getExtensions())
|
||||
{
|
||||
std::string argumentType;
|
||||
|
||||
if (extensions->isInstruction (keyword, argumentType, mState==ExplicitState))
|
||||
{
|
||||
int optionals = mExprParser.parseArguments (argumentType, scanner, mCode, true);
|
||||
|
||||
extensions->generateInstructionCode (keyword, mCode, mLiterals, mExplicit, optionals);
|
||||
mState = EndState;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (mAllowExpression)
|
||||
{
|
||||
if (keyword==Scanner::K_getdisabled || keyword==Scanner::K_getdistance)
|
||||
{
|
||||
scanner.putbackKeyword (keyword, loc);
|
||||
parseExpression (scanner, loc);
|
||||
mState = EndState;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (const Extensions *extensions = getContext().getExtensions())
|
||||
{
|
||||
char returnType;
|
||||
std::string argumentType;
|
||||
|
||||
if (extensions->isFunction (keyword, returnType, argumentType,
|
||||
!mExplicit.empty()))
|
||||
{
|
||||
scanner.putbackKeyword (keyword, loc);
|
||||
parseExpression (scanner, loc);
|
||||
mState = EndState;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mState==BeginState)
|
||||
{
|
||||
switch (keyword)
|
||||
{
|
||||
case Scanner::K_short: mState = ShortState; return true;
|
||||
case Scanner::K_long: mState = LongState; return true;
|
||||
case Scanner::K_float: mState = FloatState; return true;
|
||||
case Scanner::K_set: mState = SetState; return true;
|
||||
case Scanner::K_messagebox: mState = MessageState; return true;
|
||||
|
||||
case Scanner::K_return:
|
||||
|
||||
Generator::exit (mCode);
|
||||
mState = EndState;
|
||||
return true;
|
||||
|
||||
case Scanner::K_startscript:
|
||||
|
||||
mExprParser.parseArguments ("c", scanner, mCode, true);
|
||||
Generator::startScript (mCode);
|
||||
mState = EndState;
|
||||
return true;
|
||||
|
||||
case Scanner::K_stopscript:
|
||||
|
||||
mExprParser.parseArguments ("c", scanner, mCode, true);
|
||||
Generator::stopScript (mCode);
|
||||
mState = EndState;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (mState==SetLocalVarState && keyword==Scanner::K_to)
|
||||
{
|
||||
mExprParser.reset();
|
||||
scanner.scan (mExprParser);
|
||||
|
||||
std::vector<Interpreter::Type_Code> code;
|
||||
char type = mExprParser.append (code);
|
||||
|
||||
Generator::assignToLocal (mCode, mLocals.getType (mName),
|
||||
mLocals.getIndex (mName), code, type);
|
||||
|
||||
mState = EndState;
|
||||
return true;
|
||||
}
|
||||
else if (mState==SetGlobalVarState && keyword==Scanner::K_to)
|
||||
{
|
||||
mExprParser.reset();
|
||||
scanner.scan (mExprParser);
|
||||
|
||||
std::vector<Interpreter::Type_Code> code;
|
||||
char type = mExprParser.append (code);
|
||||
|
||||
Generator::assignToGlobal (mCode, mLiterals, mType, mName, code, type);
|
||||
|
||||
mState = EndState;
|
||||
return true;
|
||||
}
|
||||
else if (mState==SetMemberVarState2 && keyword==Scanner::K_to)
|
||||
{
|
||||
mExprParser.reset();
|
||||
scanner.scan (mExprParser);
|
||||
|
||||
std::vector<Interpreter::Type_Code> code;
|
||||
char type = mExprParser.append (code);
|
||||
|
||||
Generator::assignToMember (mCode, mLiterals, mType, mMemberName, mName, code, type);
|
||||
|
||||
mState = EndState;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mAllowExpression)
|
||||
{
|
||||
if (keyword==Scanner::K_getsquareroot || keyword==Scanner::K_menumode ||
|
||||
keyword==Scanner::K_random || keyword==Scanner::K_scriptrunning ||
|
||||
keyword==Scanner::K_getsecondspassed)
|
||||
{
|
||||
scanner.putbackKeyword (keyword, loc);
|
||||
parseExpression (scanner, loc);
|
||||
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 || mState==BeginState))
|
||||
return false;
|
||||
|
||||
if (code==Scanner::S_comma && mState==MessageState)
|
||||
{
|
||||
mState = MessageCommaState;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (code==Scanner::S_ref && mState==PotentialExplicitState)
|
||||
{
|
||||
mState = ExplicitState;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (code==Scanner::S_member && mState==PotentialExplicitState)
|
||||
{
|
||||
mState = MemberState;
|
||||
parseExpression (scanner, loc);
|
||||
mState = EndState;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (code==Scanner::S_newline && mState==MessageButtonState)
|
||||
{
|
||||
Generator::message (mCode, mLiterals, mName, mButtons);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (code==Scanner::S_comma && mState==MessageButtonState)
|
||||
{
|
||||
mState = MessageButtonCommaState;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (code==Scanner::S_member && mState==SetPotentialMemberVarState)
|
||||
{
|
||||
mState = SetMemberVarState;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mAllowExpression && mState==BeginState &&
|
||||
(code==Scanner::S_open || code==Scanner::S_minus))
|
||||
{
|
||||
scanner.putbackSpecial (code, loc);
|
||||
parseExpression (scanner, loc);
|
||||
mState = EndState;
|
||||
return true;
|
||||
}
|
||||
|
||||
return Parser::parseSpecial (code, loc, scanner);
|
||||
}
|
||||
|
||||
void LineParser::reset()
|
||||
{
|
||||
mState = BeginState;
|
||||
mName.clear();
|
||||
mExplicit.clear();
|
||||
}
|
||||
}
|
79
components/compiler/lineparser.hpp
Normal file
79
components/compiler/lineparser.hpp
Normal file
|
@ -0,0 +1,79 @@
|
|||
#ifndef COMPILER_LINEPARSER_H_INCLUDED
|
||||
#define COMPILER_LINEPARSER_H_INCLUDED
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <components/interpreter/types.hpp>
|
||||
|
||||
#include "parser.hpp"
|
||||
#include "exprparser.hpp"
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
class Locals;
|
||||
class Literals;
|
||||
|
||||
/// \brief Line parser, to be used in console scripts and as part of ScriptParser
|
||||
|
||||
class LineParser : public Parser
|
||||
{
|
||||
enum State
|
||||
{
|
||||
BeginState,
|
||||
ShortState, LongState, FloatState,
|
||||
SetState, SetLocalVarState, SetGlobalVarState, SetPotentialMemberVarState,
|
||||
SetMemberVarState, SetMemberVarState2,
|
||||
MessageState, MessageCommaState, MessageButtonState, MessageButtonCommaState,
|
||||
EndState,
|
||||
PotentialExplicitState, ExplicitState, MemberState
|
||||
};
|
||||
|
||||
Locals& mLocals;
|
||||
Literals& mLiterals;
|
||||
std::vector<Interpreter::Type_Code>& mCode;
|
||||
State mState;
|
||||
std::string mName;
|
||||
std::string mMemberName;
|
||||
int mButtons;
|
||||
std::string mExplicit;
|
||||
char mType;
|
||||
ExprParser mExprParser;
|
||||
bool mAllowExpression;
|
||||
|
||||
void parseExpression (Scanner& scanner, const TokenLoc& loc);
|
||||
|
||||
public:
|
||||
|
||||
LineParser (ErrorHandler& errorHandler, Context& context, Locals& locals,
|
||||
Literals& literals, std::vector<Interpreter::Type_Code>& code,
|
||||
bool allowExpression = false);
|
||||
///< \param allowExpression Allow lines consisting of a naked expression
|
||||
/// (result is send to the messagebox interface)
|
||||
|
||||
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
|
94
components/compiler/literals.cpp
Normal file
94
components/compiler/literals.cpp
Normal file
|
@ -0,0 +1,94 @@
|
|||
|
||||
#include "literals.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
int Literals::getIntegerSize() const
|
||||
{
|
||||
return mIntegers.size() * sizeof (Interpreter::Type_Integer);
|
||||
}
|
||||
|
||||
int Literals::getFloatSize() const
|
||||
{
|
||||
return mFloats.size() * sizeof (Interpreter::Type_Float);
|
||||
}
|
||||
|
||||
int Literals::getStringSize() const
|
||||
{
|
||||
int size = 0;
|
||||
|
||||
for (std::vector<std::string>::const_iterator iter (mStrings.begin());
|
||||
iter!=mStrings.end(); ++iter)
|
||||
size += static_cast<int> (iter->size()) + 1;
|
||||
|
||||
if (size % 4) // padding
|
||||
size += 4 - size % 4;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void Literals::append (std::vector<Interpreter::Type_Code>& code) const
|
||||
{
|
||||
for (std::vector<Interpreter::Type_Integer>::const_iterator iter (mIntegers.begin());
|
||||
iter!=mIntegers.end(); ++iter)
|
||||
code.push_back (*reinterpret_cast<const Interpreter::Type_Code *> (&*iter));
|
||||
|
||||
for (std::vector<Interpreter::Type_Float>::const_iterator iter (mFloats.begin());
|
||||
iter!=mFloats.end(); ++iter)
|
||||
code.push_back (*reinterpret_cast<const Interpreter::Type_Code *> (&*iter));
|
||||
|
||||
int stringBlockSize = getStringSize();
|
||||
int size = static_cast<int> (code.size());
|
||||
|
||||
code.resize (size+stringBlockSize/4);
|
||||
|
||||
int offset = 0;
|
||||
|
||||
for (std::vector<std::string>::const_iterator iter (mStrings.begin());
|
||||
iter!=mStrings.end(); ++iter)
|
||||
{
|
||||
int stringSize = iter->size()+1;
|
||||
|
||||
std::copy (iter->c_str(), iter->c_str()+stringSize,
|
||||
reinterpret_cast<char *> (&code[size]) + offset);
|
||||
offset += stringSize;
|
||||
}
|
||||
}
|
||||
|
||||
int Literals::addInteger (Interpreter::Type_Integer value)
|
||||
{
|
||||
int index = static_cast<int> (mIntegers.size());
|
||||
|
||||
mIntegers.push_back (value);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
int Literals::addFloat (Interpreter::Type_Float value)
|
||||
{
|
||||
int index = static_cast<int> (mFloats.size());
|
||||
|
||||
mFloats.push_back (value);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
int Literals::addString (const std::string& value)
|
||||
{
|
||||
int index = static_cast<int> (mStrings.size());
|
||||
|
||||
mStrings.push_back (value);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
void Literals::clear()
|
||||
{
|
||||
mIntegers.clear();
|
||||
mFloats.clear();
|
||||
mStrings.clear();
|
||||
}
|
||||
}
|
||||
|
49
components/compiler/literals.hpp
Normal file
49
components/compiler/literals.hpp
Normal file
|
@ -0,0 +1,49 @@
|
|||
#ifndef COMPILER_LITERALS_H_INCLUDED
|
||||
#define COMPILER_LITERALS_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <components/interpreter/types.hpp>
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
/// \brief Literal values.
|
||||
|
||||
class Literals
|
||||
{
|
||||
std::vector<Interpreter::Type_Integer> mIntegers;
|
||||
std::vector<Interpreter::Type_Float> mFloats;
|
||||
std::vector<std::string> mStrings;
|
||||
|
||||
public:
|
||||
|
||||
int getIntegerSize() const;
|
||||
///< Return size of integer block (in bytes).
|
||||
|
||||
int getFloatSize() const;
|
||||
///< Return size of float block (in bytes).
|
||||
|
||||
int getStringSize() const;
|
||||
///< Return size of string block (in bytes).
|
||||
|
||||
void append (std::vector<Interpreter::Type_Code>& code) const;
|
||||
///< Apepnd literal blocks to code.
|
||||
/// \note code blocks will be padded for 32-bit alignment.
|
||||
|
||||
int addInteger (Interpreter::Type_Integer value);
|
||||
///< add integer liternal and return index.
|
||||
|
||||
int addFloat (Interpreter::Type_Float value);
|
||||
///< add float literal and return value.
|
||||
|
||||
int addString (const std::string& value);
|
||||
///< add string literal and return value.
|
||||
|
||||
void clear();
|
||||
///< remove all literals.
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
110
components/compiler/locals.cpp
Normal file
110
components/compiler/locals.cpp
Normal file
|
@ -0,0 +1,110 @@
|
|||
|
||||
#include "locals.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <stdexcept>
|
||||
#include <algorithm>
|
||||
#include <ostream>
|
||||
#include <iterator>
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
const std::vector<std::string>& Locals::get (char type) const
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case 's': return mShorts;
|
||||
case 'l': return mLongs;
|
||||
case 'f': return mFloats;
|
||||
}
|
||||
|
||||
throw std::logic_error ("unknown variable type");
|
||||
}
|
||||
|
||||
int Locals::searchIndex (char type, const std::string& name) const
|
||||
{
|
||||
const std::vector<std::string>& collection = get (type);
|
||||
|
||||
std::vector<std::string>::const_iterator iter =
|
||||
std::find (collection.begin(), collection.end(), name);
|
||||
|
||||
if (iter==collection.end())
|
||||
return -1;
|
||||
|
||||
return iter-collection.begin();
|
||||
}
|
||||
|
||||
bool Locals::search (char type, const std::string& name) const
|
||||
{
|
||||
return searchIndex (type, name)!=-1;
|
||||
}
|
||||
|
||||
std::vector<std::string>& Locals::get (char type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case 's': return mShorts;
|
||||
case 'l': return mLongs;
|
||||
case 'f': return mFloats;
|
||||
}
|
||||
|
||||
throw std::logic_error ("unknown variable type");
|
||||
}
|
||||
|
||||
char Locals::getType (const std::string& name) const
|
||||
{
|
||||
if (search ('s', name))
|
||||
return 's';
|
||||
|
||||
if (search ('l', name))
|
||||
return 'l';
|
||||
|
||||
if (search ('f', name))
|
||||
return 'f';
|
||||
|
||||
return ' ';
|
||||
}
|
||||
|
||||
int Locals::getIndex (const std::string& name) const
|
||||
{
|
||||
int index = searchIndex ('s', name);
|
||||
|
||||
if (index!=-1)
|
||||
return index;
|
||||
|
||||
index = searchIndex ('l', name);
|
||||
|
||||
if (index!=-1)
|
||||
return index;
|
||||
|
||||
return searchIndex ('f', name);
|
||||
}
|
||||
|
||||
void Locals::write (std::ostream& localFile) const
|
||||
{
|
||||
localFile
|
||||
<< get ('s').size() << ' '
|
||||
<< get ('l').size() << ' '
|
||||
<< get ('f').size() << std::endl;
|
||||
|
||||
std::copy (get ('s').begin(), get ('s').end(),
|
||||
std::ostream_iterator<std::string> (localFile, " "));
|
||||
std::copy (get ('l').begin(), get ('l').end(),
|
||||
std::ostream_iterator<std::string> (localFile, " "));
|
||||
std::copy (get ('f').begin(), get ('f').end(),
|
||||
std::ostream_iterator<std::string> (localFile, " "));
|
||||
}
|
||||
|
||||
void Locals::declare (char type, const std::string& name)
|
||||
{
|
||||
get (type).push_back (name);
|
||||
}
|
||||
|
||||
void Locals::clear()
|
||||
{
|
||||
get ('s').clear();
|
||||
get ('l').clear();
|
||||
get ('f').clear();
|
||||
}
|
||||
}
|
||||
|
45
components/compiler/locals.hpp
Normal file
45
components/compiler/locals.hpp
Normal file
|
@ -0,0 +1,45 @@
|
|||
#ifndef COMPILER_LOCALS_H_INCLUDED
|
||||
#define COMPILER_LOCALS_H_INCLUDED
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <iosfwd>
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
/// \brief Local variable declarations
|
||||
|
||||
class Locals
|
||||
{
|
||||
std::vector<std::string> mShorts;
|
||||
std::vector<std::string> mLongs;
|
||||
std::vector<std::string> mFloats;
|
||||
|
||||
int searchIndex (char type, const std::string& name) const;
|
||||
|
||||
bool search (char type, const std::string& name) const;
|
||||
|
||||
std::vector<std::string>& get (char type);
|
||||
|
||||
public:
|
||||
|
||||
char getType (const std::string& name) const;
|
||||
///< 's': short, 'l': long, 'f': float, ' ': does not exist.
|
||||
|
||||
int getIndex (const std::string& name) const;
|
||||
///< return index for local variable \a name (-1: does not exist).
|
||||
|
||||
const std::vector<std::string>& get (char type) const;
|
||||
|
||||
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
|
6
components/compiler/nullerrorhandler.cpp
Normal file
6
components/compiler/nullerrorhandler.cpp
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
#include "nullerrorhandler.hpp"
|
||||
|
||||
void Compiler::NullErrorHandler::report (const std::string& message, const TokenLoc& loc, Type type) {}
|
||||
|
||||
void Compiler::NullErrorHandler::report (const std::string& message, Type type) {}
|
21
components/compiler/nullerrorhandler.hpp
Normal file
21
components/compiler/nullerrorhandler.hpp
Normal file
|
@ -0,0 +1,21 @@
|
|||
|
||||
#ifndef COMPILER_NULLERRORHANDLER_H_INCLUDED
|
||||
#define COMPILER_NULLERRORHANDLER_H_INCLUDED
|
||||
|
||||
#include "errorhandler.hpp"
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
/// \brief Error handler implementation: Ignore all error messages
|
||||
|
||||
class NullErrorHandler : public ErrorHandler
|
||||
{
|
||||
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
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
13
components/compiler/opcodes.cpp
Normal file
13
components/compiler/opcodes.cpp
Normal file
|
@ -0,0 +1,13 @@
|
|||
#include "opcodes.hpp"
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
namespace Control
|
||||
{
|
||||
const char *controls[numberOfControls] =
|
||||
{
|
||||
"playercontrols", "playerfighting", "playerjumping", "playerlooking", "playermagic",
|
||||
"playerviewswitch", "vanitymode"
|
||||
};
|
||||
}
|
||||
}
|
416
components/compiler/opcodes.hpp
Normal file
416
components/compiler/opcodes.hpp
Normal file
|
@ -0,0 +1,416 @@
|
|||
#ifndef COMPILER_OPCODES_H
|
||||
#define COMPILER_OPCODES_H
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
namespace Ai
|
||||
{
|
||||
const int opcodeAiTravel = 0x20000;
|
||||
const int opcodeAiTravelExplicit = 0x20001;
|
||||
const int opcodeAiEscort = 0x20002;
|
||||
const int opcodeAiEscortExplicit = 0x20003;
|
||||
const int opcodeGetAiPackageDone = 0x200007c;
|
||||
const int opcodeGetAiPackageDoneExplicit = 0x200007d;
|
||||
const int opcodeGetCurrentAiPackage = 0x20001ef;
|
||||
const int opcodeGetCurrentAiPackageExplicit = 0x20001f0;
|
||||
const int opcodeGetDetected = 0x20001f1;
|
||||
const int opcodeGetDetectedExplicit = 0x20001f2;
|
||||
const int opcodeAiWander = 0x20010;
|
||||
const int opcodeAiWanderExplicit = 0x20011;
|
||||
const int opcodeAIActivate = 0x2001e;
|
||||
const int opcodeAIActivateExplicit = 0x2001f;
|
||||
const int opcodeAiEscortCell = 0x20020;
|
||||
const int opcodeAiEscortCellExplicit = 0x20021;
|
||||
const int opcodeAiFollow = 0x20022;
|
||||
const int opcodeAiFollowExplicit = 0x20023;
|
||||
const int opcodeAiFollowCell = 0x20024;
|
||||
const int opcodeAiFollowCellExplicit = 0x20025;
|
||||
const int opcodeSetHello = 0x200015e;
|
||||
const int opcodeSetHelloExplicit = 0x200015d;
|
||||
const int opcodeSetFight = 0x200015e;
|
||||
const int opcodeSetFightExplicit = 0x200015f;
|
||||
const int opcodeSetFlee = 0x2000160;
|
||||
const int opcodeSetFleeExplicit = 0x2000161;
|
||||
const int opcodeSetAlarm = 0x2000162;
|
||||
const int opcodeSetAlarmExplicit = 0x2000163;
|
||||
const int opcodeModHello = 0x20001b7;
|
||||
const int opcodeModHelloExplicit = 0x20001b8;
|
||||
const int opcodeModFight = 0x20001b9;
|
||||
const int opcodeModFightExplicit = 0x20001ba;
|
||||
const int opcodeModFlee = 0x20001bb;
|
||||
const int opcodeModFleeExplicit = 0x20001bc;
|
||||
const int opcodeModAlarm = 0x20001bd;
|
||||
const int opcodeModAlarmExplicit = 0x20001be;
|
||||
const int opcodeGetHello = 0x20001bf;
|
||||
const int opcodeGetHelloExplicit = 0x20001c0;
|
||||
const int opcodeGetFight = 0x20001c1;
|
||||
const int opcodeGetFightExplicit = 0x20001c2;
|
||||
const int opcodeGetFlee = 0x20001c3;
|
||||
const int opcodeGetFleeExplicit = 0x20001c4;
|
||||
const int opcodeGetAlarm = 0x20001c5;
|
||||
const int opcodeGetAlarmExplicit = 0x20001c6;
|
||||
}
|
||||
|
||||
namespace Animation
|
||||
{
|
||||
const int opcodeSkipAnim = 0x2000138;
|
||||
const int opcodeSkipAnimExplicit = 0x2000139;
|
||||
const int opcodePlayAnim = 0x20006;
|
||||
const int opcodePlayAnimExplicit = 0x20007;
|
||||
const int opcodeLoopAnim = 0x20008;
|
||||
const int opcodeLoopAnimExplicit = 0x20009;
|
||||
}
|
||||
|
||||
namespace Cell
|
||||
{
|
||||
const int opcodeCellChanged = 0x2000000;
|
||||
const int opcodeCOC = 0x2000026;
|
||||
const int opcodeCOE = 0x200008e;
|
||||
const int opcodeGetInterior = 0x2000131;
|
||||
const int opcodeGetPCCell = 0x2000136;
|
||||
const int opcodeGetWaterLevel = 0x2000141;
|
||||
const int opcodeSetWaterLevel = 0x2000142;
|
||||
const int opcodeModWaterLevel = 0x2000143;
|
||||
}
|
||||
|
||||
namespace Console
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
namespace Container
|
||||
{
|
||||
const int opcodeAddItem = 0x2000076;
|
||||
const int opcodeAddItemExplicit = 0x2000077;
|
||||
const int opcodeGetItemCount = 0x2000078;
|
||||
const int opcodeGetItemCountExplicit = 0x2000079;
|
||||
const int opcodeRemoveItem = 0x200007a;
|
||||
const int opcodeRemoveItemExplicit = 0x200007b;
|
||||
const int opcodeEquip = 0x20001b3;
|
||||
const int opcodeEquipExplicit = 0x20001b4;
|
||||
const int opcodeGetArmorType = 0x20001d1;
|
||||
const int opcodeGetArmorTypeExplicit = 0x20001d2;
|
||||
const int opcodeHasItemEquipped = 0x20001d5;
|
||||
const int opcodeHasItemEquippedExplicit = 0x20001d6;
|
||||
const int opcodeHasSoulGem = 0x20001de;
|
||||
const int opcodeHasSoulGemExplicit = 0x20001df;
|
||||
const int opcodeGetWeaponType = 0x20001e0;
|
||||
const int opcodeGetWeaponTypeExplicit = 0x20001e1;
|
||||
}
|
||||
|
||||
namespace Control
|
||||
{
|
||||
const int numberOfControls = 7;
|
||||
|
||||
extern const char *controls[numberOfControls];
|
||||
|
||||
const int opcodeEnable = 0x200007e;
|
||||
const int opcodeDisable = 0x2000085;
|
||||
const int opcodeToggleCollision = 0x2000130;
|
||||
const int opcodeClearForceRun = 0x2000154;
|
||||
const int opcodeClearForceRunExplicit = 0x2000155;
|
||||
const int opcodeForceRun = 0x2000156;
|
||||
const int opcodeForceRunExplicit = 0x2000157;
|
||||
const int opcodeClearForceSneak = 0x2000158;
|
||||
const int opcodeClearForceSneakExplicit = 0x2000159;
|
||||
const int opcodeForceSneak = 0x200015a;
|
||||
const int opcodeForceSneakExplicit = 0x200015b;
|
||||
const int opcodeGetDisabled = 0x2000175;
|
||||
const int opcodeGetPcRunning = 0x20001c9;
|
||||
const int opcodeGetPcSneaking = 0x20001ca;
|
||||
const int opcodeGetForceRun = 0x20001cb;
|
||||
const int opcodeGetForceSneak = 0x20001cc;
|
||||
const int opcodeGetForceRunExplicit = 0x20001cd;
|
||||
const int opcodeGetForceSneakExplicit = 0x20001ce;
|
||||
}
|
||||
|
||||
namespace Dialogue
|
||||
{
|
||||
const int opcodeJournal = 0x2000133;
|
||||
const int opcodeSetJournalIndex = 0x2000134;
|
||||
const int opcodeGetJournalIndex = 0x2000135;
|
||||
const int opcodeAddTopic = 0x200013a;
|
||||
const int opcodeChoice = 0x2000a;
|
||||
const int opcodeForceGreeting = 0x200014f;
|
||||
const int opcodeForceGreetingExplicit = 0x2000150;
|
||||
const int opcodeGoodbye = 0x2000152;
|
||||
const int opcodeSetReputation = 0x20001ad;
|
||||
const int opcodeModReputation = 0x20001ae;
|
||||
const int opcodeSetReputationExplicit = 0x20001af;
|
||||
const int opcodeModReputationExplicit = 0x20001b0;
|
||||
const int opcodeGetReputation = 0x20001b1;
|
||||
const int opcodeGetReputationExplicit = 0x20001b2;
|
||||
const int opcodeSameFaction = 0x20001b5;
|
||||
const int opcodeSameFactionExplicit = 0x20001b6;
|
||||
}
|
||||
|
||||
namespace Gui
|
||||
{
|
||||
const int opcodeEnableBirthMenu = 0x200000e;
|
||||
const int opcodeEnableClassMenu = 0x200000f;
|
||||
const int opcodeEnableNameMenu = 0x2000010;
|
||||
const int opcodeEnableRaceMenu = 0x2000011;
|
||||
const int opcodeEnableStatsReviewMenu = 0x2000012;
|
||||
const int opcodeEnableInventoryMenu = 0x2000013;
|
||||
const int opcodeEnableMagicMenu = 0x2000014;
|
||||
const int opcodeEnableMapMenu = 0x2000015;
|
||||
const int opcodeEnableStatsMenu = 0x2000016;
|
||||
const int opcodeEnableRest = 0x2000017;
|
||||
const int opcodeShowRestMenu = 0x2000018;
|
||||
const int opcodeGetButtonPressed = 0x2000137;
|
||||
const int opcodeToggleFogOfWar = 0x2000145;
|
||||
const int opcodeToggleFullHelp = 0x2000151;
|
||||
const int opcodeShowMap = 0x20001a0;
|
||||
const int opcodeFillMap = 0x20001a1;
|
||||
}
|
||||
|
||||
namespace Misc
|
||||
{
|
||||
const int opcodeXBox = 0x200000c;
|
||||
const int opcodeOnActivate = 0x200000d;
|
||||
const int opcodeActivate = 0x2000075;
|
||||
const int opcodeLock = 0x20004;
|
||||
const int opcodeLockExplicit = 0x20005;
|
||||
const int opcodeUnlock = 0x200008c;
|
||||
const int opcodeUnlockExplicit = 0x200008d;
|
||||
const int opcodeToggleCollisionDebug = 0x2000132;
|
||||
const int opcodeToggleCollisionBoxes = 0x20001ac;
|
||||
const int opcodeToggleWireframe = 0x200013b;
|
||||
const int opcodeFadeIn = 0x200013c;
|
||||
const int opcodeFadeOut = 0x200013d;
|
||||
const int opcodeFadeTo = 0x200013e;
|
||||
const int opcodeToggleWater = 0x2000144;
|
||||
const int opcodeTogglePathgrid = 0x2000146;
|
||||
const int opcodeDontSaveObject = 0x2000153;
|
||||
const int opcodeToggleVanityMode = 0x2000174;
|
||||
const int opcodeGetPcSleep = 0x200019f;
|
||||
const int opcodeWakeUpPc = 0x20001a2;
|
||||
const int opcodeGetLocked = 0x20001c7;
|
||||
const int opcodeGetLockedExplicit = 0x20001c8;
|
||||
const int opcodeGetEffect = 0x20001cf;
|
||||
const int opcodeGetEffectExplicit = 0x20001d0;
|
||||
const int opcodeAddSoulGem = 0x20001f3;
|
||||
const int opcodeAddSoulGemExplicit = 0x20001f4;
|
||||
const int opcodeRemoveSoulGem = 0x20001f5;
|
||||
const int opcodeRemoveSoulGemExplicit = 0x20001f6;
|
||||
const int opcodeDrop = 0x20001f8;
|
||||
const int opcodeDropExplicit = 0x20001f9;
|
||||
const int opcodeDropSoulGem = 0x20001fa;
|
||||
const int opcodeDropSoulGemExplicit = 0x20001fb;
|
||||
const int opcodeGetAttacked = 0x20001d3;
|
||||
const int opcodeGetAttackedExplicit = 0x20001d4;
|
||||
const int opcodeGetWeaponDrawn = 0x20001d7;
|
||||
const int opcodeGetWeaponDrawnExplicit = 0x20001d8;
|
||||
const int opcodeGetSpellEffects = 0x20001db;
|
||||
const int opcodeGetSpellEffectsExplicit = 0x20001dc;
|
||||
const int opcodeGetCurrentTime = 0x20001dd;
|
||||
const int opcodeSetDelete = 0x20001e5;
|
||||
const int opcodeSetDeleteExplicit = 0x20001e6;
|
||||
const int opcodeGetSquareRoot = 0x20001e7;
|
||||
const int opcodeFall = 0x200020a;
|
||||
const int opcodeFallExplicit = 0x200020b;
|
||||
const int opcodeGetStandingPc = 0x200020c;
|
||||
const int opcodeGetStandingPcExplicit = 0x200020d;
|
||||
const int opcodeGetStandingActor = 0x200020e;
|
||||
const int opcodeGetStandingActorExplicit = 0x200020f;
|
||||
const int opcodeGetWindSpeed = 0x2000212;
|
||||
const int opcodePlayBink = 0x20001f7;
|
||||
const int opcodeHitOnMe = 0x2000213;
|
||||
const int opcodeHitOnMeExplicit = 0x2000214;
|
||||
const int opcodeDisableTeleporting = 0x2000215;
|
||||
const int opcodeEnableTeleporting = 0x2000216;
|
||||
const int opcodeShowVars = 0x200021d;
|
||||
const int opcodeShowVarsExplicit = 0x200021e;
|
||||
}
|
||||
|
||||
namespace Sky
|
||||
{
|
||||
const int opcodeToggleSky = 0x2000021;
|
||||
const int opcodeTurnMoonWhite = 0x2000022;
|
||||
const int opcodeTurnMoonRed = 0x2000023;
|
||||
const int opcodeGetMasserPhase = 0x2000024;
|
||||
const int opcodeGetSecundaPhase = 0x2000025;
|
||||
const int opcodeGetCurrentWeather = 0x200013f;
|
||||
const int opcodeChangeWeather = 0x2000140;
|
||||
const int opcodeModRegion = 0x20026;
|
||||
}
|
||||
|
||||
namespace Sound
|
||||
{
|
||||
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;
|
||||
|
||||
const int opcodeSayExplicit = 0x2000019;
|
||||
const int opcodeSayDoneExplicit = 0x200001a;
|
||||
const int opcodePlaySound3DExplicit = 0x200001b;
|
||||
const int opcodePlaySound3DVPExplicit = 0x200001c;
|
||||
const int opcodePlayLoopSound3DExplicit = 0x200001d;
|
||||
const int opcodePlayLoopSound3DVPExplicit = 0x200001e;
|
||||
const int opcodeStopSoundExplicit = 0x200001f;
|
||||
const int opcodeGetSoundPlayingExplicit = 0x2000020;
|
||||
}
|
||||
|
||||
namespace Stats
|
||||
{
|
||||
const int numberOfAttributes = 8;
|
||||
const int numberOfDynamics = 3;
|
||||
const int numberOfSkills = 27;
|
||||
|
||||
const int opcodeGetAttribute = 0x2000027;
|
||||
const int opcodeGetAttributeExplicit = 0x200002f;
|
||||
const int opcodeSetAttribute = 0x2000037;
|
||||
const int opcodeSetAttributeExplicit = 0x200003f;
|
||||
const int opcodeModAttribute = 0x2000047;
|
||||
const int opcodeModAttributeExplicit = 0x200004f;
|
||||
|
||||
const int opcodeGetDynamic = 0x2000057;
|
||||
const int opcodeGetDynamicExplicit = 0x200005a;
|
||||
const int opcodeSetDynamic = 0x200005d;
|
||||
const int opcodeSetDynamicExplicit = 0x2000060;
|
||||
const int opcodeModDynamic = 0x2000063;
|
||||
const int opcodeModDynamicExplicit = 0x2000066;
|
||||
const int opcodeModCurrentDynamic = 0x2000069;
|
||||
const int opcodeModCurrentDynamicExplicit = 0x200006c;
|
||||
const int opcodeGetDynamicGetRatio = 0x200006f;
|
||||
const int opcodeGetDynamicGetRatioExplicit = 0x2000072;
|
||||
|
||||
const int opcodeGetSkill = 0x200008e;
|
||||
const int opcodeGetSkillExplicit = 0x20000a9;
|
||||
const int opcodeSetSkill = 0x20000c4;
|
||||
const int opcodeSetSkillExplicit = 0x20000df;
|
||||
const int opcodeModSkill = 0x20000fa;
|
||||
const int opcodeModSkillExplicit = 0x2000115;
|
||||
|
||||
const int opcodeGetPCCrimeLevel = 0x20001ec;
|
||||
const int opcodeSetPCCrimeLevel = 0x20001ed;
|
||||
const int opcodeModPCCrimeLevel = 0x20001ee;
|
||||
|
||||
const int opcodeAddSpell = 0x2000147;
|
||||
const int opcodeAddSpellExplicit = 0x2000148;
|
||||
const int opcodeRemoveSpell = 0x2000149;
|
||||
const int opcodeRemoveSpellExplicit = 0x200014a;
|
||||
const int opcodeGetSpell = 0x200014b;
|
||||
const int opcodeGetSpellExplicit = 0x200014c;
|
||||
|
||||
const int opcodePCRaiseRank = 0x2000b;
|
||||
const int opcodePCLowerRank = 0x2000c;
|
||||
const int opcodePCJoinFaction = 0x2000d;
|
||||
const int opcodeGetPCRank = 0x2000e;
|
||||
const int opcodeGetPCRankExplicit = 0x2000f;
|
||||
const int opcodeModDisposition = 0x200014d;
|
||||
const int opcodeModDispositionExplicit = 0x200014e;
|
||||
const int opcodeSetDisposition = 0x20001a4;
|
||||
const int opcodeSetDispositionExplicit = 0x20001a5;
|
||||
const int opcodeGetDisposition = 0x20001a6;
|
||||
const int opcodeGetDispositionExplicit = 0x20001a7;
|
||||
|
||||
const int opcodeGetLevel = 0x200018c;
|
||||
const int opcodeGetLevelExplicit = 0x200018d;
|
||||
const int opcodeSetLevel = 0x200018e;
|
||||
const int opcodeSetLevelExplicit = 0x200018f;
|
||||
|
||||
const int opcodeGetDeadCount = 0x20001a3;
|
||||
|
||||
const int opcodeGetPCFacRep = 0x20012;
|
||||
const int opcodeGetPCFacRepExplicit = 0x20013;
|
||||
const int opcodeSetPCFacRep = 0x20014;
|
||||
const int opcodeSetPCFacRepExplicit = 0x20015;
|
||||
const int opcodeModPCFacRep = 0x20016;
|
||||
const int opcodeModPCFacRepExplicit = 0x20017;
|
||||
|
||||
const int opcodeGetCommonDisease = 0x20001a8;
|
||||
const int opcodeGetCommonDiseaseExplicit = 0x20001a9;
|
||||
const int opcodeGetBlightDisease = 0x20001aa;
|
||||
const int opcodeGetBlightDiseaseExplicit = 0x20001ab;
|
||||
|
||||
const int opcodeGetRace = 0x20001d9;
|
||||
const int opcodeGetRaceExplicit = 0x20001da;
|
||||
|
||||
const int opcodePcExpelled = 0x20018;
|
||||
const int opcodePcExpelledExplicit = 0x20019;
|
||||
const int opcodePcExpell = 0x2001a;
|
||||
const int opcodePcExpellExplicit = 0x2001b;
|
||||
const int opcodePcClearExpelled = 0x2001c;
|
||||
const int opcodePcClearExpelledExplicit = 0x2001d;
|
||||
const int opcodeRaiseRank = 0x20001e8;
|
||||
const int opcodeRaiseRankExplicit = 0x20001e9;
|
||||
const int opcodeLowerRank = 0x20001ea;
|
||||
const int opcodeLowerRankExplicit = 0x20001eb;
|
||||
const int opcodeOnDeath = 0x20001fc;
|
||||
const int opcodeOnDeathExplicit = 0x2000205;
|
||||
|
||||
const int opcodeBecomeWerewolf = 0x2000217;
|
||||
const int opcodeBecomeWerewolfExplicit = 0x2000218;
|
||||
const int opcodeUndoWerewolf = 0x2000219;
|
||||
const int opcodeUndoWerewolfExplicit = 0x200021a;
|
||||
const int opcodeSetWerewolfAcrobatics = 0x200021b;
|
||||
const int opcodeSetWerewolfAcrobaticsExplicit = 0x200021c;
|
||||
const int opcodeIsWerewolf = 0x20001fd;
|
||||
const int opcodeIsWerewolfExplicit = 0x20001fe;
|
||||
|
||||
const int opcodeGetWerewolfKills = 0x20001e2;
|
||||
}
|
||||
|
||||
namespace Transformation
|
||||
{
|
||||
const int opcodeSetScale = 0x2000164;
|
||||
const int opcodeSetScaleExplicit = 0x2000165;
|
||||
const int opcodeSetAngle = 0x2000166;
|
||||
const int opcodeSetAngleExplicit = 0x2000167;
|
||||
const int opcodeGetScale = 0x2000168;
|
||||
const int opcodeGetScaleExplicit = 0x2000169;
|
||||
const int opcodeGetAngle = 0x200016a;
|
||||
const int opcodeGetAngleExplicit = 0x200016b;
|
||||
const int opcodeGetPos = 0x2000190;
|
||||
const int opcodeGetPosExplicit = 0x2000191;
|
||||
const int opcodeSetPos = 0x2000192;
|
||||
const int opcodeSetPosExplicit = 0x2000193;
|
||||
const int opcodeGetStartingPos = 0x2000194;
|
||||
const int opcodeGetStartingPosExplicit = 0x2000195;
|
||||
const int opcodeGetStartingAngle = 0x2000210;
|
||||
const int opcodeGetStartingAngleExplicit = 0x2000211;
|
||||
const int opcodePosition = 0x2000196;
|
||||
const int opcodePositionExplicit = 0x2000197;
|
||||
const int opcodePositionCell = 0x2000198;
|
||||
const int opcodePositionCellExplicit = 0x2000199;
|
||||
|
||||
const int opcodePlaceItemCell = 0x200019a;
|
||||
const int opcodePlaceItem = 0x200019b;
|
||||
const int opcodePlaceAtPc = 0x200019c;
|
||||
const int opcodePlaceAtMe = 0x200019d;
|
||||
const int opcodePlaceAtMeExplicit = 0x200019e;
|
||||
const int opcodeModScale = 0x20001e3;
|
||||
const int opcodeModScaleExplicit = 0x20001e4;
|
||||
const int opcodeRotate = 0x20001ff;
|
||||
const int opcodeRotateExplicit = 0x2000200;
|
||||
const int opcodeRotateWorld = 0x2000201;
|
||||
const int opcodeRotateWorldExplicit = 0x2000202;
|
||||
const int opcodeSetAtStart = 0x2000203;
|
||||
const int opcodeSetAtStartExplicit = 0x2000204;
|
||||
const int opcodeMove = 0x2000206;
|
||||
const int opcodeMoveExplicit = 0x2000207;
|
||||
const int opcodeMoveWorld = 0x2000208;
|
||||
const int opcodeMoveWorldExplicit = 0x2000209;
|
||||
}
|
||||
|
||||
namespace User
|
||||
{
|
||||
const int opcodeUser1 = 0x200016c;
|
||||
const int opcodeUser2 = 0x200016d;
|
||||
const int opcodeUser3 = 0x200016e;
|
||||
const int opcodeUser3Explicit = 0x200016f;
|
||||
const int opcodeUser4 = 0x2000170;
|
||||
const int opcodeUser4Explicit = 0x2000171;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
74
components/compiler/output.cpp
Normal file
74
components/compiler/output.cpp
Normal file
|
@ -0,0 +1,74 @@
|
|||
|
||||
#include "output.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
|
||||
#include "locals.hpp"
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
Output::Output (Locals& locals) : mLocals (locals) {}
|
||||
|
||||
void Output::getCode (std::vector<Interpreter::Type_Code>& code) const
|
||||
{
|
||||
code.clear();
|
||||
|
||||
// header
|
||||
code.push_back (static_cast<Interpreter::Type_Code> (mCode.size()));
|
||||
|
||||
assert (mLiterals.getIntegerSize()%4==0);
|
||||
code.push_back (static_cast<Interpreter::Type_Code> (mLiterals.getIntegerSize()/4));
|
||||
|
||||
assert (mLiterals.getFloatSize()%4==0);
|
||||
code.push_back (static_cast<Interpreter::Type_Code> (mLiterals.getFloatSize()/4));
|
||||
|
||||
assert (mLiterals.getStringSize()%4==0);
|
||||
code.push_back (static_cast<Interpreter::Type_Code> (mLiterals.getStringSize()/4));
|
||||
|
||||
// code
|
||||
std::copy (mCode.begin(), mCode.end(), std::back_inserter (code));
|
||||
|
||||
// literals
|
||||
mLiterals.append (code);
|
||||
}
|
||||
|
||||
const Literals& Output::getLiterals() const
|
||||
{
|
||||
return mLiterals;
|
||||
}
|
||||
|
||||
const std::vector<Interpreter::Type_Code>& Output::getCode() const
|
||||
{
|
||||
return mCode;
|
||||
}
|
||||
|
||||
const Locals& Output::getLocals() const
|
||||
{
|
||||
return mLocals;
|
||||
}
|
||||
|
||||
Literals& Output::getLiterals()
|
||||
{
|
||||
return mLiterals;
|
||||
}
|
||||
|
||||
std::vector<Interpreter::Type_Code>& Output::getCode()
|
||||
{
|
||||
return mCode;
|
||||
}
|
||||
|
||||
Locals& Output::getLocals()
|
||||
{
|
||||
return mLocals;
|
||||
}
|
||||
|
||||
void Output::clear()
|
||||
{
|
||||
mLiterals.clear();
|
||||
mCode.clear();
|
||||
mLocals.clear();
|
||||
}
|
||||
}
|
||||
|
44
components/compiler/output.hpp
Normal file
44
components/compiler/output.hpp
Normal file
|
@ -0,0 +1,44 @@
|
|||
#ifndef COMPILER_OUTPUT_H_INCLUDED
|
||||
#define COMPILER_OUTPUT_H_INCLUDED
|
||||
|
||||
#include "literals.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <components/interpreter/types.hpp>
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
class Locals;
|
||||
|
||||
class Output
|
||||
{
|
||||
Literals mLiterals;
|
||||
std::vector<Interpreter::Type_Code> mCode;
|
||||
Locals& mLocals;
|
||||
|
||||
public:
|
||||
|
||||
Output (Locals& locals);
|
||||
|
||||
void getCode (std::vector<Interpreter::Type_Code>& code) const;
|
||||
///< store generated code in \æ code.
|
||||
|
||||
const Literals& getLiterals() const;
|
||||
|
||||
const Locals& getLocals() const;
|
||||
|
||||
const std::vector<Interpreter::Type_Code>& getCode() const;
|
||||
|
||||
Literals& getLiterals();
|
||||
|
||||
std::vector<Interpreter::Type_Code>& getCode();
|
||||
|
||||
Locals& getLocals();
|
||||
|
||||
void clear();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
185
components/compiler/parser.cpp
Normal file
185
components/compiler/parser.cpp
Normal file
|
@ -0,0 +1,185 @@
|
|||
|
||||
#include "parser.hpp"
|
||||
|
||||
#include <cctype>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
|
||||
#include "errorhandler.hpp"
|
||||
#include "exception.hpp"
|
||||
#include "scanner.hpp"
|
||||
|
||||
#include <components/misc/stringops.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 = Misc::StringUtils::lowerCase(name);
|
||||
|
||||
return lowerCase;
|
||||
}
|
||||
|
||||
Parser::Parser (ErrorHandler& errorHandler, Context& context)
|
||||
: mErrorHandler (errorHandler), mContext (context), mOptional (false), mEmpty (true)
|
||||
{}
|
||||
|
||||
// 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)
|
||||
{
|
||||
if (!(mOptional && mEmpty))
|
||||
reportSeriousError ("Unexpected numeric value", loc);
|
||||
else
|
||||
scanner.putbackInt (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)
|
||||
{
|
||||
if (!(mOptional && mEmpty))
|
||||
reportSeriousError ("Unexpected floating point value", loc);
|
||||
else
|
||||
scanner.putbackFloat (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)
|
||||
{
|
||||
if (!(mOptional && mEmpty))
|
||||
reportSeriousError ("Unexpected name", loc);
|
||||
else
|
||||
scanner.putbackName (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)
|
||||
{
|
||||
if (!(mOptional && mEmpty))
|
||||
reportSeriousError ("Unexpected keyword", loc);
|
||||
else
|
||||
scanner.putbackKeyword (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)
|
||||
{
|
||||
if (!(mOptional && mEmpty))
|
||||
reportSeriousError ("Unexpected special token", loc);
|
||||
else
|
||||
scanner.putbackSpecial (code, loc);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Parser::parseComment (const std::string& comment, const TokenLoc& loc, Scanner& scanner)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Handle an EOF token.
|
||||
//
|
||||
// - Default-implementation: Report an error.
|
||||
|
||||
void Parser::parseEOF (Scanner& scanner)
|
||||
{
|
||||
reportEOF();
|
||||
}
|
||||
|
||||
void Parser::reset()
|
||||
{
|
||||
mOptional = false;
|
||||
mEmpty = true;
|
||||
}
|
||||
|
||||
void Parser::setOptional (bool optional)
|
||||
{
|
||||
mOptional = optional;
|
||||
}
|
||||
|
||||
void Parser::start()
|
||||
{
|
||||
mEmpty = false;
|
||||
}
|
||||
|
||||
bool Parser::isEmpty() const
|
||||
{
|
||||
return mEmpty;
|
||||
}
|
||||
}
|
112
components/compiler/parser.hpp
Normal file
112
components/compiler/parser.hpp
Normal file
|
@ -0,0 +1,112 @@
|
|||
#ifndef COMPILER_PARSER_H_INCLUDED
|
||||
#define COMPILER_PARSER_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
class Scanner;
|
||||
struct TokenLoc;
|
||||
class ErrorHandler;
|
||||
class Context;
|
||||
|
||||
/// \brief Parser base class
|
||||
///
|
||||
/// This class defines a callback-parser.
|
||||
|
||||
class Parser
|
||||
{
|
||||
ErrorHandler& mErrorHandler;
|
||||
Context& mContext;
|
||||
bool mOptional;
|
||||
bool mEmpty;
|
||||
|
||||
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 bool parseComment (const std::string& comment, const TokenLoc& loc,
|
||||
Scanner& scanner);
|
||||
///< Handle comment token.
|
||||
/// \return fetch another token?
|
||||
///
|
||||
/// - Default-implementation: ignored (and return true).
|
||||
|
||||
virtual void parseEOF (Scanner& scanner);
|
||||
///< Handle EOF token.
|
||||
///
|
||||
/// - Default-implementation: Report an error.
|
||||
|
||||
virtual void reset();
|
||||
///< Reset parser to clean state.
|
||||
|
||||
void setOptional (bool optional);
|
||||
///< Optional mode: If nothign has been parsed yet and an unexpected token is delivered, stop
|
||||
/// parsing without raising an exception (after a reset the parser is in non-optional mode).
|
||||
|
||||
void start();
|
||||
///< Mark parser as non-empty (at least one token has been parser).
|
||||
|
||||
bool isEmpty() const;
|
||||
///< Has anything been parsed?
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
564
components/compiler/scanner.cpp
Normal file
564
components/compiler/scanner.cpp
Normal file
|
@ -0,0 +1,564 @@
|
|||
|
||||
#include "scanner.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <cctype>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
|
||||
#include "exception.hpp"
|
||||
#include "errorhandler.hpp"
|
||||
#include "parser.hpp"
|
||||
#include "extensions.hpp"
|
||||
|
||||
#include <components/misc/stringops.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==';')
|
||||
{
|
||||
std::string comment;
|
||||
|
||||
comment += c;
|
||||
|
||||
while (get (c))
|
||||
{
|
||||
if (c=='\n')
|
||||
{
|
||||
putback (c);
|
||||
break;
|
||||
}
|
||||
else
|
||||
comment += c;
|
||||
}
|
||||
|
||||
TokenLoc loc (mLoc);
|
||||
mLoc.mLiteral.clear();
|
||||
|
||||
return parser.parseComment (comment, loc, *this);
|
||||
}
|
||||
else if (isWhitespace (c))
|
||||
{
|
||||
mLoc.mLiteral.clear();
|
||||
return true;
|
||||
}
|
||||
else if (c==':')
|
||||
{
|
||||
// treat : as a whitespace :(
|
||||
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)
|
||||
{
|
||||
assert(c != '\0');
|
||||
std::string value;
|
||||
value += c;
|
||||
|
||||
bool error = false;
|
||||
|
||||
while (get (c))
|
||||
{
|
||||
if (std::isdigit (c))
|
||||
{
|
||||
value += c;
|
||||
}
|
||||
else if (std::isalpha (c) || c=='_')
|
||||
error = true;
|
||||
else if (c=='.' && !error)
|
||||
{
|
||||
return scanFloat (value, parser, cont);
|
||||
}
|
||||
else
|
||||
{
|
||||
putback (c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (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;
|
||||
}
|
||||
|
||||
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",
|
||||
"getdistance",
|
||||
"getsecondspassed",
|
||||
"enable", "disable", "getdisabled",
|
||||
0
|
||||
};
|
||||
|
||||
bool Scanner::scanName (char c, Parser& parser, bool& cont)
|
||||
{
|
||||
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 = Misc::StringUtils::lowerCase(name);
|
||||
|
||||
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;
|
||||
}
|
||||
// ignoring escape sequences for now, because they are messing up stupid Windows path names.
|
||||
// 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=='_' || c=='`' ||
|
||||
/// \todo add an option to disable the following hack. Also, find out who is
|
||||
/// responsible for allowing it in the first place and meet up with that person in
|
||||
/// a dark alley.
|
||||
(c=='-' && !name.empty() && std::isalpha (mStream.peek()))))
|
||||
{
|
||||
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=='.')
|
||||
{
|
||||
// check, if this starts a float literal
|
||||
if (get (c))
|
||||
{
|
||||
putback (c);
|
||||
|
||||
if (std::isdigit (c))
|
||||
return scanFloat ("", parser, cont);
|
||||
}
|
||||
|
||||
special = S_member;
|
||||
}
|
||||
else if (c=='=')
|
||||
{
|
||||
if (get (c))
|
||||
{
|
||||
if (c=='=')
|
||||
special = S_cmpEQ;
|
||||
else
|
||||
{
|
||||
special = S_cmpEQ;
|
||||
putback (c);
|
||||
// return false;
|
||||
// Allow = as synonym for ==. \todo optionally disable for post-1.0 scripting improvements.
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
||||
if (get (c) && c!='=') // <== is a allowed as an alternative to <= :(
|
||||
putback (c);
|
||||
}
|
||||
else
|
||||
{
|
||||
putback (c);
|
||||
special = S_cmpLT;
|
||||
}
|
||||
}
|
||||
else
|
||||
special = S_cmpLT;
|
||||
}
|
||||
else if (c=='>')
|
||||
{
|
||||
if (get (c))
|
||||
{
|
||||
if (c=='=')
|
||||
{
|
||||
special = S_cmpGE;
|
||||
|
||||
if (get (c) && c!='=') // >== is a allowed as an alternative to >= :(
|
||||
putback (c);
|
||||
}
|
||||
else
|
||||
{
|
||||
putback (c);
|
||||
special = S_cmpGT;
|
||||
}
|
||||
}
|
||||
else
|
||||
special = S_cmpGT;
|
||||
}
|
||||
else if (c==',')
|
||||
special = S_comma;
|
||||
else if (c=='+')
|
||||
special = S_plus;
|
||||
else if (c=='*')
|
||||
special = S_mult;
|
||||
else if (c=='/')
|
||||
special = S_div;
|
||||
else
|
||||
return false;
|
||||
|
||||
if (special==S_newline)
|
||||
mLoc.mLiteral = "<newline>";
|
||||
|
||||
TokenLoc loc (mLoc);
|
||||
mLoc.mLiteral.clear();
|
||||
|
||||
cont = parser.parseSpecial (special, loc, *this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Scanner::isWhitespace (char c)
|
||||
{
|
||||
return c==' ' || c=='\t';
|
||||
}
|
||||
|
||||
// constructor
|
||||
|
||||
Scanner::Scanner (ErrorHandler& errorHandler, std::istream& inputStream,
|
||||
const Extensions *extensions)
|
||||
: mErrorHandler (errorHandler), mStream (inputStream), mExtensions (extensions),
|
||||
mPutback (Putback_None), mPutbackCode(0), mPutbackInteger(0), mPutbackFloat(0)
|
||||
{
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void Scanner::listKeywords (std::vector<std::string>& keywords)
|
||||
{
|
||||
for (int i=0; Compiler::keywords[i]; ++i)
|
||||
keywords.push_back (Compiler::keywords[i]);
|
||||
|
||||
if (mExtensions)
|
||||
mExtensions->listKeywords (keywords);
|
||||
}
|
||||
}
|
126
components/compiler/scanner.hpp
Normal file
126
components/compiler/scanner.hpp
Normal file
|
@ -0,0 +1,126 @@
|
|||
#ifndef COMPILER_SCANNER_H_INCLUDED
|
||||
#define COMPILER_SCANNER_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
#include <iosfwd>
|
||||
#include <vector>
|
||||
|
||||
#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,
|
||||
K_getdistance,
|
||||
K_getsecondspassed,
|
||||
K_enable, K_disable, K_getdisabled
|
||||
};
|
||||
|
||||
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,
|
||||
S_member
|
||||
};
|
||||
|
||||
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
|
||||
|
||||
void listKeywords (std::vector<std::string>& keywords);
|
||||
///< Append all known keywords to \æ kaywords.
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
92
components/compiler/scriptparser.cpp
Normal file
92
components/compiler/scriptparser.cpp
Normal file
|
@ -0,0 +1,92 @@
|
|||
|
||||
#include "scriptparser.hpp"
|
||||
|
||||
#include "scanner.hpp"
|
||||
#include "skipparser.hpp"
|
||||
#include "errorhandler.hpp"
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
ScriptParser::ScriptParser (ErrorHandler& errorHandler, Context& context,
|
||||
Locals& locals, bool end)
|
||||
: Parser (errorHandler, context), mOutput (locals),
|
||||
mLineParser (errorHandler, context, locals, mOutput.getLiterals(), mOutput.getCode()),
|
||||
mControlParser (errorHandler, context, locals, mOutput.getLiterals()),
|
||||
mEnd (end)
|
||||
{}
|
||||
|
||||
void ScriptParser::getCode (std::vector<Interpreter::Type_Code>& code) const
|
||||
{
|
||||
mOutput.getCode (code);
|
||||
}
|
||||
|
||||
bool ScriptParser::parseName (const std::string& name, const TokenLoc& loc,
|
||||
Scanner& scanner)
|
||||
{
|
||||
mLineParser.reset();
|
||||
if (mLineParser.parseName (name, loc, scanner))
|
||||
scanner.scan (mLineParser);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ScriptParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner)
|
||||
{
|
||||
if (keyword==Scanner::K_while || keyword==Scanner::K_if)
|
||||
{
|
||||
mControlParser.reset();
|
||||
if (mControlParser.parseKeyword (keyword, loc, scanner))
|
||||
scanner.scan (mControlParser);
|
||||
|
||||
mControlParser.appendCode (mOutput.getCode());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// \todo add an option to disable this nonsense
|
||||
if (keyword==Scanner::K_endif)
|
||||
{
|
||||
// surplus endif
|
||||
getErrorHandler().warning ("endif without matching if/elseif", loc);
|
||||
|
||||
SkipParser skip (getErrorHandler(), getContext());
|
||||
scanner.scan (skip);
|
||||
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();
|
||||
}
|
||||
}
|
54
components/compiler/scriptparser.hpp
Normal file
54
components/compiler/scriptparser.hpp
Normal file
|
@ -0,0 +1,54 @@
|
|||
#ifndef COMPILER_SCRIPTPARSER_H_INCLUDED
|
||||
#define COMPILER_SCRIPTPARSER_H_INCLUDED
|
||||
|
||||
|
||||
#include "parser.hpp"
|
||||
#include "lineparser.hpp"
|
||||
#include "controlparser.hpp"
|
||||
#include "output.hpp"
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
class Locals;
|
||||
|
||||
// Script parser, to be used in dialogue scripts and as part of FileParser
|
||||
|
||||
class ScriptParser : public Parser
|
||||
{
|
||||
Output mOutput;
|
||||
LineParser mLineParser;
|
||||
ControlParser mControlParser;
|
||||
bool mEnd;
|
||||
|
||||
public:
|
||||
|
||||
/// \param end of script is marked by end keyword.
|
||||
ScriptParser (ErrorHandler& errorHandler, Context& context, Locals& locals,
|
||||
bool end = false);
|
||||
|
||||
void getCode (std::vector<Interpreter::Type_Code>& code) const;
|
||||
///< store generated code in \æ code.
|
||||
|
||||
virtual bool parseName (const std::string& name, const TokenLoc& loc,
|
||||
Scanner& scanner);
|
||||
///< Handle a name token.
|
||||
/// \return fetch another token?
|
||||
|
||||
virtual bool parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner);
|
||||
///< Handle a keyword token.
|
||||
/// \return fetch another token?
|
||||
|
||||
virtual bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner);
|
||||
///< Handle a special character token.
|
||||
/// \return fetch another token?
|
||||
|
||||
virtual void parseEOF (Scanner& scanner);
|
||||
///< Handle EOF token.
|
||||
|
||||
void reset();
|
||||
///< Reset parser to clean state.
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
41
components/compiler/skipparser.cpp
Normal file
41
components/compiler/skipparser.cpp
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
42
components/compiler/skipparser.hpp
Normal file
42
components/compiler/skipparser.hpp
Normal file
|
@ -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
|
||||
|
39
components/compiler/streamerrorhandler.cpp
Normal file
39
components/compiler/streamerrorhandler.cpp
Normal file
|
@ -0,0 +1,39 @@
|
|||
|
||||
#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) {}
|
||||
}
|
37
components/compiler/streamerrorhandler.hpp
Normal file
37
components/compiler/streamerrorhandler.hpp
Normal file
|
@ -0,0 +1,37 @@
|
|||
|
||||
#ifndef COMPILER_STREAMERRORHANDLER_H_INCLUDED
|
||||
#define COMPILER_STREAMERRORHANDLER_H_INCLUDED
|
||||
|
||||
#include <ostream>
|
||||
|
||||
#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
|
64
components/compiler/stringparser.cpp
Normal file
64
components/compiler/stringparser.cpp
Normal file
|
@ -0,0 +1,64 @@
|
|||
|
||||
#include "stringparser.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
|
||||
#include "scanner.hpp"
|
||||
#include "generator.hpp"
|
||||
#include <components/misc/stringops.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)
|
||||
{
|
||||
start();
|
||||
if (mSmashCase)
|
||||
Generator::pushString (mCode, mLiterals, Misc::StringUtils::lowerCase (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<Interpreter::Type_Code>& code)
|
||||
{
|
||||
std::copy (mCode.begin(), mCode.end(), std::back_inserter (code));
|
||||
}
|
||||
|
||||
void StringParser::reset()
|
||||
{
|
||||
mState = StartState;
|
||||
mCode.clear();
|
||||
mSmashCase = false;
|
||||
Parser::reset();
|
||||
}
|
||||
|
||||
void StringParser::smashCase()
|
||||
{
|
||||
mSmashCase = true;
|
||||
}
|
||||
}
|
50
components/compiler/stringparser.hpp
Normal file
50
components/compiler/stringparser.hpp
Normal file
|
@ -0,0 +1,50 @@
|
|||
#ifndef COMPILER_STRINGPARSER_H_INCLUDED
|
||||
#define COMPILER_STRINGPARSER_H_INCLUDED
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <components/interpreter/types.hpp>
|
||||
|
||||
#include "parser.hpp"
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
class Literals;
|
||||
|
||||
class StringParser : public Parser
|
||||
{
|
||||
enum State
|
||||
{
|
||||
StartState, CommaState
|
||||
};
|
||||
|
||||
Literals& mLiterals;
|
||||
State mState;
|
||||
std::vector<Interpreter::Type_Code> 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<Interpreter::Type_Code>& 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
|
20
components/compiler/tokenloc.hpp
Normal file
20
components/compiler/tokenloc.hpp
Normal file
|
@ -0,0 +1,20 @@
|
|||
#ifndef COMPILER_TOKENLOC_H_INCLUDED
|
||||
#define COMPILER_TOKENLOC_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
|
||||
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
|
30
components/doc.hpp
Normal file
30
components/doc.hpp
Normal file
|
@ -0,0 +1,30 @@
|
|||
// Note: This is not a regular source file.
|
||||
|
||||
/// \defgroup components Components
|
||||
|
||||
/// \namespace ESMS
|
||||
/// \ingroup components
|
||||
/// \brief ESM/ESP record store
|
||||
|
||||
/// \namespace ESM
|
||||
/// \ingroup components
|
||||
/// \brief ESM/ESP records
|
||||
|
||||
/// \namespace FileFinder
|
||||
/// \ingroup components
|
||||
/// \brief Linux/Windows-path resolving
|
||||
|
||||
/// \namespace ToUTF
|
||||
/// \ingroup components
|
||||
/// \brief Text encoding
|
||||
|
||||
/// \namespace Compiler
|
||||
/// \ingroup components
|
||||
/// \brief script compiler
|
||||
|
||||
/// \namespace Interpreter
|
||||
/// \ingroup components
|
||||
/// \brief script interpreter
|
||||
|
||||
// TODO put nif and nifogre in different namespaces (or merge them)
|
||||
// TODO put other components into namespaces
|
77
components/esm/aipackage.cpp
Normal file
77
components/esm/aipackage.cpp
Normal file
|
@ -0,0 +1,77 @@
|
|||
#include "aipackage.hpp"
|
||||
|
||||
#include "esmreader.hpp"
|
||||
#include "esmwriter.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
void AIData::blank()
|
||||
{
|
||||
mHello = mU1 = mFight = mFlee = mAlarm = mU2 = mU3 = mU4 = 0;
|
||||
mServices = 0;
|
||||
}
|
||||
|
||||
void AIPackageList::load(ESMReader &esm)
|
||||
{
|
||||
while (esm.hasMoreSubs()) {
|
||||
// initialize every iteration
|
||||
AIPackage pack;
|
||||
esm.getSubName();
|
||||
if (esm.retSubName() == 0x54444e43) { // CNDT
|
||||
mList.back().mCellName = esm.getHString();
|
||||
} else if (esm.retSubName() == AI_Wander) {
|
||||
pack.mType = AI_Wander;
|
||||
esm.getHExact(&pack.mWander, 14);
|
||||
mList.push_back(pack);
|
||||
} else if (esm.retSubName() == AI_Travel) {
|
||||
pack.mType = AI_Travel;
|
||||
esm.getHExact(&pack.mTravel, 16);
|
||||
mList.push_back(pack);
|
||||
} else if (esm.retSubName() == AI_Escort ||
|
||||
esm.retSubName() == AI_Follow)
|
||||
{
|
||||
pack.mType =
|
||||
(esm.retSubName() == AI_Escort) ? AI_Escort : AI_Follow;
|
||||
esm.getHExact(&pack.mTarget, 48);
|
||||
mList.push_back(pack);
|
||||
} else if (esm.retSubName() == AI_Activate) {
|
||||
pack.mType = AI_Activate;
|
||||
esm.getHExact(&pack.mActivate, 33);
|
||||
mList.push_back(pack);
|
||||
} else { // not AI package related data, so leave
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AIPackageList::save(ESMWriter &esm)
|
||||
{
|
||||
typedef std::vector<AIPackage>::iterator PackageIter;
|
||||
for (PackageIter it = mList.begin(); it != mList.end(); ++it) {
|
||||
switch (it->mType) {
|
||||
case AI_Wander:
|
||||
esm.writeHNT("AI_W", it->mWander, sizeof(it->mWander));
|
||||
break;
|
||||
|
||||
case AI_Travel:
|
||||
esm.writeHNT("AI_T", it->mTravel, sizeof(it->mTravel));
|
||||
break;
|
||||
|
||||
case AI_Activate:
|
||||
esm.writeHNT("AI_A", it->mActivate, sizeof(it->mActivate));
|
||||
break;
|
||||
|
||||
case AI_Escort:
|
||||
case AI_Follow: {
|
||||
const char *name = (it->mType == AI_Escort) ? "AI_E" : "AI_F";
|
||||
esm.writeHNT(name, it->mTarget, sizeof(it->mTarget));
|
||||
esm.writeHNOCString("CNDT", it->mCellName);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
101
components/esm/aipackage.hpp
Normal file
101
components/esm/aipackage.hpp
Normal file
|
@ -0,0 +1,101 @@
|
|||
#ifndef OPENMW_ESM_AIPACKAGE_H
|
||||
#define OPENMW_ESM_AIPACKAGE_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include "esmcommon.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class ESMReader;
|
||||
class ESMWriter;
|
||||
|
||||
#pragma pack(push)
|
||||
#pragma pack(1)
|
||||
|
||||
struct AIData
|
||||
{
|
||||
// These are probabilities
|
||||
char mHello, mU1, mFight, mFlee, mAlarm, mU2, mU3, mU4;
|
||||
int mServices; // See the Services enum
|
||||
|
||||
void blank();
|
||||
///< Set record to default state (does not touch the ID).
|
||||
}; // 12 bytes
|
||||
|
||||
struct AIWander
|
||||
{
|
||||
short mDistance;
|
||||
short mDuration;
|
||||
unsigned char mTimeOfDay;
|
||||
unsigned char mIdle[8];
|
||||
unsigned char mUnk;
|
||||
};
|
||||
|
||||
struct AITravel
|
||||
{
|
||||
float mX, mY, mZ;
|
||||
int mUnk;
|
||||
};
|
||||
|
||||
struct AITarget
|
||||
{
|
||||
float mX, mY, mZ;
|
||||
short mDuration;
|
||||
NAME32 mId;
|
||||
short mUnk;
|
||||
};
|
||||
|
||||
struct AIActivate
|
||||
{
|
||||
NAME32 mName;
|
||||
unsigned char mUnk;
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
enum
|
||||
{
|
||||
AI_Wander = 0x575f4941,
|
||||
AI_Travel = 0x545f4941,
|
||||
AI_Follow = 0x465f4941,
|
||||
AI_Escort = 0x455f4941,
|
||||
AI_Activate = 0x415f4941
|
||||
};
|
||||
|
||||
/// \note Used for storaging packages in a single container
|
||||
/// w/o manual memory allocation accordingly to policy standards
|
||||
struct AIPackage
|
||||
{
|
||||
int mType;
|
||||
|
||||
// Anonymous union
|
||||
union
|
||||
{
|
||||
AIWander mWander;
|
||||
AITravel mTravel;
|
||||
AITarget mTarget;
|
||||
AIActivate mActivate;
|
||||
};
|
||||
|
||||
/// \note for AITarget only, placed here to stick with union,
|
||||
/// overhead should be not so awful
|
||||
std::string mCellName;
|
||||
};
|
||||
|
||||
struct AIPackageList
|
||||
{
|
||||
std::vector<AIPackage> mList;
|
||||
|
||||
/// \note This breaks consistency of subrecords reading:
|
||||
/// after calling it subrecord name is already read, so
|
||||
/// it needs to use retSubName() if needed. But, hey, there
|
||||
/// is only one field left (XSCL) and only two records uses AI
|
||||
void load(ESMReader &esm);
|
||||
void save(ESMWriter &esm);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
58
components/esm/attr.cpp
Normal file
58
components/esm/attr.cpp
Normal file
|
@ -0,0 +1,58 @@
|
|||
#include "attr.hpp"
|
||||
|
||||
using namespace ESM;
|
||||
|
||||
const Attribute::AttributeID Attribute::sAttributeIds[Attribute::Length] = {
|
||||
Attribute::Strength,
|
||||
Attribute::Intelligence,
|
||||
Attribute::Willpower,
|
||||
Attribute::Agility,
|
||||
Attribute::Speed,
|
||||
Attribute::Endurance,
|
||||
Attribute::Personality,
|
||||
Attribute::Luck
|
||||
};
|
||||
|
||||
const std::string Attribute::sAttributeNames[Attribute::Length] = {
|
||||
"Strength",
|
||||
"Intelligence",
|
||||
"Willpower",
|
||||
"Agility",
|
||||
"Speed",
|
||||
"Endurance",
|
||||
"Personality",
|
||||
"Luck"
|
||||
};
|
||||
|
||||
const std::string Attribute::sGmstAttributeIds[Attribute::Length] = {
|
||||
"sAttributeStrength",
|
||||
"sAttributeIntelligence",
|
||||
"sAttributeWillpower",
|
||||
"sAttributeAgility",
|
||||
"sAttributeSpeed",
|
||||
"sAttributeEndurance",
|
||||
"sAttributePersonality",
|
||||
"sAttributeLuck"
|
||||
};
|
||||
|
||||
const std::string Attribute::sGmstAttributeDescIds[Attribute::Length] = {
|
||||
"sStrDesc",
|
||||
"sIntDesc",
|
||||
"sWilDesc",
|
||||
"sAgiDesc",
|
||||
"sSpdDesc",
|
||||
"sEndDesc",
|
||||
"sPerDesc",
|
||||
"sLucDesc"
|
||||
};
|
||||
|
||||
const std::string Attribute::sAttributeIcons[Attribute::Length] = {
|
||||
"icons\\k\\attribute_strength.dds",
|
||||
"icons\\k\\attribute_int.dds",
|
||||
"icons\\k\\attribute_wilpower.dds",
|
||||
"icons\\k\\attribute_agility.dds",
|
||||
"icons\\k\\attribute_speed.dds",
|
||||
"icons\\k\\attribute_endurance.dds",
|
||||
"icons\\k\\attribute_personality.dds",
|
||||
"icons\\k\\attribute_luck.dds"
|
||||
};
|
44
components/esm/attr.hpp
Normal file
44
components/esm/attr.hpp
Normal file
|
@ -0,0 +1,44 @@
|
|||
#ifndef OPENMW_ESM_ATTR_H
|
||||
#define OPENMW_ESM_ATTR_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace ESM {
|
||||
|
||||
/*
|
||||
* Attribute definitions
|
||||
*/
|
||||
|
||||
struct Attribute
|
||||
{
|
||||
enum AttributeID
|
||||
{
|
||||
Strength = 0,
|
||||
Intelligence = 1,
|
||||
Willpower = 2,
|
||||
Agility = 3,
|
||||
Speed = 4,
|
||||
Endurance = 5,
|
||||
Personality = 6,
|
||||
Luck = 7,
|
||||
Length
|
||||
};
|
||||
|
||||
AttributeID mId;
|
||||
std::string mName, mDescription;
|
||||
|
||||
static const AttributeID sAttributeIds[Length];
|
||||
static const std::string sAttributeNames[Length];
|
||||
static const std::string sGmstAttributeIds[Length];
|
||||
static const std::string sGmstAttributeDescIds[Length];
|
||||
static const std::string sAttributeIcons[Length];
|
||||
|
||||
Attribute(AttributeID id, const std::string &name, const std::string &description)
|
||||
: mId(id)
|
||||
, mName(name)
|
||||
, mDescription(description)
|
||||
{
|
||||
}
|
||||
};
|
||||
}
|
||||
#endif
|
87
components/esm/cellref.cpp
Normal file
87
components/esm/cellref.cpp
Normal file
|
@ -0,0 +1,87 @@
|
|||
|
||||
#include "cellref.hpp"
|
||||
|
||||
#include "esmwriter.hpp"
|
||||
|
||||
void ESM::CellRef::save(ESMWriter &esm)
|
||||
{
|
||||
esm.writeHNT("FRMR", mRefnum);
|
||||
esm.writeHNCString("NAME", mRefID);
|
||||
|
||||
if (mScale != 1.0) {
|
||||
esm.writeHNT("XSCL", mScale);
|
||||
}
|
||||
|
||||
esm.writeHNOCString("ANAM", mOwner);
|
||||
esm.writeHNOCString("BNAM", mGlob);
|
||||
esm.writeHNOCString("XSOL", mSoul);
|
||||
|
||||
esm.writeHNOCString("CNAM", mFaction);
|
||||
if (mFactIndex != -2) {
|
||||
esm.writeHNT("INDX", mFactIndex);
|
||||
}
|
||||
|
||||
if (mEnchantmentCharge != -1)
|
||||
esm.writeHNT("XCHG", mEnchantmentCharge);
|
||||
|
||||
if (mCharge != -1)
|
||||
esm.writeHNT("INTV", mCharge);
|
||||
|
||||
if (mGoldValue != 1) {
|
||||
esm.writeHNT("NAM9", mGoldValue);
|
||||
}
|
||||
|
||||
if (mTeleport)
|
||||
{
|
||||
esm.writeHNT("DODT", mDoorDest);
|
||||
esm.writeHNOCString("DNAM", mDestCell);
|
||||
}
|
||||
|
||||
if (mLockLevel != -1) {
|
||||
esm.writeHNT("FLTV", mLockLevel);
|
||||
}
|
||||
esm.writeHNOCString("KNAM", mKey);
|
||||
esm.writeHNOCString("TNAM", mTrap);
|
||||
|
||||
if (mReferenceBlocked != -1) {
|
||||
esm.writeHNT("UNAM", mReferenceBlocked);
|
||||
}
|
||||
if (mFltv != 0) {
|
||||
esm.writeHNT("FLTV", mFltv);
|
||||
}
|
||||
|
||||
esm.writeHNT("DATA", mPos, 24);
|
||||
if (mNam0 != 0) {
|
||||
esm.writeHNT("NAM0", mNam0);
|
||||
}
|
||||
}
|
||||
|
||||
void ESM::CellRef::blank()
|
||||
{
|
||||
mRefnum = 0;
|
||||
mRefID.clear();
|
||||
mScale = 1;
|
||||
mOwner.clear();
|
||||
mGlob.clear();
|
||||
mSoul.clear();
|
||||
mFaction.clear();
|
||||
mFactIndex = -1;
|
||||
mCharge = 0;
|
||||
mEnchantmentCharge = 0;
|
||||
mGoldValue = 0;
|
||||
mDestCell.clear();
|
||||
mLockLevel = 0;
|
||||
mKey.clear();
|
||||
mTrap.clear();
|
||||
mReferenceBlocked = 0;
|
||||
mFltv = 0;
|
||||
mNam0 = 0;
|
||||
|
||||
for (int i=0; i<3; ++i)
|
||||
{
|
||||
mDoorDest.pos[i] = 0;
|
||||
mDoorDest.rot[i] = 0;
|
||||
mPos.pos[i] = 0;
|
||||
mPos.rot[i] = 0;
|
||||
}
|
||||
}
|
92
components/esm/cellref.hpp
Normal file
92
components/esm/cellref.hpp
Normal file
|
@ -0,0 +1,92 @@
|
|||
#ifndef OPENMW_ESM_CELLREF_H
|
||||
#define OPENMW_ESM_CELLREF_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "defs.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class ESMWriter;
|
||||
|
||||
/* Cell reference. This represents ONE object (of many) inside the
|
||||
cell. The cell references are not loaded as part of the normal
|
||||
loading process, but are rather loaded later on demand when we are
|
||||
setting up a specific cell.
|
||||
*/
|
||||
|
||||
class CellRef
|
||||
{
|
||||
public:
|
||||
|
||||
int mRefnum; // Reference number
|
||||
std::string mRefID; // ID of object being referenced
|
||||
|
||||
float mScale; // Scale applied to mesh
|
||||
|
||||
// The NPC that owns this object (and will get angry if you steal
|
||||
// it)
|
||||
std::string mOwner;
|
||||
|
||||
// I have no idea, looks like a link to a global variable?
|
||||
std::string mGlob;
|
||||
|
||||
// ID of creature trapped in this soul gem (?)
|
||||
std::string mSoul;
|
||||
|
||||
// ?? CNAM has a faction name, might be for objects/beds etc
|
||||
// belonging to a faction.
|
||||
std::string mFaction;
|
||||
|
||||
// INDX might be PC faction rank required to use the item? Sometimes
|
||||
// is -1, which I assume means "any rank".
|
||||
int mFactIndex;
|
||||
|
||||
// For weapon or armor, this is the remaining item health.
|
||||
// For tools (lockpicks, probes, repair hammer) it is the remaining uses.
|
||||
int mCharge;
|
||||
|
||||
// Remaining enchantment charge
|
||||
float mEnchantmentCharge;
|
||||
|
||||
// This is 5 for Gold_005 references, 100 for Gold_100 and so on.
|
||||
int mGoldValue;
|
||||
|
||||
// For doors - true if this door teleports to somewhere else, false
|
||||
// if it should open through animation.
|
||||
bool mTeleport;
|
||||
|
||||
// Teleport location for the door, if this is a teleporting door.
|
||||
Position mDoorDest;
|
||||
|
||||
// Destination cell for doors (optional)
|
||||
std::string mDestCell;
|
||||
|
||||
// Lock level for doors and containers
|
||||
int mLockLevel;
|
||||
std::string mKey, mTrap; // Key and trap ID names, if any
|
||||
|
||||
// This corresponds to the "Reference Blocked" checkbox in the construction set,
|
||||
// which prevents editing that reference.
|
||||
// -1 is not blocked, otherwise it is blocked.
|
||||
signed char mReferenceBlocked;
|
||||
|
||||
// Track deleted references. 0 - not deleted, 1 - deleted, but respawns, 2 - deleted and does not respawn.
|
||||
int mDeleted;
|
||||
|
||||
// Occurs in Tribunal.esm, eg. in the cell "Mournhold, Plaza
|
||||
// Brindisi Dorom", where it has the value 100. Also only for
|
||||
// activators.
|
||||
int mFltv;
|
||||
int mNam0;
|
||||
|
||||
// Position and rotation of this object within the cell
|
||||
Position mPos;
|
||||
|
||||
void save(ESMWriter &esm);
|
||||
|
||||
void blank();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
87
components/esm/defs.hpp
Normal file
87
components/esm/defs.hpp
Normal file
|
@ -0,0 +1,87 @@
|
|||
#ifndef OPENMW_ESM_DEFS_H
|
||||
#define OPENMW_ESM_DEFS_H
|
||||
|
||||
#include <libs/platform/stdint.h>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
// Pixel color value. Standard four-byte rr,gg,bb,aa format.
|
||||
typedef int32_t Color;
|
||||
|
||||
enum Specialization
|
||||
{
|
||||
SPC_Combat = 0,
|
||||
SPC_Magic = 1,
|
||||
SPC_Stealth = 2
|
||||
};
|
||||
|
||||
enum RangeType
|
||||
{
|
||||
RT_Self = 0,
|
||||
RT_Touch = 1,
|
||||
RT_Target = 2
|
||||
};
|
||||
|
||||
#pragma pack(push)
|
||||
#pragma pack(1)
|
||||
|
||||
// Position and rotation
|
||||
struct Position
|
||||
{
|
||||
float pos[3];
|
||||
float rot[3];
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
enum RecNameInts
|
||||
{
|
||||
REC_ACTI = 0x49544341,
|
||||
REC_ALCH = 0x48434c41,
|
||||
REC_APPA = 0x41505041,
|
||||
REC_ARMO = 0x4f4d5241,
|
||||
REC_BODY = 0x59444f42,
|
||||
REC_BOOK = 0x4b4f4f42,
|
||||
REC_BSGN = 0x4e475342,
|
||||
REC_CELL = 0x4c4c4543,
|
||||
REC_CLAS = 0x53414c43,
|
||||
REC_CLOT = 0x544f4c43,
|
||||
REC_CNTC = 0x43544e43,
|
||||
REC_CONT = 0x544e4f43,
|
||||
REC_CREA = 0x41455243,
|
||||
REC_CREC = 0x43455243,
|
||||
REC_DIAL = 0x4c414944,
|
||||
REC_DOOR = 0x524f4f44,
|
||||
REC_ENCH = 0x48434e45,
|
||||
REC_FACT = 0x54434146,
|
||||
REC_GLOB = 0x424f4c47,
|
||||
REC_GMST = 0x54534d47,
|
||||
REC_INFO = 0x4f464e49,
|
||||
REC_INGR = 0x52474e49,
|
||||
REC_LAND = 0x444e414c,
|
||||
REC_LEVC = 0x4356454c,
|
||||
REC_LEVI = 0x4956454c,
|
||||
REC_LIGH = 0x4847494c,
|
||||
REC_LOCK = 0x4b434f4c,
|
||||
REC_LTEX = 0x5845544c,
|
||||
REC_MGEF = 0x4645474d,
|
||||
REC_MISC = 0x4353494d,
|
||||
REC_NPC_ = 0x5f43504e,
|
||||
REC_NPCC = 0x4343504e,
|
||||
REC_PGRD = 0x44524750,
|
||||
REC_PROB = 0x424f5250,
|
||||
REC_RACE = 0x45434152,
|
||||
REC_REGN = 0x4e474552,
|
||||
REC_REPA = 0x41504552,
|
||||
REC_SCPT = 0x54504353,
|
||||
REC_SKIL = 0x4c494b53,
|
||||
REC_SNDG = 0x47444e53,
|
||||
REC_SOUN = 0x4e554f53,
|
||||
REC_SPEL = 0x4c455053,
|
||||
REC_SSCR = 0x52435353,
|
||||
REC_STAT = 0x54415453,
|
||||
REC_WEAP = 0x50414557
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
24
components/esm/effectlist.cpp
Normal file
24
components/esm/effectlist.cpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
#include "effectlist.hpp"
|
||||
|
||||
#include "esmreader.hpp"
|
||||
#include "esmwriter.hpp"
|
||||
|
||||
namespace ESM {
|
||||
|
||||
void EffectList::load(ESMReader &esm)
|
||||
{
|
||||
ENAMstruct s;
|
||||
while (esm.isNextSub("ENAM")) {
|
||||
esm.getHT(s, 24);
|
||||
mList.push_back(s);
|
||||
}
|
||||
}
|
||||
|
||||
void EffectList::save(ESMWriter &esm)
|
||||
{
|
||||
for (std::vector<ENAMstruct>::iterator it = mList.begin(); it != mList.end(); ++it) {
|
||||
esm.writeHNT<ENAMstruct>("ENAM", *it, 24);
|
||||
}
|
||||
}
|
||||
|
||||
} // end namespace
|
43
components/esm/effectlist.hpp
Normal file
43
components/esm/effectlist.hpp
Normal file
|
@ -0,0 +1,43 @@
|
|||
#ifndef OPENMW_ESM_EFFECTLIST_H
|
||||
#define OPENMW_ESM_EFFECTLIST_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class ESMReader;
|
||||
class ESMWriter;
|
||||
|
||||
#pragma pack(push)
|
||||
#pragma pack(1)
|
||||
|
||||
/** Defines a spell effect. Shared between SPEL (Spells), ALCH
|
||||
(Potions) and ENCH (Item enchantments) records
|
||||
*/
|
||||
struct ENAMstruct
|
||||
{
|
||||
// Magical effect, hard-coded ID
|
||||
short mEffectID;
|
||||
|
||||
// Which skills/attributes are affected (for restore/drain spells
|
||||
// etc.)
|
||||
signed char mSkill, mAttribute; // -1 if N/A
|
||||
|
||||
// Other spell parameters
|
||||
int mRange; // 0 - self, 1 - touch, 2 - target (RangeType enum)
|
||||
int mArea, mDuration, mMagnMin, mMagnMax;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
struct EffectList
|
||||
{
|
||||
|
||||
std::vector<ENAMstruct> mList;
|
||||
|
||||
void load(ESMReader &esm);
|
||||
void save(ESMWriter &esm);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
94
components/esm/esmcommon.hpp
Normal file
94
components/esm/esmcommon.hpp
Normal file
|
@ -0,0 +1,94 @@
|
|||
#ifndef OPENMW_ESM_COMMON_H
|
||||
#define OPENMW_ESM_COMMON_H
|
||||
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
|
||||
#include <libs/platform/stdint.h>
|
||||
#include <libs/platform/string.h>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
enum Version
|
||||
{
|
||||
VER_12 = 0x3f99999a,
|
||||
VER_13 = 0x3fa66666
|
||||
};
|
||||
|
||||
/* A structure used for holding fixed-length strings. In the case of
|
||||
LEN=4, it can be more efficient to match the string as a 32 bit
|
||||
number, therefore the struct is implemented as a union with an int.
|
||||
*/
|
||||
template <int LEN>
|
||||
union NAME_T
|
||||
{
|
||||
char name[LEN];
|
||||
int32_t val;
|
||||
|
||||
bool operator==(const char *str) const
|
||||
{
|
||||
for(int i=0; i<LEN; i++)
|
||||
if(name[i] != str[i]) return false;
|
||||
else if(name[i] == 0) return true;
|
||||
return str[LEN] == 0;
|
||||
}
|
||||
bool operator!=(const char *str) const { return !((*this)==str); }
|
||||
|
||||
bool operator==(const std::string &str) const
|
||||
{
|
||||
return (*this) == str.c_str();
|
||||
}
|
||||
bool operator!=(const std::string &str) const { return !((*this)==str); }
|
||||
|
||||
bool operator==(int v) const { return v == val; }
|
||||
bool operator!=(int v) const { return v != val; }
|
||||
|
||||
std::string toString() const { return std::string(name, strnlen(name, LEN)); }
|
||||
|
||||
void assign (const std::string& value) { std::strncpy (name, value.c_str(), LEN); }
|
||||
};
|
||||
|
||||
typedef NAME_T<4> NAME;
|
||||
typedef NAME_T<32> NAME32;
|
||||
typedef NAME_T<64> NAME64;
|
||||
typedef NAME_T<256> NAME256;
|
||||
|
||||
#pragma pack(push)
|
||||
#pragma pack(1)
|
||||
// Data that is only present in save game files
|
||||
struct SaveData
|
||||
{
|
||||
float pos[6]; // Player position and rotation
|
||||
NAME64 cell; // Cell name
|
||||
float unk2; // Unknown value - possibly game time?
|
||||
NAME32 player; // Player name
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
/* This struct defines a file 'context' which can be saved and later
|
||||
restored by an ESMReader instance. It will save the position within
|
||||
a file, and when restored will let you read from that position as
|
||||
if you never left it.
|
||||
*/
|
||||
struct ESM_Context
|
||||
{
|
||||
std::string filename;
|
||||
uint32_t leftRec, leftSub;
|
||||
size_t leftFile;
|
||||
NAME recName, subName;
|
||||
// When working with multiple esX files, we will generate lists of all files that
|
||||
// actually contribute to a specific cell. Therefore, we need to store the index
|
||||
// of the file belonging to this contest. See CellStore::(list/load)refs for details.
|
||||
int index;
|
||||
|
||||
// True if subName has been read but not used.
|
||||
bool subCached;
|
||||
|
||||
// File position. Only used for stored contexts, not regularly
|
||||
// updated within the reader itself.
|
||||
size_t filePos;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
329
components/esm/esmreader.cpp
Normal file
329
components/esm/esmreader.cpp
Normal file
|
@ -0,0 +1,329 @@
|
|||
#include "esmreader.hpp"
|
||||
#include <stdexcept>
|
||||
|
||||
#include "../files/constrainedfiledatastream.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
using namespace Misc;
|
||||
|
||||
ESM_Context ESMReader::getContext()
|
||||
{
|
||||
// Update the file position before returning
|
||||
mCtx.filePos = mEsm->tell();
|
||||
return mCtx;
|
||||
}
|
||||
|
||||
ESMReader::ESMReader()
|
||||
: mBuffer(50*1024)
|
||||
, mRecordFlags(0)
|
||||
, mIdx(0)
|
||||
, mGlobalReaderList(NULL)
|
||||
, mEncoder(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
int ESMReader::getFormat() const
|
||||
{
|
||||
return mHeader.mFormat;
|
||||
}
|
||||
|
||||
void ESMReader::restoreContext(const ESM_Context &rc)
|
||||
{
|
||||
// Reopen the file if necessary
|
||||
if (mCtx.filename != rc.filename)
|
||||
openRaw(rc.filename);
|
||||
|
||||
// Copy the data
|
||||
mCtx = rc;
|
||||
|
||||
// Make sure we seek to the right place
|
||||
mEsm->seek(mCtx.filePos);
|
||||
}
|
||||
|
||||
void ESMReader::close()
|
||||
{
|
||||
mEsm.setNull();
|
||||
mCtx.filename.clear();
|
||||
mCtx.leftFile = 0;
|
||||
mCtx.leftRec = 0;
|
||||
mCtx.leftSub = 0;
|
||||
mCtx.subCached = false;
|
||||
mCtx.recName.val = 0;
|
||||
mCtx.subName.val = 0;
|
||||
}
|
||||
|
||||
void ESMReader::openRaw(Ogre::DataStreamPtr _esm, const std::string &name)
|
||||
{
|
||||
close();
|
||||
mEsm = _esm;
|
||||
mCtx.filename = name;
|
||||
mCtx.leftFile = mEsm->size();
|
||||
}
|
||||
|
||||
void ESMReader::open(Ogre::DataStreamPtr _esm, const std::string &name)
|
||||
{
|
||||
openRaw(_esm, name);
|
||||
|
||||
if (getRecName() != "TES3")
|
||||
fail("Not a valid Morrowind file");
|
||||
|
||||
getRecHeader();
|
||||
|
||||
mHeader.load (*this);
|
||||
}
|
||||
|
||||
void ESMReader::open(const std::string &file)
|
||||
{
|
||||
open (openConstrainedFileDataStream (file.c_str ()), file);
|
||||
}
|
||||
|
||||
void ESMReader::openRaw(const std::string &file)
|
||||
{
|
||||
openRaw (openConstrainedFileDataStream (file.c_str ()), file);
|
||||
}
|
||||
|
||||
int64_t ESMReader::getHNLong(const char *name)
|
||||
{
|
||||
int64_t val;
|
||||
getHNT(val, name);
|
||||
return val;
|
||||
}
|
||||
|
||||
std::string ESMReader::getHNOString(const char* name)
|
||||
{
|
||||
if (isNextSub(name))
|
||||
return getHString();
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string ESMReader::getHNString(const char* name)
|
||||
{
|
||||
getSubNameIs(name);
|
||||
return getHString();
|
||||
}
|
||||
|
||||
std::string ESMReader::getHString()
|
||||
{
|
||||
getSubHeader();
|
||||
|
||||
// Hack to make MultiMark.esp load. Zero-length strings do not
|
||||
// occur in any of the official mods, but MultiMark makes use of
|
||||
// them. For some reason, they break the rules, and contain a byte
|
||||
// (value 0) even if the header says there is no data. If
|
||||
// Morrowind accepts it, so should we.
|
||||
if (mCtx.leftSub == 0)
|
||||
{
|
||||
// Skip the following zero byte
|
||||
mCtx.leftRec--;
|
||||
char c;
|
||||
mEsm->read(&c, 1);
|
||||
return "";
|
||||
}
|
||||
|
||||
return getString(mCtx.leftSub);
|
||||
}
|
||||
|
||||
void ESMReader::getHExact(void*p, int size)
|
||||
{
|
||||
getSubHeader();
|
||||
if (size != static_cast<int> (mCtx.leftSub))
|
||||
fail("getHExact() size mismatch");
|
||||
getExact(p, size);
|
||||
}
|
||||
|
||||
// Read the given number of bytes from a named subrecord
|
||||
void ESMReader::getHNExact(void*p, int size, const char* name)
|
||||
{
|
||||
getSubNameIs(name);
|
||||
getHExact(p, size);
|
||||
}
|
||||
|
||||
// Get the next subrecord name and check if it matches the parameter
|
||||
void ESMReader::getSubNameIs(const char* name)
|
||||
{
|
||||
getSubName();
|
||||
if (mCtx.subName != name)
|
||||
fail(
|
||||
"Expected subrecord " + std::string(name) + " but got "
|
||||
+ mCtx.subName.toString());
|
||||
}
|
||||
|
||||
bool ESMReader::isNextSub(const char* name)
|
||||
{
|
||||
if (!mCtx.leftRec)
|
||||
return false;
|
||||
|
||||
getSubName();
|
||||
|
||||
// If the name didn't match, then mark the it as 'cached' so it's
|
||||
// available for the next call to getSubName.
|
||||
mCtx.subCached = (mCtx.subName != name);
|
||||
|
||||
// If subCached is false, then subName == name.
|
||||
return !mCtx.subCached;
|
||||
}
|
||||
|
||||
// Read subrecord name. This gets called a LOT, so I've optimized it
|
||||
// slightly.
|
||||
void ESMReader::getSubName()
|
||||
{
|
||||
// If the name has already been read, do nothing
|
||||
if (mCtx.subCached)
|
||||
{
|
||||
mCtx.subCached = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// reading the subrecord data anyway.
|
||||
mEsm->read(mCtx.subName.name, 4);
|
||||
mCtx.leftRec -= 4;
|
||||
}
|
||||
|
||||
bool ESMReader::isEmptyOrGetName()
|
||||
{
|
||||
if (mCtx.leftRec)
|
||||
{
|
||||
mEsm->read(mCtx.subName.name, 4);
|
||||
mCtx.leftRec -= 4;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ESMReader::skipHSub()
|
||||
{
|
||||
getSubHeader();
|
||||
skip(mCtx.leftSub);
|
||||
}
|
||||
|
||||
void ESMReader::skipHSubSize(int size)
|
||||
{
|
||||
skipHSub();
|
||||
if (static_cast<int> (mCtx.leftSub) != size)
|
||||
fail("skipHSubSize() mismatch");
|
||||
}
|
||||
|
||||
void ESMReader::getSubHeader()
|
||||
{
|
||||
if (mCtx.leftRec < 4)
|
||||
fail("End of record while reading sub-record header");
|
||||
|
||||
// Get subrecord size
|
||||
getT(mCtx.leftSub);
|
||||
|
||||
// Adjust number of record bytes left
|
||||
mCtx.leftRec -= mCtx.leftSub + 4;
|
||||
}
|
||||
|
||||
void ESMReader::getSubHeaderIs(int size)
|
||||
{
|
||||
getSubHeader();
|
||||
if (size != static_cast<int> (mCtx.leftSub))
|
||||
fail("getSubHeaderIs(): Sub header mismatch");
|
||||
}
|
||||
|
||||
NAME ESMReader::getRecName()
|
||||
{
|
||||
if (!hasMoreRecs())
|
||||
fail("No more records, getRecName() failed");
|
||||
getName(mCtx.recName);
|
||||
mCtx.leftFile -= 4;
|
||||
|
||||
// Make sure we don't carry over any old cached subrecord
|
||||
// names. This can happen in some cases when we skip parts of a
|
||||
// record.
|
||||
mCtx.subCached = false;
|
||||
|
||||
return mCtx.recName;
|
||||
}
|
||||
|
||||
void ESMReader::skipRecord()
|
||||
{
|
||||
skip(mCtx.leftRec);
|
||||
mCtx.leftRec = 0;
|
||||
}
|
||||
|
||||
void ESMReader::skipHRecord()
|
||||
{
|
||||
if (!mCtx.leftFile)
|
||||
return;
|
||||
getRecHeader();
|
||||
skipRecord();
|
||||
}
|
||||
|
||||
void ESMReader::getRecHeader(uint32_t &flags)
|
||||
{
|
||||
// General error checking
|
||||
if (mCtx.leftFile < 12)
|
||||
fail("End of file while reading record header");
|
||||
if (mCtx.leftRec)
|
||||
fail("Previous record contains unread bytes");
|
||||
|
||||
getUint(mCtx.leftRec);
|
||||
getUint(flags);// This header entry is always zero
|
||||
getUint(flags);
|
||||
mCtx.leftFile -= 12;
|
||||
|
||||
// Check that sizes add up
|
||||
if (mCtx.leftFile < mCtx.leftRec)
|
||||
fail("Record size is larger than rest of file");
|
||||
|
||||
// Adjust number of bytes mCtx.left in file
|
||||
mCtx.leftFile -= mCtx.leftRec;
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
*
|
||||
* Lowest level data reading and misc methods
|
||||
*
|
||||
*************************************************************************/
|
||||
|
||||
void ESMReader::getExact(void*x, int size)
|
||||
{
|
||||
int t = mEsm->read(x, size);
|
||||
if (t != size)
|
||||
fail("Read error");
|
||||
}
|
||||
|
||||
std::string ESMReader::getString(int size)
|
||||
{
|
||||
size_t s = size;
|
||||
if (mBuffer.size() <= s)
|
||||
// Add some extra padding to reduce the chance of having to resize
|
||||
// again later.
|
||||
mBuffer.resize(3*s);
|
||||
|
||||
// And make sure the string is zero terminated
|
||||
mBuffer[s] = 0;
|
||||
|
||||
// read ESM data
|
||||
char *ptr = &mBuffer[0];
|
||||
getExact(ptr, size);
|
||||
|
||||
// Convert to UTF8 and return
|
||||
return mEncoder->getUtf8(ptr, size);
|
||||
}
|
||||
|
||||
void ESMReader::fail(const std::string &msg)
|
||||
{
|
||||
using namespace std;
|
||||
|
||||
stringstream ss;
|
||||
|
||||
ss << "ESM Error: " << msg;
|
||||
ss << "\n File: " << mCtx.filename;
|
||||
ss << "\n Record: " << mCtx.recName.toString();
|
||||
ss << "\n Subrecord: " << mCtx.subName.toString();
|
||||
if (!mEsm.isNull())
|
||||
ss << "\n Offset: 0x" << hex << mEsm->tell();
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
|
||||
void ESMReader::setEncoder(ToUTF8::Utf8Encoder* encoder)
|
||||
{
|
||||
mEncoder = encoder;
|
||||
}
|
||||
|
||||
}
|
278
components/esm/esmreader.hpp
Normal file
278
components/esm/esmreader.hpp
Normal file
|
@ -0,0 +1,278 @@
|
|||
#ifndef OPENMW_ESM_READER_H
|
||||
#define OPENMW_ESM_READER_H
|
||||
|
||||
#include <libs/platform/stdint.h>
|
||||
#include <libs/platform/string.h>
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
|
||||
#include <OgreDataStream.h>
|
||||
|
||||
#include <components/misc/stringops.hpp>
|
||||
|
||||
#include <components/to_utf8/to_utf8.hpp>
|
||||
|
||||
#include "esmcommon.hpp"
|
||||
#include "loadtes3.hpp"
|
||||
|
||||
namespace ESM {
|
||||
|
||||
class ESMReader
|
||||
{
|
||||
public:
|
||||
|
||||
ESMReader();
|
||||
|
||||
/*************************************************************************
|
||||
*
|
||||
* Information retrieval
|
||||
*
|
||||
*************************************************************************/
|
||||
|
||||
int getVer() const { return mHeader.mData.version; }
|
||||
float getFVer() const { if(mHeader.mData.version == VER_12) return 1.2; else return 1.3; }
|
||||
const std::string getAuthor() const { return mHeader.mData.author.toString(); }
|
||||
const std::string getDesc() const { return mHeader.mData.desc.toString(); }
|
||||
const std::vector<Header::MasterData> &getMasters() const { return mHeader.mMaster; }
|
||||
int getFormat() const;
|
||||
const NAME &retSubName() const { return mCtx.subName; }
|
||||
uint32_t getSubSize() const { return mCtx.leftSub; }
|
||||
|
||||
/*************************************************************************
|
||||
*
|
||||
* Opening and closing
|
||||
*
|
||||
*************************************************************************/
|
||||
|
||||
/** Save the current file position and information in a ESM_Context
|
||||
struct
|
||||
*/
|
||||
ESM_Context getContext();
|
||||
|
||||
/** Restore a previously saved context */
|
||||
void restoreContext(const ESM_Context &rc);
|
||||
|
||||
/** Close the file, resets all information. After calling close()
|
||||
the structure may be reused to load a new file.
|
||||
*/
|
||||
void close();
|
||||
|
||||
/// Raw opening. Opens the file and sets everything up but doesn't
|
||||
/// parse the header.
|
||||
void openRaw(Ogre::DataStreamPtr _esm, const std::string &name);
|
||||
|
||||
/// Load ES file from a new stream, parses the header. Closes the
|
||||
/// currently open file first, if any.
|
||||
void open(Ogre::DataStreamPtr _esm, const std::string &name);
|
||||
|
||||
void open(const std::string &file);
|
||||
|
||||
void openRaw(const std::string &file);
|
||||
|
||||
/// Get the file size. Make sure that the file has been opened!
|
||||
size_t getFileSize() { return mEsm->size(); }
|
||||
/// Get the current position in the file. Make sure that the file has been opened!
|
||||
size_t getFileOffset() { return mEsm->tell(); }
|
||||
|
||||
// This is a quick hack for multiple esm/esp files. Each plugin introduces its own
|
||||
// terrain palette, but ESMReader does not pass a reference to the correct plugin
|
||||
// to the individual load() methods. This hack allows to pass this reference
|
||||
// indirectly to the load() method.
|
||||
int mIdx;
|
||||
void setIndex(const int index) {mIdx = index; mCtx.index = index;}
|
||||
const int getIndex() {return mIdx;}
|
||||
|
||||
void setGlobalReaderList(std::vector<ESMReader> *list) {mGlobalReaderList = list;}
|
||||
std::vector<ESMReader> *getGlobalReaderList() {return mGlobalReaderList;}
|
||||
|
||||
/*************************************************************************
|
||||
*
|
||||
* Medium-level reading shortcuts
|
||||
*
|
||||
*************************************************************************/
|
||||
|
||||
// Read data of a given type, stored in a subrecord of a given name
|
||||
template <typename X>
|
||||
void getHNT(X &x, const char* name)
|
||||
{
|
||||
getSubNameIs(name);
|
||||
getHT(x);
|
||||
}
|
||||
|
||||
// Optional version of getHNT
|
||||
template <typename X>
|
||||
void getHNOT(X &x, const char* name)
|
||||
{
|
||||
if(isNextSub(name))
|
||||
getHT(x);
|
||||
}
|
||||
|
||||
// Version with extra size checking, to make sure the compiler
|
||||
// doesn't mess up our struct padding.
|
||||
template <typename X>
|
||||
void getHNT(X &x, const char* name, int size)
|
||||
{
|
||||
assert(sizeof(X) == size);
|
||||
getSubNameIs(name);
|
||||
getHT(x);
|
||||
}
|
||||
|
||||
template <typename X>
|
||||
void getHNOT(X &x, const char* name, int size)
|
||||
{
|
||||
assert(sizeof(X) == size);
|
||||
if(isNextSub(name))
|
||||
getHT(x);
|
||||
}
|
||||
|
||||
int64_t getHNLong(const char *name);
|
||||
|
||||
// Get data of a given type/size, including subrecord header
|
||||
template <typename X>
|
||||
void getHT(X &x)
|
||||
{
|
||||
getSubHeader();
|
||||
if (mCtx.leftSub != sizeof(X))
|
||||
fail("getHT(): subrecord size mismatch");
|
||||
getT(x);
|
||||
}
|
||||
|
||||
// Version with extra size checking, to make sure the compiler
|
||||
// doesn't mess up our struct padding.
|
||||
template <typename X>
|
||||
void getHT(X &x, int size)
|
||||
{
|
||||
assert(sizeof(X) == size);
|
||||
getHT(x);
|
||||
}
|
||||
|
||||
// Read a string by the given name if it is the next record.
|
||||
std::string getHNOString(const char* name);
|
||||
|
||||
// Read a string with the given sub-record name
|
||||
std::string getHNString(const char* name);
|
||||
|
||||
// Read a string, including the sub-record header (but not the name)
|
||||
std::string getHString();
|
||||
|
||||
// Read the given number of bytes from a subrecord
|
||||
void getHExact(void*p, int size);
|
||||
|
||||
// Read the given number of bytes from a named subrecord
|
||||
void getHNExact(void*p, int size, const char* name);
|
||||
|
||||
/*************************************************************************
|
||||
*
|
||||
* Low level sub-record methods
|
||||
*
|
||||
*************************************************************************/
|
||||
|
||||
// Get the next subrecord name and check if it matches the parameter
|
||||
void getSubNameIs(const char* name);
|
||||
|
||||
/** Checks if the next sub record name matches the parameter. If it
|
||||
does, it is read into 'subName' just as if getSubName() was
|
||||
called. If not, the read name will still be available for future
|
||||
calls to getSubName(), isNextSub() and getSubNameIs().
|
||||
*/
|
||||
bool isNextSub(const char* name);
|
||||
|
||||
// Read subrecord name. This gets called a LOT, so I've optimized it
|
||||
// slightly.
|
||||
void getSubName();
|
||||
|
||||
// This is specially optimized for LoadINFO.
|
||||
bool isEmptyOrGetName();
|
||||
|
||||
// Skip current sub record, including header (but not including
|
||||
// name.)
|
||||
void skipHSub();
|
||||
|
||||
// Skip sub record and check its size
|
||||
void skipHSubSize(int size);
|
||||
|
||||
/* Sub-record header. This updates leftRec beyond the current
|
||||
sub-record as well. leftSub contains size of current sub-record.
|
||||
*/
|
||||
void getSubHeader();
|
||||
|
||||
/** Get sub header and check the size
|
||||
*/
|
||||
void getSubHeaderIs(int size);
|
||||
|
||||
/*************************************************************************
|
||||
*
|
||||
* Low level record methods
|
||||
*
|
||||
*************************************************************************/
|
||||
|
||||
// Get the next record name
|
||||
NAME getRecName();
|
||||
|
||||
// Skip the rest of this record. Assumes the name and header have
|
||||
// already been read
|
||||
void skipRecord();
|
||||
|
||||
// Skip an entire record, including the header (but not the name)
|
||||
void skipHRecord();
|
||||
|
||||
/* Read record header. This updatesleftFile BEYOND the data that
|
||||
follows the header, ie beyond the entire record. You should use
|
||||
leftRec to orient yourself inside the record itself.
|
||||
*/
|
||||
void getRecHeader() { getRecHeader(mRecordFlags); }
|
||||
void getRecHeader(uint32_t &flags);
|
||||
|
||||
bool hasMoreRecs() const { return mCtx.leftFile > 0; }
|
||||
bool hasMoreSubs() const { return mCtx.leftRec > 0; }
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
*
|
||||
* Lowest level data reading and misc methods
|
||||
*
|
||||
*************************************************************************/
|
||||
|
||||
template <typename X>
|
||||
void getT(X &x) { getExact(&x, sizeof(X)); }
|
||||
|
||||
void getExact(void*x, int size);
|
||||
void getName(NAME &name) { getT(name); }
|
||||
void getUint(uint32_t &u) { getT(u); }
|
||||
|
||||
// Read the next 'size' bytes and return them as a string. Converts
|
||||
// them from native encoding to UTF8 in the process.
|
||||
std::string getString(int size);
|
||||
|
||||
void skip(int bytes) { mEsm->seek(mEsm->tell()+bytes); }
|
||||
uint64_t getOffset() { return mEsm->tell(); }
|
||||
|
||||
/// Used for error handling
|
||||
void fail(const std::string &msg);
|
||||
|
||||
/// Sets font encoder for ESM strings
|
||||
void setEncoder(ToUTF8::Utf8Encoder* encoder);
|
||||
|
||||
/// Get record flags of last record
|
||||
unsigned int getRecordFlags() { return mRecordFlags; }
|
||||
|
||||
private:
|
||||
Ogre::DataStreamPtr mEsm;
|
||||
|
||||
ESM_Context mCtx;
|
||||
|
||||
unsigned int mRecordFlags;
|
||||
|
||||
// Special file signifier (see SpecialFile enum above)
|
||||
|
||||
// Buffer for ESM strings
|
||||
std::vector<char> mBuffer;
|
||||
|
||||
Header mHeader;
|
||||
|
||||
std::vector<ESMReader> *mGlobalReaderList;
|
||||
ToUTF8::Utf8Encoder* mEncoder;
|
||||
};
|
||||
}
|
||||
#endif
|
186
components/esm/esmwriter.cpp
Normal file
186
components/esm/esmwriter.cpp
Normal file
|
@ -0,0 +1,186 @@
|
|||
#include "esmwriter.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
bool count = true;
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
int ESMWriter::getVersion()
|
||||
{
|
||||
return mHeader.mData.version;
|
||||
}
|
||||
|
||||
void ESMWriter::setVersion(int ver)
|
||||
{
|
||||
mHeader.mData.version = ver;
|
||||
}
|
||||
|
||||
void ESMWriter::setAuthor(const std::string& auth)
|
||||
{
|
||||
mHeader.mData.author.assign (auth);
|
||||
}
|
||||
|
||||
void ESMWriter::setDescription(const std::string& desc)
|
||||
{
|
||||
mHeader.mData.desc.assign (desc);
|
||||
}
|
||||
|
||||
void ESMWriter::setRecordCount (int count)
|
||||
{
|
||||
mHeader.mData.records = count;
|
||||
}
|
||||
|
||||
void ESMWriter::setFormat (int format)
|
||||
{
|
||||
mHeader.mFormat = format;
|
||||
}
|
||||
|
||||
void ESMWriter::addMaster(const std::string& name, uint64_t size)
|
||||
{
|
||||
Header::MasterData d;
|
||||
d.name = name;
|
||||
d.size = size;
|
||||
mHeader.mMaster.push_back(d);
|
||||
}
|
||||
|
||||
void ESMWriter::save(const std::string& file)
|
||||
{
|
||||
std::ofstream fs(file.c_str(), std::ios_base::out | std::ios_base::trunc);
|
||||
save(fs);
|
||||
}
|
||||
|
||||
void ESMWriter::save(std::ostream& file)
|
||||
{
|
||||
m_recordCount = 0;
|
||||
m_stream = &file;
|
||||
|
||||
startRecord("TES3", 0);
|
||||
|
||||
mHeader.save (*this);
|
||||
|
||||
endRecord("TES3");
|
||||
}
|
||||
|
||||
void ESMWriter::close()
|
||||
{
|
||||
m_stream->flush();
|
||||
|
||||
if (!m_records.empty())
|
||||
throw "Unclosed record remaining";
|
||||
}
|
||||
|
||||
void ESMWriter::startRecord(const std::string& name, uint32_t flags)
|
||||
{
|
||||
m_recordCount++;
|
||||
|
||||
writeName(name);
|
||||
RecordData rec;
|
||||
rec.name = name;
|
||||
rec.position = m_stream->tellp();
|
||||
rec.size = 0;
|
||||
writeT<int>(0); // Size goes here
|
||||
writeT<int>(0); // Unused header?
|
||||
writeT(flags);
|
||||
m_records.push_back(rec);
|
||||
|
||||
assert(m_records.back().size == 0);
|
||||
}
|
||||
|
||||
void ESMWriter::startSubRecord(const std::string& name)
|
||||
{
|
||||
writeName(name);
|
||||
RecordData rec;
|
||||
rec.name = name;
|
||||
rec.position = m_stream->tellp();
|
||||
rec.size = 0;
|
||||
writeT<int>(0); // Size goes here
|
||||
m_records.push_back(rec);
|
||||
|
||||
assert(m_records.back().size == 0);
|
||||
}
|
||||
|
||||
void ESMWriter::endRecord(const std::string& name)
|
||||
{
|
||||
RecordData rec = m_records.back();
|
||||
assert(rec.name == name);
|
||||
m_records.pop_back();
|
||||
|
||||
m_stream->seekp(rec.position);
|
||||
|
||||
count = false;
|
||||
write((char*)&rec.size, sizeof(int));
|
||||
count = true;
|
||||
|
||||
m_stream->seekp(0, std::ios::end);
|
||||
|
||||
}
|
||||
|
||||
void ESMWriter::writeHNString(const std::string& name, const std::string& data)
|
||||
{
|
||||
startSubRecord(name);
|
||||
writeHString(data);
|
||||
endRecord(name);
|
||||
}
|
||||
|
||||
void ESMWriter::writeHNString(const std::string& name, const std::string& data, size_t size)
|
||||
{
|
||||
assert(data.size() <= size);
|
||||
startSubRecord(name);
|
||||
writeHString(data);
|
||||
|
||||
if (data.size() < size)
|
||||
{
|
||||
for (size_t i = data.size(); i < size; ++i)
|
||||
write("\0",1);
|
||||
}
|
||||
|
||||
endRecord(name);
|
||||
}
|
||||
|
||||
void ESMWriter::writeHString(const std::string& data)
|
||||
{
|
||||
if (data.size() == 0)
|
||||
write("\0", 1);
|
||||
else
|
||||
{
|
||||
// Convert to UTF8 and return
|
||||
std::string ascii = m_encoder->getLegacyEnc(data);
|
||||
|
||||
write(ascii.c_str(), ascii.size());
|
||||
}
|
||||
}
|
||||
|
||||
void ESMWriter::writeHCString(const std::string& data)
|
||||
{
|
||||
writeHString(data);
|
||||
if (data.size() > 0 && data[data.size()-1] != '\0')
|
||||
write("\0", 1);
|
||||
}
|
||||
|
||||
void ESMWriter::writeName(const std::string& name)
|
||||
{
|
||||
assert((name.size() == 4 && name[3] != '\0'));
|
||||
write(name.c_str(), name.size());
|
||||
}
|
||||
|
||||
void ESMWriter::write(const char* data, size_t size)
|
||||
{
|
||||
if (count && !m_records.empty())
|
||||
{
|
||||
for (std::list<RecordData>::iterator it = m_records.begin(); it != m_records.end(); ++it)
|
||||
it->size += size;
|
||||
}
|
||||
|
||||
m_stream->write(data, size);
|
||||
}
|
||||
|
||||
void ESMWriter::setEncoder(ToUTF8::Utf8Encoder* encoder)
|
||||
{
|
||||
m_encoder = encoder;
|
||||
}
|
||||
|
||||
}
|
104
components/esm/esmwriter.hpp
Normal file
104
components/esm/esmwriter.hpp
Normal file
|
@ -0,0 +1,104 @@
|
|||
#ifndef OPENMW_ESM_WRITER_H
|
||||
#define OPENMW_ESM_WRITER_H
|
||||
|
||||
#include <iosfwd>
|
||||
#include <list>
|
||||
|
||||
#include <components/to_utf8/to_utf8.hpp>
|
||||
|
||||
#include "esmcommon.hpp"
|
||||
#include "loadtes3.hpp"
|
||||
|
||||
namespace ESM {
|
||||
|
||||
class ESMWriter
|
||||
{
|
||||
struct RecordData
|
||||
{
|
||||
std::string name;
|
||||
std::streampos position;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
public:
|
||||
int getVersion();
|
||||
void setVersion(int ver);
|
||||
void setEncoder(ToUTF8::Utf8Encoder *encoding); // Write strings as UTF-8?
|
||||
void setAuthor(const std::string& author);
|
||||
void setDescription(const std::string& desc);
|
||||
void setRecordCount (int count);
|
||||
void setFormat (int format);
|
||||
|
||||
void addMaster(const std::string& name, uint64_t size);
|
||||
|
||||
void save(const std::string& file);
|
||||
void save(std::ostream& file);
|
||||
void close();
|
||||
|
||||
void writeHNString(const std::string& name, const std::string& data);
|
||||
void writeHNString(const std::string& name, const std::string& data, size_t size);
|
||||
void writeHNCString(const std::string& name, const std::string& data)
|
||||
{
|
||||
startSubRecord(name);
|
||||
writeHCString(data);
|
||||
endRecord(name);
|
||||
}
|
||||
void writeHNOString(const std::string& name, const std::string& data)
|
||||
{
|
||||
if (!data.empty())
|
||||
writeHNString(name, data);
|
||||
}
|
||||
void writeHNOCString(const std::string& name, const std::string& data)
|
||||
{
|
||||
if (!data.empty())
|
||||
writeHNCString(name, data);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void writeHNT(const std::string& name, const T& data)
|
||||
{
|
||||
startSubRecord(name);
|
||||
writeT(data);
|
||||
endRecord(name);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void writeHNT(const std::string& name, const T& data, int size)
|
||||
{
|
||||
startSubRecord(name);
|
||||
writeT(data, size);
|
||||
endRecord(name);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void writeT(const T& data)
|
||||
{
|
||||
write((char*)&data, sizeof(T));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void writeT(const T& data, size_t size)
|
||||
{
|
||||
write((char*)&data, size);
|
||||
}
|
||||
|
||||
void startRecord(const std::string& name, uint32_t flags);
|
||||
void startSubRecord(const std::string& name);
|
||||
void endRecord(const std::string& name);
|
||||
void writeHString(const std::string& data);
|
||||
void writeHCString(const std::string& data);
|
||||
void writeName(const std::string& data);
|
||||
void write(const char* data, size_t size);
|
||||
|
||||
private:
|
||||
std::list<RecordData> m_records;
|
||||
std::ostream* m_stream;
|
||||
std::streampos m_headerPos;
|
||||
ToUTF8::Utf8Encoder* m_encoder;
|
||||
int m_recordCount;
|
||||
|
||||
Header mHeader;
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
23
components/esm/filter.cpp
Normal file
23
components/esm/filter.cpp
Normal file
|
@ -0,0 +1,23 @@
|
|||
|
||||
#include "filter.hpp"
|
||||
|
||||
#include "esmreader.hpp"
|
||||
#include "esmwriter.hpp"
|
||||
|
||||
void ESM::Filter::load (ESMReader& esm)
|
||||
{
|
||||
mFilter = esm.getHNString ("FILT");
|
||||
mDescription = esm.getHNString ("DESC");
|
||||
}
|
||||
|
||||
void ESM::Filter::save (ESMWriter& esm)
|
||||
{
|
||||
esm.writeHNCString ("FILT", mFilter);
|
||||
esm.writeHNCString ("DESC", mDescription);
|
||||
}
|
||||
|
||||
void ESM::Filter::blank()
|
||||
{
|
||||
mFilter.clear();
|
||||
mDescription.clear();
|
||||
}
|
27
components/esm/filter.hpp
Normal file
27
components/esm/filter.hpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
#ifndef COMPONENTS_ESM_FILTER_H
|
||||
#define COMPONENTS_ESM_FILTER_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class ESMReader;
|
||||
class ESMWriter;
|
||||
|
||||
struct Filter
|
||||
{
|
||||
std::string mId;
|
||||
|
||||
std::string mDescription;
|
||||
|
||||
std::string mFilter;
|
||||
|
||||
void load (ESMReader& esm);
|
||||
void save (ESMWriter& esm);
|
||||
|
||||
void blank();
|
||||
///< Set record to default state (does not touch the ID).
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
27
components/esm/loadacti.cpp
Normal file
27
components/esm/loadacti.cpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
#include "loadacti.hpp"
|
||||
|
||||
#include "esmreader.hpp"
|
||||
#include "esmwriter.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
void Activator::load(ESMReader &esm)
|
||||
{
|
||||
mModel = esm.getHNString("MODL");
|
||||
mName = esm.getHNString("FNAM");
|
||||
mScript = esm.getHNOString("SCRI");
|
||||
}
|
||||
void Activator::save(ESMWriter &esm)
|
||||
{
|
||||
esm.writeHNCString("MODL", mModel);
|
||||
esm.writeHNCString("FNAM", mName);
|
||||
esm.writeHNOCString("SCRI", mScript);
|
||||
}
|
||||
|
||||
void Activator::blank()
|
||||
{
|
||||
mName.clear();
|
||||
mScript.clear();
|
||||
mModel.clear();
|
||||
}
|
||||
}
|
24
components/esm/loadacti.hpp
Normal file
24
components/esm/loadacti.hpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
#ifndef OPENMW_ESM_ACTI_H
|
||||
#define OPENMW_ESM_ACTI_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
class ESMReader;
|
||||
class ESMWriter;
|
||||
|
||||
struct Activator
|
||||
{
|
||||
std::string mId, mName, mScript, mModel;
|
||||
|
||||
void load(ESMReader &esm);
|
||||
void save(ESMWriter &esm);
|
||||
|
||||
void blank();
|
||||
///< Set record to default state (does not touch the ID).
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
38
components/esm/loadalch.cpp
Normal file
38
components/esm/loadalch.cpp
Normal file
|
@ -0,0 +1,38 @@
|
|||
#include "loadalch.hpp"
|
||||
|
||||
#include "esmreader.hpp"
|
||||
#include "esmwriter.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
void Potion::load(ESMReader &esm)
|
||||
{
|
||||
mModel = esm.getHNString("MODL");
|
||||
mIcon = esm.getHNOString("TEXT"); // not ITEX here for some reason
|
||||
mScript = esm.getHNOString("SCRI");
|
||||
mName = esm.getHNOString("FNAM");
|
||||
esm.getHNT(mData, "ALDT", 12);
|
||||
mEffects.load(esm);
|
||||
}
|
||||
void Potion::save(ESMWriter &esm)
|
||||
{
|
||||
esm.writeHNCString("MODL", mModel);
|
||||
esm.writeHNOCString("TEXT", mIcon);
|
||||
esm.writeHNOCString("SCRI", mScript);
|
||||
esm.writeHNOCString("FNAM", mName);
|
||||
esm.writeHNT("ALDT", mData, 12);
|
||||
mEffects.save(esm);
|
||||
}
|
||||
|
||||
void Potion::blank()
|
||||
{
|
||||
mData.mWeight = 0;
|
||||
mData.mValue = 0;
|
||||
mData.mAutoCalc = 0;
|
||||
mName.clear();
|
||||
mModel.clear();
|
||||
mIcon.clear();
|
||||
mScript.clear();
|
||||
mEffects.mList.clear();
|
||||
}
|
||||
}
|
39
components/esm/loadalch.hpp
Normal file
39
components/esm/loadalch.hpp
Normal file
|
@ -0,0 +1,39 @@
|
|||
#ifndef OPENMW_ESM_ALCH_H
|
||||
#define OPENMW_ESM_ALCH_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "effectlist.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
class ESMReader;
|
||||
class ESMWriter;
|
||||
|
||||
/*
|
||||
* Alchemy item (potions)
|
||||
*/
|
||||
|
||||
struct Potion
|
||||
{
|
||||
struct ALDTstruct
|
||||
{
|
||||
float mWeight;
|
||||
int mValue;
|
||||
int mAutoCalc;
|
||||
};
|
||||
ALDTstruct mData;
|
||||
|
||||
std::string mId, mName, mModel, mIcon, mScript;
|
||||
EffectList mEffects;
|
||||
|
||||
void load(ESMReader &esm);
|
||||
void save(ESMWriter &esm);
|
||||
|
||||
void blank();
|
||||
///< Set record to default state (does not touch the ID).
|
||||
|
||||
};
|
||||
}
|
||||
#endif
|
51
components/esm/loadappa.cpp
Normal file
51
components/esm/loadappa.cpp
Normal file
|
@ -0,0 +1,51 @@
|
|||
#include "loadappa.hpp"
|
||||
|
||||
#include "esmreader.hpp"
|
||||
#include "esmwriter.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
void Apparatus::load(ESMReader &esm)
|
||||
{
|
||||
// we will not treat duplicated subrecords as errors here
|
||||
while (esm.hasMoreSubs())
|
||||
{
|
||||
esm.getSubName();
|
||||
NAME subName = esm.retSubName();
|
||||
|
||||
if (subName == "MODL")
|
||||
mModel = esm.getHString();
|
||||
else if (subName == "FNAM")
|
||||
mName = esm.getHString();
|
||||
else if (subName == "AADT")
|
||||
esm.getHT(mData);
|
||||
else if (subName == "SCRI")
|
||||
mScript = esm.getHString();
|
||||
else if (subName == "ITEX")
|
||||
mIcon = esm.getHString();
|
||||
else
|
||||
esm.fail("wrong subrecord type " + subName.toString() + " for APPA record");
|
||||
}
|
||||
}
|
||||
|
||||
void Apparatus::save(ESMWriter &esm)
|
||||
{
|
||||
esm.writeHNCString("MODL", mModel);
|
||||
esm.writeHNCString("FNAM", mName);
|
||||
esm.writeHNT("AADT", mData, 16);
|
||||
esm.writeHNOCString("SCRI", mScript);
|
||||
esm.writeHNCString("ITEX", mIcon);
|
||||
}
|
||||
|
||||
void Apparatus::blank()
|
||||
{
|
||||
mData.mType = 0;
|
||||
mData.mQuality = 0;
|
||||
mData.mWeight = 0;
|
||||
mData.mValue = 0;
|
||||
mModel.clear();
|
||||
mIcon.clear();
|
||||
mScript.clear();
|
||||
mName.clear();
|
||||
}
|
||||
}
|
44
components/esm/loadappa.hpp
Normal file
44
components/esm/loadappa.hpp
Normal file
|
@ -0,0 +1,44 @@
|
|||
#ifndef OPENMW_ESM_APPA_H
|
||||
#define OPENMW_ESM_APPA_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
class ESMReader;
|
||||
class ESMWriter;
|
||||
|
||||
/*
|
||||
* Alchemist apparatus
|
||||
*/
|
||||
|
||||
struct Apparatus
|
||||
{
|
||||
enum AppaType
|
||||
{
|
||||
MortarPestle = 0,
|
||||
Albemic = 1,
|
||||
Calcinator = 2,
|
||||
Retort = 3
|
||||
};
|
||||
|
||||
struct AADTstruct
|
||||
{
|
||||
int mType;
|
||||
float mQuality;
|
||||
float mWeight;
|
||||
int mValue;
|
||||
};
|
||||
|
||||
AADTstruct mData;
|
||||
std::string mId, mModel, mIcon, mScript, mName;
|
||||
|
||||
void load(ESMReader &esm);
|
||||
void save(ESMWriter &esm);
|
||||
|
||||
void blank();
|
||||
///< Set record to default state (does not touch the ID).
|
||||
};
|
||||
}
|
||||
#endif
|
68
components/esm/loadarmo.cpp
Normal file
68
components/esm/loadarmo.cpp
Normal file
|
@ -0,0 +1,68 @@
|
|||
#include "loadarmo.hpp"
|
||||
|
||||
#include "esmreader.hpp"
|
||||
#include "esmwriter.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
void PartReferenceList::load(ESMReader &esm)
|
||||
{
|
||||
while (esm.isNextSub("INDX"))
|
||||
{
|
||||
PartReference pr;
|
||||
esm.getHT(pr.mPart); // The INDX byte
|
||||
pr.mMale = esm.getHNOString("BNAM");
|
||||
pr.mFemale = esm.getHNOString("CNAM");
|
||||
mParts.push_back(pr);
|
||||
}
|
||||
}
|
||||
|
||||
void PartReferenceList::save(ESMWriter &esm)
|
||||
{
|
||||
for (std::vector<PartReference>::iterator it = mParts.begin(); it != mParts.end(); ++it)
|
||||
{
|
||||
esm.writeHNT("INDX", it->mPart);
|
||||
esm.writeHNOString("BNAM", it->mMale);
|
||||
esm.writeHNOString("CNAM", it->mFemale);
|
||||
}
|
||||
}
|
||||
|
||||
void Armor::load(ESMReader &esm)
|
||||
{
|
||||
mModel = esm.getHNString("MODL");
|
||||
mName = esm.getHNString("FNAM");
|
||||
mScript = esm.getHNOString("SCRI");
|
||||
esm.getHNT(mData, "AODT", 24);
|
||||
mIcon = esm.getHNOString("ITEX");
|
||||
mParts.load(esm);
|
||||
mEnchant = esm.getHNOString("ENAM");
|
||||
}
|
||||
|
||||
void Armor::save(ESMWriter &esm)
|
||||
{
|
||||
esm.writeHNCString("MODL", mModel);
|
||||
esm.writeHNCString("FNAM", mName);
|
||||
esm.writeHNOCString("SCRI", mScript);
|
||||
esm.writeHNT("AODT", mData, 24);
|
||||
esm.writeHNOCString("ITEX", mIcon);
|
||||
mParts.save(esm);
|
||||
esm.writeHNOCString("ENAM", mEnchant);
|
||||
}
|
||||
|
||||
void Armor::blank()
|
||||
{
|
||||
mData.mType = 0;
|
||||
mData.mWeight = 0;
|
||||
mData.mValue = 0;
|
||||
mData.mHealth = 0;
|
||||
mData.mEnchant = 0;
|
||||
mData.mArmor = 0;
|
||||
mParts.mParts.clear();
|
||||
mName.clear();
|
||||
mModel.clear();
|
||||
mIcon.clear();
|
||||
mScript.clear();
|
||||
mEnchant.clear();
|
||||
}
|
||||
}
|
98
components/esm/loadarmo.hpp
Normal file
98
components/esm/loadarmo.hpp
Normal file
|
@ -0,0 +1,98 @@
|
|||
#ifndef OPENMW_ESM_ARMO_H
|
||||
#define OPENMW_ESM_ARMO_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
class ESMReader;
|
||||
class ESMWriter;
|
||||
|
||||
enum PartReferenceType
|
||||
{
|
||||
PRT_Head = 0,
|
||||
PRT_Hair = 1,
|
||||
PRT_Neck = 2,
|
||||
PRT_Cuirass = 3,
|
||||
PRT_Groin = 4,
|
||||
PRT_Skirt = 5,
|
||||
PRT_RHand = 6,
|
||||
PRT_LHand = 7,
|
||||
PRT_RWrist = 8,
|
||||
PRT_LWrist = 9,
|
||||
PRT_Shield = 10,
|
||||
PRT_RForearm = 11,
|
||||
PRT_LForearm = 12,
|
||||
PRT_RUpperarm = 13,
|
||||
PRT_LUpperarm = 14,
|
||||
PRT_RFoot = 15,
|
||||
PRT_LFoot = 16,
|
||||
PRT_RAnkle = 17,
|
||||
PRT_LAnkle = 18,
|
||||
PRT_RKnee = 19,
|
||||
PRT_LKnee = 20,
|
||||
PRT_RLeg = 21,
|
||||
PRT_LLeg = 22,
|
||||
PRT_RPauldron = 23,
|
||||
PRT_LPauldron = 24,
|
||||
PRT_Weapon = 25,
|
||||
PRT_Tail = 26,
|
||||
|
||||
PRT_Count = 27
|
||||
};
|
||||
|
||||
// Reference to body parts
|
||||
struct PartReference
|
||||
{
|
||||
char mPart;
|
||||
std::string mMale, mFemale;
|
||||
};
|
||||
|
||||
// A list of references to body parts
|
||||
struct PartReferenceList
|
||||
{
|
||||
std::vector<PartReference> mParts;
|
||||
|
||||
void load(ESMReader &esm);
|
||||
void save(ESMWriter &esm);
|
||||
};
|
||||
|
||||
struct Armor
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
Helmet = 0,
|
||||
Cuirass = 1,
|
||||
LPauldron = 2,
|
||||
RPauldron = 3,
|
||||
Greaves = 4,
|
||||
Boots = 5,
|
||||
LGauntlet = 6,
|
||||
RGauntlet = 7,
|
||||
Shield = 8,
|
||||
LBracer = 9,
|
||||
RBracer = 10
|
||||
};
|
||||
|
||||
struct AODTstruct
|
||||
{
|
||||
int mType;
|
||||
float mWeight;
|
||||
int mValue, mHealth, mEnchant, mArmor;
|
||||
};
|
||||
|
||||
AODTstruct mData;
|
||||
PartReferenceList mParts;
|
||||
|
||||
std::string mId, mName, mModel, mIcon, mScript, mEnchant;
|
||||
|
||||
void load(ESMReader &esm);
|
||||
void save(ESMWriter &esm);
|
||||
|
||||
void blank();
|
||||
///< Set record to default state (does not touch the ID).
|
||||
};
|
||||
}
|
||||
#endif
|
22
components/esm/loadbody.cpp
Normal file
22
components/esm/loadbody.cpp
Normal file
|
@ -0,0 +1,22 @@
|
|||
#include "loadbody.hpp"
|
||||
|
||||
#include "esmreader.hpp"
|
||||
#include "esmwriter.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
void BodyPart::load(ESMReader &esm)
|
||||
{
|
||||
mModel = esm.getHNString("MODL");
|
||||
mRace = esm.getHNString("FNAM");
|
||||
esm.getHNT(mData, "BYDT", 4);
|
||||
}
|
||||
void BodyPart::save(ESMWriter &esm)
|
||||
{
|
||||
esm.writeHNCString("MODL", mModel);
|
||||
esm.writeHNCString("FNAM", mRace);
|
||||
esm.writeHNT("BYDT", mData, 4);
|
||||
}
|
||||
|
||||
}
|
63
components/esm/loadbody.hpp
Normal file
63
components/esm/loadbody.hpp
Normal file
|
@ -0,0 +1,63 @@
|
|||
#ifndef OPENMW_ESM_BODY_H
|
||||
#define OPENMW_ESM_BODY_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
class ESMReader;
|
||||
class ESMWriter;
|
||||
|
||||
struct BodyPart
|
||||
{
|
||||
enum MeshPart
|
||||
{
|
||||
MP_Head = 0,
|
||||
MP_Hair = 1,
|
||||
MP_Neck = 2,
|
||||
MP_Chest = 3,
|
||||
MP_Groin = 4,
|
||||
MP_Hand = 5,
|
||||
MP_Wrist = 6,
|
||||
MP_Forearm = 7,
|
||||
MP_Upperarm = 8,
|
||||
MP_Foot = 9,
|
||||
MP_Ankle = 10,
|
||||
MP_Knee = 11,
|
||||
MP_Upperleg = 12,
|
||||
MP_Clavicle = 13,
|
||||
MP_Tail = 14,
|
||||
|
||||
MP_Count = 15
|
||||
};
|
||||
|
||||
enum Flags
|
||||
{
|
||||
BPF_Female = 1,
|
||||
BPF_NotPlayable = 2
|
||||
};
|
||||
|
||||
enum MeshType
|
||||
{
|
||||
MT_Skin = 0,
|
||||
MT_Clothing = 1,
|
||||
MT_Armor = 2
|
||||
};
|
||||
|
||||
struct BYDTstruct
|
||||
{
|
||||
char mPart;
|
||||
char mVampire;
|
||||
char mFlags;
|
||||
char mType;
|
||||
};
|
||||
|
||||
BYDTstruct mData;
|
||||
std::string mId, mModel, mRace;
|
||||
|
||||
void load(ESMReader &esm);
|
||||
void save(ESMWriter &esm);
|
||||
};
|
||||
}
|
||||
#endif
|
44
components/esm/loadbook.cpp
Normal file
44
components/esm/loadbook.cpp
Normal file
|
@ -0,0 +1,44 @@
|
|||
#include "loadbook.hpp"
|
||||
|
||||
#include "esmreader.hpp"
|
||||
#include "esmwriter.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
void Book::load(ESMReader &esm)
|
||||
{
|
||||
mModel = esm.getHNString("MODL");
|
||||
mName = esm.getHNOString("FNAM");
|
||||
esm.getHNT(mData, "BKDT", 20);
|
||||
mScript = esm.getHNOString("SCRI");
|
||||
mIcon = esm.getHNOString("ITEX");
|
||||
mText = esm.getHNOString("TEXT");
|
||||
mEnchant = esm.getHNOString("ENAM");
|
||||
}
|
||||
void Book::save(ESMWriter &esm)
|
||||
{
|
||||
esm.writeHNCString("MODL", mModel);
|
||||
esm.writeHNOCString("FNAM", mName);
|
||||
esm.writeHNT("BKDT", mData, 20);
|
||||
esm.writeHNOCString("SCRI", mScript);
|
||||
esm.writeHNOCString("ITEX", mIcon);
|
||||
esm.writeHNOString("TEXT", mText);
|
||||
esm.writeHNOCString("ENAM", mEnchant);
|
||||
}
|
||||
|
||||
void Book::blank()
|
||||
{
|
||||
mData.mWeight = 0;
|
||||
mData.mValue = 0;
|
||||
mData.mIsScroll = 0;
|
||||
mData.mSkillID = 0;
|
||||
mData.mEnchant = 0;
|
||||
mName.clear();
|
||||
mModel.clear();
|
||||
mIcon.clear();
|
||||
mScript.clear();
|
||||
mEnchant.clear();
|
||||
mText.clear();
|
||||
}
|
||||
}
|
34
components/esm/loadbook.hpp
Normal file
34
components/esm/loadbook.hpp
Normal file
|
@ -0,0 +1,34 @@
|
|||
#ifndef OPENMW_ESM_BOOK_H
|
||||
#define OPENMW_ESM_BOOK_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
/*
|
||||
* Books, magic scrolls, notes and so on
|
||||
*/
|
||||
|
||||
class ESMReader;
|
||||
class ESMWriter;
|
||||
|
||||
struct Book
|
||||
{
|
||||
struct BKDTstruct
|
||||
{
|
||||
float mWeight;
|
||||
int mValue, mIsScroll, mSkillID, mEnchant;
|
||||
};
|
||||
|
||||
BKDTstruct mData;
|
||||
std::string mName, mModel, mIcon, mScript, mEnchant, mText;
|
||||
std::string mId;
|
||||
|
||||
void load(ESMReader &esm);
|
||||
void save(ESMWriter &esm);
|
||||
|
||||
void blank();
|
||||
///< Set record to default state (does not touch the ID).
|
||||
};
|
||||
}
|
||||
#endif
|
35
components/esm/loadbsgn.cpp
Normal file
35
components/esm/loadbsgn.cpp
Normal file
|
@ -0,0 +1,35 @@
|
|||
#include "loadbsgn.hpp"
|
||||
|
||||
#include "esmreader.hpp"
|
||||
#include "esmwriter.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
void BirthSign::load(ESMReader &esm)
|
||||
{
|
||||
mName = esm.getHNString("FNAM");
|
||||
mTexture = esm.getHNOString("TNAM");
|
||||
mDescription = esm.getHNOString("DESC");
|
||||
|
||||
mPowers.load(esm);
|
||||
}
|
||||
|
||||
void BirthSign::save(ESMWriter &esm)
|
||||
{
|
||||
esm.writeHNCString("FNAM", mName);
|
||||
esm.writeHNOCString("TNAM", mTexture);
|
||||
esm.writeHNOCString("DESC", mDescription);
|
||||
|
||||
mPowers.save(esm);
|
||||
}
|
||||
|
||||
void BirthSign::blank()
|
||||
{
|
||||
mName.clear();
|
||||
mDescription.clear();
|
||||
mTexture.clear();
|
||||
mPowers.mList.clear();
|
||||
}
|
||||
|
||||
}
|
28
components/esm/loadbsgn.hpp
Normal file
28
components/esm/loadbsgn.hpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
#ifndef OPENMW_ESM_BSGN_H
|
||||
#define OPENMW_ESM_BSGN_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "spelllist.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
class ESMReader;
|
||||
class ESMWriter;
|
||||
|
||||
struct BirthSign
|
||||
{
|
||||
std::string mId, mName, mDescription, mTexture;
|
||||
|
||||
// List of powers and abilities that come with this birth sign.
|
||||
SpellList mPowers;
|
||||
|
||||
void load(ESMReader &esm);
|
||||
void save(ESMWriter &esm);
|
||||
|
||||
void blank();
|
||||
///< Set record to default state (does not touch the ID/index).
|
||||
};
|
||||
}
|
||||
#endif
|
304
components/esm/loadcell.cpp
Normal file
304
components/esm/loadcell.cpp
Normal file
|
@ -0,0 +1,304 @@
|
|||
#include "loadcell.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <list>
|
||||
#include <boost/concept_check.hpp>
|
||||
|
||||
#include "esmreader.hpp"
|
||||
#include "esmwriter.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
/// Some overloaded compare operators.
|
||||
bool operator==(const MovedCellRef& ref, int pRefnum)
|
||||
{
|
||||
return (ref.mRefnum == pRefnum);
|
||||
}
|
||||
|
||||
bool operator==(const CellRef& ref, int pRefnum)
|
||||
{
|
||||
return (ref.mRefnum == pRefnum);
|
||||
}
|
||||
|
||||
|
||||
void Cell::load(ESMReader &esm, bool saveContext)
|
||||
{
|
||||
// Ignore this for now, it might mean we should delete the entire
|
||||
// cell?
|
||||
// TODO: treat the special case "another plugin moved this ref, but we want to delete it"!
|
||||
if (esm.isNextSub("DELE")) {
|
||||
esm.skipHSub();
|
||||
}
|
||||
|
||||
esm.getHNT(mData, "DATA", 12);
|
||||
|
||||
// Water level
|
||||
mWater = -1;
|
||||
mNAM0 = 0;
|
||||
|
||||
if (mData.mFlags & Interior)
|
||||
{
|
||||
// Interior cells
|
||||
if (esm.isNextSub("INTV"))
|
||||
{
|
||||
int waterl;
|
||||
esm.getHT(waterl);
|
||||
mWater = (float) waterl;
|
||||
mWaterInt = true;
|
||||
}
|
||||
else if (esm.isNextSub("WHGT"))
|
||||
esm.getHT(mWater);
|
||||
|
||||
// Quasi-exterior cells have a region (which determines the
|
||||
// weather), pure interior cells have ambient lighting
|
||||
// instead.
|
||||
if (mData.mFlags & QuasiEx)
|
||||
mRegion = esm.getHNOString("RGNN");
|
||||
else if (esm.isNextSub("AMBI"))
|
||||
esm.getHT(mAmbi);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Exterior cells
|
||||
mRegion = esm.getHNOString("RGNN");
|
||||
|
||||
mMapColor = 0;
|
||||
esm.getHNOT(mMapColor, "NAM5");
|
||||
}
|
||||
if (esm.isNextSub("NAM0")) {
|
||||
esm.getHT(mNAM0);
|
||||
}
|
||||
|
||||
if (saveContext) {
|
||||
mContextList.push_back(esm.getContext());
|
||||
esm.skipRecord();
|
||||
}
|
||||
}
|
||||
|
||||
void Cell::preLoad(ESMReader &esm) //Can't be "load" because it conflicts with function in esmtool
|
||||
{
|
||||
this->load(esm, false);
|
||||
}
|
||||
|
||||
void Cell::postLoad(ESMReader &esm)
|
||||
{
|
||||
// Save position of the cell references and move on
|
||||
mContextList.push_back(esm.getContext());
|
||||
esm.skipRecord();
|
||||
}
|
||||
|
||||
void Cell::save(ESMWriter &esm)
|
||||
{
|
||||
esm.writeHNT("DATA", mData, 12);
|
||||
if (mData.mFlags & Interior)
|
||||
{
|
||||
if (mWater != -1) {
|
||||
if (mWaterInt) {
|
||||
int water =
|
||||
(mWater >= 0) ? (int) (mWater + 0.5) : (int) (mWater - 0.5);
|
||||
esm.writeHNT("INTV", water);
|
||||
} else {
|
||||
esm.writeHNT("WHGT", mWater);
|
||||
}
|
||||
}
|
||||
|
||||
if (mData.mFlags & QuasiEx)
|
||||
esm.writeHNOCString("RGNN", mRegion);
|
||||
else
|
||||
esm.writeHNT("AMBI", mAmbi, 16);
|
||||
}
|
||||
else
|
||||
{
|
||||
esm.writeHNOCString("RGNN", mRegion);
|
||||
if (mMapColor != 0)
|
||||
esm.writeHNT("NAM5", mMapColor);
|
||||
}
|
||||
|
||||
if (mNAM0 != 0)
|
||||
esm.writeHNT("NAM0", mNAM0);
|
||||
}
|
||||
|
||||
void Cell::restore(ESMReader &esm, int iCtx) const
|
||||
{
|
||||
esm.restoreContext(mContextList.at (iCtx));
|
||||
}
|
||||
|
||||
std::string Cell::getDescription() const
|
||||
{
|
||||
if (mData.mFlags & Interior)
|
||||
{
|
||||
return mName;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << mData.mX << ", " << mData.mY;
|
||||
return stream.str();
|
||||
}
|
||||
}
|
||||
|
||||
bool Cell::getNextRef(ESMReader &esm, CellRef &ref)
|
||||
{
|
||||
// TODO: Try and document reference numbering, I don't think this has been done anywhere else.
|
||||
if (!esm.hasMoreSubs())
|
||||
return false;
|
||||
|
||||
// NOTE: We should not need this check. It is a safety check until we have checked
|
||||
// more plugins, and how they treat these moved references.
|
||||
if (esm.isNextSub("MVRF")) {
|
||||
esm.skipRecord(); // skip MVRF
|
||||
esm.skipRecord(); // skip CNDT
|
||||
// That should be it, I haven't seen any other fields yet.
|
||||
}
|
||||
|
||||
// NAM0 sometimes appears here, sometimes further on
|
||||
ref.mNam0 = 0;
|
||||
if (esm.isNextSub("NAM0"))
|
||||
{
|
||||
esm.getHT(ref.mNam0);
|
||||
//esm.getHNOT(NAM0, "NAM0");
|
||||
}
|
||||
|
||||
esm.getHNT(ref.mRefnum, "FRMR");
|
||||
ref.mRefID = esm.getHNString("NAME");
|
||||
|
||||
// Identify references belonging to a parent file and adapt the ID accordingly.
|
||||
int local = (ref.mRefnum & 0xff000000) >> 24;
|
||||
size_t global = esm.getIndex() + 1;
|
||||
if (local)
|
||||
{
|
||||
// If the most significant 8 bits are used, then this reference already exists.
|
||||
// In this case, do not spawn a new reference, but overwrite the old one.
|
||||
ref.mRefnum &= 0x00ffffff; // delete old plugin ID
|
||||
const std::vector<Header::MasterData> &masters = esm.getMasters();
|
||||
global = masters[local-1].index + 1;
|
||||
ref.mRefnum |= global << 24; // insert global plugin ID
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is an addition by the present plugin. Set the corresponding plugin index.
|
||||
ref.mRefnum |= global << 24; // insert global plugin ID
|
||||
}
|
||||
|
||||
// getHNOT will not change the existing value if the subrecord is
|
||||
// missing
|
||||
ref.mScale = 1.0;
|
||||
esm.getHNOT(ref.mScale, "XSCL");
|
||||
|
||||
// TODO: support loading references from saves, there are tons of keys not recognized yet.
|
||||
// The following is just an incomplete list.
|
||||
if (esm.isNextSub("ACTN"))
|
||||
esm.skipHSub();
|
||||
if (esm.isNextSub("STPR"))
|
||||
esm.skipHSub();
|
||||
if (esm.isNextSub("ACDT"))
|
||||
esm.skipHSub();
|
||||
if (esm.isNextSub("ACSC"))
|
||||
esm.skipHSub();
|
||||
if (esm.isNextSub("ACSL"))
|
||||
esm.skipHSub();
|
||||
if (esm.isNextSub("CHRD"))
|
||||
esm.skipHSub();
|
||||
else if (esm.isNextSub("CRED")) // ???
|
||||
esm.skipHSub();
|
||||
|
||||
ref.mOwner = esm.getHNOString("ANAM");
|
||||
ref.mGlob = esm.getHNOString("BNAM");
|
||||
ref.mSoul = esm.getHNOString("XSOL");
|
||||
|
||||
ref.mFaction = esm.getHNOString("CNAM");
|
||||
ref.mFactIndex = -2;
|
||||
esm.getHNOT(ref.mFactIndex, "INDX");
|
||||
|
||||
ref.mGoldValue = 1;
|
||||
ref.mCharge = -1;
|
||||
ref.mEnchantmentCharge = -1;
|
||||
|
||||
esm.getHNOT(ref.mEnchantmentCharge, "XCHG");
|
||||
|
||||
esm.getHNOT(ref.mCharge, "INTV");
|
||||
|
||||
esm.getHNOT(ref.mGoldValue, "NAM9");
|
||||
|
||||
// Present for doors that teleport you to another cell.
|
||||
if (esm.isNextSub("DODT"))
|
||||
{
|
||||
ref.mTeleport = true;
|
||||
esm.getHT(ref.mDoorDest);
|
||||
ref.mDestCell = esm.getHNOString("DNAM");
|
||||
} else {
|
||||
ref.mTeleport = false;
|
||||
}
|
||||
|
||||
// Integer, despite the name suggesting otherwise
|
||||
ref.mLockLevel = -1;
|
||||
esm.getHNOT(ref.mLockLevel, "FLTV");
|
||||
ref.mKey = esm.getHNOString("KNAM");
|
||||
ref.mTrap = esm.getHNOString("TNAM");
|
||||
|
||||
ref.mReferenceBlocked = -1;
|
||||
ref.mFltv = 0;
|
||||
esm.getHNOT(ref.mReferenceBlocked, "UNAM");
|
||||
esm.getHNOT(ref.mFltv, "FLTV");
|
||||
|
||||
esm.getHNOT(ref.mPos, "DATA", 24);
|
||||
|
||||
// Number of references in the cell? Maximum once in each cell,
|
||||
// but not always at the beginning, and not always right. In other
|
||||
// words, completely useless.
|
||||
// Update: Well, maybe not completely useless. This might actually be
|
||||
// number_of_references + number_of_references_moved_here_Across_boundaries,
|
||||
// and could be helpful for collecting these weird moved references.
|
||||
if (esm.isNextSub("NAM0"))
|
||||
{
|
||||
esm.getHT(ref.mNam0);
|
||||
//esm.getHNOT(NAM0, "NAM0");
|
||||
}
|
||||
|
||||
if (esm.isNextSub("DELE")) {
|
||||
esm.skipHSub();
|
||||
ref.mDeleted = 2; // Deleted, will not respawn.
|
||||
// TODO: find out when references do respawn.
|
||||
} else
|
||||
ref.mDeleted = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Cell::getNextMVRF(ESMReader &esm, MovedCellRef &mref)
|
||||
{
|
||||
esm.getHT(mref.mRefnum);
|
||||
esm.getHNOT(mref.mTarget, "CNDT");
|
||||
|
||||
// Identify references belonging to a parent file and adapt the ID accordingly.
|
||||
int local = (mref.mRefnum & 0xff000000) >> 24;
|
||||
size_t global = esm.getIndex() + 1;
|
||||
mref.mRefnum &= 0x00ffffff; // delete old plugin ID
|
||||
const std::vector<Header::MasterData> &masters = esm.getMasters();
|
||||
global = masters[local-1].index + 1;
|
||||
mref.mRefnum |= global << 24; // insert global plugin ID
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Cell::blank()
|
||||
{
|
||||
mName.clear();
|
||||
mRegion.clear();
|
||||
mWater = 0;
|
||||
mWaterInt = false;
|
||||
mMapColor = 0;
|
||||
mNAM0 = 0;
|
||||
|
||||
mData.mFlags = 0;
|
||||
mData.mX = 0;
|
||||
mData.mY = 0;
|
||||
|
||||
mAmbi.mAmbient = 0;
|
||||
mAmbi.mSunlight = 0;
|
||||
mAmbi.mFog = 0;
|
||||
mAmbi.mFogDensity = 0;
|
||||
}
|
||||
}
|
154
components/esm/loadcell.hpp
Normal file
154
components/esm/loadcell.hpp
Normal file
|
@ -0,0 +1,154 @@
|
|||
#ifndef OPENMW_ESM_CELL_H
|
||||
#define OPENMW_ESM_CELL_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
|
||||
#include "esmcommon.hpp"
|
||||
#include "defs.hpp"
|
||||
#include "cellref.hpp"
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
class ESMStore;
|
||||
}
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
class ESMReader;
|
||||
class ESMWriter;
|
||||
|
||||
/* Moved cell reference tracking object. This mainly stores the target cell
|
||||
of the reference, so we can easily know where it has been moved when another
|
||||
plugin tries to move it independently.
|
||||
Unfortunately, we need to implement this here.
|
||||
*/
|
||||
class MovedCellRef
|
||||
{
|
||||
public:
|
||||
int mRefnum;
|
||||
|
||||
// Target cell (if exterior)
|
||||
int mTarget[2];
|
||||
|
||||
// TODO: Support moving references between exterior and interior cells!
|
||||
// This may happen in saves, when an NPC follows the player. Tribunal
|
||||
// introduces a henchman (which no one uses), so we may need this as well.
|
||||
};
|
||||
|
||||
/// Overloaded copare operator used to search inside a list of cell refs.
|
||||
bool operator==(const MovedCellRef& ref, int pRefnum);
|
||||
bool operator==(const CellRef& ref, int pRefnum);
|
||||
|
||||
typedef std::list<MovedCellRef> MovedCellRefTracker;
|
||||
typedef std::list<CellRef> CellRefTracker;
|
||||
|
||||
/* Cells hold data about objects, creatures, statics (rocks, walls,
|
||||
buildings) and landscape (for exterior cells). Cells frequently
|
||||
also has other associated LAND and PGRD records. Combined, all this
|
||||
data can be huge, and we cannot load it all at startup. Instead,
|
||||
the strategy we use is to remember the file position of each cell
|
||||
(using ESMReader::getContext()) and jumping back into place
|
||||
whenever we need to load a given cell.
|
||||
*/
|
||||
struct Cell
|
||||
{
|
||||
enum Flags
|
||||
{
|
||||
Interior = 0x01, // Interior cell
|
||||
HasWater = 0x02, // Does this cell have a water surface
|
||||
NoSleep = 0x04, // Is it allowed to sleep here (without a bed)
|
||||
QuasiEx = 0x80 // Behave like exterior (Tribunal+), with
|
||||
// skybox and weather
|
||||
};
|
||||
|
||||
struct DATAstruct
|
||||
{
|
||||
int mFlags;
|
||||
int mX, mY;
|
||||
};
|
||||
|
||||
struct AMBIstruct
|
||||
{
|
||||
Color mAmbient, mSunlight, mFog;
|
||||
float mFogDensity;
|
||||
};
|
||||
|
||||
// Interior cells are indexed by this (it's the 'id'), for exterior
|
||||
// cells it is optional.
|
||||
std::string mName;
|
||||
|
||||
// Optional region name for exterior and quasi-exterior cells.
|
||||
std::string mRegion;
|
||||
|
||||
std::vector<ESM_Context> mContextList; // File position; multiple positions for multiple plugin support
|
||||
DATAstruct mData;
|
||||
AMBIstruct mAmbi;
|
||||
float mWater; // Water level
|
||||
bool mWaterInt;
|
||||
int mMapColor;
|
||||
int mNAM0;
|
||||
|
||||
// References "leased" from another cell (i.e. a different cell
|
||||
// introduced this ref, and it has been moved here by a plugin)
|
||||
CellRefTracker mLeasedRefs;
|
||||
MovedCellRefTracker mMovedRefs;
|
||||
|
||||
void preLoad(ESMReader &esm);
|
||||
void postLoad(ESMReader &esm);
|
||||
|
||||
// This method is left in for compatibility with esmtool. Parsing moved references currently requires
|
||||
// passing ESMStore, bit it does not know about this parameter, so we do it this way.
|
||||
void load(ESMReader &esm, bool saveContext = true);
|
||||
void save(ESMWriter &esm);
|
||||
|
||||
bool isExterior() const
|
||||
{
|
||||
return !(mData.mFlags & Interior);
|
||||
}
|
||||
|
||||
int getGridX() const
|
||||
{
|
||||
return mData.mX;
|
||||
}
|
||||
|
||||
int getGridY() const
|
||||
{
|
||||
return mData.mY;
|
||||
}
|
||||
|
||||
bool hasWater() const
|
||||
{
|
||||
return (mData.mFlags&HasWater);
|
||||
}
|
||||
|
||||
// Restore the given reader to the stored position. Will try to open
|
||||
// the file matching the stored file name. If you want to read from
|
||||
// somewhere other than the file system, you need to pre-open the
|
||||
// ESMReader, and the filename must match the stored filename
|
||||
// exactly.
|
||||
void restore(ESMReader &esm, int iCtx) const;
|
||||
|
||||
std::string getDescription() const;
|
||||
///< Return a short string describing the cell (mostly used for debugging/logging purpose)
|
||||
|
||||
/* Get the next reference in this cell, if any. Returns false when
|
||||
there are no more references in the cell.
|
||||
|
||||
All fields of the CellRef struct are overwritten. You can safely
|
||||
reuse one memory location without blanking it between calls.
|
||||
*/
|
||||
static bool getNextRef(ESMReader &esm, CellRef &ref);
|
||||
|
||||
/* This fetches an MVRF record, which is used to track moved references.
|
||||
* Since they are comparably rare, we use a separate method for this.
|
||||
*/
|
||||
static bool getNextMVRF(ESMReader &esm, MovedCellRef &mref);
|
||||
|
||||
void blank();
|
||||
///< Set record to default state (does not touch the ID/index).
|
||||
};
|
||||
}
|
||||
#endif
|
71
components/esm/loadclas.cpp
Normal file
71
components/esm/loadclas.cpp
Normal file
|
@ -0,0 +1,71 @@
|
|||
#include "loadclas.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include "esmreader.hpp"
|
||||
#include "esmwriter.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
const Class::Specialization Class::sSpecializationIds[3] = {
|
||||
Class::Combat,
|
||||
Class::Magic,
|
||||
Class::Stealth
|
||||
};
|
||||
|
||||
const char *Class::sGmstSpecializationIds[3] = {
|
||||
"sSpecializationCombat",
|
||||
"sSpecializationMagic",
|
||||
"sSpecializationStealth"
|
||||
};
|
||||
|
||||
|
||||
int& Class::CLDTstruct::getSkill (int index, bool major)
|
||||
{
|
||||
if (index<0 || index>=5)
|
||||
throw std::logic_error ("skill index out of range");
|
||||
|
||||
return mSkills[index][major ? 1 : 0];
|
||||
}
|
||||
|
||||
int Class::CLDTstruct::getSkill (int index, bool major) const
|
||||
{
|
||||
if (index<0 || index>=5)
|
||||
throw std::logic_error ("skill index out of range");
|
||||
|
||||
return mSkills[index][major ? 1 : 0];
|
||||
}
|
||||
|
||||
void Class::load(ESMReader &esm)
|
||||
{
|
||||
mName = esm.getHNString("FNAM");
|
||||
esm.getHNT(mData, "CLDT", 60);
|
||||
|
||||
if (mData.mIsPlayable > 1)
|
||||
esm.fail("Unknown bool value");
|
||||
|
||||
mDescription = esm.getHNOString("DESC");
|
||||
}
|
||||
void Class::save(ESMWriter &esm)
|
||||
{
|
||||
esm.writeHNCString("FNAM", mName);
|
||||
esm.writeHNT("CLDT", mData, 60);
|
||||
esm.writeHNOString("DESC", mDescription);
|
||||
}
|
||||
|
||||
void Class::blank()
|
||||
{
|
||||
mName.clear();
|
||||
mDescription.clear();
|
||||
|
||||
mData.mAttribute[0] = mData.mAttribute[1] = 0;
|
||||
mData.mSpecialization = 0;
|
||||
mData.mIsPlayable = 0;
|
||||
mData.mCalc = 0;
|
||||
|
||||
for (int i=0; i<5; ++i)
|
||||
for (int i2=0; i2<2; ++i2)
|
||||
mData.mSkills[i][i2] = 0;
|
||||
}
|
||||
}
|
80
components/esm/loadclas.hpp
Normal file
80
components/esm/loadclas.hpp
Normal file
|
@ -0,0 +1,80 @@
|
|||
#ifndef OPENMW_ESM_CLAS_H
|
||||
#define OPENMW_ESM_CLAS_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
class ESMReader;
|
||||
class ESMWriter;
|
||||
|
||||
/*
|
||||
* Character class definitions
|
||||
*/
|
||||
|
||||
// These flags tells us which items should be auto-calculated for this
|
||||
// class
|
||||
struct Class
|
||||
{
|
||||
enum AutoCalc
|
||||
{
|
||||
Weapon = 0x00001,
|
||||
Armor = 0x00002,
|
||||
Clothing = 0x00004,
|
||||
Books = 0x00008,
|
||||
Ingredient = 0x00010,
|
||||
Lockpick = 0x00020,
|
||||
Probe = 0x00040,
|
||||
Lights = 0x00080,
|
||||
Apparatus = 0x00100,
|
||||
Repair = 0x00200,
|
||||
Misc = 0x00400,
|
||||
Spells = 0x00800,
|
||||
MagicItems = 0x01000,
|
||||
Potions = 0x02000,
|
||||
Training = 0x04000,
|
||||
Spellmaking = 0x08000,
|
||||
Enchanting = 0x10000,
|
||||
RepairItem = 0x20000
|
||||
};
|
||||
|
||||
enum Specialization
|
||||
{
|
||||
Combat = 0,
|
||||
Magic = 1,
|
||||
Stealth = 2
|
||||
};
|
||||
|
||||
static const Specialization sSpecializationIds[3];
|
||||
static const char *sGmstSpecializationIds[3];
|
||||
|
||||
struct CLDTstruct
|
||||
{
|
||||
int mAttribute[2]; // Attributes that get class bonus
|
||||
int mSpecialization; // 0 = Combat, 1 = Magic, 2 = Stealth
|
||||
int mSkills[5][2]; // Minor and major skills.
|
||||
int mIsPlayable; // 0x0001 - Playable class
|
||||
|
||||
// I have no idea how to autocalculate these items...
|
||||
int mCalc;
|
||||
|
||||
int& getSkill (int index, bool major);
|
||||
///< Throws an exception for invalid values of \a index.
|
||||
|
||||
int getSkill (int index, bool major) const;
|
||||
///< Throws an exception for invalid values of \a index.
|
||||
}; // 60 bytes
|
||||
|
||||
std::string mId, mName, mDescription;
|
||||
CLDTstruct mData;
|
||||
|
||||
void load(ESMReader &esm);
|
||||
void save(ESMWriter &esm);
|
||||
|
||||
void blank();
|
||||
///< Set record to default state (does not touch the ID/index).
|
||||
|
||||
};
|
||||
}
|
||||
#endif
|
51
components/esm/loadclot.cpp
Normal file
51
components/esm/loadclot.cpp
Normal file
|
@ -0,0 +1,51 @@
|
|||
#include "loadclot.hpp"
|
||||
|
||||
#include "esmreader.hpp"
|
||||
#include "esmwriter.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
void Clothing::load(ESMReader &esm)
|
||||
{
|
||||
mModel = esm.getHNString("MODL");
|
||||
mName = esm.getHNOString("FNAM");
|
||||
esm.getHNT(mData, "CTDT", 12);
|
||||
|
||||
mScript = esm.getHNOString("SCRI");
|
||||
mIcon = esm.getHNOString("ITEX");
|
||||
|
||||
mParts.load(esm);
|
||||
|
||||
|
||||
mEnchant = esm.getHNOString("ENAM");
|
||||
}
|
||||
void Clothing::save(ESMWriter &esm)
|
||||
{
|
||||
esm.writeHNCString("MODL", mModel);
|
||||
esm.writeHNOCString("FNAM", mName);
|
||||
esm.writeHNT("CTDT", mData, 12);
|
||||
|
||||
esm.writeHNOCString("SCRI", mScript);
|
||||
esm.writeHNOCString("ITEX", mIcon);
|
||||
|
||||
mParts.save(esm);
|
||||
|
||||
esm.writeHNOCString("ENAM", mEnchant);
|
||||
}
|
||||
|
||||
void Clothing::blank()
|
||||
{
|
||||
mData.mType = 0;
|
||||
mData.mWeight = 0;
|
||||
mData.mValue = 0;
|
||||
mData.mEnchant = 0;
|
||||
mParts.mParts.clear();
|
||||
mName.clear();
|
||||
mModel.clear();
|
||||
mIcon.clear();
|
||||
mIcon.clear();
|
||||
mEnchant.clear();
|
||||
mScript.clear();
|
||||
}
|
||||
}
|
54
components/esm/loadclot.hpp
Normal file
54
components/esm/loadclot.hpp
Normal file
|
@ -0,0 +1,54 @@
|
|||
#ifndef OPENMW_ESM_CLOT_H
|
||||
#define OPENMW_ESM_CLOT_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "loadarmo.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
class ESMReader;
|
||||
class ESMWriter;
|
||||
|
||||
/*
|
||||
* Clothing
|
||||
*/
|
||||
|
||||
struct Clothing
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
Pants = 0,
|
||||
Shoes = 1,
|
||||
Shirt = 2,
|
||||
Belt = 3,
|
||||
Robe = 4,
|
||||
RGlove = 5,
|
||||
LGlove = 6,
|
||||
Skirt = 7,
|
||||
Ring = 8,
|
||||
Amulet = 9
|
||||
};
|
||||
|
||||
struct CTDTstruct
|
||||
{
|
||||
int mType;
|
||||
float mWeight;
|
||||
short mValue;
|
||||
short mEnchant;
|
||||
};
|
||||
CTDTstruct mData;
|
||||
|
||||
PartReferenceList mParts;
|
||||
|
||||
std::string mId, mName, mModel, mIcon, mEnchant, mScript;
|
||||
|
||||
void load(ESMReader &esm);
|
||||
void save(ESMWriter &esm);
|
||||
|
||||
void blank();
|
||||
///< Set record to default state (does not touch the ID).
|
||||
};
|
||||
}
|
||||
#endif
|
65
components/esm/loadcont.cpp
Normal file
65
components/esm/loadcont.cpp
Normal file
|
@ -0,0 +1,65 @@
|
|||
#include "loadcont.hpp"
|
||||
|
||||
#include "esmreader.hpp"
|
||||
#include "esmwriter.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
void InventoryList::load(ESMReader &esm)
|
||||
{
|
||||
ContItem ci;
|
||||
while (esm.isNextSub("NPCO"))
|
||||
{
|
||||
esm.getHT(ci, 36);
|
||||
mList.push_back(ci);
|
||||
}
|
||||
}
|
||||
|
||||
void InventoryList::save(ESMWriter &esm)
|
||||
{
|
||||
for (std::vector<ContItem>::iterator it = mList.begin(); it != mList.end(); ++it)
|
||||
{
|
||||
esm.writeHNT("NPCO", *it, 36);
|
||||
}
|
||||
}
|
||||
|
||||
void Container::load(ESMReader &esm)
|
||||
{
|
||||
mModel = esm.getHNString("MODL");
|
||||
mName = esm.getHNOString("FNAM");
|
||||
esm.getHNT(mWeight, "CNDT", 4);
|
||||
esm.getHNT(mFlags, "FLAG", 4);
|
||||
|
||||
if (mFlags & 0xf4)
|
||||
esm.fail("Unknown flags");
|
||||
if (!(mFlags & 0x8))
|
||||
esm.fail("Flag 8 not set");
|
||||
|
||||
mScript = esm.getHNOString("SCRI");
|
||||
|
||||
mInventory.load(esm);
|
||||
}
|
||||
|
||||
void Container::save(ESMWriter &esm)
|
||||
{
|
||||
esm.writeHNCString("MODL", mModel);
|
||||
esm.writeHNOCString("FNAM", mName);
|
||||
esm.writeHNT("CNDT", mWeight, 4);
|
||||
esm.writeHNT("FLAG", mFlags, 4);
|
||||
|
||||
esm.writeHNOCString("SCRI", mScript);
|
||||
|
||||
mInventory.save(esm);
|
||||
}
|
||||
|
||||
void Container::blank()
|
||||
{
|
||||
mName.clear();
|
||||
mModel.clear();
|
||||
mScript.clear();
|
||||
mWeight = 0;
|
||||
mFlags = 0;
|
||||
mInventory.mList.clear();
|
||||
}
|
||||
}
|
55
components/esm/loadcont.hpp
Normal file
55
components/esm/loadcont.hpp
Normal file
|
@ -0,0 +1,55 @@
|
|||
#ifndef OPENMW_ESM_CONT_H
|
||||
#define OPENMW_ESM_CONT_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "esmcommon.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
class ESMReader;
|
||||
class ESMWriter;
|
||||
|
||||
/*
|
||||
* Container definition
|
||||
*/
|
||||
|
||||
struct ContItem
|
||||
{
|
||||
int mCount;
|
||||
NAME32 mItem;
|
||||
};
|
||||
|
||||
struct InventoryList
|
||||
{
|
||||
std::vector<ContItem> mList;
|
||||
|
||||
void load(ESMReader &esm);
|
||||
void save(ESMWriter &esm);
|
||||
};
|
||||
|
||||
struct Container
|
||||
{
|
||||
enum Flags
|
||||
{
|
||||
Organic = 1, // Objects cannot be placed in this container
|
||||
Respawn = 2, // Respawns after 4 months
|
||||
Unknown = 8
|
||||
};
|
||||
|
||||
std::string mId, mName, mModel, mScript;
|
||||
|
||||
float mWeight; // Not sure, might be max total weight allowed?
|
||||
int mFlags;
|
||||
InventoryList mInventory;
|
||||
|
||||
void load(ESMReader &esm);
|
||||
void save(ESMWriter &esm);
|
||||
|
||||
void blank();
|
||||
///< Set record to default state (does not touch the ID).
|
||||
};
|
||||
}
|
||||
#endif
|
82
components/esm/loadcrea.cpp
Normal file
82
components/esm/loadcrea.cpp
Normal file
|
@ -0,0 +1,82 @@
|
|||
#include "loadcrea.hpp"
|
||||
|
||||
#include "esmreader.hpp"
|
||||
#include "esmwriter.hpp"
|
||||
|
||||
namespace ESM {
|
||||
|
||||
void Creature::load(ESMReader &esm)
|
||||
{
|
||||
mPersistent = esm.getRecordFlags() & 0x0400;
|
||||
|
||||
mModel = esm.getHNString("MODL");
|
||||
mOriginal = esm.getHNOString("CNAM");
|
||||
mName = esm.getHNOString("FNAM");
|
||||
mScript = esm.getHNOString("SCRI");
|
||||
|
||||
esm.getHNT(mData, "NPDT", 96);
|
||||
|
||||
esm.getHNT(mFlags, "FLAG");
|
||||
mScale = 1.0;
|
||||
esm.getHNOT(mScale, "XSCL");
|
||||
|
||||
mInventory.load(esm);
|
||||
mSpells.load(esm);
|
||||
|
||||
if (esm.isNextSub("AIDT"))
|
||||
{
|
||||
esm.getHExact(&mAiData, sizeof(mAiData));
|
||||
mHasAI = true;
|
||||
}
|
||||
else
|
||||
mHasAI = false;
|
||||
|
||||
mAiPackage.load(esm);
|
||||
esm.skipRecord();
|
||||
}
|
||||
|
||||
void Creature::save(ESMWriter &esm)
|
||||
{
|
||||
esm.writeHNCString("MODL", mModel);
|
||||
esm.writeHNOCString("CNAM", mOriginal);
|
||||
esm.writeHNOCString("FNAM", mName);
|
||||
esm.writeHNOCString("SCRI", mScript);
|
||||
esm.writeHNT("NPDT", mData, 96);
|
||||
esm.writeHNT("FLAG", mFlags);
|
||||
if (mScale != 1.0) {
|
||||
esm.writeHNT("XSCL", mScale);
|
||||
}
|
||||
|
||||
mInventory.save(esm);
|
||||
mSpells.save(esm);
|
||||
if (mHasAI) {
|
||||
esm.writeHNT("AIDT", mAiData, sizeof(mAiData));
|
||||
}
|
||||
mAiPackage.save(esm);
|
||||
}
|
||||
|
||||
void Creature::blank()
|
||||
{
|
||||
mData.mType = 0;
|
||||
mData.mLevel = 0;
|
||||
mData.mStrength = mData.mIntelligence = mData.mWillpower = mData.mAgility =
|
||||
mData.mSpeed = mData.mEndurance = mData.mPersonality = mData.mLuck = 0;
|
||||
mData.mHealth = mData.mMana = mData.mFatigue = 0;
|
||||
mData.mSoul = 0;
|
||||
mData.mCombat = mData.mMagic = mData.mStealth = 0;
|
||||
for (int i=0; i<6; ++i) mData.mAttack[i] = 0;
|
||||
mData.mGold = 0;
|
||||
mFlags = 0;
|
||||
mScale = 0;
|
||||
mModel.clear();
|
||||
mName.clear();
|
||||
mScript.clear();
|
||||
mOriginal.clear();
|
||||
mInventory.mList.clear();
|
||||
mSpells.mList.clear();
|
||||
mHasAI = false;
|
||||
mAiData.blank();
|
||||
mAiData.mServices = 0;
|
||||
mAiPackage.mList.clear();
|
||||
}
|
||||
}
|
96
components/esm/loadcrea.hpp
Normal file
96
components/esm/loadcrea.hpp
Normal file
|
@ -0,0 +1,96 @@
|
|||
#ifndef OPENMW_ESM_CREA_H
|
||||
#define OPENMW_ESM_CREA_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "loadcont.hpp"
|
||||
#include "spelllist.hpp"
|
||||
#include "aipackage.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
class ESMReader;
|
||||
class ESMWriter;
|
||||
|
||||
/*
|
||||
* Creature definition
|
||||
*
|
||||
*/
|
||||
|
||||
struct Creature
|
||||
{
|
||||
// Default is 0x48?
|
||||
enum Flags
|
||||
{
|
||||
Biped = 0x001,
|
||||
Respawn = 0x002,
|
||||
Weapon = 0x004, // Has weapon and shield
|
||||
None = 0x008, // ??
|
||||
Swims = 0x010,
|
||||
Flies = 0x020, // Don't know what happens if several
|
||||
Walks = 0x040, // of these are set
|
||||
Essential = 0x080,
|
||||
Skeleton = 0x400, // Does not have normal blood
|
||||
Metal = 0x800 // Has 'golden' blood
|
||||
};
|
||||
|
||||
enum Type
|
||||
{
|
||||
Creatures = 0,
|
||||
Deadra = 1,
|
||||
Undead = 2,
|
||||
Humanoid = 3
|
||||
};
|
||||
|
||||
struct NPDTstruct
|
||||
{
|
||||
int mType;
|
||||
// For creatures we obviously have to use ints, not shorts and
|
||||
// bytes like we use for NPCs.... this file format just makes so
|
||||
// much sense! (Still, _much_ easier to decode than the NIFs.)
|
||||
int mLevel;
|
||||
int mStrength,
|
||||
mIntelligence,
|
||||
mWillpower,
|
||||
mAgility,
|
||||
mSpeed,
|
||||
mEndurance,
|
||||
mPersonality,
|
||||
mLuck;
|
||||
|
||||
int mHealth, mMana, mFatigue; // Stats
|
||||
int mSoul; // The creatures soul value (used with soul gems.)
|
||||
int mCombat, mMagic, mStealth; // Don't know yet.
|
||||
int mAttack[6]; // AttackMin1, AttackMax1, ditto2, ditto3
|
||||
int mGold;
|
||||
}; // 96 bytes
|
||||
|
||||
NPDTstruct mData;
|
||||
|
||||
int mFlags;
|
||||
|
||||
bool mPersistent;
|
||||
|
||||
float mScale;
|
||||
|
||||
std::string mId, mModel, mName, mScript;
|
||||
std::string mOriginal; // Base creature that this is a modification of
|
||||
|
||||
InventoryList mInventory;
|
||||
SpellList mSpells;
|
||||
|
||||
|
||||
bool mHasAI;
|
||||
AIData mAiData;
|
||||
AIPackageList mAiPackage;
|
||||
|
||||
void load(ESMReader &esm);
|
||||
void save(ESMWriter &esm);
|
||||
|
||||
void blank();
|
||||
///< Set record to default state (does not touch the ID).
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
47
components/esm/loadcrec.hpp
Normal file
47
components/esm/loadcrec.hpp
Normal file
|
@ -0,0 +1,47 @@
|
|||
#ifndef OPENMW_ESM_CREC_H
|
||||
#define OPENMW_ESM_CREC_H
|
||||
|
||||
#include <string>
|
||||
|
||||
// TODO create implementation files and remove this one
|
||||
#include "esmreader.hpp"
|
||||
|
||||
namespace ESM {
|
||||
|
||||
class ESMReader;
|
||||
class ESMWriter;
|
||||
|
||||
/* These two are only used in save games. They are not decoded yet.
|
||||
*/
|
||||
|
||||
/// Changes a creature
|
||||
struct LoadCREC
|
||||
{
|
||||
std::string mId;
|
||||
|
||||
void load(ESMReader &esm)
|
||||
{
|
||||
esm.skipRecord();
|
||||
}
|
||||
|
||||
void save(ESMWriter &esm)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/// Changes an item list / container
|
||||
struct LoadCNTC
|
||||
{
|
||||
std::string mId;
|
||||
|
||||
void load(ESMReader &esm)
|
||||
{
|
||||
esm.skipRecord();
|
||||
}
|
||||
|
||||
void save(ESMWriter &esm)
|
||||
{
|
||||
}
|
||||
};
|
||||
}
|
||||
#endif
|
39
components/esm/loaddial.cpp
Normal file
39
components/esm/loaddial.cpp
Normal file
|
@ -0,0 +1,39 @@
|
|||
#include "loaddial.hpp"
|
||||
|
||||
#include "esmreader.hpp"
|
||||
#include "esmwriter.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
void Dialogue::load(ESMReader &esm)
|
||||
{
|
||||
esm.getSubNameIs("DATA");
|
||||
esm.getSubHeader();
|
||||
int si = esm.getSubSize();
|
||||
if (si == 1)
|
||||
esm.getT(mType);
|
||||
else if (si == 4)
|
||||
{
|
||||
// These are just markers, their values are not used.
|
||||
int i;
|
||||
esm.getT(i);
|
||||
esm.getHNT(i, "DELE");
|
||||
mType = Deleted;
|
||||
}
|
||||
else
|
||||
esm.fail("Unknown sub record size");
|
||||
}
|
||||
|
||||
void Dialogue::save(ESMWriter &esm)
|
||||
{
|
||||
if (mType != Deleted)
|
||||
esm.writeHNT("DATA", mType);
|
||||
else
|
||||
{
|
||||
esm.writeHNT("DATA", (int)1);
|
||||
esm.writeHNT("DELE", (int)1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
40
components/esm/loaddial.hpp
Normal file
40
components/esm/loaddial.hpp
Normal file
|
@ -0,0 +1,40 @@
|
|||
#ifndef OPENMW_ESM_DIAL_H
|
||||
#define OPENMW_ESM_DIAL_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "loadinfo.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
class ESMReader;
|
||||
class ESMWriter;
|
||||
|
||||
/*
|
||||
* Dialogue topic and journal entries. The actual data is contained in
|
||||
* the INFO records following the DIAL.
|
||||
*/
|
||||
|
||||
struct Dialogue
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
Topic = 0,
|
||||
Voice = 1,
|
||||
Greeting = 2,
|
||||
Persuasion = 3,
|
||||
Journal = 4,
|
||||
Deleted = -1
|
||||
};
|
||||
|
||||
std::string mId;
|
||||
signed char mType;
|
||||
std::vector<DialInfo> mInfo;
|
||||
|
||||
void load(ESMReader &esm);
|
||||
void save(ESMWriter &esm);
|
||||
};
|
||||
}
|
||||
#endif
|
35
components/esm/loaddoor.cpp
Normal file
35
components/esm/loaddoor.cpp
Normal file
|
@ -0,0 +1,35 @@
|
|||
#include "loaddoor.hpp"
|
||||
|
||||
#include "esmreader.hpp"
|
||||
#include "esmwriter.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
void Door::load(ESMReader &esm)
|
||||
{
|
||||
mModel = esm.getHNString("MODL");
|
||||
mName = esm.getHNOString("FNAM");
|
||||
mScript = esm.getHNOString("SCRI");
|
||||
mOpenSound = esm.getHNOString("SNAM");
|
||||
mCloseSound = esm.getHNOString("ANAM");
|
||||
}
|
||||
|
||||
void Door::save(ESMWriter &esm)
|
||||
{
|
||||
esm.writeHNCString("MODL", mModel);
|
||||
esm.writeHNOCString("FNAM", mName);
|
||||
esm.writeHNOCString("SCRI", mScript);
|
||||
esm.writeHNOCString("SNAM", mOpenSound);
|
||||
esm.writeHNOCString("ANAM", mCloseSound);
|
||||
}
|
||||
|
||||
void Door::blank()
|
||||
{
|
||||
mName.clear();
|
||||
mModel.clear();
|
||||
mScript.clear();
|
||||
mOpenSound.clear();
|
||||
mCloseSound.clear();
|
||||
}
|
||||
}
|
23
components/esm/loaddoor.hpp
Normal file
23
components/esm/loaddoor.hpp
Normal file
|
@ -0,0 +1,23 @@
|
|||
#ifndef OPENMW_ESM_DOOR_H
|
||||
#define OPENMW_ESM_DOOR_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
class ESMReader;
|
||||
class ESMWriter;
|
||||
|
||||
struct Door
|
||||
{
|
||||
std::string mId, mName, mModel, mScript, mOpenSound, mCloseSound;
|
||||
|
||||
void load(ESMReader &esm);
|
||||
void save(ESMWriter &esm);
|
||||
|
||||
void blank();
|
||||
///< Set record to default state (does not touch the ID).
|
||||
};
|
||||
}
|
||||
#endif
|
21
components/esm/loadench.cpp
Normal file
21
components/esm/loadench.cpp
Normal file
|
@ -0,0 +1,21 @@
|
|||
#include "loadench.hpp"
|
||||
|
||||
#include "esmreader.hpp"
|
||||
#include "esmwriter.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
void Enchantment::load(ESMReader &esm)
|
||||
{
|
||||
esm.getHNT(mData, "ENDT", 16);
|
||||
mEffects.load(esm);
|
||||
}
|
||||
|
||||
void Enchantment::save(ESMWriter &esm)
|
||||
{
|
||||
esm.writeHNT("ENDT", mData, 16);
|
||||
mEffects.save(esm);
|
||||
}
|
||||
|
||||
}
|
45
components/esm/loadench.hpp
Normal file
45
components/esm/loadench.hpp
Normal file
|
@ -0,0 +1,45 @@
|
|||
#ifndef OPENMW_ESM_ENCH_H
|
||||
#define OPENMW_ESM_ENCH_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "effectlist.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
class ESMReader;
|
||||
class ESMWriter;
|
||||
|
||||
/*
|
||||
* Enchantments
|
||||
*/
|
||||
|
||||
struct Enchantment
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
CastOnce = 0,
|
||||
WhenStrikes = 1,
|
||||
WhenUsed = 2,
|
||||
ConstantEffect = 3
|
||||
};
|
||||
|
||||
struct ENDTstruct
|
||||
{
|
||||
int mType;
|
||||
int mCost;
|
||||
int mCharge;
|
||||
int mAutocalc; // Guessing this is 1 if we are supposed to auto
|
||||
// calculate
|
||||
};
|
||||
|
||||
std::string mId;
|
||||
ENDTstruct mData;
|
||||
EffectList mEffects;
|
||||
|
||||
void load(ESMReader &esm);
|
||||
void save(ESMWriter &esm);
|
||||
};
|
||||
}
|
||||
#endif
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue