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