~ubuntu-branches/ubuntu/wily/mp3check/wily

« back to all changes in this revision

Viewing changes to .pc/01_restore_pristine_code.patch/mp3check.cc

  • Committer: Package Import Robot
  • Author(s): Sandro Tosi
  • Date: 2012-05-12 16:08:03 UTC
  • mfrom: (1.1.3)
  • Revision ID: package-import@ubuntu.com-20120512160803-o064g848g3lnvrzc
Tags: 0.8.4-1
* New upstream release
* debian/watch
  - updated
* debian/control
  - added Vcs-{Git, Browser} fields
  - added ${misc:Depends} to Depends line
  - bump Standards-Version to 3.9.3 (no changes needed)
* Converted to 3.0 (quilt) source format
* Converted to debhelper 9 and dh sequencer
* debian/copyright
  - updated to DEP5 and to new upstream release
* debian/patches/20_use_destdir_to_install.patch
  - install using DESTDIR variable
* debian/rules
  - upstream doesn't ship doc anymore
  - install the manpage (no longer done by upstream Makefile)
* debian/patches/15-bts667288-gcc-4.7.patch
  - fix FTBFS with GCC-4.7 (due to uncoordinated upload by gcc "maintainer");
    thanks to Jari Aalto for the patch; Closes: #667288
* debian/patches/30_bts624138_manpage_typo.patch
  - fix a typo in the manpage; thanks to Reuben Thomas for the report;
    Closes: #624138

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*GPL*START*
 
2
 * mp3check - check mp3 file for consistency and print infos
 
3
 * 
 
4
 * Copyright (C) 1998-2005 by Johannes Overmann <Johannes.Overmann@gmx.de>
 
5
 * 
 
6
 * This program is free software; you can redistribute it and/or modify
 
7
 * it under the terms of the GNU General Public License as published by
 
8
 * the Free Software Foundation; either version 2 of the License, or
 
9
 * (at your option) any later version.
 
10
 * 
 
11
 * This program is distributed in the hope that it will be useful,
 
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 * GNU General Public License for more details.
 
15
 * 
 
16
 * You should have received a copy of the GNU General Public License
 
17
 * along with this program; if not, write to the Free Software
 
18
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
19
 * *GPL*END*/  
 
20
 
 
21
 
 
22
#include <sys/stat.h>
 
23
#include <sys/mman.h>
 
24
#include <sys/ioctl.h>
 
25
#include <fcntl.h>
 
26
#include <unistd.h>
 
27
#include <stdio.h>
 
28
#include <stdlib.h>
 
29
#include "tappconfig.h"
 
30
#include "crc16.h"
 
31
#include "id3tag.h"
 
32
#include "tfiletools.h"
 
33
 
 
34
 
 
35
 
 
36
#define ONLY_MP3 "mp3,MP3,Mp3,mP3"
 
37
 
 
38
// please update also HISTORY
 
39
#define VERSION "0.8.4"
 
40
 
 
41
 
 
42
const char *options[] = {
 
43
   "#usage='Usage: [OPTIONS, FILES AND DIRECTORIES] [--] [FILES AND DIRECTORIES]\n\n"
 
44
     "this program checks audio mpeg layer 1,2 and 3 (*.mp3) files for\n"       
 
45
     "consistency (headers and crc) and anomalies'",
 
46
   "#trailer='\n%n version %v *** (C) 1998-2003,2005,2008 by Johannes Overmann\ncomments, bugs and suggestions welcome: %e\n%gpl'",
 
47
   "#stopat--",
 
48
   "#onlycl",                                    
 
49
   // options
 
50
   "name=list             , type=switch, char=l, help='list parameters by examining the first valid header and size', headline=mode:",
 
51
   "name=compact-list     , type=switch, char=c, help='list parameters of one file per line in a very compact format: "
 
52
     "version (l=1.0, L=2.0), layer, sampling frequency [kHz] (44=44.1), bitrate [kbit/s], mode (js=joint stereo, st=stereo, sc=single channel, dc=dual channel), "
 
53
     "emphasis (n=none, 5=50/15 usecs, J=CCITT J.17), COY (has [C]rc, [O]riginal, cop[Y]right), length [min:sec], filename (poss. truncated)'",
 
54
   "name=error-check      , type=switch, char=e, help='check crc and headers for consistency and print several error messages'",
 
55
   "name=max-errors       , type=int   , char=m, param=N, lower=0, help='with -e: set maximum number of errors N to print per file (default 0==infinity)'",
 
56
   "name=anomaly-check    , type=switch, char=a, help='report all differences from these parameters: layer 3, 44.1kHz, 128kB, joint stereo, no emphasis, has crc'",
 
57
   "name=dump-header      , type=switch, char=d, help='dump all possible header with sync=0xfff'",
 
58
   "name=dump-tag         , type=switch, char=t, help='dump all possible tags of known version'",
 
59
   "name=raw-list         , type=switch,       , help='list parameters in raw output format for use with external programs'",
 
60
   "name=raw-elem-sep     , type=string,       , default=0x09, param=N, help='separate elements in one line by char N (numerical ASCII code)'",
 
61
   "name=raw-line-sep     , type=string,       , default=0x0a, param=N, help='separate lines by char N (numerical ASCII code)'",
 
62
   "name=edit-frame-b     , type=string,       , param=P, help='modify a single byte of a specific frame at a specific offset; B has the format \\'frame,offset,byteval\\', (use 0xff for hex or 255 for dec or 0377 for octal); this mode operates on all given files and is useful for your own experiment with broken streams or while testing this tool ;-)'",
 
63
                                       
 
64
   "name=cut-junk-start   , type=switch,       , help='remove junk before first frame', headline='fix errors:'",
 
65
   "name=cut-junk-end     , type=switch,       , help='remove junk after last frame'",
 
66
   "name=cut-tag-end      , type=switch,       , help='remove trailing tag'",
 
67
   "name=fix-headers      , type=switch,       , help='fix invalid headers (prevent constant parameter switching), implies -e, use with care'",
 
68
   "name=fix-crc          , type=switch,       , help='fix crc (set crc to the calculated one), implies -e, use with care\n(note: it is not possible to add crc to files which have been created without crc)'",
 
69
   "name=add-tag          , type=switch,       , help='add ID3 v1.1 tag calculated from filename and path using simple heuristics if a file does not already have a tag'",
 
70
 
 
71
   "name=ign-tag128       , type=switch, char=G, help='ignore 128 byte TAG after last frame', headline='disable error messages for -e --error-check:'",
 
72
   "name=ign-resync       , type=switch, char=Y, help='ignore synchronization errors (invalid frame header/frame too long/short)'",
 
73
   "name=ign-junk-end     , type=switch, char=E, help='ignore junk after last frame'",
 
74
   "name=ign-crc-error    , type=switch, char=Z, help='ignore crc errors'",
 
75
   "name=ign-non-ampeg    , type=switch, char=N, help='ignore non audio mpeg streams'",
 
76
   "name=ign-truncated    , type=switch, char=T, help='ignore truncated last frames'",
 
77
   "name=ign-junk-start   , type=switch, char=S, help='ignore junk before first frame'",
 
78
   "name=ign-bitrate-sw   , type=switch, char=B, help='ignore bitrate switching and enable VBR support'",
 
79
   "name=ign-constant-sw  , type=switch, char=W, help='ignore switching of constant parameters, such as sampling frequency'",
 
80
   "name=show-valid       , type=switch,       , help='print the message \\'valid audio mpeg stream\\' for all files which appear to be error free (after ignoring errors)",
 
81
                                         
 
82
   "name=any-crc          , type=switch, char=C, help='ignore crc anomalies', headline='disable anomaly messages for -a --anomaly-check'",
 
83
   "name=any-mode         , type=switch, char=M, help='ignore mode anomalies'",
 
84
   "name=any-layer        , type=switch, char=L, help='ignore layer anomalies'",
 
85
   "name=any-bitrate      , type=switch, char=K, help='ignore bitrate anomalies'",
 
86
   "name=any-version      , type=switch, char=I, help='ignore version anomalies'",
 
87
   "name=any-sampling     , type=switch, char=F, help='ignore sampling frequency anomalies'",
 
88
   "name=any-emphasis     , type=switch, char=P, help='ignore emphasis anomalies'",
 
89
   
 
90
   "name=recursive        , type=switch, char=r, help='process any given directories recursively (the default is to ignore all directories specified on the command line)', headline='file options:'",
 
91
   "name=filelist         , type=string, char=f, param=FILE, help='process all files specified in FILE (one filename per line) in addition to the command line'",
 
92
   "name=accept           , type=string, char=A, param=LIST, help='process only files with filename extensions specified by comma separated LIST'",
 
93
   "name=reject           , type=string, char=R, param=LIST, help='do not process files with a filename extension specified by comma separated LIST'",
 
94
   "name=only-mp3         , type=switch, char=3, help='same as --accept " ONLY_MP3 "'",
 
95
#ifndef __CYGWIN__
 
96
   "name=xdev             , type=switch,         help='do not descend into other filesystems when recursing directories'",
 
97
#endif
 
98
   "name=print-files      , type=switch,         help='just print all filenames without processing them, then exit (for debugging purposes, also useful to create files for --filelist)'",
 
99
     
 
100
   "name=single-line      , type=switch, char=s, help='print one line per file and message instead of splitting into several lines', headline='output options:'",
 
101
   "name=no-summary       , type=switch,       , help='suppress the summary printed below all messages if multiple files are given'",
 
102
   "name=log-file         , type=string, char=g, param=FILE, help='print names of erroneous files to FILE, one per line'",
 
103
   "name=quiet            , type=switch, char=q, help='quiet mode, hide messages about directories, non-regular or non-existing files'",
 
104
   "name=color            , type=switch, char=o, help='colorize output with ANSI sequences'",
 
105
   "name=alt-color        , type=switch, char=b, help='colorize: do not use bold ANSI sequences'",
 
106
   "name=ascii-only       , type=switch,         help='replace the range of ASCII chars 160-255 (which is usually printable: e.g. ISO-8859) by \\'?\\''",
 
107
   "name=progress         , type=switch, char=p, help='show progress information on stderr'", 
 
108
   "name=verbose          , type=switch, char=v, help='be more verbose'",
 
109
   "name=no-mmap          , type=switch,       , help='do not use mmap (e.g. when you get \\'mmap: No such device\\')'",
 
110
   "name=dummy            , type=switch, char=0, help='do not write/modify anything other than the logfile', headline=common options:",
 
111
   "EOL" // end of list     
 
112
};
 
113
 
 
114
// default value for systems which do not define O_BINARY
 
115
#ifndef O_BINARY
 
116
#define O_BINARY 0
 
117
#endif
 
118
 
 
119
                                         
 
120
 
 
121
// color config:
 
122
// 30 black
 
123
// 31 red
 
124
// 32 green
 
125
// 33 yellow
 
126
// 34 blue
 
127
// 35 magenta
 
128
// 36 cyan
 
129
// 37 white
 
130
const char *cfil = "\033[1;37m";
 
131
const char *cano = "\033[1;33m";
 
132
const char *cerror = "\033[1;31m";
 
133
const char *cval = "\033[1;34m";
 
134
const char *cok  = "\033[0;32m";
 
135
const char *cnor = "\033[0m";
 
136
 
 
137
const char *c_fil = "\033[37m";
 
138
const char *c_ano = "\033[33m";
 
139
const char *c_err = "\033[31m";
 
140
const char *c_val = "\033[34m";
 
141
const char *c_ok  = "\033[32m";
 
142
const char *c_nor = "\033[0m";
 
143
 
 
144
// minimum number of sequential valid and constant frame headers to validate header
 
145
const int MIN_VALID = 6;
 
146
 
 
147
 
 
148
// global data
 
149
bool progress = false;
 
150
bool single_line = false;
 
151
bool dummy = false;
 
152
int max_errors = 0;
 
153
unsigned int columns = 0;
 
154
bool show_valid_files = false;
 
155
bool quiet = false;
 
156
bool only_ascii = false;
 
157
 
 
158
bool ano_any_crc   = false;
 
159
bool ano_any_bit   = false;
 
160
bool ano_any_emp   = false;
 
161
bool ano_any_rate  = false;
 
162
bool ano_any_mode  = false;
 
163
bool ano_any_layer = false;
 
164
bool ano_any_ver   = false;
 
165
 
 
166
bool ign_crc   = false;
 
167
bool ign_start = false;
 
168
bool ign_end   = false;
 
169
bool ign_tag   = false;
 
170
bool ign_bit   = false;
 
171
bool ign_const = false;
 
172
bool ign_trunc = false;
 
173
bool ign_noamp = false;
 
174
bool ign_sync  = false;
 
175
 
 
176
 
 
177
// header info
 
178
                                         
 
179
int layer_tab[4]= {0, 3, 2, 1};
 
180
 
 
181
const int FREEFORMAT = 0;
 
182
const int FORBIDDEN = -1;
 
