libmspack 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (150) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.travis.yml +5 -0
  4. data/.yardopts +1 -0
  5. data/Gemfile +4 -0
  6. data/README.md +75 -0
  7. data/Rakefile +22 -0
  8. data/UNLICENSE +24 -0
  9. data/ext/Rakefile +16 -0
  10. data/ext/i386-windows/libmspack.dll +0 -0
  11. data/ext/libmspack/AUTHORS +12 -0
  12. data/ext/libmspack/COPYING.LIB +504 -0
  13. data/ext/libmspack/ChangeLog +491 -0
  14. data/ext/libmspack/Makefile.am +100 -0
  15. data/ext/libmspack/NEWS +0 -0
  16. data/ext/libmspack/README +130 -0
  17. data/ext/libmspack/TODO +8 -0
  18. data/ext/libmspack/cleanup.sh +9 -0
  19. data/ext/libmspack/configure.ac +50 -0
  20. data/ext/libmspack/debian/changelog +6 -0
  21. data/ext/libmspack/debian/control +14 -0
  22. data/ext/libmspack/debian/rules +101 -0
  23. data/ext/libmspack/doc/Doxyfile.in +22 -0
  24. data/ext/libmspack/doc/Makefile.in +14 -0
  25. data/ext/libmspack/doc/szdd_kwaj_format.html +331 -0
  26. data/ext/libmspack/libmspack.pc.in +10 -0
  27. data/ext/libmspack/mspack/cab.h +127 -0
  28. data/ext/libmspack/mspack/cabc.c +24 -0
  29. data/ext/libmspack/mspack/cabd.c +1444 -0
  30. data/ext/libmspack/mspack/chm.h +122 -0
  31. data/ext/libmspack/mspack/chmc.c +24 -0
  32. data/ext/libmspack/mspack/chmd.c +1392 -0
  33. data/ext/libmspack/mspack/crc32.c +95 -0
  34. data/ext/libmspack/mspack/crc32.h +17 -0
  35. data/ext/libmspack/mspack/des.h +15 -0
  36. data/ext/libmspack/mspack/hlp.h +33 -0
  37. data/ext/libmspack/mspack/hlpc.c +24 -0
  38. data/ext/libmspack/mspack/hlpd.c +24 -0
  39. data/ext/libmspack/mspack/kwaj.h +118 -0
  40. data/ext/libmspack/mspack/kwajc.c +24 -0
  41. data/ext/libmspack/mspack/kwajd.c +561 -0
  42. data/ext/libmspack/mspack/lit.h +35 -0
  43. data/ext/libmspack/mspack/litc.c +24 -0
  44. data/ext/libmspack/mspack/litd.c +24 -0
  45. data/ext/libmspack/mspack/lzss.h +66 -0
  46. data/ext/libmspack/mspack/lzssd.c +93 -0
  47. data/ext/libmspack/mspack/lzx.h +221 -0
  48. data/ext/libmspack/mspack/lzxc.c +18 -0
  49. data/ext/libmspack/mspack/lzxd.c +895 -0
  50. data/ext/libmspack/mspack/mspack.def +28 -0
  51. data/ext/libmspack/mspack/mspack.h +2353 -0
  52. data/ext/libmspack/mspack/mszip.h +126 -0
  53. data/ext/libmspack/mspack/mszipc.c +18 -0
  54. data/ext/libmspack/mspack/mszipd.c +514 -0
  55. data/ext/libmspack/mspack/oab.h +60 -0
  56. data/ext/libmspack/mspack/oabc.c +24 -0
  57. data/ext/libmspack/mspack/oabd.c +408 -0
  58. data/ext/libmspack/mspack/qtm.h +128 -0
  59. data/ext/libmspack/mspack/qtmc.c +18 -0
  60. data/ext/libmspack/mspack/qtmd.c +489 -0
  61. data/ext/libmspack/mspack/readbits.h +207 -0
  62. data/ext/libmspack/mspack/readhuff.h +173 -0
  63. data/ext/libmspack/mspack/sha.h +15 -0
  64. data/ext/libmspack/mspack/system.c +239 -0
  65. data/ext/libmspack/mspack/system.h +124 -0
  66. data/ext/libmspack/mspack/szdd.h +39 -0
  67. data/ext/libmspack/mspack/szddc.c +24 -0
  68. data/ext/libmspack/mspack/szddd.c +247 -0
  69. data/ext/libmspack/rebuild.sh +8 -0
  70. data/ext/libmspack/test/cabd_c10 +19 -0
  71. data/ext/libmspack/test/cabd_compare +34 -0
  72. data/ext/libmspack/test/cabd_md5.c +161 -0
  73. data/ext/libmspack/test/cabd_memory.c +179 -0
  74. data/ext/libmspack/test/cabd_test.c +386 -0
  75. data/ext/libmspack/test/cabrip.c +81 -0
  76. data/ext/libmspack/test/chmd_compare +38 -0
  77. data/ext/libmspack/test/chmd_find.c +95 -0
  78. data/ext/libmspack/test/chmd_md5.c +67 -0
  79. data/ext/libmspack/test/chmd_order.c +144 -0
  80. data/ext/libmspack/test/chminfo.c +284 -0
  81. data/ext/libmspack/test/chmx.c +216 -0
  82. data/ext/libmspack/test/error.h +22 -0
  83. data/ext/libmspack/test/expand.c +79 -0
  84. data/ext/libmspack/test/md5.c +457 -0
  85. data/ext/libmspack/test/md5.h +165 -0
  86. data/ext/libmspack/test/md5_fh.h +123 -0
  87. data/ext/libmspack/test/msdecompile_md5 +24 -0
  88. data/ext/libmspack/test/msexpand_md5 +39 -0
  89. data/ext/libmspack/test/multifh.c +435 -0
  90. data/ext/libmspack/test/oabx.c +41 -0
  91. data/ext/libmspack/test/test_files/cabd/1.pl +84 -0
  92. data/ext/libmspack/test/test_files/cabd/2.pl +75 -0
  93. data/ext/libmspack/test/test_files/cabd/bad_folderindex.cab +0 -0
  94. data/ext/libmspack/test/test_files/cabd/bad_nofiles.cab +0 -0
  95. data/ext/libmspack/test/test_files/cabd/bad_nofolders.cab +0 -0
  96. data/ext/libmspack/test/test_files/cabd/bad_signature.cab +0 -0
  97. data/ext/libmspack/test/test_files/cabd/multi_basic_pt1.cab +0 -0
  98. data/ext/libmspack/test/test_files/cabd/multi_basic_pt2.cab +0 -0
  99. data/ext/libmspack/test/test_files/cabd/multi_basic_pt3.cab +0 -0
  100. data/ext/libmspack/test/test_files/cabd/multi_basic_pt4.cab +0 -0
  101. data/ext/libmspack/test/test_files/cabd/multi_basic_pt5.cab +0 -0
  102. data/ext/libmspack/test/test_files/cabd/normal_255c_filename.cab +0 -0
  103. data/ext/libmspack/test/test_files/cabd/normal_2files_1folder.cab +0 -0
  104. data/ext/libmspack/test/test_files/cabd/partial_nodata.cab +0 -0
  105. data/ext/libmspack/test/test_files/cabd/partial_nofiles.cab +0 -0
  106. data/ext/libmspack/test/test_files/cabd/partial_nofolder.cab +0 -0
  107. data/ext/libmspack/test/test_files/cabd/partial_shortextheader.cab +0 -0
  108. data/ext/libmspack/test/test_files/cabd/partial_shortfile1.cab +0 -0
  109. data/ext/libmspack/test/test_files/cabd/partial_shortfile2.cab +0 -0
  110. data/ext/libmspack/test/test_files/cabd/partial_shortfolder.cab +0 -0
  111. data/ext/libmspack/test/test_files/cabd/partial_shortheader.cab +0 -0
  112. data/ext/libmspack/test/test_files/cabd/partial_str_nofname.cab +0 -0
  113. data/ext/libmspack/test/test_files/cabd/partial_str_noninfo.cab +0 -0
  114. data/ext/libmspack/test/test_files/cabd/partial_str_nonname.cab +0 -0
  115. data/ext/libmspack/test/test_files/cabd/partial_str_nopinfo.cab +0 -0
  116. data/ext/libmspack/test/test_files/cabd/partial_str_nopname.cab +0 -0
  117. data/ext/libmspack/test/test_files/cabd/partial_str_shortfname.cab +0 -0
  118. data/ext/libmspack/test/test_files/cabd/partial_str_shortninfo.cab +0 -0
  119. data/ext/libmspack/test/test_files/cabd/partial_str_shortnname.cab +0 -0
  120. data/ext/libmspack/test/test_files/cabd/partial_str_shortpinfo.cab +0 -0
  121. data/ext/libmspack/test/test_files/cabd/partial_str_shortpname.cab +0 -0
  122. data/ext/libmspack/test/test_files/cabd/reserve_---.cab +0 -0
  123. data/ext/libmspack/test/test_files/cabd/reserve_--D.cab +0 -0
  124. data/ext/libmspack/test/test_files/cabd/reserve_-F-.cab +0 -0
  125. data/ext/libmspack/test/test_files/cabd/reserve_-FD.cab +0 -0
  126. data/ext/libmspack/test/test_files/cabd/reserve_H--.cab +0 -0
  127. data/ext/libmspack/test/test_files/cabd/reserve_H-D.cab +0 -0
  128. data/ext/libmspack/test/test_files/cabd/reserve_HF-.cab +0 -0
  129. data/ext/libmspack/test/test_files/cabd/reserve_HFD.cab +0 -0
  130. data/ext/libmspack/test/test_files/cabd/search_basic.cab +0 -0
  131. data/ext/libmspack/test/test_files/cabd/search_tricky1.cab +0 -0
  132. data/ext/libmspack/winbuild.sh +26 -0
  133. data/ext/x86_64-windows/libmspack.dll +0 -0
  134. data/lib/libmspack/constants.rb +9 -0
  135. data/lib/libmspack/exceptions.rb +12 -0
  136. data/lib/libmspack/mscab.rb +722 -0
  137. data/lib/libmspack/mschm.rb +301 -0
  138. data/lib/libmspack/mshlp.rb +15 -0
  139. data/lib/libmspack/mskwaj.rb +124 -0
  140. data/lib/libmspack/mslit.rb +18 -0
  141. data/lib/libmspack/msoab.rb +36 -0
  142. data/lib/libmspack/mspack.rb +208 -0
  143. data/lib/libmspack/msszdd.rb +81 -0
  144. data/lib/libmspack/system.rb +84 -0
  145. data/lib/libmspack/version.rb +4 -0
  146. data/lib/libmspack.rb +121 -0
  147. data/libmspack.gemspec +33 -0
  148. data/spec/libmspack_spec.rb +26 -0
  149. data/spec/spec_helper.rb +5 -0
  150. metadata +309 -0
