Subversion Repositories freemyipod

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
141 cmwslw 1
/*
2
 This program is free software; you can redistribute it and/or modify
3
 it under the terms of the GNU General Public License version 2.1 as
4
 published by the Free Software Foundation.
5
 
6
 This program is distributed in the hope that it will be useful,
7
 but WITHOUT ANY WARRANTY; without even the implied warranty of
8
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9
 GNU General Public License for more details.
10
 
11
 You should have received a copy of the GNU General Public License
12
 along with this program; if not, write to the Free Software
13
 Foundation Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA
14
*/
15
 
16
/*
17
  $Id$
18
 
19
  Revision history:
20
  1.0 - 2007-01-25 - Brossillon J.Damien <brossill@enseirb.fr>
21
  -- Initial version
22
 
23
  1.1 - 2007-09-15
24
  -- Stuff added to guess the header versions.
25
 
26
  Todo list:
27
  -- Debug
28
*/
29
 
30
#include <stdlib.h>
31
#include <stdio.h>
32
#include <string.h>
33
#include <getopt.h>
34
#include <limits.h>
35
 
36
#include "extract2g.h"
37
 
38
#define VERSION_H 1
39
#define VERSION_L 1
40
 
41
#define B (0x0FFFFFFF)
42
 
43
/* Change endian of a 4 bytes memory zone */
44
void change_endian( char* buf )
45
{
46
 
47
  char tmp[4];
48
 
49
  tmp[3] = buf[0];
50
  tmp[2] = buf[1];
51
  tmp[1] = buf[2];
52
  tmp[0] = buf[3];
53
 
54
  memcpy(buf,tmp,4*sizeof(char));
55
 
56
  return;
57
}
58
 
59
unsigned int hash_part(FILE* hIn, int start, int len, int header_len)
60
{
61
  unsigned int hash = 0;
62
 
63
  fseek(hIn, start, SEEK_SET);
64
 
65
  unsigned int tmp;
66
 
67
  int pos;
68
  for(pos=0;pos<(len+header_len);pos++)
69
  {
70
    tmp = fgetc(hIn);
71
 
72
    hash = (hash*B + tmp*pos);
73
  }
74
 
75
  return hash;
76
}
77
 
78
void extract_part(FILE* hIn, FILE* hOut,
79
                    char* name,
80
                    int start, int len,
81
                    int header_len)
82
{
83
  int pos;
84
  char tmp;
85
 
86
  char* filename = NULL;
87
 
88
  // No extract name specified
89
  if( !hOut )
90
  {
91
    filename = (char*)malloc( strlen(name) + 4 );
92
 
93
    if( !filename )
94
    {
95
      fprintf(stderr, "Memory allocation failure.");
96
      exit(EXIT_FAILURE);
97
    }
98
 
99
    strcpy(filename, name);
100
    strcat(filename, ".fw");
101
 
102
    hOut = fopen(filename, "wb");
103
  }
104
 
105
  fprintf(stdout,"Extracting from 0x%8.X to 0x%8.X in %s.\n",
106
           start, start+len+header_len, filename);
107
 
108
  // Go to the beginning of the part
109
  fseek(hIn, start, SEEK_SET);
110
 
111
  // Extracting
112
  for(pos=0;pos<(len+header_len);pos++)
113
  {
114
    tmp = fgetc(hIn);
115
    fputc(tmp,hOut);
116
  }
117
 
118
  fclose(hOut);
119
 
120
  free(filename);
121
 
122
  return;
123
}
124
 
125
char is_valid( DIRECTORY* p_dir )
126
{
127
  // Test if the directory contain a valid header for the part.
128
  return ( !strncmp( p_dir->dev, "NAND", 4) 
129
            || !strncmp( p_dir->dev, "ATA!", 4) );
130
}
131
 
132
char check_header(FILE* fh, unsigned int l)
133
{
134
  DIRECTORY dir;
135
 
136
  fseek(fh, l, SEEK_SET);
137
  fread( (void*)&dir, LEN_DIR, sizeof(char), fh);
138
 
139
  change_endian(dir.dev);
140
 
141
  return is_valid(&dir);
142
}
143
 
