SourcePP
Several modern C++20 libraries for sanely parsing Valve's formats.
Loading...
Searching...
No Matches
PPL.cpp
Go to the documentation of this file.
1#include <vtfpp/PPL.h>
2
3#include <ranges>
4
5#include <BufferStream.h>
6
7using namespace sourcepp;
8using namespace vtfpp;
9
10PPL::PPL(uint32_t modelChecksum, ImageFormat format_, uint32_t version_)
11 : version(version_)
12 , checksum(modelChecksum)
13 , format(format_) {}
14
15PPL::PPL(std::span<const std::byte> pplData) {
16 BufferStreamReadOnly reader{pplData.data(), pplData.size()};
17 reader >> this->version >> this->checksum >> this->format;
18
19 const auto imageCount = reader.read<uint32_t>();
20 reader.skip<uint32_t>(4);
21 for (uint32_t i = 0; i < imageCount; i++) {
22 const auto lod = reader.read<uint32_t>();
23 const auto offset = reader.read<uint32_t>();
24 const auto length = reader.read<uint32_t>();
25 const auto width = reader.read<uint32_t>();
26 const auto height = reader.read<uint32_t>();
27 reader.skip<uint32_t>(3);
28
29 this->images[lod] = {
30 .width = width,
31 .height = height,
32 .data = {pplData.data() + offset, pplData.data() + offset + length},
33 };
34 }
35}
36
37PPL::PPL(const std::string& pplPath)
38 : PPL(fs::readFileBuffer(pplPath)) {}
39
40PPL::operator bool() const {
41 return !this->images.empty();
42}
43
44uint32_t PPL::getVersion() const {
45 return this->version;
46}
47
48void PPL::setVersion(uint32_t newVersion) {
49 this->version = newVersion;
50}
51
52uint32_t PPL::getModelChecksum() const {
53 return this->checksum;
54}
55
56void PPL::setModelChecksum(uint32_t newChecksum) {
57 this->checksum = newChecksum;
58}
59
61 return this->format;
62}
63
64void PPL::setFormat(ImageFormat newFormat) {
65 for (auto& [lod, image] : this->images) {
66 image.data = ImageConversion::convertImageDataToFormat(image.data, this->format, newFormat, image.width, image.height);
67 }
68 this->format = newFormat;
69}
70
71bool PPL::hasImageForLOD(uint32_t lod) const {
72 return this->images.contains(lod);
73}
74
75std::vector<uint32_t> PPL::getImageLODs() const {
76 auto view = std::views::keys(this->images);
77 return {view.begin(), view.end()};
78}
79
80const PPL::Image* PPL::getImageRaw(uint32_t lod) const {
81 if (!this->hasImageForLOD(lod)) {
82 return nullptr;
83 }
84 return &this->images.at(lod);
85}
86
87std::optional<PPL::Image> PPL::getImageAs(ImageFormat newFormat, uint32_t lod) const {
88 if (!this->hasImageForLOD(lod)) {
89 return std::nullopt;
90 }
91 const auto& [width, height, data] = this->images.at(lod);
92 return Image{
93 .width = width,
94 .height = height,
95 .data = ImageConversion::convertImageDataToFormat(data, this->format, newFormat, width, height),
96 };
97}
98
99std::optional<PPL::Image> PPL::getImageAsRGB888(uint32_t lod) const {
100 return this->getImageAs(ImageFormat::RGB888, lod);
101}
102
103bool PPL::setImage(std::span<const std::byte> imageData, ImageFormat format_, uint32_t width, uint32_t height, uint32_t lod) {
104 if (!width || !height) {
105 return false;
106 }
107 this->images[lod] = {
108 .width = width,
109 .height = height,
110 .data = format_ == this->format ? std::vector<std::byte>{imageData.begin(), imageData.end()} : ImageConversion::convertImageDataToFormat(imageData, format_, this->format, width, height),
111 };
112 return true;
113}
114
115bool PPL::setImage(std::span<const std::byte> imageData, ImageFormat format_, uint32_t width, uint32_t height, uint32_t resizedWidth, uint32_t resizedHeight, uint32_t lod, ImageConversion::ResizeFilter filter) {
116 if (!width || !height || !resizedWidth || !resizedHeight) {
117 return false;
118 }
119 const auto unresizedData = format_ == this->format ? std::vector<std::byte>{imageData.begin(), imageData.end()} : ImageConversion::convertImageDataToFormat(imageData, format_, this->format, width, height);
120 this->images[lod] = {
121 .width = width,
122 .height = height,
123 .data = ImageConversion::resizeImageData(unresizedData, this->format, width, resizedWidth, height, resizedHeight, false, filter),
124 };
125 return true;
126}
127
128bool PPL::setImage(const std::string& imagePath, uint32_t lod) {
129 ImageFormat inputFormat;
130 int inputWidth, inputHeight, inputFrameCount;
131 auto imageData_ = ImageConversion::convertFileToImageData(fs::readFileBuffer(imagePath), inputFormat, inputWidth, inputHeight, inputFrameCount);
132
133 // Unable to decode file
134 if (inputFormat == ImageFormat::EMPTY || !inputWidth || !inputHeight || !inputFrameCount) {
135 return false;
136 }
137
138 // One frame (normal)
139 if (inputFrameCount == 1) {
140 return this->setImage(imageData_, inputFormat, inputWidth, inputHeight, lod);
141 }
142
143 // Multiple frames (GIF) - discard extra frames
144 return this->setImage({imageData_.data(), ImageFormatDetails::getDataLength(inputFormat, inputWidth, inputHeight)}, inputFormat, inputWidth, inputHeight, lod);
145}
146
147bool PPL::setImage(const std::string& imagePath, uint32_t resizedWidth, uint32_t resizedHeight, uint32_t lod, ImageConversion::ResizeFilter filter) {
148 ImageFormat inputFormat;
149 int inputWidth, inputHeight, inputFrameCount;
150 auto imageData_ = ImageConversion::convertFileToImageData(fs::readFileBuffer(imagePath), inputFormat, inputWidth, inputHeight, inputFrameCount);
151
152 // Unable to decode file
153 if (inputFormat == ImageFormat::EMPTY || !inputWidth || !inputHeight || !inputFrameCount) {
154 return false;
155 }
156
157 // One frame (normal)
158 if (inputFrameCount == 1) {
159 return this->setImage(imageData_, inputFormat, inputWidth, inputHeight, resizedWidth, resizedHeight, lod, filter);
160 }
161
162 // Multiple frames (GIF) - discard extra frames
163 return this->setImage({imageData_.data(), ImageFormatDetails::getDataLength(inputFormat, inputWidth, inputHeight)}, inputFormat, inputWidth, inputHeight, resizedWidth, resizedHeight, lod, filter);
164}
165
166std::vector<std::byte> PPL::saveImageToFile(uint32_t lod, ImageConversion::FileFormat fileFormat) const {
167 if (const auto image = this->getImageRaw(lod)) {
168 return ImageConversion::convertImageDataToFile(image->data, this->format, image->width, image->height, fileFormat);
169 }
170 return {};
171}
172
173bool PPL::saveImageToFile(const std::string& imagePath, uint32_t lod, ImageConversion::FileFormat fileFormat) const {
174 if (auto data = this->saveImageToFile(lod, fileFormat); !data.empty()) {
175 return fs::writeFileBuffer(imagePath, data);
176 }
177 return false;
178}
179
180std::vector<std::byte> PPL::bake() {
181 static constexpr auto ALIGNMENT = 512;
182
183 std::vector<std::byte> out;
184 BufferStream writer{out};
185
186 static constexpr auto HEADER_SIZE = sizeof(uint32_t) * 8;
187 writer << this->version << this->checksum << this->format;
188 writer.write<uint32_t>(this->images.size());
189
190 while (writer.tell() < ALIGNMENT) {
191 writer.write<uint32_t>(0);
192 }
193 writer.seek(HEADER_SIZE);
194
195 uint32_t currentOffset = ALIGNMENT;
196 for (const auto& [lod, image] : this->images) {
197 writer << lod << currentOffset << static_cast<uint32_t>(image.data.size()) << image.width << image.height;
198 for (int i = 0; i < 3; i++) {
199 writer.write<uint32_t>(0);
200 }
201 const auto seekPoint = writer.tell();
202 writer.seek_u(currentOffset).write(image.data);
203 const auto alignment = math::paddingForAlignment(ALIGNMENT, writer.tell());
204 for (int i = 0; i < alignment; i++) {
205 writer.write<uint8_t>(0);
206 }
207 writer.seek_u(seekPoint);
208 currentOffset += image.data.size() + alignment;
209 }
210 writer.seek(0, std::ios::end);
211
212 out.resize(writer.size());
213 return out;
214}
215
216bool PPL::bake(const std::string& pplPath) {
217 return fs::writeFileBuffer(pplPath, this->bake());
218}
Definition: PPL.h:14
ImageFormat format
Definition: PPL.h:71
ImageFormat getFormat() const
Definition: PPL.cpp:60
const Image * getImageRaw(uint32_t lod=0) const
Definition: PPL.cpp:80
std::vector< std::byte > saveImageToFile(uint32_t lod=0, ImageConversion::FileFormat fileFormat=ImageConversion::FileFormat::DEFAULT) const
Definition: PPL.cpp:166
std::optional< Image > getImageAsRGB888(uint32_t lod=0) const
Definition: PPL.cpp:99
bool hasImageForLOD(uint32_t lod) const
Definition: PPL.cpp:71
void setFormat(ImageFormat newFormat)
Definition: PPL.cpp:64
std::vector< std::byte > bake()
Definition: PPL.cpp:180
uint32_t checksum
Definition: PPL.h:70
bool setImage(std::span< const std::byte > imageData, ImageFormat format_, uint32_t width, uint32_t height, uint32_t lod=0)
Definition: PPL.cpp:103
uint32_t getVersion() const
Definition: PPL.cpp:44
uint32_t version
Definition: PPL.h:69
std::unordered_map< uint32_t, Image > images
Definition: PPL.h:73
PPL(uint32_t modelChecksum, ImageFormat format_=ImageFormat::RGB888, uint32_t version_=0)
Definition: PPL.cpp:10
std::optional< Image > getImageAs(ImageFormat newFormat, uint32_t lod=0) const
Definition: PPL.cpp:87
std::vector< uint32_t > getImageLODs() const
Definition: PPL.cpp:75
void setVersion(uint32_t newVersion)
Definition: PPL.cpp:48
uint32_t getModelChecksum() const
Definition: PPL.cpp:52
void setModelChecksum(uint32_t newChecksum)
Definition: PPL.cpp:56
std::vector< std::byte > readFileBuffer(const std::string &filepath, std::size_t startOffset=0)
Definition: FS.cpp:9
bool writeFileBuffer(const std::string &filepath, std::span< const std::byte > buffer)
Definition: FS.cpp:27
constexpr uint16_t paddingForAlignment(uint16_t alignment, uint64_t n)
Definition: Math.h:57
Definition: LZMA.h:11
std::vector< std::byte > convertFileToImageData(std::span< const std::byte > fileData, ImageFormat &format, int &width, int &height, int &frameCount)
std::vector< std::byte > convertImageDataToFile(std::span< const std::byte > imageData, ImageFormat format, uint16_t width, uint16_t height, FileFormat fileFormat=FileFormat::DEFAULT)
Converts image data to the given file format (PNG or EXR by default).
std::vector< std::byte > convertImageDataToFormat(std::span< const std::byte > imageData, ImageFormat oldFormat, ImageFormat newFormat, uint16_t width, uint16_t height)
Converts an image from one format to another.
std::vector< std::byte > resizeImageData(std::span< const std::byte > imageData, ImageFormat format, uint16_t width, uint16_t newWidth, uint16_t height, uint16_t newHeight, bool srgb, ResizeFilter filter, ResizeEdge edge=ResizeEdge::CLAMP)
Resize given image data to the new dimensions.
constexpr uint32_t getDataLength(ImageFormat format, uint16_t width, uint16_t height, uint16_t sliceCount=1)
Definition: ImageFormats.h:689
ImageFormat
Definition: ImageFormats.h:7
uint32_t width
Definition: PPL.h:17