1 /*
2  * OggStream.cpp
3  * -------------
4  * Purpose: Basic Ogg stream parsing functionality
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 #include "stdafx.h"
11 #include "OggStream.h"
12 #include "mpt/crc/crc.hpp"
13 #include "../common/FileReader.h"
14 
15 
16 OPENMPT_NAMESPACE_BEGIN
17 
18 
19 namespace Ogg
20 {
21 
22 
GetPagePhysicalSize() const23 uint16 PageInfo::GetPagePhysicalSize() const
24 {
25 	uint16 size = 0;
26 	size += sizeof(PageHeader);
27 	size += header.page_segments;
28 	for(uint8 segment = 0; segment < header.page_segments; ++segment)
29 	{
30 		size += segment_table[segment];
31 	}
32 	return size;
33 }
34 
35 
GetPageHeaderSize() const36 uint16 PageInfo::GetPageHeaderSize() const
37 {
38 	uint16 size = 0;
39 	size += sizeof(PageHeader);
40 	size += header.page_segments;
41 	return size;
42 }
43 
44 
GetPageDataSize() const45 uint16 PageInfo::GetPageDataSize() const
46 {
47 	uint16 size = 0;
48 	for(uint8 segment = 0; segment < header.page_segments; ++segment)
49 	{
50 		size += segment_table[segment];
51 	}
52 	return size;
53 }
54 
55 
AdvanceToPageMagic(FileReader & file)56 bool AdvanceToPageMagic(FileReader &file)
57 {
58 #if MPT_COMPILER_MSVC
59 #pragma warning(push)
60 #pragma warning(disable:4127) // conditional expression is constant
61 #endif // MPT_COMPILER_MSVC
62 	while(true)
63 #if MPT_COMPILER_MSVC
64 #pragma warning(pop)
65 #endif // MPT_COMPILER_MSVC
66 	{
67 		if(!file.CanRead(4))
68 		{
69 			return false;
70 		}
71 		if(file.ReadMagic("OggS"))
72 		{
73 			file.SkipBack(4);
74 			return true;
75 		}
76 		file.Skip(1);
77 	}
78 }
79 
80 
ReadPage(FileReader & file,PageInfo & pageInfo,std::vector<uint8> * pageData)81 bool ReadPage(FileReader &file, PageInfo &pageInfo, std::vector<uint8> *pageData)
82 {
83 	pageInfo = PageInfo();
84 	if(pageData)
85 	{
86 		(*pageData).clear();
87 	}
88 	if(!file.ReadMagic("OggS"))
89 	{
90 		return false;
91 	}
92 	file.SkipBack(4);
93 	FileReader filePageReader = file; // do not modify original file read position
94 	if(!filePageReader.ReadStruct(pageInfo.header))
95 	{
96 		return false;
97 	}
98 	if(!filePageReader.CanRead(pageInfo.header.page_segments))
99 	{
100 		return false;
101 	}
102 	uint16 pageDataSize = 0;
103 	for(uint8 segment = 0; segment < pageInfo.header.page_segments; ++segment)
104 	{
105 		pageInfo.segment_table[segment] = filePageReader.ReadIntLE<uint8>();
106 		pageDataSize += pageInfo.segment_table[segment];
107 	}
108 	if(!filePageReader.CanRead(pageDataSize))
109 	{
110 		return false;
111 	}
112 	if(pageData)
113 	{
114 		filePageReader.ReadVector(*pageData, pageDataSize);
115 	} else
116 	{
117 		filePageReader.Skip(pageDataSize);
118 	}
119 	filePageReader.SkipBack(pageInfo.GetPagePhysicalSize());
120 	{
121 		mpt::crc32_ogg calculatedCRC;
122 		uint8 rawHeader[sizeof(PageHeader)];
123 		MemsetZero(rawHeader);
124 		filePageReader.ReadArray(rawHeader);
125 		std::memset(rawHeader + 22, 0, 4); // clear out old crc
126 		calculatedCRC.process(rawHeader, rawHeader + sizeof(rawHeader));
127 		filePageReader.Skip(pageInfo.header.page_segments);
128 		calculatedCRC.process(pageInfo.segment_table, pageInfo.segment_table + pageInfo.header.page_segments);
129 		if(pageData)
130 		{
131 			filePageReader.Skip(pageDataSize);
132 			calculatedCRC.process(*pageData);
133 		} else
134 		{
135 			FileReader pageDataReader = filePageReader.ReadChunk(pageDataSize);
136 			auto pageDataView = pageDataReader.GetPinnedView();
137 			calculatedCRC.process(pageDataView.GetSpan());
138 		}
139 		if(calculatedCRC != pageInfo.header.CRC_checksum)
140 		{
141 			return false;
142 		}
143 	}
144 	file.Skip(pageInfo.GetPagePhysicalSize());
145 	return true;
146 }
147 
148 
ReadPage(FileReader & file,PageInfo & pageInfo,std::vector<uint8> & pageData)149 bool ReadPage(FileReader &file, PageInfo &pageInfo, std::vector<uint8> &pageData)
150 {
151 	return ReadPage(file, pageInfo, &pageData);
152 }
153 
154 
ReadPage(FileReader & file)155 bool ReadPage(FileReader &file)
156 {
157 	PageInfo pageInfo;
158 	return ReadPage(file, pageInfo);
159 }
160 
161 
ReadPageAndSkipJunk(FileReader & file,PageInfo & pageInfo,std::vector<uint8> & pageData)162 bool ReadPageAndSkipJunk(FileReader &file, PageInfo &pageInfo, std::vector<uint8> &pageData)
163 {
164 	pageInfo = PageInfo();
165 	pageData.clear();
166 #if MPT_COMPILER_MSVC
167 #pragma warning(push)
168 #pragma warning(disable:4127) // conditional expression is constant
169 #endif // MPT_COMPILER_MSVC
170 	while(true)
171 #if MPT_COMPILER_MSVC
172 #pragma warning(pop)
173 #endif // MPT_COMPILER_MSVC
174 	{
175 		if(!AdvanceToPageMagic(file))
176 		{
177 			return false;
178 		}
179 		if(ReadPage(file, pageInfo, pageData))
180 		{
181 			return true;
182 		} else
183 		{
184 			pageInfo = PageInfo();
185 			pageData.clear();
186 		}
187 		file.Skip(1);
188 	}
189 }
190 
191 
UpdatePageCRC(PageInfo & pageInfo,const std::vector<uint8> & pageData)192 bool UpdatePageCRC(PageInfo &pageInfo, const std::vector<uint8> &pageData)
193 {
194 	if(pageData.size() != pageInfo.GetPageDataSize())
195 	{
196 		return false;
197 	}
198 	mpt::crc32_ogg crc;
199 	pageInfo.header.CRC_checksum = 0;
200 	char rawHeader[sizeof(PageHeader)];
201 	std::memcpy(rawHeader, &pageInfo.header, sizeof(PageHeader));
202 	crc.process(rawHeader, rawHeader + sizeof(PageHeader));
203 	crc.process(pageInfo.segment_table, pageInfo.segment_table + pageInfo.header.page_segments);
204 	crc.process(pageData);
205 	pageInfo.header.CRC_checksum = crc;
206 	return true;
207 }
208 
209 
210 } // namespace Ogg
211 
212 
213 OPENMPT_NAMESPACE_END
214