It is revealed that this code doesn't worth the time I spent once it is done.
The code of rangemap.h is not provided. It is the most unqualified for being part of the lib.
#include <cassert>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <string>
#include <list>
#include <conio.h>
#include <qtl/ctnr/rangemap.h>
#define GENUSE_BUFSIZE 1024
using namespace std;
using namespace qtl;
typedef unsigned long movtime_t;
struct TimeMapTraits : public RangeMapTraits<movtime_t>
{
static bool DoMapSToD (TsCref sIni, TsCref sEnd, TdCref dIni, TdCref dEnd,
bool rev, TsCref s, TdRef d)
{
d = (s - sIni) + dIni;
return true;
}
static bool DoMapDToS (TdCref dIni, TdCref dEnd, TsCref sIni, TsCref sEnd,
bool rev, TdCref d, TsRef s)
{
s = (d - dIni) + sIni;
return true;
}
};
struct TimeMap : public SegRangeMap<movtime_t, movtime_t, TimeMapTraits>
{
private:
typedef SegRangeMap<movtime_t, movtime_t, TimeMapTraits> MyBase;
public:
LsItr recent;
struct DstRange
{
movtime_t ini, end;
DstRange (movtime_t i, movtime_t e) : ini(i), end(e) {}
};
typedef vector<DstRange> DrVec;
TimeMap ()
{
Clear();
}
void Clear ()
{
MyBase::Clear();
InitRecent();
}
void InitRecent()
{
recent = listS.begin();
}
bool MapSToD (TsCref s, OutVecD &ds)
{
LsItr itr;
bool found = false;
ds.clear();
LsItr begin = recent;
LsItr end = listS.end();
movtime_t d;
LsItr newRecent;
// backward
for (itr = recent; itr != listS.begin();)
{
itr--;
if (TrySToD(*itr, s, d))
{
ds.push_back(d);
found = true;
newRecent = recent;
}
else if (s >= itr->end)
{
break;
}
}
// forward
for (itr = recent; itr != listS.end(); ++itr)
{
if (TrySToD(*itr, s, d))
{
ds.push_back(d);
if (!found)
{
newRecent = recent;
found = true;
}
}
else if (s < itr->ini)
{
break;
}
}
if (found)
{
recent = newRecent;
}
return found;
}
bool MapRangeSToD (TsCref sIni, TsCref sEnd, DrVec &drs)
{
OutVecD ds;
if (!MapSToD(sIni, ds))
{
return false;
}
movtime_t d = sEnd - sIni;
drs.clear();
for (int i = 0; i < ds.size(); i++)
{
movtime_t ini = ds[i];
movtime_t end = ini + d;
int j;
for (j = 0; j < drs.size(); j++)
{
if (drs[j].ini > ini)
{
break;
}
}
drs.insert(drs.begin() + j, DstRange(ini, end));
}
return true;
}
};
/* Sub to abs time (many to one) mapping */
typedef TimeMap SubToAbsMap;
/* Mov to abs time (one to many) mapping */
typedef TimeMap AbsToMovMap;
struct SubMovMapper
{
SubToAbsMap stoa;
AbsToMovMap atom;
typedef AbsToMovMap::OutVecD OutVec;
typedef AbsToMovMap::DrVec DrVec;
typedef AbsToMovMap::DstRange DstRange;
void AddSubToAbs (movtime_t sIni, movtime_t sEnd, movtime_t aIni, movtime_t aEnd)
{
stoa.AddMap(sIni, sEnd, aIni, aEnd);
}
void AddAbsToMov (movtime_t aIni, movtime_t aEnd, movtime_t mIni, movtime_t mEnd)
{
atom.AddMap(aIni, aEnd, mIni, mEnd);
}
bool MapSubToAbs (movtime_t s, movtime_t &a)
{
SubToAbsMap::OutVecD as;
if (!stoa.MapSToD(s, as)) return false;
a = as[0];
return true;
}
bool MapAbsToMov (movtime_t a, OutVec &m)
{
return atom.MapSToD(a, m);
}
bool MapSubToMov (movtime_t s, OutVec &m)
{
movtime_t a;
if (!MapSubToAbs(s, a)) return false;
return MapAbsToMov(a, m);
}
bool MapRangeSubToAbs (movtime_t sIni, movtime_t sEnd, movtime_t &dIni, movtime_t &dEnd)
{
SubToAbsMap::DrVec drs;
if (!stoa.MapRangeSToD(sIni, sEnd, drs)) return false;
dIni = drs[0].ini;
dEnd = drs[0].end;
return true;
}
bool MapRangeAbsToMov (movtime_t sIni, movtime_t sEnd, DrVec &drs)
{
return atom.MapRangeSToD(sIni, sEnd, drs);
}
void Clear ()
{
stoa.Clear();
atom.Clear();
}
void InitRecent ()
{
stoa.InitRecent();
atom.InitRecent();
}
};
struct CommonParser
{
static int ReadAsManyDigits (const char *&psz, unsigned long &val)
{
int r;
val = 0;
if (psz[0] == 0)
{
return 0;
}
for (r = 0 ; psz[0] >= '0' && psz[0] <= '9'; ++psz)
{
val *= 10;
val += psz[0] - '0';
r++;
}
return r;
}
static bool GoUntil (const char *&psz, const char *csz)
{
const char *pcsz = csz;
for ( ; *psz != 0 && *pcsz != 0; psz++)
{
if (*psz == *pcsz)
{
pcsz++;
}
else
{ /* unfortunately it's not kmp */
psz -= pcsz - csz;
pcsz = csz;
}
}
return *pcsz == 0;
}
static void SkipBlanks (const char *&psz)
{
for ( ; psz[0] == ' ' || psz[0] == '/t'; ++psz);
}
static bool Pass (const char *&psz, const char *csz)
{
SkipBlanks(psz);
for ( ; *csz != 0 && *psz == *csz; psz++, csz++);
SkipBlanks(psz);
return (*csz == 0);
}
};
struct TmapParser : public CommonParser
{
typedef vector<movtime_t> TimeVec;
TimeVec baseTime;
TimeVec volDur; // explicitly indicated volumn duration
movtime_t oldst, olddt;
enum PlusType
{
k_ptNormal,
k_ptPrevious,
k_ptExpected,
};
TmapParser ()
{
SetNumVols(0);
}
static bool ParseDigitalTime (const char *&psz, movtime_t &ot, int *pVol = 0)
{
unsigned long val;
unsigned long hr, min, sec, msec;
int vol = -1;
int len = ReadAsManyDigits(psz, val);
if (len == 0) return false;
if (psz[0] == '.')
{
if (val == 0)
{
return false;
}
vol = val - 1; // volumne number starts from 1
}
else if (psz[0] == ':')
{
hr = val;
}
else
{
ot = val;
return true;
}
if (vol >= 0)
{
psz++;
len = ReadAsManyDigits(psz, hr);
if (len == 0 || psz[0] != ':') return false;
}
psz++;
len = ReadAsManyDigits(psz, min);
if (len == 0 || psz[0] != ':') return false;
psz++;
len = ReadAsManyDigits(psz, sec);
if (len == 0) return false;
if (psz[0] == ',')
{
psz++;
len = ReadAsManyDigits(psz, msec);
}
ot = ((hr * 60+min)*60+sec)*1000+msec;
if (pVol)
{
*pVol = vol;
}
return true;
}
bool ParseTime (const char *&psz, movtime_t &ot, PlusType &plus)
{
int vol = -1, hr, min, sec, msec;
int len;
movtime_t t;
vector<movtime_t> vt;
vector<bool> st;
int sig = 1;
bool expectnote= true;
bool expectval = true;
plus = k_ptNormal;
// overflow doesn't fail the result
ot = 0;
while (1)
{
SkipBlanks(psz);
if (psz[0] == '+' && expectnote)
{
sig = 1;
expectnote = false; expectval = true;
psz++;
}
else if (psz[0] == '-' && expectnote)
{
sig = -1;
expectnote = false; expectval = true;
psz++;
}
else if ((psz[0] == '^' || psz[0] == '*') && expectval)
{
if (sig < 0) { return false; }
if (plus != k_ptNormal)
{
return false;
}
plus = psz[0] == '^'? k_ptPrevious : k_ptExpected;
expectval = false; expectnote = true;
++psz;
}
else if (expectval)
{
if (!ParseDigitalTime(psz, t, &vol))
{
return false;
}
if (vol >= 0)
{
int numVols = baseTime.size();
if (vol >= numVols)
{
return false;
}
t += baseTime[vol];
if (vol + 1 < numVols && baseTime[vol+1] < t)
{
baseTime[vol+1] = t; // default base time for next volume
if (baseTime[vol+1] - baseTime[vol] < volDur[vol])
{
baseTime[vol+1] = baseTime[vol] + volDur[vol];
}
}
}
printf("t=%d/n", t);
st.push_back(sig);
vt.push_back(t);
if (sig>0)
{
ot += t;
}
else
{
ot -= t;
}
expectval = false; expectnote = true;
}
else
{
break;
}
}
if (expectval == true)
{ // syntax error
return false;
}
return true;
}
bool ParseTmapLine (const char *szLine, movtime_t &st1, movtime_t &st2,
movtime_t &dt1, movtime_t &dt2)
{
int val;
const char *psz = szLine;
PlusType plus[4];
if (!ParseTime(psz, st1, plus[0])) return false;
if (plus[0] == k_ptPrevious)
{
st1 += oldst;
}
if (!Pass(psz, "~")) return false;
if (!ParseTime(psz, st2, plus[1])) return false;
if (plus[1] == k_ptPrevious)
{
st2 += oldst;
}
if (!Pass(psz, "=>")) return false;
if (!ParseTime(psz, dt1, plus[2])) return false;
if (plus[2] == k_ptPrevious)
{
dt1 += olddt;
}
if (!Pass(psz, "~")) return false;
if (!ParseTime(psz, dt2, plus[3])) return false;
if (plus[3] == k_ptPrevious)
{
dt2 += olddt;
}
int plusexp, i;
for (plusexp = 0, i = 0; i < 4; i++)
{
if (plus[i] == k_ptExpected)
{
plusexp++;
}
}
if (plusexp > 1)
{
return false;
}
if (plus[0] == k_ptExpected)
{
st1 += st2 - (dt2 - dt1);
}
else if (plus[1] == k_ptExpected)
{
st2 += st1 + (dt2 - dt1);
}
else if (plus[2] == k_ptExpected)
{
dt1 += dt2 - (st2 - st1);
}
else if (plus[3] == k_ptExpected)
{
dt2 += dt1 + (st2 - st1);
}
oldst = st2;
olddt = dt2;
return true;
}
void SetNumVols (int n)
{
baseTime.resize(n, 0);
volDur.resize(n, 0);
oldst = olddt = 0;
}
void SetVolDur (int i, movtime_t d)
{
volDur[i] = d;
}
};
struct StResync
{
struct Item
{
movtime_t absini;
movtime_t absend;
char strbuf[GENUSE_BUFSIZE];
};
SubMovMapper mapper;
struct In
{
TmapParser parser;
SubMovMapper *pMapper;
vector<FILE *> files;
int fvidx;
bool isEnd;
int sn;
In (SubMovMapper *pm) : pMapper(pm)
{
Clear();
}
~In ()
{
for (int i = 0; i < files.size(); i++)
{
if (files[i])
{
fclose(files[i]);
}
}
}
void Clear ()
{
fvidx = 0;
isEnd = false;
sn = -1;
parser.SetNumVols(0);
files.clear();
}
bool IsSn (const char *buf, int &sn) const
{
const char *psz = buf;
unsigned long val;
int len = CommonParser::ReadAsManyDigits(psz, val);
if (len == 0 || psz[0] != 0)
{
return false;
}
sn = val;
return true;
}
bool Get (Item &item)
{
char buf[GENUSE_BUFSIZE];
__again:
if (sn < 0)
{
do
{
if (!StResync::GetLine(buf, GENUSE_BUFSIZE, files[fvidx])) return false;
} while (!IsSn(buf, sn));
}
if (!StResync::GetLine(buf, GENUSE_BUFSIZE, files[fvidx])) return false;
movtime_t ini, end;
const char *psz = buf; // use it at present for holding data
TmapParser::SkipBlanks(psz);
if (!TmapParser::ParseDigitalTime(psz, ini)) return false;
TmapParser::SkipBlanks(psz);
TmapParser::Pass(psz, "-->");
TmapParser::SkipBlanks(psz);
if (!TmapParser::ParseDigitalTime(psz, end)) return false;
TmapParser::SkipBlanks(psz);
ini += parser.baseTime[fvidx];
end += parser.baseTime[fvidx];
if (!pMapper->MapRangeSubToAbs(ini, end, item.absini, item.absend)) return false;
sn = -1;
item.strbuf[0] = 0;
int fileCount = files.size();
while (1)
{ /* content */
if (!StResync::GetLine(buf, GENUSE_BUFSIZE, files[fvidx]))
{
fvidx++;
if (fvidx < fileCount) continue;
isEnd = true;
return false;
}
if (IsSn(buf, sn))
{
break;
}
strcat(item.strbuf, buf);
strcat(item.strbuf, "/n");
}
if (item.absini == item.absend)
{
goto __again;
}
return true;
}
bool IsEnd ()
{
return isEnd;
}
void SetNumVols (int n)
{
parser.SetNumVols(n);
files.resize(n);
fvidx = 0;
}
void AddFile (int i, FILE *f, movtime_t dur)
{
files[i] = f;
parser.SetVolDur(i, dur);
}
};
struct Out
{
TmapParser parser;
SubMovMapper *pMapper;
vector<FILE *> files;
vector<movtime_t> baseTime;
vector<int> sns;
int fvidx;
Out (SubMovMapper *pm) : pMapper(pm)
{
Clear();
}
~Out ()
{
for (int i = 0; i < files.size(); i++)
{
if (files[i])
{
fclose(files[i]);
}
}
}
void Clear ()
{
SetNumVols(0);
}
static void AnalyseTime (movtime_t t, int &h, int &m, int &s, int &ms)
{
ms = t % 1000;
t /= 1000;
s = t % 60;
t /= 60;
m = t % 60;
t /= 60;
h = t;
}
bool Set (Item &item)
{
char tempbuf[64] = {0, };
char outbuf[GENUSE_BUFSIZE] = {0, };
SubMovMapper::DrVec movrs;
if (!pMapper->MapRangeAbsToMov(item.absini, item.absend, movrs))
{ // no matching time segment in the movie
return false;
}
int n = movrs.size();
int fn = files.size();
static int lastI;
for (int i = 0; i < n; i++)
{
movtime_t ini = movrs[i].ini;
movtime_t end = movrs[i].end;
int fvibegin = fvidx;
int fviend = fvidx;
for (fvibegin = 0; fvibegin < fn && ini < baseTime[fvibegin]; fvibegin++)
{
}
for ( ; fvibegin + 1 < fn && ini >= baseTime[fvibegin + 1]; fvibegin++)
{
}
ini -= baseTime[fvibegin];
fviend = fvibegin; // can only be too small
for ( ; fviend+1 < fn && end > baseTime[fviend+1]; fviend++)
{
}
/*
for ( ; fviend > 0 && end <= baseTime[fviend]; fvend--)
{
}
*/
end -= baseTime[fviend];
for (int i = fvibegin; i <= fviend; i++)
{
movtime_t vi, ve;
if (i == fvibegin)
{
vi = ini;
}
else
{
vi = 0;
}
if (i == fviend)
{
ve = end;
}
else
{
ve = baseTime[i + 1] - baseTime[i];
}
// SN
sprintf(outbuf, "%d/n", ++sns[i]);
// Time stamp
int h, m, s, ms;
AnalyseTime(vi, h, m, s, ms);
sprintf(tempbuf, "%02d:%02d:%02d,%03d", h, m, s, ms);
strcat(outbuf, tempbuf);
AnalyseTime(ve, h, m, s, ms);
sprintf(tempbuf, " --> %02d:%02d:%02d,%03d/n", h, m, s, ms);
strcat(outbuf, tempbuf);
// Text
strcat(outbuf, item.strbuf);
fprintf(files[i], "%s", outbuf);
}
}
return true;
}
void SetNumVols (int n)
{
files.resize(n, 0);
baseTime.resize(n, 0);
sns.resize(n, 0);
parser.SetNumVols(n);
fvidx = 0;
}
void AddFile (int i, FILE *f, movtime_t dur)
{
files[i] = f;
if (i == 0)
{
baseTime[i] = 0;
}
if (i < baseTime.size() - 1)
{
baseTime[i+1] = baseTime[i] + dur;
}
parser.SetVolDur(i, dur);
}
};
In in;
Out out;
StResync () : in(&mapper), out(&mapper)
{
}
static bool GetLine (char *buf, int bufsize, FILE *f)
{
if (feof(f))
{
return false;
}
buf[0] = 0;
fgets(buf, bufsize, f);
int len = strlen(buf);
if (len>0 && buf[len-1]==0xa) len--;
if (len>0 && buf[len-1]==0xd) len--;
buf[len] = 0;
return true;
}
static const char *ProcessFileInfo(char *buf, movtime_t &dur)
{
const char *psz = buf;
CommonParser::SkipBlanks(psz);
const char *psz2 = psz;
if (CommonParser::GoUntil(psz2, ">>"))
{
const char *psz3 = psz2;
psz2 -= 2;
if (psz == psz2)
{
return 0;
}
for ( ; psz2[-1] == ' ' || psz2[-1] == '/t'; psz2--)
{
}
char *psz4 = const_cast<char *>(psz2);
psz4[0] = 0;
CommonParser::SkipBlanks(psz3);
if (!TmapParser::ParseDigitalTime(psz3, dur))
{
return 0;
}
}
return psz;
}
static bool IsEmptyLine (const char *buf)
{
const char *psz = buf;
CommonParser::SkipBlanks(psz);
return psz[0] == 0;
}
static bool IsSepLine (const char *buf)
{
const char *psz = buf;
CommonParser::SkipBlanks(psz);
return (psz[0] == '*' && psz[1] == '*' && psz[1] == '*');
}
bool ParseConfig (FILE *fConfig)
{
char buf[GENUSE_BUFSIZE];
const char *psz;
int len;
unsigned long n;
in.Clear();
out.Clear();
mapper.Clear();
// 1. first go the file names of input subtitle files and its length if available
if (!GetLine(buf, GENUSE_BUFSIZE, fConfig))
{
printf("! Unexpected end of file./n");
return false;
}
psz = buf;
CommonParser::SkipBlanks(psz);
len = CommonParser::ReadAsManyDigits(psz, n);
if (len == 0)
{
printf("! The number of input subtitle files unspecified./n");
return false;
}
in.SetNumVols(n);
for (int i = 0; i < n; i++)
{
// format:
// abc.srt -- 00:59:28,600
// or
// abc.srt -- 3568600
movtime_t dur;
FILE *fIn;
GetLine(buf, GENUSE_BUFSIZE, fConfig);
const char *pfn = ProcessFileInfo(buf, dur);
fIn = fopen(pfn, "r");
if (fIn == 0)
{
printf("! The input file '%s' cannot be opened./n", pfn);
return false;
}
else
{
int hr, min, sec, msec;
Out::AnalyseTime(dur, hr, min, sec, msec);
printf(": The input file '%s' has successfully been loaded with duration claimed to be %02d:%02d:%02d,%03d/n", pfn, hr, min, sec, msec);
}
in.AddFile(i, fIn, dur);
}
// 2. then come the file names of output subtitle files and its length according to the movie files if available
if (!GetLine(buf, GENUSE_BUFSIZE, fConfig))
{
printf("! Unexpected end of file./n");
return false;
}
psz = buf;
CommonParser::SkipBlanks(psz);
len = CommonParser::ReadAsManyDigits(psz, n);
if (len == 0)
{
printf("! The number of output subtitle files unspecified./n");
return false;
}
out.SetNumVols(n);
for (int i = 0; i < n; i++)
{
// format, same as input
movtime_t dur;
FILE *fOut;
if (!GetLine(buf, GENUSE_BUFSIZE, fConfig))
{
printf("! Unexpected end of file./n");
return false;
}
const char *pfn = ProcessFileInfo(buf, dur);
fOut = fopen(pfn, "w");
if (fOut == 0)
{
printf("! The output file '%s' cannot be opened./n", pfn);
return false;
}
else
{
int hr, min, sec, msec;
Out::AnalyseTime(dur, hr, min, sec, msec);
printf(": The output file '%s' has successfully been loaded with duration claimed to be %02d:%02d:%02d,%03d/n", pfn, hr, min, sec, msec);
}
out.AddFile(i, fOut, dur);
}
movtime_t st1, st2, dt1, dt2;
// 3. here is the original subtitle temporal description
printf(": Loading mapping from original subtitle to absolute timeaxis./n");
while (1)
{
// format: time~time-->abstime~abstime
if (!GetLine(buf, GENUSE_BUFSIZE, fConfig))
{
printf("! Unexpected end of file./n");
return false;
}
printf("%s/n", buf);
if (IsEmptyLine(buf))
{
continue;
}
else if (IsSepLine(buf))
{
break;
}
if (!in.parser.ParseTmapLine(buf, st1, st2, dt1, dt2))
{
printf("! Time syntax error./n");
return false;
}
mapper.AddSubToAbs(st1, st2, dt1, dt2);
printf(": successfully mapped %d~%d to %d~%d./n", st1, st2, dt1, dt2);
}
// 4. the movie temporal description
bool noout = true;
printf(": Loading mapping from absolute timeaxis to movie./n");
while (1)
{
// format: abstime~abstime-->time~time
if (!GetLine(buf, GENUSE_BUFSIZE, fConfig))
{
if (noout)
{
printf("! Unexpected end of file./n");
return false;
}
else
{
mapper.InitRecent();
return true;
}
}
if (IsEmptyLine(buf))
{
continue;
}
if (!out.parser.ParseTmapLine(buf, st1, st2, dt1, dt2))
{
printf("! Time syntax error./n");
return false;
}
mapper.AddAbsToMov(st1, st2, dt1, dt2);
printf(": successfully mapped %d~%d to %d~%d./n", st1, st2, dt1, dt2);
noout = false;
}
return false;
}
bool Rectify ()
{
Item item;
while (in.Get(item))
{
out.Set(item);
}
return in.IsEnd();
}
};
int main (void)
{
FILE *fConfig = fopen("stresync_config.txt", "r");
StResync resync;
if (!resync.ParseConfig(fConfig))
{
return 0;
}
if (!resync.Rectify())
{
return 0;
}
printf(": Conversion was successfully made./n");
return 0;
}