gz wrapper to write and read "container files".
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).
This commit is contained in:
@@ -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}")
|
||||
|
||||
260
simgear/misc/gzcontainerfile.cxx
Normal file
260
simgear/misc/gzcontainerfile.cxx
Normal file
@@ -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;
|
||||
}
|
||||
64
simgear/misc/gzcontainerfile.hxx
Normal file
64
simgear/misc/gzcontainerfile.hxx
Normal file
@@ -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
|
||||
Reference in New Issue
Block a user