Index: src/client/c/hu/varadiistvan/xplra/Exception.h
===================================================================
--- src/client/c/hu/varadiistvan/xplra/Exception.h	(revision 14)
+++ src/client/c/hu/varadiistvan/xplra/Exception.h	(revision 19)
@@ -177,5 +177,35 @@
      * Construct the exception
      */
-    NotConnectedException();
+    NotConnectedException() throw();
+};
+
+//------------------------------------------------------------------------------
+
+/**
+ * Exception thrown when we want to get or set a dataref in a
+ * MultiBuffer into or from a value of the wrong type.
+ */
+class TypeMismatchException : public Exception
+{
+public:
+    /**
+     * Construct the exception
+     */
+    TypeMismatchException() throw();
+};
+
+//------------------------------------------------------------------------------
+
+/**
+ * Exception thrown when we want to get or set a dataref with an
+ * invalid ID.
+ */
+class InvalidIDException : public Exception
+{
+public:
+    /**
+     * Construct the exception
+     */
+    InvalidIDException() throw();
 };
 
@@ -221,6 +251,22 @@
 //------------------------------------------------------------------------------
 
-inline NotConnectedException::NotConnectedException() :
+inline NotConnectedException::NotConnectedException() throw() :
     Exception("xplra::NotConnectedException: not connected to the simulator")
+{
+}
+
+//------------------------------------------------------------------------------
+//------------------------------------------------------------------------------
+
+inline TypeMismatchException::TypeMismatchException() throw() :
+    Exception("xplra::TypeMismatchException")
+{
+}
+
+//------------------------------------------------------------------------------
+//------------------------------------------------------------------------------
+
+inline InvalidIDException::InvalidIDException() throw() :
+    Exception("xplra::InvalidIDException")
 {
 }
Index: src/client/c/hu/varadiistvan/xplra/Makefile.am
===================================================================
--- src/client/c/hu/varadiistvan/xplra/Makefile.am	(revision 14)
+++ src/client/c/hu/varadiistvan/xplra/Makefile.am	(revision 19)
@@ -4,6 +4,8 @@
 
 libxplra_la_SOURCES=\
+        Exception.cc    \
         XPlane.cc       \
-        Exception.cc    \
+        MultiBuffer.cc  \
+        MultiGetter.cc  \
         xplra.cc
 
@@ -16,5 +18,7 @@
 include_xplradir=$(includedir)/hu/varadiistvan/xplra
 include_xplra_HEADERS=\
+        Exception.h     \
         XPlane.h        \
-        Exception.h     \
+        MultiBuffer.h   \
+        MultiGetter.h   \
         xplra.h
