Skip to content

Commit 7c99dd9

Browse files
committed
Update decompressing with liblzma
Use lzma_alone_decoder that allows decompressing streams without an end of stream block. This requires feeding liblzma first a dummy lzma header so that it won't check for the eos block.
1 parent d95cd79 commit 7c99dd9

File tree

1 file changed

+76
-11
lines changed

1 file changed

+76
-11
lines changed

gframe/replay.cpp

+76-11
Original file line numberDiff line numberDiff line change
@@ -107,19 +107,84 @@ bool Replay::OpenReplayFromBuffer(std::vector<uint8_t>&& contents) {
107107
}
108108
if(pheader.base.flag & REPLAY_COMPRESSED) {
109109
size_t replay_size = pheader.base.datasize;
110-
auto comp_size = contents.size() - header_size;
111-
replay_data.resize(pheader.base.datasize);
110+
size_t comp_size = contents.size() - header_size;
112111
replay_data.resize(replay_size);
113-
lzma_filter filters[]{
114-
{ LZMA_FILTER_LZMA1, nullptr },
115-
{ LZMA_VLI_UNKNOWN, nullptr},
116-
};
117-
if(lzma_properties_decode(filters, nullptr, pheader.base.props, 5) != LZMA_OK)
112+
113+
const auto fake_header = [&]() {
114+
/* the lzma header consists of :
115+
1 byte LZMA properties byte that encodes lc/lp/pb
116+
4 bytes dictionary size as little endian uint32_t
117+
8 bytes uncompressed size as little endian uint64_t
118+
119+
with the first 5 bytes corresponding to the "props"
120+
stored in the replay header
121+
*/
122+
std::array<uint8_t, sizeof(uint8_t) + sizeof(uint32_t) + sizeof(uint64_t)> header;
123+
memcpy(header.data(), pheader.base.props, 5);
124+
header[5] = (pheader.base.datasize >> 8 * 0) & 0xff;
125+
header[6] = (pheader.base.datasize >> 8 * 1) & 0xff;
126+
header[7] = (pheader.base.datasize >> 8 * 2) & 0xff;
127+
header[8] = (pheader.base.datasize >> 8 * 3) & 0xff;
128+
header[9] = 0;
129+
header[10] = 0;
130+
header[11] = 0;
131+
header[12] = 0;
132+
return header;
133+
}();
134+
135+
lzma_stream stream = LZMA_STREAM_INIT;
136+
stream.avail_in = fake_header.size();
137+
stream.next_in = fake_header.data();
138+
139+
stream.avail_out = pheader.base.datasize;
140+
stream.next_out = replay_data.data();
141+
142+
if(lzma_alone_decoder(&stream, UINT64_MAX) != LZMA_OK) {
143+
Reset();
144+
return false;
145+
}
146+
147+
while(stream.avail_in != 0) {
148+
// this is should only feed the fake header, if for some reasons
149+
// LZMA_STREAM_END is returned, then something went wrong
150+
if(lzma_code(&stream, LZMA_RUN) != LZMA_OK) {
151+
lzma_end(&stream);
152+
Reset();
153+
return false;
154+
}
155+
}
156+
157+
if(stream.total_out != 0) {
158+
lzma_end(&stream);
159+
Reset();
118160
return false;
119-
size_t in_pos = 0;
120-
size_t out_pos = 0;
121-
lzma_raw_buffer_decode(filters, nullptr, contents.data() + header_size, &in_pos, comp_size, replay_data.data(), &out_pos, replay_size);
122-
free(filters[0].options);
161+
}
162+
163+
stream.avail_in = comp_size;
164+
stream.next_in = contents.data() + header_size;
165+
166+
while(stream.avail_in != 0) {
167+
auto ret = lzma_code(&stream, LZMA_RUN);
168+
if(ret == LZMA_STREAM_END) {
169+
if(stream.total_out != pheader.base.datasize) {
170+
lzma_end(&stream);
171+
Reset();
172+
return false;
173+
}
174+
break;
175+
}
176+
if(ret != LZMA_OK) {
177+
// if liblzma finds both the header and the end of stream marker, it returns
178+
// LZMA_DATA_ERROR, we ignore that error and just ensure that the total written
179+
// size matches the uncompressed size
180+
if(ret == LZMA_DATA_ERROR && stream.total_out == pheader.base.datasize)
181+
break;
182+
Reset();
183+
lzma_end(&stream);
184+
return false;
185+
}
186+
}
187+
lzma_end(&stream);
123188
} else {
124189
contents.erase(contents.begin(), contents.begin() + header_size);
125190
replay_data = std::move(contents);

0 commit comments

Comments
 (0)