1 // Written by Thorsten Brehm, started November 2012.
3 // Copyright (C) 2012 Thorsten Brehm, brehmt at gmail dt com
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Library General Public
7 // License as published by the Free Software Foundation; either
8 // version 2 of the License, or (at your option) any later version.
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // Library General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 * GZ Container File Format
24 * A (gzipped) binary file.
26 * Byte-order is little endian (we're ignoring big endian machines - for now...,
27 * - still usable, but they are writing an incompatible format).
29 * The binary file may contain an arbitrary number of containers.
31 * Each container has three elements:
33 * - container payload size
34 * - container payload data (of given "size" in bytes)
36 * A container may consist of strings, property trees, raw binary data, and whatever else
39 * Use this for storing/loading user-data only - not for distribution files (scenery, aircraft etc).
43 # include <simgear_config.h>
46 #include "gzcontainerfile.hxx"
48 #include <simgear/props/props_io.hxx>
49 #include <simgear/misc/stdint.hxx>
54 using namespace simgear;
57 #define MY_SG_DEBUG SG_DEBUG
59 #define MY_SG_DEBUG SG_ALERT
62 /** The header is always the first container in each file, so it's ID doesn't
63 * really matter (as long as it isn't changed) - it cannot conflict with user-specific types. */
64 const ContainerType HeaderType = 0;
66 /** Magic word to detect big/little-endian file formats */
67 static const uint32_t EndianMagic = 0x11223344;
69 /**************************************************************************
71 **************************************************************************/
73 gzContainerWriter::gzContainerWriter(const std::string& name,
74 const std::string& fileMagic) :
75 sg_gzofstream(name, ios_out | ios_binary),
78 /* write byte-order marker **************************************/
79 write((char*)&EndianMagic, sizeof(EndianMagic));
81 /* write file header ********************************************/
82 writeContainer(HeaderType, fileMagic.c_str());
85 /** Write the header of a single container. */
87 gzContainerWriter::writeContainerHeader(ContainerType Type, size_t Size)
92 SG_LOG(SG_IO, MY_SG_DEBUG, "Writing container " << Type << ", size " << Size);
94 // write container type
96 write((char*)&ui32Data, sizeof(ui32Data));
100 // write container payload size
102 write((char*)&ui64Data, sizeof(ui64Data));
106 /** Write a complete container. */
108 gzContainerWriter::writeContainer(ContainerType Type, const char* pData, size_t Size)
110 // write container type and size
111 if (!writeContainerHeader(Type, Size))
114 // write container payload
120 /** Save a single string in a separate container */
122 gzContainerWriter::writeContainer(ContainerType Type, const char* stringBuffer)
124 return writeContainer(Type, stringBuffer, strlen(stringBuffer)+1);
127 /** Save a property tree in a separate container */
129 gzContainerWriter::writeContainer(ContainerType Type, SGPropertyNode* root)
132 writeProperties(oss, root, true);
133 return writeContainer(Type, oss.str().c_str());
136 /**************************************************************************
138 **************************************************************************/
140 gzContainerReader::gzContainerReader(const std::string& name,
141 const std::string& fileMagic) :
142 sg_gzifstream(name, ios_in | ios_binary),
145 bool ok = (good() && !eof());
147 /* check byte-order marker **************************************/
150 uint32_t EndianCheck;
151 read((char*)&EndianCheck, sizeof(EndianCheck));
152 if (eof() || !good())
154 SG_LOG(SG_IO, SG_ALERT, "Error reading file " << name);
158 if (EndianCheck != EndianMagic)
160 SG_LOG(SG_IO, SG_ALERT, "Byte-order mismatch. This file was created for another architecture: " << filename);
165 /* read file header *********************************************/
168 char* FileHeader = NULL;
170 ContainerType Type = -1;
171 if (!readContainer(&Type, &FileHeader, &Size))
173 SG_LOG(SG_IO, SG_ALERT, "File format not recognized. Missing file header: " << filename);
177 if ((HeaderType != Type)||(strcmp(FileHeader, fileMagic.c_str())))
179 SG_LOG(SG_IO, MY_SG_DEBUG, "Invalid header. Container type " << Type << ", Header " <<
180 ((FileHeader) ? FileHeader : "(none)"));
181 SG_LOG(SG_IO, SG_ALERT, "File not recognized. This is not a valid '" << fileMagic << "' file: " << filename);
198 /** Read the header of a single container. */
200 gzContainerReader::readContainerHeader(ContainerType* pType, size_t* pSize)
208 read((char*) &ui32Data, sizeof(ui32Data));
211 SG_LOG(SG_IO, SG_ALERT, "File corrupt? Unexpected end of file when reading container type in " << filename);
214 *pType = (ContainerType) ui32Data;
216 read((char*) &ui64Data, sizeof(ui64Data));
219 SG_LOG(SG_IO, SG_ALERT, "File corrupt? Unexpected end of file when reading container size in " << filename);
227 /** Read a single container and return its contents as a binary blob */
229 gzContainerReader::readContainer(ContainerType* pType, char** ppData, size_t* pSize)
235 if (!readContainerHeader(pType, &Size))
237 SG_LOG(SG_IO, SG_ALERT, "Cannot load data. Invalid container header in " << filename);
241 char* pData = (char*) malloc(Size);
244 SG_LOG(SG_IO, SG_ALERT, "Cannot load data. No more memory when reading " << filename);
251 SG_LOG(SG_IO, SG_ALERT, "File corrupt? Unexpected end of file when reading " << filename);