11#pragma once
22
33#include < array>
4+ #include < cctype>
45#include < optional>
56
67#include < BufferStream.h>
@@ -26,6 +27,7 @@ class WAV : public RIFF {
2627 CHUNK_PAD = sourcepp::parser::binary::makeFourCC (" PAD " ), // Useless
2728 CHUNK_JUNK = sourcepp::parser::binary::makeFourCC (" JUNK" ), // Useless
2829 CHUNK_FLLR = sourcepp::parser::binary::makeFourCC (" FLLR" ), // Useless
30+ CHUNK_VDAT = sourcepp::parser::binary::makeFourCC (" VDAT" ), // Valve lipsync data
2931
3032 // CHUNK_LIST = sourcepp::parser::binary::makeFourCC("LIST"), // Metadata
3133 // CHUNK_LIST_WAVL = sourcepp::parser::binary::makeFourCC("WAVL"), // Wave list
@@ -144,6 +146,34 @@ class WAV : public RIFF {
144146 std::array<uint8_t , 16 > md5;
145147 };
146148
149+ struct ChunkVDAT {
150+ struct Emphasis {
151+ float time;
152+ float value;
153+ };
154+
155+ struct Phoneme {
156+ float startTime{};
157+ float endTime{};
158+ float volume{};
159+ uint32_t code{};
160+ std::string name;
161+ };
162+
163+ struct Word {
164+ float startTime{};
165+ float endTime{};
166+ std::string word;
167+ std::vector<Phoneme> phonemes;
168+ };
169+
170+ float version;
171+ std::string plaintext;
172+ std::vector<Word> words;
173+ std::vector<Emphasis> emphasis;
174+ std::unordered_map<std::string, std::string> options;
175+ };
176+
147177 // struct ChunkLISTINFO {
148178 // struct META {
149179 // ChunkType infoType;
@@ -264,6 +294,92 @@ class WAV : public RIFF {
264294 stream >> chunk->md5 ;
265295 }
266296 return chunk;
297+ } else if constexpr (Type == CHUNK_VDAT ) {
298+ std::optional<ChunkVDAT> chunk = std::nullopt ;
299+ if (stream.size () > 0 ) {
300+ chunk = ChunkVDAT{};
301+ const auto getToken = [&stream] {
302+ std::string token;
303+ if (stream.tell () >= stream.size ()) {
304+ return token;
305+ }
306+ bool gotToken = false ;
307+ char c = stream.read <char >();
308+ while (true ) {
309+ if (!gotToken && std::isspace (c)) {
310+ // skip leading whitespace
311+ } else if (!gotToken && !std::isspace (c)) {
312+ gotToken = true ;
313+ token += c;
314+ } else if (gotToken && !std::isspace (c)) {
315+ token += c;
316+ } else if (gotToken && std::isspace (c)) {
317+ break ;
318+ }
319+ c = stream.read <char >();
320+ }
321+ return token;
322+ };
323+ std::string token = getToken ();
324+ while (!token.empty ()) {
325+ if (token == " VERSION" ) {
326+ token = getToken ();
327+ chunk->version = std::stof (token);
328+ } else if (token == " PLAINTEXT" ) {
329+ token = getToken (); // opening brace
330+ token = getToken (); // first token
331+ while (true ) {
332+ chunk->plaintext += token;
333+ token = getToken ();
334+ if (token.empty () || token == " }" )
335+ break ;
336+ chunk->plaintext += ' ' ;
337+ }
338+ } else if (token == " WORDS" ) {
339+ token = getToken (); // opening brace
340+ token = getToken (); // first token
341+ while (token == " WORD" ) {
342+ ChunkVDAT::Word word;
343+ word.word = getToken ();
344+ word.startTime = std::stof (getToken ());
345+ word.endTime = std::stof (getToken ());
346+ token = getToken (); // opening brace
347+ token = getToken (); // first token
348+ while (token != " }" ) {
349+ ChunkVDAT::Phoneme phoneme;
350+ phoneme.code = std::stoi (token);
351+ phoneme.name = getToken ();
352+ phoneme.startTime = std::stof (getToken ());
353+ phoneme.endTime = std::stof (getToken ());
354+ phoneme.volume = std::stof (getToken ());
355+ word.phonemes .push_back (phoneme);
356+ token = getToken ();
357+ }
358+ chunk->words .push_back (word);
359+ token = getToken ();
360+ }
361+ } else if (token == " EMPHASIS" ) {
362+ token = getToken (); // opening brace
363+ token = getToken (); // first token
364+ while (token != " }" ) {
365+ ChunkVDAT::Emphasis emphasis{};
366+ emphasis.time = std::stof (token);
367+ emphasis.value = std::stof (getToken ());
368+ chunk->emphasis .push_back (emphasis);
369+ token = getToken ();
370+ }
371+ } else if (token == " OPTIONS" ) {
372+ token = getToken (); // opening brace
373+ token = getToken (); // first token
374+ while (token != " }" ) {
375+ chunk->options [token] = getToken ();
376+ token = getToken ();
377+ }
378+ }
379+ token = getToken ();
380+ }
381+ }
382+ return chunk;
267383 } else {
268384 return this ->hasNthChunk (Type, n) ? std::optional{std::move (chunkData)} : std::nullopt ;
269385 }
0 commit comments