SourcePP
Several modern C++20 libraries for sanely parsing Valve's formats.
Loading...
Searching...
No Matches
LZMA.cpp
Go to the documentation of this file.
2
3#include <BufferStream.h>
4#include <lzma.h>
5
6using namespace sourcepp;
7
8std::optional<std::vector<std::byte>> compression::compressValveLZMA(std::span<const std::byte> data, uint8_t compressLevel) {
9 // Shift over 4 bytes for Valve LZMA header signature
10 std::vector<std::byte> compressedData(sizeof(uint32_t) + data.size() * 2);
11
12 lzma_options_lzma options{};
13 if (lzma_lzma_preset(&options, std::clamp<uint8_t>(compressLevel, 0, 9))) {
14 return std::nullopt;
15 }
16
17 lzma_stream stream = LZMA_STREAM_INIT;
18
19 if (lzma_alone_encoder(&stream, &options) != LZMA_OK) {
20 lzma_end(&stream);
21 return std::nullopt;
22 }
23
24 stream.next_in = reinterpret_cast<const uint8_t*>(data.data());
25 stream.avail_in = data.size();
26 stream.next_out = reinterpret_cast<uint8_t*>(compressedData.data() + sizeof(uint32_t));
27 stream.avail_out = compressedData.size() - sizeof(uint32_t);
28
29 lzma_ret ret = lzma_code(&stream, LZMA_FINISH);
30 lzma_end(&stream);
31 if (ret != LZMA_STREAM_END) {
32 return std::nullopt;
33 }
34
35 // Switch out normal header with Valve one
36 BufferStream compressedStream{compressedData};
37 compressedStream.seek(0).write(VALVE_LZMA_SIGNATURE);
38 const auto properties = compressedStream.read<uint8_t>();
39 const auto dictionarySize = compressedStream.read<uint32_t>();
40 compressedStream
41 .seek_u(sizeof(uint32_t))
42 .write<uint32_t>(data.size())
43 .write(compressedData.size() - (sizeof(uint32_t) * 3) + (sizeof(uint8_t) * 5))
44 .write(properties)
45 .write(dictionarySize);
46
47 compressedData.resize(stream.total_out + sizeof(uint32_t));
48 return compressedData;
49}
50
51std::optional<std::vector<std::byte>> compression::decompressValveLZMA(std::span<const std::byte> data) {
52 if (data.size() < 18) {
53 // Valve LZMA header length + 1 null byte
54 return std::nullopt;
55 }
56
57 std::vector<std::byte> compressedData{data.begin() + sizeof(uint32_t), data.end()};
58 std::vector<std::byte> uncompressedData;
59 {
60 // Switch out Valve header with normal one
61 BufferStreamReadOnly in{data.data(), data.size()};
62 if (in.read<uint32_t>() != VALVE_LZMA_SIGNATURE) {
63 return std::nullopt;
64 }
65 const uint64_t uncompressedLength = in.read<uint32_t>();
66 in.skip<uint32_t>(); // compressed length is easily inferred
67 const auto properties = in.read<uint8_t>();
68 const auto dictionarySize = in.read<uint32_t>();
69 BufferStream out{compressedData.data(), compressedData.size()};
70 out
71 .write(properties)
72 .write(dictionarySize)
73 .write(uncompressedLength);
74
75 uncompressedData.resize(uncompressedLength);
76 }
77
78 lzma_stream stream = LZMA_STREAM_INIT;
79 stream.next_in = reinterpret_cast<const uint8_t*>(compressedData.data());
80 stream.avail_in = compressedData.size();
81 stream.next_out = reinterpret_cast<uint8_t*>(uncompressedData.data());
82 stream.avail_out = uncompressedData.size();
83
84 if (lzma_alone_decoder(&stream, UINT64_MAX) != LZMA_OK) {
85 lzma_end(&stream);
86 return std::nullopt;
87 }
88 while (true) {
89 const auto ret = lzma_code(&stream, LZMA_RUN);
90 if (ret == LZMA_STREAM_END || stream.avail_out == 0) {
91 break;
92 }
93 if (ret != LZMA_OK) {
94 lzma_end(&stream);
95 return std::nullopt;
96 }
97 }
98 if (const auto ret = lzma_code(&stream, LZMA_FINISH); ret != LZMA_OK && ret != LZMA_STREAM_END) {
99 lzma_end(&stream);
100 return std::nullopt;
101 }
102 lzma_end(&stream);
103
104 return uncompressedData;
105}
std::optional< std::vector< std::byte > > compressValveLZMA(std::span< const std::byte > data, uint8_t compressLevel=6)
Definition: LZMA.cpp:8
constexpr auto VALVE_LZMA_SIGNATURE
Definition: LZMA.h:13
std::optional< std::vector< std::byte > > decompressValveLZMA(std::span< const std::byte > data)
Definition: LZMA.cpp:51
Definition: LZMA.h:11