183
int bitrate1_tab[16][3] = {   
 
184
   {FREEFORMAT, FREEFORMAT, FREEFORMAT},
 
185
   {32, 32, 32},
 
186
   {64, 48, 40},
 
187
   {96, 56, 48},
 
188
   {128, 64, 56},
 
189
   {160, 80, 64},
 
190
   {192, 96, 80},
 
191
   {224, 112, 96},
 
192
   {256, 128, 112},
 
193
   {288, 160, 128},
 
194
   {320, 192, 160},
 
195
   {352, 224, 192},
 
196
   {384, 256, 224},
 
197
   {416, 320, 256},
 
198
   {448, 384, 320},
 
199
   {FORBIDDEN, FORBIDDEN, FORBIDDEN}
 
200
};
 
201
// 
 
202
int bitrate2_tab[16][3] = {   
 
203
   {FREEFORMAT, FREEFORMAT, FREEFORMAT},
 
204
   { 32,  8,  8},
 
205
   { 48, 16, 16},
 
206
   { 56, 24, 24},
 
207
   { 64, 32, 32},
 
208
   { 80, 40, 40},
 
209
   { 96, 48, 48},
 
210
   {112, 56, 56},
 
211
   {128, 64, 64},
 
212
   {144, 80, 80},
 
213
   {160, 96, 96},
 
214
   {176,112,112},
 
215
   {192,128,128},
 
216
   {224,144,144},
 
217
   {256,160,160},
 
218
   {FORBIDDEN, FORBIDDEN, FORBIDDEN}
 
219
};
 
220
 
 
221
 
 
222
double sampd1_tab[4]={44.1, 48.0, 32.0, 0.0};
 
223
int samp_1_tab[4]={44100, 48000, 32000, 50000};
 
224
double sampd2_tab[4]={22.05, 24.0, 16.0, 0.0};
 
225
int samp_2_tab[4]={22050, 24000, 16000, 50000};
 
226
 
 
227
const unsigned int CONST_MASK = 0xffffffff;
 
228
struct Header {
 
229
#ifdef WORDS_BIGENDIAN
 
230
   unsigned int
 
231
     syncword: 12,         // fix must 0xfff
 
232
     ID: 1,                // fix 1==mpeg1.0 0==mpeg2.0
 
233
     layer_index: 2,       // fix 0 reserved
 
234
     protection_bit: 1,    // fix
 
235
     bitrate_index: 4,     //    15 forbidden
 
236
     sampling_frequency: 2,// fix 3 reserved
 
237
     padding_bit: 1,       //
 
238
     private_bit: 1,       //
 
239
     mode: 2,              // fix
 
240
     mode_extension: 2,    //      (not fix!)
 
241
     copyright: 1,         // fix
 
242
     original: 1,          // fix
 
243
     emphasis: 2;          // fix 2 reserved
 
244
#else                  
 
245
   unsigned int
 
246
     emphasis: 2,          // fix 2 reserved
 
247
     original: 1,          // fix
 
248
     copyright: 1,         // fix
 
249
     mode_extension: 2,    //      (not fix!)
 
250
     mode: 2,              // fix
 
251
     private_bit: 1,       //
 
252
     padding_bit: 1,       //
 
253
     sampling_frequency: 2,// fix 3 reserved
 
254
     bitrate_index: 4,     //    15 forbidden
 
255
     protection_bit: 1,    // fix
 
256
     layer_index: 2,       // fix 0 reserved
 
257
     ID: 1,                // fix 1==mpeg1.0 0==mpeg2.0
 
258
     syncword: 12;         // fix must 0xfff
 
259
#endif  // ifdef BIGENDIAN
 
260
   
 
261
   bool isValid() const {
 
262
      if(syncword!=0xfff) return false;
 
263
      if(ID==1) { // mpeg 1.0
 
264
         if((layer_index!=0) && (bitrate_index!=15) &&
 
265
            (sampling_frequency!=3) && (emphasis!=2)) return true;
 
266
         return false;
 
267
      } else {    // mpeg 2.0
 
268
         if((layer_index!=0) && (bitrate_index!=15) &&
 
269
            (sampling_frequency!=3) && (emphasis!=2)) return true;
 
270
         return false;
 
271
      }
 
272
   }
 
273
   
 
274
   bool sameConstant(Header h) const {
 
275
      if((*(unsigned int*)this) == (*(unsigned int*)&h)) return true;
 
276
      if((syncword          ==h.syncword          ) &&
 
277
         (ID                ==h.ID                ) &&
 
278
         (layer_index       ==h.layer_index       ) &&
 
279
         (protection_bit    ==h.protection_bit    ) &&
 
280
//       (bitrate_index     ==h.bitrate_index     ) &&
 
281
         (sampling_frequency==h.sampling_frequency) &&
 
282
         (mode              ==h.mode              ) &&
 
283
//       (mode_extension    ==h.mode_extension    ) &&
 
284
         (copyright         ==h.copyright         ) &&
 
285
         (original          ==h.original          ) &&
 
286
         (emphasis          ==h.emphasis          ) &&
 
287
         1) return true;
 
288
      else return false;
 
289
   }
 
290
   
 
291
   int bitrate() const {
 
292
      if(ID)
 
293
        return bitrate1_tab[bitrate_index][layer()-1];
 
294
      else
 
295
        return bitrate2_tab[bitrate_index][layer()-1];
 
296
   }
 
297
   int layer() const {return layer_tab[layer_index];}
 
298
   
 
299
   tstring print() const {
 
300
      tstring s;
 
301
      
 
302
      s.sprintf("(%03x,ID%d,l%d,prot%d,%2d,%4.1fkHz,pad%d,priv%d,mode%d,ext%d,copy%d,orig%d,emp%d)", 
 
303
                syncword, ID, layer(), protection_bit, bitrate_index, 
 
304
                samp_rate(), padding_bit, private_bit, mode,
 
305
                mode_extension, copyright, original, emphasis);
 
306
      return s;
 
307
   }
 
308
   double version() const {
 
309
      if(ID) return 1.0;
 
310
      else   return 2.0;
 
311
   }
 
312
   enum {STEREO, JOINT_STEREO, DUAL_CHANNEL, SINGLE_CHANNEL};
 
313
   const char *mode_str() const {
 
314
      switch(mode) {
 
315
       case STEREO:         return "stereo";
 
316
       case JOINT_STEREO:   return "joint stereo";
 
317
       case DUAL_CHANNEL:   return "dual channel";
 
318
       case SINGLE_CHANNEL: return "single chann";
 
319
      }
 
320
      return 0;
 
321
   }
 
322
   const char *short_mode_str() const {
 
323
      switch(mode) {
 
324
       case STEREO:         return "st";
 
325
       case JOINT_STEREO:   return "js";
 
326
       case DUAL_CHANNEL:   return "dc";
 
327
       case SINGLE_CHANNEL: return "sc";
 
328
      }
 
329
      return 0;
 
330
   }
 
331
   enum {emp_NONE, emp_50_15_MICROSECONDS, emp_RESERVED, emp_CCITT_J_17};
 
332
   const char *emphasis_str() const {
 
333
      switch(emphasis) {
 
334
       case emp_NONE:               return "no emph";
 
335
       case emp_50_15_MICROSECONDS: return "50/15us";
 
336
       case emp_RESERVED:           return "reservd";
 
337
       case emp_CCITT_J_17:         return "C. J.17";
 
338
      }
 
339
      return 0;
 
340
   }
 
341
   const char *short_emphasis_str() const {
 
342
      switch(emphasis) {
 
343
       case emp_NONE:               return "n";
 
344
       case emp_50_15_MICROSECONDS: return "5";
 
345
       case emp_RESERVED:           return "!";
 
346
       case emp_CCITT_J_17:         return "J";
 
347
      }
 
348
      return 0;
 
349
   }
 
350
   double samp_rate() const {
 
351
      if(ID)
 
352
        return sampd1_tab[sampling_frequency];
 
353
      else
 
354
        return sampd2_tab[sampling_frequency];
 
355
   }
 
356
   int samp_int_rate() const {
 
357
      if(ID)
 
358
        return samp_1_tab[sampling_frequency];
 
359
      else
 
360
        return samp_2_tab[sampling_frequency];
 
361
   }
 
362
   // this should be not affected by endianess
 
363
   int get_int() const {return *((const int *)this);}
 
364
};
 
365
 
 
366
 
 
367
// get header from pointer
 
368
inline Header get_header(const unsigned char *p) {
 
369
   Header h;
 
370
   unsigned char *q = (unsigned char *)&h;
 
371
#ifdef WORDS_BIGENDIAN
 
372
      q[0]=p[0];
 
373
      q[1]=p[1];
 
374
      q[2]=p[2];
 
375
      q[3]=p[3];   
 
376
#else
 
377
      q[0]=p[3];
 
378
      q[1]=p[2];
 
379
      q[2]=p[1];
 
380
      q[3]=p[0];   
 
381
#endif   
 
382
   return h;
 
383
}
 
384
 
 
385
 
 
386
// set header to pointer
 
387
inline void set_header(unsigned char *p, Header h) {
 
388
   unsigned char *q = (unsigned char *)&h;
 
389
#ifdef WORDS_BIGENDIAN
 
390
      p[0]=q[0];
 
391
      p[1]=q[1];
 
392
      p[2]=q[2];
 
393
      p[3]=q[3];
 
394
#else
 
395
      p[0]=q[3];
 
396
      p[1]=q[2];
 
397
      p[2]=q[1];
 
398
      p[3]=q[0];
 
399
#endif   
 
400
}
 
401
 
 
402
 
 
403
// set header from header
 
404
// preserves padding bit and mode extension (under conditions),
 
405
// among other things
 
406
void set_header(Header &to, Header from) {
 
407
   if(to.mode!=from.mode)
 
408
   {
 
409
      to.mode            = from.mode;
 
410
      to.mode_extension  = from.mode_extension;
 
411
   }
 
412
   to.ID                 = from.ID;
 
413
   to.layer_index        = from.layer_index;
 
414
   to.protection_bit     = from.protection_bit;
 
415
   to.sampling_frequency = from.sampling_frequency;
 
416
   to.copyright          = from.copyright;
 
417
   to.original           = from.original;
 
418
   to.emphasis           = from.emphasis;
 
419
}
 
420
 
 
421
 
 
422
// set crc to pointer
 
423
inline bool set_crc_value(unsigned char *p, unsigned short c) {
 
424
    p[1] = (unsigned char) c;
 
425
    p[0] = (unsigned char) (c>>8);
 
426
    return true;
 
427
}
 
428
 
 
429
 
 
430
// return length of frame in bytes
 
431
inline int frame_length(Header h) {
 
432
   if(h.version() == 1.0) {
 
433
      switch(h.layer()) {
 
434
       case 1:
 
435
         return (((12000*h.bitrate()) / h.samp_int_rate()) + h.padding_bit) * 4;
 
436
       default:
 
437
         return ((144000*h.bitrate()) / h.samp_int_rate()) + h.padding_bit;
 
438
      }
 
439
   } else {  
 
440
      switch(h.layer()) {
 
441
       case 1:
 
442
         return (((6000*h.bitrate()) / h.samp_int_rate()) + h.padding_bit) * 4;
 
443
       default:
 
444
         return ((72000*h.bitrate()) / h.samp_int_rate()) + h.padding_bit;
 
445
      }
 
446
   }
 
447
}
 
448
 
 
449
                                         
 
450
// return duration of frame in ms
 
451
inline double frame_duration(Header h) {
 
452
   return (((double)frame_length(h)*8) / h.bitrate());
 
453
}
 
454
 
 
455
 
 
456
// return next pos of min_valid sequential valid and constant header 
 
457
// or -1 if not found
 
458
inline int find_next_header(const unsigned char *p, int len, int min_valid) {
 
459
   int i;
 
460
   const unsigned char *q = p;
 
461
   const unsigned char *t;   
 
462
   int rest, k, l;
 
463
   Header h, h2;
 
464
   
 
465
   for(i=0; i < len-3; i++, q++) {
 
466
      if(*q==255) {
 
467
         h = get_header(q);
 
468
         l = frame_length(h);
 
469
         if(h.isValid() && (l>=21)) {
 
470
            t = q + l;
 
471
            rest = len - i - l;
 
472
            for(k=1; (k < min_valid) && (rest >= 4); k++) {
 
473
               h2 = get_header(t);
 
474
               if(!h2.isValid()) break;
 
475
               if(!h2.sameConstant(h)) break;
 
476
               l = frame_length(h2);
 
477
               if(l < 21) break;
 
478
               t += l;
 
479
               rest -= l;
 
480
            }
 
481
            if(k == min_valid) return i;
 
482
         }
 
483
      }
 
484
   }
 
485
   
 
486
   return -1;  // not found
 
487
}
 
488
 
 
489
// return pointer to beginning of nth frame from start (0 is start) or 0 if frame not found
 