Index: src/client/c/hu/varadiistvan/xplra/MultiBuffer.cc
===================================================================
--- src/client/c/hu/varadiistvan/xplra/MultiBuffer.cc	(revision 19)
+++ src/client/c/hu/varadiistvan/xplra/MultiBuffer.cc	(revision 19)
@@ -0,0 +1,591 @@
+// Copyright (c) 2013 by István Váradi
+
+// This file is part of XPLRA, a remote-access plugin for X-Plane
+
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// The views and conclusions contained in the software and documentation are those
+// of the authors and should not be interpreted as representing official policies,
+// either expressed or implied, of the FreeBSD Project.
+
+//------------------------------------------------------------------------------
+
+#include "MultiBuffer.h"
+
+#include "XPlane.h"
+
+#include <xplra/Protocol.h>
+
+#include <hu/varadiistvan/scpl/io/DataStream.h>
+
+#include <cstring>
+#include <cstdio>
+
+//------------------------------------------------------------------------------
+
+using hu::varadiistvan::xplra::MultiBuffer;
+
+using xplra::Protocol;
+
+using std::string;
+using std::min;
+
+//------------------------------------------------------------------------------
+
+inline MultiBuffer::DataRef::DataRef(const string& name, uint8_t type,
+                                     const DataRef* previous,
+                                     size_t length, size_t offset) throw() :
+    name(name),
+    type(type),
+    length(length),
+    offset(offset),
+    dataOffset(0)
+{
+    if (previous!=0) {
+        size_t alignment = getAlignment();
+        dataOffset = previous->dataOffset + previous->getSize();
+        dataOffset += alignment - 1;
+        dataOffset &= ~(alignment-1);
+    }
+}
+
+//------------------------------------------------------------------------------
+
+inline size_t MultiBuffer::DataRef::getSize() const throw()
+{
+    switch (type) {
+      case Protocol::TYPE_INT:
+        return sizeof(int32_t);
+      case Protocol::TYPE_FLOAT:
+        return sizeof(float);
+      case Protocol::TYPE_DOUBLE:
+        return sizeof(double);
+      case Protocol::TYPE_FLOAT_ARRAY:
+        return sizeof(float) * length;
+      case Protocol::TYPE_INT_ARRAY:
+        return sizeof(int32_t) * length;
+      case Protocol::TYPE_BYTE_ARRAY:
+        return sizeof(uint8_t) * length;
+      default:
+        return 0;
+    }
+}
+
+//------------------------------------------------------------------------------
+
+inline size_t MultiBuffer::DataRef::getAlignment() const throw()
+{
+    switch (type) {
+      case Protocol::TYPE_INT:
+      case Protocol::TYPE_INT_ARRAY:
+        return sizeof(int32_t);
+      case Protocol::TYPE_FLOAT:
+      case Protocol::TYPE_FLOAT_ARRAY:
+        return sizeof(float);
+      case Protocol::TYPE_DOUBLE:
+        return sizeof(double);
+      case Protocol::TYPE_BYTE_ARRAY:
+      default:
+        return sizeof(uint8_t);
+    }
+}
+
+//------------------------------------------------------------------------------
+
+bool MultiBuffer::DataRef::isArray() const throw()
+{
+    return
+        type==Protocol::TYPE_FLOAT_ARRAY ||
+        type==Protocol::TYPE_INT_ARRAY ||
+        type==Protocol::TYPE_BYTE_ARRAY;
+}
+
+//------------------------------------------------------------------------------
+
+inline size_t
+MultiBuffer::DataRef::getCopyLength(size_t userLength, size_t userOffset)
+    const throw()
+{
+    size_t maxLength =
+        (userOffset>length) ? 0 : (length - userOffset);
+    return (userLength==0) ? maxLength : min(userLength, maxLength);
+}
+
+//------------------------------------------------------------------------------
+//------------------------------------------------------------------------------
+
+inline void MultiBuffer::unfinalize() throw()
+{
+    delete data; data = 0;
+}
+
+//------------------------------------------------------------------------------
+
+inline const MultiBuffer::DataRef* MultiBuffer::getLastDataRef() const throw()
+{
+    return dataRefs.empty() ? 0 : &(dataRefs.back());
+}
+
+//------------------------------------------------------------------------------
+
+inline const MultiBuffer::DataRef& MultiBuffer::getDataRef(size_t id, uint8_t type) const
+    throw(InvalidIDException, TypeMismatchException)
+{
+    if (id>=dataRefs.size()) throw InvalidIDException();
+    const DataRef& dataRef = dataRefs[id];
+    if (dataRef.type!=type) throw TypeMismatchException();
+    return dataRef;
+}
+
+//------------------------------------------------------------------------------
+
+inline void* MultiBuffer::getData(size_t id, uint8_t type)
+    throw(InvalidIDException, TypeMismatchException)
+{
+    const DataRef& dataRef = getDataRef(id, type);
+    finalize();
+    return data + dataRef.dataOffset;
+}
+
+//------------------------------------------------------------------------------
+
+inline const void* MultiBuffer::getData(size_t id, uint8_t type) const
+    throw(InvalidIDException, TypeMismatchException)
+{
+    const DataRef& dataRef = getDataRef(id, type);
+    finalize();
+    return data + dataRef.dataOffset;
+}
+
+//------------------------------------------------------------------------------
+
+template <typename T, uint8_t type> size_t
+inline MultiBuffer::setArray(size_t id, const T* value,
+                             size_t length, size_t offset)
+    throw(InvalidIDException, TypeMismatchException)
+{
+    const DataRef& dataRef = getDataRef(id, type);
+    finalize();
+
+    size_t toCopy = dataRef.getCopyLength(length, offset);
+
+    if (toCopy>0) {
+        memcpy( data + dataRef.dataOffset + offset * sizeof(T),
+                value, toCopy * sizeof(T) );
+    }
+
+    return toCopy;
+}
+
+//------------------------------------------------------------------------------
+
+template <typename T, uint8_t type>
+inline size_t MultiBuffer::getArray(size_t id, T* value,
+                                    size_t length, size_t offset) const
+    throw(InvalidIDException, TypeMismatchException)
+{
+    const DataRef& dataRef = getDataRef(id, type);
+    finalize();
+
+    size_t toCopy = dataRef.getCopyLength(length, offset);
+
+    if (toCopy>0) {
+        memcpy( value, data + dataRef.dataOffset + offset * sizeof(T),
+                toCopy * sizeof(T) );
+    }
+
+    return toCopy;
+}
+
+//------------------------------------------------------------------------------
+
+template <typename T, uint8_t type>
+inline const T* MultiBuffer::getArray(size_t id, size_t offset) const
+    throw(InvalidIDException, TypeMismatchException)
+{
+    const DataRef& dataRef = getDataRef(id, type);
+    finalize();
+    return (offset<dataRef.length) ?
+        reinterpret_cast<const T*>(data + dataRef.dataOffset +
+                                   offset * sizeof(T)) : 0;
+}
+
+//------------------------------------------------------------------------------
+
+MultiBuffer::MultiBuffer(XPlane& xplane) throw() :
+    data(0),
+    xplane(xplane),
+    unregisterCommand(0),
+    registeredID(-1)
+{
+}
+
+//------------------------------------------------------------------------------
+
+MultiBuffer::~MultiBuffer() throw(Exception)
+{
+    unregisterFromXPlane();
+    delete[] data;
+}
+
+//------------------------------------------------------------------------------
+
+size_t MultiBuffer::addInt(const string& name) throw()
+{
+    unfinalize();
+    dataRefs.push_back(DataRef(name, Protocol::TYPE_INT, getLastDataRef()));
+    return dataRefs.size() - 1;
+}
+
+//------------------------------------------------------------------------------
+
+size_t MultiBuffer::addFloat(const string& name) throw()
+{
+    unfinalize();
+    dataRefs.push_back(DataRef(name, Protocol::TYPE_FLOAT, getLastDataRef()));
+    return dataRefs.size() - 1;
+}
+
+//------------------------------------------------------------------------------
+
+size_t MultiBuffer::addDouble(const string& name) throw()
+{
+    unfinalize();
+    dataRefs.push_back(DataRef(name, Protocol::TYPE_DOUBLE, getLastDataRef()));
+    return dataRefs.size() - 1;
+}
+
+//------------------------------------------------------------------------------
+
+size_t MultiBuffer::addFloatArray(const string& name, size_t length,
+                                  size_t offset) throw()
+{
+    unfinalize();
+    dataRefs.push_back(DataRef(name, Protocol::TYPE_FLOAT_ARRAY,
+                               getLastDataRef(), length, offset));
+    return dataRefs.size() - 1;
+}
+
+//------------------------------------------------------------------------------
+
+size_t MultiBuffer::addIntArray(const string& name, size_t length,
+                                size_t offset) throw()
+{
+    unfinalize();
+    dataRefs.push_back(DataRef(name, Protocol::TYPE_INT_ARRAY,
+                               getLastDataRef(), length, offset));
+    return dataRefs.size() - 1;
+}
+
+//------------------------------------------------------------------------------
+
+size_t MultiBuffer::addByteArray(const string& name, size_t length,
+                                 size_t offset) throw()
+{
+    unfinalize();
+    dataRefs.push_back(DataRef(name, Protocol::TYPE_BYTE_ARRAY,
+                               getLastDataRef(), length, offset));
+    return dataRefs.size() - 1;
+}
+
+//------------------------------------------------------------------------------
+
+bool MultiBuffer::finalize() const
+{
+    if (data==0 && !dataRefs.empty()) {
+        const DataRef* lastDataRef = getLastDataRef();
+        size_t dataSize = lastDataRef->dataOffset + lastDataRef->getSize();
+        data = new unsigned char[dataSize];
+        memset(data, 0, dataSize);
+        return true;
+    } else {
+        return data!=0;
+    }
+}
+
+//------------------------------------------------------------------------------
+
+void MultiBuffer::registerInXPlane() throw(Exception)
+{
+    if (finalize() && registeredID<0) {
+        xplane.checkStream();
+
+        unregisterCommand = doRegister();
+
+        xplane.stream->flush();
+
+        uint8_t result = xplane.stream->readU8();
+        uint32_t id = xplane.stream->readU32();
+        xplane.checkStream();
+        xplane.checkResult(result);
+
+        registeredID = static_cast<int>(id);
+    }
+}
+
+//------------------------------------------------------------------------------
+
+void MultiBuffer::unregisterFromXPlane() throw(Exception)
+{
+    if (registeredID>=0) {
+        xplane.stream->writeU8(unregisterCommand);
+        xplane.stream->writeU32(static_cast<uint32_t>(registeredID));
+        xplane.checkResult();
+        registeredID = -1;
+    }
+}
+
+//------------------------------------------------------------------------------
+
+void MultiBuffer::execute() throw(Exception)
+{
+    if (data==0 && registeredID>=0) {
+        unregisterFromXPlane();
+        registerInXPlane();
+    } else {
+        finalize();
+    }
+
+    if (registeredID>=0) {
+        doExecute();
+    } else {
+        doExecuteUnregistered();
+    }
+}
+
+//------------------------------------------------------------------------------
+
+void MultiBuffer::setInt(size_t id, int value)
+    throw(InvalidIDException, TypeMismatchException)
+{
+    *reinterpret_cast<int32_t*>(getData(id, Protocol::TYPE_INT)) = value;
+}
+
+//------------------------------------------------------------------------------
+
+int MultiBuffer::getInt(size_t id) const
+    throw(InvalidIDException, TypeMismatchException)
+{
+    return *reinterpret_cast<const int32_t*>(getData(id, Protocol::TYPE_INT));
+}
+
+//------------------------------------------------------------------------------
+
+const int32_t& MultiBuffer::getIntRef(size_t id) const
+    throw(InvalidIDException, TypeMismatchException)
+{
+    return *reinterpret_cast<const int32_t*>(getData(id, Protocol::TYPE_INT));
+}
+
+//------------------------------------------------------------------------------
+
+int32_t& MultiBuffer::getIntRef(size_t id)
+    throw(InvalidIDException, TypeMismatchException)
+{
+    return *reinterpret_cast<int32_t*>(getData(id, Protocol::TYPE_INT));
+}
+
+//------------------------------------------------------------------------------
+
+void MultiBuffer::setFloat(size_t id, float value)
+    throw(InvalidIDException, TypeMismatchException)
+{
+    *reinterpret_cast<float*>(getData(id, Protocol::TYPE_FLOAT)) = value;
+}
+
+//------------------------------------------------------------------------------
+
+float MultiBuffer::getFloat(size_t id) const
+    throw(InvalidIDException, TypeMismatchException)
+{
+    return *reinterpret_cast<const float*>(getData(id, Protocol::TYPE_FLOAT));
+}
+
+//------------------------------------------------------------------------------
+
+const float& MultiBuffer::getFloatRef(size_t id) const
+    throw(InvalidIDException, TypeMismatchException)
+{
+    return *reinterpret_cast<const float*>(getData(id, Protocol::TYPE_FLOAT));
+}
+
+//------------------------------------------------------------------------------
+
+float& MultiBuffer::getFloatRef(size_t id)
+    throw(InvalidIDException, TypeMismatchException)
+{
+    return *reinterpret_cast<float*>(getData(id, Protocol::TYPE_FLOAT));
+}
+
+//------------------------------------------------------------------------------
+
+void MultiBuffer::setDouble(size_t id, double value)
+    throw(InvalidIDException, TypeMismatchException)
+{
+    *reinterpret_cast<double*>(getData(id, Protocol::TYPE_DOUBLE)) = value;
+}
+
+//------------------------------------------------------------------------------
+
+double MultiBuffer::getDouble(size_t id) const
+    throw(InvalidIDException, TypeMismatchException)
+{
+    return *reinterpret_cast<const double*>(getData(id, Protocol::TYPE_DOUBLE));
+}
+
+//------------------------------------------------------------------------------
+
+const double& MultiBuffer::getDoubleRef(size_t id) const
+    throw(InvalidIDException, TypeMismatchException)
+{
+    return *reinterpret_cast<const double*>(getData(id, Protocol::TYPE_DOUBLE));
+}
+
+//------------------------------------------------------------------------------
+
+double& MultiBuffer::getDoubleRef(size_t id)
+    throw(InvalidIDException, TypeMismatchException)
+{
+    return *reinterpret_cast<double*>(getData(id, Protocol::TYPE_DOUBLE));
+}
+
+//------------------------------------------------------------------------------
+
+size_t MultiBuffer::setFloatArray(size_t id, const float* value,
+                                  size_t length, size_t offset)
+    throw(InvalidIDException, TypeMismatchException)
+{
+    return setArray<float, Protocol::TYPE_FLOAT_ARRAY>(id, value, length, offset);
+}
+
+//------------------------------------------------------------------------------
+
+size_t MultiBuffer::getFloatArray(size_t id, float* value,
+                                  size_t length, size_t offset) const
+    throw(InvalidIDException, TypeMismatchException)
+{
+    return getArray<float, Protocol::TYPE_FLOAT_ARRAY>(id, value, length, offset);
+}
+
+//------------------------------------------------------------------------------
+
+const float* MultiBuffer::getFloatArray(size_t id, size_t offset) const
+    throw(InvalidIDException, TypeMismatchException)
+{
+    return getArray<float, Protocol::TYPE_FLOAT_ARRAY>(id, offset);
+}
+
+//------------------------------------------------------------------------------
+
+size_t MultiBuffer::setIntArray(size_t id, const int32_t* value,
+                                size_t length, size_t offset)
+    throw(InvalidIDException, TypeMismatchException)
+{
+    return setArray<int32_t, Protocol::TYPE_INT_ARRAY>(id, value, length, offset);
+}
+
+//------------------------------------------------------------------------------
+
+size_t MultiBuffer::getIntArray(size_t id, int32_t* value,
+                                size_t length, size_t offset) const
+    throw(InvalidIDException, TypeMismatchException)
+{
+    return getArray<int32_t, Protocol::TYPE_INT_ARRAY>(id, value, length, offset);
+}
+
+//------------------------------------------------------------------------------
+
+const int32_t* MultiBuffer::getIntArray(size_t id, size_t offset) const
+    throw(InvalidIDException, TypeMismatchException)
+{
+    return getArray<int32_t, Protocol::TYPE_INT_ARRAY>(id, offset);
+}
+
+//------------------------------------------------------------------------------
+
+size_t MultiBuffer::setByteArray(size_t id, const uint8_t* value,
+                                size_t length, size_t offset)
+    throw(InvalidIDException, TypeMismatchException)
+{
+    return setArray<uint8_t, Protocol::TYPE_BYTE_ARRAY>(id, value, length, offset);
+}
+
+//------------------------------------------------------------------------------
+
+size_t MultiBuffer::getByteArray(size_t id, uint8_t* value,
+                                size_t length, size_t offset) const
+    throw(InvalidIDException, TypeMismatchException)
+{
+    return getArray<uint8_t, Protocol::TYPE_BYTE_ARRAY>(id, value, length, offset);
+}
+
+//------------------------------------------------------------------------------
+
+const uint8_t* MultiBuffer::getByteArray(size_t id, size_t offset) const
+    throw(InvalidIDException, TypeMismatchException)
+{
+    return getArray<uint8_t, Protocol::TYPE_BYTE_ARRAY>(id, offset);
+}
+
+//------------------------------------------------------------------------------
+
+size_t MultiBuffer::setString(size_t id, const string& value,
+                              size_t offset)
+    throw(InvalidIDException, TypeMismatchException)
+{
+    return setByteArray(id, reinterpret_cast<const uint8_t*>(value.c_str()),
+                        value.length(), offset);
+}
+
+//------------------------------------------------------------------------------
+
+string MultiBuffer::getString(size_t id, size_t offset) const
+    throw(InvalidIDException, TypeMismatchException)
+{
+    const DataRef& dataRef = getDataRef(id, Protocol::TYPE_BYTE_ARRAY);
+    finalize();
+
+    size_t toCopy = dataRef.getCopyLength(0, offset);
+    const uint8_t* str = data + dataRef.dataOffset + offset;
+
+    size_t length = 0;
+    if (toCopy!=0) {
+        while(length<toCopy && str[length]!=0) ++length;
+    }
+
+    return string(reinterpret_cast<const char*>(str), length);
+}
+
+//------------------------------------------------------------------------------
+
+const char* MultiBuffer::getStringPtr(size_t id, size_t offset) const
+        throw(InvalidIDException, TypeMismatchException)
+{
+    return reinterpret_cast<const char*>(getByteArray(id, offset));
+}
+
+//------------------------------------------------------------------------------
+
+// Local Variables:
+// mode: C++
+// c-basic-offset: 4
+// indent-tabs-mode: nil
+// End:
Index: src/client/c/hu/varadiistvan/xplra/MultiBuffer.h
===================================================================
--- src/client/c/hu/varadiistvan/xplra/MultiBuffer.h	(revision 19)
+++ src/client/c/hu/varadiistvan/xplra/MultiBuffer.h	(revision 19)
@@ -0,0 +1,539 @@
+// Copyright (c) 2013 by István Váradi
+
+// This file is part of XPLRA, a remote-access plugin for X-Plane
+
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// The views and conclusions contained in the software and documentation are those
+// of the authors and should not be interpreted as representing official policies,
+// either expressed or implied, of the FreeBSD Project.
+
+#ifndef HU_VARADIISTVAN_XPLRA_MULTIBUFFER_H
+#define HU_VARADIISTVAN_XPLRA_MULTIBUFFER_H
+//------------------------------------------------------------------------------
+
+#include "Exception.h"
+
+#include <string>
+#include <vector>
+
+#include <inttypes.h>
+
+//------------------------------------------------------------------------------
+
+namespace hu { namespace varadiistvan { namespace xplra {
+
+//------------------------------------------------------------------------------
+
+class XPlane;
+
+//------------------------------------------------------------------------------
+
+/**
+ * Base class for the multi-get and multi-set handlers. It contains
+ * the meta-data of the datarefs as well as a buffer for storing the
+ * data.
+ */
+class MultiBuffer
+{
+protected:
+    /**
+     * Information about a dataref.
+     */
+    class DataRef
+    {
+    public:
+        /**
+         * The name.
+         */
+        std::string name;
+
+        /**
+         * The type (the protocol constant).
+         */
+        uint8_t type;
+
+        /**
+         * The length in case of array types.
+         */
+        size_t length;
+
+        /**
+         * The offset in case of array types.
+         */
+        size_t offset;
+
+        /**
+         * The offset within the data buffer.
+         */
+        size_t dataOffset;
+
+        /**
+         * Construct the dataref with the given data.
+         */
+        DataRef(const std::string& name, uint8_t type,
+                const DataRef* previous = 0,
+                size_t length = 0, size_t offset = 0) throw();
+
+        /**
+         * Get the size of the data represented by this object.
+         */
+        size_t getSize() const throw();
+
+        /**
+         * Get the alignment requirement of the data represented by
+         * this dataref.
+         */
+        size_t getAlignment() const throw();
+
+        /**
+         * Determine if the dataref is an array.
+         */
+        bool isArray() const throw();
+
+        /**
+         * Get the length of the data the can be copied for the given
+         * user parameters.
+         */
+        size_t getCopyLength(size_t userLength, size_t userOffset) const throw();
+    };
+
+    /**
+     * The datarefs.
+     */
+    std::vector<DataRef> dataRefs;
+
+    /**
+     * The data buffer.
+     */
+    mutable unsigned char* data;
+
+    /**
+     * The XPlane object this buffer belongs to.
+     */
+    XPlane& xplane;
+
+    /**
+     * The command to unregister the buffer.
+     */
+    uint8_t unregisterCommand;
+
+    /**
+     * The ID with which this buffer is registered in the plugin. If
+     * negative, the buffer is not registerd yet.
+     */
+    int registeredID;
+
+protected:
+    /**
+     * Construct an empty buffer for the given XPlane instance.
+     */
+    MultiBuffer(XPlane& xplane) throw();
+
+public:
+    /**
+     * Destroy the buffer. If it is registered, an attempt will be
+     * made to unregister it.
+     */
+    virtual ~MultiBuffer() throw(Exception);
+
+    /**
+     * Add an integer dataref.
+     *
+     * @return the ID if the dataref, that can be used later to set or
+     * get the value.
+     */
+    size_t addInt(const std::string& name) throw();
+
+    /**
+     * Add a float dataref.
+     *
+     * @return the ID if the dataref, that can be used later to set or
+     * get the value.
+     */
+    size_t addFloat(const std::string& name) throw();
+
+    /**
+     * Add a double dataref.
+     *
+     * @return the ID if the dataref, that can be used later to set or
+     * get the value.
+     */
+    size_t addDouble(const std::string& name) throw();
+
+    /**
+     * Add a float array dataref.
+     *
+     * @return the ID if the dataref, that can be used later to set or
+     * get the value.
+     */
+    size_t addFloatArray(const std::string& name, size_t length,
+                         size_t offset = 0) throw();
+
+    /**
+     * Add an integer array dataref.
+     *
+     * @return the ID if the dataref, that can be used later to set or
+     * get the value.
+     */
+    size_t addIntArray(const std::string& name, size_t length,
+                       size_t offset = 0) throw();
+
+    /**
+     * Add a byte array dataref.
+     *
+     * @return the ID if the dataref, that can be used later to set or
+     * get the value.
+     */
+    size_t addByteArray(const std::string& name, size_t length,
+                        size_t offset = 0) throw();
+
+    /**
+     * Finalize the buffer, if not finalized yet.
+     *
+     * @return if there is any data in it
+     */
+    bool finalize() const;
+
+    /**
+     * Register this buffer in X-Plane the for either getting or
+     * setting, depending on the actual subclass. It will be finalized
+     * too.
+     */
+    void registerInXPlane() throw(Exception);
+
+    /**
+     * Unregister this buffer from X-Plane.
+     */
+    void unregisterFromXPlane() throw(Exception);
+
+    /**
+     * Execute the buffer by either reading or writing it. If the
+     * buffer is not finalized, it will be finalized. If it is not
+     * finalized, but registered, the registration will be cleared,
+     * and it will be re-registered after finalizing.
+     */
+    void execute() throw(Exception);
+
+    /**
+     * Set the value of an integer dataref with the given ID.
+     */
+    void setInt(size_t id, int value)
+        throw(InvalidIDException, TypeMismatchException);
+
+    /**
+     * Get the value of an integer dataref with the given ID.
+     */
+    int getInt(size_t id) const
+        throw(InvalidIDException, TypeMismatchException);
+
+    /**
+     * Get a reference to the integer dataref with the given ID
+     */
+    const int32_t& getIntRef(size_t id) const
+        throw(InvalidIDException, TypeMismatchException);
+
+    /**
+     * Get a reference to the integer dataref with the given ID
+     */
+    int32_t& getIntRef(size_t id)
+        throw(InvalidIDException, TypeMismatchException);
+
+    /**
+     * Set the value of a float dataref with the given ID.
+     */
+    void setFloat(size_t id, float value)
+        throw(InvalidIDException, TypeMismatchException);
+
+    /**
+     * Get the value of a float dataref with the given ID.
+     */
+    float getFloat(size_t id) const
+        throw(InvalidIDException, TypeMismatchException);
+
+    /**
+     * Get a reference to the float dataref with the given ID
+     */
+    const float& getFloatRef(size_t id) const
+        throw(InvalidIDException, TypeMismatchException);
+
+    /**
+     * Get a reference to the integer dataref with the given ID
+     */
+    float& getFloatRef(size_t id)
+        throw(InvalidIDException, TypeMismatchException);
+
+    /**
+     * Set the value of a double dataref with the given ID.
+     */
+    void setDouble(size_t id, double value)
+        throw(InvalidIDException, TypeMismatchException);
+
+    /**
+     * Get the value of a double dataref with the given ID.
+     */
+    double getDouble(size_t id) const
+        throw(InvalidIDException, TypeMismatchException);
+
+    /**
+     * Get a reference to the double dataref with the given ID
+     */
+    const double& getDoubleRef(size_t id) const
+        throw(InvalidIDException, TypeMismatchException);
+
+    /**
+     * Get a reference to the double dataref with the given ID
+     */
+    double& getDoubleRef(size_t id)
+        throw(InvalidIDException, TypeMismatchException);
+
+    /**
+     * Set the value of the float array dataref with the given ID.
+     *
+     * @param length the amount of data, i.e. you can set only a part
+     * of the data in the buffer. If 0, it is assumed to be the length
+     * of the data in the buffer minus the offset (see below).
+     *
+     * @param offset the offset within the buffer to set the data from
+     *
+     * @return the number of data items set
+     */
+    size_t setFloatArray(size_t id, const float* value,
+                         size_t length = 0, size_t offset = 0)
+        throw(InvalidIDException, TypeMismatchException);
+
+    /**
+     * Get the value of the float array dataref with the given ID
+     *
+     * @param length the size of the buffer. If 0, it is assumed to be
+     * the length of the data in the buffer minus the offset (see
+     * below).
+     *
+     * @param offset the offset within the buffer to get the data from
+     *
+     * @return the number of data items retrieved.
+     */
+    size_t getFloatArray(size_t id, float* value,
+                         size_t length = 0, size_t offset = 0) const
+        throw(InvalidIDException, TypeMismatchException);
+
+    /**
+     * Get the array of floating point values as a directly readable
+     * buffer.
+     *
+     * @param offset the offset within the buffer. If it is not less
+     * than the length of the buffer, 0 is returned.
+     */
+    const float* getFloatArray(size_t id, size_t offset = 0) const
+        throw(InvalidIDException, TypeMismatchException);
+
+    /**
+     * Set the value of the integer array dataref with the given ID.
+     *
+     * @param length the amount of data, i.e. you can set only a part
+     * of the data in the buffer. If 0, it is assumed to be the length
+     * of the data in the buffer minus the offset (see below).
+     *
+     * @param offset the offset within the buffer to set the data from
+     *
+     * @return the number of data items set
+     */
+    size_t setIntArray(size_t id, const int32_t* value,
+                       size_t length = 0, size_t offset = 0)
+        throw(InvalidIDException, TypeMismatchException);
+
+    /**
+     * Get the value of the integer array dataref with the given ID
+     *
+     * @param length the size of the buffer. If 0, it is assumed to be
+     * the length of the data in the buffer minus the offset (see
+     * below).
+     *
+     * @param offset the offset within the buffer to get the data from
+     *
+     * @return the number of data items retrieved.
+     */
+    size_t getIntArray(size_t id, int32_t* value,
+                       size_t length = 0, size_t offset = 0) const
+        throw(InvalidIDException, TypeMismatchException);
+
+    /**
+     * Get the array of integer values as a directly readable buffer.
+     *
+     * @param offset the offset within the buffer. If it is not less
+     * than the length of the buffer, 0 is returned.
+     */
+    const int32_t* getIntArray(size_t id, size_t offset = 0) const
+        throw(InvalidIDException, TypeMismatchException);
+
+
+    /**
+     * Set the value of the byte array dataref with the given ID.
+     *
+     * @param length the amount of data, i.e. you can set only a part
+     * of the data in the buffer. If 0, it is assumed to be the length
+     * of the data in the buffer minus the offset (see below).
+     *
+     * @param offset the offset within the buffer to set the data from
+     *
+     * @return the number of data items set
+     */
+    size_t setByteArray(size_t id, const uint8_t* value,
+                        size_t length = 0, size_t offset = 0)
+        throw(InvalidIDException, TypeMismatchException);
+
+    /**
+     * Get the value of the byte array dataref with the given ID
+     *
+     * @param length the size of the buffer. If 0, it is assumed to be
+     * the length of the data in the buffer minus the offset (see
+     * below).
+     *
+     * @param offset the offset within the buffer to get the data from
+     *
+     * @return the number of data items retrieved.
+     */
+    size_t getByteArray(size_t id, uint8_t* value,
+                       size_t length = 0, size_t offset = 0) const
+        throw(InvalidIDException, TypeMismatchException);
+
+    /**
+     * Get the array of byte values as a directly readable buffer.
+     *
+     * @param offset the offset within the buffer. If it is not less
+     * than the length of the buffer, 0 is returned.
+     */
+    const uint8_t* getByteArray(size_t id, size_t offset = 0) const
+        throw(InvalidIDException, TypeMismatchException);
+
+
+    /**
+     * Set the value of the byte array dataref with the given ID
+     * from the given string. If the string contains less characters
+     * than the array's length, the rest will be filled with 0s.
+     *
+     * @return the number of bytes set
+     */
+    size_t setString(size_t id, const std::string& value,
+                     size_t offset = 0)
+        throw(InvalidIDException, TypeMismatchException);
+
+    /**
+     * Get the value of the byte array dataref with the given ID as
+     * a string.
+     */
+    std::string getString(size_t id, size_t offset = 0) const
+        throw(InvalidIDException, TypeMismatchException);
+
+    /**
+     * Get a string pointer to the value of the byte array dataref
+     * with the given ID.
+     */
+    const char* getStringPtr(size_t id, size_t offset = 0) const
+        throw(InvalidIDException, TypeMismatchException);
+
+protected:
+    /**
+     * Perform the first part of the registration depending on the
+     * actual type.
+     */
+    virtual uint8_t doRegister() throw(Exception) = 0;
+
+    /**
+     * Perform the main part of the execution if the buffer is
+     * registered.
+     */
+    virtual void doExecute() throw(Exception) = 0;
+
+    /**
+     * Perform the main part of the execution if the buffer is
+     * not registered.
+     */
+    virtual void doExecuteUnregistered() throw(Exception) = 0;
+
+private:
+    /**
+     * Unfinalize the buffer, if it is finalized.
+     */
+    void unfinalize() throw();
+
+    /**
+     * Get the last dataref, or 0 if there are not datarefs yet.
+     */
+    const DataRef* getLastDataRef() const throw();
+
+    /**
+     * Get the dataref with the given ID if it is of the given
+     * type. Otherwise throw an exception.
+     */
+    const DataRef& getDataRef(size_t id, uint8_t type) const
+        throw(InvalidIDException, TypeMismatchException);
+
+    /**
+     * Get the data area belonging to the given dataref it is of the
+     * given type. If the buffer is not finalized yet, it will be.
+     */
+    void* getData(size_t id, uint8_t type)
+        throw(InvalidIDException, TypeMismatchException);
+
+    /**
+     * Get the data area belonging to the given dataref it is of the
+     * given type. If the buffer is not finalized yet, it will be.
+     */
+    const void* getData(size_t id, uint8_t type) const
+        throw(InvalidIDException, TypeMismatchException);
+
+    /**
+     * Template function for setting an array of a certain type.
+     */
+    template <typename T, uint8_t type> size_t
+    setArray(size_t id, const T* value, size_t length, size_t offset)
+        throw(InvalidIDException, TypeMismatchException);
+
+    /**
+     * Template function for getting the elements of an array of a
+     * certain type.
+     */
+    template <typename T, uint8_t type> size_t
+    getArray(size_t id, T* value, size_t length, size_t offset) const
+        throw(InvalidIDException, TypeMismatchException);
+
+    /**
+     * Template function for getting the elements of an array of a
+     * certain type.
+     */
+    template <typename T, uint8_t type> const T*
+    getArray(size_t id, size_t offset) const
+        throw(InvalidIDException, TypeMismatchException);
+};
+
+//------------------------------------------------------------------------------
+
+} /* namespace hu::varadiistvan::xplra */ } /* namespace hu::varadiistvan */ } /* namespace hu */
+
+//------------------------------------------------------------------------------
+#endif // HU_VARADIISTVAN_XPLRA_MULTIBUFFER_H
+
+// Local Variables:
+// mode: C++
+// c-basic-offset: 4
+// indent-tabs-mode: nil
+// End:
Index: src/client/c/hu/varadiistvan/xplra/MultiGetter.cc
===================================================================
--- src/client/c/hu/varadiistvan/xplra/MultiGetter.cc	(revision 19)
+++ src/client/c/hu/varadiistvan/xplra/MultiGetter.cc	(revision 19)
@@ -0,0 +1,152 @@
+// Copyright (c) 2013 by István Váradi
+
+// This file is part of XPLRA, a remote-access plugin for X-Plane
+
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// The views and conclusions contained in the software and documentation are those
+// of the authors and should not be interpreted as representing official policies,
+// either expressed or implied, of the FreeBSD Project.
+
+//------------------------------------------------------------------------------
+
+#include "MultiGetter.h"
+
+#include "XPlane.h"
+
+#include <xplra/Protocol.h>
+
+#include <hu/varadiistvan/scpl/io/DataStream.h>
+
+#include <cstdio>
+
+//------------------------------------------------------------------------------
+
+using hu::varadiistvan::xplra::MultiGetter;
+
+using xplra::Protocol;
+
+using std::vector;
+using std::min;
+
+//------------------------------------------------------------------------------
+
+inline void MultiGetter::read(const DataRef& dataRef) throw(Exception)
+{
+    switch (dataRef.type) {
+      case Protocol::TYPE_INT:
+        *reinterpret_cast<int32_t*>(data + dataRef.dataOffset) =
+            xplane.stream->readS32();
+        break;
+      case Protocol::TYPE_FLOAT:
+        *reinterpret_cast<float*>(data + dataRef.dataOffset) =
+            xplane.stream->readFloat();
+        break;
+      case Protocol::TYPE_DOUBLE:
+        *reinterpret_cast<double*>(data + dataRef.dataOffset) =
+            xplane.stream->readDouble();
+        break;
+      case Protocol::TYPE_FLOAT_ARRAY:
+      case Protocol::TYPE_INT_ARRAY:
+      case Protocol::TYPE_BYTE_ARRAY:
+      {
+          size_t length = xplane.stream->readS32();
+          xplane.checkStream();
+          length = min(length, dataRef.length);
+          size_t elementSize =
+              (dataRef.type==Protocol::TYPE_FLOAT_ARRAY) ? sizeof(float) :
+              ((dataRef.type==Protocol::TYPE_INT_ARRAY) ?
+               sizeof(int32_t) : sizeof(uint8_t));
+          xplane.stream->read(data + dataRef.dataOffset, length * elementSize);
+      }
+    }
+}
+
+//------------------------------------------------------------------------------
+
+uint8_t MultiGetter::doRegister() throw(Exception)
+{
+    xplane.stream->writeU8(Protocol::COMMAND_REGISTER_GET_MULTI);
+    xplane.stream->writeU32(dataRefs.size());
+
+    for(vector<DataRef>::const_iterator i = dataRefs.begin();
+        i!=dataRefs.end(); ++i)
+    {
+        const DataRef& dataRef = *i;
+        xplane.stream->writeString(dataRef.name);
+        xplane.stream->writeU8(dataRef.type);
+        if (dataRef.isArray()) {
+            xplane.stream->writeS32(dataRef.length);
+            xplane.stream->writeS32(dataRef.offset);
+        }
+    }
+
+    return Protocol::COMMAND_UNREGISTER_GET_MULTI;
+}
+
+//------------------------------------------------------------------------------
+
+void MultiGetter::doExecute() throw(Exception)
+{
+    xplane.checkStream();
+    xplane.stream->writeU8(Protocol::COMMAND_EXECUTE_GET_MULTI);
+    xplane.stream->writeU32(registeredID);
+    xplane.stream->flush();
+    xplane.checkResult();
+
+    for(vector<DataRef>::const_iterator i = dataRefs.begin();
+        i!=dataRefs.end(); ++i)
+    {
+        xplane.checkStream();
+        const DataRef& dataRef = *i;
+        read(dataRef);
+    }
+}
+
+
+//------------------------------------------------------------------------------
+
+void MultiGetter::doExecuteUnregistered() throw(Exception)
+{
+    for(vector<DataRef>::const_iterator i = dataRefs.begin();
+        i!=dataRefs.end(); ++i)
+    {
+        const DataRef& dataRef = *i;
+        xplane.stream->writeU8(Protocol::COMMAND_GET_SINGLE);
+        xplane.stream->writeString(dataRef.name);
+        xplane.stream->writeU8(dataRef.type);
+        if (dataRef.isArray()) {
+            xplane.stream->writeS32(dataRef.length);
+            xplane.stream->writeS32(dataRef.offset);
+        }
+        xplane.stream->flush();
+        xplane.checkResult();
+        read(dataRef);
+    }
+}
+
+//------------------------------------------------------------------------------
+
+// Local Variables:
+// mode: C++
+// c-basic-offset: 4
+// indent-tabs-mode: nil
+// End:
Index: src/client/c/hu/varadiistvan/xplra/MultiGetter.h
===================================================================
--- src/client/c/hu/varadiistvan/xplra/MultiGetter.h	(revision 19)
+++ src/client/c/hu/varadiistvan/xplra/MultiGetter.h	(revision 19)
@@ -0,0 +1,96 @@
+// Copyright (c) 2013 by István Váradi
+
+// This file is part of XPLRA, a remote-access plugin for X-Plane
+
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// The views and conclusions contained in the software and documentation are those
+// of the authors and should not be interpreted as representing official policies,
+// either expressed or implied, of the FreeBSD Project.
+
+#ifndef HU_VARADIISTVAN_XPLRA_MULTIGETTER_H
+#define HU_VARADIISTVAN_XPLRA_MULTIGETTER_H
+//------------------------------------------------------------------------------
+
+#include "MultiBuffer.h"
+
+//------------------------------------------------------------------------------
+
+namespace hu { namespace varadiistvan { namespace xplra {
+
+//------------------------------------------------------------------------------
+
+/**
+ * A multi-dataref buffer used to query those data.
+ */
+class MultiGetter : public MultiBuffer
+{
+public:
+    /**
+     * Construct the object for the given XPlane instance.
+     */
+    MultiGetter(XPlane& xplane) throw();
+
+protected:
+    /**
+     * @see MultiBuffer::doRegister
+     */
+    virtual uint8_t doRegister() throw(Exception);
+
+    /**
+     * @see MultiBuffer::doExecute
+     */
+    virtual void doExecute() throw(Exception);
+
+    /**
+     * @see MultiBuffer::doExecuteUnregistered
+     */
+    virtual void doExecuteUnregistered() throw(Exception);
+
+private:
+    /**
+     * Read the value of the given dataref from the stream into the
+     * buffer.
+     */
+    void read(const DataRef& dataRef) throw(Exception);
+};
+
+//------------------------------------------------------------------------------
+// Inline definitions
+//------------------------------------------------------------------------------
+
+inline MultiGetter::MultiGetter(XPlane& xplane) throw() :
+    MultiBuffer(xplane)
+{
+}
+
+//------------------------------------------------------------------------------
+
+} /* namespace hu::varadiistvan::xplra */ } /* namespace hu::varadiistvan */ } /* namespace hu */
+
+//------------------------------------------------------------------------------
+#endif // HU_VARADIISTVAN_XPLRA_MULTIGETTER_H
+
+// Local Variables:
+// mode: C++
+// c-basic-offset: 4
+// indent-tabs-mode: nil
+// End:
Index: src/client/c/hu/varadiistvan/xplra/XPlane.cc
===================================================================
--- src/client/c/hu/varadiistvan/xplra/XPlane.cc	(revision 18)
+++ src/client/c/hu/varadiistvan/xplra/XPlane.cc	(revision 19)
@@ -53,5 +53,5 @@
 //------------------------------------------------------------------------------
 
