-
-
Notifications
You must be signed in to change notification settings - Fork 179
/
lznt1.hexpat
206 lines (178 loc) · 7.69 KB
/
lznt1.hexpat
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
#pragma description LZNT1
#pragma endian little
#pragma pattern_limit 1000000
/*
* References:
* https://github.com/libyal/libfwnt/blob/main/documentation/Compression%20methods.asciidoc
* https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-xca/5655f4a3-6ba4-489b-959f-e1f407c52f15
* https://github.com/tuxera/ntfs-3g/blob/edge/libntfs-3g/compress.c
*/
import std.core;
import std.io;
import std.mem;
import std.sys;
using BitfieldOrder = std::core::BitfieldOrder;
bool createDecompressedSection in;
std::mem::Section decompressedSection = 0;
u128 uncompressedDataSize = 0;
u128 maxUnitSize = 4096;
fn appendData(ref auto data) {
if (createDecompressedSection) {
std::mem::copy_value_to_section(data,
decompressedSection,
std::mem::get_section_size(decompressedSection));
}
uncompressedDataSize += sizeof(data);
};
struct Value {
u8 value;
};
fn appendU8(u8 data) {
Value value;
value.value = data;
appendData(value);
};
bitfield Flag {
bool A : 1;
bool B : 1;
bool C : 1;
bool D : 1;
bool E : 1;
bool F : 1;
bool G : 1;
bool H : 1;
} [[bitfield_order(BitfieldOrder::LeastToMostSignificant, 8)]];
bitfield CompressedTuple<auto lengthSize, auto displacementSize> {
std::assert(lengthSize >= 4 && lengthSize <= 12,
std::format("lengthSize has an invalid value {}. Must be between 4 and 12.", lengthSize));
std::assert(displacementSize >= 4 && displacementSize <= 12,
std::format("displacementSizehas an invalid value {}. Must be between 4 and 12.", displacementSize));
std::assert((lengthSize + displacementSize) == 16,
std::format("lengthSize {} and displacementSize {} must add up to 16.", lengthSize, displacementSize));
unsigned length : lengthSize;
unsigned displacement : displacementSize;
u16 actualLength = u16(length) + 3 [[export]];
s16 actualDisplacement = -1 * (s16(displacement) + 1) [[export]];
} [[bitfield_order(BitfieldOrder::LeastToMostSignificant, 16)]];
fn calculateLengthSize() {
s8 lengthSize = 12;
for (u128 i = uncompressedDataSize - 1, i >= 0x10, i = i >> 1) {
lengthSize = lengthSize - 1;
}
if (lengthSize < 4) {
lengthSize = 4;
}
return u8(lengthSize);
};
fn copySequentially(std::mem::Section section, u128 sourcePos, u128 destinationPos, u128 length) {
for (u128 i = 0, i < length, i = i + 1) {
std::mem::copy_section_to_section(section,
sourcePos + i,
section,
destinationPos + i,
1);
}
};
struct Tuple {
std::assert(uncompressedDataSize > 0,
"uncompressedDataSize must be greater than zero"
+ " because otherwise there would be no data to backrefrence!");
u8 ls = calculateLengthSize();
CompressedTuple<ls, 16 - ls> ct[[inline]];
std::assert((-1 * ct.actualDisplacement) <= uncompressedDataSize,
std::format("The actualDisplacement {} is referencing data before the beginning"
+ " of the current decompressed chunk! Current decompressed size is {}.",
ct.actualDisplacement,
uncompressedDataSize));
if (createDecompressedSection) {
u128 destinationPos = std::mem::get_section_size(decompressedSection);
u128 destinationBackrefPos = destinationPos + ct.actualDisplacement;
u128 maxNonOverlap = destinationPos - destinationBackrefPos;
if (ct.actualLength <= maxNonOverlap) { // Not overlapping
std::mem::copy_section_to_section(decompressedSection,
destinationBackrefPos,
decompressedSection,
destinationPos,
ct.actualLength);
} else { // Overlapping
// Copy non-overlapping part
std::mem::copy_section_to_section(decompressedSection,
destinationBackrefPos,
decompressedSection,
destinationPos,
maxNonOverlap);
// Copy overlapping part
destinationPos += maxNonOverlap;
destinationBackrefPos += maxNonOverlap;
copySequentially(decompressedSection, destinationBackrefPos,
destinationPos, ct.actualLength - maxNonOverlap);
}
}
uncompressedDataSize += ct.actualLength;
std::assert(uncompressedDataSize <= maxUnitSize,
std::format("uncompressedDataSize {} is larger than the maximum allowed size of {}.",
uncompressedDataSize,
maxUnitSize));
};
struct FlagGroup<auto endOffset> {
Flag flag [[comment("Each bit represents whether a data element in a group of 8 is compressed "
+ "with the first value being the least significant bit.")]];
// (up to) 8 data elements
if ($ >= endOffset) break;
if (flag.A) { Tuple A; } else { u8 A; appendU8(A); }
if ($ >= endOffset) break;
if (flag.B) { Tuple B; } else { u8 B; appendU8(B); }
if ($ >= endOffset) break;
if (flag.C) { Tuple C; } else { u8 C; appendU8(C); }
if ($ >= endOffset) break;
if (flag.D) { Tuple D; } else { u8 D; appendU8(D); }
if ($ >= endOffset) break;
if (flag.E) { Tuple E; } else { u8 E; appendU8(E); }
if ($ >= endOffset) break;
if (flag.F) { Tuple F; } else { u8 F; appendU8(F); }
if ($ >= endOffset) break;
if (flag.G) { Tuple G; } else { u8 G; appendU8(G); }
if ($ >= endOffset) break;
if (flag.H) { Tuple H; } else { u8 H; appendU8(H); }
};
bitfield ChunkHeader {
unsigned chunkDataSize : 12 [[comment("The actual value stored is the chunk data size - 1.")]];
unsigned signatureValue : 3 [[comment("The value is always 3 except in an all-zero chunk header.")]];
bool isCompressed : 1;
} [[bitfield_order(BitfieldOrder::LeastToMostSignificant, 16)]];
struct Chunk {
auto headerPos = $;
ChunkHeader header;
/* An all-zero chunk header indicates the end of an LZNT1 compression stream.
Might not be present if there is not enough space to store it. */
if (header.chunkDataSize == 0
&& header.signatureValue == 0
&& !header.isCompressed) {
break;
}
std::assert_warn(header.signatureValue == 3,
std::format("ChunkHeader @ {:#x} has a signatureValue other than 3: {}",
headerPos, header.signatureValue));
u128 actualChunkDataSize = u128(header.chunkDataSize) + 1 [[export]];
if (header.isCompressed) {
auto currentEndOffset = $ + actualChunkDataSize;
uncompressedDataSize = 0;
FlagGroup<currentEndOffset> data[while($ < currentEndOffset)];
std::assert($ == currentEndOffset,
std::format("Invalid size of Chunk @ {:#x}.", headerPos));
} else {
std::assert_warn(actualChunkDataSize == maxUnitSize,
std::format("actualChunkDataSize {} must be equal to maxUnitSize {}.",
actualChunkDataSize,
maxUnitSize));
u8 data[actualChunkDataSize];
appendData(data);
}
};
struct LZNT1 {
if (createDecompressedSection) {
decompressedSection = std::mem::create_section(std::format("decompressed @ {:#x}", $));
}
Chunk chunks[while($ < sizeof($))];
};
LZNT1 lznt1 @ 0x00;