490
const unsigned char *skip_n_frames(const unsigned char *start, int len, int n) {
 
491
   while(1) {
 
492
      // skip invalid data
 
493
      int s = find_next_header(start, len, MIN_VALID);
 
494
      if(s < 0) return 0;
 
495
      start += s;
 
496
      len -= s;
 
497
      Header h = get_header(start);
 
498
      while(1) {
 
499
         if(len < 4) return 0;
 
500
         h = get_header(start);
 
501
         if(h.isValid()) {
 
502
            if(n <= 0) return start;
 
503
            int l = frame_length(h);
 
504
            start += l;
 
505
            len -= l;
 
506
            n--;
 
507
         } else {
 
508
            break;
 
509
         }
 
510
      }
 
511
   }
 
512
}
 
513
 
 
514
 
 
515
#ifdef __GNUC__
 
516
void fmes(const char *name, const char *format, ...) __attribute__ ((format(printf,2,3)));
 
517
#endif
 
518
 
 
519
void fmes(const char *name, const char *format, ...) {
 
520
   if(quiet) return;
 
521
   va_list ap;
 
522
   static tstring lastname;
 
523
   tstring pname = name;
 
524
   pname.replaceUnprintable(only_ascii);
 
525
   
 
526
   if(progress) putc('\r', stderr);
 
527
   if(strcmp(format, "\n") == 0) {
 
528
      printf("%s%s%s:\n", cfil, pname.c_str(), cnor);
 
529
      return;
 
530
   }     
 
531
   if(single_line) {
 
532
      printf("%s%s%s: ", cfil, pname.c_str(), cnor);
 
533
   } else {
 
534
      if(name != lastname) {
 
535
         lastname = name;        
 
536
         tstring s = pname.shortFilename(columns-1);
 
537
         printf("%s%s%s:\n", cfil, s.c_str(), cnor);     
 
538
      }
 
539
   }
 
540
   va_start(ap, format);
 
541
   vprintf(format, ap);
 
542
   va_end(ap);
 
543
}
 
544
 
 
545
 
 
546
// returns true on error
 
547
bool error_check(const char *name, const unsigned char *stream, int len, CRC16& crc, bool fix_headers, bool fix_crc) {
 
548
   int errors = 0;
 
549
   const unsigned char *p = stream;
 
550
   int start = find_next_header(p, len, MIN_VALID);
 
551
   int rest = len;
 
552
   int frame=0;
 
553
   double time=0.0;
 
554
   int l=0,s;
 
555
   Tagv1* tag;
 
556
   
 
557
   if(start<0) {
 
558
      if(!ign_noamp) {
 
559
         fmes(name, "%s%s%s\n", cerror, (len?"not an audio mpeg stream":"empty file"), cnor);
 
560
         errors++;
 
561
      }
 
562
   } else {
 
563
      
 
564
      // check for junk at beginning
 
565
      if(start>0) {
 
566
         int pos=0;
 
567
         if(!ign_start) {
 
568
            fmes(name, "%s%d%s %sbyte%s of junk before first frame header%s\n", 
 
569
                 cval, start, cnor, cerror, (start>1)?"s":"", cnor);
 
570
            errors++;
 
571
            // check for possible id3 tags within the junk
 
572
            while(start-pos >= 128)
 
573
            {
 
574
                int offset=Tagv1::find_next_tag(p+pos, start-pos);
 
575
                if(offset!=-1) {
 
576
                   pos+=offset;
 
577
                   tag=new Tagv1(p+pos);
 
578
                   fmes(name, "in leading junk: %spossible %s id3 tag v%u.%u%s at %s0x%08x%s\n",
 
579
                        cerror, (tag->isValidSpecs()?"valid":"invalid"),
 
580
                        (tag->version())>>8, (tag->version())&0xff, cnor,
 
581
                        cval, pos, cnor);
 
582
                   delete tag;
 
583
                   pos+=3;
 
584
                } else {
 
585
                   pos=start;
 
586
                }
 
587
            }  
 
588
         }
 
589
      }
 
590
      
 
591
      // check for TAG trailers
 
592
      // Note that we emit a warning if we found more than one tag, even
 
593
      // if ign_tag is set, unless ign_end is also set.
 
594
      tag=new Tagv1(p+rest-128);
 
595
      int tag_counter=0;
 
596
      while((rest>=128)&&tag->isValid()) {
 
597
         tag_counter++;
 
598
         if((!ign_tag)||((tag_counter>1)&&!ign_end)) {
 
599
            fmes(name, "%s%s%s id3 tag trailer v%u.%u found%s\n", 
 
600
                 cerror, (tag_counter>1?"another ":""), (tag->isValidSpecs()?"valid":"invalid"),
 
601
                 (tag->version())>>8, (tag->version())&0xff, cnor);
 
602
            errors++;
 
603
         }
 
604
         rest-=128;
 
605
         tag->setTarget(p+rest-128);
 
606
      } 
 
607
      delete tag;
 
608
      
 
609
      // check whole file
 
610
      rest -= start;
 
611
      p += start;
 
612
      Header head = get_header(p);
 
613
      while(rest>=4) {
 
614
         Header h = get_header(p);
 
615
         if(progress) {
 
616
            if((frame%1000)==0) {
 
617
               putc('.', stderr);
 
618
               fflush(stderr);
 
619
            }
 
620
         }
 
621
         if(!(h.isValid()&&(frame_length(h)>=21))) {
 
622
            // invalid header 
 
623
            
 
624
            // search for next valid header
 
625
            if(!l) {
 
626
               printf("ERROR! Invalid header with no previous frame. Needs debuging.\n");
 
627
            }
 
628
            // search within previous frame
 
629
            p-=(l-4);
 
630
            start-=(l-4);
 
631
            rest+=(l-4);
 
632
            
 
633
            // first look for any isolated frame with the same header
 
634
            s = find_next_header(p, rest, 1);
 
635
            if(s<0) { // error: junk at eof
 
636
               p+=(l-4);
 
637
               start+=(l-4);
 
638
               rest-=(l-4);
 
639
               break;
 
640
            }
 
641
            
 
642
            // else look for a regular stream
 
643
            h = get_header(p+s);
 
644
            if(!head.sameConstant(h))
 
645
               s = find_next_header(p, rest, MIN_VALID);
 
646
            if(s<0) { // error: junk at eof
 
647
               p+=(l-4);
 
648
               start+=(l-4);
 
649
               rest-=(l-4);
 
650
               break;
 
651
            }
 
652
            
 
653
            if(!ign_sync) {
 
654
               if(s<l-4) {
 
655
                  fmes(name, "frame %s%5d%s/%s%2u:%02u%s: %ssync error (frame too short)%s at %s0x%08x%s, %s%d%s byte%s mising\n", 
 
656
                       cval, frame - 1, cnor,
 
657
                       cval, (unsigned int)(time/1000)/60, (unsigned int)(time/1000)%60, cnor,
 
658
                       cerror, cnor, cval, start - 4, cnor,
 
659
                       cval, l-4-s, cnor, (l-4-s>1)?"s":"");
 
660
                  errors++;
 
661
               } else {
 
662
                  fmes(name, "frame %s%5d%s/%s%2u:%02u%s: %ssync error (frame too long)%s at %s0x%08x%s, skipping %s%d%s byte%s at %s0x%08x%s\n",
 
663
                       cval, frame - 1, cnor,
 
664
                       cval, (unsigned int)(time/1000)/60, (unsigned int)(time/1000)%60, cnor,
 
665
                       cerror, cnor, cval, start - 4, cnor,
 
666
                       cval, s-l+4, cnor, (s-l+4>1)?"s":"",
 
667
                       cval, start - 4 + l, cnor);
 
668
                  errors++;
 
669
               }
 
670
            }       
 
671
 
 
672
            // try to fix header including sync information
 
673
            if(fix_headers && (s>l-4)) {
 
674
               unsigned int old_padding_bit = head.padding_bit;
 
675
               head.padding_bit = 0;
 
676
               if(s-l+4 == frame_length(head)) {
 
677
                  fmes(name, "frame %s%5d%s/%s%2u:%02u%s: %sfixing header (including sync)%s\n",
 
678
                       cval, frame, cnor,
 
679
                       cval, (unsigned int)(time/1000)/60, (unsigned int)(time/1000)%60, cnor,
 
680
                       cerror, cnor);
 
681
                  set_header((unsigned char *)(p+l-4), head);
 
682
                  frame++; // we just created a new frame
 
683
                  time+=frame_duration(head);
 
684
                  l = s-l+4;
 
685
               } else {
 
686
                  head.padding_bit = 1;
 
687
                  if(s-l+4 == frame_length(head)) {
 
688
                     fmes(name, "frame %s%5d%s/%s%2u:%02u%s: %sfixing header (including sync)%s\n",
 
689
                          cval, frame, cnor,
 
690
                          cval, (unsigned int)(time/1000)/60, (unsigned int)(time/1000)%60, cnor,
 
691
                          cerror, cnor);
 
692
                     set_header((unsigned char *)(p+l-4), head);
 
693
                     frame++; // we just created a new frame
 
694
                     time+=frame_duration(head);
 
695
                     l = s-l+4;
 
696
                  } else {
 
697
                     // prevent possible side effect
 
698
                     head.padding_bit = old_padding_bit;
 
699
                  }               
 
700
               }
 
701
            } 
 
702
 
 
703
            // position on next frame
 
704
            p += s;
 
705
            rest -= s;
 
706
            start += s;
 
707
         } else {
 
708
            // valid header
 
709
            
 
710
            // check for constant parameters
 
711
            if(!head.sameConstant(h)) {
 
712
               if(!ign_const) {
 
713
                  fmes(name, "frame %s%5d%s/%s%2u:%02u%s: %sconstant parameter switching%s at %s0x%08x%s (%s0x%08x%s -> %s0x%08x%s)\n",
 
714
                       cval, frame, cnor,
 
715
                       cval, (unsigned int)(time/1000)/60, (unsigned int)(time/1000)%60, cnor,
 
716
                       cerror, cnor,
 
717
                       cval, start, cnor,
 
718
                       cval, head.get_int()&CONST_MASK, cnor,
 
719
                       cval, h.get_int()&CONST_MASK, cnor);
 
720
                  if(h.ID!=head.ID)
 
721
                     printf("frame %s%5d%s/%s%2u:%02u%s:   %sMPEG version switching%s (MPEG %s%1.1f%s -> MPEG %s%1.1f%s)\n",
 
722
                            cval, frame, cnor,
 
723
                            cval, (unsigned int)(time/1000)/60, (unsigned int)(time/1000)%60, cnor,
 
724
                            cerror, cnor,
 
725
                            cval, head.version(), cnor,
 
726
                            cval, h.version(), cnor);
 
727
                  if(h.layer_index!=head.layer_index)
 
728
                     printf("frame %s%5d%s/%s%2u:%02u%s:   %sMPEG layer switching%s (layer %s%1d%s -> layer %s%1d%s)\n",
 
729
                            cval, frame, cnor,
 
730
                            cval, (unsigned int)(time/1000)/60, (unsigned int)(time/1000)%60, cnor,
 
731
                            cerror, cnor,
 
732
                            cval, head.layer(), cnor,
 
733
                            cval, h.layer(), cnor);
 
734
                  if(h.samp_rate()!=head.samp_rate())
 
735
                     printf("frame %s%5d%s/%s%2u:%02u%s:   %ssampling frequency switching%s (%s%f%skHz -> %s%f%skHz)\n",
 
736
                            cval, frame, cnor,
 
737
                            cval, (unsigned int)(time/1000)/60, (unsigned int)(time/1000)%60, cnor,
 
738
                            cerror, cnor,
 
739
                            cval, head.samp_rate(), cnor,
 
740
                            cval, h.samp_rate(), cnor);
 
741
                  if(h.mode!=head.mode)
 
742
                     printf("frame %s%5d%s/%s%2u:%02u%s:   %smode switching%s (%s%s%s -> %s%s%s)\n",
 
743
                            cval, frame, cnor,
 
744
                            cval, (unsigned int)(time/1000)/60, (unsigned int)(time/1000)%60, cnor,
 
745
                            cerror, cnor,
 
746
                            cval, head.mode_str(), cnor,
 
747
                            cval, h.mode_str(), cnor);
 
748
                  if(h.protection_bit!=head.protection_bit)
 
749
                     printf("frame %s%5d%s/%s%2u:%02u%s:   %sprotection bit switching%s (%s%s%s -> %s%s%s)\n",
 
750
                            cval, frame, cnor,
 
751
                            cval, (unsigned int)(time/1000)/60, (unsigned int)(time/1000)%60, cnor,
 
752
                            cerror, cnor,
 
753
                            cval, head.protection_bit?"no crc":"crc", cnor,
 
754
                            cval, h.protection_bit?"no crc":"crc", cnor);
 
755
                  if(h.copyright!=head.copyright)
 
756
                     printf("frame %s%5d%s/%s%2u:%02u%s:   %scopyright bit switching%s (%s%s%s -> %s%s%s)\n",
 
757
                            cval, frame, cnor,
 
758
                            cval, (unsigned int)(time/1000)/60, (unsigned int)(time/1000)%60, cnor,
 
759
                            cerror, cnor,
 
760
                            cval, head.copyright?"copyright":"no copyright", cnor,
 
761
                            cval, h.copyright?"copyright":"no copyright", cnor);
 
762
                  if(h.original!=head.original)
 
763
                     printf("frame %s%5d%s/%s%2u:%02u%s:   %soriginal bit switching%s (%s%s%s -> %s%s%s)\n",
 
764
                            cval, frame, cnor,
 
765
                            cval, (unsigned int)(time/1000)/60, (unsigned int)(time/1000)%60, cnor,
 
766
                            cerror, cnor,
 
767
                            cval, head.original?"original":"not original", cnor,
 
768
                            cval, h.original?"original":"not original", cnor);
 
769
                  errors++;
 
770
               }
 
771
               if(fix_headers) {
 
772
                  fmes(name, "frame %s%5d%s/%s%2u:%02u%s: %sfixing header%s\n", 
 
773
                       cval, frame, cnor,
 
774
                       cval, (unsigned int)(time/1000)/60, (unsigned int)(time/1000)%60, cnor,
 
775
                       cerror, cnor);
 
776
                  // fix only what should be
 
777
                  set_header(h, head);
 
778
                  set_header((unsigned char *)p, h);
 
779
               } 
 
780
            }
 
781
            if(head.bitrate_index != h.bitrate_index) {
 
782
               if(!ign_bit) {
 
783
                  fmes(name, "frame %s%5d%s/%s%2u:%02u%s: %sbitrate switching%s (%s%d%s -> %s%d%s)\n", 
 
784
                       cval, frame, cnor,
 
785
                       cval, (unsigned int)(time/1000)/60, (unsigned int)(time/1000)%60, cnor,
 
786
                       cerror, cnor, 
 
787
                       cval, head.bitrate(), cnor,
 
788
                       cval, h.bitrate(), cnor);
 
789
                  errors++;
 
790
                  if(fix_headers) {
 
791
                     fmes(name, "frame %s%5d%s/%s%2u:%02u%s: %sfixing header%s\n", 
 
792
                          cval, frame, cnor,
 
793
                          cval, (unsigned int)(time/1000)/60, (unsigned int)(time/1000)%60, cnor,
 
794
                          cerror, cnor);
 
795
                     // fix only what should be
 
796
                     h.bitrate_index=head.bitrate_index;
 
797
                     set_header((unsigned char *)p, h);
 
798
                  } 
 
799
               }
 
800
            }
 
801
            head = h;
 
802
                    
 
803
            // check crc16
 
804
            if((!ign_crc)&&(h.protection_bit==0)&&(rest>=32+6)) {
 
805
               // reset crc checker
 
806
               crc.reset(0xffff);
 
807
               // get length of side info
 
808
               s = 0;
 
809
               if(h.version()==1.0) { // mpeg 1.0
 
810
                  switch(h.layer()) { 
 
811
                   case 3:            // layer 3
 
812
                     if(h.mode==Header::SINGLE_CHANNEL) s = 17;
 
813
                     else s = 32;
 
814
                     break;
 
815
                     
 
816
                   case 1:            // layer 1
 
817
                     switch(h.mode) {
 
818
                      case Header::SINGLE_CHANNEL: s = 16; break;
 
819
                      case Header::DUAL_CHANNEL:   s = 32; break;
 
820
                      case Header::STEREO:         s = 32; break;
 
821
                      case Header::JOINT_STEREO:   s = 18+h.mode_extension*2; break;
 
822
                     }
 
823
                     break;
 
824
                     
 
825
                   default:
 
826
                     s = 0; // mpeg 1.0 layer 2 not yet supported
 
827
                     break;                  
 
828
                  } 
 
829
               } else {               // mpeg 2.0 or 2.5
 
830
                  if(h.layer()==3) {  // layer 3
 
831
                     if(h.mode==Header::SINGLE_CHANNEL) s = 9; 
 
832
                     else s = 17;
 
833
                  } else {
 
834
                     s = 0; // mpeg 2.0 or 2.5 layer 1 and 2 not yet supported
 
835
                  }
 
836
               }
 
837
               if(s) {
 
838
                  // calc crc
 
839
                  crc.add(p[2]);
 
840
                  crc.add(p[3]);
 
841
                  for(int i=0; i < s; i++) crc.add(p[i+6]);
 
842
                  // check crc
 
843
                  unsigned short c = p[5] | ((unsigned short)(p[4])<<8);
 
844
                  int fixed_crc = 0;
 
845
                  if(c != crc.crc()) {
 
846
                     if(fix_crc) {
 
847
                        fixed_crc = set_crc_value((unsigned char *) &(p[4]), crc.crc());
 
848
                     }
 
849
                     fmes(name, "frame %s%5d%s/%s%2u:%02u%s: %scrc error%s (%s0x%04x%s!=%s0x%04x%s)%s\n",
 
850
                          cval, frame, cnor,
 
851
                          cval, (unsigned int)(time/1000)/60, (unsigned int)(time/1000)%60, cnor,
 
852
                          cerror, cnor, 
 
853
                          cval, c, cnor, cval, crc.crc(), cnor, fixed_crc ? " fixed" : "");
 
854
                     errors++;
 
855
                  }
 
856
//                printf("frame=%d, pos=%d, s=%d, c=%04x crc.crc()=%04x\n", frame, start, s, c, crc.crc());
 
857
               }
 
858
            }
 
859
            
 
860
            // skip to next frame
 
861
            l = frame_length(h);
 
862
            p += l;
 
863
            rest -= l;
 
864
            start += l;
 
865
            frame++;
 
866
            time+=frame_duration(h);
 
867
         }
 
868
         
 
869
         // maximum number of error reached?
 
870
         if(max_errors && (errors >= max_errors))
 
871
         {
 
872
            fmes(name, "frame %s%5d%s/%s%2u:%02u%s: %smaximum number of errors exceeded%s\n", 
 
873
                 cval, frame, cnor,
 
874
                 cval, (unsigned int)(time/1000)/60, (unsigned int)(time/1000)%60, cnor,
 
875
                 cerror, cnor);
 
876
            rest = 0;
 
877
            break;
 
878
         }
 
879
      }
 
880
      
 
881
      // check for truncated file
 
882
      if(rest < 0) {
 
883
         if(!ign_trunc) {
 
884
            fmes(name, "frame %s%5d%s/%s%2u:%02u%s: %sfile truncated%s, %s%d%s byte%s missing for last frame\n", 
 
885
                 cval, frame, cnor,
 
886
                 cval, (unsigned int)(time/1000)/60, (unsigned int)(time/1000)%60, cnor,
 
887
                 cerror, cnor, cval, -rest, cnor, (-rest)>1?"s":"");        
 
888
            errors++;
 
889
         }
 
890
      }
 
891
      
 
892
      // check for trailing junk
 
893
      if(rest > 0) {
 
894
         if(!ign_end) {
 
895
            fmes(name, "frame %s%5d%s/%s%2u:%02u%s: %s%d%s %sbyte%s of junk after last frame%s at %s0x%08x%s\n", 
 
896
                 cval, frame, cnor,
 
897
                 cval, (unsigned int)(time/1000)/60, (unsigned int)(time/1000)%60, cnor,
 
898
                 cval, rest, cnor, cerror, (rest>1)?"s":"", cnor, cval, start, cnor);
 
899
            errors++;
 
900
            // check for possible id3 tags within the junk
 
901
            while(rest >= 128)
 
902
            {
 
903
                int offset=Tagv1::find_next_tag(p, rest);
 
904
                if(offset!=-1) {
 
905
                   start+=offset;
 
906
                   p+=offset;
 
907
                   tag=new Tagv1(p);
 
908
                   fmes(name, "in trailing junk: %spossible %s id3 tag v%u.%u%s at %s0x%08x%s\n",
 
909
                        cerror, (tag->isValidSpecs()?"valid":"invalid"),
 
910
                                (tag->version())>>8, (tag->version())&0xff, cnor,
 
911
                        cval, start, cnor);
 
912
                   delete tag;
 
913
                   p+=3;
 
914
                   start+=3;
 
915
                   rest-=(offset+3);
 
916
                } else {
 
917
                   start+=rest;
 
918
                   p+=rest;
 
919
                   rest=0;
 
920
                }
 
921
            }
 
922
         }
 
923
      }      
 
924
   }   
 
925
   if(progress) {
 
926
      fputs("\r                                                                              \r", stderr);
 
927
      fflush(stderr);
 
928
   }
 
929
   if((errors == 0) && show_valid_files)
 
930
      fmes(name, "%svalid audio mpeg stream%s\n", cok, cnor);
 
931
   return errors > 0;
 
932
}
 
