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