NOW I do it right: #woxblox#
[divverent/netradiant.git] / libs / archivelib.h
1 /*
2 Copyright (C) 2001-2006, William Joseph.
3 All Rights Reserved.
4
5 This file is part of GtkRadiant.
6
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 */
21
22 #if !defined (INCLUDED_ARCHIVELIB_H)
23 #define INCLUDED_ARCHIVELIB_H
24
25 #include "debugging/debugging.h"
26 #include "iarchive.h"
27 #include "stream/filestream.h"
28 #include "stream/textfilestream.h"
29 #include "memory/allocator.h"
30 #include "string/string.h"
31
32 /// \brief A single-byte-reader wrapper around an InputStream.
33 /// Optimised for reading one byte at a time.
34 /// Uses a buffer to reduce the number of times the wrapped stream must be read.
35 template<typename InputStreamType, int SIZE = 1024>
36 class SingleByteInputStream
37 {
38   typedef typename InputStreamType::byte_type byte_type;
39
40   InputStreamType& m_inputStream;
41   byte_type m_buffer[SIZE];
42   byte_type* m_cur;
43   byte_type* m_end;
44
45 public:
46
47   SingleByteInputStream(InputStreamType& inputStream) : m_inputStream(inputStream), m_cur(m_buffer + SIZE), m_end(m_cur)
48   {
49   }
50   bool readByte(byte_type& b)
51   {
52     if(m_cur == m_end)
53     {
54       if(m_end != m_buffer + SIZE)
55       {
56         return false;
57       }
58
59       m_end = m_buffer + m_inputStream.read(m_buffer, SIZE);
60       m_cur = m_buffer;
61
62       if(m_end == m_buffer)
63       {
64         return false;
65       }
66     }
67
68     b = *m_cur++;
69
70     return true;
71   }
72 };
73
74 /// \brief A binary-to-text wrapper around an InputStream.
75 /// Converts CRLF or LFCR line-endings to LF line-endings.
76 template<typename BinaryInputStreamType>
77 class BinaryToTextInputStream : public TextInputStream
78 {
79   SingleByteInputStream<BinaryInputStreamType> m_inputStream;
80 public:
81   BinaryToTextInputStream(BinaryInputStreamType& inputStream) : m_inputStream(inputStream)
82   {
83   }
84   std::size_t read(char* buffer, std::size_t length)
85   {
86     char* p = buffer;
87     for(;;)
88     {
89       if(length != 0 && m_inputStream.readByte(*reinterpret_cast<typename BinaryInputStreamType::byte_type*>(p)))
90       {
91         if(*p != '\r')
92         {
93           ++p;
94           --length;
95         }
96       }
97       else
98       {
99         return p - buffer;
100       }
101     }
102   }
103 };
104
105 /// \brief An ArchiveFile which is stored uncompressed as part of a larger archive file.
106 class StoredArchiveFile : public ArchiveFile
107 {
108   CopiedString m_name;
109   FileInputStream m_filestream;
110   SubFileInputStream m_substream;
111   FileInputStream::size_type m_size;
112 public:
113   typedef FileInputStream::size_type size_type;
114   typedef FileInputStream::position_type position_type;
115
116   StoredArchiveFile(const char* name, const char* archiveName, position_type position, size_type stream_size, size_type file_size)
117     : m_name(name), m_filestream(archiveName), m_substream(m_filestream, position, stream_size), m_size(file_size)
118   {
119   }
120
121   static StoredArchiveFile* create(const char* name, const char* archiveName, position_type position, size_type stream_size, size_type file_size)
122   {
123     return New<StoredArchiveFile>().scalar(name, archiveName, position, stream_size, file_size);
124   }
125
126   void release()
127   {
128     Delete<StoredArchiveFile>().scalar(this);
129   }
130   size_type size() const
131   {
132     return m_size;
133   }
134   const char* getName() const
135   {
136     return m_name.c_str();
137   }
138   InputStream& getInputStream()
139   {
140     return m_substream;
141   }
142 };
143
144 /// \brief An ArchiveTextFile which is stored uncompressed as part of a larger archive file.
145 class StoredArchiveTextFile : public ArchiveTextFile
146 {
147   CopiedString m_name;
148   FileInputStream m_filestream;
149   SubFileInputStream m_substream;
150   BinaryToTextInputStream<SubFileInputStream> m_textStream;
151 public:
152   typedef FileInputStream::size_type size_type;
153   typedef FileInputStream::position_type position_type;
154
155   StoredArchiveTextFile(const char* name, const char* archiveName, position_type position, size_type stream_size)
156     : m_name(name), m_filestream(archiveName), m_substream(m_filestream, position, stream_size), m_textStream(m_substream)
157   {
158   }
159
160   static StoredArchiveTextFile* create(const char* name, const char* archiveName, position_type position, size_type stream_size)
161   {
162     return New<StoredArchiveTextFile>().scalar(name, archiveName, position, stream_size);
163   }
164
165   void release()
166   {
167     Delete<StoredArchiveTextFile>().scalar(this);
168   }
169   const char* getName() const
170   {
171     return m_name.c_str();
172   }
173   TextInputStream& getInputStream()
174   {
175     return m_textStream;
176   }
177 };
178
179 /// \brief An ArchiveFile which is stored as a single file on disk.
180 class DirectoryArchiveFile : public ArchiveFile
181 {
182   CopiedString m_name;
183   FileInputStream m_istream;
184   FileInputStream::size_type m_size;
185 public:
186   typedef FileInputStream::size_type size_type;
187
188   DirectoryArchiveFile(const char* name, const char* filename)
189     : m_name(name), m_istream(filename)
190   {
191     if(!failed())
192     {
193       m_istream.seek(0, FileInputStream::end);
194       m_size = m_istream.tell();
195       m_istream.seek(0);
196     }
197     else
198     {
199       m_size = 0;
200     }
201   }
202   bool failed() const
203   {
204     return m_istream.failed();
205   }
206
207   void release()
208   {
209     delete this;
210   }
211   size_type size() const
212   {
213     return m_size;
214   }
215   const char* getName() const
216   {
217     return m_name.c_str();
218   }
219   InputStream& getInputStream()
220   {
221     return m_istream;
222   }
223 };
224
225 /// \brief An ArchiveTextFile which is stored as a single file on disk.
226 class DirectoryArchiveTextFile : public ArchiveTextFile
227 {
228   CopiedString m_name;
229   TextFileInputStream m_inputStream;
230 public:
231
232   DirectoryArchiveTextFile(const char* name, const char* filename)
233     : m_name(name), m_inputStream(filename)
234   {
235   }
236   bool failed() const
237   {
238     return m_inputStream.failed();
239   }
240
241   void release()
242   {
243     delete this;
244   }
245   const char* getName() const
246   {
247     return m_name.c_str();
248   }
249   TextInputStream& getInputStream()
250   {
251     return m_inputStream;
252   }
253 };
254
255
256 #endif