Imported Upstream version 0.26.0

This commit is contained in:
Bret Curtis 2013-10-17 16:37:22 +02:00
commit 9a2b6c69b6
1398 changed files with 212217 additions and 0 deletions

102
components/CMakeLists.txt Normal file
View 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)

View 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);
}
}

View 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
View 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
View 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

View 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

View 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");
}

View 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;
}

View 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.

View file

@ -0,0 +1,2 @@
Opening file: textures\tx_natural_cavern_wall13.dds
Size: 43808

18
components/bsa/tests/test.sh Executable file
View 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

View 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

View 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();
}
}

View 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

View 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;
}
}

View 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

View 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

View 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;
}
}

View 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

View 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);
}
}

View 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

View 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);
}
}
}

View 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

View 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();
}
}

View 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

View 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);
}
}
}
}

View 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

View 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();
}
}

View 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

View 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();
}
}

View 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

View 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();
}
}

View 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

View 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) {}

View 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

View file

@ -0,0 +1,13 @@
#include "opcodes.hpp"
namespace Compiler
{
namespace Control
{
const char *controls[numberOfControls] =
{
"playercontrols", "playerfighting", "playerjumping", "playerlooking", "playermagic",
"playerviewswitch", "vanitymode"
};
}
}

View 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

View 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();
}
}

View 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

View 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;
}
}

View 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

View 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);
}
}

View 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

View 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();
}
}

View 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

View 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;
}
}

View 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

View 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) {}
}

View 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

View 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;
}
}

View 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

View 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
View 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

View 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;
}
}
}
}

View 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
View 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
View 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

View 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;
}
}

View 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
View 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

View 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

View 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

View 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

View 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;
}
}

View 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

View 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;
}
}

View 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
View 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
View 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

View 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();
}
}

View 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

View 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();
}
}

View 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

View 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();
}
}

View 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

View 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();
}
}

View 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

View 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);
}
}

View 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

View 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();
}
}

View 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

View 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();
}
}

View 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
View 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
View 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

View 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;
}
}

View 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

View 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();
}
}

View 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

View 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();
}
}

View 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

View 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();
}
}

View 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

View 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

View 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);
}
}
}

View 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

View 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();
}
}

View 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

View 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);
}
}

View 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