933
 
 
934
 
 
935
// returns true on anomaly
 
936
bool anomaly_check(const char *name, const unsigned char *p, int len, bool err_check, int& err) {
 
937
   bool had_ano = false;
 
938
   int start = find_next_header(p, len, MIN_VALID);
 
939
   if(start>=0) {
 
940
      Header h = get_header(p+start);
 
941
      if(!ano_any_ver) {
 
942
         if(h.version()!=1.0) {
 
943
            fmes(name, "%sanomaly%s: audio mpeg version %s%3.1f%s stream\n", 
 
944
                 cano, cnor, cval, h.version(), cnor);   
 
945
            had_ano = true;
 
946
         } 
 
947
      }
 
948
      if(!ano_any_layer) {
 
949
         if(h.layer()!=3) {
 
950
            fmes(name, "%sanomaly%s: audio mpeg %slayer %d%s stream\n", 
 
951
                 cano, cnor, cval, h.layer(), cnor);     
 
952
            had_ano = true;
 
953
         } 
 
954
      }
 
955
      if(!ano_any_rate) {
 
956
         if(h.samp_rate()!=44.1) {
 
957
            fmes(name, "%sanomaly%s: sampling rate %s%4.1fkHz%s\n", 
 
958
                 cano, cnor, cval, h.samp_rate(), cnor); 
 
959
            had_ano = true;
 
960
         }
 
961
      }
 
962
      if(!ano_any_bit) {
 
963
         if(h.bitrate()!=128) {
 
964
            fmes(name, "%sanomaly%s: bitrate %s%3dkbit/s%s\n", 
 
965
                 cano, cnor, cval, h.bitrate(), cnor); 
 
966
            had_ano = true;
 
967
         }
 
968
      }
 
969
      if(!ano_any_mode) {
 
970
         if(h.mode!=Header::JOINT_STEREO) {
 
971
            fmes(name, "%sanomaly%s: mode %s%s%s\n", 
 
972
                 cano, cnor, cval, h.mode_str(), cnor);  
 
973
            had_ano = true;
 
974
         }
 
975
      }
 
976
      if(!ano_any_crc) {
 
977
         if(h.protection_bit==1) {
 
978
            fmes(name, "%sanomaly%s: %sno crc%s\n", 
 
979
                 cano, cnor, cval, cnor);           
 
980
            had_ano = true;
 
981
         }
 
982
      }
 
983
      if(!ano_any_emp) {
 
984
         if(h.emphasis!=Header::emp_NONE) {
 
985
            fmes(name, "%sanomaly%s: emphasis %s%s%s\n", 
 
986
                 cano, cnor, cval, h.emphasis_str(), cnor); 
 
987
            had_ano = true;
 
988
         }
 
989
      }
 
990
   } else {
 
991
      if((!err_check)&&(!ign_noamp)) {
 
992
         fmes(name, "%s%s%s\n", cerror, (len?"not an audio mpeg stream":"empty file"), cnor);
 
993
         ++err;
 
994
      }
 
995
   }
 
996
   return had_ano;
 
997
}
 
998
                                        
 
999
 
 
1000
// returns the stream duration in ms
 
1001
// also returns minimum, maximum and average bitrates if told to
 
1002
unsigned int stream_duration(const unsigned char *p, int len, unsigned short int *minbr, unsigned short int *maxbr, unsigned short int *avgbr) {
 
1003
   int next = find_next_header(p, len, MIN_VALID);
 
1004
   int rest = len - next;
 
1005
   double duration = 0.0;
 
1006
   unsigned short int min = 2048, max = 0;
 
1007
   unsigned long int bytes = 0;
 
1008
 
 
1009
   if(next<0) return 0;
 
1010
 
 
1011
   while(rest>=4) {
 
1012
      Header h=get_header(p+next);
 
1013
      if(!h.isValid()) {
 
1014
         int old = next;
 
1015
         next = find_next_header(p+old, rest, MIN_VALID);
 
1016
         if(next<0) break;
 
1017
         rest -= next;
 
1018
         next += old;
 
1019
      }
 
1020
      else
 
1021
      {
 
1022
         int l=frame_length(h);
 
1023
         int br=h.bitrate();
 
1024
         if(br>max) max=br;
 
1025
         if(br<min) min=br;
 
1026
         bytes+=l;
 
1027
         next+=l;
 
1028
         rest-=l;
 
1029
         duration+=frame_duration(h);
 
1030
      }
 
1031
   }
 
1032
 
 
1033
   if(minbr!=NULL) { *minbr = min; }
 
1034
   if(maxbr!=NULL) { *maxbr = max; }
 
1035
   if(avgbr!=NULL) { *avgbr = (bytes * 8) / (unsigned int)duration; }
 
1036
   return (unsigned int)duration;
 
1037
}
 