144
int guess_directory_address(FILE* fh)
145
{
146
  // Check for 1G header
147
  if(check_header(fh,ADDR_DIR_1G))
148
  {
149
    printf("> iPod nano 1g header detected.\n");
150
    return ADDR_DIR_1G;
151
  }
152
 
153
  // Check for 2G header
154
  if(check_header(fh,ADDR_DIR_2G))
155
  {
156
    printf("> iPod nano 2g header detected.\n");
157
    return ADDR_DIR_2G;
158
  }
159
 
160
  // Check for 3G header
161
  if(check_header(fh,ADDR_DIR_3G))
162
  {
163
    printf("> iPod nano 3g header detected.\n");
164
    return ADDR_DIR_3G;
165
  }
166
 
167
  // At least try to find an header
168
  fseek(fh, 0, SEEK_SET);
169
 
170
  DIRECTORY dir;
171
 
172
  while(!feof(fh))
173
  {
174
    fread( (void*)&dir, LEN_DIR, sizeof(char), fh);
175
    change_endian(dir.dev);
176
 
177
    if(is_valid(&dir))
178
    {
179
      int addr = ftell(fh) - LEN_DIR;
180
      printf("> Unknown header found at 0x%X\n",addr);
181
      return addr;
182
    }
183
 
184
    fseek(fh, -LEN_DIR + 1, SEEK_CUR);
185
  }
186
 
187
  return ADDR_DIR_DEFAULT;
188
}
189
 
190
void print_directory_infos(DIRECTORY* p_dir, char extended)
191
{
192
  if( extended )
193
    printf("dev: %.4s type: %.4s\n\
194
id: %X\n\
195
devOffset: %X\n\
196
len: %X\n\
197
addr: %X\n\
198
entryOffset: %X\n\
199
checksum: %X\n\
200
version: %X\n\
201
loadAddr: %X\n\n",
202
            p_dir->dev,
203
            p_dir->type,
204
            p_dir->id,
205
            p_dir->devOffset,
206
            p_dir->len,
207
            p_dir->addr,
208
            p_dir->entryOffset,
209
            p_dir->checksum,
210
            p_dir->version,
211
            p_dir->loadAddr );
212
 
213
   else
214
      printf("dev: %.4s type: %.4s devOffset: %X len: %X\n",
215
              p_dir->dev,
216
              p_dir->type,
217
              p_dir->devOffset,
218
              p_dir->len);
219
 
220
}
221
 
222
 
