- Main Page
- Namespaces
- Classes
- Files
- Directories

- File List
- File Members

# VobSubFile.cpp

    00001 /*
    00002  *      Copyright (C) 2003-2005 Gabest
    00003  *      http://www.gabest.org
    00004  *
    00005  *  This Program is free software; you can redistribute it and/or modify
    00006  *  it under the terms of the GNU General Public License as published by
    00007  *  the Free Software Foundation; either version 2, or (at your option)
    00008  *  any later version.
    00009  *
    00010  *  This Program is distributed in the hope that it will be useful,
    00011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
    00012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    00013  *  GNU General Public License for more details.
    00014  *
    00015  *  You should have received a copy of the GNU General Public License
    00016  *  along with GNU Make; see the file COPYING.  If not, write to
    00017  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
    00018  *  http://www.gnu.org/copyleft/gpl.html
    00019  *
    00020  */
    00021
    00022 #include "stdafx.h"
    00023 #include <winioctl.h>
    00024 #include "TextFile.h"
    00025 #include "..\..\include\unrar\unrar.h"
    00026 #include "VobSubFile.h"
    00027
    00028 //
    00029
    00030 struct lang_type {unsigned short id; TCHAR lang_long[64];} lang_tbl[] =
    00031 {
    00032         {'--', _T("(Not detected)")},
    00033         {'cc', _T("Closed Caption")},
    00034         {'aa', _T("Afar")},
    00035         {'ab', _T("Abkhazian")},
    00036         {'af', _T("Afrikaans")},
    00037         {'am', _T("Amharic")},
    00038         {'ar', _T("Arabic")},
    00039         {'as', _T("Assamese")},
    00040         {'ay', _T("Aymara")},
    00041         {'az', _T("Azerbaijani")},
    00042         {'ba', _T("Bashkir")},
    00043         {'be', _T("Byelorussian")},
    00044         {'bg', _T("Bulgarian")},
    00045         {'bh', _T("Bihari")},
    00046         {'bi', _T("Bislama")},
    00047         {'bn', _T("Bengali; Bangla")},
    00048         {'bo', _T("Tibetan")},
    00049         {'br', _T("Breton")},
    00050         {'ca', _T("Catalan")},
    00051         {'co', _T("Corsican")},
    00052         {'cs', _T("Czech")},
    00053         {'cy', _T("Welsh")},
    00054         {'da', _T("Dansk")},
    00055         {'de', _T("Deutsch")},
    00056         {'dz', _T("Bhutani")},
    00057         {'el', _T("Greek")},
    00058         {'en', _T("English")},
    00059         {'eo', _T("Esperanto")},
    00060         {'es', _T("Espanol")},
    00061         {'et', _T("Estonian")},
    00062         {'eu', _T("Basque")},
    00063         {'fa', _T("Persian")},
    00064         {'fi', _T("Finnish")},
    00065         {'fj', _T("Fiji")},
    00066         {'fo', _T("Faroese")},
    00067         {'fr', _T("Francais")},
    00068         {'fy', _T("Frisian")},
    00069         {'ga', _T("Irish")},
    00070         {'gd', _T("Scots Gaelic")},
    00071         {'gl', _T("Galician")},
    00072         {'gn', _T("Guarani")},
    00073         {'gu', _T("Gujarati")},
    00074         {'ha', _T("Hausa")},
    00075         {'he', _T("Hebrew")},
    00076         {'hi', _T("Hindi")},
    00077         {'hr', _T("Hrvatski")},
    00078         {'hu', _T("Hungarian")},
    00079         {'hy', _T("Armenian")},
    00080         {'ia', _T("Interlingua")},
    00081         {'id', _T("Indonesian")},
    00082         {'ie', _T("Interlingue")},
    00083         {'ik', _T("Inupiak")},
    00084         {'in', _T("Indonesian")},
    00085         {'is', _T("Islenska")},
    00086         {'it', _T("Italiano")},
    00087         {'iu', _T("Inuktitut")},
    00088         {'iw', _T("Hebrew")},
    00089         {'ja', _T("Japanese")},
    00090         {'ji', _T("Yiddish")},
    00091         {'jw', _T("Javanese")},
    00092         {'ka', _T("Georgian")},
    00093         {'kk', _T("Kazakh")},
    00094         {'kl', _T("Greenlandic")},
    00095         {'km', _T("Cambodian")},
    00096         {'kn', _T("Kannada")},
    00097         {'ko', _T("Korean")},
    00098         {'ks', _T("Kashmiri")},
    00099         {'ku', _T("Kurdish")},
    00100         {'ky', _T("Kirghiz")},
    00101         {'la', _T("Latin")},
    00102         {'ln', _T("Lingala")},
    00103         {'lo', _T("Laothian")},
    00104         {'lt', _T("Lithuanian")},
    00105         {'lv', _T("Latvian, Lettish")},
    00106         {'mg', _T("Malagasy")},
    00107         {'mi', _T("Maori")},
    00108         {'mk', _T("Macedonian")},
    00109         {'ml', _T("Malayalam")},
    00110         {'mn', _T("Mongolian")},
    00111         {'mo', _T("Moldavian")},
    00112         {'mr', _T("Marathi")},
    00113         {'ms', _T("Malay")},
    00114         {'mt', _T("Maltese")},
    00115         {'my', _T("Burmese")},
    00116         {'na', _T("Nauru")},
    00117         {'ne', _T("Nepali")},
    00118         {'nl', _T("Nederlands")},
    00119         {'no', _T("Norsk")},
    00120         {'oc', _T("Occitan")},
    00121         {'om', _T("(Afan) Oromo")},
    00122         {'or', _T("Oriya")},
    00123         {'pa', _T("Punjabi")},
    00124         {'pl', _T("Polish")},
    00125         {'ps', _T("Pashto, Pushto")},
    00126         {'pt', _T("Portugues")},
    00127         {'qu', _T("Quechua")},
    00128         {'rm', _T("Rhaeto-Romance")},
    00129         {'rn', _T("Kirundi")},
    00130         {'ro', _T("Romanian")},
    00131         {'ru', _T("Russian")},
    00132         {'rw', _T("Kinyarwanda")},
    00133         {'sa', _T("Sanskrit")},
    00134         {'sd', _T("Sindhi")},
    00135         {'sg', _T("Sangho")},
    00136         {'sh', _T("Serbo-Croatian")},
    00137         {'si', _T("Sinhalese")},
    00138         {'sk', _T("Slovak")},
    00139         {'sl', _T("Slovenian")},
    00140         {'sm', _T("Samoan")},
    00141         {'sn', _T("Shona")},
    00142         {'so', _T("Somali")},
    00143         {'sq', _T("Albanian")},
    00144         {'sr', _T("Serbian")},
    00145         {'ss', _T("Siswati")},
    00146         {'st', _T("Sesotho")},
    00147         {'su', _T("Sundanese")},
    00148         {'sv', _T("Svenska")},
    00149         {'sw', _T("Swahili")},
    00150         {'ta', _T("Tamil")},
    00151         {'te', _T("Telugu")},
    00152         {'tg', _T("Tajik")},
    00153         {'th', _T("Thai")},
    00154         {'ti', _T("Tigrinya")},
    00155         {'tk', _T("Turkmen")},
    00156         {'tl', _T("Tagalog")},
    00157         {'tn', _T("Setswana")},
    00158         {'to', _T("Tonga")},
    00159         {'tr', _T("Turkish")},
    00160         {'ts', _T("Tsonga")},
    00161         {'tt', _T("Tatar")},
    00162         {'tw', _T("Twi")},
    00163         {'ug', _T("Uighur")},
    00164         {'uk', _T("Ukrainian")},
    00165         {'ur', _T("Urdu")},
    00166         {'uz', _T("Uzbek")},
    00167         {'vi', _T("Vietnamese")},
    00168         {'vo', _T("Volapuk")},
    00169         {'wo', _T("Wolof")},
    00170         {'xh', _T("Xhosa")},
    00171         {'yi', _T("Yiddish")},                          // formerly ji
    00172         {'yo', _T("Yoruba")},
    00173         {'za', _T("Zhuang")},
    00174         {'zh', _T("Chinese")},
    00175         {'zu', _T("Zulu")},
    00176 };
    00177
    00178 int find_lang(unsigned short id)
    00179 {
    00180         int mid, lo = 0, hi = countof(lang_tbl) - 1;
    00181
    00182         while(lo < hi)
    00183         {
    00184                 mid = (lo + hi) >> 1;
    00185                 if(id < lang_tbl[mid].id) hi = mid;
    00186                 else if(id > lang_tbl[mid].id) lo = mid + 1;
    00187                 else return(mid);
    00188         }
    00189
    00190         return(id == lang_tbl[lo].id ? lo : 0);
    00191 }
    00192
    00193 CString FindLangFromId(WORD id)
    00194 {
    00195         return(lang_tbl[find_lang(id)].lang_long);
    00196 }
    00197
    00198 //
    00199 // CVobSubFile
    00200 //
    00201
    00202 CVobSubFile::CVobSubFile(CCritSec* pLock)
    00203         : ISubPicProviderImpl(pLock)
    00204         , m_sub(1024*1024)
    00205 {
    00206 }
    00207
    00208 CVobSubFile::~CVobSubFile()
    00209 {
    00210 }
    00211
    00212 //
    00213
    00214 bool CVobSubFile::Copy(CVobSubFile& vsf)
    00215 {
    00216         Close();
    00217
    00218         *(CVobSubSettings*)this = *(CVobSubSettings*)&vsf;
    00219         m_title = vsf.m_title;
    00220         m_iLang = vsf.m_iLang;
    00221
    00222         m_sub.SetLength(vsf.m_sub.GetLength());
    00223         m_sub.SeekToBegin();
    00224
    00225         for(int i = 0; i < 32; i++)
    00226         {
    00227                 SubLang& src = vsf.m_langs[i];
    00228                 SubLang& dst = m_langs[i];
    00229
    00230                 dst.id = src.id;
    00231                 dst.name = src.name;
    00232                 dst.alt = src.alt;
    00233
    00234                 for(int j = 0; j < src.subpos.GetCount(); j++)
    00235                 {
    00236                         SubPos& sp = src.subpos[j];
    00237                         if(!sp.fValid) continue;
    00238
    00239                         if(sp.filepos != vsf.m_sub.Seek(sp.filepos, CFile::begin))
    00240                                 continue;
    00241
    00242                         sp.filepos = m_sub.GetPosition();
    00243
    00244                         BYTE buff[2048];
    00245                         vsf.m_sub.Read(buff, 2048);
    00246                         m_sub.Write(buff, 2048);
    00247
    00248                         WORD packetsize = (buff[buff[0x16]+0x18]<<8) | buff[buff[0x16]+0x19];
    00249
    00250                         for(int k = 0, size, sizeleft = packetsize - 4;
    00251                                 k < packetsize - 4;
    00252                                 k += size, sizeleft -= size)
    00253                         {
    00254                                 int hsize = buff[0x16]+0x18 + ((buff[0x15]&0x80) ? 4 : 0);
    00255                                 size = min(sizeleft, 2048 - hsize);
    00256
    00257                                 if(size != sizeleft)
    00258                                 {
    00259                                         while(vsf.m_sub.Read(buff, 2048))
    00260                                         {
    00261                                                 if(!(buff[0x15]&0x80) && buff[buff[0x16]+0x17] == (i|0x20)) break;
    00262                                         }
    00263
    00264                                         m_sub.Write(buff, 2048);
    00265                                 }
    00266                         }
    00267
    00268                         dst.subpos.Add(sp);
    00269                 }
    00270         }
    00271
    00272         m_sub.SetLength(m_sub.GetPosition());
    00273
    00274         return(true);
    00275 }
    00276
    00277 //
    00278
    00279 void CVobSubFile::TrimExtension(CString& fn)
    00280 {
    00281         int i = fn.ReverseFind('.');
    00282         if(i > 0)
    00283         {
    00284                 CString ext = fn.Mid(i).MakeLower();
    00285                 if(ext == _T(".ifo") || ext == _T(".idx") || ext == _T(".sub")
    00286                 || ext == _T(".sst") || ext == _T(".son") || ext == _T(".rar"))
    00287                         fn = fn.Left(i);
    00288         }
    00289 }
    00290
    00291 bool CVobSubFile::Open(CString fn)
    00292 {
    00293         TrimExtension(fn);
    00294
    00295         do
    00296         {
    00297                 Close();
    00298
    00299                 int ver;
    00300                 if(!ReadIdx(fn + _T(".idx"), ver))
    00301                         break;
    00302
    00303                 if(ver < 6 && !ReadIfo(fn + _T(".ifo")))
    00304                         break;
    00305
    00306                 if(!ReadSub(fn + _T(".sub")) && !ReadRar(fn + _T(".rar")))
    00307                         break;
    00308
    00309                 m_title = fn;
    00310
    00311                 for(int i = 0; i < 32; i++)
    00312                 {
    00313                         CArray<SubPos>& sp = m_langs[i].subpos;
    00314
    00315                         for(int j = 0; j < sp.GetCount(); j++)
    00316                         {
    00317                                 sp[j].stop = sp[j].start;
    00318                                 sp[j].fForced = false;
    00319
    00320                                 int packetsize = 0, datasize = 0;
    00321                                 BYTE* buff = GetPacket(j, packetsize, datasize, i);
    00322                                 if(!buff) continue;
    00323
    00324                                 m_img.delay = j < (sp.GetSize()-1) ? sp[j+1].start - sp[j].start : 3000;
    00325                                 m_img.GetPacketInfo(buff, packetsize, datasize);
    00326                                 if(j < (sp.GetSize()-1)) m_img.delay = min(m_img.delay, sp[j+1].start - sp[j].start);
    00327
    00328                                 sp[j].stop = sp[j].start + m_img.delay;
    00329                                 sp[j].fForced = m_img.fForced;
    00330
    00331                                 if(j > 0 && sp[j-1].stop > sp[j].start)
    00332                                         sp[j-1].stop = sp[j].start;
    00333
    00334                                 delete [] buff;
    00335                         }
    00336                 }
    00337
    00338                 return(true);
    00339         }
    00340         while(false);
    00341
    00342         Close();
    00343
    00344         return(false);
    00345 }
    00346
    00347 bool CVobSubFile::Save(CString fn, SubFormat sf)
    00348 {
    00349         TrimExtension(fn);
    00350
    00351         CVobSubFile vsf(NULL);
    00352         if(!vsf.Copy(*this))
    00353                 return(false);
    00354
    00355         switch(sf)
    00356         {
    00357         case VobSub: return vsf.SaveVobSub(fn); break;
    00358         case WinSubMux: return vsf.SaveWinSubMux(fn); break;
    00359         case Scenarist: return vsf.SaveScenarist(fn); break;
    00360         case Maestro: return vsf.SaveMaestro(fn); break;
    00361         default: break;
    00362         }
    00363
    00364         return(false);
    00365 }
    00366
    00367 void CVobSubFile::Close()
    00368 {
    00369         InitSettings();
    00370         m_title.Empty();
    00371         m_sub.SetLength(0);
    00372         m_img.Invalidate();
    00373         m_iLang = -1;
    00374         for(int i = 0; i < 32; i++)
    00375         {
    00376                 m_langs[i].id = 0;
    00377                 m_langs[i].name.Empty();
    00378                 m_langs[i].alt.Empty();
    00379                 m_langs[i].subpos.RemoveAll();
    00380         }
    00381 }
    00382
    00383 //
    00384
    00385 bool CVobSubFile::ReadIdx(CString fn, int& ver)
    00386 {
    00387         CWebTextFile f;
    00388         if(!f.Open(fn))
    00389                 return(false);
    00390
    00391         bool fError = false;
    00392
    00393         int id = -1, delay = 0, vobid = -1, cellid = -1;
    00394         __int64 celltimestamp = 0;
    00395
    00396         CString str;
    00397         for(int line = 0; !fError && f.ReadString(str); line++)
    00398         {
    00399                 str.Trim();
    00400
    00401                 if(line == 0)
    00402                 {
    00403                         TCHAR buff[] = _T("VobSub index file, v");
    00404
    00405                         const TCHAR* s = str;
    00406
    00407                         int i = str.Find(buff);
    00408                         if(i < 0 || _stscanf(&s[i+_tcslen(buff)], _T("%d"), &ver) != 1
    00409                         || ver > VOBSUBIDXVER)
    00410                         {
    00411                                 AfxMessageBox(_T("Wrong file version!"));
    00412                                 fError = true;
    00413                                 continue;
    00414                         }
    00415                 }
    00416                 else if(!str.GetLength())
    00417                 {
    00418                         continue;
    00419                 }
    00420                 else if(str[0] == _T('#'))
    00421                 {
    00422                         TCHAR buff[] = _T("Vob/Cell ID:");
    00423
    00424             const TCHAR* s = str;
    00425
    00426                         int i = str.Find(buff);
    00427                         if(i >= 0)
    00428                         {
    00429                                 _stscanf(&s[i+_tcslen(buff)], _T("%d, %d (PTS: %d)"), &vobid, &cellid, &celltimestamp);
    00430                         }
    00431
    00432                         continue;
    00433                 }
    00434
    00435                 int i = str.Find(':');
    00436                 if(i <= 0) continue;
    00437
    00438                 CString entry = str.Left(i).MakeLower();
    00439
    00440                 str = str.Mid(i+1);
    00441                 str.Trim();
    00442                 if(str.IsEmpty()) continue;
    00443
    00444                 if(entry == _T("size"))
    00445                 {
    00446                         int x, y;
    00447                         if(_stscanf(str, _T("%dx%d"), &x, &y) != 2) fError = true;
    00448                         m_size.cx = x;
    00449                         m_size.cy = y;
    00450                 }
    00451                 else if(entry == _T("org"))
    00452                 {
    00453                         if(_stscanf(str, _T("%d,%d"), &m_x, &m_y) != 2) fError = true;
    00454                         else m_org = CPoint(m_x, m_y);
    00455                 }
    00456                 else if(entry == _T("scale"))
    00457                 {
    00458                         if(ver < 5)
    00459                         {
    00460                                 int scale = 100;
    00461                                 if(_stscanf(str, _T("%d%%"), &scale) != 1) fError = true;
    00462                                 m_scale_x = m_scale_y = scale;
    00463                         }
    00464                         else
    00465                         {
    00466                                 if(_stscanf(str, _T("%d%%,%d%%"), &m_scale_x, &m_scale_y) != 2) fError = true;
    00467                         }
    00468                 }
    00469                 else if(entry == _T("alpha"))
    00470                 {
    00471                         if(_stscanf(str, _T("%d"), &m_alpha) != 1) fError = true;
    00472                 }
    00473                 else if(entry == _T("smooth"))
    00474                 {
    00475                         str.MakeLower();
    00476
    00477                         if(str.Find(_T("old")) >= 0 || str.Find(_T("2")) >= 0) m_fSmooth = 2;
    00478                         else if(str.Find(_T("on")) >= 0 || str.Find(_T("1")) >= 0) m_fSmooth = 1;
    00479                         else if(str.Find(_T("off")) >= 0 || str.Find(_T("0")) >= 0) m_fSmooth = 0;
    00480                         else fError = true;
    00481                 }
    00482                 else if(entry == _T("fadein/out"))
    00483                 {
    00484                         if(_stscanf(str, _T("%d,%d"), &m_fadein, &m_fadeout) != 2) fError = true;
    00485                 }
    00486                 else if(entry == _T("align"))
    00487                 {
    00488                         str.MakeLower();
    00489
    00490                         int i = 0, j = 0;
    00491                         for(CString token = str.Tokenize(_T(" "), i);
    00492                                 j < 3 && !fError && !token.IsEmpty();
    00493                                 token = str.Tokenize(_T(" "), i), j++)
    00494                         {
    00495                                 if(j == 0)
    00496                                 {
    00497                                         if(token == _T("on") || token == _T("1")) m_fAlign = true;
    00498                                         else if(token == _T("off") || token == _T("0")) m_fAlign = false;
    00499                                         else fError = true;
    00500                                 }
    00501                                 else if(j == 1)
    00502                                 {
    00503                                         if(token == _T("at")) {j--; continue;}
    00504
    00505                                         if(token == _T("left")) m_alignhor = 0;
    00506                                         else if(token == _T("center")) m_alignhor = 1;
    00507                                         else if(token == _T("right")) m_alignhor = 2;
    00508                                         else fError = true;
    00509                                 }
    00510                                 else if(j == 2)
    00511                                 {
    00512                                         if(token == _T("top")) m_alignver = 0;
    00513                                         else if(token == _T("center")) m_alignver = 1;
    00514                                         else if(token == _T("bottom")) m_alignver = 2;
    00515                                         else fError = true;
    00516                                 }
    00517                         }
    00518                 }
    00519                 else if(entry == _T("time offset"))
    00520                 {
    00521                         bool fNegative = false;
    00522                         if(str[0] == '-') fNegative = true;
    00523                         str.TrimLeft(_T("+-"));
    00524
    00525                         TCHAR c;
    00526                         int hh, mm, ss, ms;
    00527                         int n = _stscanf(str, _T("%d%c%d%c%d%c%d"), &hh, &c, &mm, &c, &ss, &c, &ms);
    00528
    00529                         m_toff = n == 1
    00530                                         ? hh * (fNegative ? -1 : 1)
    00531                                         : n == 4+3
    00532                                         ? (hh*60*60*1000 + mm*60*1000 + ss*1000 + ms) * (fNegative ? -1 : 1)
    00533                                         : fError = true, 0;
    00534                 }
    00535                 else if(entry == _T("forced subs"))
    00536                 {
    00537                         str.MakeLower();
    00538
    00539                         if(str.Find(_T("on")) >= 0 || str.Find(_T("1")) >= 0) m_fOnlyShowForcedSubs = true;
    00540                         else if(str.Find(_T("off")) >= 0 || str.Find(_T("0")) >= 0) m_fOnlyShowForcedSubs = false;
    00541                         else fError = true;
    00542                 }
    00543                 else if(entry == _T("langidx"))
    00544                 {
    00545                         if(_stscanf(str, _T("%d"), &m_iLang) != 1) fError = true;
    00546                 }
    00547                 else if(entry == _T("palette"))
    00548                 {
    00549                         if(_stscanf(str, _T("%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x"),
    00550                                 &m_orgpal[0], &m_orgpal[1], &m_orgpal[2], &m_orgpal[3],
    00551                                 &m_orgpal[4], &m_orgpal[5], &m_orgpal[6], &m_orgpal[7],
    00552                                 &m_orgpal[8], &m_orgpal[9], &m_orgpal[10], &m_orgpal[11],
    00553                                 &m_orgpal[12], &m_orgpal[13], &m_orgpal[14], &m_orgpal[15]
    00554                                 ) != 16) fError = true;
    00555                 }
    00556                 else if(entry == _T("custom colors"))
    00557                 {
    00558                         str.MakeLower();
    00559
    00560                         if(str.Find(_T("on")) == 0 || str.Find(_T("1")) == 0) m_fCustomPal = true;
    00561                         else if(str.Find(_T("off")) == 0 || str.Find(_T("0")) == 0) m_fCustomPal = false;
    00562                         else fError = true;
    00563
    00564                         i = str.Find(_T("tridx:"));
    00565                         if(i < 0) {fError = true; continue;}
    00566                         str = str.Mid(i + (int)_tcslen(_T("tridx:")));
    00567
    00568                         int tridx;
    00569                         if(_stscanf(str, _T("%x"), &tridx) != 1) {fError = true; continue;}
    00570                         tridx = ((tridx&0x1000)>>12) | ((tridx&0x100)>>7) | ((tridx&0x10)>>2) | ((tridx&1)<<3);
    00571
    00572                         i = str.Find(_T("colors:"));
    00573                         if(i < 0) {fError = true; continue;}
    00574                         str = str.Mid(i + (int)_tcslen(_T("colors:")));
    00575
    00576                         RGBQUAD pal[4];
    00577                         if(_stscanf(str, _T("%x,%x,%x,%x"), &pal[0], &pal[1], &pal[2], &pal[3]) != 4) {fError = true; continue;}
    00578
    00579                         SetCustomPal(pal, tridx);
    00580                 }
    00581                 else if(entry == _T("id"))
    00582                 {
    00583                         str.MakeLower();
    00584
    00585                         int langid = ((str[0]&0xff)<<8)|(str[1]&0xff);
    00586
    00587                         i = str.Find(_T("index:"));
    00588                         if(i < 0) {fError = true; continue;}
    00589                         str = str.Mid(i + (int)_tcslen(_T("index:")));
    00590
    00591                         if(_stscanf(str, _T("%d"), &id) != 1 || id < 0 || id >= 32) {fError = true; continue;}
    00592
    00593                         m_langs[id].id = langid;
    00594                         m_langs[id].name = lang_tbl[find_lang(langid)].lang_long;
    00595                         m_langs[id].alt = lang_tbl[find_lang(langid)].lang_long;
    00596
    00597                         delay = 0;
    00598                 }
    00599                 else if(id >= 0 && entry == _T("alt"))
    00600                 {
    00601                         m_langs[id].alt = str;
    00602                 }
    00603                 else if(id >= 0 && entry == _T("delay"))
    00604                 {
    00605                         bool fNegative = false;
    00606                         if(str[0] == '-') fNegative = true;
    00607                         str.TrimLeft(_T("+-"));
    00608
    00609                         TCHAR c;
    00610                         int hh, mm, ss, ms;
    00611                         if(_stscanf(str, _T("%d%c%d%c%d%c%d"), &hh, &c, &mm, &c, &ss, &c, &ms) != 4+3)  {fError = true; continue;}
    00612
    00613                         delay += (hh*60*60*1000 + mm*60*1000 + ss*1000 + ms) * (fNegative ? -1 : 1);
    00614                 }
    00615                 else if(id >= 0 && entry == _T("timestamp"))
    00616                 {
    00617                         SubPos sb;
    00618
    00619                         sb.vobid = vobid;
    00620                         sb.cellid = cellid;
    00621                         sb.celltimestamp = celltimestamp;
    00622                         sb.fValid = true;
    00623
    00624                         bool fNegative = false;
    00625                         if(str[0] == '-') fNegative = true;
    00626                         str.TrimLeft(_T("+-"));
    00627
    00628                         TCHAR c;
    00629                         int hh, mm, ss, ms;
    00630                         if(_stscanf(str, _T("%d%c%d%c%d%c%d"), &hh, &c, &mm, &c, &ss, &c, &ms) != 4+3)  {fError = true; continue;}
    00631
    00632                         sb.start = (hh*60*60*1000 + mm*60*1000 + ss*1000 + ms) * (fNegative ? -1 : 1) + delay;
    00633
    00634                         i = str.Find(_T("filepos:"));
    00635                         if(i < 0) {fError = true; continue;}
    00636                         str = str.Mid(i + (int)_tcslen(_T("filepos:")));
    00637
    00638                         if(_stscanf(str, _T("%I64x"), &sb.filepos) != 1) {fError = true; continue;}
    00639
    00640                         if(delay < 0 && m_langs[id].subpos.GetSize() > 0)
    00641                         {
    00642                                 __int64 ts = m_langs[id].subpos[m_langs[id].subpos.GetSize()-1].start;
    00643
    00644                                 if(sb.start < ts)
    00645                                 {
    00646                                         delay += (int)(ts - sb.start);
    00647                                         sb.start = ts;
    00648                                 }
    00649                         }
    00650
    00651                         m_langs[id].subpos.Add(sb);
    00652                 }
    00653                 else fError = true;
    00654         }
    00655
    00656         return(!fError);
    00657 }
    00658
    00659 bool CVobSubFile::ReadSub(CString fn)
    00660 {
    00661         CFile f;
    00662         if(!f.Open(fn, CFile::modeRead|CFile::typeBinary|CFile::shareDenyWrite))
    00663                 return(false);
    00664
    00665         m_sub.SetLength(f.GetLength());
    00666         m_sub.SeekToBegin();
    00667
    00668         int len;
    00669         BYTE buff[2048];
    00670         while((len = f.Read(buff, sizeof(buff))) > 0 && *(DWORD*)buff == 0xba010000)
    00671                 m_sub.Write(buff, len);
    00672
    00673         return(true);
    00674 }
    00675
    00676 static unsigned char* RARbuff = NULL;
    00677 static unsigned int RARpos = 0;
    00678
    00679 static int PASCAL MyProcessDataProc(unsigned char* Addr, int Size)
    00680 {
    00681         ASSERT(RARbuff);
    00682
    00683         memcpy(&RARbuff[RARpos], Addr, Size);
    00684         RARpos += Size;
    00685
    00686         return(1);
    00687 }
    00688
    00689 bool CVobSubFile::ReadRar(CString fn)
    00690 {
    00691         HMODULE h = LoadLibrary(_T("unrar.dll"));
    00692         if(!h) return(false);
    00693
    00694         RAROpenArchiveEx OpenArchiveEx = (RAROpenArchiveEx)GetProcAddress(h, "RAROpenArchiveEx");
    00695         RARCloseArchive CloseArchive = (RARCloseArchive)GetProcAddress(h, "RARCloseArchive");
    00696         RARReadHeaderEx ReadHeaderEx = (RARReadHeaderEx)GetProcAddress(h, "RARReadHeaderEx");
    00697         RARProcessFile ProcessFile = (RARProcessFile)GetProcAddress(h, "RARProcessFile");
    00698         RARSetChangeVolProc SetChangeVolProc = (RARSetChangeVolProc)GetProcAddress(h, "RARSetChangeVolProc");
    00699         RARSetProcessDataProc SetProcessDataProc = (RARSetProcessDataProc)GetProcAddress(h, "RARSetProcessDataProc");
    00700         RARSetPassword SetPassword = (RARSetPassword)GetProcAddress(h, "RARSetPassword");
    00701
    00702         if(!(OpenArchiveEx && CloseArchive && ReadHeaderEx && ProcessFile
    00703         && SetChangeVolProc && SetProcessDataProc && SetPassword))
    00704         {
    00705                 FreeLibrary(h);
    00706                 return(false);
    00707         }
    00708
    00709         struct RAROpenArchiveDataEx ArchiveDataEx;
    00710         memset(&ArchiveDataEx, 0, sizeof(ArchiveDataEx));
    00711 #ifdef UNICODE
    00712         ArchiveDataEx.ArcNameW = (LPTSTR)(LPCTSTR)fn;
    00713         char fnA[MAX_PATH];
    00714         if(wcstombs(fnA, fn, fn.GetLength()+1) == -1) fnA[0] = 0;
    00715         ArchiveDataEx.ArcName = fnA;
    00716 #else
    00717         ArchiveDataEx.ArcName = (LPTSTR)(LPCTSTR)fn;
    00718 #endif
    00719         ArchiveDataEx.OpenMode = RAR_OM_EXTRACT;
    00720         ArchiveDataEx.CmtBuf = 0;
    00721         HANDLE hrar = OpenArchiveEx(&ArchiveDataEx);
    00722         if(!hrar)
    00723         {
    00724                 FreeLibrary(h);
    00725                 return(false);
    00726         }
    00727
    00728         SetProcessDataProc(hrar, MyProcessDataProc);
    00729
    00730         struct RARHeaderDataEx HeaderDataEx;
    00731         HeaderDataEx.CmtBuf = NULL;
    00732
    00733         while(ReadHeaderEx(hrar, &HeaderDataEx) == 0)
    00734         {
    00735 #ifdef UNICODE
    00736                 CString subfn(HeaderDataEx.FileNameW);
    00737 #else
    00738                 CString subfn(HeaderDataEx.FileName);
    00739 #endif
    00740
    00741                 if(!subfn.Right(4).CompareNoCase(_T(".sub")))
    00742                 {
    00743                         CAutoVectorPtr<BYTE> buff;
    00744                         if(!buff.Allocate(HeaderDataEx.UnpSize))
    00745                         {
    00746                                 CloseArchive(hrar);
    00747                                 FreeLibrary(h);
    00748                                 return(false);
    00749                         }
    00750
    00751                         RARbuff = buff;
    00752                         RARpos = 0;
    00753
    00754                         if(ProcessFile(hrar, RAR_TEST, NULL, NULL))
    00755                         {
    00756                                 CloseArchive(hrar);
    00757                                 FreeLibrary(h);
    00758
    00759                                 return(false);
    00760                         }
    00761
    00762                         m_sub.SetLength(HeaderDataEx.UnpSize);
    00763                         m_sub.SeekToBegin();
    00764                         m_sub.Write(buff, HeaderDataEx.UnpSize);
    00765                         m_sub.SeekToBegin();
    00766
    00767                         RARbuff = NULL;
    00768                         RARpos = 0;
    00769
    00770                         break;
    00771                 }
    00772
    00773                 ProcessFile(hrar, RAR_SKIP, NULL, NULL);
    00774         }
    00775
    00776         CloseArchive(hrar);
    00777         FreeLibrary(h);
    00778
    00779         return(true);
    00780 }
    00781
    00782 #define ReadBEdw(var) \
    00783     f.Read(&((BYTE*)&var)[3], 1); \
    00784         f.Read(&((BYTE*)&var)[2], 1); \
    00785         f.Read(&((BYTE*)&var)[1], 1); \
    00786         f.Read(&((BYTE*)&var)[0], 1); \
    00787
    00788 bool CVobSubFile::ReadIfo(CString fn)
    00789 {
    00790         CFile f;
    00791         if(!f.Open(fn, CFile::modeRead|CFile::typeBinary|CFile::shareDenyWrite))
    00792                 return(false);
    00793
    00794         /* PGC1 */
    00795
    00796         f.Seek(0xc0+0x0c, SEEK_SET);
    00797
    00798         DWORD pos;
    00799         ReadBEdw(pos);
    00800
    00801         f.Seek(pos*0x800 + 0x0c, CFile::begin);
    00802
    00803         DWORD offset;
    00804         ReadBEdw(offset);
    00805
    00806         /* Subpic palette */
    00807
    00808         f.Seek(pos*0x800 + offset + 0xa4, CFile::begin);
    00809
    00810         for(int i = 0; i < 16; i++)
    00811         {
    00812                 BYTE y, u, v, tmp;
    00813
    00814                 f.Read(&tmp, 1);
    00815                 f.Read(&y, 1);
    00816                 f.Read(&u, 1);
    00817                 f.Read(&v, 1);
    00818
    00819                 y = (y-16)*255/219;
    00820
    00821                 m_orgpal[i].rgbRed = (BYTE)min(max(1.0*y + 1.4022*(u-128), 0), 255);
    00822                 m_orgpal[i].rgbGreen = (BYTE)min(max(1.0*y - 0.3456*(u-128) - 0.7145*(v-128), 0), 255);
    00823                 m_orgpal[i].rgbBlue = (BYTE)min(max(1.0*y + 1.7710*(v-128), 0) , 255);
    00824         }
    00825
    00826         return(true);
    00827 }
    00828
    00829 bool CVobSubFile::WriteIdx(CString fn)
    00830 {
    00831         CTextFile f;
    00832         if(!f.Save(fn, CTextFile::ASCII))
    00833                 return(false);
    00834
    00835         CString str;
    00836         str.Format(_T("# VobSub index file, v%d (do not modify this line!)\n"), VOBSUBIDXVER);
    00837
    00838         f.WriteString(str);
    00839         f.WriteString(_T("# \n"));
    00840         f.WriteString(_T("# To repair desyncronization, you can insert gaps this way:\n"));
    00841         f.WriteString(_T("# (it usually happens after vob id changes)\n"));
    00842         f.WriteString(_T("# \n"));
    00843         f.WriteString(_T("#\t delay: [sign]hh:mm:ss:ms\n"));
    00844         f.WriteString(_T("# \n"));
    00845         f.WriteString(_T("# Where:\n"));
    00846         f.WriteString(_T("#\t [sign]: +, - (optional)\n"));
    00847         f.WriteString(_T("#\t hh: hours (0 <= hh)\n"));
    00848         f.WriteString(_T("#\t mm/ss: minutes/seconds (0 <= mm/ss <= 59)\n"));
    00849         f.WriteString(_T("#\t ms: milliseconds (0 <= ms <= 999)\n"));
    00850         f.WriteString(_T("# \n"));
    00851         f.WriteString(_T("#\t Note: You can't position a sub before the previous with a negative value.\n"));
    00852         f.WriteString(_T("# \n"));
    00853         f.WriteString(_T("# You can also modify timestamps or delete a few subs you don't like.\n"));
    00854         f.WriteString(_T("# Just make sure they stay in increasing order.\n"));
    00855         f.WriteString(_T("\n"));
    00856         f.WriteString(_T("\n"));
    00857
    00858         // Settings
    00859
    00860         f.WriteString(_T("# Settings\n\n"));
    00861
    00862         f.WriteString(_T("# Original frame size\n"));
    00863         str.Format(_T("size: %dx%d\n\n"), m_size.cx, m_size.cy);
    00864         f.WriteString(str);
    00865
    00866         f.WriteString(_T("# Origin, relative to the upper-left corner, can be overloaded by aligment\n"));
    00867         str.Format(_T("org: %d, %d\n\n"), m_x, m_y);
    00868         f.WriteString(str);
    00869
    00870         f.WriteString(_T("# Image scaling (hor,ver), origin is at the upper-left corner or at the alignment coord (x, y)\n"));
    00871         str.Format(_T("scale: %d%%, %d%%\n\n"), m_scale_x, m_scale_y);
    00872         f.WriteString(str);
    00873
    00874         f.WriteString(_T("# Alpha blending\n"));
    00875         str.Format(_T("alpha: %d%%\n\n"), m_alpha);
    00876         f.WriteString(str);
    00877
    00878         f.WriteString(_T("# Smoothing for very blocky images (use OLD for no filtering)\n"));
    00879         str.Format(_T("smooth: %s\n\n"), m_fSmooth == 0 ? _T("OFF") : m_fSmooth == 1 ? _T("ON") : _T("OLD"));
    00880         f.WriteString(str);
    00881
    00882         f.WriteString(_T("# In millisecs\n"));
    00883         str.Format(_T("fadein/out: %d, %d\n\n"), m_fadein, m_fadeout);
    00884         f.WriteString(str);
    00885
    00886         f.WriteString(_T("# Force subtitle placement relative to (org.x, org.y)\n"));
    00887         str.Format(_T("align: %s %s %s\n\n"),
    00888                 m_fAlign ? _T("ON at") : _T("OFF at"),
    00889                 m_alignhor == 0 ? _T("LEFT") : m_alignhor == 1 ? _T("CENTER") : m_alignhor == 2 ? _T("RIGHT") : _T(""),
    00890                 m_alignver == 0 ? _T("TOP") : m_alignver == 1 ? _T("CENTER") : m_alignver == 2 ? _T("BOTTOM") : _T(""));
    00891         f.WriteString(str);
    00892
    00893         f.WriteString(_T("# For correcting non-progressive desync. (in millisecs or hh:mm:ss:ms)\n"));
    00894         f.WriteString(_T("# Note: Not effective in DirectVobSub, use \"delay: ... \" instead.\n"));
    00895         str.Format(_T("time offset: %d\n\n"), m_toff);
    00896         f.WriteString(str);
    00897
    00898         f.WriteString(_T("# ON: displays only forced subtitles, OFF: shows everything\n"));
    00899         str.Format(_T("forced subs: %s\n\n"), m_fOnlyShowForcedSubs ? _T("ON") : _T("OFF"));
    00900         f.WriteString(str);
    00901
    00902         f.WriteString(_T("# The original palette of the DVD\n"));
    00903         str.Format(_T("palette: %06x, %06x, %06x, %06x, %06x, %06x, %06x, %06x, %06x, %06x, %06x, %06x, %06x, %06x, %06x, %06x\n\n"),
    00904                 *((unsigned int*)&m_orgpal[0])&0xffffff,
    00905                 *((unsigned int*)&m_orgpal[1])&0xffffff,
    00906                 *((unsigned int*)&m_orgpal[2])&0xffffff,
    00907                 *((unsigned int*)&m_orgpal[3])&0xffffff,
    00908                 *((unsigned int*)&m_orgpal[4])&0xffffff,
    00909                 *((unsigned int*)&m_orgpal[5])&0xffffff,
    00910                 *((unsigned int*)&m_orgpal[6])&0xffffff,
    00911                 *((unsigned int*)&m_orgpal[7])&0xffffff,
    00912                 *((unsigned int*)&m_orgpal[8])&0xffffff,
    00913                 *((unsigned int*)&m_orgpal[9])&0xffffff,
    00914                 *((unsigned int*)&m_orgpal[10])&0xffffff,
    00915                 *((unsigned int*)&m_orgpal[11])&0xffffff,
    00916                 *((unsigned int*)&m_orgpal[12])&0xffffff,
    00917                 *((unsigned int*)&m_orgpal[13])&0xffffff,
    00918                 *((unsigned int*)&m_orgpal[14])&0xffffff,
    00919                 *((unsigned int*)&m_orgpal[15])&0xffffff);
    00920         f.WriteString(str);
    00921
    00922         int tridx = (!!(m_tridx&1))*0x1000 + (!!(m_tridx&2))*0x100 + (!!(m_tridx&4))*0x10 + (!!(m_tridx&8));
    00923
    00924         f.WriteString(_T("# Custom colors (transp idxs and the four colors)\n"));
    00925         str.Format(_T("custom colors: %s, tridx: %04x, colors: %06x, %06x, %06x, %06x\n\n"),
    00926                 m_fCustomPal ? _T("ON") : _T("OFF"),
    00927                 tridx,
    00928                 *((unsigned int*)&m_cuspal[0])&0xffffff,
    00929                 *((unsigned int*)&m_cuspal[1])&0xffffff,
    00930                 *((unsigned int*)&m_cuspal[2])&0xffffff,
    00931                 *((unsigned int*)&m_cuspal[3])&0xffffff);
    00932         f.WriteString(str);
    00933
    00934         f.WriteString(_T("# Language index in use\n"));
    00935         str.Format(_T("langidx: %d\n\n"), m_iLang);
    00936         f.WriteString(str);
    00937
    00938         // Subs
    00939
    00940         for(int i = 0; i < 32; i++)
    00941         {
    00942                 SubLang& sl = m_langs[i];
    00943
    00944                 CArray<SubPos>& sp = sl.subpos;
    00945                 if(sp.IsEmpty() && !sl.id) continue;
    00946
    00947                 str.Format(_T("# %s\n"), sl.name);
    00948                 f.WriteString(str);
    00949
    00950                 ASSERT(sl.id);
    00951                 if(!sl.id) sl.id = '--';
    00952                 str.Format(_T("id: %c%c, index: %d\n"), sl.id>>8, sl.id&0xff, i);
    00953                 f.WriteString(str);
    00954
    00955                 str.Format(_T("# Decomment next line to activate alternative name in DirectVobSub / Windows Media Player 6.x\n"));
    00956                 f.WriteString(str);
    00957                 str.Format(_T("alt: %s\n"), sl.alt);
    00958                 if(sl.name == sl.alt) str = _T("# ") + str;
    00959                 f.WriteString(str);
    00960
    00961                 char vobid = -1, cellid = -1;
    00962
    00963                 for(int j = 0; j < sp.GetCount(); j++)
    00964                 {
    00965                         if(!sp[j].fValid) continue;
    00966
    00967                         if(sp[j].vobid != vobid || sp[j].cellid != cellid)
    00968                         {
    00969                                 str.Format(_T("# Vob/Cell ID: %d, %d (PTS: %d)\n"), sp[j].vobid, sp[j].cellid, sp[j].celltimestamp);
    00970                                 f.WriteString(str);
    00971                                 vobid = sp[j].vobid;
    00972                                 cellid = sp[j].cellid;
    00973                         }
    00974
    00975                         str.Format(_T("timestamp: %s%02d:%02d:%02d:%03d, filepos: %09I64x\n"),
    00976                                 sp[j].start < 0 ? _T("-") : _T(""),
    00977                                 abs(int((sp[j].start/1000/60/60)%60)),
    00978                                 abs(int((sp[j].start/1000/60)%60)),
    00979                                 abs(int((sp[j].start/1000)%60)),
    00980                                 abs(int((sp[j].start)%1000)),
    00981                                 sp[j].filepos);
    00982                         f.WriteString(str);
    00983                 }
    00984
    00985                 f.WriteString(_T("\n"));
    00986         }
    00987
    00988         return(true);
    00989 }
    00990
    00991 bool CVobSubFile::WriteSub(CString fn)
    00992 {
    00993         CFile f;
    00994         if(!f.Open(fn, CFile::modeCreate|CFile::modeWrite|CFile::typeBinary|CFile::shareDenyWrite))
    00995                 return(false);
    00996
    00997         if(m_sub.GetLength() == 0)
    00998                 return(true); // nothing to do...
    00999
    01000         m_sub.SeekToBegin();
    01001
    01002         int len;
    01003         BYTE buff[2048];
    01004         while((len = m_sub.Read(buff, sizeof(buff))) > 0 && *(DWORD*)buff == 0xba010000)
    01005                 f.Write(buff, len);
    01006
    01007         return(true);
    01008 }
    01009
    01010 //
    01011
    01012 BYTE* CVobSubFile::GetPacket(int idx, int& packetsize, int& datasize, int iLang)
    01013 {
    01014         BYTE* ret = NULL;
    01015
    01016         if(iLang < 0 || iLang >= 32) iLang = m_iLang;
    01017         CArray<SubPos>& sp = m_langs[iLang].subpos;
    01018
    01019         do
    01020         {
    01021                 if(idx < 0 || idx >= sp.GetSize())
    01022                         break;
    01023
    01024                 if(m_sub.Seek(sp[idx].filepos, CFile::begin) != sp[idx].filepos)
    01025                         break;
    01026
    01027                 BYTE buff[0x800];
    01028                 if(sizeof(buff) != m_sub.Read(buff, sizeof(buff)))
    01029                         break;
    01030
    01031                 BYTE offset = buff[0x16];
    01032
    01033                 // let's check a few things to make sure...
    01034                 if(*(DWORD*)&buff[0x00] != 0xba010000
    01035                 || *(DWORD*)&buff[0x0e] != 0xbd010000
    01036                 || !(buff[0x15] & 0x80)
    01037                 || (buff[0x17] & 0xf0) != 0x20
    01038                 || (buff[buff[0x16] + 0x17] & 0xe0) != 0x20
    01039                 || (buff[buff[0x16] + 0x17] & 0x1f) != iLang)
    01040                         break;
    01041
    01042         packetsize = (buff[buff[0x16] + 0x18] << 8) + buff[buff[0x16] + 0x19];
    01043                 datasize = (buff[buff[0x16] + 0x1a] << 8) + buff[buff[0x16] + 0x1b];
    01044
    01045                 ret = new BYTE[packetsize];
    01046                 if(!ret) break;
    01047
    01048                 int i = 0, sizeleft = packetsize;
    01049         for(int size;
    01050                         i < packetsize;
    01051                         i += size, sizeleft -= size)
    01052                 {
    01053                         int hsize = 0x18 + buff[0x16];
    01054                         size = min(sizeleft, 0x800 - hsize);
    01055                         memcpy(&ret[i], &buff[hsize], size);
    01056
    01057             if(size != sizeleft)
    01058                         {
    01059                                 while(m_sub.Read(buff, sizeof(buff)))
    01060                                 {
    01061                                         if( buff[buff[0x16] + 0x17] == (iLang|0x20))
    01062                                                 break;
    01063                                 }
    01064                         }
    01065                 }
    01066
    01067                 if(i != packetsize || sizeleft > 0)
    01068                         delete [] ret, ret = NULL;
    01069         }
    01070         while(false);
    01071
    01072         return(ret);
    01073 }
    01074
    01075 bool CVobSubFile::GetFrame(int idx, int iLang)
    01076 {
    01077         if(iLang < 0 || iLang >= 32) iLang = m_iLang;
    01078         CArray<SubPos>& sp = m_langs[iLang].subpos;
    01079
    01080         if(idx < 0 || idx >= sp.GetCount())
    01081                 return(false);
    01082
    01083         if(m_img.iLang != iLang || m_img.iIdx != idx)
    01084         {
    01085                 int packetsize = 0, datasize = 0;
    01086                 CAutoVectorPtr<BYTE> buff;
    01087                 buff.Attach(GetPacket(idx, packetsize, datasize, iLang));
    01088                 if(!buff || packetsize <= 0 || datasize <= 0) return(false);
    01089
    01090                 m_img.start = sp[idx].start;
    01091                 m_img.delay = idx < (sp.GetSize()-1)
    01092                         ? sp[idx+1].start - sp[idx].start
    01093                         : 3000;
    01094
    01095                 bool ret = m_img.Decode(buff, packetsize, datasize, m_fCustomPal, m_tridx, m_orgpal, m_cuspal, true);
    01096
    01097                 if(idx < (sp.GetSize()-1))
    01098                         m_img.delay = min(m_img.delay, sp[idx+1].start - m_img.start);
    01099
    01100                 if(!ret) return(false);
    01101
    01102                 m_img.iIdx = idx;
    01103                 m_img.iLang = iLang;
    01104         }
    01105
    01106         return(m_fOnlyShowForcedSubs ? m_img.fForced : true);
    01107 }
    01108
    01109 bool CVobSubFile::GetFrameByTimeStamp(__int64 time)
    01110 {
    01111         return(GetFrame(GetFrameIdxByTimeStamp(time)));
    01112 }
    01113
    01114 int CVobSubFile::GetFrameIdxByTimeStamp(__int64 time)
    01115 {
    01116         if(m_iLang < 0 || m_iLang >= 32)
    01117                 return(-1);
    01118
    01119         CArray<SubPos>& sp = m_langs[m_iLang].subpos;
    01120
    01121         int i = 0, j = (int)sp.GetCount() - 1, ret = -1;
    01122
    01123         if(j >= 0 && time >= sp[j].start)
    01124                 return(j);
    01125
    01126         while(i < j)
    01127         {
    01128                 int mid = (i + j) >> 1;
    01129                 int midstart = (int)sp[mid].start;
    01130
    01131                 if(time == midstart) {ret = mid; break;}
    01132                 else if(time < midstart) {ret = -1; if(j == mid) mid--; j = mid;}
    01133                 else if(time > midstart) {ret = mid; if(i == mid) mid++; i = mid;}
    01134         }
    01135
    01136         return(ret);
    01137 }
    01138
    01139 //
    01140
    01141 STDMETHODIMP CVobSubFile::NonDelegatingQueryInterface(REFIID riid, void** ppv)
    01142 {
    01143     CheckPointer(ppv, E_POINTER);
    01144     *ppv = NULL;
    01145
    01146     return
    01147                 QI(IPersist)
    01148                 QI(ISubStream)
    01149                 QI(ISubPicProvider)
    01150                 __super::NonDelegatingQueryInterface(riid, ppv);
    01151 }
    01152
    01153 // ISubPicProvider
    01154
    01155 // TODO: return segments for the fade-in/out time (with animated set to "true" of course)
    01156
    01157 STDMETHODIMP_(POSITION) CVobSubFile::GetStartPosition(REFERENCE_TIME rt, double fps)
    01158 {
    01159         rt /= 10000;
    01160
    01161         int i = GetFrameIdxByTimeStamp(rt);
    01162
    01163         if(!GetFrame(i))
    01164                 return(NULL);
    01165
    01166         if(rt >= (m_img.start + m_img.delay))
    01167         {
    01168                 if(!GetFrame(++i))
    01169                         return(NULL);
    01170         }
    01171
    01172         return((POSITION)(i+1));
    01173 }
    01174
    01175 STDMETHODIMP_(POSITION) CVobSubFile::GetNext(POSITION pos)
    01176 {
    01177         int i = (int)pos;
    01178         return(GetFrame(i) ? (POSITION)(i+1) : NULL);
    01179 }
    01180
    01181 STDMETHODIMP_(REFERENCE_TIME) CVobSubFile::GetStart(POSITION pos, double fps)
    01182 {
    01183         int i = (int)pos-1;
    01184         return(GetFrame(i) ? 10000i64*m_img.start : 0);
    01185 }
    01186
    01187 STDMETHODIMP_(REFERENCE_TIME) CVobSubFile::GetStop(POSITION pos, double fps)
    01188 {
    01189         int i = (int)pos-1;
    01190         return(GetFrame(i) ? 10000i64*(m_img.start + m_img.delay) : 0);
    01191 }
    01192
    01193 STDMETHODIMP_(bool) CVobSubFile::IsAnimated(POSITION pos)
    01194 {
    01195         return(false);
    01196 }
    01197
    01198 STDMETHODIMP CVobSubFile::Render(SubPicDesc& spd, REFERENCE_TIME rt, double fps, RECT& bbox)
    01199 {
    01200         if(spd.bpp != 32) return E_INVALIDARG;
    01201
    01202         rt /= 10000;
    01203
    01204         if(!GetFrame(GetFrameIdxByTimeStamp(rt)))
    01205                 return E_FAIL;
    01206
    01207         if(rt >= (m_img.start + m_img.delay))
    01208                 return E_FAIL;
    01209
    01210         return __super::Render(spd, bbox);
    01211 }
    01212
    01213 // IPersist
    01214
    01215 STDMETHODIMP CVobSubFile::GetClassID(CLSID* pClassID)
    01216 {
    01217         return pClassID ? *pClassID = __uuidof(this), S_OK : E_POINTER;
    01218 }
    01219
    01220 // ISubStream
    01221
    01222 STDMETHODIMP_(int) CVobSubFile::GetStreamCount()
    01223 {
    01224         int iStreamCount = 0;
    01225         for(int i = 0; i < 32; i++)
    01226                 if(m_langs[i].subpos.GetCount()) iStreamCount++;
    01227         return(iStreamCount);
    01228 }
    01229
    01230 STDMETHODIMP CVobSubFile::GetStreamInfo(int iStream, WCHAR** ppName, LCID* pLCID)
    01231 {
    01232         for(int i = 0; i < 32; i++)
    01233         {
    01234                 SubLang& sl = m_langs[i];
    01235
    01236                 if(sl.subpos.IsEmpty() || iStream-- > 0)
    01237                         continue;
    01238
    01239                 if(ppName)
    01240                 {
    01241                         if(!(*ppName = (WCHAR*)CoTaskMemAlloc((sl.alt.GetLength()+1)*sizeof(WCHAR))))
    01242                                 return E_OUTOFMEMORY;
    01243
    01244                         wcscpy(*ppName, CStringW(sl.alt));
    01245                 }
    01246
    01247                 if(pLCID)
    01248                 {
    01249                         *pLCID = 0; // TODO: make lcid out of "sl.id"
    01250                 }
    01251
    01252                 return S_OK;
    01253         }
    01254
    01255         return E_FAIL;
    01256 }
    01257
    01258 STDMETHODIMP_(int) CVobSubFile::GetStream()
    01259 {
    01260         int iStream = 0;
    01261
    01262         for(int i = 0; i < m_iLang; i++)
    01263                 if(!m_langs[i].subpos.IsEmpty()) iStream++;
    01264
    01265         return(iStream);
    01266 }
    01267
    01268 STDMETHODIMP CVobSubFile::SetStream(int iStream)
    01269 {
    01270         for(int i = 0; i < 32; i++)
    01271         {
    01272                 CArray<SubPos>& sp = m_langs[i].subpos;
    01273
    01274                 if(sp.IsEmpty() || iStream-- > 0)
    01275                         continue;
    01276
    01277                 m_iLang = i;
    01278
    01279                 m_img.Invalidate();
    01280
    01281                 break;
    01282         }
    01283
    01284         return iStream < 0 ? S_OK : E_FAIL;
    01285 }
    01286
    01287 STDMETHODIMP CVobSubFile::Reload()
    01288 {
    01289         CFileStatus s;
    01290         if(!CFile::GetStatus(m_title + _T(".idx"), s)) return E_FAIL;
    01291         return !m_title.IsEmpty() && Open(m_title) ? S_OK : E_FAIL;
    01292 }
    01293
    01294 // StretchBlt
    01295
    01296 static void PixelAtBiLinear(RGBQUAD& c, int x, int y, CVobSubImage& src)
    01297 {
    01298         int w = src.rect.Width(),
    01299                 h = src.rect.Height();
    01300
    01301         int x1 = (x >> 16), y1 = (y >> 16) * w,
    01302                 x2 = min(x1 + 1, w-1), y2 = min(y1 + w, (h-1)*w);
    01303
    01304         RGBQUAD* ptr = src.lpPixels;
    01305
    01306         RGBQUAD c11 = ptr[y1 + x1],     c12 = ptr[y1 + x2],
    01307                         c21 = ptr[y2 + x1],     c22 = ptr[y2 + x2];
    01308
    01309         __int64 u2 = x & 0xffff,
    01310                         v2 = y & 0xffff,
    01311                         u1 = 0x10000 - u2,
    01312                         v1 = 0x10000 - v2;
    01313
    01314         int v1u1 = int(v1*u1 >> 16) * c11.rgbReserved,
    01315                 v1u2 = int(v1*u2 >> 16) * c12.rgbReserved,
    01316                 v2u1 = int(v2*u1 >> 16) * c21.rgbReserved,
    01317                 v2u2 = int(v2*u2 >> 16) * c22.rgbReserved;
    01318
    01319         c.rgbRed = (c11.rgbRed * v1u1 + c12.rgbRed * v1u2
    01320                           + c21.rgbRed * v2u1 + c22.rgbRed * v2u2) >> 24;
    01321         c.rgbGreen = (c11.rgbGreen * v1u1 + c12.rgbGreen * v1u2
    01322                                 + c21.rgbGreen * v2u1 + c22.rgbGreen * v2u2) >> 24;
    01323         c.rgbBlue = (c11.rgbBlue * v1u1 + c12.rgbBlue * v1u2
    01324                                 + c21.rgbBlue * v2u1 + c22.rgbBlue * v2u2) >> 24;
    01325         c.rgbReserved = (v1u1 + v1u2
    01326                                         + v2u1 + v2u2) >> 16;
    01327 }
    01328
    01329 static void StretchBlt(SubPicDesc& spd, CRect dstrect, CVobSubImage& src)
    01330 {
    01331         if(dstrect.IsRectEmpty()) return;
    01332
    01333         if((dstrect & CRect(0, 0, spd.w, spd.h)).IsRectEmpty()) return;
    01334
    01335         int sw = src.rect.Width(),
    01336                 sh = src.rect.Height(),
    01337                 dw = dstrect.Width(),
    01338                 dh = dstrect.Height();
    01339
    01340         int srcx = 0,
    01341                 srcy = 0,
    01342                 srcdx = (sw << 16) / dw >> 1,
    01343                 srcdy = (sh << 16) / dh >> 1;
    01344
    01345         if(dstrect.left < 0) {srcx = -dstrect.left * (srcdx<<1); dstrect.left = 0;}
    01346         if(dstrect.top < 0) {srcy = -dstrect.top * (srcdy<<1); dstrect.top = 0;}
    01347         if(dstrect.right > spd.w) {dstrect.right = spd.w;}
    01348         if(dstrect.bottom > spd.h) {dstrect.bottom = spd.h;}
    01349
    01350         if((dstrect & CRect(0, 0, spd.w, spd.h)).IsRectEmpty()) return;
    01351
    01352         dw = dstrect.Width();
    01353         dh = dstrect.Height();
    01354
    01355         for(int y = dstrect.top; y < dstrect.bottom; y++, srcy += (srcdy<<1))
    01356         {
    01357                 RGBQUAD* ptr = (RGBQUAD*)&((BYTE*)spd.bits)[y*spd.pitch] + dstrect.left;
    01358                 RGBQUAD* endptr = ptr + dw;
    01359
    01360                 for(int sx = srcx; ptr < endptr; sx += (srcdx<<1), ptr++)
    01361                 {
    01362 //                      PixelAtBiLinear(*ptr,   sx,                     srcy,           src);
    01364                         RGBQUAD cc[4];
    01365
    01366                         PixelAtBiLinear(cc[0],  sx,                     srcy,           src);
    01367                         PixelAtBiLinear(cc[1],  sx+srcdx,       srcy,           src);
    01368                         PixelAtBiLinear(cc[2],  sx,                     srcy+srcdy,     src);
    01369                         PixelAtBiLinear(cc[3],  sx+srcdx,       srcy+srcdy,     src);
    01370
    01371                         ptr->rgbRed = (cc[0].rgbRed + cc[1].rgbRed + cc[2].rgbRed + cc[3].rgbRed) >> 2;
    01372                         ptr->rgbGreen = (cc[0].rgbGreen + cc[1].rgbGreen + cc[2].rgbGreen + cc[3].rgbGreen) >> 2;
    01373                         ptr->rgbBlue = (cc[0].rgbBlue + cc[1].rgbBlue + cc[2].rgbBlue + cc[3].rgbBlue) >> 2;
    01374                         ptr->rgbReserved = (cc[0].rgbReserved + cc[1].rgbReserved + cc[2].rgbReserved + cc[3].rgbReserved) >> 2;
    01376                         ptr->rgbRed = ptr->rgbRed * ptr->rgbReserved >> 8;
    01377                         ptr->rgbGreen = ptr->rgbGreen * ptr->rgbReserved >> 8;
    01378                         ptr->rgbBlue = ptr->rgbBlue * ptr->rgbReserved >> 8;
    01379                         ptr->rgbReserved = ~ptr->rgbReserved;
    01380
    01381                 }
    01382         }
    01383 }
    01384
    01385 //
    01386 // CVobSubSettings
    01387 //
    01388
    01389 void CVobSubSettings::InitSettings()
    01390 {
    01391         m_size.SetSize(720, 480);
    01392         m_toff = m_x = m_y = 0;
    01393         m_org.SetPoint(0, 0);
    01394         m_scale_x = m_scale_y = m_alpha = 100;
    01395         m_fadein = m_fadeout = 50;
    01396         m_fSmooth = 0;
    01397         m_fAlign = false;
    01398         m_alignhor = m_alignver = 0;
    01399         m_fOnlyShowForcedSubs = false;
    01400         m_fCustomPal = false;
    01401         m_tridx = 0;
    01402         memset(m_orgpal, 0, sizeof(m_orgpal));
    01403         memset(m_cuspal, 0, sizeof(m_cuspal));
    01404 }
    01405
    01406 bool CVobSubSettings::GetCustomPal(RGBQUAD* cuspal, int& tridx)
    01407 {
    01408         memcpy(cuspal, m_cuspal, sizeof(RGBQUAD)*4);
    01409         tridx = m_tridx;
    01410         return(m_fCustomPal);
    01411 }
    01412
    01413 void CVobSubSettings::SetCustomPal(RGBQUAD* cuspal, int tridx)
    01414 {
    01415         memcpy(m_cuspal, cuspal, sizeof(RGBQUAD)*4);
    01416         m_tridx = tridx & 0xf;
    01417         for(int i = 0; i < 4; i++) m_cuspal[i].rgbReserved = (tridx&(1<<i)) ? 0 : 0xff;
    01418         m_img.Invalidate();
    01419 }
    01420
    01421 void CVobSubSettings::GetDestrect(CRect& r)
    01422 {
    01423         int w = MulDiv(m_img.rect.Width(), m_scale_x, 100);
    01424         int h = MulDiv(m_img.rect.Height(), m_scale_y, 100);
    01425
    01426         if(!m_fAlign)
    01427         {
    01428                 r.left = MulDiv(m_img.rect.left, m_scale_x, 100);
    01429                 r.right = MulDiv(m_img.rect.right, m_scale_x, 100);
    01430                 r.top = MulDiv(m_img.rect.top, m_scale_y, 100);
    01431                 r.bottom = MulDiv(m_img.rect.bottom, m_scale_y, 100);
    01432         }
    01433         else
    01434         {
    01435                 switch(m_alignhor)
    01436                 {
    01437                 case 0: r.left = 0; r.right = w; break; // left
    01438                 case 1: r.left = -(w>>1); r.right = -(w>>1) + w; break; // center
    01439                 case 2: r.left = -w; r.right = 0; break; // right
    01440                 default:
    01441                         r.left = MulDiv(m_img.rect.left, m_scale_x, 100);
    01442                         r.right = MulDiv(m_img.rect.right, m_scale_x, 100);
    01443                         break;
    01444                 }
    01445
    01446                 switch(m_alignver)
    01447                 {
    01448                 case 0: r.top = 0; r.bottom = h; break; // top
    01449                 case 1: r.top = -(h>>1); r.bottom = -(h>>1) + h; break; // center
    01450                 case 2: r.top = -h; r.bottom = 0; break; // bottom
    01451                 default:
    01452                         r.top = MulDiv(m_img.rect.top, m_scale_y, 100);
    01453                         r.bottom = MulDiv(m_img.rect.bottom, m_scale_y, 100);
    01454                         break;
    01455                 }
    01456         }
    01457
    01458         r += m_org;
    01459 }
    01460
    01461 void CVobSubSettings::GetDestrect(CRect& r, int w, int h)
    01462 {
    01463         GetDestrect(r);
    01464
    01465         r.left = MulDiv(r.left, w, m_size.cx);
    01466         r.right = MulDiv(r.right, w, m_size.cx);
    01467         r.top = MulDiv(r.top, h, m_size.cy);
    01468         r.bottom = MulDiv(r.bottom, h, m_size.cy);
    01469 }
    01470
    01471 void CVobSubSettings::SetAlignment(bool fAlign, int x, int y, int hor, int ver)
    01472 {
    01473         if(m_fAlign = fAlign)
    01474         {
    01475                 m_org.x = MulDiv(m_size.cx, x, 100);
    01476                 m_org.y = MulDiv(m_size.cy, y, 100);
    01477                 m_alignhor = min(max(hor, 0), 2);
    01478                 m_alignver = min(max(ver, 0), 2);
    01479         }
    01480         else
    01481         {
    01482                 m_org.x = m_x;
    01483                 m_org.y = m_y;
    01484         }
    01485 }
    01486
    01487 #include "RTS.h"
    01488
    01489 HRESULT CVobSubSettings::Render(SubPicDesc& spd, RECT& bbox)
    01490 {
    01491         CRect r;
    01492         GetDestrect(r, spd.w, spd.h);
    01493         StretchBlt(spd, r, m_img);
    01494 /*
    01495 CRenderedTextSubtitle rts(NULL);
    01496 rts.CreateDefaultStyle(DEFAULT_CHARSET);
    01497 rts.m_dstScreenSize.SetSize(m_size.cx, m_size.cy);
    01498 CStringW assstr;
    01499 m_img.Polygonize(assstr, false);
    01500 REFERENCE_TIME rtStart = 10000i64*m_img.start, rtStop = 10000i64*(m_img.start+m_img.delay);
    01501 rts.Add(assstr, true, rtStart, rtStop);
    01502 rts.Render(spd, (rtStart+rtStop)/2, 25, r);
    01503 */
    01504         r &= CRect(CPoint(0, 0), CSize(spd.w, spd.h));
    01505         bbox = r;
    01506         return !r.IsRectEmpty() ? S_OK : S_FALSE;
    01507 }
    01508
    01510
    01511 static bool CompressFile(CString fn)
    01512 {
    01513         if(GetVersion() < 0)
    01514                 return(false);
    01515
    01516         BOOL b = FALSE;
    01517
    01518         HANDLE h = CreateFile(fn, GENERIC_WRITE|GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0);
    01519         if(h != INVALID_HANDLE_VALUE)
    01520         {
    01521                 USHORT us = COMPRESSION_FORMAT_DEFAULT;
    01522                 DWORD nBytesReturned;
    01523                 b = DeviceIoControl(h, FSCTL_SET_COMPRESSION, (LPVOID)&us, 2, NULL, 0, (LPDWORD)&nBytesReturned, NULL);
    01524                 CloseHandle(h);
    01525         }
    01526
    01527         return(!!b);
    01528 }
    01529
    01530 bool CVobSubFile::SaveVobSub(CString fn)
    01531 {
    01532         return WriteIdx(fn + _T(".idx")) && WriteSub(fn + _T(".sub"));
    01533 }
    01534
    01535 bool CVobSubFile::SaveWinSubMux(CString fn)
    01536 {
    01537         TrimExtension(fn);
    01538
    01539         CStdioFile f;
    01540         if(!f.Open(fn + _T(".sub"), CFile::modeCreate|CFile::modeWrite|CFile::typeText|CFile::shareDenyWrite))
    01541                 return(false);
    01542
    01543         m_img.Invalidate();
    01544
    01545         CAutoVectorPtr<BYTE> p4bpp;
    01546         if(!p4bpp.Allocate(720*576/2))
    01547                 return(false);
    01548
    01549         CArray<SubPos>& sp = m_langs[m_iLang].subpos;
    01550         for(int i = 0; i < sp.GetCount(); i++)
    01551         {
    01552                 if(!GetFrame(i)) continue;
    01553
    01554                 int pal[4] = {0, 1, 2, 3};
    01555
    01556                 for(int j = 0; j < 5; j++)
    01557                 {
    01558                         if(j == 4 || !m_img.pal[j].tr)
    01559                         {
    01560                                 j &= 3;
    01561                                 memset(p4bpp, (j<<4)|j, 720*576/2);
    01562                                 pal[j] ^= pal[0], pal[0] ^= pal[j], pal[j] ^= pal[0];
    01563                                 break;
    01564                         }
    01565                 }
    01566
    01567                 int tr[4] = {m_img.pal[pal[0]].tr, m_img.pal[pal[1]].tr, m_img.pal[pal[2]].tr, m_img.pal[pal[3]].tr};
    01568
    01569                 DWORD uipal[4+12];
    01570
    01571                 if(!m_fCustomPal)
    01572                 {
    01573                         uipal[0] = *((DWORD*)&m_img.orgpal[m_img.pal[pal[0]].pal]);
    01574                         uipal[1] = *((DWORD*)&m_img.orgpal[m_img.pal[pal[1]].pal]);
    01575                         uipal[2] = *((DWORD*)&m_img.orgpal[m_img.pal[pal[2]].pal]);
    01576                         uipal[3] = *((DWORD*)&m_img.orgpal[m_img.pal[pal[3]].pal]);
    01577                 }
    01578                 else
    01579                 {
    01580                         uipal[0] = *((DWORD*)&m_img.cuspal[pal[0]]) & 0xffffff;
    01581                         uipal[1] = *((DWORD*)&m_img.cuspal[pal[1]]) & 0xffffff;
    01582                         uipal[2] = *((DWORD*)&m_img.cuspal[pal[2]]) & 0xffffff;
    01583                         uipal[3] = *((DWORD*)&m_img.cuspal[pal[3]]) & 0xffffff;
    01584                 }
    01585
    01586                 CMap<DWORD,DWORD&,BYTE,BYTE&> palmap;
    01587                 palmap[uipal[0]] = 0;
    01588                 palmap[uipal[1]] = 1;
    01589                 palmap[uipal[2]] = 2;
    01590                 palmap[uipal[3]] = 3;
    01591
    01592                 uipal[0] = 0xff; // blue background
    01593
    01594                 int w = m_img.rect.Width()-2;
    01595                 int h = m_img.rect.Height()-2;
    01596                 int pitch = (((w+1)>>1) + 3) & ~3;
    01597
    01598                 for(int y = 0; y < h; y++)
    01599                 {
    01600                         DWORD* p = (DWORD*)&m_img.lpPixels[(y+1)*(w+2)+1];
    01601
    01602                         for(int x = 0; x < w; x++, p++)
    01603                         {
    01604                                 BYTE c = 0;
    01605
    01606                                 if(*p & 0xff000000)
    01607                                 {
    01608                                         DWORD uic = *p & 0xffffff;
    01609                                         palmap.Lookup(uic, c);
    01610                                 }
    01611
    01612                                 BYTE& c4bpp = p4bpp[(h-y-1)*pitch+(x>>1)];
    01613                                 c4bpp = (x&1) ? ((c4bpp&0xf0)|c) : ((c4bpp&0x0f)|(c<<4));
    01614                         }
    01615                 }
    01616
    01617                 int t1 = m_img.start, t2 = t1 + m_img.delay /*+ (m_size.cy==480?(1000/29.97+1):(1000/25))*/;
    01618
    01619                 ASSERT(t2>t1);
    01620
    01621                 if(t2 <= 0) continue;
    01622                 if(t1 < 0) t1 = 0;
    01623
    01624                 CString bmpfn;
    01625                 bmpfn.Format(_T("%s_%06d.bmp"), fn, i+1);
    01626
    01627                 CString str;
    01628                 str.Format(_T("%s\t%02d:%02d:%02d:%02d %02d:%02d:%02d:%02d\t%03d %03d %03d %03d %d %d %d %d\n"),
    01629                         bmpfn,
    01630                         t1/1000/60/60, (t1/1000/60)%60, (t1/1000)%60, (t1%1000)/10,
    01631                         t2/1000/60/60, (t2/1000/60)%60, (t2/1000)%60, (t2%1000)/10,
    01632                         m_img.rect.Width(), m_img.rect.Height(), m_img.rect.left, m_img.rect.top,
    01633                         (tr[0]<<4)|tr[0], (tr[1]<<4)|tr[1], (tr[2]<<4)|tr[2], (tr[3]<<4)|tr[3]);
    01634                 f.WriteString(str);
    01635
    01636                 BITMAPFILEHEADER fhdr =
    01637                 {
    01638                         0x4d42,
    01639                         sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + 16*sizeof(RGBQUAD) + pitch*h,
    01640                         0, 0,
    01641                         sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + 16*sizeof(RGBQUAD)
    01642                 };
    01643
    01644                 BITMAPINFOHEADER ihdr =
    01645                 {
    01646                         sizeof(BITMAPINFOHEADER),
    01647                         w, h, 1, 4, 0,
    01648                         0,
    01649                         pitch*h, 0,
    01650                         16, 4
    01651                 };
    01652
    01653                 CFile bmp;
    01654                 if(bmp.Open(bmpfn, CFile::modeCreate|CFile::modeWrite|CFile::typeBinary|CFile::shareDenyWrite))
    01655                 {
    01656                         bmp.Write(&fhdr, sizeof(fhdr));
    01657                         bmp.Write(&ihdr, sizeof(ihdr));
    01658                         bmp.Write(uipal, sizeof(RGBQUAD)*16);
    01659                         bmp.Write(p4bpp, pitch*h);
    01660                         bmp.Close();
    01661
    01662                         CompressFile(bmpfn);
    01663                 }
    01664         }
    01665
    01666         return(true);
    01667 }
    01668
    01669 bool CVobSubFile::SaveScenarist(CString fn)
    01670 {
    01671         TrimExtension(fn);
    01672
    01673         CStdioFile f;
    01674         if(!f.Open(fn + _T(".sst"), CFile::modeCreate|CFile::modeWrite|CFile::typeText|CFile::shareDenyWrite))
    01675                 return(false);
    01676
    01677         m_img.Invalidate();
    01678
    01679         fn.Replace('\\', '/');
    01680         CString title = fn.Mid(fn.ReverseFind('/')+1);
    01681
    01682         TCHAR buff[MAX_PATH], * pFilePart = buff;
    01683         if(GetFullPathName(fn, MAX_PATH, buff, &pFilePart) == 0)
    01684                 return(false);
    01685
    01686         CString fullpath = CString(buff).Left(pFilePart - buff);
    01687         fullpath.TrimRight(_T("\\/"));
    01688         if(fullpath.IsEmpty())
    01689                 return(false);
    01690
    01691         CString str, str2;
    01692         str += _T("st_format\t2\n");
    01693         str += _T("Display_Start\t%s\n");
    01694         str += _T("TV_Type\t\t%s\n");
    01695         str += _T("Tape_Type\tNON_DROP\n");
    01696         str += _T("Pixel_Area\t(0 %d)\n");
    01697         str += _T("Directory\t%s\n");
    01698         str += _T("Subtitle\t%s\n");
    01699         str += _T("Display_Area\t(0 2 719 %d)\n");
    01700         str += _T("Contrast\t(15 15 15 0)\n");
    01701         str += _T("\n");
    01702         str += _T("PA\t(0 0 255 - - - )\n");
    01703         str += _T("E1\t(255 0 0 - - - )\n");
    01704         str += _T("E2\t(0 0 0 - - - )\n");
    01705         str += _T("BG\t(255 255 255 - - - )\n");
    01706         str += _T("\n");
    01707         str += _T("SP_NUMBER\tSTART\tEND\tFILE_NAME\n");
    01708         str2.Format(str,
    01709                 !m_fOnlyShowForcedSubs ? _T("non_forced") : _T("forced"),
    01710                 m_size.cy == 480 ? _T("NTSC") : _T("PAL"),
    01711                 m_size.cy-3,
    01712                 fullpath,
    01713                 title,
    01714                 m_size.cy == 480 ? 479 : 574);
    01715
    01716         f.WriteString(str2);
    01717
    01718         f.Flush();
    01719
    01720         RGBQUAD pal[16] =
    01721         {
    01722                 {255, 0, 0, 0},
    01723                 {0, 0, 255, 0},
    01724                 {0, 0, 0, 0},
    01725                 {255, 255, 255, 0},
    01726                 {0, 255, 0, 0},
    01727                 {255, 0, 255, 0},
    01728                 {0, 255, 255, 0},
    01729                 {125, 125, 0, 0},
    01730                 {125, 125, 125, 0},
    01731                 {225, 225, 225, 0},
    01732                 {0, 0, 125, 0},
    01733                 {0, 125, 0, 0},
    01734                 {125, 0, 0, 0},
    01735                 {255, 0, 222, 0},
    01736                 {0, 125, 222, 0},
    01737                 {125, 0, 125, 0},
    01738         };
    01739
    01740         BITMAPFILEHEADER fhdr =
    01741         {
    01742                 0x4d42,
    01743                 sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + 16*sizeof(RGBQUAD) + 360*(m_size.cy-2),
    01744                 0, 0,
    01745                 sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + 16*sizeof(RGBQUAD)
    01746         };
    01747
    01748         BITMAPINFOHEADER ihdr =
    01749         {
    01750         sizeof(BITMAPINFOHEADER),
    01751                 720, m_size.cy-2, 1, 4, 0,
    01752                 360*(m_size.cy-2),
    01753                 0, 0,
    01754                 16, 4
    01755         };
    01756
    01757         bool fCustomPal = m_fCustomPal;
    01758         m_fCustomPal = true;
    01759         RGBQUAD tempCusPal[4], newCusPal[4+12] = {{255, 0, 0, 0}, {0, 0, 255, 0}, {0, 0, 0, 0}, {255, 255, 255, 0}};
    01760         memcpy(tempCusPal, m_cuspal, sizeof(tempCusPal));
    01761         memcpy(m_cuspal, newCusPal, sizeof(m_cuspal));
    01762
    01763         CAutoVectorPtr<BYTE> p4bpp;
    01764         if(!p4bpp.Allocate((m_size.cy-2)*360))
    01765                 return(false);
    01766
    01767         BYTE colormap[16];
    01768
    01769         for(int i = 0; i < 16; i++)
    01770         {
    01771                 int idx = 0, maxdif = 255*255*3+1;
    01772
    01773                 for(int j = 0; j < 16 && maxdif; j++)
    01774                 {
    01775                         int rdif = pal[j].rgbRed - m_orgpal[i].rgbRed;
    01776                         int gdif = pal[j].rgbGreen - m_orgpal[i].rgbGreen;
    01777                         int bdif = pal[j].rgbBlue - m_orgpal[i].rgbBlue;
    01778
    01779                         int dif = rdif*rdif + gdif*gdif + bdif*bdif;
    01780                         if(dif < maxdif) {maxdif = dif; idx = j;}
    01781                 }
    01782
    01783                 colormap[i] = idx+1;
    01784         }
    01785
    01786         int pc[4] = {1, 1, 1, 1}, pa[4] = {15, 15, 15, 0};
    01787
    01788         CArray<SubPos>& sp = m_langs[m_iLang].subpos;
    01789         for(int i = 0, k = 0; i < sp.GetCount(); i++)
    01790         {
    01791                 if(!GetFrame(i)) continue;
    01792
    01793                 for(int j = 0; j < 5; j++)
    01794                 {
    01795                         if(j == 4 || !m_img.pal[j].tr)
    01796                         {
    01797                                 j &= 3;
    01798                                 memset(p4bpp, (j<<4)|j, (m_size.cy-2)*360);
    01799                                 break;
    01800                         }
    01801                 }
    01802
    01803                 for(int y = max(m_img.rect.top+1, 2); y < m_img.rect.bottom-1; y++)
    01804                 {
    01805                         ASSERT(m_size.cy-y-1 >= 0);
    01806                         if(m_size.cy-y-1 < 0) break;
    01807
    01808                         DWORD* p = (DWORD*)&m_img.lpPixels[(y-m_img.rect.top)*m_img.rect.Width()+1];
    01809
    01810                         for(int x = m_img.rect.left+1; x < m_img.rect.right-1; x++, p++)
    01811                         {
    01812                                 DWORD rgb = *p&0xffffff;
    01813                                 BYTE c = rgb == 0x0000ff ? 0 : rgb == 0xff0000 ? 1 : rgb == 0x000000 ? 2 : 3;
    01814                                 BYTE& c4bpp = p4bpp[(m_size.cy-y-1)*360+(x>>1)];
    01815                                 c4bpp = (x&1) ? ((c4bpp&0xf0)|c) : ((c4bpp&0x0f)|(c<<4));
    01816                         }
    01817                 }
    01818
    01819                 CString bmpfn;
    01820                 bmpfn.Format(_T("%s_%04d.bmp"), fn, i+1);
    01821                 title = bmpfn.Mid(bmpfn.ReverseFind('/')+1);
    01822
    01823                 // E1, E2, P, Bg
    01824                 int c[4] = {colormap[m_img.pal[1].pal], colormap[m_img.pal[2].pal], colormap[m_img.pal[0].pal], colormap[m_img.pal[3].pal]};
    01825                 c[0]^=c[1], c[1]^=c[0], c[0]^=c[1];
    01826
    01827                 if(memcmp(pc, c, sizeof(c)))
    01828                 {
    01829                         memcpy(pc, c, sizeof(c));
    01830                         str.Format(_T("Color\t (%d %d %d %d)\n"), c[0], c[1], c[2], c[3]);
    01831                         f.WriteString(str);
    01832                 }
    01833
    01834                 // E1, E2, P, Bg
    01835                 int a[4] = {m_img.pal[1].tr, m_img.pal[2].tr, m_img.pal[0].tr, m_img.pal[3].tr};
    01836                 a[0]^=a[1], a[1]^=a[0], a[0]^=a[1];
    01837
    01838                 if(memcmp(pa, a, sizeof(a)))
    01839                 {
    01840                         memcpy(pa, a, sizeof(a));
    01841                         str.Format(_T("Contrast (%d %d %d %d)\n"), a[0], a[1], a[2], a[3]);
    01842                         f.WriteString(str);
    01843                 }
    01844
    01845                 int t1 = sp[i].start;
    01846                 int h1 = t1/1000/60/60, m1 = (t1/1000/60)%60, s1 = (t1/1000)%60;
    01847                 int f1 = (int)((m_size.cy==480?29.97:25)*(t1%1000)/1000);
    01848
    01849                 int t2 = sp[i].stop;
    01850                 int h2 = t2/1000/60/60, m2 = (t2/1000/60)%60, s2 = (t2/1000)%60;
    01851                 int f2 = (int)((m_size.cy==480?29.97:25)*(t2%1000)/1000);
    01852
    01853                 if(t2 <= 0) continue;
    01854                 if(t1 < 0) t1 = 0;
    01855
    01856                 if(h1 == h2 && m1 == m2 && s1 == s2 && f1 == f2)
    01857                 {
    01858                         f2++;
    01859                         if(f2 == (m_size.cy==480?30:25)) {f2 = 0; s2++; if(s2 == 60) {s2 = 0; m2++; if(m2 == 60) {m2 = 0; h2++;}}}
    01860                 }
    01861
    01862                 if(i < sp.GetCount()-1)
    01863                 {
    01864                         int t3 = sp[i+1].start;
    01865                         int h3 = t3/1000/60/60, m3 = (t3/1000/60)%60, s3 = (t3/1000)%60;
    01866                         int f3 = (int)((m_size.cy==480?29.97:25)*(t3%1000)/1000);
    01867
    01868                         if(h3 == h2 && m3 == m2 && s3 == s2 && f3 == f2)
    01869                         {
    01870                                 f2--;
    01871                                 if(f2 == -1) {f2 = (m_size.cy==480?29:24); s2--; if(s2 == -1) {s2 = 59; m2--; if(m2 == -1) {m2 = 59; if(h2 > 0) h2--;}}}
    01872                         }
    01873                 }
    01874
    01875                 if(h1 == h2 && m1 == m2 && s1 == s2 && f1 == f2)
    01876                         continue;
    01877
    01878                 str.Format(_T("%04d\t%02d:%02d:%02d:%02d\t%02d:%02d:%02d:%02d\t%s\n"),
    01879                         ++k,
    01880                         h1, m1, s1, f1,
    01881                         h2, m2, s2, f2,
    01882                         title);
    01883                 f.WriteString(str);
    01884
    01885                 CFile bmp;
    01886                 if(bmp.Open(bmpfn, CFile::modeCreate|CFile::modeWrite|CFile::modeRead|CFile::typeBinary))
    01887                 {
    01888                         bmp.Write(&fhdr, sizeof(fhdr));
    01889                         bmp.Write(&ihdr, sizeof(ihdr));
    01890                         bmp.Write(newCusPal, sizeof(RGBQUAD)*16);
    01891                         bmp.Write(p4bpp, 360*(m_size.cy-2));
    01892                         bmp.Close();
    01893
    01894                         CompressFile(bmpfn);
    01895                 }
    01896         }
    01897
    01898         m_fCustomPal = fCustomPal;
    01899         memcpy(m_cuspal, tempCusPal, sizeof(m_cuspal));
    01900
    01901         return(true);
    01902 }
    01903
    01904 bool CVobSubFile::SaveMaestro(CString fn)
    01905 {
    01906         TrimExtension(fn);
    01907
    01908         CStdioFile f;
    01909         if(!f.Open(fn + _T(".son"), CFile::modeCreate|CFile::modeWrite|CFile::typeText|CFile::shareDenyWrite))
    01910                 return(false);
    01911
    01912         m_img.Invalidate();
    01913
    01914         fn.Replace('\\', '/');
    01915         CString title = fn.Mid(fn.ReverseFind('/')+1);
    01916
    01917         TCHAR buff[MAX_PATH], * pFilePart = buff;
    01918         if(GetFullPathName(fn, MAX_PATH, buff, &pFilePart) == 0)
    01919                 return(false);
    01920
    01921         CString fullpath = CString(buff).Left(pFilePart - buff);
    01922         fullpath.TrimRight(_T("\\/"));
    01923         if(fullpath.IsEmpty())
    01924                 return(false);
    01925
    01926         CString str, str2;
    01927         str += _T("st_format\t2\n");
    01928         str += _T("Display_Start\t%s\n");
    01929         str += _T("TV_Type\t\t%s\n");
    01930         str += _T("Tape_Type\tNON_DROP\n");
    01931         str += _T("Pixel_Area\t(0 %d)\n");
    01932         str += _T("Directory\t%s\n");
    01933         str += _T("Subtitle\t%s\n");
    01934         str += _T("Display_Area\t(0 2 719 %d)\n");
    01935         str += _T("Contrast\t(15 15 15 0)\n");
    01936         str += _T("\n");
    01937         str += _T("SP_NUMBER\tSTART\tEND\tFILE_NAME\n");
    01938         str2.Format(str,
    01939                 !m_fOnlyShowForcedSubs ? _T("non_forced") : _T("forced"),
    01940                 m_size.cy == 480 ? _T("NTSC") : _T("PAL"),
    01941                 m_size.cy-3,
    01942                 fullpath,
    01943                 title,
    01944                 m_size.cy == 480 ? 479 : 574);
    01945
    01946         f.WriteString(str2);
    01947
    01948         f.Flush();
    01949
    01950         RGBQUAD pal[16] =
    01951         {
    01952                 {255, 0, 0, 0},
    01953                 {0, 0, 255, 0},
    01954                 {0, 0, 0, 0},
    01955                 {255, 255, 255, 0},
    01956                 {0, 255, 0, 0},
    01957                 {255, 0, 255, 0},
    01958                 {0, 255, 255, 0},
    01959                 {125, 125, 0, 0},
    01960                 {125, 125, 125, 0},
    01961                 {225, 225, 225, 0},
    01962                 {0, 0, 125, 0},
    01963                 {0, 125, 0, 0},
    01964                 {125, 0, 0, 0},
    01965                 {255, 0, 222, 0},
    01966                 {0, 125, 222, 0},
    01967                 {125, 0, 125, 0},
    01968         };
    01969
    01970         BITMAPFILEHEADER fhdr =
    01971         {
    01972                 0x4d42,
    01973                 sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + 16*sizeof(RGBQUAD) + 360*(m_size.cy-2),
    01974                 0, 0,
    01975                 sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + 16*sizeof(RGBQUAD)
    01976         };
    01977
    01978         BITMAPINFOHEADER ihdr =
    01979         {
    01980         sizeof(BITMAPINFOHEADER),
    01981                 720, m_size.cy-2, 1, 4, 0,
    01982                 360*(m_size.cy-2),
    01983                 0, 0,
    01984                 16, 4
    01985         };
    01986
    01987         bool fCustomPal = m_fCustomPal;
    01988         m_fCustomPal = true;
    01989         RGBQUAD tempCusPal[4], newCusPal[4+12] = {{255, 0, 0, 0}, {0, 0, 255, 0}, {0, 0, 0, 0}, {255, 255, 255, 0}};
    01990         memcpy(tempCusPal, m_cuspal, sizeof(tempCusPal));
    01991         memcpy(m_cuspal, newCusPal, sizeof(m_cuspal));
    01992
    01993         CAutoVectorPtr<BYTE> p4bpp;
    01994         if(!p4bpp.Allocate((m_size.cy-2)*360))
    01995                 return(false);
    01996
    01997         BYTE colormap[16];
    01998         for(int i = 0; i < 16; i++)
    01999                 colormap[i] = i;
    02000
    02001         CFile spf;
    02002         if(spf.Open(fn + _T(".spf"), CFile::modeCreate|CFile::modeWrite|CFile::typeBinary))
    02003         {
    02004                 for(int i = 0; i < 16; i++)
    02005                 {
    02006                         COLORREF c = (m_orgpal[i].rgbBlue<<16) | (m_orgpal[i].rgbGreen<<8) | m_orgpal[i].rgbRed;
    02007                         spf.Write(&c, sizeof(COLORREF));
    02008                 }
    02009
    02010                 spf.Close();
    02011         }
    02012
    02013         int pc[4] = {1,1,1,1}, pa[4] = {15,15,15,0};
    02014
    02015         CArray<SubPos>& sp = m_langs[m_iLang].subpos;
    02016         for(int i = 0, k = 0; i < sp.GetCount(); i++)
    02017         {
    02018                 if(!GetFrame(i)) continue;
    02019
    02020                 for(int j = 0; j < 5; j++)
    02021                 {
    02022                         if(j == 4 || !m_img.pal[j].tr)
    02023                         {
    02024                                 j &= 3;
    02025                                 memset(p4bpp, (j<<4)|j, (m_size.cy-2)*360);
    02026                                 break;
    02027                         }
    02028                 }
    02029
    02030                 for(int y = max(m_img.rect.top+1, 2); y < m_img.rect.bottom-1; y++)
    02031                 {
    02032                         ASSERT(m_size.cy-y-1 >= 0);
    02033                         if(m_size.cy-y-1 < 0) break;
    02034
    02035                         DWORD* p = (DWORD*)&m_img.lpPixels[(y-m_img.rect.top)*m_img.rect.Width()+1];
    02036
    02037                         for(int x = m_img.rect.left+1; x < m_img.rect.right-1; x++, p++)
    02038                         {
    02039                                 DWORD rgb = *p&0xffffff;
    02040                                 BYTE c = rgb == 0x0000ff ? 0 : rgb == 0xff0000 ? 1 : rgb == 0x000000 ? 2 : 3;
    02041                                 BYTE& c4bpp = p4bpp[(m_size.cy-y-1)*360+(x>>1)];
    02042                                 c4bpp = (x&1) ? ((c4bpp&0xf0)|c) : ((c4bpp&0x0f)|(c<<4));
    02043                         }
    02044                 }
    02045
    02046                 CString bmpfn;
    02047                 bmpfn.Format(_T("%s_%04d.bmp"), fn, i+1);
    02048                 title = bmpfn.Mid(bmpfn.ReverseFind('/')+1);
    02049
    02050                 // E1, E2, P, Bg
    02051                 int c[4] = {colormap[m_img.pal[1].pal], colormap[m_img.pal[2].pal], colormap[m_img.pal[0].pal], colormap[m_img.pal[3].pal]};
    02052
    02053                 if(memcmp(pc, c, sizeof(c)))
    02054                 {
    02055                         memcpy(pc, c, sizeof(c));
    02056                         str.Format(_T("Color\t (%d %d %d %d)\n"), c[0], c[1], c[2], c[3]);
    02057                         f.WriteString(str);
    02058                 }
    02059
    02060                 // E1, E2, P, Bg
    02061                 int a[4] = {m_img.pal[1].tr, m_img.pal[2].tr, m_img.pal[0].tr, m_img.pal[3].tr};
    02062
    02063                 if(memcmp(pa, a, sizeof(a)))
    02064                 {
    02065                         memcpy(pa, a, sizeof(a));
    02066                         str.Format(_T("Contrast (%d %d %d %d)\n"), a[0], a[1], a[2], a[3]);
    02067                         f.WriteString(str);
    02068                 }
    02069
    02070                 int t1 = sp[i].start;
    02071                 int h1 = t1/1000/60/60, m1 = (t1/1000/60)%60, s1 = (t1/1000)%60;
    02072                 int f1 = (int)((m_size.cy==480?29.97:25)*(t1%1000)/1000);
    02073
    02074                 int t2 = sp[i].stop;
    02075                 int h2 = t2/1000/60/60, m2 = (t2/1000/60)%60, s2 = (t2/1000)%60;
    02076                 int f2 = (int)((m_size.cy==480?29.97:25)*(t2%1000)/1000);
    02077
    02078                 if(t2 <= 0) continue;
    02079                 if(t1 < 0) t1 = 0;
    02080
    02081                 if(h1 == h2 && m1 == m2 && s1 == s2 && f1 == f2)
    02082                 {
    02083                         f2++;
    02084                         if(f2 == (m_size.cy==480?30:25)) {f2 = 0; s2++; if(s2 == 60) {s2 = 0; m2++; if(m2 == 60) {m2 = 0; h2++;}}}
    02085                 }
    02086
    02087                 if(i < sp.GetCount()-1)
    02088                 {
    02089                         int t3 = sp[i+1].start;
    02090                         int h3 = t3/1000/60/60, m3 = (t3/1000/60)%60, s3 = (t3/1000)%60;
    02091                         int f3 = (int)((m_size.cy==480?29.97:25)*(t3%1000)/1000);
    02092
    02093                         if(h3 == h2 && m3 == m2 && s3 == s2 && f3 == f2)
    02094                         {
    02095                                 f2--;
    02096                                 if(f2 == -1) {f2 = (m_size.cy==480?29:24); s2--; if(s2 == -1) {s2 = 59; m2--; if(m2 == -1) {m2 = 59; if(h2 > 0) h2--;}}}
    02097                         }
    02098                 }
    02099
    02100                 if(h1 == h2 && m1 == m2 && s1 == s2 && f1 == f2)
    02101                         continue;
    02102
    02103                 str.Format(_T("%04d\t%02d:%02d:%02d:%02d\t%02d:%02d:%02d:%02d\t%s\n"),
    02104                         ++k,
    02105                         h1, m1, s1, f1,
    02106                         h2, m2, s2, f2,
    02107                         title);
    02108                 f.WriteString(str);
    02109
    02110                 CFile bmp;
    02111                 if(bmp.Open(bmpfn, CFile::modeCreate|CFile::modeWrite|CFile::typeBinary))
    02112                 {
    02113                         bmp.Write(&fhdr, sizeof(fhdr));
    02114                         bmp.Write(&ihdr, sizeof(ihdr));
    02115                         bmp.Write(newCusPal, sizeof(RGBQUAD)*16);
    02116                         bmp.Write(p4bpp, 360*(m_size.cy-2));
    02117                         bmp.Close();
    02118
    02119                         CompressFile(bmpfn);
    02120                 }
    02121         }
    02122
    02123         m_fCustomPal = fCustomPal;
    02124         memcpy(m_cuspal, tempCusPal, sizeof(m_cuspal));
    02125
    02126         return(true);
    02127 }
    02128
    02129 //
    02130 // CVobSubStream
    02131 //
    02132
    02133 CVobSubStream::CVobSubStream(CCritSec* pLock)
    02134         : ISubPicProviderImpl(pLock)
    02135 {
    02136 }
    02137
    02138 CVobSubStream::~CVobSubStream()
    02139 {
    02140 }
    02141
    02142 void CVobSubStream::Open(CString name, BYTE* pData, int len)
    02143 {
    02144         CAutoLock cAutoLock(&m_csSubPics);
    02145
    02146         m_name = name;
    02147
    02148         CList<CString> lines;
    02149         Explode(CString(CStringA((CHAR*)pData, len)), lines, '\n');
    02150         while(lines.GetCount())
    02151         {
    02152                 CList<CString> sl;
    02153                 Explode(lines.RemoveHead(), sl, ':', 2);
    02154                 if(sl.GetCount() != 2) continue;
    02155                 CString key = sl.GetHead();
    02156                 CString value = sl.GetTail();
    02157                 if(key == _T("size"))
    02158                         _stscanf(value, _T("%dx %d"), &m_size.cx, &m_size.cy);
    02159                 else if(key == _T("org"))
    02160                         _stscanf(value, _T("%d, %d"), &m_org.x, &m_org.y);
    02161                 else if(key == _T("scale"))
    02162                         _stscanf(value, _T("%d%%, %d%%"), &m_scale_x, &m_scale_y);
    02163                 else if(key == _T("alpha"))
    02164                         _stscanf(value, _T("%d%%"), &m_alpha);
    02165                 else if(key == _T("smooth"))
    02166                         m_fSmooth =
    02167                                 value == _T("0") || value == _T("OFF") ? 0 :
    02168                                 value == _T("1") || value == _T("ON") ? 1 :
    02169                                 value == _T("2") || value == _T("OLD") ? 2 :
    02170                                 0;
    02171                 else if(key == _T("align"))
    02172                 {
    02173                         Explode(value, sl, ' ');
    02174                         if(sl.GetCount() == 4) sl.RemoveAt(sl.FindIndex(1));
    02175                         if(sl.GetCount() == 3)
    02176                         {
    02177                                 m_fAlign = sl.RemoveHead() == _T("ON");
    02178                                 CString hor = sl.GetHead(), ver = sl.GetTail();
    02179                                 m_alignhor = hor == _T("LEFT") ? 0 : hor == _T("CENTER") ? 1 : hor == _T("RIGHT") ? 2 : 1;
    02180                                 m_alignver = ver == _T("TOP") ? 0 : ver == _T("CENTER") ? 1 : ver == _T("BOTTOM") ? 2 : 2;
    02181                         }
    02182                 }
    02183                 else if(key == _T("fade in/out"))
    02184                         _stscanf(value, _T("%d%, %d%"), &m_fadein, &m_fadeout);
    02185                 else if(key == _T("time offset"))
    02186                         m_toff = _tcstol(value, NULL, 10);
    02187                 else if(key == _T("forced subs"))
    02188                         m_fOnlyShowForcedSubs = value == _T("1") || value == _T("ON");
    02189                 else if(key == _T("palette"))
    02190                 {
    02191                         Explode(value, sl, ',', 16);
    02192                         for(int i = 0; i < 16 && sl.GetCount(); i++)
    02193                                 *(DWORD*)&m_orgpal[i] = _tcstol(sl.RemoveHead(), NULL, 16);
    02194                 }
    02195                 else if(key == _T("custom colors"))
    02196                 {
    02197                         m_fCustomPal = Explode(value, sl, ',', 3) == _T("ON");
    02198                         if(sl.GetCount() == 3)
    02199                         {
    02200                                 sl.RemoveHead();
    02201                                 CList<CString> tridx, colors;
    02202                                 Explode(sl.RemoveHead(), tridx, ':', 2);
    02203                                 if(tridx.RemoveHead() == _T("tridx"))
    02204                                 {
    02205                                         TCHAR tr[4];
    02206                                         _stscanf(tridx.RemoveHead(), _T("%c%c%c%c"), &tr[0], &tr[1], &tr[2], &tr[3]);
    02207                                         for(int i = 0; i < 4; i++)
    02208                                                 m_tridx |= ((tr[i]=='1')?1:0)<<i;
    02209                                 }
    02210                                 Explode(sl.RemoveHead(), colors, ':', 2);
    02211                                 if(colors.RemoveHead() == _T("colors"))
    02212                                 {
    02213                                         Explode(colors.RemoveHead(), colors, ',', 4);
    02214                                         for(int i = 0; i < 4 && colors.GetCount(); i++)
    02215                                                 *(DWORD*)&m_cuspal[i] = _tcstol(colors.RemoveHead(), NULL, 16);
    02216                                 }
    02217                         }
    02218                 }
    02219         }
    02220 }
    02221
    02222 void CVobSubStream::Add(REFERENCE_TIME tStart, REFERENCE_TIME tStop, BYTE* pData, int len)
    02223 {
    02224         if(len <= 4 || ((pData[0]<<8)|pData[1]) != len) return;
    02225
    02226         CVobSubImage vsi;
    02227         vsi.GetPacketInfo(pData, (pData[0]<<8)|pData[1], (pData[2]<<8)|pData[3]);
    02228
    02229         CAutoPtr<SubPic> p(new SubPic());
    02230         p->tStart = tStart;
    02231         p->tStop = vsi.delay > 0 ? (tStart + 10000i64*vsi.delay) : tStop;
    02232         p->pData.SetSize(len);
    02233         memcpy(p->pData.GetData(), pData, p->pData.GetSize());
    02234
    02235         CAutoLock cAutoLock(&m_csSubPics);
    02236         while(m_subpics.GetCount() && m_subpics.GetTail()->tStart >= tStart)
    02237                 m_subpics.RemoveTail();
    02238         m_subpics.AddTail(p);
    02239 }
    02240
    02241 void CVobSubStream::RemoveAll()
    02242 {
    02243         CAutoLock cAutoLock(&m_csSubPics);
    02244         m_subpics.RemoveAll();
    02245 }
    02246
    02247 STDMETHODIMP CVobSubStream::NonDelegatingQueryInterface(REFIID riid, void** ppv)
    02248 {
    02249     CheckPointer(ppv, E_POINTER);
    02250     *ppv = NULL;
    02251
    02252     return
    02253                 QI(IPersist)
    02254                 QI(ISubStream)
    02255                 QI(ISubPicProvider)
    02256                 __super::NonDelegatingQueryInterface(riid, ppv);
    02257 }
    02258
    02259 // ISubPicProvider
    02260
    02261 STDMETHODIMP_(POSITION) CVobSubStream::GetStartPosition(REFERENCE_TIME rt, double fps)
    02262 {
    02263         CAutoLock cAutoLock(&m_csSubPics);
    02264         POSITION pos = m_subpics.GetTailPosition();
    02265         for(; pos; m_subpics.GetPrev(pos))
    02266         {
    02267                 SubPic* sp = m_subpics.GetAt(pos);
    02268                 if(sp->tStart <= rt)
    02269                 {
    02270                         if(sp->tStop <= rt) m_subpics.GetNext(pos);
    02271                         break;
    02272                 }
    02273         }
    02274         return(pos);
    02275 }
    02276
    02277 STDMETHODIMP_(POSITION) CVobSubStream::GetNext(POSITION pos)
    02278 {
    02279         CAutoLock cAutoLock(&m_csSubPics);
    02280         m_subpics.GetNext(pos);
    02281     return pos;
    02282 }
    02283
    02284 STDMETHODIMP_(REFERENCE_TIME) CVobSubStream::GetStart(POSITION pos, double fps)
    02285 {
    02286         CAutoLock cAutoLock(&m_csSubPics);
    02287         return m_subpics.GetAt(pos)->tStart;
    02288 }
    02289
    02290 STDMETHODIMP_(REFERENCE_TIME) CVobSubStream::GetStop(POSITION pos, double fps)
    02291 {
    02292         CAutoLock cAutoLock(&m_csSubPics);
    02293         return m_subpics.GetAt(pos)->tStop;
    02294 }
    02295
    02296 STDMETHODIMP_(bool) CVobSubStream::IsAnimated(POSITION pos)
    02297 {
    02298         return(false);
    02299 }
    02300
    02301 STDMETHODIMP CVobSubStream::Render(SubPicDesc& spd, REFERENCE_TIME rt, double fps, RECT& bbox)
    02302 {
    02303         if(spd.bpp != 32) return E_INVALIDARG;
    02304
    02305         POSITION pos = m_subpics.GetTailPosition();
    02306         for(; pos; m_subpics.GetPrev(pos))
    02307         {
    02308                 SubPic* sp = m_subpics.GetAt(pos);
    02309                 if(sp->tStart <= rt && rt < sp->tStop)
    02310                 {
    02311                         if(m_img.iIdx != (int)pos)
    02312                         {
    02313                                 BYTE* pData = sp->pData.GetData();
    02314                                 m_img.Decode(
    02315                                         pData, (pData[0]<<8)|pData[1], (pData[2]<<8)|pData[3],
    02316                                         m_fCustomPal, m_tridx, m_orgpal, m_cuspal, true);
    02317                                 m_img.iIdx = (int)pos;
    02318                         }
    02319
    02320                         return __super::Render(spd, bbox);
    02321                 }
    02322         }
    02323
    02324         return E_FAIL;
    02325 }
    02326
    02327 // IPersist
    02328
    02329 STDMETHODIMP CVobSubStream::GetClassID(CLSID* pClassID)
    02330 {
    02331         return pClassID ? *pClassID = __uuidof(this), S_OK : E_POINTER;
    02332 }
    02333
    02334 // ISubStream
    02335
    02336 STDMETHODIMP_(int) CVobSubStream::GetStreamCount()
    02337 {
    02338         return 1;
    02339 }
    02340
    02341 STDMETHODIMP CVobSubStream::GetStreamInfo(int i, WCHAR** ppName, LCID* pLCID)
    02342 {
    02343         CAutoLock cAutoLock(&m_csSubPics);
    02344
    02345         if(ppName)
    02346         {
    02347                 if(!(*ppName = (WCHAR*)CoTaskMemAlloc((m_name.GetLength()+1)*sizeof(WCHAR))))
    02348                         return E_OUTOFMEMORY;
    02349                 wcscpy(*ppName, CStringW(m_name));
    02350         }
    02351
    02352         if(pLCID)
    02353         {
    02354                 *pLCID = 0; // TODO
    02355         }
    02356
    02357         return S_OK;
    02358 }
    02359
    02360 STDMETHODIMP_(int) CVobSubStream::GetStream()
    02361 {
    02362         return 0;
    02363 }
    02364
    02365 STDMETHODIMP CVobSubStream::SetStream(int iStream)
    02366 {
    02367         return iStream == 0 ? S_OK : E_FAIL;
    02368 }