1 #ifdef HAVE_CONFIG_H
2 # include <config.h>
3 #endif
4 
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <unistd.h>
8 #include <string.h>
9 
10 #include <mspack.h>
11 #include "system.h"
12 
13 #define FILENAME ".chminfo-temp"
14 
load_sys_data(struct mschm_decompressor * chmd,struct mschmd_header * chm,const char * filename,off_t * length_ptr)15 unsigned char *load_sys_data(struct mschm_decompressor *chmd,
16                              struct mschmd_header *chm,
17                              const char *filename,
18                              off_t *length_ptr)
19 {
20   struct mschmd_file *file;
21   unsigned char *data;
22   FILE *fh;
23 
24   for (file = chm->sysfiles; file; file = file->next) {
25     if (strcmp(file->filename, filename) == 0) break;
26   }
27   if (!file || file->section->id != 0) return NULL;
28   if (chmd->extract(chmd, file, FILENAME)) return NULL;
29   if (length_ptr) *length_ptr = file->length;
30   if (!(data = (unsigned char *) malloc((size_t) file->length))) return NULL;
31   if ((fh = fopen(FILENAME, "rb"))) {
32     fread(data, (size_t) file->length, 1, fh);
33     fclose(fh);
34   }
35   else {
36     free(data);
37     data = NULL;
38   }
39   unlink(FILENAME);
40   return data;
41 }
42 
guid(unsigned char * data)43 char *guid(unsigned char *data) {
44   static char result[43];
45   snprintf(result, sizeof(result),
46            "{%08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X%02X%02X%02X}",
47            EndGetI32(&data[0]),
48            data[4] | (data[5] << 8),
49            data[6] | (data[7] << 8),
50            data[8] | (data[9] << 8),
51            data[10], data[11], data[12], data[13],
52            data[14], data[15], data[16], data[17]);
53   return result;
54 }
55 
56 #define READ_ENCINT(var, label) do {                    \
57     (var) = 0;                                          \
58     do {                                                \
59         if (p > &chunk[chm->chunk_size-2]) goto label;  \
60         (var) = ((var) << 7) | (*p & 0x7F);             \
61     } while (*p++ & 0x80);                              \
62 } while (0)
63 
print_dir(struct mschmd_header * chm,char * filename)64 void print_dir(struct mschmd_header *chm, char *filename) {
65   unsigned char dir[0x54], *chunk;
66   unsigned int i;
67   FILE *fh;
68 
69   if (!(chunk = (unsigned char *) malloc(chm->chunk_size))) return;
70 
71   if ((fh = fopen(filename, "rb"))) {
72 #if HAVE_FSEEKO
73     fseeko(fh, chm->dir_offset - 84, SEEK_SET);
74 #else
75     fseek(fh, chm->dir_offset - 84, SEEK_SET);
76 #endif
77     fread(&dir[0], 84, 1, fh);
78     printf("  chmhs1_Signature  = %4.4s\n", &dir[0]);
79     printf("  chmhs1_Version    = %d\n", EndGetI32(&dir[4]));
80     printf("  chmhs1_HeaderLen  = %d\n", EndGetI32(&dir[8]));
81     printf("  chmhs1_Unknown1   = %d\n", EndGetI32(&dir[12]));
82     printf("  chmhs1_ChunkSize  = %d\n", EndGetI32(&dir[16]));
83     printf("  chmhs1_Density    = %d\n", EndGetI32(&dir[20]));
84     printf("  chmhs1_Depth      = %d\n", EndGetI32(&dir[24]));
85     printf("  chmhs1_IndexRoot  = %d\n", EndGetI32(&dir[28]));
86     printf("  chmhs1_FirstPMGL  = %d\n", EndGetI32(&dir[32]));
87     printf("  chmhs1_LastPMGL   = %d\n", EndGetI32(&dir[36]));
88     printf("  chmhs1_Unknown2   = %d\n", EndGetI32(&dir[40]));
89     printf("  chmhs1_NumChunks  = %d\n", EndGetI32(&dir[44]));
90     printf("  chmhs1_LanguageID = %d\n", EndGetI32(&dir[48]));
91     printf("  chmhs1_GUID       = %s\n", guid(&dir[52]));
92     printf("  chmhs1_Unknown3   = %d\n", EndGetI32(&dir[68]));
93     printf("  chmhs1_Unknown4   = %d\n", EndGetI32(&dir[72]));
94     printf("  chmhs1_Unknown5   = %d\n", EndGetI32(&dir[76]));
95     printf("  chmhs1_Unknown6   = %d\n", EndGetI32(&dir[80]));
96 
97     for (i = 0; i < chm->num_chunks; i++) {
98       unsigned int num_entries, quickref_size, j, k;
99       unsigned char *p, *name;
100       printf("  CHUNK %u:\n", i);
101       fread(chunk, chm->chunk_size, 1, fh);
102 
103       if ((chunk[0] == 'P') && (chunk[1] == 'M') &&
104           (chunk[2] == 'G') && (chunk[3] == 'L'))
105       {
106         k = chm->chunk_size - 2;
107         num_entries = chunk[k] | (chunk[k+1] << 8);
108         quickref_size = EndGetI32(&chunk[4]);
109         if (quickref_size > (chm->chunk_size - 20)) {
110             printf("    QR size of %d too large (max is %d)\n",
111                    quickref_size, chm->chunk_size - 20);
112             quickref_size = chm->chunk_size - 20;
113         }
114         printf("    PMGL entries=%u qrsize=%u zero=%u prev=%d next=%d\n",
115                num_entries, quickref_size, EndGetI32(&chunk[8]),
116                EndGetI32(&chunk[12]), EndGetI32(&chunk[16]));
117 
118         printf("    QR: entry %4u = offset %u\n", 0, 20);
119         j = (1 << chm->density) + 1;
120         while (j < num_entries) {
121           k -= 2;
122           if (k < (chm->chunk_size - quickref_size)) break;
123           printf("    QR: entry %4u = offset %u\n",
124                  j, (chunk[k] | (chunk[k+1] << 8)) + 20);
125           j += (1 << chm->density) + 1;
126         }
127 
128         p = &chunk[20];
129         for (j = 0; j < num_entries; j++) {
130           unsigned int name_len = 0, section = 0, offset = 0, length = 0;
131           printf("    %4d: ", (int) (p - &chunk[0]));
132           READ_ENCINT(name_len, PMGL_end); name = p; p += name_len;
133           READ_ENCINT(section, PMGL_end);
134           READ_ENCINT(offset, PMGL_end);
135           READ_ENCINT(length, PMGL_end);
136           printf("sec=%u off=%-10u len=%-10u name=\"",section,offset,length);
137           if (name_len) fwrite(name, 1, name_len, stdout);
138           printf("\"\n");
139         }
140       PMGL_end:
141         if (j != num_entries) printf("premature end of chunk\n");
142 
143       }
144       else if  ((chunk[0] == 'P') && (chunk[1] == 'M') &&
145                 (chunk[2] == 'G') && (chunk[3] == 'I'))
146       {
147         k = chm->chunk_size - 2;
148         num_entries = chunk[k] | (chunk[k+1] << 8);
149         quickref_size = EndGetI32(&chunk[4]);
150         printf("    PMGI entries=%u free=%u\n", num_entries, quickref_size);
151 
152         printf("    QR: entry %4u = offset %u\n", 0, 8);
153         j = (1 << chm->density) + 1;
154         while (j < num_entries) {
155           k -= 2;
156           printf("    QR: entry %4u = offset %u\n",
157                  j, (chunk[k] | (chunk[k+1] << 8)) + 8);
158           j += (1 << chm->density) + 1;
159         }
160 
161         p = &chunk[8];
162         for (j = 0; j < num_entries; j++) {
163           unsigned int name_len, section;
164           printf("    %4d: ", (int) (p - &chunk[0]));
165           READ_ENCINT(name_len, PMGI_end); name = p; p += name_len;
166           READ_ENCINT(section, PMGI_end);
167           printf("chunk=%-4u name=\"",section);
168           if (name_len) fwrite(name, 1, name_len, stdout);
169           printf("\"\n");
170         }
171       PMGI_end:
172         if (j != num_entries) printf("premature end of chunk\n");
173       }
174       else {
175         printf("    unknown format\n");
176       }
177     }
178 
179     fclose(fh);
180   }
181 }
182 
183 
main(int argc,char * argv[])184 int main(int argc, char *argv[]) {
185   struct mschm_decompressor *chmd;
186   struct mschmd_header *chm;
187   struct mschmd_file *file;
188   unsigned int numf, i;
189   unsigned char *data;
190   off_t pos, len;
191 
192   setbuf(stdout, NULL);
193   setbuf(stderr, NULL);
194 
195   MSPACK_SYS_SELFTEST(i);
196   if (i) return 0;
197 
198   if ((chmd = mspack_create_chm_decompressor(NULL))) {
199     for (argv++; *argv; argv++) {
200       printf("%s\n", *argv);
201       if ((chm = chmd->open(chmd, *argv))) {
202         printf("  chmhead_Version     %u\n",      chm->version);
203         printf("  chmhead_Timestamp   %u\n",      chm->timestamp);
204         printf("  chmhead_LanguageID  %u\n",      chm->language);
205         printf("  chmhs0_FileLen      %" LD "\n", chm->length);
206         printf("  chmhst_OffsetHS1    %" LD "\n", chm->dir_offset);
207         printf("  chmhst3_OffsetCS0   %" LD "\n", chm->sec0.offset);
208 
209         print_dir(chm, *argv);
210 
211         if ((data = load_sys_data(chmd, chm,
212              "::DataSpace/Storage/MSCompressed/ControlData", &len)))
213         {
214           printf("  lzxcd_Length        %u\n",    EndGetI32(&data[0]));
215           printf("  lzxcd_Signature     %4.4s\n", &data[4]);
216           printf("  lzxcd_Version       %u\n",    EndGetI32(&data[8]));
217           printf("  lzxcd_ResetInterval %u\n",    EndGetI32(&data[12]));
218           printf("  lzxcd_WindowSize    %u\n",    EndGetI32(&data[16]));
219           printf("  lzxcd_CacheSize     %u\n",    EndGetI32(&data[20]));
220           printf("  lzxcd_Unknown1      %u\n",    EndGetI32(&data[24]));
221           free(data);
222         }
223 
224         if ((data = load_sys_data(chmd, chm,
225              "::DataSpace/Storage/MSCompressed/Transform/{7FC28940-"
226              "9D31-11D0-9B27-00A0C91E9C7C}/InstanceData/ResetTable", &len)))
227         {
228           off_t contents = chm->sec0.offset;
229           printf("  lzxrt_Unknown1      %u\n",   EndGetI32(&data[0]));
230           printf("  lzxrt_NumEntries    %u\n",   EndGetI32(&data[4]));
231           printf("  lzxrt_EntrySize     %u\n",   EndGetI32(&data[8]));
232           printf("  lzxrt_TableOffset   %u\n",   EndGetI32(&data[12]));
233           printf("  lzxrt_UncompLen     %llu\n", EndGetI64(&data[16]));
234           printf("  lzxrt_CompLen       %llu\n", EndGetI64(&data[24]));
235           printf("  lzxrt_FrameLen      %u\n",   EndGetI32(&data[32]));
236 
237           for (file = chm->sysfiles; file; file = file->next) {
238             if (strcmp(file->filename,
239                        "::DataSpace/Storage/MSCompressed/Content") == 0)
240             {
241               contents += file->offset;
242               break;
243             }
244           }
245 
246           printf("  - reset table (uncomp offset -> stream offset "
247                  "[real offset, length in file]\n");
248 
249           numf = EndGetI32(&data[4]);
250           pos = ((unsigned int) EndGetI32(&data[12]));
251           switch (EndGetI32(&data[8])) {
252           case 4:
253             for (i = 0; i < numf && pos < len; i++, pos += 4) {
254               unsigned int rtdata = EndGetI32(&data[pos]);
255               printf("    %-10u -> %-10u [ %" LU " %u ]\n",
256                      i * EndGetI32(&data[32]),
257                      rtdata,
258                      contents + rtdata,
259                      (i == (numf-1))
260                      ? (EndGetI32(&data[24]) - rtdata)
261                      : (EndGetI32(&data[pos + 4]) - rtdata)
262                      );
263             }
264             break;
265           case 8:
266             for (i = 0; i < numf && pos < len; i++, pos += 8) {
267               unsigned long long int rtdata = EndGetI64(&data[pos]);
268               printf("    %-10llu -> %-10llu [ %llu %llu ]\n",
269                      i * EndGetI64(&data[32]), rtdata, contents + rtdata,
270                      (i == (numf-1))
271                      ? (EndGetI64(&data[24]) - rtdata)
272                      : (EndGetI64(&data[pos + 8]) - rtdata)
273                      );
274             }
275             break;
276           }
277           free(data);
278         }
279         chmd->close(chmd, chm);
280       }
281     }
282     mspack_destroy_chm_decompressor(chmd);
283   }
284   return 0;
285 }
286