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 }

Generated on Tue Dec 13 14:47:56 2005 for guliverkli by  doxygen 1.4.5