1 /*
2 * leafpak.c
3 * decode leaf archive file.
4 * 10/06/1997 by TF
5 */
6 /*
7 * Changes:
8 * 3/12/1998 generalize to support the fandisk format.
9 * 10/17/1997 Add auto key calculation.
10 * Support maxxdata.pak in 'Saorin to Issho!!'
11 */
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15
16 #include "leafpak.h"
17
18 static int lp1_get_file_num(FILE *);
19 static void lp1_set_type(LEAFPACK *);
20 static void lp1_regularize_name(char *);
21 static void lp1_guess_key(LEAFPACK *);
22
23 #ifdef DEBUG
24 static void lp1_verify_key(LEAFPACK *);
25 #endif
26
27 /*
28 * Open a leafpak(type1) archive.
29 */
lp1_open(LEAFPACK * lp)30 int lp1_open(LEAFPACK *lp)
31 {
32 lp->file_num = lp1_get_file_num(lp->fp);
33 lp1_set_type(lp);
34
35 if (lp->type == UNKNOWN) {
36 lp1_guess_key(lp);
37 }
38
39 return 0;
40 }
41
42 /*
43 * Extract all archive into current directory. Archive file
44 * shuold already be opend.
45 */
lp1_extract_all(LEAFPACK * lp)46 void lp1_extract_all(LEAFPACK *lp)
47 {
48 int i, k;
49 int val, pos;
50 FILE *fp;
51
52 for (i = 0; i < lp->file_num; i++) {
53 fp = fopen(lp->name[i], "w");
54 printf("extracting %s...", lp->name[i]);
55 fflush(stdout);
56
57 pos = lp->pos[i];
58 fseek(lp->fp, (long)pos, SEEK_SET);
59 k = 0;
60 while (pos != lp->nextpos[i]) {
61 val = (fgetc(lp->fp) - lp->key[k]) & 0xff;
62 k = (++k) % KEY_LEN;
63 fputc(val, fp);
64 pos++;
65 }
66 fclose(fp);
67 printf("done.\n");
68 }
69 printf("\nAll files are extracted.\n");
70 }
71
72 /*
73 * Extract a file specified by name from archive. Archive file
74 * shuold already be opened.
75 */
lp1_extract_file(LEAFPACK * lp,const char * file)76 int lp1_extract_file(LEAFPACK *lp, const char *file)
77 {
78 int i = 0, k = 0;
79 int val, pos;
80 FILE *fp;
81
82 /* lookup table */
83 while (i < lp->file_num) {
84 if (!strcasecmp(file, lp->name[i])) {
85 break;
86 }
87 i++;
88 }
89
90 if (i == lp->file_num) {
91 fprintf(stderr, "%s isn't included in this archive.\n", file);
92 return 1;
93 }
94
95 printf("extracting %s...", file);
96 fflush(stdout);
97
98 fp = fopen(file, "w");
99 pos = lp->pos[i];
100 fseek(lp->fp, (long)pos, SEEK_SET);
101 while (pos != lp->nextpos[i]) {
102 val = (fgetc(lp->fp) - lp->key[k]) & 0xff;
103 k = (++k) % KEY_LEN;
104 fputc(val, fp);
105 pos++;
106 }
107 fclose(fp);
108 printf("done.\n");
109
110 return 0;
111 }
112
113 /*
114 * a char array 'buf' should have size more than 9.
115 */
lp1_read_magic(LEAFPACK * lp,const char * file,char * buf)116 void lp1_read_magic(LEAFPACK *lp, const char *file, char *buf)
117 {
118 int i = 0;
119 int pos;
120
121 /* lookup table */
122 while (i < lp->file_num) {
123 if (!strcasecmp(file, lp->name[i])) {
124 break;
125 }
126 i++;
127 }
128
129 if (i == lp->file_num) {
130 printf("%s isn't included in archive.\n", file);
131 buf[0] = '\0';
132 return;
133 }
134
135 pos = lp->pos[i];
136 fseek(lp->fp, (long)pos, SEEK_SET);
137 for (i = 0; i < 8; i++) {
138 buf[i] = (fgetc(lp->fp) - lp->key[i]) & 0xff;
139 }
140 buf[8] = '\0';
141
142 return;
143 }
144
lp1_extract_file_to_data(LEAFPACK * lp,const char * file,int * size)145 unsigned char *lp1_extract_file_to_data(LEAFPACK *lp, const char *file, int *size)
146 {
147 int i = 0, j = 0, k = 0;
148 int pos;
149 unsigned char *res;
150
151 /* lookup table */
152 while (i < lp->file_num) {
153 if (!strcasecmp(file, lp->name[i])) {
154 break;
155 }
156 i++;
157 }
158
159 if (i == lp->file_num) {
160 fprintf(stderr, "%s isn't included in archive.\n", file);
161 return NULL;
162 }
163
164 *size = lp->len[i];
165
166 printf("extracting %s...", file);
167 fflush(stdout);
168
169 res = (unsigned char *)calloc(lp->len[i], sizeof(unsigned char));
170 pos = lp->pos[i];
171 fseek(lp->fp, (long)pos, SEEK_SET);
172 while (pos != lp->nextpos[i]) {
173 res[j] = (fgetc(lp->fp) - lp->key[k]) & 0xff;
174 k = (++k) % KEY_LEN;
175 pos++;
176 j++;
177 }
178 printf("done.\n");
179
180 return res;
181 }
182
183 /*
184 * PRIVATE FUNCTIONS
185 */
lp1_get_file_num(FILE * fp)186 static int lp1_get_file_num(FILE *fp)
187 {
188 int u, l;
189
190 l = fgetc(fp);
191 u = fgetc(fp);
192
193 return u << 8 | l;
194 }
195
196 /*
197 * Type is set by the number of files included in an archive file.
198 * (And this type is used to select an encryption key!)
199 * Then we can't extract an unkown archive.
200 */
201 /*
202 * 10/16/1997: Add auto key calculation feature(experimental).
203 */
lp1_set_type(LEAFPACK * lp)204 static void lp1_set_type(LEAFPACK *lp)
205 {
206 lp1_guess_key(lp);
207
208 if (lp->file_num == 0x0248 || lp->file_num == 0x03e1) {
209 lp->type = TOHEART;
210 return;
211 } else if (lp->file_num == 0x01fb) {
212 lp->type = KIZUWIN;
213 return;
214 } else if (lp->file_num == 0x0193) {
215 lp->type = SIZUWIN;
216 return;
217 } else if (lp->file_num == 0x0072) {
218 lp->type = SAORIN;
219 } else {
220 lp->type = UNKNOWN;
221 }
222
223 #ifdef DEBUG
224 lp1_verify_key(lp);
225 #endif
226 }
227
228 #ifdef DEBUG
lp1_verify_key(LEAFPACK * lp)229 static void lp1_verify_key(LEAFPACK *lp)
230 {
231 int i;
232 int result = TRUE;
233
234 if (lp->type == UNKNOWN || lp->type == SAORIN) {
235 return;
236 }
237
238 for (i = 0; i < KEY_LEN; i++) {
239 if (key_const[lp->type][i] != lp->key[i]) {
240 fprintf(stderr, "Warning: An auto-calculated key is bad.\n");
241 result = FALSE;
242 break;
243 }
244 }
245
246 if (result == FALSE) {
247 for (i = 0; i < KEY_LEN; i++) {
248 lp->key[i] = key_const[lp->type][i];
249 }
250 }
251 }
252 #endif
253
lp1_extract_table(LEAFPACK * lp)254 void lp1_extract_table(LEAFPACK *lp)
255 {
256 int i, j;
257 int k = 0;
258 int b[4];
259
260 /* set a position to the head of the header part */
261 fseek(lp->fp, (long)(-24 * lp->file_num), SEEK_END);
262
263 for (i = 0; i < lp->file_num; i++) {
264 /* get filename */
265 for (j = 0; j < 12; j++) {
266 lp->name[i][j] = (fgetc(lp->fp) - lp->key[k]) & 0xff;
267 k = (++k) % KEY_LEN;
268 }
269 lp1_regularize_name(lp->name[i]);
270
271 /* a position in the archive file */
272 for (j = 0; j < 4; j++) {
273 b[j] = (fgetc(lp->fp) - lp->key[k]) & 0xff;
274 k = (++k) % KEY_LEN;
275 }
276 lp->pos[i] = (b[3] << 24) | (b[2] << 16) | (b[1] << 8) | b[0];
277
278 /* file length */
279 for (j = 0; j < 4; j++) {
280 b[j] = (fgetc(lp->fp) - lp->key[k]) & 0xff;
281 k = (++k) % KEY_LEN;
282 }
283 lp->len[i] = (b[3] << 24) | (b[2] << 16) | (b[1] << 8) | b[0];
284
285 /* the head of the next file */
286 for (j = 0; j < 4; j++) {
287 b[j] = (fgetc(lp->fp) - lp->key[k]) & 0xff;
288 k = (++k) % KEY_LEN;
289 }
290 lp->nextpos[i] = (b[3] << 24) | (b[2] << 16) | (b[1] << 8) | b[0];
291 }
292 }
293
lp1_regularize_name(char * name)294 static void lp1_regularize_name(char *name)
295 {
296 char buf[12];
297 int i = 0;
298
299 strcpy(buf, name);
300 while (i < 8 && buf[i] != 0x20) {
301 name[i] = buf[i];
302 i++;
303 }
304
305 name[i++] = '.';
306
307 /* file extention */
308 name[i++] = buf[8];
309 name[i++] = buf[9];
310 name[i++] = buf[10];
311
312 name[i] = '\0';
313 }
314
lp1_print_type(LEAFPACK * lp)315 void lp1_print_type(LEAFPACK *lp)
316 {
317 printf("Archive file: ");
318
319 switch (lp->type) {
320 case SIZUWIN:
321 printf("SHIZUKU for Windows95\n\n");
322 break;
323 case KIZUWIN:
324 printf("KIZUATO for Windows95\n\n");
325 break;
326 case TOHEART:
327 printf("To Heart\n\n");
328 break;
329 case SAORIN:
330 printf("Saorin to Issho!! (maxxdata.pak)\n\n");
331 break;
332 default:
333 printf("Unknown\n\n");
334 }
335 }
336
lp1_print_table(LEAFPACK * lp,int verbose)337 void lp1_print_table(LEAFPACK *lp, int verbose)
338 {
339 int i;
340
341 if (verbose == TRUE) {
342 printf("Key: ");
343 for (i = 0; i < KEY_LEN; i++) {
344 printf("%02x ", lp->key[i]);
345 }
346 printf("\n\n");
347 }
348
349 if (verbose == TRUE) {
350 printf("Filename Position Length Next\n");
351 printf("------------ -------- ------- --------\n");
352 } else {
353 printf("Filename Length\n");
354 printf("------------ -------\n");
355 }
356 for (i = 0; i < lp->file_num; i++) {
357 printf("%12s ", lp->name[i]);
358 if (verbose == TRUE) {
359 printf("%08x ", lp->pos[i]);
360 }
361 printf("%8d ", lp->len[i]);
362 if (verbose == TRUE) {
363 printf("%08x", lp->nextpos[i]);
364 }
365 printf("\n");
366 }
367 printf("%d files.\n", lp->file_num);
368 }
369
370 /*
371 * Calculate key using length of archive data.
372 * To use this function, the archive must include at least 3 files.
373 */
lp1_guess_key(LEAFPACK * lp)374 static void lp1_guess_key(LEAFPACK *lp)
375 {
376 int buf[72];
377 int i;
378
379 /* find the top of table */
380 fseek(lp->fp, (long)(-24 * lp->file_num), SEEK_END);
381
382 /* read 3 table entries, needed to calculate keys. */
383 for (i = 0; i < 72; i++) {
384 buf[i] = fgetc(lp->fp);
385 }
386
387 /* zero */
388 lp->key[0] = buf[11];
389
390 /* 1st position, (maybe :-)) constant */
391 lp->key[1] = (buf[12] - 0x0a) & 0xff;
392 lp->key[2] = buf[13];
393 lp->key[3] = buf[14];
394 lp->key[4] = buf[15];
395
396 /* 2nd position, from 1st next position */
397 lp->key[5] = (buf[38] - buf[22] + lp->key[0]) & 0xff;
398 lp->key[6] = (buf[39] - buf[23] + lp->key[1]) & 0xff;
399
400 /* 3rd position, from 2nd next position */
401 lp->key[7] = (buf[62] - buf[46] + lp->key[2]) & 0xff;
402 lp->key[8] = (buf[63] - buf[47] + lp->key[3]) & 0xff;
403
404 /* 1st next position, from 2nd position */
405 lp->key[9] = (buf[20] - buf[36] + lp->key[3]) & 0xff;
406 lp->key[10] = (buf[21] - buf[37] + lp->key[4]) & 0xff;
407 }
408 /* end of file */
409