223
/* Display help */
224
void usage(int status)
225
{
226
  if (status != EXIT_SUCCESS)
227
    fputs("Try `extract_2g --help' for more information.\n", stderr);
228
  else
229
  {
230
    fputs("Usage: extract_2g [OPTION] [FILE]\n", stdout);
231
    fprintf(stdout,"Read & extract iPod Nano 2G data parts from a FILE dump.\n\
232
\n\
233
 -l, --list               only list avaible parts according to the dump directory\n\n\
234
 -H, --hash               do a hash on every part of the dump\n\n\
235
 -A, --all                extract ALL founded parts from dump (default names used)\n\n\
236
 -e, --extract=NAME       select which part you want to extract (default none)\n\
237
 -o, --output=FILE        put the extracted part into FILE (default NAME.fw)\n\n\
238
 -d, --directory-address  specify the directory address (default 0x%X )\n\
239
 -a, --header-length      specify the header length of each part (default 0x800)\n\n\
240
 -h, --help               display this help and exit\n", ADDR_DIR_DEFAULT);
241
  }
242
 
243
  exit(status);
244
}
245
 
246
int main(int argc, char **argv)
247
{
248
  FILE *file = NULL;
249
  FILE *hExtract = NULL;
250
 
251
  char str_target[32];
252
 
253
  char extract_is_set = 0;
254
  char extract_all = 0;
255
  char list_directories = 0;
256
  char is_found = 0;
257
  char hash_parts = 0;
258
 
259
  /* REMOVE */
260
  char c;
261
 
262
  int tmp;
263
 
264
  int i;
265
 
266
  int directories_size = 0;
267
  DIRECTORY* directories = NULL;
268
 
269
  int addr_directory = -1;
270
 
271
  int length_header = LEN_HEADER;
272
 
273
  /* Getopt short options */
274
  static char const short_options[] = "e:o:d:a:AHlhv";
275
 
276
  /* Getopt long options */
277
  static struct option const long_options[] = {
278
    {"extract", required_argument, NULL, 'e'},
279
    {"output", required_argument, NULL, 'o'},
280
    {"hash", no_argument, NULL, 'H'},
281
    {"list", no_argument, NULL, 'l'},
282
    {"all", no_argument, NULL, 'A'},
283
    {"directory-address", required_argument, NULL, 'd'},
284
    {"header-length", required_argument, NULL, 'a'},
285
    {"help", no_argument, NULL, 'h'},
286
    {"version", no_argument, NULL, 'v'},
287
    {NULL, 0, NULL, 0}
288
  };
289
 
290
  /* Some informations */
291
  printf("%s compiled at %s %s.\n\n",argv[0],__TIME__,__DATE__);
292
 
293
  /* Parse command line options.  */
294
  while ((c = getopt_long(argc, argv,
295
                          short_options, long_options, NULL)) != -1) {
296
    switch (c) {
297
    case 'o':
298
        hExtract = fopen(optarg,"wb");
299
 
300
        if(!hExtract)
301
        {
302
          fprintf(stderr,"Cannot write/create %s.\n", optarg);
303
          usage(EXIT_FAILURE);
304
        }
305
 
306
      break;
307
 
308
    case 'a':
309
      if ( (tmp = strtol(optarg, NULL, 0)) > 0 )
310
        length_header = tmp;
311
      else {
312
        fprintf(stderr, "Invalid `header-length' value `%s'.\n", optarg);
313
        usage(EXIT_FAILURE);
314
      }
315
      break;
316
 
317
    case 'd':
318
      if ( (tmp = strtol(optarg, NULL, 0)) > 0 )
319
        addr_directory = tmp;
320
      else {
321
        fprintf(stderr, "Invalid `directory-address' value `%s'.\n", optarg);
322
        usage(EXIT_FAILURE);
323
      }
324
      break;
325
 
326
    case 'e':
327
        strncpy(str_target,optarg,32);
328
        extract_is_set = 1;
329
      break;
330
 
331
    case 'H':
332
        hash_parts = 1;
333
      break;
334
 
335
    case 'l':
336
        list_directories = 1;
337
      break;
338
 
339
    case 'A':
340
        extract_all = 1;
341
      break;
342
 
343
    case 'h':
344
      usage(EXIT_SUCCESS);
345
      break;
346
 
347
    case 'v':
348
      fprintf(stdout,"extract_2g version %d.%d\n",VERSION_H,VERSION_L);
349
      fputs("Copyright (C) 2007 Brossillon J.Damien\n\
350
This is free software. You may redistribute copies of it under the terms\n\
351
of the GNU General Public License <http://www.gnu.org/licenses/gpl.html>.\n\
352
There is NO WARRANTY, to the extent permitted by law.\n", stdout);
353
      exit(EXIT_SUCCESS);
354
      break;
355
 
356
    default:
357
      usage(EXIT_FAILURE);
358
    }
359
  }
360
 
361
  if (!argv[optind]) {
362
    fprintf(stderr,"Missing dump filename.\n");
363
    usage(EXIT_FAILURE);
364
  } else {
365
    if (!(file = fopen(argv[optind], "rb")))
366
    {
367
      fprintf(stderr,"Cannot open file `%s'.\n", argv[optind]);
368
      usage(EXIT_FAILURE);
369
    }
370
  }
371
 
372
 
373
  if( addr_directory < 0 )
374
    addr_directory = guess_directory_address(file);
375
  else
376
    addr_directory = ADDR_DIR_DEFAULT;
377
 
378
 
379
  fseek(file, addr_directory, SEEK_SET);
380
 
381
  DIRECTORY dir;
382
 
383
  // List directories
384
  while(1)
385
  {
386
    /* FIXME
387
    for(i=0;i<(int)LEN_DIR;i++)
388
    {
389
      ((char*)&dir)[i] = fgetc(file);
390
    }*/
391
    fread( (void*)&dir, LEN_DIR, sizeof(char), file);
392
 
393
    change_endian(dir.dev);
394
    change_endian(dir.type);
395
 
396
    if( is_valid(&dir) )
397
    {
398
      directories_size++;
399
      directories = (DIRECTORY*)realloc(directories,
400
                                        directories_size*sizeof(DIRECTORY));
401
 
402
      if(!directories)
403
      {
404
        fprintf(stderr,"Memory allocation failure.\n");
405
        exit(EXIT_FAILURE);
406
      }
407
 
408
      memcpy(directories + directories_size - 1, &dir, sizeof(DIRECTORY));
409
    }
410
    else
411
      break;
412
  }
413
 
414
  // Check if we have, at least, one valid part
415
  if( directories_size <= 0 )
416
  {
417
    fclose(file);
418
 
419
    if(directories)
420
      free(directories);
421
 
422
    fprintf(stderr, "Cannot find at least one valid part in the dump.\n");
423
    exit(EXIT_FAILURE);
424
  }
425
 
426
  // User want to hash every parts of the dump
427
  if( hash_parts )
428
  {
429
 
430
    for(i=0;i<directories_size;i++)
431
    {
432
      fprintf(stdout,"%.4s: 0x%X\n",directories[i].type,
433
                hash_part(file,
434
                           directories[i].devOffset, directories[i].len,
435
                           length_header) );
436
    }
437
  }
438
  else
439
  if( extract_all )
440
  {
441
    // User want to extract every part of the dump
442
    for(i=0;i<directories_size;i++)
443
    {
444
      char name[5];
445
 
446
      memcpy(name,directories[i].type,4*sizeof(char));
447
      name[4] = '\0';
448
 
449
      extract_part(file, NULL,
450
                    name,
451
                    directories[i].devOffset, directories[i].len,
452
                    length_header);
453
    }
454
 
455
    fprintf(stdout,"Done.\n");
456
 
457
 
458
  }
459
  else
460
  {
461
    if( list_directories )
462
    {
463
      // User ask a big directory listing 
464
 
465
      for(i=0;i<directories_size; i++)
466
        print_directory_infos( &(directories[i]), 1 );
467
    }
468
    else
469
    { 
470
 
471
      if( extract_is_set )
472
      {
473
        is_found = 0;
474
 
475
        for(i=0;i<directories_size;i++)
476
          if( !strcmp( directories[i].type, str_target ) )
477
          { // Extract & correct part name
478
 
479
            is_found = 1;
480
 
481
            // Lock & load, got name, file, position and length !         
482
            extract_part(file, hExtract,
483
                          directories[i].type,
484
                          directories[i].devOffset, directories[i].len,
485
                          length_header);
486
 
487
          }
488
 
489
        // Wrong part name, error message & small parts list
490
        if(!is_found)
491
        {
492
          fprintf(stderr,"No part named '%s' found.\n\n",str_target);
493
 
494
          for(i=0;i<directories_size;i++)
495
            print_directory_infos( &(directories[i]), 0);  
496
        }
497
 
498
      }
499
      else
500
      { // No actions, small parts list
501
 
502
        for(i=0;i<directories_size;i++)
503
          print_directory_infos( &(directories[i]), 0);   
504
      }
505
 
506
    }
507
  }
508
 
509
  fclose(file);
510
 
511
  free(directories);
512
 
513
  return EXIT_SUCCESS;
514
}