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