1 //=============================================================================
2 //
3 // Adventure Game Studio (AGS)
4 //
5 // Copyright (C) 1999-2011 Chris Jones and 2011-20xx others
6 // The full list of copyright holders can be found in the Copyright.txt
7 // file, which is part of this source code distribution.
8 //
9 // The AGS source code is provided under the Artistic License 2.0.
10 // A copy of this license can be found in the file License.txt and at
11 // http://www.opensource.org/licenses/artistic-license-2.0.php
12 //
13 //=============================================================================
14 
15 #include "debug/assert.h"
16 #include "util/alignedstream.h"
17 #include "util/stream.h"
18 #include "util/math.h"
19 
20 namespace AGS
21 {
22 namespace Common
23 {
24 
AlignedStream(Stream * stream,AlignedStreamMode mode,ObjectOwnershipPolicy stream_ownership_policy,size_t base_alignment)25 AlignedStream::AlignedStream(Stream *stream, AlignedStreamMode mode, ObjectOwnershipPolicy stream_ownership_policy,
26                              size_t base_alignment)
27     : ProxyStream(stream, stream_ownership_policy)
28     , _mode(mode)
29     , _baseAlignment(base_alignment)
30     , _maxAlignment(0)
31     , _block(0)
32 {
33 }
34 
~AlignedStream()35 AlignedStream::~AlignedStream()
36 {
37     Close();
38 }
39 
Reset()40 void AlignedStream::Reset()
41 {
42     if (!_stream)
43     {
44         return;
45     }
46 
47     FinalizeBlock();
48 }
49 
Close()50 void AlignedStream::Close()
51 {
52     if (!_stream)
53     {
54         return;
55     }
56 
57     FinalizeBlock();
58     ProxyStream::Close();
59 }
60 
CanRead() const61 bool AlignedStream::CanRead() const
62 {
63     return _stream ? (_mode == kAligned_Read && _stream->CanRead()) : false;
64 }
65 
CanWrite() const66 bool AlignedStream::CanWrite() const
67 {
68     return _stream ? (_mode == kAligned_Write && _stream->CanWrite()) : false;
69 }
70 
CanSeek() const71 bool AlignedStream::CanSeek() const
72 {
73     // Aligned stream does not support seeking, hence that will break padding count
74     return false;
75 }
76 
Read(void * buffer,size_t size)77 size_t AlignedStream::Read(void *buffer, size_t size)
78 {
79     if (_stream)
80     {
81         ReadPadding(sizeof(int8_t));
82         size = _stream->Read(buffer, size);
83         _block += size;
84         return size;
85     }
86     return 0;
87 }
88 
ReadByte()89 int32_t AlignedStream::ReadByte()
90 {
91     uint8_t b = 0;
92     if (_stream)
93     {
94         ReadPadding(sizeof(uint8_t));
95         b = _stream->ReadByte();
96         _block += sizeof(uint8_t);
97     }
98     return b;
99 }
100 
ReadInt16()101 int16_t AlignedStream::ReadInt16()
102 {
103     int16_t val = 0;
104     if (_stream)
105     {
106         ReadPadding(sizeof(int16_t));
107         val = _stream->ReadInt16();
108         _block += sizeof(int16_t);
109     }
110     return val;
111 }
112 
ReadInt32()113 int32_t AlignedStream::ReadInt32()
114 {
115     int32_t val = 0;
116     if (_stream)
117     {
118         ReadPadding(sizeof(int32_t));
119         val = _stream->ReadInt32();
120         _block += sizeof(int32_t);
121     }
122     return val;
123 }
124 
ReadInt64()125 int64_t AlignedStream::ReadInt64()
126 {
127     int64_t val = 0;
128     if (_stream)
129     {
130         ReadPadding(sizeof(int64_t));
131         val = _stream->ReadInt64();
132         _block += sizeof(int64_t);
133     }
134     return val;
135 }
136 
ReadArray(void * buffer,size_t elem_size,size_t count)137 size_t AlignedStream::ReadArray(void *buffer, size_t elem_size, size_t count)
138 {
139     if (_stream)
140     {
141         ReadPadding(elem_size);
142         count = _stream->ReadArray(buffer, elem_size, count);
143         _block += count * elem_size;
144         return count;
145     }
146     return 0;
147 }
148 
ReadArrayOfInt16(int16_t * buffer,size_t count)149 size_t AlignedStream::ReadArrayOfInt16(int16_t *buffer, size_t count)
150 {
151     if (_stream)
152     {
153         ReadPadding(sizeof(int16_t));
154         count = _stream->ReadArrayOfInt16(buffer, count);
155         _block += count * sizeof(int16_t);
156         return count;
157     }
158     return 0;
159 }
160 
ReadArrayOfInt32(int32_t * buffer,size_t count)161 size_t AlignedStream::ReadArrayOfInt32(int32_t *buffer, size_t count)
162 {
163     if (_stream)
164     {
165         ReadPadding(sizeof(int32_t));
166         count = _stream->ReadArrayOfInt32(buffer, count);
167         _block += count * sizeof(int32_t);
168         return count;
169     }
170     return 0;
171 }
172 
ReadArrayOfInt64(int64_t * buffer,size_t count)173 size_t AlignedStream::ReadArrayOfInt64(int64_t *buffer, size_t count)
174 {
175     if (_stream)
176     {
177         ReadPadding(sizeof(int64_t));
178         count = _stream->ReadArrayOfInt64(buffer, count);
179         _block += count * sizeof(int64_t);
180         return count;
181     }
182     return 0;
183 }
184 
Write(const void * buffer,size_t size)185 size_t AlignedStream::Write(const void *buffer, size_t size)
186 {
187     if (_stream)
188     {
189         WritePadding(sizeof(int8_t));
190         size = _stream->Write(buffer, size);
191         _block += size;
192         return size;
193     }
194     return 0;
195 }
196 
WriteByte(uint8_t b)197 int32_t AlignedStream::WriteByte(uint8_t b)
198 {
199     if (_stream)
200     {
201         WritePadding(sizeof(uint8_t));
202         b = _stream->WriteByte(b);
203         _block += sizeof(uint8_t);
204         return b;
205     }
206     return 0;
207 }
208 
WriteInt16(int16_t val)209 size_t AlignedStream::WriteInt16(int16_t val)
210 {
211     if (_stream)
212     {
213         WritePadding(sizeof(int16_t));
214         size_t size = _stream->WriteInt16(val);
215         _block += sizeof(int16_t);
216         return size;
217     }
218     return 0;
219 }
220 
WriteInt32(int32_t val)221 size_t AlignedStream::WriteInt32(int32_t val)
222 {
223     if (_stream)
224     {
225         WritePadding(sizeof(int32_t));
226         size_t size = _stream->WriteInt32(val);
227         _block += sizeof(int32_t);
228         return size;
229     }
230     return 0;
231 }
232 
WriteInt64(int64_t val)233 size_t AlignedStream::WriteInt64(int64_t val)
234 {
235     if (_stream)
236     {
237         WritePadding(sizeof(int64_t));
238         size_t size = _stream->WriteInt64(val);
239         _block += sizeof(int64_t);
240         return size;
241     }
242     return 0;
243 }
244 
WriteArray(const void * buffer,size_t elem_size,size_t count)245 size_t AlignedStream::WriteArray(const void *buffer, size_t elem_size, size_t count)
246 {
247     if (_stream)
248     {
249         WritePadding(elem_size);
250         count = _stream->WriteArray(buffer, elem_size, count);
251         _block += count * elem_size;
252         return count;
253     }
254     return 0;
255 }
256 
WriteArrayOfInt16(const int16_t * buffer,size_t count)257 size_t AlignedStream::WriteArrayOfInt16(const int16_t *buffer, size_t count)
258 {
259     if (_stream)
260     {
261         WritePadding(sizeof(int16_t));
262         count = _stream->WriteArrayOfInt16(buffer, count);
263         _block += count * sizeof(int16_t);
264         return count;
265     }
266     return 0;
267 }
268 
WriteArrayOfInt32(const int32_t * buffer,size_t count)269 size_t AlignedStream::WriteArrayOfInt32(const int32_t *buffer, size_t count)
270 {
271     if (_stream)
272     {
273         WritePadding(sizeof(int32_t));
274         count = _stream->WriteArrayOfInt32(buffer, count);
275         _block += count * sizeof(int32_t);
276         return count;
277     }
278     return 0;
279 }
280 
WriteArrayOfInt64(const int64_t * buffer,size_t count)281 size_t AlignedStream::WriteArrayOfInt64(const int64_t *buffer, size_t count)
282 {
283     if (_stream)
284     {
285         WritePadding(sizeof(int64_t));
286         count = _stream->WriteArrayOfInt64(buffer, count);
287         _block += count * sizeof(int64_t);
288         return count;
289     }
290     return 0;
291 }
292 
Seek(int offset,StreamSeek origin)293 size_t AlignedStream::Seek(int offset, StreamSeek origin)
294 {
295     // TODO: split out Seekable Stream interface
296     assert(false); // aligned stream should not be used in cases
297                    // where Seek() is required to be called
298     return GetPosition();
299 }
300 
ReadPadding(size_t next_type)301 void AlignedStream::ReadPadding(size_t next_type)
302 {
303     if (!IsValid())
304     {
305         return;
306     }
307 
308     if (next_type == 0)
309     {
310         return;
311     }
312 
313     // The next is going to be evenly aligned data type,
314     // therefore a padding check must be made
315     if (next_type % _baseAlignment == 0)
316     {
317         int pad = _block % next_type;
318         // Read padding only if have to
319         if (pad)
320         {
321             // We do not know and should not care if the underlying stream
322             // supports seek, so use read to skip the padding instead.
323             for (size_t i = next_type - pad; i > 0; --i)
324                 _stream->ReadByte();
325             _block += next_type - pad;
326         }
327 
328         _maxAlignment = Math::Max(_maxAlignment, next_type);
329         // Data is evenly aligned now
330         if (_block % LargestPossibleType == 0)
331         {
332             _block = 0;
333         }
334     }
335 }
336 
WritePadding(size_t next_type)337 void AlignedStream::WritePadding(size_t next_type)
338 {
339     if (!IsValid())
340     {
341         return;
342     }
343 
344     if (next_type == 0)
345     {
346         return;
347     }
348 
349     // The next is going to be evenly aligned data type,
350     // therefore a padding check must be made
351     if (next_type % _baseAlignment == 0)
352     {
353         int pad = _block % next_type;
354         // Write padding only if have to
355         if (pad)
356         {
357             _stream->WriteByteCount(0, next_type - pad);
358             _block += next_type - pad;
359         }
360 
361         _maxAlignment = Math::Max(_maxAlignment, next_type);
362         // Data is evenly aligned now
363         if (_block % LargestPossibleType == 0)
364         {
365             _block = 0;
366         }
367     }
368 }
369 
FinalizeBlock()370 void AlignedStream::FinalizeBlock()
371 {
372     if (!IsValid())
373     {
374         return;
375     }
376 
377     // Force the stream to read or write remaining padding to match the alignment
378     if (_mode == kAligned_Read)
379     {
380         ReadPadding(_maxAlignment);
381     }
382     else if (_mode == kAligned_Write)
383     {
384         WritePadding(_maxAlignment);
385     }
386 
387     _maxAlignment = 0;
388     _block = 0;
389 }
390 
391 } // namespace Common
392 } // namespace AGS
393