@@ -0,0 +1,1392 @@
1
+ /* This file is part of libmspack.
2
+ * (C) 2003-2011 Stuart Caie.
3
+ *
4
+ * libmspack is free software; you can redistribute it and/or modify it under
5
+ * the terms of the GNU Lesser General Public License (LGPL) version 2.1
6
+ *
7
+ * For further details, see the file COPYING.LIB distributed with libmspack
8
+ */
9
+
10
+ /* CHM decompression implementation */
11
+
12
+ #include <system.h>
13
+ #include <chm.h>
14
+
15
+ /* prototypes */
16
+ static struct mschmd_header * chmd_open(
17
+ struct mschm_decompressor *base, const char *filename);
18
+ static struct mschmd_header * chmd_fast_open(
19
+ struct mschm_decompressor *base, const char *filename);
20
+ static struct mschmd_header *chmd_real_open(
21
+ struct mschm_decompressor *base, const char *filename, int entire);
22
+ static void chmd_close(
23
+ struct mschm_decompressor *base, struct mschmd_header *chm);
24
+ static int chmd_read_headers(
25
+ struct mspack_system *sys, struct mspack_file *fh,
26
+ struct mschmd_header *chm, int entire);
27
+ static int chmd_fast_find(
28
+ struct mschm_decompressor *base, struct mschmd_header *chm,
29
+ const char *filename, struct mschmd_file *f_ptr, int f_size);
30
+ static unsigned char *read_chunk(
31
+ struct mschm_decompressor_p *self, struct mschmd_header *chm,
32
+ struct mspack_file *fh, unsigned int chunk);
33
+ static int search_chunk(
34
+ struct mschmd_header *chm, const unsigned char *chunk, const char *filename,
35
+ const unsigned char **result, const unsigned char **result_end);
36
+ static inline int compare(
37
+ const char *s1, const char *s2, int l1, int l2);
38
+ static int chmd_extract(
39
+ struct mschm_decompressor *base, struct mschmd_file *file,
40
+ const char *filename);
41
+ static int chmd_sys_write(
42
+ struct mspack_file *file, void *buffer, int bytes);
43
+ static int chmd_init_decomp(
44
+ struct mschm_decompressor_p *self, struct mschmd_file *file);
45
+ static int read_reset_table(
46
+ struct mschm_decompressor_p *self, struct mschmd_sec_mscompressed *sec,
47
+ int entry, off_t *length_ptr, off_t *offset_ptr);
48
+ static int read_spaninfo(
49
+ struct mschm_decompressor_p *self, struct mschmd_sec_mscompressed *sec,
50
+ off_t *length_ptr);
51
+ static int find_sys_file(
52
+ struct mschm_decompressor_p *self, struct mschmd_sec_mscompressed *sec,
53
+ struct mschmd_file **f_ptr, const char *name);
54
+ static unsigned char *read_sys_file(
55
+ struct mschm_decompressor_p *self, struct mschmd_file *file);
56
+ static int chmd_error(
57
+ struct mschm_decompressor *base);
58
+ static int read_off64(
59
+ off_t *var, unsigned char *mem, struct mspack_system *sys,
60
+ struct mspack_file *fh);
61
+
62
+ /* filenames of the system files used for decompression.
63
+ * Content and ControlData are essential.
64
+ * ResetTable is preferred, but SpanInfo can be used if not available
65
+ */
66
+ static const char *content_name = "::DataSpace/Storage/MSCompressed/Content";
67
+ static const char *control_name = "::DataSpace/Storage/MSCompressed/ControlData";
68
+ static const char *spaninfo_name = "::DataSpace/Storage/MSCompressed/SpanInfo";
69
+ static const char *rtable_name = "::DataSpace/Storage/MSCompressed/Transform/"
70
+ "{7FC28940-9D31-11D0-9B27-00A0C91E9C7C}/InstanceData/ResetTable";
71
+
72
+ /***************************************
73
+ * MSPACK_CREATE_CHM_DECOMPRESSOR
74
+ ***************************************
75
+ * constructor
76
+ */
77
+ struct mschm_decompressor *
78
+ mspack_create_chm_decompressor(struct mspack_system *sys)
79
+ {
80
+ struct mschm_decompressor_p *self = NULL;
81
+
82
+ if (!sys) sys = mspack_default_system;
83
+ if (!mspack_valid_system(sys)) return NULL;
84
+
85
+ if ((self = (struct mschm_decompressor_p *) sys->alloc(sys, sizeof(struct mschm_decompressor_p)))) {
86
+ self->base.open = &chmd_open;
87
+ self->base.close = &chmd_close;
88
+ self->base.extract = &chmd_extract;
89
+ self->base.last_error = &chmd_error;
90
+ self->base.fast_open = &chmd_fast_open;
91
+ self->base.fast_find = &chmd_fast_find;
92
+ self->system = sys;
93
+ self->error = MSPACK_ERR_OK;
94
+ self->d = NULL;
95
+ }
96
+ return (struct mschm_decompressor *) self;
97
+ }
98
+
99
+ /***************************************
100
+ * MSPACK_DESTROY_CAB_DECOMPRESSOR
101
+ ***************************************
102
+ * destructor
103
+ */
104
+ void mspack_destroy_chm_decompressor(struct mschm_decompressor *base) {
105
+ struct mschm_decompressor_p *self = (struct mschm_decompressor_p *) base;
106
+ if (self) {
107
+ struct mspack_system *sys = self->system;
108
+ if (self->d) {
109
+ if (self->d->infh) sys->close(self->d->infh);
110
+ if (self->d->state) lzxd_free(self->d->state);
111
+ sys->free(self->d);
112
+ }
113
+ sys->free(self);
114
+ }
115
+ }
116
+
117
+ /***************************************
118
+ * CHMD_OPEN
119
+ ***************************************
120
+ * opens a file and tries to read it as a CHM file.
121
+ * Calls chmd_real_open() with entire=1.
122
+ */
123
+ static struct mschmd_header *chmd_open(struct mschm_decompressor *base,
124
+ const char *filename)
125
+ {
126
+ return chmd_real_open(base, filename, 1);
127
+ }
128
+
129
+ /***************************************
130
+ * CHMD_FAST_OPEN
131
+ ***************************************
132
+ * opens a file and tries to read it as a CHM file, but does not read
133
+ * the file headers. Calls chmd_real_open() with entire=0
134
+ */
135
+ static struct mschmd_header *chmd_fast_open(struct mschm_decompressor *base,
136
+ const char *filename)
137
+ {
138
+ return chmd_real_open(base, filename, 0);
139
+ }
140
+
141
+ /***************************************
142
+ * CHMD_REAL_OPEN
143
+ ***************************************
144
+ * the real implementation of chmd_open() and chmd_fast_open(). It simply
145
+ * passes the "entire" parameter to chmd_read_headers(), which will then
146
+ * either read all headers, or a bare mininum.
147
+ */
148
+ static struct mschmd_header *chmd_real_open(struct mschm_decompressor *base,
149
+ const char *filename, int entire)
150
+ {
151
+ struct mschm_decompressor_p *self = (struct mschm_decompressor_p *) base;
152
+ struct mschmd_header *chm = NULL;
153
+ struct mspack_system *sys;
154
+ struct mspack_file *fh;
155
+ int error;
156
+
157
+ if (!base) return NULL;
158
+ sys = self->system;
159
+
160
+ if ((fh = sys->open(sys, filename, MSPACK_SYS_OPEN_READ))) {
161
+ if ((chm = (struct mschmd_header *) sys->alloc(sys, sizeof(struct mschmd_header)))) {
162
+ chm->filename = filename;
163
+ error = chmd_read_headers(sys, fh, chm, entire);
164
+ if (error) {
165
+ /* if the error is DATAFORMAT, and there are some results, return
166
+ * partial results with a warning, rather than nothing */
167
+ if (error == MSPACK_ERR_DATAFORMAT && (chm->files || chm->sysfiles)) {
168
+ sys->message(fh, "WARNING; contents are corrupt");
169
+ error = MSPACK_ERR_OK;
170
+ }
171
+ else {
172
+ chmd_close(base, chm);
173
+ chm = NULL;
174
+ }
175
+ }
176
+ self->error = error;
177
+ }
178
+ else {
179
+ self->error = MSPACK_ERR_NOMEMORY;
180
+ }
181
+ sys->close(fh);
182
+ }
183
+ else {
184
+ self->error = MSPACK_ERR_OPEN;
185
+ }
186
+ return chm;
187
+ }
188
+
189
+ /***************************************
190
+ * CHMD_CLOSE
191
+ ***************************************
192
+ * frees all memory associated with a given mschmd_header
193
+ */
194
+ static void chmd_close(struct mschm_decompressor *base,
195
+ struct mschmd_header *chm)
196
+ {
197
+ struct mschm_decompressor_p *self = (struct mschm_decompressor_p *) base;
198
+ struct mschmd_file *fi, *nfi;
199
+ struct mspack_system *sys;
200
+ unsigned int i;
201
+
202
+ if (!base) return;
203
+ sys = self->system;
204
+
205
+ self->error = MSPACK_ERR_OK;
206
+
207
+ /* free files */
208
+ for (fi = chm->files; fi; fi = nfi) {
209
+ nfi = fi->next;
210
+ sys->free(fi);
211
+ }
212
+ for (fi = chm->sysfiles; fi; fi = nfi) {
213
+ nfi = fi->next;
214
+ sys->free(fi);
215
+ }
216
+
217
+ /* if this CHM was being decompressed, free decompression state */
218
+ if (self->d && (self->d->chm == chm)) {
219
+ if (self->d->infh) sys->close(self->d->infh);
220
+ if (self->d->state) lzxd_free(self->d->state);
221
+ sys->free(self->d);
222
+ self->d = NULL;
223
+ }
224
+
225
+ /* if this CHM had a chunk cache, free it and contents */
226
+ if (chm->chunk_cache) {
227
+ for (i = 0; i < chm->num_chunks; i++) sys->free(chm->chunk_cache[i]);
228
+ sys->free(chm->chunk_cache);
229
+ }
230
+
231
+ sys->free(chm);
232
+ }
233
+
234
+ /***************************************
235
+ * CHMD_READ_HEADERS
236
+ ***************************************
237
+ * reads the basic CHM file headers. If the "entire" parameter is
238
+ * non-zero, all file entries will also be read. fills out a pre-existing
239
+ * mschmd_header structure, allocates memory for files as necessary
240
+ */
241
+
242
+ /* The GUIDs found in CHM headers */
243
+ static const unsigned char guids[32] = {
244
+ /* {7C01FD10-7BAA-11D0-9E0C-00A0-C922-E6EC} */
245
+ 0x10, 0xFD, 0x01, 0x7C, 0xAA, 0x7B, 0xD0, 0x11,
246
+ 0x9E, 0x0C, 0x00, 0xA0, 0xC9, 0x22, 0xE6, 0xEC,
247
+ /* {7C01FD11-7BAA-11D0-9E0C-00A0-C922-E6EC} */
248
+ 0x11, 0xFD, 0x01, 0x7C, 0xAA, 0x7B, 0xD0, 0x11,
249
+ 0x9E, 0x0C, 0x00, 0xA0, 0xC9, 0x22, 0xE6, 0xEC
250
+ };
251
+
252
+ /* reads an encoded integer into a variable; 7 bits of data per byte,
253
+ * the high bit is used to indicate that there is another byte */
254
+ #define READ_ENCINT(var) do { \
255
+ (var) = 0; \
256
+ do { \
257
+ if (p > end) goto chunk_end; \
258
+ (var) = ((var) << 7) | (*p & 0x7F); \
259
+ } while (*p++ & 0x80); \
260
+ } while (0)
261
+
262
+ static int chmd_read_headers(struct mspack_system *sys, struct mspack_file *fh,
263
+ struct mschmd_header *chm, int entire)
264
+ {
265
+ unsigned int section, name_len, x, errors, num_chunks;
266
+ unsigned char buf[0x54], *chunk = NULL, *name, *p, *end;
267
+ struct mschmd_file *fi, *link = NULL;
268
+ off_t offset, length;
269
+ int num_entries, i;
270
+
271
+ /* initialise pointers */
272
+ chm->files = NULL;
273
+ chm->sysfiles = NULL;
274
+ chm->chunk_cache = NULL;
275
+ chm->sec0.base.chm = chm;
276
+ chm->sec0.base.id = 0;
277
+ chm->sec1.base.chm = chm;
278
+ chm->sec1.base.id = 1;
279
+ chm->sec1.content = NULL;
280
+ chm->sec1.control = NULL;
281
+ chm->sec1.spaninfo = NULL;
282
+ chm->sec1.rtable = NULL;
283
+
284
+ /* read the first header */
285
+ if (sys->read(fh, &buf[0], chmhead_SIZEOF) != chmhead_SIZEOF) {
286
+ return MSPACK_ERR_READ;
287
+ }
288
+
289
+ /* check ITSF signature */
290
+ if (EndGetI32(&buf[chmhead_Signature]) != 0x46535449) {
291
+ return MSPACK_ERR_SIGNATURE;
292
+ }
293
+
294
+ /* check both header GUIDs */
295
+ if (mspack_memcmp(&buf[chmhead_GUID1], &guids[0], 32L) != 0) {
296
+ D(("incorrect GUIDs"))
297
+ return MSPACK_ERR_SIGNATURE;
298
+ }
299
+
300
+ chm->version = EndGetI32(&buf[chmhead_Version]);
301
+ chm->timestamp = EndGetM32(&buf[chmhead_Timestamp]);
302
+ chm->language = EndGetI32(&buf[chmhead_LanguageID]);
303
+ if (chm->version > 3) {
304
+ sys->message(fh, "WARNING; CHM version > 3");
305
+ }
306
+
307
+ /* read the header section table */
308
+ if (sys->read(fh, &buf[0], chmhst3_SIZEOF) != chmhst3_SIZEOF) {
309
+ return MSPACK_ERR_READ;
310
+ }
311
+
312
+ /* chmhst3_OffsetCS0 does not exist in version 1 or 2 CHM files.
313
+ * The offset will be corrected later, once HS1 is read.
314
+ */
315
+ if (read_off64(&offset, &buf[chmhst_OffsetHS0], sys, fh) ||
316
+ read_off64(&chm->dir_offset, &buf[chmhst_OffsetHS1], sys, fh) ||
317
+ read_off64(&chm->sec0.offset, &buf[chmhst3_OffsetCS0], sys, fh))
318
+ {
319
+ return MSPACK_ERR_DATAFORMAT;
320
+ }
321
+
322
+ /* seek to header section 0 */
323
+ if (sys->seek(fh, offset, MSPACK_SYS_SEEK_START)) {
324
+ return MSPACK_ERR_SEEK;
325
+ }
326
+
327
+ /* read header section 0 */
328
+ if (sys->read(fh, &buf[0], chmhs0_SIZEOF) != chmhs0_SIZEOF) {
329
+ return MSPACK_ERR_READ;
330
+ }
331
+ if (read_off64(&chm->length, &buf[chmhs0_FileLen], sys, fh)) {
332
+ return MSPACK_ERR_DATAFORMAT;
333
+ }
334
+
335
+ /* seek to header section 1 */
336
+ if (sys->seek(fh, chm->dir_offset, MSPACK_SYS_SEEK_START)) {
337
+ return MSPACK_ERR_SEEK;
338
+ }
339
+
340
+ /* read header section 1 */
341
+ if (sys->read(fh, &buf[0], chmhs1_SIZEOF) != chmhs1_SIZEOF) {
342
+ return MSPACK_ERR_READ;
343
+ }
344
+
345
+ chm->dir_offset = sys->tell(fh);
346
+ chm->chunk_size = EndGetI32(&buf[chmhs1_ChunkSize]);
347
+ chm->density = EndGetI32(&buf[chmhs1_Density]);
348
+ chm->depth = EndGetI32(&buf[chmhs1_Depth]);
349
+ chm->index_root = EndGetI32(&buf[chmhs1_IndexRoot]);
350
+ chm->num_chunks = EndGetI32(&buf[chmhs1_NumChunks]);
351
+ chm->first_pmgl = EndGetI32(&buf[chmhs1_FirstPMGL]);
352
+ chm->last_pmgl = EndGetI32(&buf[chmhs1_LastPMGL]);
353
+
354
+ if (chm->version < 3) {
355
+ /* versions before 3 don't have chmhst3_OffsetCS0 */
356
+ chm->sec0.offset = chm->dir_offset + (chm->chunk_size * chm->num_chunks);
357
+ }
358
+
359
+ /* check if content offset or file size is wrong */
360
+ if (chm->sec0.offset > chm->length) {
361
+ D(("content section begins after file has ended"))
362
+ return MSPACK_ERR_DATAFORMAT;
363
+ }
364
+
365
+ /* ensure there are chunks and that chunk size is
366
+ * large enough for signature and num_entries */
367
+ if (chm->chunk_size < (pmgl_Entries + 2)) {
368
+ D(("chunk size not large enough"))
369
+ return MSPACK_ERR_DATAFORMAT;
370
+ }
371
+ if (chm->num_chunks == 0) {
372
+ D(("no chunks"))
373
+ return MSPACK_ERR_DATAFORMAT;
374
+ }
375
+
376
+ /* The chunk_cache data structure is not great; large values for num_chunks
377
+ * or num_chunks*chunk_size can exhaust all memory. Until a better chunk
378
+ * cache is implemented, put arbitrary limits on num_chunks and chunk size.
379
+ */
380
+ if (chm->num_chunks > 100000) {
381
+ D(("more than 100,000 chunks"))
382
+ return MSPACK_ERR_DATAFORMAT;
383
+ }
384
+ if ((off_t)chm->chunk_size * (off_t)chm->num_chunks > chm->length) {
385
+ D(("chunks larger than entire file"))
386
+ return MSPACK_ERR_DATAFORMAT;
387
+ }
388
+
389
+ /* common sense checks on header section 1 fields */
390
+ if ((chm->chunk_size & (chm->chunk_size - 1)) != 0) {
391
+ sys->message(fh, "WARNING; chunk size is not a power of two");
392
+ }
393
+ if (chm->first_pmgl != 0) {
394
+ sys->message(fh, "WARNING; first PMGL chunk is not zero");
395
+ }
396
+ if (chm->first_pmgl > chm->last_pmgl) {
397
+ D(("first pmgl chunk is after last pmgl chunk"))
398
+ return MSPACK_ERR_DATAFORMAT;
399
+ }
400
+ if (chm->index_root != 0xFFFFFFFF && chm->index_root > chm->num_chunks) {
401
+ D(("index_root outside valid range"))
402
+ return MSPACK_ERR_DATAFORMAT;
403
+ }
404
+
405
+ /* if we are doing a quick read, stop here! */
406
+ if (!entire) {
407
+ return MSPACK_ERR_OK;
408
+ }
409
+
410
+ /* seek to the first PMGL chunk, and reduce the number of chunks to read */
411
+ if ((x = chm->first_pmgl) != 0) {
412
+ if (sys->seek(fh,(off_t) (x * chm->chunk_size), MSPACK_SYS_SEEK_CUR)) {
413
+ return MSPACK_ERR_SEEK;
414
+ }
415
+ }
416
+ num_chunks = chm->last_pmgl - x + 1;
417
+
418
+ if (!(chunk = (unsigned char *) sys->alloc(sys, (size_t)chm->chunk_size))) {
419
+ return MSPACK_ERR_NOMEMORY;
420
+ }
421
+
422
+ /* read and process all chunks from FirstPMGL to LastPMGL */
423
+ errors = 0;
424
+ while (num_chunks--) {
425
+ /* read next chunk */
426
+ if (sys->read(fh, chunk, (int)chm->chunk_size) != (int)chm->chunk_size) {
427
+ sys->free(chunk);
428
+ return MSPACK_ERR_READ;
429
+ }
430
+
431
+ /* process only directory (PMGL) chunks */
432
+ if (EndGetI32(&chunk[pmgl_Signature]) != 0x4C474D50) continue;
433
+
434
+ if (EndGetI32(&chunk[pmgl_QuickRefSize]) < 2) {
435
+ sys->message(fh, "WARNING; PMGL quickref area is too small");
436
+ }
437
+ if (EndGetI32(&chunk[pmgl_QuickRefSize]) >
438
+ ((int)chm->chunk_size - pmgl_Entries))
439
+ {
440
+ sys->message(fh, "WARNING; PMGL quickref area is too large");
441
+ }
442
+
443
+ p = &chunk[pmgl_Entries];
444
+ end = &chunk[chm->chunk_size - 2];
445
+ num_entries = EndGetI16(end);
446
+
447
+ while (num_entries--) {
448
+ READ_ENCINT(name_len); name = p; p += name_len;
449
+ READ_ENCINT(section);
450
+ READ_ENCINT(offset);
451
+ READ_ENCINT(length);
452
+
453
+ /* empty files and directory names are stored as a file entry at
454
+ * offset 0 with length 0. We want to keep empty files, but not
455
+ * directory names, which end with a "/" */
456
+ if ((offset == 0) && (length == 0)) {
457
+ if ((name_len > 0) && (name[name_len-1] == '/')) continue;
458
+ }
459
+
460
+ if (section > 1) {
461
+ sys->message(fh, "invalid section number '%u'.", section);
462
+ continue;
463
+ }
464
+
465
+ if (!(fi = (struct mschmd_file *) sys->alloc(sys, sizeof(struct mschmd_file) + name_len + 1))) {
466
+ sys->free(chunk);
467
+ return MSPACK_ERR_NOMEMORY;
468
+ }
469
+
470
+ fi->next = NULL;
471
+ fi->filename = (char *) &fi[1];
472
+ fi->section = ((section == 0) ? (struct mschmd_section *) (&chm->sec0)
473
+ : (struct mschmd_section *) (&chm->sec1));
474
+ fi->offset = offset;
475
+ fi->length = length;
476
+ sys->copy(name, fi->filename, (size_t) name_len);
477
+ fi->filename[name_len] = '\0';
478
+
479
+ if (name[0] == ':' && name[1] == ':') {
480
+ /* system file */
481
+ if (mspack_memcmp(&name[2], &content_name[2], 31L) == 0) {
482
+ if (mspack_memcmp(&name[33], &content_name[33], 8L) == 0) {
483
+ chm->sec1.content = fi;
484
+ }
485
+ else if (mspack_memcmp(&name[33], &control_name[33], 11L) == 0) {
486
+ chm->sec1.control = fi;
487
+ }
488
+ else if (mspack_memcmp(&name[33], &spaninfo_name[33], 8L) == 0) {
489
+ chm->sec1.spaninfo = fi;
490
+ }
491
+ else if (mspack_memcmp(&name[33], &rtable_name[33], 72L) == 0) {
492
+ chm->sec1.rtable = fi;
493
+ }
494
+ }
495
+ fi->next = chm->sysfiles;
496
+ chm->sysfiles = fi;
497
+ }
498
+ else {
499
+ /* normal file */
500
+ if (link) link->next = fi; else chm->files = fi;
501
+ link = fi;
502
+ }
503
+ }
504
+
505
+ /* this is reached either when num_entries runs out, or if
506
+ * reading data from the chunk reached a premature end of chunk */
507
+ chunk_end:
508
+ if (num_entries >= 0) {
509
+ D(("chunk ended before all entries could be read"))
510
+ errors++;
511
+ }
512
+
513
+ }
514
+ sys->free(chunk);
515
+ return (errors > 0) ? MSPACK_ERR_DATAFORMAT : MSPACK_ERR_OK;
516
+ }
517
+
518
+ /***************************************
519
+ * CHMD_FAST_FIND
520
+ ***************************************
521
+ * uses PMGI index chunks and quickref data to quickly locate a file
522
+ * directly from the on-disk index.
523
+ *
524
+ * TODO: protect against infinite loops in chunks (where pgml_NextChunk
525
+ * or a PGMI index entry point to an already visited chunk)
526
+ */
527
+ static int chmd_fast_find(struct mschm_decompressor *base,
528
+ struct mschmd_header *chm, const char *filename,
529
+ struct mschmd_file *f_ptr, int f_size)
530
+ {
531
+ struct mschm_decompressor_p *self = (struct mschm_decompressor_p *) base;
532
+ struct mspack_system *sys;
533
+ struct mspack_file *fh;
534
+ const unsigned char *chunk, *p, *end;
535
+ int err = MSPACK_ERR_OK, result = -1;
536
+ unsigned int n, sec;
537
+
538
+ if (!self || !chm || !f_ptr || (f_size != sizeof(struct mschmd_file))) {
539
+ return MSPACK_ERR_ARGS;
540
+ }
541
+ sys = self->system;
542
+
543
+ /* clear the results structure */
544
+ memset(f_ptr, 0, f_size);
545
+
546
+ if (!(fh = sys->open(sys, chm->filename, MSPACK_SYS_OPEN_READ))) {
547
+ return MSPACK_ERR_OPEN;
548
+ }
549
+
550
+ /* go through PMGI chunk hierarchy to reach PMGL chunk */
551
+ if (chm->index_root < chm->num_chunks) {
552
+ n = chm->index_root;
553
+ for (;;) {
554
+ if (!(chunk = read_chunk(self, chm, fh, n))) {
555
+ sys->close(fh);
556
+ return self->error;
557
+ }
558
+
559
+ /* search PMGI/PMGL chunk. exit early if no entry found */
560
+ if ((result = search_chunk(chm, chunk, filename, &p, &end)) <= 0) {
561
+ break;
562
+ }
563
+
564
+ /* found result. loop around for next chunk if this is PMGI */
565
+ if (chunk[3] == 0x4C) break; else READ_ENCINT(n);
566
+ }
567
+ }
568
+ else {
569
+ /* PMGL chunks only, search from first_pmgl to last_pmgl */
570
+ for (n = chm->first_pmgl; n <= chm->last_pmgl;
571
+ n = EndGetI32(&chunk[pmgl_NextChunk]))
572
+ {
573
+ if (!(chunk = read_chunk(self, chm, fh, n))) {
574
+ err = self->error;
575
+ break;
576
+ }
577
+
578
+ /* search PMGL chunk. exit if file found */
579
+ if ((result = search_chunk(chm, chunk, filename, &p, &end)) > 0) {
580
+ break;
581
+ }
582
+
583
+ /* stop simple infinite loops: can't visit the same chunk twice */
584
+ if ((int)n == EndGetI32(&chunk[pmgl_NextChunk])) {
585
+ break;
586
+ }
587
+ }
588
+ }
589
+
590
+ /* if we found a file, read it */
591
+ if (result > 0) {
592
+ READ_ENCINT(sec);
593
+ f_ptr->section = (sec == 0) ? (struct mschmd_section *) &chm->sec0
594
+ : (struct mschmd_section *) &chm->sec1;
595
+ READ_ENCINT(f_ptr->offset);
596
+ READ_ENCINT(f_ptr->length);
597
+ }
598
+ else if (result < 0) {
599
+ err = MSPACK_ERR_DATAFORMAT;
600
+ }
601
+
602
+ sys->close(fh);
603
+ return self->error = err;
604
+
605
+ chunk_end:
606
+ D(("read beyond end of chunk entries"))
607
+ sys->close(fh);
608
+ return self->error = MSPACK_ERR_DATAFORMAT;
609
+ }
610
+
611
+ /* reads the given chunk into memory, storing it in a chunk cache
612
+ * so it doesn't need to be read from disk more than once
613
+ */
614
+ static unsigned char *read_chunk(struct mschm_decompressor_p *self,
615
+ struct mschmd_header *chm,
616
+ struct mspack_file *fh,
617
+ unsigned int chunk_num)
618
+ {
619
+ struct mspack_system *sys = self->system;
620
+ unsigned char *buf;
621
+
622
+ /* check arguments - most are already checked by chmd_fast_find */
623
+ if (chunk_num > chm->num_chunks) return NULL;
624
+
625
+ /* ensure chunk cache is available */
626
+ if (!chm->chunk_cache) {
627
+ size_t size = sizeof(unsigned char *) * chm->num_chunks;
628
+ if (!(chm->chunk_cache = (unsigned char **) sys->alloc(sys, size))) {
629
+ self->error = MSPACK_ERR_NOMEMORY;
630
+ return NULL;
631
+ }
632
+ memset(chm->chunk_cache, 0, size);
633
+ }
634
+
635
+ /* try to answer out of chunk cache */
636
+ if (chm->chunk_cache[chunk_num]) return chm->chunk_cache[chunk_num];
637
+
638
+ /* need to read chunk - allocate memory for it */
639
+ if (!(buf = (unsigned char *) sys->alloc(sys, chm->chunk_size))) {
640
+ self->error = MSPACK_ERR_NOMEMORY;
641
+ return NULL;
642
+ }
643
+
644
+ /* seek to block and read it */
645
+ if (sys->seek(fh, (off_t) (chm->dir_offset + (chunk_num * chm->chunk_size)),
646
+ MSPACK_SYS_SEEK_START))
647
+ {
648
+ self->error = MSPACK_ERR_SEEK;
649
+ sys->free(buf);
650
+ return NULL;
651
+ }
652
+ if (sys->read(fh, buf, (int)chm->chunk_size) != (int)chm->chunk_size) {
653
+ self->error = MSPACK_ERR_READ;
654
+ sys->free(buf);
655
+ return NULL;
656
+ }
657
+
658
+ /* check the signature. Is is PMGL or PMGI? */
659
+ if (!((buf[0] == 0x50) && (buf[1] == 0x4D) && (buf[2] == 0x47) &&
660
+ ((buf[3] == 0x4C) || (buf[3] == 0x49))))
661
+ {
662
+ self->error = MSPACK_ERR_SEEK;
663
+ sys->free(buf);
664
+ return NULL;
665
+ }
666
+
667
+ /* all OK. Store chunk in cache and return it */
668
+ return chm->chunk_cache[chunk_num] = buf;
669
+ }
670
+
671
+ /* searches a PMGI/PMGL chunk for a given filename entry. Returns -1 on
672
+ * data format error, 0 if entry definitely not found, 1 if entry
673
+ * found. In the latter case, *result and *result_end are set pointing
674
+ * to that entry's data (either the "next chunk" ENCINT for a PMGI or
675
+ * the section, offset and length ENCINTs for a PMGL).
676
+ *
677
+ * In the case of PMGL chunks, the entry has definitely been
678
+ * found. In the case of PMGI chunks, the entry which points to the
679
+ * chunk that may eventually contain that entry has been found.
680
+ */
681
+ static int search_chunk(struct mschmd_header *chm,
682
+ const unsigned char *chunk,
683
+ const char *filename,
684
+ const unsigned char **result,
685
+ const unsigned char **result_end)
686
+ {
687
+ const unsigned char *start, *end, *p;
688
+ unsigned int qr_size, num_entries, qr_entries, qr_density, name_len;
689
+ unsigned int L, R, M, sec, fname_len, entries_off, is_pmgl;
690
+ int cmp;
691
+
692
+ fname_len = strlen(filename);
693
+
694
+ /* PMGL chunk or PMGI chunk? (note: read_chunk() has already
695
+ * checked the rest of the characters in the chunk signature) */
696
+ if (chunk[3] == 0x4C) {
697
+ is_pmgl = 1;
698
+ entries_off = pmgl_Entries;
699
+ }
700
+ else {
701
+ is_pmgl = 0;
702
+ entries_off = pmgi_Entries;
703
+ }
704
+
705
+ /* Step 1: binary search first filename of each QR entry
706
+ * - target filename == entry
707
+ * found file
708
+ * - target filename < all entries
709
+ * file not found
710
+ * - target filename > all entries
711
+ * proceed to step 2 using final entry
712
+ * - target filename between two searched entries
713
+ * proceed to step 2
714
+ */
715
+ qr_size = EndGetI32(&chunk[pmgl_QuickRefSize]);
716
+ start = &chunk[chm->chunk_size - 2];
717
+ end = &chunk[chm->chunk_size - qr_size];
718
+ num_entries = EndGetI16(start);
719
+ qr_density = 1 + (1 << chm->density);
720
+ qr_entries = (num_entries + qr_density-1) / qr_density;
721
+
722
+ if (num_entries == 0) {
723
+ D(("chunk has no entries"))
724
+ return -1;
725
+ }
726
+
727
+ if (qr_size > chm->chunk_size) {
728
+ D(("quickref size > chunk size"))
729
+ return -1;
730
+ }
731
+
732
+ *result_end = end;
733
+
734
+ if (((int)qr_entries * 2) > (start - end)) {
735
+ D(("WARNING; more quickrefs than quickref space"))
736
+ qr_entries = 0; /* but we can live with it */
737
+ }
738
+
739
+ if (qr_entries > 0) {
740
+ L = 0;
741
+ R = qr_entries - 1;
742
+ do {
743
+ /* pick new midpoint */
744
+ M = (L + R) >> 1;
745
+
746
+ /* compare filename with entry QR points to */
747
+ p = &chunk[entries_off + (M ? EndGetI16(start - (M << 1)) : 0)];
748
+ READ_ENCINT(name_len);
749
+ if (p + name_len > end) goto chunk_end;
750
+ cmp = compare(filename, (char *)p, fname_len, name_len);
751
+
752
+ if (cmp == 0) break;
753
+ else if (cmp < 0) { if (M) R = M - 1; else return 0; }
754
+ else if (cmp > 0) L = M + 1;
755
+ } while (L <= R);
756
+ M = (L + R) >> 1;
757
+
758
+ if (cmp == 0) {
759
+ /* exact match! */
760
+ p += name_len;
761
+ *result = p;
762
+ return 1;
763
+ }
764
+
765
+ /* otherwise, read the group of entries for QR entry M */
766
+ p = &chunk[entries_off + (M ? EndGetI16(start - (M << 1)) : 0)];
767
+ num_entries -= (M * qr_density);
768
+ if (num_entries > qr_density) num_entries = qr_density;
769
+ }
770
+ else {
771
+ p = &chunk[entries_off];
772
+ }
773
+
774
+ /* Step 2: linear search through the set of entries reached in step 1.
775
+ * - filename == any entry
776
+ * found entry
777
+ * - filename < all entries (PMGI) or any entry (PMGL)
778
+ * entry not found, stop now
779
+ * - filename > all entries
780
+ * entry not found (PMGL) / maybe found (PMGI)
781
+ * -
782
+ */
783
+ *result = NULL;
784
+ while (num_entries-- > 0) {
785
+ READ_ENCINT(name_len);
786
+ if (p + name_len > end) goto chunk_end;
787
+ cmp = compare(filename, (char *)p, fname_len, name_len);
788
+ p += name_len;
789
+
790
+ if (cmp == 0) {
791
+ /* entry found */
792
+ *result = p;
793
+ return 1;
794
+ }
795
+
796
+ if (cmp < 0) {
797
+ /* entry not found (PMGL) / maybe found (PMGI) */
798
+ break;
799
+ }
800
+
801
+ /* read and ignore the rest of this entry */
802
+ if (is_pmgl) {
803
+ READ_ENCINT(R); /* skip section */
804
+ READ_ENCINT(R); /* skip offset */
805
+ READ_ENCINT(R); /* skip length */
806
+ }
807
+ else {
808
+ *result = p; /* store potential final result */
809
+ READ_ENCINT(R); /* skip chunk number */
810
+ }
811
+ }
812
+
813
+ /* PMGL? not found. PMGI? maybe found */
814
+ return (is_pmgl) ? 0 : (*result ? 1 : 0);
815
+
816
+ chunk_end:
817
+ D(("reached end of chunk data while searching"))
818
+ return -1;
819
+ }
820
+
821
+ #if HAVE_TOWLOWER
822
+ # if HAVE_WCTYPE_H
823
+ # include <wctype.h>
824
+ # endif
825
+ # define TOLOWER(x) towlower(x)
826
+ #elif HAVE_TOLOWER
827
+ # if HAVE_CTYPE_H
828
+ # include <ctype.h>
829
+ # endif
830
+ # define TOLOWER(x) tolower(x)
831
+ #else
832
+ # define TOLOWER(x) (((x)<0||(x)>256)?(x):mspack_tolower_map[(x)])
833
+ /* Map of char -> lowercase char for the first 256 chars. Generated with:
834
+ * LC_CTYPE=en_GB.utf-8 perl -Mlocale -le 'print map{ord(lc chr).","} 0..255'
835
+ */
836
+ static const unsigned char mspack_tolower_map[256] = {
837
+ 0,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,
838
+ 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,
839
+ 53,54,55,56,57,58,59,60,61,62,63,64,97,98,99,100,101,102,103,104,105,106,
840
+ 107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,91,92,93,94,
841
+ 95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,
842
+ 115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,
843
+ 134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,
844
+ 153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,
845
+ 172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,
846
+ 191,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,
847
+ 242,243,244,245,246,215,248,249,250,251,252,253,254,223,224,225,226,227,228,
848
+ 229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,
849
+ 248,249,250,251,252,253,254,255
850
+ };
851
+ #endif
852
+
853
+ /* decodes a UTF-8 character from s[] into c. Will not read past e. */
854
+ #define GET_UTF8_CHAR(s, e, c) do { \
855
+ unsigned char x = *s++; \
856
+ if (x < 0x80) c = x; \
857
+ else if (x < 0xC0) c = -1; \
858
+ else if (x < 0xE0) { \
859
+ c = (s >= e) ? -1 : ((x & 0x1F) << 6) | (*s++ & 0x3F); \
860
+ } \
861
+ else if (x < 0xF0) { \
862
+ c = (s+2 > e) ? -1 : ((x & 0x0F) << 12) | ((s[0] & 0x3F) << 6) \
863
+ | (s[1] & 0x3F); \
864
+ s += 2; \
865
+ } \
866
+ else if (x < 0xF8) { \
867
+ c = (s+3 > e) ? -1 : ((x & 0x07) << 18) | ((s[0] & 0x3F) << 12) \
868
+ | ((s[1] & 0x3F) << 6) | (s[2] & 0x3F); \
869
+ s += 3; \
870
+ } \
871
+ else if (x < 0xFC) { \
872
+ c = (s+4 > e) ? -1 : ((x & 0x03) << 24) | ((s[0] & 0x3F) << 18) \
873
+ | ((s[1] & 0x3F) << 12)|((s[2] & 0x3F) << 6)|(s[3] & 0x3F); \
874
+ s += 4; \
875
+ } \
876
+ else if (x < 0xFE) { \
877
+ c = (s+5>e)?-1:((x&1)<<30)|((s[0]&0x3F)<<24)|((s[1]&0x3F)<<18)| \
878
+ ((s[2] & 0x3F) << 12) | ((s[3] & 0x3F) << 6)|(s[4] & 0x3F); \
879
+ s += 5; \
880
+ } \
881
+ else c = -1; \
882
+ } while (0)
883
+
884
+ /* case-insensitively compares two UTF8 encoded strings. String length for
885
+ * both strings must be provided, null bytes are not terminators */
886
+ static inline int compare(const char *s1, const char *s2, int l1, int l2) {
887
+ register const unsigned char *p1 = (const unsigned char *) s1;
888
+ register const unsigned char *p2 = (const unsigned char *) s2;
889
+ register const unsigned char *e1 = p1 + l1, *e2 = p2 + l2;
890
+ int c1, c2;
891
+
892
+ while (p1 < e1 && p2 < e2) {
893
+ GET_UTF8_CHAR(p1, e1, c1);
894
+ GET_UTF8_CHAR(p2, e2, c2);
895
+ if (c1 == c2) continue;
896
+ c1 = TOLOWER(c1);
897
+ c2 = TOLOWER(c2);
898
+ if (c1 != c2) return c1 - c2;
899
+ }
900
+ return l1 - l2;
901
+ }
902
+
903
+
904
+ /***************************************
905
+ * CHMD_EXTRACT
906
+ ***************************************
907
+ * extracts a file from a CHM helpfile
908
+ */
909
+ static int chmd_extract(struct mschm_decompressor *base,
910
+ struct mschmd_file *file, const char *filename)
911
+ {
912
+ struct mschm_decompressor_p *self = (struct mschm_decompressor_p *) base;
913
+ struct mspack_system *sys;
914
+ struct mschmd_header *chm;
915
+ struct mspack_file *fh;
916
+ off_t bytes;
917
+
918
+ if (!self) return MSPACK_ERR_ARGS;
919
+ if (!file || !file->section) return self->error = MSPACK_ERR_ARGS;
920
+ sys = self->system;
921
+ chm = file->section->chm;
922
+
923
+ /* create decompression state if it doesn't exist */
924
+ if (!self->d) {
925
+ self->d = (struct mschmd_decompress_state *) sys->alloc(sys, sizeof(struct mschmd_decompress_state));
926
+ if (!self->d) return self->error = MSPACK_ERR_NOMEMORY;
927
+ self->d->chm = chm;
928
+ self->d->offset = 0;
929
+ self->d->state = NULL;
930
+ self->d->sys = *sys;
931
+ self->d->sys.write = &chmd_sys_write;
932
+ self->d->infh = NULL;
933
+ self->d->outfh = NULL;
934
+ }
935
+
936
+ /* open input chm file if not open, or the open one is a different chm */
937
+ if (!self->d->infh || (self->d->chm != chm)) {
938
+ if (self->d->infh) sys->close(self->d->infh);
939
+ if (self->d->state) lzxd_free(self->d->state);
940
+ self->d->chm = chm;
941
+ self->d->offset = 0;
942
+ self->d->state = NULL;
943
+ self->d->infh = sys->open(sys, chm->filename, MSPACK_SYS_OPEN_READ);
944
+ if (!self->d->infh) return self->error = MSPACK_ERR_OPEN;
945
+ }
946
+
947
+ /* open file for output */
948
+ if (!(fh = sys->open(sys, filename, MSPACK_SYS_OPEN_WRITE))) {
949
+ return self->error = MSPACK_ERR_OPEN;
950
+ }
951
+
952
+ /* if file is empty, simply creating it is enough */
953
+ if (!file->length) {
954
+ sys->close(fh);
955
+ return self->error = MSPACK_ERR_OK;
956
+ }
957
+
958
+ self->error = MSPACK_ERR_OK;
959
+
960
+ switch (file->section->id) {
961
+ case 0: /* Uncompressed section file */
962
+ /* simple seek + copy */
963
+ if (sys->seek(self->d->infh, file->section->chm->sec0.offset
964
+ + file->offset, MSPACK_SYS_SEEK_START))
965
+ {
966
+ self->error = MSPACK_ERR_SEEK;
967
+ }
968
+ else {
969
+ unsigned char buf[512];
970
+ off_t length = file->length;
971
+ while (length > 0) {
972
+ int run = sizeof(buf);
973
+ if ((off_t)run > length) run = (int)length;
974
+ if (sys->read(self->d->infh, &buf[0], run) != run) {
975
+ self->error = MSPACK_ERR_READ;
976
+ break;
977
+ }
978
+ if (sys->write(fh, &buf[0], run) != run) {
979
+ self->error = MSPACK_ERR_WRITE;
980
+ break;
981
+ }
982
+ length -= run;
983
+ }
984
+ }
985
+ break;
986
+
987
+ case 1: /* MSCompressed section file */
988
+ /* (re)initialise compression state if we it is not yet initialised,
989
+ * or we have advanced too far and have to backtrack
990
+ */
991
+ if (!self->d->state || (file->offset < self->d->offset)) {
992
+ if (self->d->state) {
993
+ lzxd_free(self->d->state);
994
+ self->d->state = NULL;
995
+ }
996
+ if (chmd_init_decomp(self, file)) break;
997
+ }
998
+
999
+ /* seek to input data */
1000
+ if (sys->seek(self->d->infh, self->d->inoffset, MSPACK_SYS_SEEK_START)) {
1001
+ self->error = MSPACK_ERR_SEEK;
1002
+ break;
1003
+ }
1004
+
1005
+ /* get to correct offset. */
1006
+ self->d->outfh = NULL;
1007
+ if ((bytes = file->offset - self->d->offset)) {
1008
+ self->error = lzxd_decompress(self->d->state, bytes);
1009
+ }
1010
+
1011
+ /* if getting to the correct offset was error free, unpack file */
1012
+ if (!self->error) {
1013
+ self->d->outfh = fh;
1014
+ self->error = lzxd_decompress(self->d->state, file->length);
1015
+ }
1016
+
1017
+ /* save offset in input source stream, in case there is a section 0
1018
+ * file between now and the next section 1 file extracted */
1019
+ self->d->inoffset = sys->tell(self->d->infh);
1020
+
1021
+ /* if an LZX error occured, the LZX decompressor is now useless */
1022
+ if (self->error) {
1023
+ if (self->d->state) lzxd_free(self->d->state);
1024
+ self->d->state = NULL;
1025
+ }
1026
+ break;
1027
+ }
1028
+
1029
+ sys->close(fh);
1030
+ return self->error;
1031
+ }
1032
+
1033
+ /***************************************
1034
+ * CHMD_SYS_WRITE
1035
+ ***************************************
1036
+ * chmd_sys_write is the internal writer function which the decompressor
1037
+ * uses. If either writes data to disk (self->d->outfh) with the real
1038
+ * sys->write() function, or does nothing with the data when
1039
+ * self->d->outfh == NULL. advances self->d->offset.
1040
+ */
1041
+ static int chmd_sys_write(struct mspack_file *file, void *buffer, int bytes) {
1042
+ struct mschm_decompressor_p *self = (struct mschm_decompressor_p *) file;
1043
+ self->d->offset += bytes;
1044
+ if (self->d->outfh) {
1045
+ return self->system->write(self->d->outfh, buffer, bytes);
1046
+ }
1047
+ return bytes;
1048
+ }
1049
+
1050
+ /***************************************
1051
+ * CHMD_INIT_DECOMP
1052
+ ***************************************
1053
+ * Initialises the LZX decompressor to decompress the compressed stream,
1054
+ * from the nearest reset offset and length that is needed for the given
1055
+ * file.
1056
+ */
1057
+ static int chmd_init_decomp(struct mschm_decompressor_p *self,
1058
+ struct mschmd_file *file)
1059
+ {
1060
+ int window_size, window_bits, reset_interval, entry, err;
1061
+ struct mspack_system *sys = self->system;
1062
+ struct mschmd_sec_mscompressed *sec;
1063
+ unsigned char *data;
1064
+ off_t length, offset;
1065
+
1066
+ sec = (struct mschmd_sec_mscompressed *) file->section;
1067
+
1068
+ /* ensure we have a mscompressed content section */
1069
+ err = find_sys_file(self, sec, &sec->content, content_name);
1070
+ if (err) return self->error = err;
1071
+
1072
+ /* ensure we have a ControlData file */
1073
+ err = find_sys_file(self, sec, &sec->control, control_name);
1074
+ if (err) return self->error = err;
1075
+
1076
+ /* read ControlData */
1077
+ if (sec->control->length < lzxcd_SIZEOF) {
1078
+ D(("ControlData file is too short"))
1079
+ return self->error = MSPACK_ERR_DATAFORMAT;
1080
+ }
1081
+ if (!(data = read_sys_file(self, sec->control))) {
1082
+ D(("can't read mscompressed control data file"))
1083
+ return self->error;
1084
+ }
1085
+
1086
+ /* check LZXC signature */
1087
+ if (EndGetI32(&data[lzxcd_Signature]) != 0x43585A4C) {
1088
+ sys->free(data);
1089
+ return self->error = MSPACK_ERR_SIGNATURE;
1090
+ }
1091
+
1092
+ /* read reset_interval and window_size and validate version number */
1093
+ switch (EndGetI32(&data[lzxcd_Version])) {
1094
+ case 1:
1095
+ reset_interval = EndGetI32(&data[lzxcd_ResetInterval]);
1096
+ window_size = EndGetI32(&data[lzxcd_WindowSize]);
1097
+ break;
1098
+ case 2:
1099
+ reset_interval = EndGetI32(&data[lzxcd_ResetInterval]) * LZX_FRAME_SIZE;
1100
+ window_size = EndGetI32(&data[lzxcd_WindowSize]) * LZX_FRAME_SIZE;
1101
+ break;
1102
+ default:
1103
+ D(("bad controldata version"))
1104
+ sys->free(data);
1105
+ return self->error = MSPACK_ERR_DATAFORMAT;
1106
+ }
1107
+
1108
+ /* free ControlData */
1109
+ sys->free(data);
1110
+
1111
+ /* find window_bits from window_size */
1112
+ switch (window_size) {
1113
+ case 0x008000: window_bits = 15; break;
1114
+ case 0x010000: window_bits = 16; break;
1115
+ case 0x020000: window_bits = 17; break;
1116
+ case 0x040000: window_bits = 18; break;
1117
+ case 0x080000: window_bits = 19; break;
1118
+ case 0x100000: window_bits = 20; break;
1119
+ case 0x200000: window_bits = 21; break;
1120
+ default:
1121
+ D(("bad controldata window size"))
1122
+ return self->error = MSPACK_ERR_DATAFORMAT;
1123
+ }
1124
+
1125
+ /* validate reset_interval */
1126
+ if (reset_interval % LZX_FRAME_SIZE) {
1127
+ D(("bad controldata reset interval"))
1128
+ return self->error = MSPACK_ERR_DATAFORMAT;
1129
+ }
1130
+
1131
+ /* which reset table entry would we like? */
1132
+ entry = file->offset / reset_interval;
1133
+ /* convert from reset interval multiple (usually 64k) to 32k frames */
1134
+ entry *= reset_interval / LZX_FRAME_SIZE;
1135
+
1136
+ /* read the reset table entry */
1137
+ if (read_reset_table(self, sec, entry, &length, &offset)) {
1138
+ /* the uncompressed length given in the reset table is dishonest.
1139
+ * the uncompressed data is always padded out from the given
1140
+ * uncompressed length up to the next reset interval */
1141
+ length += reset_interval - 1;
1142
+ length &= -reset_interval;
1143
+ }
1144
+ else {
1145
+ /* if we can't read the reset table entry, just start from
1146
+ * the beginning. Use spaninfo to get the uncompressed length */
1147
+ entry = 0;
1148
+ offset = 0;
1149
+ err = read_spaninfo(self, sec, &length);
1150
+ }
1151
+ if (err) return self->error = err;
1152
+
1153
+ /* get offset of compressed data stream:
1154
+ * = offset of uncompressed section from start of file
1155
+ * + offset of compressed stream from start of uncompressed section
1156
+ * + offset of chosen reset interval from start of compressed stream */
1157
+ self->d->inoffset = file->section->chm->sec0.offset + sec->content->offset + offset;
1158
+
1159
+ /* set start offset and overall remaining stream length */
1160
+ self->d->offset = entry * LZX_FRAME_SIZE;
1161
+ length -= self->d->offset;
1162
+
1163
+ /* initialise LZX stream */
1164
+ self->d->state = lzxd_init(&self->d->sys, self->d->infh,
1165
+ (struct mspack_file *) self, window_bits,
1166
+ reset_interval / LZX_FRAME_SIZE,
1167
+ 4096, length, 0);
1168
+ if (!self->d->state) self->error = MSPACK_ERR_NOMEMORY;
1169
+ return self->error;
1170
+ }
1171
+
1172
+ /***************************************
1173
+ * READ_RESET_TABLE
1174
+ ***************************************
1175
+ * Reads one entry out of the reset table. Also reads the uncompressed
1176
+ * data length. Writes these to offset_ptr and length_ptr respectively.
1177
+ * Returns non-zero for success, zero for failure.
1178
+ */
1179
+ static int read_reset_table(struct mschm_decompressor_p *self,
1180
+ struct mschmd_sec_mscompressed *sec,
1181
+ int entry, off_t *length_ptr, off_t *offset_ptr)
1182
+ {
1183
+ struct mspack_system *sys = self->system;
1184
+ unsigned char *data;
1185
+ unsigned int pos, entrysize;
1186
+
1187
+ /* do we have a ResetTable file? */
1188
+ int err = find_sys_file(self, sec, &sec->rtable, rtable_name);
1189
+ if (err) return 0;
1190
+
1191
+ /* read ResetTable file */
1192
+ if (sec->rtable->length < lzxrt_headerSIZEOF) {
1193
+ D(("ResetTable file is too short"))
1194
+ return 0;
1195
+ }
1196
+ if (!(data = read_sys_file(self, sec->rtable))) {
1197
+ D(("can't read reset table"))
1198
+ return 0;
1199
+ }
1200
+
1201
+ /* check sanity of reset table */
1202
+ if (EndGetI32(&data[lzxrt_FrameLen]) != LZX_FRAME_SIZE) {
1203
+ D(("bad reset table frame length"))
1204
+ sys->free(data);
1205
+ return 0;
1206
+ }
1207
+
1208
+ /* get the uncompressed length of the LZX stream */
1209
+ if (read_off64(length_ptr, &data[lzxrt_UncompLen], sys, self->d->infh)) {
1210
+ sys->free(data);
1211
+ return 0;
1212
+ }
1213
+
1214
+ entrysize = EndGetI32(&data[lzxrt_EntrySize]);
1215
+ pos = EndGetI32(&data[lzxrt_TableOffset]) + (entry * entrysize);
1216
+
1217
+ /* ensure reset table entry for this offset exists */
1218
+ if (entry < EndGetI32(&data[lzxrt_NumEntries]) &&
1219
+ pos <= (sec->rtable->length - entrysize))
1220
+ {
1221
+ switch (entrysize) {
1222
+ case 4:
1223
+ *offset_ptr = EndGetI32(&data[pos]);
1224
+ err = 0;
1225
+ break;
1226
+ case 8:
1227
+ err = read_off64(offset_ptr, &data[pos], sys, self->d->infh);
1228
+ break;
1229
+ default:
1230
+ D(("reset table entry size neither 4 nor 8"))
1231
+ err = 1;
1232
+ break;
1233
+ }
1234
+ }
1235
+ else {
1236
+ D(("bad reset interval"))
1237
+ err = 1;
1238
+ }
1239
+
1240
+ /* free the reset table */
1241
+ sys->free(data);
1242
+
1243
+ /* return success */
1244
+ return (err == 0);
1245
+ }
1246
+
1247
+ /***************************************
1248
+ * READ_SPANINFO
1249
+ ***************************************
1250
+ * Reads the uncompressed data length from the spaninfo file.
1251
+ * Returns zero for success or a non-zero error code for failure.
1252
+ */
1253
+ static int read_spaninfo(struct mschm_decompressor_p *self,
1254
+ struct mschmd_sec_mscompressed *sec,
1255
+ off_t *length_ptr)
1256
+ {
1257
+ struct mspack_system *sys = self->system;
1258
+ unsigned char *data;
1259
+
1260
+ /* find SpanInfo file */
1261
+ int err = find_sys_file(self, sec, &sec->spaninfo, spaninfo_name);
1262
+ if (err) return MSPACK_ERR_DATAFORMAT;
1263
+
1264
+ /* check it's large enough */
1265
+ if (sec->spaninfo->length != 8) {
1266
+ D(("SpanInfo file is wrong size"))
1267
+ return MSPACK_ERR_DATAFORMAT;
1268
+ }
1269
+
1270
+ /* read the SpanInfo file */
1271
+ if (!(data = read_sys_file(self, sec->spaninfo))) {
1272
+ D(("can't read SpanInfo file"))
1273
+ return self->error;
1274
+ }
1275
+
1276
+ /* get the uncompressed length of the LZX stream */
1277
+ err = read_off64(length_ptr, data, sys, self->d->infh);
1278
+
1279
+ sys->free(data);
1280
+ return (err) ? MSPACK_ERR_DATAFORMAT : MSPACK_ERR_OK;
1281
+ }
1282
+
1283
+ /***************************************
1284
+ * FIND_SYS_FILE
1285
+ ***************************************
1286
+ * Uses chmd_fast_find to locate a system file, and fills out that system
1287
+ * file's entry and links it into the list of system files. Returns zero
1288
+ * for success, non-zero for both failure and the file not existing.
1289
+ */
1290
+ static int find_sys_file(struct mschm_decompressor_p *self,
1291
+ struct mschmd_sec_mscompressed *sec,
1292
+ struct mschmd_file **f_ptr, const char *name)
1293
+ {
1294
+ struct mspack_system *sys = self->system;
1295
+ struct mschmd_file result;
1296
+
1297
+ /* already loaded */
1298
+ if (*f_ptr) return MSPACK_ERR_OK;
1299
+
1300
+ /* try using fast_find to find the file - return DATAFORMAT error if
1301
+ * it fails, or successfully doesn't find the file */
1302
+ if (chmd_fast_find((struct mschm_decompressor *) self, sec->base.chm,
1303
+ name, &result, (int)sizeof(result)) || !result.section)
1304
+ {
1305
+ return MSPACK_ERR_DATAFORMAT;
1306
+ }
1307
+
1308
+ if (!(*f_ptr = (struct mschmd_file *) sys->alloc(sys, sizeof(result)))) {
1309
+ return MSPACK_ERR_NOMEMORY;
1310
+ }
1311
+
1312
+ /* copy result */
1313
+ *(*f_ptr) = result;
1314
+ (*f_ptr)->filename = (char *) name;
1315
+
1316
+ /* link file into sysfiles list */
1317
+ (*f_ptr)->next = sec->base.chm->sysfiles;
1318
+ sec->base.chm->sysfiles = *f_ptr;
1319
+ return MSPACK_ERR_OK;
1320
+ }
1321
+
1322
+ /***************************************
1323
+ * READ_SYS_FILE
1324
+ ***************************************
1325
+ * Allocates memory for a section 0 (uncompressed) file and reads it into
1326
+ * memory.
1327
+ */
1328
+ static unsigned char *read_sys_file(struct mschm_decompressor_p *self,
1329
+ struct mschmd_file *file)
1330
+ {
1331
+ struct mspack_system *sys = self->system;
1332
+ unsigned char *data = NULL;
1333
+ int len;
1334
+
1335
+ if (!file || !file->section || (file->section->id != 0)) {
1336
+ self->error = MSPACK_ERR_DATAFORMAT;
1337
+ return NULL;
1338
+ }
1339
+
1340
+ len = (int) file->length;
1341
+
1342
+ if (!(data = (unsigned char *) sys->alloc(sys, (size_t) len))) {
1343
+ self->error = MSPACK_ERR_NOMEMORY;
1344
+ return NULL;
1345
+ }
1346
+ if (sys->seek(self->d->infh, file->section->chm->sec0.offset
1347
+ + file->offset, MSPACK_SYS_SEEK_START))
1348
+ {
1349
+ self->error = MSPACK_ERR_SEEK;
1350
+ sys->free(data);
1351
+ return NULL;
1352
+ }
1353
+ if (sys->read(self->d->infh, data, len) != len) {
1354
+ self->error = MSPACK_ERR_READ;
1355
+ sys->free(data);
1356
+ return NULL;
1357
+ }
1358
+ return data;
1359
+ }
1360
+
1361
+ /***************************************
1362
+ * CHMD_ERROR
1363
+ ***************************************
1364
+ * returns the last error that occurred
1365
+ */
1366
+ static int chmd_error(struct mschm_decompressor *base) {
1367
+ struct mschm_decompressor_p *self = (struct mschm_decompressor_p *) base;
1368
+ return (self) ? self->error : MSPACK_ERR_ARGS;
1369
+ }
1370
+
1371
+ /***************************************
1372
+ * READ_OFF64
1373
+ ***************************************
1374
+ * Reads a 64-bit signed integer from memory in Intel byte order.
1375
+ * If running on a system with a 64-bit off_t, this is simply done.
1376
+ * If running on a system with a 32-bit off_t, offsets up to 0x7FFFFFFF
1377
+ * are accepted, offsets beyond that cause an error message.
1378
+ */
1379
+ static int read_off64(off_t *var, unsigned char *mem,
1380
+ struct mspack_system *sys, struct mspack_file *fh)
1381
+ {
1382
+ #ifdef LARGEFILE_SUPPORT
1383
+ *var = EndGetI64(mem);
1384
+ #else
1385
+ *var = EndGetI32(mem);
1386
+ if ((*var & 0x80000000) || EndGetI32(mem+4)) {
1387
+ sys->message(fh, (char *)largefile_msg);
1388
+ return 1;
1389
+ }
1390
+ #endif
1391
+ return 0;
1392
+ }