1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program 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 this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "ags/shared/util/aligned_stream.h"
24 #include "ags/shared/util/math.h"
25
26 namespace AGS3 {
27 namespace AGS {
28 namespace Shared {
29
AlignedStream(Stream * stream,AlignedStreamMode mode,ObjectOwnershipPolicy stream_ownership_policy,size_t base_alignment)30 AlignedStream::AlignedStream(Stream *stream, AlignedStreamMode mode, ObjectOwnershipPolicy stream_ownership_policy,
31 size_t base_alignment)
32 : ProxyStream(stream, stream_ownership_policy)
33 , _mode(mode)
34 , _baseAlignment(base_alignment)
35 , _maxAlignment(0)
36 , _block(0) {
37 }
38
~AlignedStream()39 AlignedStream::~AlignedStream() {
40 AlignedStream::Close();
41 }
42
Reset()43 void AlignedStream::Reset() {
44 if (!_stream) {
45 return;
46 }
47
48 FinalizeBlock();
49 }
50
Close()51 void AlignedStream::Close() {
52 if (!_stream) {
53 return;
54 }
55
56 FinalizeBlock();
57 ProxyStream::Close();
58 }
59
CanRead() const60 bool AlignedStream::CanRead() const {
61 return _stream ? (_mode == kAligned_Read && _stream->CanRead()) : false;
62 }
63
CanWrite() const64 bool AlignedStream::CanWrite() const {
65 return _stream ? (_mode == kAligned_Write && _stream->CanWrite()) : false;
66 }
67
CanSeek() const68 bool AlignedStream::CanSeek() const {
69 // Aligned stream does not support seeking, hence that will break padding count
70 return false;
71 }
72
Read(void * buffer,size_t size)73 size_t AlignedStream::Read(void *buffer, size_t size) {
74 if (_stream) {
75 ReadPadding(sizeof(int8_t));
76 size = _stream->Read(buffer, size);
77 _block += size;
78 return size;
79 }
80 return 0;
81 }
82
ReadByte()83 int32_t AlignedStream::ReadByte() {
84 uint8_t b = 0;
85 if (_stream) {
86 ReadPadding(sizeof(uint8_t));
87 b = _stream->ReadByte();
88 _block += sizeof(uint8_t);
89 }
90 return b;
91 }
92
ReadInt16()93 int16_t AlignedStream::ReadInt16() {
94 int16_t val = 0;
95 if (_stream) {
96 ReadPadding(sizeof(int16_t));
97 val = _stream->ReadInt16();
98 _block += sizeof(int16_t);
99 }
100 return val;
101 }
102
ReadInt32()103 int32_t AlignedStream::ReadInt32() {
104 int32_t val = 0;
105 if (_stream) {
106 ReadPadding(sizeof(int32_t));
107 val = _stream->ReadInt32();
108 _block += sizeof(int32_t);
109 }
110 return val;
111 }
112
ReadInt64()113 int64_t AlignedStream::ReadInt64() {
114 int64_t val = 0;
115 if (_stream) {
116 ReadPadding(sizeof(int64_t));
117 val = _stream->ReadInt64();
118 _block += sizeof(int64_t);
119 }
120 return val;
121 }
122
ReadArray(void * buffer,size_t elem_size,size_t count)123 size_t AlignedStream::ReadArray(void *buffer, size_t elem_size, size_t count) {
124 if (_stream) {
125 ReadPadding(elem_size);
126 count = _stream->ReadArray(buffer, elem_size, count);
127 _block += count * elem_size;
128 return count;
129 }
130 return 0;
131 }
132
ReadArrayOfInt16(int16_t * buffer,size_t count)133 size_t AlignedStream::ReadArrayOfInt16(int16_t *buffer, size_t count) {
134 if (_stream) {
135 ReadPadding(sizeof(int16_t));
136 count = _stream->ReadArrayOfInt16(buffer, count);
137 _block += count * sizeof(int16_t);
138 return count;
139 }
140 return 0;
141 }
142
ReadArrayOfInt32(int32_t * buffer,size_t count)143 size_t AlignedStream::ReadArrayOfInt32(int32_t *buffer, size_t count) {
144 if (_stream) {
145 ReadPadding(sizeof(int32_t));
146 count = _stream->ReadArrayOfInt32(buffer, count);
147 _block += count * sizeof(int32_t);
148 return count;
149 }
150 return 0;
151 }
152
ReadArrayOfInt64(int64_t * buffer,size_t count)153 size_t AlignedStream::ReadArrayOfInt64(int64_t *buffer, size_t count) {
154 if (_stream) {
155 ReadPadding(sizeof(int64_t));
156 count = _stream->ReadArrayOfInt64(buffer, count);
157 _block += count * sizeof(int64_t);
158 return count;
159 }
160 return 0;
161 }
162
Write(const void * buffer,size_t size)163 size_t AlignedStream::Write(const void *buffer, size_t size) {
164 if (_stream) {
165 WritePadding(sizeof(int8_t));
166 size = _stream->Write(buffer, size);
167 _block += size;
168 return size;
169 }
170 return 0;
171 }
172
WriteByte(uint8_t b)173 int32_t AlignedStream::WriteByte(uint8_t b) {
174 if (_stream) {
175 WritePadding(sizeof(uint8_t));
176 b = _stream->WriteByte(b);
177 _block += sizeof(uint8_t);
178 return b;
179 }
180 return 0;
181 }
182
WriteInt16(int16_t val)183 size_t AlignedStream::WriteInt16(int16_t val) {
184 if (_stream) {
185 WritePadding(sizeof(int16_t));
186 size_t size = _stream->WriteInt16(val);
187 _block += sizeof(int16_t);
188 return size;
189 }
190 return 0;
191 }
192
WriteInt32(int32_t val)193 size_t AlignedStream::WriteInt32(int32_t val) {
194 if (_stream) {
195 WritePadding(sizeof(int32_t));
196 size_t size = _stream->WriteInt32(val);
197 _block += sizeof(int32_t);
198 return size;
199 }
200 return 0;
201 }
202
WriteInt64(int64_t val)203 size_t AlignedStream::WriteInt64(int64_t val) {
204 if (_stream) {
205 WritePadding(sizeof(int64_t));
206 size_t size = _stream->WriteInt64(val);
207 _block += sizeof(int64_t);
208 return size;
209 }
210 return 0;
211 }
212
WriteArray(const void * buffer,size_t elem_size,size_t count)213 size_t AlignedStream::WriteArray(const void *buffer, size_t elem_size, size_t count) {
214 if (_stream) {
215 WritePadding(elem_size);
216 count = _stream->WriteArray(buffer, elem_size, count);
217 _block += count * elem_size;
218 return count;
219 }
220 return 0;
221 }
222
WriteArrayOfInt16(const int16_t * buffer,size_t count)223 size_t AlignedStream::WriteArrayOfInt16(const int16_t *buffer, size_t count) {
224 if (_stream) {
225 WritePadding(sizeof(int16_t));
226 count = _stream->WriteArrayOfInt16(buffer, count);
227 _block += count * sizeof(int16_t);
228 return count;
229 }
230 return 0;
231 }
232
WriteArrayOfInt32(const int32_t * buffer,size_t count)233 size_t AlignedStream::WriteArrayOfInt32(const int32_t *buffer, size_t count) {
234 if (_stream) {
235 WritePadding(sizeof(int32_t));
236 count = _stream->WriteArrayOfInt32(buffer, count);
237 _block += count * sizeof(int32_t);
238 return count;
239 }
240 return 0;
241 }
242
WriteArrayOfInt64(const int64_t * buffer,size_t count)243 size_t AlignedStream::WriteArrayOfInt64(const int64_t *buffer, size_t count) {
244 if (_stream) {
245 WritePadding(sizeof(int64_t));
246 count = _stream->WriteArrayOfInt64(buffer, count);
247 _block += count * sizeof(int64_t);
248 return count;
249 }
250 return 0;
251 }
252
Seek(soff_t offset,StreamSeek origin)253 bool AlignedStream::Seek(soff_t offset, StreamSeek origin) {
254 // TODO: split out Seekable Stream interface
255 return false;
256 }
257
ReadPadding(size_t next_type)258 void AlignedStream::ReadPadding(size_t next_type) {
259 if (!IsValid()) {
260 return;
261 }
262
263 if (next_type == 0) {
264 return;
265 }
266
267 // The next is going to be evenly aligned data type,
268 // therefore a padding check must be made
269 if (next_type % _baseAlignment == 0) {
270 int pad = _block % next_type;
271 // Read padding only if have to
272 if (pad) {
273 // We do not know and should not care if the underlying stream
274 // supports seek, so use read to skip the padding instead.
275 for (size_t i = next_type - pad; i > 0; --i)
276 _stream->ReadByte();
277 _block += next_type - pad;
278 }
279
280 _maxAlignment = Math::Max(_maxAlignment, next_type);
281 // Data is evenly aligned now
282 if (_block % LargestPossibleType == 0) {
283 _block = 0;
284 }
285 }
286 }
287
WritePadding(size_t next_type)288 void AlignedStream::WritePadding(size_t next_type) {
289 if (!IsValid()) {
290 return;
291 }
292
293 if (next_type == 0) {
294 return;
295 }
296
297 // The next is going to be evenly aligned data type,
298 // therefore a padding check must be made
299 if (next_type % _baseAlignment == 0) {
300 int pad = _block % next_type;
301 // Write padding only if have to
302 if (pad) {
303 _stream->WriteByteCount(0, next_type - pad);
304 _block += next_type - pad;
305 }
306
307 _maxAlignment = Math::Max(_maxAlignment, next_type);
308 // Data is evenly aligned now
309 if (_block % LargestPossibleType == 0) {
310 _block = 0;
311 }
312 }
313 }
314
FinalizeBlock()315 void AlignedStream::FinalizeBlock() {
316 if (!IsValid()) {
317 return;
318 }
319
320 // Force the stream to read or write remaining padding to match the alignment
321 if (_mode == kAligned_Read) {
322 ReadPadding(_maxAlignment);
323 } else if (_mode == kAligned_Write) {
324 WritePadding(_maxAlignment);
325 }
326
327 _maxAlignment = 0;
328 _block = 0;
329 }
330
331 } // namespace Shared
332 } // namespace AGS
333 } // namespace AGS3
334