-inline void XPlane::checkStream() throw(NotConnectedException, IOException)
+void XPlane::checkStream() throw(NotConnectedException, IOException)
 {
     if (stream==0) {
@@ -91,5 +91,5 @@
 //------------------------------------------------------------------------------
 
-inline void XPlane::checkResult() throw(ProtocolException)
+void XPlane::checkResult() throw(ProtocolException)
 {
     uint8_t result = stream->readU8();
Index: src/client/c/hu/varadiistvan/xplra/XPlane.h
===================================================================
--- src/client/c/hu/varadiistvan/xplra/XPlane.h	(revision 18)
+++ src/client/c/hu/varadiistvan/xplra/XPlane.h	(revision 19)
@@ -270,4 +270,8 @@
     void setArray(const char* name, uint8_t type, size_t length,
                   size_t offset) throw(Exception);
+
+    friend class MultiBuffer;
+    friend class MultiGetter;
+    friend class MultiSetter;
 };
 
Index: test/Makefile.am
===================================================================
--- test/Makefile.am	(revision 14)
+++ test/Makefile.am	(revision 19)
@@ -6,5 +6,5 @@
 endif
 
-noinst_PROGRAMS=ctest basictest
+noinst_PROGRAMS=ctest basictest multigettest
 
 ctest_SOURCES=ctest.c
@@ -14,2 +14,4 @@
 
 basictest_SOURCES=basictest.cc
+
+multigettest_SOURCES=multigettest.cc
Index: test/multigettest.cc
===================================================================
--- test/multigettest.cc	(revision 19)
+++ test/multigettest.cc	(revision 19)
@@ -0,0 +1,181 @@
+// Copyright (c) 2013 by István Váradi
+
+// This file is part of XPLRA, a remote-access plugin for X-Plane
+
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// The views and conclusions contained in the software and documentation are those
+// of the authors and should not be interpreted as representing official policies,
+// either expressed or implied, of the FreeBSD Project.
+
+//------------------------------------------------------------------------------
+
+#include <hu/varadiistvan/xplra/XPlane.h>
+#include <hu/varadiistvan/xplra/MultiGetter.h>
+
+#include <hu/varadiistvan/scpl/Thread.h>
+
+#include <cstdio>
+#include <cstring>
+
+//------------------------------------------------------------------------------
+
+using hu::varadiistvan::xplra::XPlane;
+using hu::varadiistvan::xplra::MultiGetter;
+using hu::varadiistvan::xplra::Exception;
+
+using hu::varadiistvan::scpl::Thread;
+
+//------------------------------------------------------------------------------
+
+int main(int argc, char* argv[])
+{
+    bool dontregister = false;
+    for(int i = 1; i<argc; ++i) {
+        if (strcmp(argv[i], "dontregister")) {
+            dontregister = true;
+        }
+    }
+
+    try {
+        XPlane xplane;
+
+        printf("Connecting to X-Plane...\n");
+        xplane.connect();
+        printf("Connected to X-Plane.\n\n");
+
+        MultiGetter getter(xplane);
+
+        const size_t pausedID = getter.addInt("sim/time/paused");
+        const size_t latitudeID = getter.addDouble("sim/flightmodel/position/latitude");
+        const size_t longitudeID = getter.addDouble("sim/flightmodel/position/longitude");
+        const size_t replayID = getter.addInt("sim/operation/prefs/replay_mode");
+        const size_t overspeedID = getter.addInt("sim/flightmodel/failures/over_vne");
+        const size_t stalledID = getter.addInt("sim/flightmodel/failures/stallwarning");
+        const size_t onTheGroundID = getter.addInt("sim/flightmodel/failures/onground_any");
+        const size_t emptyWeightID = getter.addFloat("sim/aircraft/weight/acf_m_empty");
+        const size_t payloadWeightID = getter.addFloat("sim/flightmodel/weight/m_fixed");
+        const size_t grossWeightID = getter.addFloat("sim/flightmodel/weight/m_total");
+        const size_t headingID = getter.addFloat("sim/flightmodel/position/psi");
+        const size_t pitchID = getter.addFloat("sim/flightmodel/position/theta");
+        const size_t bankID = getter.addFloat("sim/flightmodel/position/phi");
+        const size_t iasID = getter.addFloat("sim/flightmodel/position/indicated_airspeed2");
+        const size_t machID = getter.addFloat("sim/flightmodel/misc/machno");
+        const size_t groundSpeedID = getter.addFloat("sim/flightmodel/position/groundspeed");
+        const size_t vsID = getter.addFloat("sim/flightmodel/position/vh_ind_fpm2");
+        const size_t radioAltitudeID = getter.addFloat("sim/flightmodel/position/y_agl");
+        const size_t altitudeID = getter.addFloat("sim/flightmodel/position/elevation");
+        const size_t gLoadID = getter.addFloat("sim/flightmodel/forces/g_nrml");
+        const size_t flapsControlID = getter.addFloat("sim/flightmodel/controls/flaprqst");
+        const size_t flapsLeftID = getter.addFloat("sim/flightmodel/controls/flaprat");
+        const size_t flapsRightID = getter.addFloat("sim/flightmodel/controls/flap2rat");
+        const size_t navLightsID = getter.addInt("sim/cockpit/electrical/nav_lights_on");
+        const size_t beaconLightsID = getter.addInt("sim/cockpit/electrical/beacon_lights_on");
+        const size_t strobeLightsID = getter.addInt("sim/cockpit/electrical/strobe_lights_on");
+        const size_t landingLightsID = getter.addInt("sim/cockpit/electrical/landing_lights_on");
+        const size_t pitotID = getter.addInt("sim/cockpit/switches/pitot_heat_on");
+        const size_t parkingID = getter.addFloat("sim/flightmodel/controls/parkbrake");
+        //const size_t gearControlID = getter.addInt("sim/cockpit/switches/gear_handle_status");
+        const size_t gearControlID = getter.addInt("sim/cockpit2/controls/gear_handle_down");
+        const size_t noseGearID = getter.addFloatArray("sim/flightmodel2/gear/deploy_ratio", 1);
+        const size_t altimeterID = getter.addFloat("sim/cockpit/misc/barometer_setting");
+        const size_t qnhID = getter.addFloat("sim/flightmodel/misc/Qstatic");
+
+        if (dontregister) {
+            getter.finalize();
+        } else {
+            printf("Registering getter\n\n");
+            getter.registerInXPlane();
+        }
+
+        const int32_t& paused = getter.getIntRef(pausedID);
+        const double& latitude = getter.getDoubleRef(latitudeID);
+        const double& longitude = getter.getDoubleRef(longitudeID);
+        // frozen
+        // slew
+        const int32_t& replay = getter.getIntRef(replayID);
+        const int32_t& overspeed = getter.getIntRef(overspeedID);
+        const int32_t& stalled = getter.getIntRef(stalledID);
+        const int32_t& onTheGround = getter.getIntRef(onTheGroundID);
+        const float& emptyWeight = getter.getFloatRef(emptyWeightID);
+        const float& payloadWeight = getter.getFloatRef(payloadWeightID);
+        const float& grossWeight = getter.getFloatRef(grossWeightID);
+        const float& heading = getter.getFloatRef(headingID);
+        const float& pitch = getter.getFloatRef(pitchID);
+        const float& bank = getter.getFloatRef(bankID);
+        const float& ias = getter.getFloatRef(iasID);
+        const float& mach = getter.getFloatRef(machID);
+        const float& groundSpeed = getter.getFloatRef(groundSpeedID);
+        const float& vs = getter.getFloatRef(vsID);
+        const float& radioAltitude = getter.getFloatRef(radioAltitudeID);
+        const float& altitude = getter.getFloatRef(altitudeID);
+        const float& gLoad = getter.getFloatRef(gLoadID);
+        const float& flapsControl = getter.getFloatRef(flapsControlID);
+        const float& flapsLeft = getter.getFloatRef(flapsLeftID);
+        const float& flapsRight = getter.getFloatRef(flapsRightID);
+        const int32_t& navLights = getter.getIntRef(navLightsID);
+        const int32_t& beaconLights = getter.getIntRef(beaconLightsID);
+        const int32_t& strobeLights = getter.getIntRef(strobeLightsID);
+        const int32_t& landingLights = getter.getIntRef(landingLightsID);
+        const int32_t& pitot = getter.getIntRef(pitotID);
+        const float& parking = getter.getFloatRef(parkingID);
+        const int32_t& gearControl = getter.getInt(gearControlID);
+        const float& noseGear = getter.getFloatArray(noseGearID)[0];
+        const float& altimeter = getter.getFloatRef(altimeterID);
+        const float& qnh = getter.getFloatRef(qnhID);
+
+        while(true) {
+            getter.execute();
+            printf("paused=%d, replay=%d, overspeed=%d, stalled=%d, onTheGround=%d\n",
+                   paused, replay, overspeed, stalled, onTheGround);
+            printf("emptyWeight=%f, payloadWeight=%f, grossWeight=%f\n",
+                   emptyWeight, payloadWeight, grossWeight);
+            printf("latitude=%f, longitude=%f, radioAltitude=%f, altitude=%f, gLoad=%f\n",
+                   latitude, longitude, radioAltitude/.3048, altitude/.3048, gLoad);
+            printf("heading=%f, pitch=%f, bank=%f, ias=%f, mach=%f, groundSpeed=%f, vs=%f\n",
+                   heading, pitch,bank, ias, mach, groundSpeed, vs);
+            printf("flapsControl=%f, flapsLeft=%f, flapsRight=%f\n",
+                   flapsControl, flapsLeft, flapsRight);
+            printf("Lights: NAV: %d, beacon: %d, strobe: %d, landing: %d\n",
+                   navLights, beaconLights, strobeLights, landingLights);
+            printf("pitot: %d, parking: %f, gearControl: %d, noseGear: %f\n",
+                   pitot, parking, gearControl, noseGear);
+            printf("altimeter: %f, qnh: %f\n",
+                   altimeter, qnh);
+            printf("\n");
+            Thread::sleep(1000);
+        }
+
+        return 0;
+    } catch(Exception& exception) {
+        printf("\n>>>>>>>>>>>>>>>>> Exception caugth: %s\n", exception.what());
+
+        return 1;
+    }
+}
+
+//------------------------------------------------------------------------------
+
+// Local Variables:
+// mode: C++
+// c-basic-offset: 4
+// indent-tabs-mode: nil
+// End:
