summaryrefslogtreecommitdiff
path: root/src/export/extract_version_2.c
blob: 6686dca0618ac0000aab25365a4c8c6cdce6ac03 (plain)
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
#include <fcntl.h>
#include "mystdlib.h"
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>

/* lha header:

00 Header length
01 Header checksum [02-length]
02 0x2d  ('-')
03 0x6c  ('l')
04 0x68  ('h')
05 0x??  ('0' or '5') unsure
06 0x2d  ('-')
07 0x??  LSB of compressed size
08 0x??  ..
09 0x00  ..
10 0x00  MSB of compressed size, i.e. 0
..
21       Length of path name


*/

static uint8_t mantra_in[] = { 0x68, 0x35, 0x2d, 0x6c };

int main( int args, char **argv )
{
  int      filenum = 0, run = 1;
  size_t   offset = 0, reported = 0, enc_len = 32;
  ssize_t  oldoffset = -1;
  uint8_t  mantra[4], id0, id5, *mappedfile;
  MAP      map;

  /* For streets we do have a enc_len of 34 */
  while( run ) {
    switch( getopt( args, argv, ":e:" ) ) {
      case -1 : run = 0; break;
      case 'e':
        enc_len = atol( optarg );
        break;
      default:
        fputs( "Syntax: %s [-e encrypted_length (default: 32, for streets 34 or 0)] path-to-teiln.dat", stderr ); exit( 1 );
        break;
    }
  }
  run = 1;

  if( optind == args  )
    { fputs( "Missing filename.", stderr ); exit( 1 ); }

  map = map_file( argv[optind], 1 );
  mappedfile = map->addr;

  mantra[0] = mantra_in[0] ^ mappedfile[4];
  mantra[1] = mantra_in[1] ^ mappedfile[5];
  mantra[2] = mantra_in[2] ^ mappedfile[2];
  mantra[3] = mantra_in[3] ^ mappedfile[3];

  id0 = mappedfile[0];
  id5 = mappedfile[5];

  while( run )
  {
    while( ( offset < map->size ) && (
           ( mappedfile[ offset + 0 ] != id0                         ) ||
           ( mappedfile[ offset + 2 ] != ( '-'          ^ mantra[2] )) ||
           ( mappedfile[ offset + 3 ] != ( 'l'          ^ mantra[3] )) ||
           ( mappedfile[ offset + 4 ] != ( 'h'          ^ mantra[0] )) ||
           ( mappedfile[ offset + 5 ] != id5                         ) ||
           ( mappedfile[ offset + 6 ] != ( '-'          ^ mantra[2] ))
          ) ) offset++;

    // printf( "Found an appropriate offset at: %zd\n", offset );

    if( reported < ( offset * 10 ) / map->size )
    {
      reported++;
      printf( "%zd%% ", 10 * reported );
      fflush( stdout );
    }

    if( offset == map->size )
        run = 0;

    if( oldoffset != -1 )
    {
        uint8_t *mf = mappedfile + oldoffset, df[128];
        size_t filename_len, header_len, i;
        char filename_template[32], filename[32];

        /* De-"crypt" obfuscation to our header copy */
        for( i=0; i<enc_len; ++i)
           df[i] = mf[i] ^ mantra[i%4];

        /* Get values from LHA header */
        header_len = df[0] + 2;
        filename_len = df[21];

        /* Copy rest of header, so we can checksum */
        for( i=enc_len; i<header_len; ++i)
           df[i] = mf[i];

        /* Make up new sequental file name */
        snprintf( filename_template, sizeof(filename_template), "%%0%dd.lha", (int)filename_len );
        snprintf( filename, sizeof( filename ), filename_template, filenum++ );
        memcpy( ((uint8_t*)df) + 22, filename, filename_len);

        /* Recalculate checksum with new file name */
        df[1] = 0; for( i=2; i<header_len; ++i) df[1] += df[i];

        /* Open file and dump our de-"crypted" header and then rest of file */
        i = open( filename, O_CREAT | O_TRUNC | O_WRONLY, 0644 );
        if( enc_len > header_len ) {
            write( i, df, enc_len );
            write( i, mf + enc_len, offset - oldoffset - enc_len );
        } else {
            write( i, df, header_len );
            write( i, mf + header_len, offset - oldoffset - header_len );
        }
        close( i );
    }
    oldoffset = offset;
    offset++;
  }

  unmap_file( &map );
  return 0;
}