1 /*
2  * FileReader.h
3  * ------------
4  * Purpose: A basic class for transparent reading of memory-based files.
5  * Notes  : (currently none)
6  * Authors: OpenMPT Devs
7  * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
8  */
9 
10 
11 #pragma once
12 
13 #include "openmpt/all/BuildSettings.hpp"
14 
15 #include "mpt/io_read/filecursor.hpp"
16 #include "mpt/io_read/filecursor_filename_traits.hpp"
17 #include "mpt/io_read/filecursor_traits_filedata.hpp"
18 #include "mpt/io_read/filecursor_traits_memory.hpp"
19 #include "mpt/io_read/filereader.hpp"
20 
21 #include "openmpt/base/Types.hpp"
22 
23 #include "mptPathString.h"
24 #include "mptStringBuffer.h"
25 
26 #include <algorithm>
27 #include <array>
28 #include <limits>
29 #include <optional>
30 #include <string>
31 #include <vector>
32 
33 #include <cstring>
34 
35 #include "FileReaderFwd.h"
36 
37 
38 OPENMPT_NAMESPACE_BEGIN
39 
40 
41 namespace FileReaderExt
42 {
43 
44 	// Read a string of length srcSize into fixed-length char array destBuffer using a given read mode.
45 	// The file cursor is advanced by "srcSize" bytes.
46 	// Returns true if at least one byte could be read or 0 bytes were requested.
47 	template<mpt::String::ReadWriteMode mode, size_t destSize, typename TFileCursor>
ReadString(TFileCursor & f,char (& destBuffer)[destSize],const typename TFileCursor::pos_type srcSize)48 	bool ReadString(TFileCursor &f, char (&destBuffer)[destSize], const typename TFileCursor::pos_type srcSize)
49 	{
50 		typename TFileCursor::PinnedView source = f.ReadPinnedView(srcSize); // Make sure the string is cached properly.
51 		typename TFileCursor::pos_type realSrcSize = source.size();	// In case fewer bytes are available
52 		mpt::String::WriteAutoBuf(destBuffer) = mpt::String::ReadBuf(mode, mpt::byte_cast<const char*>(source.data()), realSrcSize);
53 		return (realSrcSize > 0 || srcSize == 0);
54 	}
55 
56 	// Read a string of length srcSize into a std::string dest using a given read mode.
57 	// The file cursor is advanced by "srcSize" bytes.
58 	// Returns true if at least one character could be read or 0 characters were requested.
59 	template<mpt::String::ReadWriteMode mode, typename TFileCursor>
ReadString(TFileCursor & f,std::string & dest,const typename TFileCursor::pos_type srcSize)60 	bool ReadString(TFileCursor &f, std::string &dest, const typename TFileCursor::pos_type srcSize)
61 	{
62 		dest.clear();
63 		typename TFileCursor::PinnedView source = f.ReadPinnedView(srcSize);	// Make sure the string is cached properly.
64 		typename TFileCursor::pos_type realSrcSize = source.size();	// In case fewer bytes are available
65 		dest = mpt::String::ReadBuf(mode, mpt::byte_cast<const char*>(source.data()), realSrcSize);
66 		return (realSrcSize > 0 || srcSize == 0);
67 	}
68 
69 	// Read a string of length srcSize into a mpt::charbuf dest using a given read mode.
70 	// The file cursor is advanced by "srcSize" bytes.
71 	// Returns true if at least one character could be read or 0 characters were requested.
72 	template<mpt::String::ReadWriteMode mode, std::size_t len, typename TFileCursor>
ReadString(TFileCursor & f,mpt::charbuf<len> & dest,const typename TFileCursor::pos_type srcSize)73 	bool ReadString(TFileCursor &f, mpt::charbuf<len> &dest, const typename TFileCursor::pos_type srcSize)
74 	{
75 		typename TFileCursor::PinnedView source = f.ReadPinnedView(srcSize);	// Make sure the string is cached properly.
76 		typename TFileCursor::pos_type realSrcSize = source.size();	// In case fewer bytes are available
77 		dest = mpt::String::ReadBuf(mode, mpt::byte_cast<const char*>(source.data()), realSrcSize);
78 		return (realSrcSize > 0 || srcSize == 0);
79 	}
80 
81 	// Read a charset encoded string of length srcSize into a mpt::ustring dest using a given read mode.
82 	// The file cursor is advanced by "srcSize" bytes.
83 	// Returns true if at least one character could be read or 0 characters were requested.
84 	template<mpt::String::ReadWriteMode mode, typename TFileCursor>
ReadString(TFileCursor & f,mpt::ustring & dest,mpt::Charset charset,const typename TFileCursor::pos_type srcSize)85 	bool ReadString(TFileCursor &f, mpt::ustring &dest, mpt::Charset charset, const typename TFileCursor::pos_type srcSize)
86 	{
87 		dest.clear();
88 		typename TFileCursor::PinnedView source = f.ReadPinnedView(srcSize);	// Make sure the string is cached properly.
89 		typename TFileCursor::pos_type realSrcSize = source.size();	// In case fewer bytes are available
90 		dest = mpt::ToUnicode(charset, mpt::String::ReadBuf(mode, mpt::byte_cast<const char*>(source.data()), realSrcSize));
91 		return (realSrcSize > 0 || srcSize == 0);
92 	}
93 
94 	// Read a string with a preprended length field of type Tsize (must be a packed<*,*> type) into a std::string dest using a given read mode.
95 	// The file cursor is advanced by the string length.
96 	// Returns true if the size field could be read and at least one character could be read or 0 characters were requested.
97 	template<typename Tsize, mpt::String::ReadWriteMode mode, size_t destSize, typename TFileCursor>
98 	bool ReadSizedString(TFileCursor &f, char (&destBuffer)[destSize], const typename TFileCursor::pos_type maxLength = std::numeric_limits<typename TFileCursor::pos_type>::max())
99 	{
100 		mpt::packed<typename Tsize::base_type, typename Tsize::endian_type> srcSize;	// Enforce usage of a packed type by ensuring that the passed type has the required typedefs
101 		if(!mpt::IO::FileReader::Read(f, srcSize))
102 		{
103 			return false;
104 		}
105 		return FileReaderExt::ReadString<mode>(f, destBuffer, std::min(static_cast<typename TFileCursor::pos_type>(srcSize), maxLength));
106 	}
107 
108 	// Read a string with a preprended length field of type Tsize (must be a packed<*,*> type) into a std::string dest using a given read mode.
109 	// The file cursor is advanced by the string length.
110 	// Returns true if the size field could be read and at least one character could be read or 0 characters were requested.
111 	template<typename Tsize, mpt::String::ReadWriteMode mode, typename TFileCursor>
112 	bool ReadSizedString(TFileCursor &f, std::string &dest, const typename TFileCursor::pos_type maxLength = std::numeric_limits<typename TFileCursor::pos_type>::max())
113 	{
114 		mpt::packed<typename Tsize::base_type, typename Tsize::endian_type> srcSize;	// Enforce usage of a packed type by ensuring that the passed type has the required typedefs
115 		if(!mpt::IO::FileReader::Read(f, srcSize))
116 		{
117 			return false;
118 		}
119 		return FileReaderExt::ReadString<mode>(f, dest, std::min(static_cast<typename TFileCursor::pos_type>(srcSize), maxLength));
120 	}
121 
122 	// Read a string with a preprended length field of type Tsize (must be a packed<*,*> type) into a mpt::charbuf dest using a given read mode.
123 	// The file cursor is advanced by the string length.
124 	// Returns true if the size field could be read and at least one character could be read or 0 characters were requested.
125 	template<typename Tsize, mpt::String::ReadWriteMode mode, std::size_t len, typename TFileCursor>
126 	bool ReadSizedString(TFileCursor &f, mpt::charbuf<len> &dest, const typename TFileCursor::pos_type maxLength = std::numeric_limits<typename TFileCursor::pos_type>::max())
127 	{
128 		mpt::packed<typename Tsize::base_type, typename Tsize::endian_type> srcSize;	// Enforce usage of a packed type by ensuring that the passed type has the required typedefs
129 		if(!mpt::IO::FileReader::Read(f, srcSize))
130 		{
131 			return false;
132 		}
133 		return FileReaderExt::ReadString<mode>(f, dest, std::min(static_cast<typename TFileCursor::pos_type>(srcSize), maxLength));
134 	}
135 
136 } // namespace FileReaderExt
137 
138 namespace detail {
139 
140 template <typename Ttraits, typename Tfilenametraits>
141 using FileCursor = mpt::IO::FileCursor<Ttraits, Tfilenametraits>;
142 
143 template <typename Ttraits, typename Tfilenametraits>
144 class FileReader
145 	: public FileCursor<Ttraits, Tfilenametraits>
146 {
147 
148 private:
149 
150 	using traits_type = Ttraits;
151 	using filename_traits_type = Tfilenametraits;
152 
153 public:
154 
155 	using pos_type = typename traits_type::pos_type;
156 	using off_t = pos_type;
157 
158 	using data_type = typename traits_type::data_type;
159 	using ref_data_type = typename traits_type::ref_data_type;
160 	using shared_data_type = typename traits_type::shared_data_type;
161 	using value_data_type = typename traits_type::value_data_type;
162 
163 	using shared_filename_type = typename filename_traits_type::shared_filename_type;
164 
165 public:
166 
167 	// Initialize invalid file reader object.
FileReader()168 	FileReader()
169 	{
170 		return;
171 	}
172 
FileReader(const FileCursor<Ttraits,Tfilenametraits> & other)173 	FileReader(const FileCursor<Ttraits, Tfilenametraits> &other)
174 		: FileCursor<Ttraits, Tfilenametraits>(other)
175 	{
176 		return;
177 	}
FileReader(FileCursor<Ttraits,Tfilenametraits> && other)178 	FileReader(FileCursor<Ttraits, Tfilenametraits> &&other)
179 		: FileCursor<Ttraits, Tfilenametraits>(std::move(other))
180 	{
181 		return;
182 	}
183 
184 	// Initialize file reader object with pointer to data and data length.
185 	template <typename Tbyte>
186 	explicit FileReader(mpt::span<Tbyte> bytedata, shared_filename_type filename = shared_filename_type{})
187 		: FileCursor<Ttraits, Tfilenametraits>(bytedata, std::move(filename))
188 	{
189 		return;
190 	}
191 
192 	// Initialize file reader object based on an existing file reader object window.
193 	explicit FileReader(value_data_type other, shared_filename_type filename = shared_filename_type{})
194 		: FileCursor<Ttraits, Tfilenametraits>(std::move(other), std::move(filename))
195 	{
196 		return;
197 	}
198 
199 public:
200 
201 	template <typename T>
Read(T & target)202 	bool Read(T &target)
203 	{
204 		return mpt::IO::FileReader::Read(*this, target);
205 	}
206 
207 	template <typename T>
ReadIntLE()208 	T ReadIntLE()
209 	{
210 		return mpt::IO::FileReader::ReadIntLE<T>(*this);
211 	}
212 
213 	template <typename T>
ReadIntBE()214 	T ReadIntBE()
215 	{
216 		return mpt::IO::FileReader::ReadIntLE<T>(*this);
217 	}
218 
219 	template <typename T>
ReadTruncatedIntLE(pos_type size)220 	T ReadTruncatedIntLE(pos_type size)
221 	{
222 		return mpt::IO::FileReader::ReadTruncatedIntLE<T>(*this, size);
223 	}
224 
225 	template <typename T>
ReadSizedIntLE(pos_type size)226 	T ReadSizedIntLE(pos_type size)
227 	{
228 		return mpt::IO::FileReader::ReadSizedIntLE<T>(*this, size);
229 	}
230 
ReadUint32LE()231 	uint32 ReadUint32LE()
232 	{
233 		return mpt::IO::FileReader::ReadUint32LE(*this);
234 	}
235 
ReadUint32BE()236 	uint32 ReadUint32BE()
237 	{
238 		return mpt::IO::FileReader::ReadUint32BE(*this);
239 	}
240 
ReadInt32LE()241 	int32 ReadInt32LE()
242 	{
243 		return mpt::IO::FileReader::ReadInt32LE(*this);
244 	}
245 
ReadInt32BE()246 	int32 ReadInt32BE()
247 	{
248 		return mpt::IO::FileReader::ReadInt32BE(*this);
249 	}
250 
ReadUint24LE()251 	uint32 ReadUint24LE()
252 	{
253 		return mpt::IO::FileReader::ReadUint24LE(*this);
254 	}
255 
ReadUint24BE()256 	uint32 ReadUint24BE()
257 	{
258 		return mpt::IO::FileReader::ReadUint24BE(*this);
259 	}
260 
ReadUint16LE()261 	uint16 ReadUint16LE()
262 	{
263 		return mpt::IO::FileReader::ReadUint16LE(*this);
264 	}
265 
ReadUint16BE()266 	uint16 ReadUint16BE()
267 	{
268 		return mpt::IO::FileReader::ReadUint16BE(*this);
269 	}
270 
ReadInt16LE()271 	int16 ReadInt16LE()
272 	{
273 		return mpt::IO::FileReader::ReadInt16LE(*this);
274 	}
275 
ReadInt16BE()276 	int16 ReadInt16BE()
277 	{
278 		return mpt::IO::FileReader::ReadInt16BE(*this);
279 	}
280 
ReadChar()281 	char ReadChar()
282 	{
283 		return mpt::IO::FileReader::ReadChar(*this);
284 	}
285 
ReadUint8()286 	uint8 ReadUint8()
287 	{
288 		return mpt::IO::FileReader::ReadUint8(*this);
289 	}
290 
ReadInt8()291 	int8 ReadInt8()
292 	{
293 		return mpt::IO::FileReader::ReadInt8(*this);
294 	}
295 
ReadFloatLE()296 	float ReadFloatLE()
297 	{
298 		return mpt::IO::FileReader::ReadFloatLE(*this);
299 	}
300 
ReadFloatBE()301 	float ReadFloatBE()
302 	{
303 		return mpt::IO::FileReader::ReadFloatBE(*this);
304 	}
305 
ReadDoubleLE()306 	double ReadDoubleLE()
307 	{
308 		return mpt::IO::FileReader::ReadDoubleLE(*this);
309 	}
310 
ReadDoubleBE()311 	double ReadDoubleBE()
312 	{
313 		return mpt::IO::FileReader::ReadDoubleBE(*this);
314 	}
315 
316 	template <typename T>
ReadStruct(T & target)317 	bool ReadStruct(T &target)
318 	{
319 		return mpt::IO::FileReader::ReadStruct(*this, target);
320 	}
321 
322 	template <typename T>
323 	size_t ReadStructPartial(T &target, size_t partialSize = sizeof(T))
324 	{
325 		return mpt::IO::FileReader::ReadStructPartial(*this, target, partialSize);
326 	}
327 
328 	bool ReadNullString(std::string &dest, const pos_type maxLength = std::numeric_limits<pos_type>::max())
329 	{
330 		return mpt::IO::FileReader::ReadNullString(*this, dest, maxLength);
331 	}
332 
333 	bool ReadLine(std::string &dest, const pos_type maxLength = std::numeric_limits<pos_type>::max())
334 	{
335 		return mpt::IO::FileReader::ReadLine(*this, dest, maxLength);
336 	}
337 
338 	template<typename T, std::size_t destSize>
ReadArray(T (& destArray)[destSize])339 	bool ReadArray(T (&destArray)[destSize])
340 	{
341 		return mpt::IO::FileReader::ReadArray(*this, destArray);
342 	}
343 
344 	template<typename T, std::size_t destSize>
ReadArray(std::array<T,destSize> & destArray)345 	bool ReadArray(std::array<T, destSize> &destArray)
346 	{
347 		return mpt::IO::FileReader::ReadArray(*this, destArray);
348 	}
349 
350 	template <typename T, std::size_t destSize>
ReadArray()351 	std::array<T, destSize> ReadArray()
352 	{
353 		return mpt::IO::FileReader::ReadArray<T, destSize>(*this);
354 	}
355 
356 	template<typename T>
ReadVector(std::vector<T> & destVector,size_t destSize)357 	bool ReadVector(std::vector<T> &destVector, size_t destSize)
358 	{
359 		return mpt::IO::FileReader::ReadVector(*this, destVector, destSize);
360 	}
361 
362 	template<size_t N>
ReadMagic(const char (& magic)[N])363 	bool ReadMagic(const char (&magic)[N])
364 	{
365 		return mpt::IO::FileReader::ReadMagic(*this, magic);
366 	}
367 
368 	template<typename T>
ReadVarInt(T & target)369 	bool ReadVarInt(T &target)
370 	{
371 		return mpt::IO::FileReader::ReadVarInt(*this, target);
372 	}
373 
374 	template <typename T>
375 	using Item = mpt::IO::FileReader::Chunk<T, FileReader>;
376 
377 	template <typename T>
378 	using ChunkList = mpt::IO::FileReader::ChunkList<T, FileReader>;
379 
380 	template<typename T>
ReadNextChunk(off_t alignment)381 	Item<T> ReadNextChunk(off_t alignment)
382 	{
383 		return mpt::IO::FileReader::ReadNextChunk<T, FileReader>(*this, alignment);
384 	}
385 
386 	template<typename T>
ReadChunks(off_t alignment)387 	ChunkList<T> ReadChunks(off_t alignment)
388 	{
389 		return mpt::IO::FileReader::ReadChunks<T, FileReader>(*this, alignment);
390 	}
391 
392 	template<typename T>
393 	ChunkList<T> ReadChunksUntil(off_t alignment, decltype(T().GetID()) stopAtID)
394 	{
395 		return mpt::IO::FileReader::ReadChunksUntil<T, FileReader>(*this, alignment, stopAtID);
396 	}
397 
398 	template<mpt::String::ReadWriteMode mode, size_t destSize>
ReadString(char (& destBuffer)[destSize],const pos_type srcSize)399 	bool ReadString(char (&destBuffer)[destSize], const pos_type srcSize)
400 	{
401 		return FileReaderExt::ReadString<mode>(*this, destBuffer, srcSize);
402 	}
403 
404 	template<mpt::String::ReadWriteMode mode>
ReadString(std::string & dest,const pos_type srcSize)405 	bool ReadString(std::string &dest, const pos_type srcSize)
406 	{
407 		return FileReaderExt::ReadString<mode>(*this, dest, srcSize);
408 	}
409 
410 	template<mpt::String::ReadWriteMode mode, std::size_t len>
ReadString(mpt::charbuf<len> & dest,const pos_type srcSize)411 	bool ReadString(mpt::charbuf<len> &dest, const pos_type srcSize)
412 	{
413 		return FileReaderExt::ReadString<mode>(*this, dest, srcSize);
414 	}
415 
416 	template<mpt::String::ReadWriteMode mode>
ReadString(mpt::ustring & dest,mpt::Charset charset,const pos_type srcSize)417 	bool ReadString(mpt::ustring &dest, mpt::Charset charset, const pos_type srcSize)
418 	{
419 		return FileReaderExt::ReadString<mode>(*this, dest, charset, srcSize);
420 	}
421 
422 	template<typename Tsize, mpt::String::ReadWriteMode mode, size_t destSize>
423 	bool ReadSizedString(char (&destBuffer)[destSize], const pos_type maxLength = std::numeric_limits<pos_type>::max())
424 	{
425 		return FileReaderExt::ReadSizedString<Tsize, mode>(*this, destBuffer, maxLength);
426 	}
427 
428 	template<typename Tsize, mpt::String::ReadWriteMode mode>
429 	bool ReadSizedString(std::string &dest, const pos_type maxLength = std::numeric_limits<pos_type>::max())
430 	{
431 		return FileReaderExt::ReadSizedString<Tsize, mode>(*this, dest, maxLength);
432 	}
433 
434 	template<typename Tsize, mpt::String::ReadWriteMode mode, std::size_t len>
435 	bool ReadSizedString(mpt::charbuf<len> &dest, const pos_type maxLength = std::numeric_limits<pos_type>::max())
436 	{
437 		return FileReaderExt::ReadSizedString<Tsize, mode, len>(*this, dest, maxLength);
438 	}
439 
440 };
441 
442 } // namespace detail
443 
444 using FileCursor = detail::FileCursor<mpt::IO::FileCursorTraitsFileData, mpt::IO::FileCursorFilenameTraits<mpt::PathString>>;
445 using FileReader = detail::FileReader<mpt::IO::FileCursorTraitsFileData, mpt::IO::FileCursorFilenameTraits<mpt::PathString>>;
446 
447 using ChunkReader = FileReader;
448 
449 using MemoryFileCursor = detail::FileCursor<mpt::IO::FileCursorTraitsMemory, mpt::IO::FileCursorFilenameTraitsNone>;
450 using MemoryFileReader = detail::FileReader<mpt::IO::FileCursorTraitsMemory, mpt::IO::FileCursorFilenameTraitsNone>;
451 
452 
453 OPENMPT_NAMESPACE_END
454