00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
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")},
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
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
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
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
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
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);
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
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
01154
01155
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
01214
01215 STDMETHODIMP CVobSubFile::GetClassID(CLSID* pClassID)
01216 {
01217 return pClassID ? *pClassID = __uuidof(this), S_OK : E_POINTER;
01218 }
01219
01220
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;
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
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
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
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;
01438 case 1: r.left = -(w>>1); r.right = -(w>>1) + w; break;
01439 case 2: r.left = -w; r.right = 0; break;
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;
01449 case 1: r.top = -(h>>1); r.bottom = -(h>>1) + h; break;
01450 case 2: r.top = -h; r.bottom = 0; break;
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
01496
01497
01498
01499
01500
01501
01502
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;
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 ;
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
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
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
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
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
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
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
02328
02329 STDMETHODIMP CVobSubStream::GetClassID(CLSID* pClassID)
02330 {
02331 return pClassID ? *pClassID = __uuidof(this), S_OK : E_POINTER;
02332 }
02333
02334
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;
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 }