1038
 
 
1039
 
 
1040
// return true if junk was found and cut
 
1041
bool cut_junk_end(const char *name, const unsigned char *p, int len, const unsigned char *free_p, int fd, int& err) {
 
1042
// this is basically the error_check routine which treats only the trainling junk case
 
1043
// (implemented by Pollyanna Lindgren <jtlindgr@cs.helsinki.fi>)
 
1044
   int start = find_next_header(p, len, MIN_VALID);
 
1045
   int rest = len;
 
1046
   int frame=0;
 
1047
   int l,s;
 
1048
   Tagv1 *tag = 0;
 
1049
   bool have_a_tag = false;
 
1050
 
 
1051
   if(start<0) {
 
1052
      if(!ign_noamp) {
 
1053
         fmes(name, "%s%s%s\n", cerror, (len?"not an audio mpeg stream":"empty file"), cnor);
 
1054
         err++;
 
1055
      }
 
1056
      return false;
 
1057
   } else {
 
1058
      
 
1059
      // check for TAG trailer
 
1060
      if(rest >= 128) {
 
1061
         tag = new Tagv1(p + rest - 128);
 
1062
         if(tag->isValid()) {
 
1063
            if(progress) putc('\r', stderr);
 
1064
            if(tag->store())
 
1065
            {
 
1066
               fmes(name, "%scut-junk-end: %s id3 tag trailer v%u.%u found, protecting it%s\n",
 
1067
                  cok, (tag->isValidSpecs()?"valid":"invalid"),
 
1068
                  (tag->version())>>8, (tag->version())&0xff, cnor);
 
1069
               have_a_tag = true;
 
1070
            }
 
1071
            else
 
1072
            {
 
1073
               fmes(name, "%scut-junk-end: %s id3 tag trailer v%u.%u found, could not protect it, sorry%s\n",
 
1074
                  cok, (tag->isValidSpecs()?"valid":"invalid"),
 
1075
                  (tag->version())>>8, (tag->version())&0xff, cnor);
 
1076
            }
 
1077
         }
 
1078
      } 
 
1079
 
 
1080
      // check whole file
 
1081
      rest -= start;
 
1082
      p += start;
 
1083
      Header head = get_header(p);
 
1084
      l = frame_length(head);
 
1085
      p += l;
 
1086
      rest -= l;
 
1087
      start += l;
 
1088
      while(rest>=4) {
 
1089
         Header h = get_header(p);
 
1090
         frame++;
 
1091
         if(progress) {
 
1092
            if((frame%1000)==0) {
 
1093
               putc('.', stderr);
 
1094
               fflush(stderr);
 
1095
            }
 
1096
         }
 
1097
         if(!(h.isValid()&&(frame_length(h)>=21))) {
 
1098
            // invalid header
 
1099
            
 
1100
            // search for next valid header
 
1101
            s = find_next_header(p, rest, MIN_VALID);
 
1102
            
 
1103
            if(s<0) break; // error: junk at eof
 
1104
            
 
1105
            // skip s invalid bytes
 
1106
            p += s;
 
1107
            rest -= s;
 
1108
            start += s;
 
1109
         } else {
 
1110
            // valid header
 
1111
            
 
1112
            head = h;
 
1113
            
 
1114
            // skip to next frame
 
1115
            l = frame_length(h);
 
1116
            p += l;
 
1117
            rest -= l;
 
1118
            start += l;
 
1119
         }
 
1120
      }
 
1121
      
 
1122
      // in case we had found a tag
 
1123
      if((rest >= 128) && have_a_tag)
 
1124
      {
 
1125
        rest-=128;
 
1126
      }
 
1127
      
 
1128
      // remove incomplete last frames
 
1129
      //if((rest < 0) && remove_truncated_last_frame)
 
1130
      //{
 
1131
        // rest += l;
 
1132
      //}
 
1133
      
 
1134
      // remove trailing junk
 
1135
      if(rest > 0) {
 
1136
         fmes(name, "%scut-junk-end: removing last %s%d%s byte%s, %sretrying%s\n",
 
1137
              cok, cval, rest, cok, (rest>1)?"s":"", dummy?"not (due to dummy) ":"", cnor);
 
1138
         if(!dummy) {
 
1139
            // rewrite tag
 
1140
            if(have_a_tag)
 
1141
            {
 
1142
               if(tag->restore((unsigned char*)p)) {
 
1143
                  fmes(name, "%scut-junk-end: tag successfully restored%s\n", cok, cnor);
 
1144
               } else {
 
1145
                  fmes(name, "%scut-junk-end: unable to restore tag, sorry%s\n", cok, cnor);
 
1146
               }
 
1147
               delete tag;
 
1148
                     
 
1149
            }
 
1150
            // unmap file
 
1151
            if(munmap((char*)free_p, len)) {
 
1152
               perror("munmap");
 
1153
               userError("can't unmap file '%s'!\n", name);
 
1154
            }
 
1155
            // truncate file and close
 
1156
            len-=rest;
 
1157
#ifndef __STRICT_ANSI__
 
1158
            if(ftruncate(fd, len) < 0) {
 
1159
                perror("ftruncate");
 
1160
            }
 
1161
#else
 
1162
            userError("cannot truncate file since this executable was compiled with __STRICT_ANSI__ defined!\n");
 
1163
#endif
 
1164
            close(fd);      
 
1165
         }
 
1166
         return true;
 
1167
      } else {
 
1168
         fmes(name, "%scut-junk-end: no junk found%s\n", cok, cnor);        
 
1169
         return false;
 
1170
      }
 
1171
   }
 
1172
}
 
1173
                                         
 
1174
   
 
1175
// return true if trailing tag was found and cut
 
1176
bool cut_tag_end(const char *name, const unsigned char *p, int len, int fd, int& err) {
 
1177
// this is basically the cut_junk_end routine that only looks for a 128 bytes trailing tag
 
1178
// (implemented by Jean Delvare <delvare@ensicaen.ismra.fr>)
 
1179
   int start = find_next_header(p, len, MIN_VALID);
 
1180
   Tagv1* tag;
 
1181
 
 
1182
   if(start<0) {
 
1183
      if(!ign_noamp) {
 
1184
         fmes(name, "%s%s%s\n", cerror, (len?"not an audio mpeg stream":"empty file"), cnor);
 
1185
         err++;
 
1186
      }
 
1187
      return false;
 
1188
   } else {
 
1189
      
 
1190
      // check for TAG trailer
 
1191
      if(len>=128) {
 
1192
         tag=new Tagv1(p+len-128);
 
1193
         if(tag->isValid()) {
 
1194
            if(progress) putc('\r', stderr);
 
1195
            fmes(name, "%scut-tag-end: %s id3 tag trailer v%u.%u found and removed, %sretrying%s\n",
 
1196
                 cok, (tag->isValidSpecs()?"valid":"invalid"),
 
1197
                 (tag->version())>>8, (tag->version())&0xff, dummy?"not (due to dummy) ":"", cnor);
 
1198
            if(!dummy) {
 
1199
 
 
1200
               // unmap file
 
1201
               if(munmap((char*)p, len)) {
 
1202
                  perror("munmap");
 
1203
                  userError("can't unmap file '%s'!\n", name);
 
1204
               }
 
1205
                  
 
1206
               // truncate file and close
 
1207
               len-=128;
 
1208
#ifndef __STRICT_ANSI__
 
1209
               if(ftruncate(fd, len) < 0) {
 
1210
                  perror("ftruncate");
 
1211
               }
 
1212
#else
 
1213
               userError("cannot truncate file since this executable was compiled with __STRICT_ANSI__ defined!\n");
 
1214
#endif
 
1215
               close(fd);      
 
1216
            }
 
1217
            delete tag;
 
1218
            return true;
 
1219
         }
 
1220
         delete tag;
 
1221
      }
 
1222
      fmes(name, "%scut-tag-end: no tag found%s\n", cok, cnor);
 
1223
      return false;
 
1224
   }
 
1225
}
 
1226
 
 
1227
// check for ID3 v1.x tag
 
1228
bool checkForID3V1(const unsigned char *p, size_t len)
 
1229
{
 
1230
    if(len < 128)
 
1231
        return false;
 
1232
    return memcmp(p + len - 128, "TAG", 3) == 0;
 
1233
}
 
1234
 
 
1235
// check for ID3 v2.x tag
 
1236
bool checkForID3V2(const unsigned char *p, size_t len)
 
1237
{
 
1238
    if(len < 10)
 
1239
        return false;
 
1240
    return (memcmp(p + len - 10, "3DI", 3) == 0) || (memcmp(p, "ID3", 3) == 0);
 
1241
}
 
1242
 
 
1243
// check for plausible string
 
1244
bool isValidStr(const unsigned char *p, size_t len)
 
1245
{
 
1246
    for(size_t i = 0; (i < len) && (i < 30); i++)
 
1247
    {
 
1248
        if(p[i] == 0)
 
1249
            return i > 2;
 
1250
        if(!isprint(p[i]))
 
1251
            return false;
 
1252
    }
 
1253
        return true;
 
1254
}
 
1255
 
 
1256
// split artist and title
 
1257
void splitArtistTitle(const tstring& title, tstring &artistOut, tstring &titleOut)
 
1258
{
 
1259
    tstring origtitle = title;
 
1260
    const char *start = title.c_str();
 
1261
    int patlen = 3;
 
1262
    const char *p = strstr(start, " - ");
 
1263
    if(p == 0)
 
1264
    {
 
1265
        patlen = 1;
 
1266
        p = strstr(start, "-");
 
1267
    }
 
1268
    if(p)
 
1269
    {
 
1270
        size_t len = p - start;
 
1271
        artistOut = title.substr(0, len);
 
1272
        titleOut = title.substr(len + patlen);
 
1273
        if((strcasecmp(titleOut.substr(0, 3).c_str(), "Vol") == 0) ||
 
1274
           (strcasecmp(titleOut.substr(0, 3).c_str(), "CD1") == 0) ||
 
1275
           (strcasecmp(titleOut.substr(0, 3).c_str(), "CD3") == 0) ||
 
1276
           (strcasecmp(titleOut.substr(0, 3).c_str(), "CD4") == 0) ||
 
1277
           (strcasecmp(titleOut.substr(0, 3).c_str(), "CD2") == 0))
 
1278
        {
 
1279
            titleOut = origtitle;
 
1280
            artistOut = "";
 
1281
        }
 
1282
        if(artistOut == "Various")
 
1283
            artistOut = "";
 
1284
        if(artistOut == "Sampler")
 
1285
            artistOut = "";
 
1286
    }
 
1287
    else
 
1288
    {
 
1289
        artistOut = "";
 
1290
        titleOut = origtitle;
 
1291
    }
 
1292
}
 
1293
 
 
1294
 
 
1295
// check for possible tags more sloppy
 
1296
bool checkForTagsSloppy(const unsigned char *p, size_t len)
 
1297
{
 
1298
    if(len < 10)
 
1299
        return false;
 
1300
    size_t max = 1024*1024*1024;
 
1301
    if(max > len - 3)
 
1302
        max = len - 3;
 
1303
    for(size_t i = 0; i <= max; i++)
 
1304
    {
 
1305
        if(
 
1306
           ((memcmp(p + i, "TAG", 3) == 0) && (isValidStr(p + i + 3, len - i - 3))) ||
 
1307
           (
 
1308
            (
 
1309
             (memcmp(p + i, "ID3", 3) == 0) ||
 
1310
             (memcmp(p + i, "3DI", 3) == 0)
 
1311
             ) && 
 
1312
            (p[i + 3] < 10) && (p[i + 4] < 10) && ((p[i + 5] & 0xf) == 0) && 
 
1313
            (p[i + 6] < 128) && (p[i + 7] < 128) && (p[i + 8] < 128) && (p[i + 9] < 128)))
 
1314
        {
 
1315
            printf("%swarning: possible %c%c%c tag found at offset %d (%d from end) (%02x %02x %02x %02x %02x %02x %02x)%s\n", 
 
1316
                   cano, p[i+0], p[i+1], p[i+2], int(i), int(len - 3 - i), p[i+3]&255, p[i+4]&255, p[i+5]&255, 
 
1317
                   p[i+6]&255, p[i+7]&255, p[i+8]&255, p[i+9]&255, cnor);
 
1318
            return true;
 
1319
        }
 
1320
    }
 
1321
    return false;
 
1322
}
 
1323
 
 
1324
// capitalize string
 
1325
void capitalize(tstring &str)
 
1326
{
 
1327
    bool lastAlpha = false;
 
1328
    for(size_t i = 0; i < str.length(); i++)
 
1329
    {
 
1330
        if((!lastAlpha) && islower(str[i]))
 
1331
            str[i] = toupper(str[i]);
 
1332
        lastAlpha = isalpha(str[i]);
 
1333
    }
 
1334
}
 
1335
 
 
1336
// main
 
