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