File: ChunkReader.h

package info (click to toggle)
libopenmpt 0.4.3-1%2Bdeb10u1
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 7,724 kB
  • sloc: cpp: 99,820; sh: 4,503; ansic: 3,449; makefile: 480
file content (143 lines) | stat: -rw-r--r-- 3,498 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/*
 * ChunkReader.h
 * -------------
 * Purpose: An extended FileReader to read Iff-like chunk-based file structures.
 * Notes  : (currently none)
 * Authors: OpenMPT Devs
 * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
 */


#pragma once

#include "BuildSettings.h"

#include "../common/FileReader.h"

#include <vector>


OPENMPT_NAMESPACE_BEGIN


class ChunkReader : public FileReader
{
public:

	template <typename Tbyte> ChunkReader(mpt::span<Tbyte> bytedata) : FileReader(bytedata) { }
	ChunkReader(const FileReader &other) : FileReader(other) { }
	ChunkReader(FileReader &&other) : FileReader(std::move(other)) { }

	template<typename T>
	class Item
	{
	private:
		T chunkHeader;
		FileReader chunkData;

	public:
		Item(const T &header, FileReader &&data) : chunkHeader(header), chunkData(std::move(data)) { }
		Item(const Item<T> &) = default;
		Item(Item<T> &&) noexcept = default;

		const T &GetHeader() const { return chunkHeader; }
		const FileReader &GetData() const { return chunkData; }
	};

	template<typename T>
	class ChunkList : public std::vector<Item<T>>
	{
	public:
		typedef decltype(T().GetID()) id_type;

		// Check if the list contains a given chunk.
		bool ChunkExists(id_type id) const
		{
			return std::find_if(this->cbegin(), this->cend(), [&id](const Item<T> &item) { return item.GetHeader().GetID() == id; }) != this->cend();
		}

		// Retrieve the first chunk with a given ID.
		FileReader GetChunk(id_type id) const
		{
			auto item = std::find_if(this->cbegin(), this->cend(), [&id](const Item<T> &item) { return item.GetHeader().GetID() == id; });
			if(item != this->cend())
				return item->GetData();
			return FileReader();
		}

		// Retrieve all chunks with a given ID.
		std::vector<FileReader> GetAllChunks(id_type id) const
		{
			std::vector<FileReader> result;
			for(const auto &item : *this)
			{
				if(item.GetHeader().GetID() == id)
				{
					result.push_back(item.GetData());
				}
			}
			return result;
		}
	};

	// Read a single "T" chunk.
	// T is required to have the methods GetID() and GetLength().
	// GetLength() must return the chunk size in bytes, and GetID() the chunk ID.
	template<typename T>
	Item<T> GetNextChunk(off_t padding)
	{
		T chunkHeader;
		off_t dataSize = 0;
		if(Read(chunkHeader))
		{
			dataSize = chunkHeader.GetLength();
		}
		Item<T> resultItem(chunkHeader, ReadChunk(dataSize));

		// Skip padding bytes
		if(padding != 0 && dataSize % padding != 0)
		{
			Skip(padding - (dataSize % padding));
		}

		return resultItem;
	}

	// Read a series of "T" chunks until the end of file is reached.
	// T is required to have the methods GetID() and GetLength().
	// GetLength() must return the chunk size in bytes, and GetID() the chunk ID.
	template<typename T>
	ChunkList<T> ReadChunks(off_t padding)
	{
		ChunkList<T> result;
		while(CanRead(sizeof(T)))
		{
			result.push_back(GetNextChunk<T>(padding));
		}

		return result;
	}

	// Read a series of "T" chunks until a given chunk ID is found.
	// T is required to have the methods GetID() and GetLength().
	// GetLength() must return the chunk size in bytes, and GetID() the chunk ID.
	template<typename T>
	ChunkList<T> ReadChunksUntil(off_t padding, decltype(T().GetID()) stopAtID)
	{
		ChunkList<T> result;
		while(CanRead(sizeof(T)))
		{
			result.push_back(GetNextChunk<T>(padding));
			if(result.back().GetHeader().GetID() == stopAtID)
			{
				break;
			}
		}

		return result;
	}

};


OPENMPT_NAMESPACE_END