SourcePP
Several modern C++20 libraries for sanely parsing Valve's formats.
Loading...
Searching...
No Matches
XWV.cpp
Go to the documentation of this file.
1// ReSharper disable CppRedundantQualifier
2
3#include <sndpp/XWV.h>
4
5#include <BufferStream.h>
6#include <sourcepp/FS.h>
7
8using namespace sndpp;
9using namespace sourcepp;
10
11namespace {
12
14[[nodiscard]] constexpr uint64_t xmaBytesToSamples(uint64_t bytes, uint8_t channels) {
15 const auto blockAlign = channels * 0x24;
16 const auto mod = bytes % blockAlign;
17 return bytes / blockAlign * (blockAlign - 4 * channels) * 2 / channels + (mod > 0 && mod > 0x04 * channels ? (mod - 0x04*channels) * 2 / channels : 0);
18}
19
20} // namespace
21
22XWV::XWV(std::span<const std::byte> xwvData)
24 , loopStart{-1}
25 , format{Format::PCM} {
26 BufferStreamReadOnly stream{xwvData};
27
28 switch (stream >> this->version; this->version) {
29 case Version::V0: {
30 const auto dataLength = stream.read<uint32_t>();
31 const auto dataOffset = stream.read<uint32_t>();
32 this->audioData = stream.at_bytes(dataLength, dataOffset);
33
34 stream >> this->loopStart;
35
36 if (const auto valveDataLength = stream.read<uint16_t>()) {
37 this->valveData = stream.at_bytes(valveDataLength, sizeof(uint32_t) * 3 + sizeof(int32_t) + sizeof(uint8_t) * 4);
38 }
39
40 stream >> this->format;
41
42 const auto packed = stream.read<uint8_t>();
43 this->frequency = static_cast<Frequency>(packed & 0x0f);
44 this->channelCount = packed >> 4;
45
46 // Fill in new values
47 this->leadingSampleCount = 0;
48 this->trailingSampleCount = 0;
49 this->quality = 63; // useless?
50
51 this->loopBlock = 0; // used in XMA2
52 if (this->format == Format::PCM) {
53 this->bitsPerSample = 16;
54 this->decodedSampleCount = this->audioData.size() / this->channelCount / sizeof(int16_t);
55 } else {
56 this->bitsPerSample = 4;
57 this->decodedSampleCount = ::xmaBytesToSamples(this->audioData.size(), this->channelCount);
58 }
59 break;
60 }
61 case Version::V1: {
62 const auto headerSize = stream.read<uint32_t>();
63 if (headerSize != sizeof(uint32_t) * 4 + sizeof(int32_t) + sizeof(uint16_t) + sizeof(uint8_t) * 4) {
64 return;
65 }
66
67 const auto dataLength = stream.read<uint32_t>();
68 const auto dataOffset = stream.read<uint32_t>();
69 this->audioData = stream.at_bytes(dataLength, dataOffset);
70
71 stream >> this->loopStart;
72
73 if (const auto valveDataLength = stream.read<uint16_t>()) {
74 this->valveData = stream.at_bytes(valveDataLength, headerSize);
75 }
76
77 stream >> this->format >> this->bitsPerSample;
78
79 const auto packed = stream.read<uint8_t>();
80 this->frequency = static_cast<Frequency>(packed & 0x0f);
81 this->channelCount = packed >> 4;
82
83 // Fill in new values
84 this->leadingSampleCount = 0;
85 this->trailingSampleCount = 0;
86 this->quality = 63; // useless?
87
88 this->loopBlock = 0; // used in XMA2
89 if (this->format == Format::PCM) {
90 this->decodedSampleCount = this->audioData.size() / this->channelCount / sizeof(int16_t);
91 } else {
92 this->decodedSampleCount = ::xmaBytesToSamples(this->audioData.size(), this->channelCount);
93 }
94 break;
95 }
96 case Version::V4: {
97 stream.set_big_endian(true);
98 if (stream.read<uint32_t>() != 4) {
99 return;
100 }
101
102 const auto headerSize = stream.read<uint32_t>();
103 if (headerSize != sizeof(uint32_t) * 7 + sizeof(int32_t) + sizeof(uint16_t) * 4 + sizeof(uint8_t) * 8) {
104 return;
105 }
106
107 const auto staticDataSize = stream.read<uint32_t>();
108 this->staticData = stream.at_bytes(staticDataSize, headerSize);
109
110 const auto dataOffset = stream.read<uint32_t>();
111 const auto dataLength = stream.read<uint32_t>();
112 this->audioData = stream.at_bytes(dataLength, dataOffset);
113
114 stream >> this->decodedSampleCount >> this->loopStart >> this->loopBlock >> this->leadingSampleCount >> this->trailingSampleCount;
115
116 if (const auto valveDataLength = stream.read<uint16_t>()) {
117 this->valveData = stream.at_bytes(valveDataLength, headerSize);
118 }
119
120 stream >> this->format;
121 if (this->format == Format::XMA) {
122 // This format uses the same index as XMA, we need to remap it so the enum entries are different here
123 this->format = Format::XMA2;
124 }
125
126 stream >> this->bitsPerSample >> this->frequency >> this->channelCount >> this->quality;
127
128 if (stream.read<uint8_t>()) {
129 this->seekTable = stream.read_bytes(this->audioData.size() * sizeof(uint32_t) / 2048);
130 }
131 break;
132 }
133 }
134 this->opened = true;
135}
136
137XWV::XWV(const std::filesystem::path& xwvPath)
138 : XWV(fs::readFileBuffer(xwvPath)) {}
139
140XWV::operator bool() const {
141 return this->opened;
142}
143
145 return this->version;
146}
147
148const std::vector<std::byte>& XWV::getAudioDataRaw() const {
149 return this->audioData;
150}
151
152const std::vector<std::byte>& XWV::getStaticData() const {
153 return this->staticData;
154}
155
156const std::vector<std::byte>& XWV::getValveData() const {
157 return this->valveData;
158}
159
160const std::vector<std::byte>& XWV::getSeekTableData() const {
161 return this->seekTable;
162}
163
165 return this->decodedSampleCount;
166}
167
168int32_t XWV::getLoopStart() const {
169 return this->loopStart;
170}
171
172uint16_t XWV::getLoopBlock() const {
173 return this->loopBlock;
174}
175
177 return this->leadingSampleCount;
178}
179
181 return this->trailingSampleCount;
182}
183
185 return this->format;
186}
187
188uint8_t XWV::getBitsPerSample() const {
189 return this->bitsPerSample;
190}
191
193 return this->frequency;
194}
195
196uint8_t XWV::getChannelCount() const {
197 return this->channelCount;
198}
199
200uint8_t XWV::getQuality() const {
201 return this->quality;
202}
bool opened
Definition XWV.h:86
const std::vector< std::byte > & getStaticData() const
Definition XWV.cpp:152
const std::vector< std::byte > & getValveData() const
Definition XWV.cpp:156
std::vector< std::byte > valveData
Definition XWV.h:71
uint8_t getQuality() const
Definition XWV.cpp:200
Format getFormat() const
Definition XWV.cpp:184
Format format
Definition XWV.h:80
uint8_t bitsPerSample
Definition XWV.h:81
Frequency
Definition XWV.h:26
Version
Definition XWV.h:13
std::vector< std::byte > audioData
Definition XWV.h:69
const std::vector< std::byte > & getSeekTableData() const
Definition XWV.cpp:160
uint16_t loopBlock
Definition XWV.h:77
int32_t getLoopStart() const
Definition XWV.cpp:168
uint8_t getChannelCount() const
Definition XWV.cpp:196
const std::vector< std::byte > & getAudioDataRaw() const
Definition XWV.cpp:148
uint16_t getLoopBlock() const
Definition XWV.cpp:172
Frequency getFrequency() const
Definition XWV.cpp:192
uint16_t trailingSampleCount
Definition XWV.h:79
Version getVersion() const
Definition XWV.cpp:144
uint8_t quality
Definition XWV.h:84
uint32_t decodedSampleCount
Definition XWV.h:75
uint8_t channelCount
Definition XWV.h:83
std::vector< std::byte > seekTable
Definition XWV.h:72
uint16_t leadingSampleCount
Definition XWV.h:78
Version version
Definition XWV.h:74
XWV(std::span< const std::byte > xwvData)
Definition XWV.cpp:22
uint8_t getBitsPerSample() const
Definition XWV.cpp:188
uint16_t getLeadingSampleCount() const
Definition XWV.cpp:176
std::vector< std::byte > staticData
Definition XWV.h:70
uint16_t getTrailingSampleCount() const
Definition XWV.cpp:180
Frequency frequency
Definition XWV.h:82
int32_t loopStart
Definition XWV.h:76
uint32_t getDecodedSampleCount() const
Definition XWV.cpp:164
Definition RIFF.h:10