1337
int main(int argc, char *argv[]) {      
 
1338
 
 
1339
   // get parameters
 
1340
   TAppConfig ac(options, "options", argc, argv, 0, 0, VERSION);
 
1341
   
 
1342
   // get the terminal width if available
 
1343
   struct winsize win;
 
1344
   if(ioctl(1, TIOCGWINSZ, &win) == 0) 
 
1345
       columns = win.ws_col;
 
1346
   const char* scolumns=getenv("COLUMNS");
 
1347
   if(scolumns)
 
1348
      columns=atoi(scolumns)-1;
 
1349
   if(columns<30)
 
1350
      columns=79;
 
1351
 
 
1352
   // setup options
 
1353
   quiet = ac("quiet");
 
1354
   dummy = ac("dummy");
 
1355
   progress = ac("progress");
 
1356
   max_errors = ac.getInt("max-errors");
 
1357
   show_valid_files = ac("show-valid");
 
1358
   bool nommap = ac("no-mmap");
 
1359
   int opt=0;
 
1360
   // alt mode
 
1361
   if(ac("error-check")) opt=1; 
 
1362
   if(ac("fix-headers")) opt=1;
 
1363
   if(ac("fix-crc")) opt=1;
 
1364
   if(ac("add-tag")) opt=1;
 
1365
   if(ac("anomaly-check")) opt=1; 
 
1366
   if(ac("cut-junk-start")) opt=1; 
 
1367
   if(ac("cut-junk-end")) opt=1;
 
1368
   if(ac("cut-tag-end")) opt=1;
 
1369
   if(!ac.getString("edit-frame-b").empty()) opt=1;
 
1370
   // main mode
 
1371
   if(ac("dump-header")) opt++; 
 
1372
   if(ac("dump-tag")) opt++;
 
1373
   if(ac("list")) opt++; 
 
1374
   if(ac("compact-list")) opt++; 
 
1375
   if(ac("raw-list")) opt++; 
 
1376
   if(ac("print-files")) opt=1;
 
1377
   // check for mode
 
1378
   if(opt==0) 
 
1379
     userError("you must specify the mode of operation!  (try --help for more info)\n");
 
1380
   if(opt>1) 
 
1381
     userError("incompatible modes specified!  (try --help for more info)\n");
 
1382
   // color
 
1383
   if(!ac("color"))
 
1384
     cval = cnor = cano = cerror = cfil = cok = "";
 
1385
   if(ac("alt-color")) {
 
1386
      cval = c_val;
 
1387
      cano = c_ano;
 
1388
      cerror = c_err;
 
1389
      cfil = c_fil;
 
1390
      cnor = c_nor;
 
1391
      cok = c_ok;
 
1392
   }
 
1393
   tstring rawsepstr = ac.getString("raw-elem-sep");
 
1394
   char rawsep = strtol(rawsepstr.c_str(), 0, 0);
 
1395
   if(!rawsepstr.empty()) if(!isdigit(rawsepstr[0])) rawsep = rawsepstr[0];
 
1396
   tstring rawlinesepstr = ac.getString("raw-line-sep");
 
1397
   char rawlinesep = strtol(rawlinesepstr.c_str(), 0, 0);     
 
1398
   if(!rawlinesepstr.c_str()) if(!isdigit(rawlinesepstr[0])) rawsep = rawlinesepstr[0];
 
1399
   if(ac("raw-list")) {
 
1400
      quiet=true;
 
1401
      progress=false;
 
1402
   }
 
1403
   bool edit_frame_byte = !ac.getString("edit-frame-b").empty();
 
1404
   int efb_value = 0;
 
1405
   int efb_offset = 0;
 
1406
   int efb_frame = 0;
 
1407
   if(edit_frame_byte) {
 
1408
      tvector<tstring> a = split(ac.getString("edit-frame-b"), ",");
 
1409
      if((a.size() != 3) || !a[0].toInt(efb_frame) || !a[1].toInt(efb_offset) || !a[2].toInt(efb_value))
 
1410
        userError("format of parameter for --edit-frame-b is 'frame,offset,byteval'!\n");
 
1411
   }
 
1412
   bool recursive = ac("recursive");
 
1413
#ifdef __CYGWIN__
 
1414
   bool cross_filesystems = true;
 
1415
#else
 
1416
   bool cross_filesystems = !ac("xdev");
 
1417
#endif
 
1418
   tstring extensions = ac.getString("accept");
 
1419
   tstring reject_extensions = ac.getString("reject");
 
1420
   if(ac("only-mp3")) {
 
1421
      if(extensions.empty()) extensions = ONLY_MP3;
 
1422
      else                   extensions += "," ONLY_MP3;
 
1423
   }
 
1424
   single_line = ac("single-line");
 
1425
   only_ascii = ac("ascii-only");
 
1426
   
 
1427
   
 
1428
   // check params
 
1429
   if(ac.numParam()==0) 
 
1430
     userError("need at least one file or directory! (try --help for more info)\n");
 
1431
   
 
1432
   // setup ignores/anys
 
1433
   ano_any_crc   = ac("any-crc");
 
1434
   ano_any_mode  = ac("any-mode");
 
1435
   ano_any_layer = ac("any-layer");
 
1436
   ano_any_bit   = ac("any-bitrate");
 
1437
   ano_any_emp   = ac("any-emphasis");
 
1438
   ano_any_rate  = ac("any-sampling");
 
1439
   ano_any_ver   = ac("any-version");
 
1440
   ign_crc   = ac("ign-crc-error");
 
1441
   ign_start = ac("ign-junk-start");
 
1442
   ign_end   = ac("ign-junk-end");
 
1443
   ign_tag   = ac("ign-tag128");
 
1444
   ign_bit   = ac("ign-bitrate-sw");
 
1445
   ign_const = ac("ign-constant-sw");
 
1446
   ign_trunc = ac("ign-truncated");
 
1447
   ign_noamp = ac("ign-non-ampeg");
 
1448
   ign_sync  = ac("ign-resync");
 
1449
 
 
1450
   // get file list
 
1451
   tvector<tstring> filelist;
 
1452
   // from command line (perhaps recurse directories)
 
1453
   for(size_t i = 0; i < ac.numParam(); i++) {
 
1454
      if(recursive) {
 
1455
         try {
 
1456
            TFile f(ac.param(i));
 
1457
            if(f.isdir()) {
 
1458
               TSubTreeContext context(cross_filesystems);
 
1459
               TDir d(f, context);
 
1460
               filelist += findFilesRecursive(d);
 
1461
               continue;
 
1462
            }
 
1463
         }
 
1464
         catch(...) {}
 
1465
      } 
 
1466
      filelist.push_back(ac.param(i));
 
1467
   }     
 
1468
   // read filenames from text file
 
1469
   tstring filelistfile = ac.getString("filelist");
 
1470
   if(!filelistfile.empty()) {
 
1471
      try {
 
1472
         filelist += loadTextFile(filelistfile.c_str());
 
1473
      }
 
1474
      catch(...) {
 
1475
         userError("cannot open file '%s' for reading!\n", filelistfile.c_str());
 
1476
      }      
 
1477
   }
 
1478
   // filter filenames
 
1479
   if(!extensions.empty())
 
1480
     filelist = filterExtensions(filelist, split(extensions, ",;:"));
 
1481
   if(!reject_extensions.empty())
 
1482
     filelist = filterExtensions(filelist, split(reject_extensions, ",;:"), true);
 
1483
 
 
1484
   // print filelist
 
1485
   if(ac("print-files")) {
 
1486
      for(size_t i = 0; i < filelist.size(); i++) 
 
1487
        printf("%s\n", filelist[i].c_str());
 
1488
      exit(0);
 
1489
   }
 
1490
   
 
1491
   // check all files
 
1492
   int err=0;
 
1493
   int checked=0;
 
1494
   int num_ano=0;
 
1495
   int num_tagsadded = 0;
 
1496
   CRC16 crc(CRC16::CRC_16);   
 
1497
   FILE *log = NULL;
 
1498
   if(!ac.getString("log-file").empty()) {
 
1499
      log = fopen(ac.getString("log-file").c_str(), "a");
 
1500
      if(log==NULL)
 
1501
        userError("can't open logfile '%s'!\n", ac.getString("log-file").c_str());
 
1502
   }
 
1503
   for(size_t i = 0; i < filelist.size(); i++) {
 
1504
      const char *name = filelist[i].c_str();
 
1505
      // ignore all files starting with ._ which are apple metafiles
 
1506
      {
 
1507
          tstring t = name;
 
1508
          t.extractFilename();
 
1509
          if((t[0] == '.') && (t[1] == '_'))
 
1510
              continue;
 
1511
      }
 
1512
       
 
1513
      // check for file
 
1514
      struct stat buf;
 
1515
      if(stat(name, &buf)) {
 
1516
         fmes(name, "%scan't stat file (dangling symbolic link?)%s\n", cerror, cnor);
 
1517
         continue;
 
1518
      }
 
1519
      if(S_ISDIR(buf.st_mode)) {
 
1520
         fmes(name, "%signoring directory%s\n", cerror, cnor);
 
1521
         continue;
 
1522
      }
 
1523
      if(!S_ISREG(buf.st_mode)) {
 
1524
         fmes(name, "%signoring non regular file%s\n", cerror, cnor);
 
1525
         continue;
 
1526
      }
 
1527
      off_t len = buf.st_size;
 
1528
      
 
1529
      // open file
 
1530
      int flags = O_RDONLY;
 
1531
      int prot = PROT_READ;
 
1532
      if(!dummy) {
 
1533
         if(ac("fix-headers")||ac("cut-junk-start")||ac("fix-crc")||ac("cut-junk-end")||ac("cut-tag-end")||edit_frame_byte) {
 
1534
            flags = O_RDWR;
 
1535
            prot |= PROT_WRITE;
 
1536
            if(nommap)
 
1537
                 userError("option --no-mmap does not yet support options which may modify a file (e.g --fix-crc)!\n");
 
1538
         }
 
1539
      }
 
1540
      flags |= O_BINARY;
 
1541
      int fd = open(name, flags);
 
1542
      if(fd==-1) {
 
1543
         perror("open");
 
1544
         userError("can't open file '%s' for reading!\n", name);      
 
1545
      }
 
1546
       
 
1547
      // mmap or read file
 
1548
      const unsigned char *p;
 
1549
      const unsigned char *free_p = 0;
 
1550
      if(nommap) {
 
1551
          // read file
 
1552
          free_p = p = new unsigned char[len];
 
1553
          if(read(fd, (void*)p, len) != len) {
 
1554
              perror("read");
 
1555
              userError("error while reading file '%s'!\n", name);
 
1556
          }
 
1557
      } else {
 
1558
          // mmap file
 
1559
          if(len) {
 
1560
              free_p = p = (const unsigned char *) mmap(0, len, prot, MAP_SHARED, fd, 0);
 
1561
          } else {
 
1562
              p = NULL;
 
1563
          }
 
1564
          if(p==(const unsigned char *)MAP_FAILED) {
 
1565
              perror("mmap");
 
1566
              userError("can't map file '%s'!\n", name);
 
1567
          }
 
1568
      }
 
1569
 
 
1570
      // edit single byte of a frame
 
1571
      if(edit_frame_byte) {
 
1572
         if(progress) {
 
1573
            tstring s = tstring(name).shortFilename(79);
 
1574
            fprintf(stderr, "%-79.79s\r", s.c_str());
 
1575
            fflush(stderr);
 
1576
         }       
 
1577
         unsigned char *pp = const_cast<unsigned char *>(skip_n_frames(free_p, len, efb_frame));
 
1578
         if(pp) {
 
1579
            if(!dummy) pp[efb_offset] = efb_value;
 
1580
         } else {
 
1581
            fmes(name, "%sframe %s%d%s not found%s\n", cerror, cval, efb_frame, cerror, cnor);
 
1582
            err++;
 
1583
         }               
 
1584
      }      
 
1585
            
 
1586
      // list
 
1587
      if(ac("list")||ac("compact-list")||ac("raw-list")) {
 
1588
         // speed up list of very large files (like *.wav)
 
1589
         int maxl = 128*1024; // search max 128k
 
1590
         int start = find_next_header(p, len<maxl?len:maxl, MIN_VALID);
 
1591
         if(start<0) {
 
1592
            if(!ign_noamp) {
 
1593
               if(ac("raw-list")) {
 
1594
                  printf("%s%c%s%c%c", (len?"invalid_stream":"empty_stream"), rawsep, name, rawsep, rawlinesep);
 
1595
               } else if(ac("compact-list")) {
 
1596
                  printf("%s%-25s%s%s %s%s%s\n", cerror, (len?"not an audio mpeg stream":"empty file"), cnor, (columns>=82?"  ":""), cfil, name, cnor);
 
1597
               } else {        
 
1598
                  fmes(name, "%s%s%s\n", cerror, (len?"not an audio mpeg stream":"empty file"), cnor);
 
1599
               }
 
1600
               err++;
 
1601
            }
 
1602
         } else {
 
1603
            Header h = get_header(p+start);
 
1604
            unsigned short int minbr = 0, maxbr = 0, avgbr;
 
1605
            unsigned int l_min = (ign_bit?stream_duration(p, len, &minbr, &maxbr, &avgbr):len/(h.bitrate()/8));
 
1606
            unsigned int l_mil = l_min%1000;
 
1607
            l_min/=1000;
 
1608
            unsigned int l_sec = l_min%60;
 
1609
            l_min/=60;
 
1610
            tstring l_str;
 
1611
            if(l_min >= 60)
 
1612
              l_str.sprintf("%2u:%02u", l_min/60, l_min%60);
 
1613
            else 
 
1614
              l_str.sprintf("   %2u", l_min);
 
1615
            Tagv1 *tag=new Tagv1(p+len-128);
 
1616
            unsigned short int tag_version=0;
 
1617
            if(tag->isValid())
 
1618
                tag_version=tag->version();
 
1619
            if(ac("list")) {
 
1620
               unsigned int xwidth = 0;
 
1621
               tstring n = single_line?tstring(name):tstring(name).shortFilename(columns-1);
 
1622
               fmes(name, "mpeg %s%3.1f%s layer %s%d%s %s%2.1f%skHz %s%3d%skbps",
 
1623
                      h.version()==1.0?cval:cano, h.version(), cnor, 
 
1624
                      h.layer()==3?cval:cano, h.layer(), cnor, 
 
1625
                      h.samp_rate()==44.1?cval:cano, h.samp_rate(), cnor, 
 
1626
                      (h.bitrate()==128&&minbr==maxbr)?cval:cano, minbr!=maxbr?avgbr:h.bitrate(), cnor);
 
1627
               if(ign_bit && columns>=83) {
 
1628
                  printf(" %s%s%s",
 
1629
                         minbr!=maxbr?cano:cval,minbr!=maxbr?"VBR":"CBR",cnor);
 
1630
                  xwidth+=4;
 
1631
               } 
 
1632
               printf(" %s%-12.12s%s %s%-7.7s%s %s%s%s %s%s%s %s%s%s %s%s:%02u.%02u%s",
 
1633
                      h.mode==Header::JOINT_STEREO?cval:cano, h.mode_str(), cnor,
 
1634
                      h.emphasis==Header::emp_NONE?cval:cano, h.emphasis_str(), cnor,
 
1635
                      h.protection_bit?cano:cval, h.protection_bit?"---":"crc", cnor, 
 
1636
                      h.original?cval:cano, h.original?"orig":"----", cnor,
 
1637
                      cval, h.copyright?"copy":"----", cnor,
 
1638
                      cval, l_str.c_str(), l_sec, l_mil/10, cnor);
 
1639
               if(columns>=87+xwidth) {
 
1640
                 if(tag_version)
 
1641
                   printf(" id3 %s%1u.%1u%s", cval, tag_version>>8,
 
1642
                          tag_version&0xff,cnor);
 
1643
//               xwidth+=8;
 
1644
               }
 
1645
               printf("\n");
 
1646
            } else if(ac("compact-list")) {
 
1647
               unsigned int xwidth = 0;
 
1648
               printf("%s%c%s%s%d%s %s%2.0f%s %s%3d%s",
 
1649
                      h.version()==1.0?cval:cano, h.version()==1.0?'l':'L', cnor, 
 
1650
                      h.layer()==3?cval:cano, h.layer(), cnor, 
 
1651
                      h.samp_rate()==44.1?cval:cano, h.samp_rate(), cnor, 
 
1652
                      (h.bitrate()==128&&minbr==maxbr)?cval:cano, minbr!=maxbr?avgbr:h.bitrate(), cnor);
 
1653
               if(ign_bit && columns>=80) {
 
1654
                  printf("%s%c%s",
 
1655
                         minbr==maxbr?cval:cano, minbr==maxbr?' ':'V', cnor);
 
1656
                  xwidth+=1;
 
1657
               }
 
1658
               printf(" %s%s%s %s%s%s %s%s%s%s%s%s%s%s%s",
 
1659
                      h.mode==Header::JOINT_STEREO?cval:cano, h.short_mode_str(), cnor,
 
1660
                      h.emphasis==Header::emp_NONE?cval:cano, h.short_emphasis_str(), cnor,
 
1661
                      h.protection_bit?cano:cval, h.protection_bit?"-":"C", cnor, 
 
1662
                      h.original?cval:cano, h.original?"O":"-", cnor,
 
1663
                      cval, h.copyright?"Y":"-", cnor);
 
1664
               if(columns>=81+xwidth) {
 
1665
                 if(tag_version)
 
1666
                   printf(" %s%1u%s",cval,tag_version>>8,cnor);
 
1667
                 else
 
1668
                   printf(" %s-%s",cval,cnor);
 
1669
                 xwidth+=2;
 
1670
               }
 
1671
               tstring n = tstring(name).shortFilename(columns-(26+xwidth));
 
1672
               n.replaceUnprintable(only_ascii);
 
1673
               printf(" %s%3u:%02u%s %s%s%s\n",
 
1674
                      cval, l_min, l_sec, cnor, cfil, n.c_str(), cnor);  
 
1675
            } else if(ac("raw-list")) {        
 
1676
               printf("valid_stream%c%.1f%c%d%c%.1f%c%d%c%s%c%s%c%s%c%s%c%s%c%s%c%u%c%u%c%u%c%s%c%c",
 
1677
                      rawsep,
 
1678
                      h.version(), rawsep,
 
1679
                      h.layer(), rawsep, 
 
1680
                      h.samp_rate(), rawsep,
 
1681
                      minbr!=maxbr?avgbr:h.bitrate(), rawsep,
 
1682
                      ign_bit?(minbr!=maxbr?"VBR":"CBR"):"?", rawsep,
 
1683
                      h.mode_str(), rawsep,
 
1684
                      h.emphasis_str(), rawsep,
 
1685
                      h.protection_bit?"---":"crc", rawsep,
 
1686
                      h.original?"orig":"copy", rawsep,
 
1687
                      h.copyright?"cprgt":"-----", rawsep,
 
1688
                      l_min, rawsep, 
 
1689
                      l_sec, rawsep, 
 
1690
                      l_mil, rawsep,
 
1691
                      name, rawsep, rawlinesep);
 
1692
            }
 
1693
            delete tag;
 
1694
         }
 
1695
      }
 
1696
      
 
1697
      // cut-junk-start
 
1698
      if(ac("cut-junk-start")) {
 
1699
         int start = find_next_header(p, len, MIN_VALID);
 
1700
         if(start<0) {
 
1701
            fmes(name, "%s%s%s\n", cerror, (len?"not an audio mpeg stream":"empty file"), cnor);
 
1702
            err++;
 
1703
         } else if(start==0) {
 
1704
            fmes(name, "%scut-junk-start: no junk found%s\n", cok, cnor);           
 
1705
         } else {
 
1706
            fmes(name, "%scut-junk-start: removing first %s%d%s byte%s, %sretrying%s\n",
 
1707
                 cok, cval, start, cok, (start>1)?"s":"", dummy?"not (due to dummy) ":"", cnor);
 
1708
            if(!dummy) {
 
1709
               // move start to begining and truncate the file
 
1710
               memmove((char*)free_p, free_p + start, len - start);
 
1711
               if(munmap((char*)free_p, len)) {
 
1712
                  perror("munmap");
 
1713
                  userError("can't unmap file '%s'!\n", name);
 
1714
               }
 
1715
               len-=start;
 
1716
#ifndef __STRICT_ANSI__
 
1717
               if(ftruncate(fd, len) < 0) {
 
1718
                  perror("ftruncate");
 
1719
               }
 
1720
#else
 
1721
               userError("cannot truncate file since this executable was compiled with __STRICT_ANSI__ defined!\n");
 
1722
#endif
 
1723
               close(fd);      
 
1724
               
 
1725
               // retry this file 
 
1726
               --i;
 
1727
               continue;
 
1728
            }
 
1729
         }
 
1730
      }
 
1731
 
 
1732
      // cut-tag-end
 
1733
      if(ac("cut-tag-end")) {
 
1734
         // lots of side effects: perhaps unmaps and closes file
 
1735
         if(cut_tag_end(name, p, len, fd, err)) {
 
1736
            // retry this file if not dummy
 
1737
            if(!dummy) {
 
1738
               --i;
 
1739
               continue;
 
1740
            }
 
1741
         }
 
1742
      }
 
1743
 
 
1744
      // cut-junk-end
 
1745
      if(ac("cut-junk-end")) {
 
1746
         // lots of side effects: perhaps unmaps and closes file
 
1747
         if(cut_junk_end(name, p, len, free_p, fd, err)) {
 
1748
            // retry this file if not dummy
 
1749
            if(!dummy) {
 
1750
               --i;
 
1751
               continue;           
 
1752
            }      
 
1753
         }
 
1754
      }
 
1755
      
 
1756
      // check for errors
 
1757
      if(ac("error-check") || ac("fix-headers") || ac("fix-crc")) {
 
1758
         if(progress) {
 
1759
            tstring s = tstring(name).shortFilename(79);
 
1760
            fprintf(stderr, "%-79.79s\r", s.c_str());
 
1761
            fflush(stderr);
 
1762
         }
 
1763
         if(error_check(name, p, len, crc, ac("fix-headers"), ac("fix-crc"))) {
 
1764
            if(log) fprintf(log, "%s\n", name);
 
1765
            ++err;
 
1766
         }
 
1767
      }
 
1768
 
 
1769
      // check for anomalies
 
1770
      if(ac("anomaly-check")) {
 
1771
         if(progress) {
 
1772
            tstring s = tstring(name).shortFilename(79);
 
1773
            fprintf(stderr, "%-79.79s\r", s.c_str());
 
1774
            fflush(stderr);
 
1775
         }
 
1776
         if(anomaly_check(name, p, len, ac("error-check"), err)) ++num_ano;
 
1777
      }      
 
1778
            
 
1779
      // dump header
 
1780
      if(ac("dump-header")) {
 
1781
         fmes(name, "\n");
 
1782
         for(int k=0; k<len-3; p++, k++) {
 
1783
            if(*p==255) {
 
1784
               Header h;               
 
1785
               h = get_header(p);
 
1786
               if(h.syncword==0xfff) {
 
1787
                  tstring s=h.print();
 
1788
                  printf("%7d %s\n", k, s.c_str());
 
1789
                  int l = frame_length(h);
 
1790
                  if(l>=21) {
 
1791
                     p+=l;
 
1792
                     p--;
 
1793
                     k+=l;
 
1794
                     k--;
 
1795
                  }
 
1796
               }
 
1797
            }
 
1798
         }
 
1799
      }      
 
1800
      
 
1801
      // dump tag
 
1802
      if(ac("dump-tag")) {
 
1803
         unsigned int err_thisfile=0;
 
1804
         fmes(name, "\n");
 
1805
         Tagv1 *tag=new Tagv1;
 
1806
         for(int k=0; k<len-127; k++) {
 
1807
            tag->setTarget(p+k);
 
1808
            if(tag->isValidGuess()) {
 
1809
               printf("  Found at: %s0x%08x%s (%s%s%s)\n", cval, k, cnor,
 
1810
                      (k==len-128?cok:cerror),
 
1811
                      ((k==len-128)||!(++err_thisfile)?"end":"in the stream"), cnor);
 
1812
               tag->fillFields();
 
1813
               printf("  Version: %s%u.%u%s\n", cval, tag->fields_version>>8,
 
1814
                      tag->fields_version&0xff, cnor);
 
1815
               printf("  Conforms to specification: %s%s%s\n",
 
1816
                      (tag->isValidSpecs()&&!tag->fields_spacefilled?cok:cerror),
 
1817
                      (tag->isValidSpecs()||!(++err_thisfile)?(tag->fields_spacefilled?"space filled":"yes"):"no"),
 
1818
                      cnor);
 
1819
               printf("  Title: \"%s%s%s\"\n", cval, tag->field_title, cnor);
 
1820
               printf("  Artist: \"%s%s%s\"\n", cval, tag->field_artist, cnor);
 
1821
               printf("  Album: \"%s%s%s\"\n", cval, tag->field_album, cnor);
 
1822
               printf("  Year: \"%s%s%s\"\n", cval, tag->field_year, cnor);
 
1823
               printf("  Comment: \"%s%s%s\"\n", cval, tag->field_comment, cnor);
 
1824
               if(tag->field_genre==0xff) // not set
 
1825
                  printf("  Genre: not set\n");
 
1826
               else
 
1827
               {
 
1828
                  printf("  Genre: %s%s%s\n", ((tag->field_genre>=Tagv1::genres_count)?cerror:cval),
 
1829
                         ((tag->field_genre<Tagv1::genres_count)?(Tagv1::id3_genres[tag->field_genre]):"unknown"),
 
1830
                         cnor);
 
1831
               }
 
1832
               if(tag->field_track)
 
1833
                  printf("  Track: %s%u%s\n", cval, tag->field_track, cnor);
 
1834
               k+=2;
 
1835
            }
 
1836
         }
 
1837
         delete tag;
 
1838
         if(err_thisfile) ++err;
 
1839
      }
 
1840
       
 
1841
       // --add-id3
 
1842
       bool addTag = false;
 
1843
       if(ac("add-tag"))
 
1844
       {
 
1845
           if(progress) {
 
1846
               tstring s = tstring(name).shortFilename(79);
 
1847
               fprintf(stderr, "%-79.79s\r", s.c_str());
 
1848
               fflush(stderr);
 
1849
           }
 
1850
           // check for existing tag
 
1851
           if(checkForID3V1(p, len))
 
1852
           {
 
1853
               if(ac("verbose"))
 
1854
                   fmes(name, "id3 tag v1.x found, not adding anything\n");
 
1855
           }
 
1856
           else if(checkForID3V2(p, len))
 
1857
           {
 
1858
               if(ac("verbose"))
 
1859
                   fmes(name, "id3 tag v2.x found, not adding anything\n");
 
1860
           }
 
1861
#if 0      
 
1862
           // this wqs just here to make sure we do not mis something
 
1863
           else if(checkForTagsSloppy(p, len))
 
1864
           {
 
1865
               fmes(name, "some tag found\n");         
 
1866
           }
 
1867
#endif     
 
1868
           else
 
1869
           {
 
1870
               // no tag found: add tag
 
1871
               addTag = true;
 
1872
           }
 
1873
       }
 
1874
      
 
1875
      if(nommap) {
 
1876
          // free mem
 
1877
          delete[] free_p;
 
1878
      } else {
 
1879
          // unmap file and close
 
1880
          if((free_p!=NULL)&&munmap((char*)free_p, len)) {
 
1881
              perror("munmap");
 
1882
              userError("can't unmap file '%s'!\n", name);
 
1883
          }
 
1884
      }
 
1885
       // close file
 
1886
      close(fd);       
 
1887
       
 
1888
       // add tag? (see above)
 
1889
       if(addTag)
 
1890
       {           
 
1891
           // extract data from filename
 
1892
           tstring title;  // 30
 
1893
           tstring artist; // 30
 
1894
           tstring album;  // 30
 
1895
           tstring comment;// 28
 
1896
           char track = 0;     // 1
 
1897
           tstring fname = name;
 
1898
           fname.translateChar('_', ' ');
 
1899
           fname.searchReplace("cd 1", "cd1");
 
1900
           fname.searchReplace("cd 2", "cd2");
 
1901
           fname.searchReplace("cd 3", "cd3");
 
1902
           fname.searchReplace("cd 4", "cd4");
 
1903
           fname.searchReplace(" - ms/disc1/", "/cd1 - ");
 
1904
           fname.searchReplace(" - ms/disc2/", "/cd2 - ");
 
1905
           fname.searchReplace("/cd1/", " - cd1/");
 
1906
           fname.searchReplace("/cd2/", " - cd2/");
 
1907
           fname.searchReplace("/cd3/", " - cd3/");
 
1908
           fname.searchReplace("/cd4/", " - cd4/");
 
1909
           fname.searchReplace("zance - a decade of dance from ztt", "zance decade of dance from ztt");
 
1910
           fname.searchReplace("/captain future soundtrack - ", "/");
 
1911
           fname.searchReplace("-ms/", "/");
 
1912
           fname.collapseSpace();
 
1913
           title = fname;
 
1914
           album = fname;
 
1915
           comment = fname;
 
1916
           title.extractFilename();
 
1917
           album.extractPath();
 
1918
           album.removeDirSlash();
 
1919
           album.extractFilename();
 
1920
           comment.extractPath();
 
1921
           comment.removeDirSlash();
 
1922
           comment.extractPath();
 
1923
           comment.removeDirSlash();
 
1924
           comment.extractFilename();
 
1925
 
 
1926
           // title
 
1927
           // remove album
 
1928
           if(title != album)
 
1929
               title.searchReplace(album, "");
 
1930
           // remove .mp3
 
1931
           if((title.length() >= 4) && strcasecmp(title.c_str() + title.length(), ".mp3"))
 
1932
               title.truncate(title.length() - 4);
 
1933
           // check for cd1 - 
 
1934
           if((strcasecmp(title.substr(0, 3).c_str(), "CD1") == 0) ||
 
1935
              (strcasecmp(title.substr(0, 3).c_str(), "CD1") == 0) ||
 
1936
              (strcasecmp(title.substr(0, 3).c_str(), "CD2") == 0))
 
1937
           {
 
1938
               album += " " + title.substr(0,3);
 
1939
               title = title.substr(3);
 
1940
               // skip separator
 
1941
               while(strchr(" -.", title.c_str()[0])) 
 
1942
                   title = title.substr(1);
 
1943
           }
 
1944
           // check for AA-TT format
 
1945
           if(isdigit(title[0]) && isdigit(title[1]) && isdigit(title[3]) && isdigit(title[4]) && (title[2] == '-'))
 
1946
           {
 
1947
               album += " cd" + title.substr(1,2);
 
1948
               title = title.substr(3);
 
1949
           }
 
1950
           // check for 'audio '
 
1951
           tstring ltitle = title;
 
1952
           ltitle.lower();
 
1953
           if(ltitle.substr(0, 5) == "audio")
 
1954
               title = title.substr(5) + " " + title.substr(0, 5);
 
1955
           else if(ltitle.substr(0, 10) == "track cd -")
 
1956
           {
 
1957
               title = title.substr(10);
 
1958
               album += " track cd";
 
1959
           }
 
1960
           else if(ltitle.substr(0, 8) == "mix cd -")
 
1961
           {
 
1962
               title = title.substr(8);
 
1963
               album += " mix cd";
 
1964
           }
 
1965
           else if(ltitle.substr(0, 6) == "track-")
 
1966
               title = title.substr(6) + " " + title.substr(0, 6);
 
1967
           else if(ltitle.substr(0, 5) == "track")
 
1968
               title = title.substr(5) + " " + title.substr(0, 5);
 
1969
           title.cropSpace();
 
1970
           title.collapseSpace();
 
1971
           
 
1972
           // scan track
 
1973
           size_t pos = 0;
 
1974
           while(isdigit(title.c_str()[pos])) pos++;
 
1975
           if((pos >= 1) && (pos <=2))
 
1976
           {
 
1977
               int t = 0;
 
1978
               title.substr(0, pos).toInt(t, 10);
 
1979
               track = t;              
 
1980
           }
 
1981
           else if(pos > 2)
 
1982
               pos = 0;
 
1983
           // skip separator
 
1984
           while(strchr(" -.", title.c_str()[pos])) pos++;
 
1985
           title = title.substr(pos);
 
1986
           // split artist - title
 
1987
           splitArtistTitle(title, artist, title);
 
1988
           title.cropSpace();
 
1989
 
 
1990
           // album
 
1991
           tstring ar;
 
1992
           // split artist - album
 
1993
           if((album == "alben") ||
 
1994
              (album == "cdrom2") ||
 
1995
              (album == "cdrom") ||
 
1996
              (album == "misc1") ||
 
1997
              (album == "mp3") ||
 
1998
              (album == "ov") ||
 
1999
              (album == "all"))
 
2000
               album = "";
 
2001
           splitArtistTitle(album, ar, album);
 
2002
           if((!artist.empty()) && (!ar.empty()))
 
2003
           {
 
2004
               if(artist != ar)
 
2005
               {
 
2006
                   title = artist + "-" + title;
 
2007
                   artist = ar;
 
2008
               }
 
2009
           }
 
2010
           else if(artist.empty())
 
2011
           {
 
2012
               artist = ar;
 
2013
               ar = "";
 
2014
           }
 
2015
           if(artist.empty())
 
2016
           {
 
2017
               artist = album;
 
2018
               album = "";
 
2019
           }
 
2020
           album.cropSpace();
 
2021
           artist.cropSpace();
 
2022
           if(artist == "diverse")
 
2023
               artist = "";
 
2024
           
 
2025
           // comment
 
2026
           comment.cropSpace();
 
2027
           if((comment == "alben") ||
 
2028
              (comment == "cdrom2") ||
 
2029
              (comment == "cdrom") ||
 
2030
              (comment == "mp3") ||
 
2031
              (comment == "ov") ||
 
2032
              (comment == "chr music") ||
 
2033
              (comment == "mp3 dl") ||
 
2034
              (comment == "diverse") ||
 
2035
              (comment.substr(0, 2) == "M0") ||
 
2036
              (comment.substr(0, 7) == "Unknown") ||
 
2037
              (comment.substr(0, 7) == "Various") ||
 
2038
              (comment == "all"))
 
2039
               comment = "";
 
2040
 
 
2041
           // capitalize strings
 
2042
           capitalize(title);
 
2043
           capitalize(artist);
 
2044
           capitalize(album);
 
2045
           capitalize(comment);
 
2046
           
 
2047
           // move rest of long strings into comment
 
2048
           if(title.length() > 30)
 
2049
               title.searchReplace(" - ", "-");
 
2050
           if(artist.length() > 30)
 
2051
               artist.searchReplace(" - ", "-");
 
2052
           if(album.length() > 30)
 
2053
               album.searchReplace(" - ", "-");
 
2054
           if(title.length() > 30)
 
2055
           {
 
2056
               comment = "T:" + title.substr(30);
 
2057
               title.truncate(30);
 
2058
           }
 
2059
           if(artist.length() > 30)
 
2060
           {
 
2061
               comment = "R:" + artist.substr(30);
 
2062
               artist.truncate(30);
 
2063
           }
 
2064
           if(album.length() > 30)
 
2065
           {
 
2066
               comment = "L:" + album.substr(30);
 
2067
               album.truncate(30);
 
2068
           }
 
2069
 
 
2070
           // print and check tag
 
2071
//         fmes(name, "appending id3 tag v1.1:\ntitle=  '%s%s%s'\nartist= '%s%s%s'\nalbum=  '%s%s%s'\ncomment='%s%s%s'\ntrack=  %s%d%s\n", 
 
2072
//              cval, title.c_str(), cnor, cval, artist.c_str(), cnor, cval, album.c_str(), cnor, cval, comment.c_str(), cnor, cval, track, cnor);
 
2073
           fmes(name, "'%s%-30.30s%s' '%s%-30.30s%s' '%s%-30.30s%s' '%s%-28.28s%s' %s%d%s\n",
 
2074
                cval, title.c_str(), cnor, cval, artist.c_str(), cnor, cval, album.c_str(), cnor, cval, comment.c_str(), cnor, cval, track, cnor);
 
2075
           if(title.length() > 30)
 
2076
               printf("%swarning%s: title > 30 chars\n", cerror, cnor);
 
2077
           if(artist.length() > 30)
 
2078
               printf("%swarning%s: artist > 30 chars\n", cerror, cnor);
 
2079
           if(album.length() > 30)
 
2080
               printf("%swarning%s: album > 30 chars\n", cerror, cnor);
 
2081
           if(comment.length() > 28)
 
2082
               printf("%swarning%s: comment > 28\n", cerror, cnor);
 
2083
           if(track == 0)
 
2084
               printf("%swarning%s: no track\n", cerror, cnor);
 
2085
           
 
2086
           // write tag
 
2087
           char tag[128];
 
2088
           memset(tag, 0, 128);
 
2089
           tag[0] = 'T';
 
2090
           tag[1] = 'A';
 
2091
           tag[2] = 'G';
 
2092
           strncpy(tag + 3, title.c_str(), 30);
 
2093
           strncpy(tag + 3 + 30, artist.c_str(), 30);
 
2094
           strncpy(tag + 3 + 60, album.c_str(), 30);
 
2095
           strncpy(tag + 3 + 90 + 4, comment.c_str(), 28);
 
2096
           tag[126] = track;
 
2097
           tag[127] = -1;
 
2098
           num_tagsadded++;
 
2099
#if 1
 
2100
           if(!dummy)
 
2101
           {
 
2102
               // append to file
 
2103
               FILE *f = fopen(name, "ab");
 
2104
               if(f == 0)
 
2105
                   userError("can't open file '%s' for writing!\n", name);
 
2106
               if(fwrite(tag, 1, 128, f) != 128)
 
2107
                   userError("error while writing to file '%s'\n", name);
 
2108
               fclose(f);
 
2109
           }
 
2110
#endif
 
2111
       }
 
2112
       
 
2113
      ++checked;
 
2114
   } // for all params
 
2115
 
 
2116
   // print final statistics
 
2117
   if((filelist.size()>1)&&(!ac("raw-list")) && (!ac("no-summary")) && (!quiet)) {
 
2118
      printf("--                                                                             \n"
 
2119
             "%s%d%s file%s %s, %s%d%s erroneous file%s found\n", 
 
2120
             cval, checked, cnor, checked==1?"":"s", 
 
2121
             ac("list")?"listed":"checked", cval, err, cnor, err==1?"":"s");
 
2122
      if(num_ano) printf("(%s%d%s %sanomal%s%s found)\n", 
 
2123
                         cval, num_ano, cnor, cano, num_ano==1?"y":"ies", cnor);
 
2124
       if(num_tagsadded)
 
2125
           printf("(%s%d%s tags added)\n", 
 
2126
                  cval, num_tagsadded, cnor);
 
2127
   }
 
2128
   
 
2129
   // end
 
2130
   if(log!=NULL) fclose(log);
 
2131
   
 
2132
   return (err || num_ano)?1:0;
 
2133
}
 
2134