summaryrefslogtreecommitdiff
path: root/micro.cc
diff options
context:
space:
mode:
Diffstat (limited to 'micro.cc')
-rwxr-xr-xmicro.cc1312
1 files changed, 1312 insertions, 0 deletions
diff --git a/micro.cc b/micro.cc
new file mode 100755
index 0000000..b39c28a
--- /dev/null
+++ b/micro.cc
@@ -0,0 +1,1312 @@
+/* TODO: buffer <- replace strcpy with strncpy etc.
+ *
+ *
+ */
+
+#include <iostream>
+#include <cstring>
+#include <cstdlib>
+#include <fstream>
+#include <math.h>
+#ifdef _WIN32
+ #include <direct.h>
+ #define COMPARESTRING strnicmp
+#else
+ #include <unistd.h>
+ #define COMPARESTRING strncasecmp
+#endif
+
+#define VERSION "2.76" // program version
+#define BUFFER_SIZE 4096 // input line buffer size
+
+class line; // prototype of line class
+
+char const *params[]={ // parameters table
+ "if", // 0
+ "t", // 1
+ "e", // 2
+ "o", // 3
+ "n", // 4
+ "of", // 5
+ "v", // 6
+ "i", // 7
+ "s", // 8
+ "j", // 9
+ "c", // 10
+ "tol", // 11
+ "m", // 12
+ "m0", // 13
+ "tt", // 14
+ "m1", // 15
+ "l", // 16
+ "f", // 17
+#ifdef _WIN32
+ "g", // 18
+#endif
+ "o1", // 19
+ "o2", // 20
+ "i1", // 21
+ "i2" // 22
+};
+
+// Subtitle stat types
+enum what {chars, lines, subs, length, subchars};
+// Work mode of program
+enum state {normal, info, generate};
+// Subtitle formats
+enum format {unknown, micro, sam};
+
+char buffer[BUFFER_SIZE];
+line* root; // beginning of the text (pointer on the first line)
+double iframerate=25.0; // default framerate for input subtitle
+double oframerate=iframerate; // default framerate for output subtitle
+double a=0.0, b=3.0, c=0.05; // default time conversion ratios
+long int tolerance1; // time tolerance for moving subtitles
+long int tolerance2; // time tolerance for squeezing subtitles
+double offset=0; // default offset - don't move
+unsigned int nlines=0; // number of read lines
+long int splittime; // split time in fames
+long int jointime; // join time in frames for 2nd subtitle file
+long int cut1time, cut2time; // cut times in frames
+int compactlines=2, compactchars=60; // number of lines and characters during compacting
+int compactoffset=2; // 'window size' during compacting subtitles
+int llength, clength; // length and number of lines which are displayed in report
+state pmode=normal; // mode
+format iformat; // input format (autodetect)
+format oformat=unknown; // output format
+#ifdef _WIN32
+char* language; // language name during generation of .mvd file
+#endif
+
+class flags { // work modes
+public:
+unsigned int endtime : 1;
+unsigned int calculate : 1;
+unsigned int oframerate : 1;
+unsigned int verbose : 1;
+unsigned int split : 1;
+unsigned int join : 1;
+unsigned int cut : 1;
+unsigned int tolerance : 1;
+unsigned int compact0 : 1;
+unsigned int compact1 : 1;
+unsigned int compact2 : 1;
+unsigned int length : 1;
+unsigned int forcecompact : 1;
+unsigned int autodetect : 1;
+flags() {endtime=0; calculate=0; oframerate=0; verbose=1; split=1; join=1; cut=1; tolerance=1; compact0=1; compact1=1; compact2=1; length=1; forcecompact=1; autodetect=0; };
+};
+flags* disable=new flags();
+
+char* input; // input filename
+char* output; // output filename
+char* join; // joining subtitle filename
+
+// error message function
+void error(const int num, const void* message1,const void* message2)
+{
+std::cerr << "Error: ";
+switch(num){
+ case 1:
+ std::cerr <<"Cannot open file '"<<(char*)message1<<"'.\n";
+ break;
+ case 2:
+ std::cerr <<"Cannot write to file '"<<(char*)message1<<"'\n.";
+ break;
+ case 4:
+ std::cerr <<"Wrong switch '"<<(char*)message1<<"'.\n";
+ break;
+ case 9:
+ std::cerr <<"Wrong number of arguments.\n";
+ break;
+ case 16:
+ std::cerr <<"Switches -j and -i/-g are exclusive.\n";
+ break;
+ case 17:
+ std::cerr <<"<time2> should be greater than <time1>.\n";
+ break;
+ case 21:
+ std::cerr <<"Minimal available offset is "<<*(double *)message1<<" .\n";
+ break;
+ case 50:
+ std::cerr <<"Missing "<<(char *)message1<<" argument.\n";
+ break;
+ case 51:
+ std::cerr <<"Bad "<<(char *)message1<<" argument.\n";
+ break;
+ case 52:
+ std::cerr <<"Bad "<<(char*)message1<<"argument of "<<(char *)message2<<" switch.\n";
+ break;
+ case 53:
+ std::cerr <<(char *)message1<<" parameter should be "<<(char *)message2<<".\n";
+ break;
+ default:
+ std::cerr <<"General error.\n";
+ break;
+}
+exit (num);
+}
+
+// warning message function
+void warning(const int num, const void* message1, const void* message2, const void* message3)
+{
+std::cerr << "Warning: ";
+switch(num){
+ case 1:
+ std::cerr <<"Skipping line "<<*(unsigned int *)message1<<" of '"<<(char *)message2<<"' ("<<(char *)message3<<").\n";
+ break;
+ case 2:
+ std::cerr <<"Line "<<*(unsigned int *)message1<<" of '"<<(char *)message2<<"' contains more than 5 lines.\n";
+ break;
+ case 3:
+ std::cerr <<"Enabling "<<(char *)message1<<" ("<<(char *)message2<<" set).\n";
+ break;
+ default:
+ std::cerr <<"Something strange.\n";
+ break;
+}
+}
+
+// short help function
+void help(char* _filename)
+{
+std::cout << "microAdjust "<<VERSION<<" (C)2000-2001,2003 Gringo\n";
+std::cout << "----------------\n";
+std::cout << "Program allows changing duration time, offset, framerate,\n";
+std::cout << "cut, join, split and compact MicroDVD or SAM format subtitles.\n";
+std::cout << "Usage:\n";
+std::cout << _filename <<" [<options>] <input file> <output file>\n";
+std::cout << _filename << " -j <time> [<options>] <input file1> <input file2> <output file>\n";
+#ifdef _WIN32
+std::cout << _filename << " -g <language> <subtitle file> <video file> <output file>\n";
+#endif
+std::cout << _filename << " -i <input file>\n\n";
+std::cout << "Options:\n";
+std::cout << "-if <framerate>\t\tSet input framerate of subtitles [Default:25]\n";
+std::cout << "-of <framerate>\t\tSet output framerate of subtitles\n";
+std::cout << "\t\t\t[Default:same as input framerate]\n";
+std::cout << "-i<X>\t\t\tForce input format of subtitle.\n";
+std::cout << "\t\t\t<X>= 1: MicroDVD, 2: SAM [Default: Autodetect]\n";
+std::cout << "-o<X>\t\t\tSet output format of subtitle.\n";
+std::cout << "\t\t\t<X>= 1: MicroDVD, 2: SAM [Default:Same as input]\n";
+std::cout << "-o <offset>\t\tSet offset for subtitles in seconds [Default:0].\n";
+std::cout << "-t <a> <b> <c>\t\tSet time ratios which calculate duration of each\n";
+std::cout << "\t\t\tsubtitle with formula: time[s]=a*sqrt(n) + b + c*n\n";
+std::cout << "\t\t\t[Default:a=0 b=3.0 c=0.05]\n";
+std::cout << "\t\t\tn - length of each subtitle in chars.\n";
+std::cout << "-n\t\t\tDon't make time calculations.\n";
+std::cout << "\t\t\tIf set parameter -t is ignored.\n";
+std::cout << "-tt\t\t\tEnable time tolerance during time calculations\n";
+std::cout << "\t\t\t[Default:disabled].\n";
+std::cout << "-c <time1> <time2>\tCut subtitles from <time1> to <time2> (time2>time1).\n";
+std::cout << "\t\t\t<time> - <minutes>:<seconds>.\n";
+std::cout << "-s <time>\t\tSplit subtitle after specified period of time.\n";
+std::cout << "-j <time>\t\tJoin two subtitle files after specified period of time.\n";
+std::cout << "-m\t\t\tSame as: -m0 -m1 2 60 2 (optimal settings)\n";
+std::cout << "-m0\t\t\tCompact subtitles (remove unnecessary spaces).\n";
+std::cout << "\t\t\tOption implies time calculations.\n";
+std::cout << "-m1 <a> <b> <c>\t\tCompact subtitles into <a=lines>*<b=chars>\n";
+std::cout << "\t\t\ttextbox if needed (subtitle appears to short).\n";
+std::cout << "\t\t\t<c> set amount of subtitles processed at once (>0).\n";
+std::cout << "\t\t\tOption implies time calculations.\n";
+std::cout << "-f\t\t\tForce compacting to <a>*<b> textbox if possible.\n";
+std::cout << "-tol <a> <b>\t\tTolerance in seconds during: <a> - time calculations\n";
+std::cout << "\t\t\t<b> - subtitle compacting (-m1) [Default: a=0.5 b=2.0]\n";
+std::cout << "-e\t\t\tPut empty endtime in each subtitle.\n";
+std::cout << "\t\t\tIf set parameter -t is ignored.\n";
+#ifdef _WIN32
+std::cout << "-g <language>\t\tGenerate startup file for MicroDVD.\n";
+#endif
+std::cout << "-v\t\t\tVerbose mode (Display additional information).\n";
+std::cout << "-i\t\t\tDisplay info about subtitle file.\n";
+std::cout << "-l <chars> <lines>\tDisplay subtitles with more/less than <chars>*<lines>.\n";
+}
+
+// return class of character (type)
+int whatsign(char _char)
+{
+switch(_char){
+ case ' ':
+ case '\t':
+ return 1;
+ case ',':
+ case ';':
+ case ':':
+ case '!':
+ case '.':
+ case '?':
+ return 2;
+ case '|':
+ return 3;
+ case '\n':
+ case '\r':
+ return 4;
+ default:
+ return 0;
+}
+}
+
+// convert time from MM:SS.SS to number of seconds
+// MM - minutes; SS.SS - seconds
+double gettime(const char* _string, const char* _param, const char* _switch)
+{
+char* tmp1;
+char* tmp2;
+double time;
+
+time=strtod(_string,&tmp1);
+if(! tmp1) error(52,_param,_switch);
+while(*tmp1)
+ { switch(*tmp1) {
+ case ':':
+ tmp1++;
+ time=60*time+strtod(tmp1,&tmp2);
+ if(! tmp2) error(52,_param,_switch);
+ break;
+ default:
+ error(52,_param,_switch);
+ break;
+ }
+ tmp1=tmp2;
+ }
+#ifdef DEBUG
+std::cout << "*** Debug: Read time (" << _string << ") = " << time << " sec\n";
+#endif
+return time;
+}
+
+// Remove multiple dots from the beginning and the end of the line
+char* nodots(char* _text, int index, int num)
+{
+char* start=_text;
+char* end;
+int i=0;
+
+while(*start++=='.');
+strcpy(buffer,((index)?start-1:_text));
+
+end=buffer+strlen(buffer);
+
+if (index!=(num-1)){
+ while(*--end=='.') i++;
+ if(i>1) *(end+1)='\0';
+ }
+
+return buffer;
+}
+
+
+// autodetect input file type (MicroDVD/SAM)
+format detect(char* _bufor)
+{
+char* tmp=_bufor;
+
+for(;*tmp; tmp++){
+ switch(*tmp){
+ case '{':
+ case '}':
+ return micro; // MicroDVD format
+ case ':':
+ return sam; // SAM format
+ }
+ }
+return unknown; // unknown format
+}
+
+// return max value
+long int max(long int a, long int b)
+{ return (a>b)? a: b; }
+
+// return min value
+long int min(long int a, long int b)
+{ return (a<b)? a: b; }
+
+// return sign of the value
+int sign(int num)
+{ return (num)? ((num>0)? 1: -1):0; }
+
+// converts number of frames into human readable time
+// eg. 1h 3m 10s
+void frames2time(std::ostream& s, long int _frames)
+{
+int hours, minutes, seconds;
+long int tmp=(long int)(_frames/oframerate);
+seconds=tmp%60;
+tmp/=60;
+minutes=tmp%60;
+tmp/=60;
+hours=tmp%60;
+s << ((hours>9)? "":"0") << hours << ":";
+s << ((minutes>9)? "":"0") << minutes << ":";
+s << ((seconds>9)? "":"0") << seconds;
+}
+
+//----------------------------------
+//--- The main class of subtitle ---
+//----------------------------------
+
+class line{ // object stores one line of text
+
+private:
+long int start; // begin
+long int end; // end
+unsigned int timetag : 1; // not enough time tag
+char* text; // text string
+line* previous; // pointer to the previous object
+line* next; // pointer to the next object
+
+line* manager(int,int, int);
+int check(const int);
+int compress(int,int);
+void calc_line(int);
+line* go(int);
+void add(char*);
+void print(std::ofstream&,long int);
+
+public:
+line(const unsigned long int, const unsigned long int, const char*, line* = 0, line* = 0);
+~line();
+void update(const unsigned long int, const unsigned long int, const char*);
+void set_offset(double);
+void calc_time();
+void calc_framerate(double,double);
+void clean();
+void compact2(int);
+int scan(what);
+void load(char*, long int);
+void save(char*, long int);
+void display();
+//friend std::ostream& operator<<(std::ostream&, line*);
+friend long int max(long int, long int);
+friend long int min(long int, long int);
+};
+
+line::line(const unsigned long int _start, const unsigned long int _end, const char* _text, line* _previous, line* _next)
+{
+start=_start;
+end=_end;
+text=new char[strlen(_text)+1];
+strcpy(text,_text);
+timetag=0;
+previous=(_previous)?_previous:0;
+next=(_next)?_next:0;
+}
+
+line::~line(void)
+{
+if (text) delete text;
+if (previous) previous->next=next;
+if (next) next->previous=previous;
+}
+
+/*std::ostream& operator<<(std::ostream& s, line* l)
+{
+line* tmp=l;
+while (tmp) {
+ if (disable->endtime)
+ s << "{" << tmp->start << "}{}" << tmp->text<<"\n";
+ else s << "{" << tmp->start << "}{" << tmp->end << "}" << tmp->text<<"\n";
+tmp=tmp->next;
+};
+return s;
+}*/
+
+// Update object values
+void line::update(const unsigned long int _start, const unsigned long int _end, const char* _text)
+{
+start=_start;
+end=_end;
+delete text;
+text=new char[strlen(_text)+1];
+strcpy(text,_text);
+}
+
+// Move forward and backward between objects(lines)
+line* line::go(int num)
+{
+line* tmp=this;
+int i;
+
+switch(sign(num)){
+ case 1:
+ for(i=0; i<num; i++)
+ if (!(tmp=tmp->next)) return 0;
+ break;
+ case -1:
+ for(i=num; i; i++)
+ if (!(tmp=tmp->previous)) return 0;
+ break;
+}
+return tmp;
+}
+
+// Concatenate string to existing text
+void line::add(char* add)
+{
+char* new_string=new char[strlen(text)+strlen(add)+2];
+strcpy(new_string,text);
+strcat(new_string,"|");
+strcat(new_string,add);
+delete text;
+text=new_string;
+}
+
+// Load input file
+void line::load(char* _filename, long int _jointime)
+{
+static line* current=0; // pointer to last record (static need when joining subtitles)
+line* next; // next line
+long int start;
+long int end;
+unsigned int current_line=0;
+unsigned int hours;
+unsigned int minutes;
+unsigned int seconds;
+char* tmp1;
+char* tmp2;
+
+#ifdef _WIN32
+std::ifstream inputf(_filename, ios::nocreate);
+#else
+std::ifstream inputf(_filename);
+#endif
+if (!inputf) error(1,_filename,NULL);
+
+while (inputf.getline(buffer,BUFFER_SIZE))
+{
+if (!disable->autodetect) iformat=detect(buffer);
+current_line++;
+switch (iformat){
+ case micro: // MicroDVD format
+ tmp1=buffer;
+ while(*(tmp1++)!='{'&&(*tmp1));
+ start=strtoul(tmp1,&tmp2,10)+_jointime;
+ if (tmp1==tmp2) {warning(1,&current_line,_filename,"Missing startframe"); continue;}
+ tmp1=tmp2;
+ while(*(tmp1++)!='{'&&(*tmp1));
+ if (!*tmp1) {warning(1,&current_line,_filename,"No endframe"); continue;}
+ end=strtoul(tmp1,&tmp2,10)+_jointime;
+ if (!*tmp2||!*(tmp2+1)) {warning(1,&current_line,_filename,"No text"); continue;}
+ nlines++;
+ break;
+ case sam: // SAM format
+ tmp1=buffer;
+ hours=strtoul(tmp1,&tmp2,10);
+ if (tmp1==tmp2) {warning(1,&current_line,_filename,"Missing hours"); continue;}
+ tmp1=tmp2+1;
+ minutes=strtoul(tmp1,&tmp2,10);
+ if (tmp1==tmp2) {warning(1,&current_line,_filename,"Missing minutes"); continue;}
+ tmp1=tmp2+1;
+ seconds=strtoul(tmp1,&tmp2,10);
+ if (tmp1==tmp2) {warning(1,&current_line,_filename,"Missing seconds"); continue;}
+ start=(long int)((hours*3600+minutes*60+seconds)*iframerate)+_jointime;
+ if (current) {
+ if (start==current->start) {current->add(tmp2+1); continue;}
+ if (start<current->start) {warning(1,&current_line,_filename,"Time integrity"); continue;} }
+ end=0;
+ nlines++;
+ break;
+ default:
+ warning(1,&current_line,_filename,"Unknown format");
+ continue;
+ }
+if ((!_jointime)&&(!disable->join)&&(start>=jointime)) {warning(1,&current_line,_filename,"Time overlap"); continue;}
+next=new line(start,end,tmp2+1);
+if (current) { current->next=next; next->previous=current; }
+ else { root=next; next->previous=0; }
+current=next;
+}
+inputf.close();
+}
+
+// Print single line to output file in specified format
+void line::print(std::ofstream& file, long int _offset)
+{
+if (disable->cut || (((this->start>cut1time)&&(this->end<cut2time))||
+ ((cut1time>this->start)&&(cut1time<this->end))||
+ ((cut2time>this->start)&&(cut2time<this->end))) )
+if (disable->endtime)
+ switch(oformat){
+ case micro:
+ file <<"{"<<(((disable->cut)?this->start:max(this->start,cut1time))-_offset)<<"}{}"<<this->text<<"\n";
+ break;
+ case sam:
+ frames2time(file,((disable->cut)?this->start:max(this->start,cut1time))-_offset);
+ file <<":"<<this->text<<"\n";
+ break;
+ }
+else switch(oformat){
+ case micro:
+ file<<"{"<<(((disable->cut)?this->start:max(this->start,cut1time))-_offset)<<"}{"<<(((disable->cut)?this->end:min(this->end,cut2time))-_offset)<<"}"<<this->text<<"\n";
+ break;
+ case sam:
+ frames2time(file,((disable->cut)?this->start:max(this->start,cut1time))-_offset);
+ file <<":"<<this->text<<"\n";
+ break;
+ }
+}
+
+// Save file
+void line::save(char* _filename, long int _splittime)
+{
+char* name=new char[strlen(_filename)+3];
+line* tmp=this;
+unsigned int linenum=0; // line number
+
+if (disable->split) {
+ std::ofstream outputf(_filename);
+ if (!outputf) error(2,_filename,NULL);
+
+ while (tmp) {
+ linenum++;
+ if (tmp->scan(subs)>5) warning(2,&linenum,_filename,"");
+ tmp->print(outputf,0);
+ tmp=tmp->next;
+ };
+ outputf.close();}
+else {
+ strcpy(name,_filename);
+ strcat(name,".1");
+ std::ofstream part1(name);
+ if (!part1) error(2,name,NULL);
+
+ while (tmp && (tmp->start<_splittime)) {
+ linenum++;
+ if (tmp->scan(subs)>5) warning(2,&linenum,name,"");
+ tmp->print(part1,0);
+ tmp=tmp->next;
+ };
+ part1.close();
+
+ strcpy(name,_filename);
+ strcat(name,".2");
+ std::ofstream part2(name);
+ if (!part2) error(2,name,NULL);
+
+ linenum=0;
+ while (tmp) {
+ linenum++;
+ if (tmp->scan(subs)>5) warning(2,&linenum,name,"");
+ tmp->print(part2,_splittime);
+ tmp=tmp->next;
+ };
+ }
+delete name;
+}
+
+// Perform time translations on one line only
+// or set timetag depending on situation
+void line::calc_line(int calc)
+{
+long int value;
+long int vmin;
+int length;
+
+ length=strlen(this->text);
+ value=this->start+(unsigned long int)((oframerate*(b+(a*sqrt(length))+(c*length))));
+
+ if (this->next) // if next exists
+ if ((this->next->start)>value)
+ {this->timetag=0; if (calc) this->end=value;}
+ else {this->timetag=1;
+ if(calc){
+ this->end=this->next->start-1;
+ if(!disable->compact1) {// change start time if -tt specified
+ this->timetag=0;
+ vmin=min(value-end,min(tolerance1,this->start-this->previous->end));
+ if (vmin<(value-this->end)) this->timetag=1;
+ this->start-=vmin-1;}}}
+ else {this->timetag=0; this->end=value;}
+}
+
+// Time transformation subroutine
+void line::calc_time(void)
+{
+line* tmp=this;
+
+while (tmp){
+tmp->calc_line(1);
+tmp=tmp->next;
+};
+}
+
+// Change framerate subroutine
+void line::calc_framerate(double _iframerate, double _oframerate)
+{
+line* tmp=this;
+
+while (tmp){
+ tmp->start=(long int)(tmp->start*_oframerate/_iframerate);
+ tmp->end=(long int)(tmp->end*_oframerate/_iframerate);
+ tmp=tmp->next;
+};
+}
+
+// Change offset subroutine
+void line::set_offset(double _offset)
+{
+line* tmp=this;
+double time;
+
+while (tmp){
+ if ((tmp->start+(long int)(oframerate*_offset))<0) {
+ time=-tmp->start/oframerate;
+ error(21,&time,NULL);
+ }
+ tmp->start+=(long int)(oframerate*_offset);
+ tmp->end+=(long int)(oframerate*_offset);
+ tmp=tmp->next;
+};
+}
+
+// Move new-line('|') character to optimal place in the sentence
+// ( after ,.;:!? characters) if possible
+void post(char* _start)
+{
+char* _end=_start+strlen(_start);
+char* tmp=_end;
+char* tmp2;
+
+while(tmp>_start){
+
+while((*tmp!='|'||(*tmp=='|'&&(*(tmp+1)=='-')))&&(tmp>_start)) tmp--;
+ tmp2=tmp;
+
+ while((tmp>_start)&&((_end-tmp)<=compactchars))
+ {
+ if ((*tmp==' ')&&(whatsign(*(tmp-1)) == 2))
+ {
+ *tmp2=' ';
+ *tmp='|';
+ tmp2=tmp;
+ break;
+ }
+ tmp--;
+ }
+_end=tmp2;
+tmp=tmp2-1;
+}
+return;
+}
+
+// Remove unnecessary white characters
+void line::clean(void)
+{
+line* tmp=this;
+char* c;
+char* ostring;
+char* nstring; // pointer to the new string
+int prv; // previous character indicator:
+ // 1 - regular; 0 - new line; -1 - white
+
+while (tmp){
+ prv=0;
+ ostring=tmp->text;
+ nstring=new char[strlen(tmp->text)+1];
+ c=nstring;
+ while (*ostring) {
+ switch(whatsign(*ostring)){
+ case 0: // regular
+ *c++=*ostring;
+ prv=1;
+ break;
+ case 1: // white
+ if (prv>0) {*c++=' '; prv=-1;}
+ break;
+ case 2: // special
+ if (prv<0)
+ if (c!=nstring) *(c-1)=*ostring;
+ else *c++=*ostring;
+ else *c++=*ostring;
+ prv=1;
+ break;
+ case 3: // new line '|'
+ if (prv>0) *c++=*ostring;
+ else if (c!=nstring) *(c-1)=*ostring;
+ prv=0;
+ break;
+ case 4: // new-line characters \n \r
+ if (prv>0) {*c++=' '; prv=-1;}
+ break;
+ }
+ ostring++;
+ }
+(prv<0)? *(c-1)='\0':*c='\0';
+delete tmp->text;
+tmp->text=nstring;
+tmp=tmp->next;
+};
+}
+
+// Cut off line with accuracy +- 1 word
+char* cutline(char* _start,const int _limit)
+{
+char* tmp=_start;
+char* prv=0;
+
+while(*tmp&&(((tmp-_start)<=_limit)||!prv)){
+ if((*tmp==' ')||(*tmp=='|'))
+ if ((*tmp=='|')&&(*(tmp+1)=='-')) {prv=tmp; break;} else
+ {prv=tmp; *tmp=' ';}
+ tmp++;}
+if(!*tmp&&(tmp-_start<=_limit)) prv=tmp;
+return prv;
+}
+
+// Compress line. Return pointer to the next line
+int line::compress(int num, int mode)
+{
+line* tmp;
+line* tmp2;
+char** table; // text table
+char* bufor; // bufor with lines being compressed
+long int** stable; // pointer table to time table
+long int* size; // time table
+char* start;
+char* end;
+int count;
+int i,j;
+int length;
+
+ for (i=0,count=0,length=0,tmp=this; ((i<num)&&tmp); i++)
+ {count+=tmp->scan(subs);
+ length+=strlen(nodots(tmp->text,i,num))+1;
+ tmp=tmp->next;}
+ table = new char*[count+2];
+ stable = new long int*[count+2];
+ bufor = new char[length];
+ size = new long int[length];
+// copy next lines
+ for (i=0,start=bufor,tmp=this; ((i<num)&&tmp); i++) {
+ #ifdef DEBUG
+// std::cout << "***Debug: In(" << strlen(tmp->text) << "): '" << tmp->text << "'\n";
+ #endif
+ strcpy(start,nodots(tmp->text,i,num));
+ #ifdef DEBUG
+// std::cout << "***Debug: Line(" << strlen(buffer) << "): '" << buffer << "'\n";
+ #endif
+ for (j=0; j<(strlen(buffer)+1); j++)
+ size[start-bufor+j]=tmp->start+j*(tmp->end-tmp->start)/(strlen(buffer));
+ start+=strlen(buffer)+1;
+ *(start-1)='|';
+ tmp=tmp->next;
+ }
+ *(start-1)='\0';
+ start=bufor;
+ end=bufor;
+ i=0; j=0;
+
+#ifdef DEBUG
+//std::cout << "***Debug: Merged(" << strlen(bufor) << "): '" << bufor << "'\n";
+#endif
+
+ if(count>compactlines)
+ { // divide lines by <compactchars> characters
+ while((end=cutline(end,compactchars))&&(j<count+2))
+ {
+ if ((++i<compactlines)&&(*end)) *end='|';
+ else {
+ *end='\0';
+ i=0;
+ table[j]=start;
+ stable[j++]=size+(start-bufor);
+ start=end+1;
+ }
+ if (end==(bufor+length-1)) break;
+ end++;
+ }
+ }
+ else { // when merged lines can fit into one subtitle
+ table[j]=bufor;
+ stable[j++]=size;
+ }
+// when compressed text is longer than original one
+ if (j<count+2)
+ {
+ stable[j]=size+length-1;
+// Move '|' to the end of the sentence is possible
+ for(i=0; i<j; i++) post(table[i]);
+
+// when compression was successful or mode=1
+ if ((j<num)||((j==num)&&mode)) {
+
+#ifdef DEBUG
+std::cout << "***Debug: -----j:"<<j<<" num:"<<num<<" mode:"<<mode<<"-----\n";
+for (i=0, tmp=this; ((i<num)&&tmp); i++, tmp=tmp->next)
+std::cout << "***Debug: <<<{" << tmp->start<<"}{"<<tmp->end<<"}"<<tmp->text <<"\n";
+for (i=0; i<j; i++)
+std::cout << "***Debug: >>>{" <<*stable[i]<<"}{"<<*stable[i+1]<<"}"<<table[i]<<"\n";
+#endif
+
+// Rewrite compresses subtitle into table and free resources
+ for (i=num-1,tmp=this->go(num-1); i>=0; i--)
+ { if (i>=j) // delete
+ { tmp2=tmp;
+ delete tmp2; }
+ else // rewrite
+ { tmp->update(*stable[i],*stable[i+1],table[i]);
+ tmp->calc_line(0); }
+ tmp=tmp->previous; }
+}
+}
+// Free memory
+ delete bufor;
+ delete size;
+ delete table;
+ delete stable;
+return (j<num)? 1: 0;
+}
+
+// Check if line can/needs to be compressed
+int line::check(const int num)
+{
+line* tmp=this;
+int i=0;
+
+while(i++<num){
+ if(!tmp) return 0;
+ if(tmp->next)
+ if((tmp->next->start-tmp->end)>tolerance2) return 0;
+ tmp=tmp->next; }
+return 1;
+}
+
+// Compression manager which sets what should be compressed,
+// in which order and in what offset
+// first - size of first window
+// last - size of the last window
+// mode - 0: normal, 1: accept only if no lossless compression
+line* line::manager(int first, int last, int mode)
+{
+line* tmp;
+int i,j;
+
+ for(i=first; i<=last; i++) // offsets
+ for(j=i-1; j>=0; j--) // offset windows
+ { tmp=this->go(-j);
+ if(!tmp) break;
+ else if (tmp->check(i)||mode) // do not check if mode=1
+ if(tmp->compress(i,mode)) return tmp; }
+return this->next;
+}
+
+void line::compact2(int num)
+{
+line* tmp=this;
+
+while(tmp){
+ if ((!disable->forcecompact)&&((tmp->scan(subchars)>compactchars)||(tmp->scan(subs)>compactlines)))
+ tmp=tmp->manager(1,num,1);
+ else if (tmp->scan(subs)>compactlines) tmp->compress(1,1);
+ if(tmp->timetag) tmp=tmp->manager(2,num,0);
+ else tmp=tmp->next;
+ }
+}
+
+// Generate stats (number of lines, characters etc.)
+int line::scan(what _what)
+{
+line* tmp=this;
+char* start;
+char* end;
+int max_lines=0;
+int count_lines;
+int max_chars=0;
+int count_chars;
+int counter=1;
+unsigned short tag;
+
+while(tmp){
+ start=tmp->text;
+ count_lines=0;
+ tag=0;
+
+ do {
+ end=strchr(start,'|');
+ count_chars=(end)?end-start:strlen(start);
+ if (clength<0)
+ {if (count_chars<abs(clength)) tag=1;}
+ else {if (count_chars>clength) tag=1;};
+ start=end+1;
+ max_chars=max(count_chars,max_chars);
+ count_lines++;
+ } while (end);
+ if ((_what==length)&&tag)
+ if (llength<0)
+ {if (count_lines<abs(llength)) std::cout<<" "<<counter;}
+ else {if (count_lines>llength) std::cout<<" "<<counter;};
+ if (_what==subs) return count_lines;
+ if (_what==subchars) return max_chars;
+ max_lines=max(count_lines,max_lines);
+ tmp=tmp->next;
+ counter++;
+};
+if (_what==lines) return max_lines;
+else return max_chars;
+}
+
+void line::display()
+{
+if (!disable->verbose||(pmode==info)) {
+ std::cout << "--------------------------------------------------------------------------------\n";
+ if (pmode!=info) {
+ std::cout << "Input framerate: "<<iframerate<<" [fr/s]\n";
+ std::cout << "Output framerate: "<<oframerate<<" [fr/s]\n";
+ std::cout << "Time: "<<a<<" "<<b<<" "<<c<<"\n";
+ std::cout << "Offset: "<<offset<<" [s]\n";}
+ std::cout << "Lines: "<< nlines <<"\n";
+ std::cout << "Max lines in subtitle: "<<root->scan(lines)<<"\n";
+ std::cout << "Max chars in line: "<<root->scan(chars)<<"\n";
+ if (!disable->length)
+ {std::cout << "Lines "<<((clength<0)? "<":">")<<abs(clength)<<" chars and "<<((llength<0)?"<":">")<<llength<<" subs:";
+ root->scan(length); std::cout << "\n";}
+ std::cout << "--------------------------------------------------------------------------------\n";}
+
+}
+#ifdef _WIN32
+// give intermediate path between base and source
+char* path(char* base, char* source)
+{
+char* start1=base;
+char* start2=source;
+char* out=buffer;
+int size=128;
+char* cwd=new char[size];
+
+while (!getcwd(cwd,size)) {delete cwd; cwd=new char[size+=64];}
+
+if ((*start1!='.')&&(*(start1+1)!=':'))
+ {
+ start1=new char[strlen(cwd)+strlen(base)+2];
+ strcpy(start1,cwd);
+ strcpy(start1+strlen(cwd),"\\");
+ strcpy(start1+strlen(cwd)+1,base);
+ }
+if ((*start2!='.')&&(*(start2+1)!=':'))
+ {
+ start2=new char[strlen(cwd)+strlen(source)+2];
+ strcpy(start2,cwd);
+ strcpy(start2+strlen(cwd),"\\");
+ strcpy(start2+strlen(cwd)+1,source);
+ }
+if (strrchr(start1,'\\')&&strrchr(start2,'\\'))
+ while(!COMPARESTRING(start1,start2,strchr(start1,'\\')+1-start1))
+ {start1=strchr(start1,'\\')+1;
+ start2=strchr(start2,'\\')+1;}
+while ((start1=strchr(start1,'\\')))
+ {strcpy(out,"..\\");
+ out+=3;
+ start1++;}
+if (strrchr(start2,'\\'))
+ {strncpy(out,start2,strrchr(start2,'\\')-start2);
+ *(out+(strrchr(start2,'\\')-start2))='\0';}
+else if (out==buffer) strcpy(out,".");
+return buffer;
+}
+#endif
+
+// check if paramaters is known (in parameters table)
+// and return its number
+int test_param(char* param)
+{
+int i;
+for(i=0; i<(sizeof(params)/sizeof(char*)); i++) if (!strcmp(param,params[i])) return i;
+return -1; // not found
+}
+
+// Parse input parameters
+int parse_input(int argc,char* argv[])
+{
+int i=0;
+int j=3;
+int k;
+char* tmp;
+
+while(++i<argc)
+ switch(argv[i][0]){
+ case '-':
+ switch(test_param(argv[i]+1)){
+ case 0:
+ if ((i+1)<argc){
+ iframerate=strtod(argv[i+1],&tmp);
+ if(!disable->oframerate) oframerate=iframerate;
+ if (tmp!=argv[i+1]+strlen(argv[i+1])) error(51,"-if",NULL);
+ }
+ else error(50,"-if",NULL);
+ i++;
+ break;
+ case 1:
+ if ((i+3)<argc){
+ a=strtod(argv[i+1],&tmp);
+ if (tmp!=argv[i+1]+strlen(argv[i+1])) error(52,"first ","-t");
+ b=strtod(argv[i+2],&tmp);
+ if (tmp!=argv[i+2]+strlen(argv[i+2])) error(52,"second ","-t");
+ c=strtod(argv[i+3],&tmp);
+ if (tmp!=argv[i+3]+strlen(argv[i+3])) error(52,"third ","-t");
+ }
+ else error(50,"-t",NULL);
+ i+=3;
+ break;
+ case 2:
+ disable->endtime=1;
+ break;
+ case 3:
+ if ((i+1)<argc){
+ offset=strtod(argv[i+1],&tmp);
+ if (tmp!=argv[i+1]+strlen(argv[i+1])) error(51,"-o",NULL);
+ } else error(50,"-o",NULL);
+ i++;
+ break;
+ case 4:
+ disable->calculate=1;
+ break;
+ case 5:
+ if ((i+1)<argc){
+ oframerate=strtod(argv[i+1],&tmp);
+ disable->oframerate=1;
+ if (tmp!=argv[i+1]+strlen(argv[i+1])) error(51,"-of",NULL);
+ }
+ else error(50,"-of",NULL);
+ i++;
+ break;
+ case 6:
+ disable->verbose=0;
+ break;
+ case 7:
+ pmode=info;
+ break;
+ case 8:
+ if ((i+1)<argc){
+ splittime=(long int)(oframerate*gettime(argv[i+1],"","-s"));
+ disable->split=0;
+ }
+ else error(50,"-s",NULL);
+ i++;
+ break;
+ case 9:
+ if ((i+1)<argc){
+ jointime=(long int)(iframerate*gettime(argv[i+1],"","-j"));
+ disable->join=0;
+ }
+ else error(50,"-j",NULL);
+ i++;
+ break;
+ case 10:
+ if ((i+2)<argc){
+ cut1time=(long int)(oframerate*gettime(argv[i+1],"first ","-c"));
+ cut2time=(long int)(oframerate*gettime(argv[i+2],"second ","-c"));
+ if (cut1time>=cut2time) error(17,NULL,NULL);
+ disable->cut=0;
+ }
+ else error(50,"-c",NULL);
+ i+=2;
+ break;
+ case 11:
+ if ((i+2)<argc){
+ tolerance1=(long int)(oframerate*strtod(argv[i+1],&tmp))+1;
+ if (tmp!=argv[i+1]+strlen(argv[i+1])) error(52,"first ","-tol");
+ tolerance2=(long int)(oframerate*strtod(argv[i+2],&tmp));
+ if (tmp!=argv[i+2]+strlen(argv[i+2])) error(52,"second ","-tol");
+ }
+ else error(50,"-tol",NULL);
+ disable->tolerance=0;
+ i+=2;
+ break;
+ case 12:
+ disable->compact0=0;
+ disable->compact2=0;
+ break;
+ case 13:
+ disable->compact0=0;
+ break;
+ case 14:
+ disable->compact1=0;
+ break;
+ case 15:
+ if ((i+3)<argc){
+ compactlines=strtol(argv[i+1],&tmp,10);
+ if (tmp!=argv[i+1]+strlen(argv[i+1])) error(52,"first ","-m1");
+ compactchars=strtol(argv[i+2],&tmp,10);
+ if (tmp!=argv[i+2]+strlen(argv[i+2])) error(52,"second ","-m1");
+ compactoffset=strtol(argv[i+3],&tmp,10);
+ if (tmp!=argv[i+3]+strlen(argv[i+3])) error(52,"third ","-m1");
+ }
+ else error(50,"-m1",NULL);
+ disable->compact2=0;
+ i+=3;
+ break;
+ case 16:
+ if((i+2)<argc){
+ clength=strtol(argv[i+1],&tmp,10);
+ if (tmp!=argv[i+1]+strlen(argv[i+1])) error(51,"-l",NULL);
+ llength=strtol(argv[i+2],&tmp,10);
+ if (tmp!=argv[i+2]+strlen(argv[i+2])) error(51,"-l",NULL);
+ }
+ else error(50,"-l",NULL);
+ disable->length=0;
+ i+=2;
+ break;
+ case 17:
+ disable->forcecompact=0;
+ break;
+#ifdef _WIN32
+ case 18:
+ if ((i+1)<argc){
+ language=new char[min(strlen(argv[i+1]),3)+strlen(argv[i+1])+2];
+ tmp=language;
+ for (k=0; k<min(strlen(argv[i+1]),3); k++) *(language+k)=toupper(*(argv[i+1]+k));
+ *(language+k)=' ';
+ strcpy(language+min(strlen(argv[i+1]),3)+1,argv[i+1]);
+ pmode=generate;
+ }
+ else error(50,"-g",NULL);
+ i++;
+ break;
+#endif
+ case 19:
+ oformat=micro;
+ break;
+ case 20:
+ oformat=sam;
+ break;
+ case 21:
+ disable->autodetect=1;
+ iformat=micro;
+ break;
+ case 22:
+ disable->autodetect=1;
+ iformat=sam;
+ break;
+ default:
+ error(4,argv[i],NULL);
+ break;
+ }
+ break;
+ default:
+ switch(j--){
+ case 3:
+ input=new char[strlen(argv[i])+1];
+ strcpy(input,argv[i]);
+ break;
+ case 2:
+ if (disable->join&&pmode!=generate) {
+ output=new char[strlen(argv[i])+1];
+ strcpy(output,argv[i]);}
+ else {
+ join=new char[strlen(argv[i])+1];
+ strcpy(join,argv[i]);}
+ break;
+ case 1:
+ output=new char[strlen(argv[i])+1];
+ strcpy(output,argv[i]);
+ break;
+ default:
+ error(9,NULL,NULL);
+ break;
+ }
+ break;
+ }
+return j;
+}
+
+void check_input(int j)
+{
+if ((!disable->compact1)||(!disable->compact2))
+
+if(disable->calculate)
+ {disable->calculate=0;
+ warning(3,"time calculations","-m1 or/and -tt","");}
+if (iframerate<=0) error(53,"Input framerate","positive");
+if (oframerate<=0) error(53,"Output framerate","positive");
+if (compactlines<=0) error(53,"First -m1","positive");
+if (compactchars<=0) error(53,"Second -m1","positive");
+if (compactoffset<=0) error(53,"Third -m1","positive");
+if (disable->tolerance)
+ {tolerance2=(long int)(2*oframerate);
+ tolerance1=(long int)(0.5*oframerate)+1;}
+if ((!disable->length))
+ if (pmode!=info) {pmode=info; warning(3,"info","-l","");}
+if (!disable->forcecompact)
+ if (disable->compact2)
+ {disable->compact2=0;
+ warning(3,"compacting","-f","");}
+if ((pmode!=normal)&&(!disable->join)) error(16,NULL,NULL);
+if (j-(disable->join&(pmode!=generate))+((pmode!=info)&disable->length)-1) error(9,NULL,NULL);
+}
+
+//----------Main-program-----------
+int main(int argc, char* argv[])
+{ char* tmp;
+// Display help when no parameters
+if (argc==1) {
+ if (strrchr(argv[0],'/')) help(strrchr(argv[0],'/')+1);
+ else if (strrchr(argv[0],'\\')) help(strrchr(argv[0],'\\')+1);
+ else help(argv[0]);
+ exit(0);}
+// Read and check input parameters
+check_input(parse_input(argc,argv));
+
+root->load(input, 0);
+if (!disable->join) root->load(join, jointime);
+root->display();
+if (oformat==unknown) oformat=iformat;
+
+switch(pmode){
+case normal:
+ // calculations
+ if (!disable->compact0) root->clean();
+ if (iframerate!=oframerate) root->calc_framerate(iframerate,oframerate);
+ if (offset) root->set_offset(offset);
+ if ((!disable->calculate) && (!disable->endtime)) root->calc_time();
+ if (!disable->compact2) root->compact2(compactoffset);
+
+ // Write output to file
+ root->save(output,splittime);
+ break;
+case info:
+ break;
+#ifdef _WIN32
+case generate:
+ // Check if videofile exists
+#ifdef _WIN32
+ std::ifstream video(join, ios::nocreate);
+#else
+ std::ifstream video(join, ios::nocreate);
+#endif
+ if (!video) error(1,join,NULL);
+ video.close();
+
+ if (!(strstr(output,".mvd")||strstr(output,".MVD")))
+ { tmp=new char[strlen(output)+5];
+ strcpy(tmp,output);
+ strcpy(tmp+strlen(output),".mvd");
+ delete output;
+ output=tmp; }
+ std::ofstream outputf(output);
+ if (!outputf) error(2,output,NULL);
+ outputf << "[Micro DVD Ini File]\n\n";
+ outputf << "[MAIN]\nTitle=";
+ if ((tmp=strrchr(input, '\\'))) tmp++;
+ else tmp=input;
+
+ while(*tmp&&*tmp!='.') outputf << (char)toupper(*tmp++);
+ outputf <<"\n\n";
+
+ outputf << "[MOVIE]\nDirectory="<<path(output,join)<<"\nAVIName=";
+
+ if ((tmp=strrchr(join,'\\'))){
+ outputf <<(tmp+1)<<"\n\n";
+ }
+ else outputf << join<<"\n\n";
+
+ outputf << "[SUBTITLES]\nLines="<<root->scan(lines)<<"\nDirectory="<<path(output,input)<<"\n1="<<language<<"\nFile=";
+ if ((tmp=strrchr(input,'\\'))){
+ outputf <<(tmp+1)<<"\n";
+ }
+ else outputf <<input<<"\n";
+ break;
+#endif
+}
+
+return 0;
+}