]> git.mxchange.org Git - simgear.git/blob - simgear/misc/gzcontainerfile.cxx
Fix VS2010 lack of fminf
[simgear.git] / simgear / misc / gzcontainerfile.cxx
1 // Written by Thorsten Brehm, started November 2012.
2 //
3 // Copyright (C) 2012  Thorsten Brehm, brehmt at gmail dt com
4 //
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.
9 //
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.
14 //
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.
18 //
19 // $Id$
20
21 /**
22  * GZ Container File Format
23  *
24  * A (gzipped) binary file.
25  *
26  * Byte-order is little endian (we're ignoring big endian machines - for now...,
27  * - still usable, but they are writing an incompatible format).
28  *
29  * The binary file may contain an arbitrary number of containers.
30  *
31  * Each container has three elements:
32  *  - container type
33  *  - container payload size
34  *  - container payload data (of given "size" in bytes)
35  *
36  * A container may consist of strings, property trees, raw binary data, and whatever else
37  * is implemented.
38  *
39  * Use this for storing/loading user-data only - not for distribution files (scenery, aircraft etc).
40  */
41
42 #ifdef HAVE_CONFIG_H
43 #  include <simgear_config.h>
44 #endif
45
46 #include "gzcontainerfile.hxx"
47
48 #include <simgear/props/props_io.hxx>
49 #include <simgear/misc/stdint.hxx>
50
51 #include <string.h>
52
53 using namespace std;
54 using namespace simgear;
55
56 #if 1
57     #define MY_SG_DEBUG SG_DEBUG
58 #else
59     #define MY_SG_DEBUG SG_ALERT
60 #endif
61
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;
65
66 /** Magic word to detect big/little-endian file formats */
67 static const uint32_t EndianMagic = 0x11223344;
68
69 /**************************************************************************
70  * gzContainerWriter
71  **************************************************************************/
72
73 gzContainerWriter::gzContainerWriter(const std::string& name,
74                                      const std::string& fileMagic) :
75         sg_gzofstream(name, ios_out | ios_binary),
76         filename(name)
77 {
78     /* write byte-order marker **************************************/
79     write((char*)&EndianMagic, sizeof(EndianMagic));
80
81     /* write file header ********************************************/
82     writeContainer(HeaderType, fileMagic.c_str());
83 }
84
85 /** Write the header of a single container. */
86 bool
87 gzContainerWriter::writeContainerHeader(ContainerType Type, size_t Size)
88 {
89     uint32_t ui32Data;
90     uint64_t ui64Data;
91
92     SG_LOG(SG_IO, MY_SG_DEBUG, "Writing container " << Type << ", size " << Size);
93
94     // write container type
95     ui32Data = Type;
96     write((char*)&ui32Data, sizeof(ui32Data));
97     if (fail())
98         return false;
99
100     // write container payload size
101     ui64Data = Size;
102     write((char*)&ui64Data, sizeof(ui64Data));
103     return !fail();
104 }
105
106 /** Write a complete container. */
107 bool
108 gzContainerWriter::writeContainer(ContainerType Type, const char* pData, size_t Size)
109 {
110     // write container type and size
111     if (!writeContainerHeader(Type, Size))
112         return false;
113
114     // write container payload
115     write(pData, Size);
116     return !fail();
117 }
118
119
120 /** Save a single string in a separate container */
121 bool
122 gzContainerWriter::writeContainer(ContainerType Type, const char* stringBuffer)
123 {
124     return writeContainer(Type, stringBuffer, strlen(stringBuffer)+1);
125 }
126
127 /** Save a property tree in a separate container */
128 bool
129 gzContainerWriter::writeContainer(ContainerType Type, SGPropertyNode* root)
130 {
131     stringstream oss;
132     writeProperties(oss, root, true);
133     return writeContainer(Type, oss.str().c_str());
134 }
135
136 /**************************************************************************
137  * gzContainerReader
138  **************************************************************************/
139
140 gzContainerReader::gzContainerReader(const std::string& name,
141                                      const std::string& fileMagic) :
142         sg_gzifstream(name, ios_in | ios_binary),
143         filename(name)
144 {
145     bool ok = (good() && !eof());
146
147     /* check byte-order marker **************************************/
148     if (ok)
149     {
150         uint32_t EndianCheck;
151         read((char*)&EndianCheck, sizeof(EndianCheck));
152         if (eof() || !good())
153         {
154             SG_LOG(SG_IO, SG_ALERT, "Error reading file " << name);
155             ok = false;
156         }
157         else
158         if (EndianCheck != EndianMagic)
159         {
160             SG_LOG(SG_IO, SG_ALERT, "Byte-order mismatch. This file was created for another architecture: " << filename);
161             ok = false;
162         }
163     }
164
165     /* read file header *********************************************/
166     if (ok)
167     {
168         char* FileHeader = NULL;
169         size_t Size = 0;
170         ContainerType Type = -1;
171         if (!readContainer(&Type, &FileHeader, &Size))
172         {
173             SG_LOG(SG_IO, SG_ALERT, "File format not recognized. Missing file header: " << filename);
174             ok = false;
175         }
176         else
177         if ((HeaderType != Type)||(strcmp(FileHeader, fileMagic.c_str())))
178         {
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);
182             ok = false;
183         }
184
185         if (FileHeader)
186         {
187             free(FileHeader);
188             FileHeader = NULL;
189         }
190     }
191
192     if (!ok)
193     {
194         setstate(badbit);
195     }
196 }
197
198 /** Read the header of a single container. */
199 bool
200 gzContainerReader::readContainerHeader(ContainerType* pType, size_t* pSize)
201 {
202     uint32_t ui32Data;
203     uint64_t ui64Data;
204
205     *pSize = 0;
206     *pType = -1;
207
208     read((char*) &ui32Data, sizeof(ui32Data));
209     if (eof())
210     {
211         SG_LOG(SG_IO, SG_ALERT, "File corrupt? Unexpected end of file when reading container type in " << filename);
212         return false;
213     }
214     *pType = (ContainerType) ui32Data;
215
216     read((char*) &ui64Data, sizeof(ui64Data));
217     if (eof())
218     {
219         SG_LOG(SG_IO, SG_ALERT, "File corrupt? Unexpected end of file when reading container size in " << filename);
220         return false;
221     }
222     *pSize = ui64Data;
223
224     return !fail();
225 }
226
227 /** Read a single container and return its contents as a binary blob */
228 bool
229 gzContainerReader::readContainer(ContainerType* pType, char** ppData, size_t* pSize)
230 {
231     size_t Size = 0;
232
233     *ppData = 0;
234
235     if (!readContainerHeader(pType, &Size))
236     {
237         SG_LOG(SG_IO, SG_ALERT, "Cannot load data. Invalid container header in " << filename);
238         return false;
239     }
240
241     char* pData = (char*) malloc(Size);
242     if (!pData)
243     {
244         SG_LOG(SG_IO, SG_ALERT, "Cannot load data. No more memory when reading " << filename);
245         return false;
246     }
247
248     read(pData, Size);
249     if (fail())
250     {
251         SG_LOG(SG_IO, SG_ALERT, "File corrupt? Unexpected end of file when reading " << filename);
252         free(pData);
253         pData = 0;
254         return false;
255     }
256
257     *ppData = pData;
258     *pSize = Size;
259     return true;
260 }