]> git.mxchange.org Git - simgear.git/commitdiff
gz wrapper to write and read "container files".
authorThorstenB <brehmt@gmail.com>
Sat, 10 Nov 2012 09:44:19 +0000 (10:44 +0100)
committerThorstenB <brehmt@gmail.com>
Sat, 10 Nov 2012 09:44:19 +0000 (10:44 +0100)
Packs arbitrary data (strings, property trees, binary blobs, ...) into a
single file.
Use this for storing/loading user-data only, not for distribution files
(scenery, aircraft etc).

simgear/misc/CMakeLists.txt
simgear/misc/gzcontainerfile.cxx [new file with mode: 0644]
simgear/misc/gzcontainerfile.hxx [new file with mode: 0644]

index d07b6718b714f2e18a793b532fde310a3cf54d02..6743cf89d90ce87689d3b23d5681e8b238220191 100644 (file)
@@ -13,7 +13,7 @@ set(HEADERS
     tabbed_values.hxx
     texcoord.hxx
     zfstream.hxx
-    
+    gzcontainerfile.hxx
     )
 
 set(SOURCES 
@@ -26,6 +26,7 @@ set(SOURCES
     tabbed_values.cxx
     texcoord.cxx
     zfstream.cxx
+    gzcontainerfile.cxx
     )
 
 simgear_component(misc misc "${SOURCES}" "${HEADERS}")
diff --git a/simgear/misc/gzcontainerfile.cxx b/simgear/misc/gzcontainerfile.cxx
new file mode 100644 (file)
index 0000000..b10b5f7
--- /dev/null
@@ -0,0 +1,260 @@
+// Written by Thorsten Brehm, started November 2012.
+//
+// Copyright (C) 2012  Thorsten Brehm, brehmt at gmail dt com
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library 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
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//
+// $Id$
+
+/**
+ * GZ Container File Format
+ *
+ * A (gzipped) binary file.
+ *
+ * Byte-order is little endian (we're ignoring big endian machines - for now...,
+ * - still usable, but they are writing an incompatible format).
+ *
+ * The binary file may contain an arbitrary number of containers.
+ *
+ * Each container has three elements:
+ *  - container type
+ *  - container payload size
+ *  - container payload data (of given "size" in bytes)
+ *
+ * A container may consist of strings, property trees, raw binary data, and whatever else
+ * is implemented.
+ *
+ * Use this for storing/loading user-data only - not for distribution files (scenery, aircraft etc).
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include <simgear_config.h>
+#endif
+
+#include "gzcontainerfile.hxx"
+
+#include <simgear/props/props_io.hxx>
+#include <simgear/misc/stdint.hxx>
+
+#include <string.h>
+
+using namespace std;
+using namespace simgear;
+
+#if 1
+    #define MY_SG_DEBUG SG_DEBUG
+#else
+    #define MY_SG_DEBUG SG_ALERT
+#endif
+
+/** The header is always the first container in each file, so it's ID doesn't
+ * really matter (as long as it isn't changed) - it cannot conflict with user-specific types. */
+const ContainerType HeaderType = 0;
+
+/** Magic word to detect big/little-endian file formats */
+static const uint32_t EndianMagic = 0x11223344;
+
+/**************************************************************************
+ * gzContainerWriter
+ **************************************************************************/
+
+gzContainerWriter::gzContainerWriter(const std::string& name,
+                                     const std::string& fileMagic) :
+        sg_gzofstream(name, ios_out | ios_binary),
+        filename(name)
+{
+    /* write byte-order marker **************************************/
+    write((char*)&EndianMagic, sizeof(EndianMagic));
+
+    /* write file header ********************************************/
+    writeContainer(HeaderType, fileMagic.c_str());
+}
+
+/** Write the header of a single container. */
+bool
+gzContainerWriter::writeContainerHeader(ContainerType Type, size_t Size)
+{
+    uint32_t ui32Data;
+    uint64_t ui64Data;
+
+    SG_LOG(SG_IO, MY_SG_DEBUG, "Writing container " << Type << ", size " << Size);
+
+    // write container type
+    ui32Data = Type;
+    write((char*)&ui32Data, sizeof(ui32Data));
+    if (fail())
+        return false;
+
+    // write container payload size
+    ui64Data = Size;
+    write((char*)&ui64Data, sizeof(ui64Data));
+    return !fail();
+}
+
+/** Write a complete container. */
+bool
+gzContainerWriter::writeContainer(ContainerType Type, const char* pData, size_t Size)
+{
+    // write container type and size
+    if (!writeContainerHeader(Type, Size))
+        return false;
+
+    // write container payload
+    write(pData, Size);
+    return !fail();
+}
+
+
+/** Save a single string in a separate container */
+bool
+gzContainerWriter::writeContainer(ContainerType Type, const char* stringBuffer)
+{
+    return writeContainer(Type, stringBuffer, strlen(stringBuffer)+1);
+}
+
+/** Save a property tree in a separate container */
+bool
+gzContainerWriter::writeContainer(ContainerType Type, SGPropertyNode* root)
+{
+    stringstream oss;
+    writeProperties(oss, root, true);
+    return writeContainer(Type, oss.str().c_str());
+}
+
+/**************************************************************************
+ * gzContainerReader
+ **************************************************************************/
+
+gzContainerReader::gzContainerReader(const std::string& name,
+                                     const std::string& fileMagic) :
+        sg_gzifstream(name, ios_in | ios_binary),
+        filename(name)
+{
+    bool ok = (good() && !eof());
+
+    /* check byte-order marker **************************************/
+    if (ok)
+    {
+        uint32_t EndianCheck;
+        read((char*)&EndianCheck, sizeof(EndianCheck));
+        if (eof() || !good())
+        {
+            SG_LOG(SG_IO, SG_ALERT, "Error reading file " << name);
+            ok = false;
+        }
+        else
+        if (EndianCheck != EndianMagic)
+        {
+            SG_LOG(SG_IO, SG_ALERT, "Byte-order mismatch. This file was created for another architecture: " << filename);
+            ok = false;
+        }
+    }
+
+    /* read file header *********************************************/
+    if (ok)
+    {
+        char* FileHeader = NULL;
+        size_t Size = 0;
+        ContainerType Type = -1;
+        if (!readContainer(&Type, &FileHeader, &Size))
+        {
+            SG_LOG(SG_IO, SG_ALERT, "File format not recognized. Missing file header: " << filename);
+            ok = false;
+        }
+        else
+        if ((HeaderType != Type)||(strcmp(FileHeader, fileMagic.c_str())))
+        {
+            SG_LOG(SG_IO, MY_SG_DEBUG, "Invalid header. Container type " << Type << ", Header " <<
+                   ((FileHeader) ? FileHeader : "(none)"));
+            SG_LOG(SG_IO, SG_ALERT, "File not recognized. This is not a valid '" << fileMagic << "' file: " << filename);
+            ok = false;
+        }
+
+        if (FileHeader)
+        {
+            free(FileHeader);
+            FileHeader = NULL;
+        }
+    }
+
+    if (!ok)
+    {
+        setstate(badbit);
+    }
+}
+
+/** Read the header of a single container. */
+bool
+gzContainerReader::readContainerHeader(ContainerType* pType, size_t* pSize)
+{
+    uint32_t ui32Data;
+    uint64_t ui64Data;
+
+    *pSize = 0;
+    *pType = -1;
+
+    read((char*) &ui32Data, sizeof(ui32Data));
+    if (eof())
+    {
+        SG_LOG(SG_IO, SG_ALERT, "File corrupt? Unexpected end of file when reading container type in " << filename);
+        return false;
+    }
+    *pType = (ContainerType) ui32Data;
+
+    read((char*) &ui64Data, sizeof(ui64Data));
+    if (eof())
+    {
+        SG_LOG(SG_IO, SG_ALERT, "File corrupt? Unexpected end of file when reading container size in " << filename);
+        return false;
+    }
+    *pSize = ui64Data;
+
+    return !fail();
+}
+
+/** Read a single container and return its contents as a binary blob */
+bool
+gzContainerReader::readContainer(ContainerType* pType, char** ppData, size_t* pSize)
+{
+    size_t Size = 0;
+
+    *ppData = 0;
+
+    if (!readContainerHeader(pType, &Size))
+    {
+        SG_LOG(SG_IO, SG_ALERT, "Cannot load data. Invalid container header in " << filename);
+        return false;
+    }
+
+    char* pData = (char*) malloc(Size);
+    if (!pData)
+    {
+        SG_LOG(SG_IO, SG_ALERT, "Cannot load data. No more memory when reading " << filename);
+        return false;
+    }
+
+    read(pData, Size);
+    if (fail())
+    {
+        SG_LOG(SG_IO, SG_ALERT, "File corrupt? Unexpected end of file when reading " << filename);
+        free(pData);
+        pData = 0;
+        return false;
+    }
+
+    *ppData = pData;
+    *pSize = Size;
+    return true;
+}
diff --git a/simgear/misc/gzcontainerfile.hxx b/simgear/misc/gzcontainerfile.hxx
new file mode 100644 (file)
index 0000000..bb30f69
--- /dev/null
@@ -0,0 +1,64 @@
+// Written by Thorsten Brehm, started November 2012.
+//
+// Copyright (C) 2012  Thorsten Brehm, brehmt at gmail dt com
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library 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
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//
+// $Id$
+
+#ifndef GZ_CONTAINER_FILE_HXX
+#define GZ_CONTAINER_FILE_HXX
+
+#include <string>
+#include <simgear/misc/sgstream.hxx>
+
+class SGPropertyNode;
+
+namespace simgear
+{
+
+typedef int ContainerType;
+
+/** A class to write container files. */
+class gzContainerReader : public sg_gzifstream
+{
+public:
+    gzContainerReader( const std::string& name,
+                       const std::string& fileMagic);
+
+    bool readContainerHeader(ContainerType* pType, size_t* pSize);
+    bool readContainer(ContainerType* pType, char** ppData, size_t* pSize);
+private:
+    std::string filename;
+};
+
+/** A class to read container files. */
+class gzContainerWriter : public sg_gzofstream
+{
+public:
+    gzContainerWriter( const std::string& name,
+                       const std::string& fileMagic);
+
+    bool writeContainerHeader(ContainerType Type, size_t Size);
+    bool writeContainer(ContainerType Type, const char* pData, size_t Size);
+    bool writeContainer(ContainerType Type, const char* stringBuffer);
+    bool writeContainer(ContainerType Type, SGPropertyNode* root);
+private:
+    std::string filename;
+};
+
+}
+
+#endif // GZ_CONTAINER_FILE_HXX