Xy Flyweight. [Part 3]
[xy_vsfilter.git] / src / subtitles / VobSubFile.cpp
blobf238aa30a93891fe99229308793413d859e98aa8
1 /*
2 * Copyright (C) 2003-2006 Gabest
3 * http://www.gabest.org
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
8 * any later version.
9 *
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with GNU Make; see the file COPYING. If not, write to
17 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18 * http://www.gnu.org/copyleft/gpl.html
22 #include "stdafx.h"
23 #include <winioctl.h>
24 #include "TextFile.h"
25 #include "..\..\include\unrar\unrar.h"
26 #include "VobSubFile.h"
30 struct lang_type {unsigned short id; LPCSTR lang_long;} lang_tbl[] =
32 {'--', "(Not detected)"},
33 {'cc', "Closed Caption"},
34 {'aa', "Afar"},
35 {'ab', "Abkhazian"},
36 {'af', "Afrikaans"},
37 {'am', "Amharic"},
38 {'ar', "Arabic"},
39 {'as', "Assamese"},
40 {'ay', "Aymara"},
41 {'az', "Azerbaijani"},
42 {'ba', "Bashkir"},
43 {'be', "Byelorussian"},
44 {'bg', "Bulgarian"},
45 {'bh', "Bihari"},
46 {'bi', "Bislama"},
47 {'bn', "Bengali; Bangla"},
48 {'bo', "Tibetan"},
49 {'br', "Breton"},
50 {'ca', "Catalan"},
51 {'co', "Corsican"},
52 {'cs', "Czech"},
53 {'cy', "Welsh"},
54 {'da', "Dansk"},
55 {'de', "Deutsch"},
56 {'dz', "Bhutani"},
57 {'el', "Greek"},
58 {'en', "English"},
59 {'eo', "Esperanto"},
60 {'es', "Espanol"},
61 {'et', "Estonian"},
62 {'eu', "Basque"},
63 {'fa', "Persian"},
64 {'fi', "Finnish"},
65 {'fj', "Fiji"},
66 {'fo', "Faroese"},
67 {'fr', "Francais"},
68 {'fy', "Frisian"},
69 {'ga', "Irish"},
70 {'gd', "Scots Gaelic"},
71 {'gl', "Galician"},
72 {'gn', "Guarani"},
73 {'gu', "Gujarati"},
74 {'ha', "Hausa"},
75 {'he', "Hebrew"},
76 {'hi', "Hindi"},
77 {'hr', "Hrvatski"},
78 {'hu', "Hungarian"},
79 {'hy', "Armenian"},
80 {'ia', "Interlingua"},
81 {'id', "Indonesian"},
82 {'ie', "Interlingue"},
83 {'ik', "Inupiak"},
84 {'in', "Indonesian"},
85 {'is', "Islenska"},
86 {'it', "Italiano"},
87 {'iu', "Inuktitut"},
88 {'iw', "Hebrew"},
89 {'ja', "Japanese"},
90 {'ji', "Yiddish"},
91 {'jw', "Javanese"},
92 {'ka', "Georgian"},
93 {'kk', "Kazakh"},
94 {'kl', "Greenlandic"},
95 {'km', "Cambodian"},
96 {'kn', "Kannada"},
97 {'ko', "Korean"},
98 {'ks', "Kashmiri"},
99 {'ku', "Kurdish"},
100 {'ky', "Kirghiz"},
101 {'la', "Latin"},
102 {'ln', "Lingala"},
103 {'lo', "Laothian"},
104 {'lt', "Lithuanian"},
105 {'lv', "Latvian, Lettish"},
106 {'mg', "Malagasy"},
107 {'mi', "Maori"},
108 {'mk', "Macedonian"},
109 {'ml', "Malayalam"},
110 {'mn', "Mongolian"},
111 {'mo', "Moldavian"},
112 {'mr', "Marathi"},
113 {'ms', "Malay"},
114 {'mt', "Maltese"},
115 {'my', "Burmese"},
116 {'na', "Nauru"},
117 {'ne', "Nepali"},
118 {'nl', "Nederlands"},
119 {'no', "Norsk"},
120 {'oc', "Occitan"},
121 {'om', "(Afan) Oromo"},
122 {'or', "Oriya"},
123 {'pa', "Punjabi"},
124 {'pl', "Polish"},
125 {'ps', "Pashto, Pushto"},
126 {'pt', "Portugues"},
127 {'qu', "Quechua"},
128 {'rm', "Rhaeto-Romance"},
129 {'rn', "Kirundi"},
130 {'ro', "Romanian"},
131 {'ru', "Russian"},
132 {'rw', "Kinyarwanda"},
133 {'sa', "Sanskrit"},
134 {'sd', "Sindhi"},
135 {'sg', "Sangho"},
136 {'sh', "Serbo-Croatian"},
137 {'si', "Sinhalese"},
138 {'sk', "Slovak"},
139 {'sl', "Slovenian"},
140 {'sm', "Samoan"},
141 {'sn', "Shona"},
142 {'so', "Somali"},
143 {'sq', "Albanian"},
144 {'sr', "Serbian"},
145 {'ss', "Siswati"},
146 {'st', "Sesotho"},
147 {'su', "Sundanese"},
148 {'sv', "Svenska"},
149 {'sw', "Swahili"},
150 {'ta', "Tamil"},
151 {'te', "Telugu"},
152 {'tg', "Tajik"},
153 {'th', "Thai"},
154 {'ti', "Tigrinya"},
155 {'tk', "Turkmen"},
156 {'tl', "Tagalog"},
157 {'tn', "Setswana"},
158 {'to', "Tonga"},
159 {'tr', "Turkish"},
160 {'ts', "Tsonga"},
161 {'tt', "Tatar"},
162 {'tw', "Twi"},
163 {'ug', "Uighur"},
164 {'uk', "Ukrainian"},
165 {'ur', "Urdu"},
166 {'uz', "Uzbek"},
167 {'vi', "Vietnamese"},
168 {'vo', "Volapuk"},
169 {'wo', "Wolof"},
170 {'xh', "Xhosa"},
171 {'yi', "Yiddish"}, // formerly ji
172 {'yo', "Yoruba"},
173 {'za', "Zhuang"},
174 {'zh', "Chinese"},
175 {'zu', "Zulu"},
178 int find_lang(unsigned short id)
180 int mid, lo = 0, hi = countof(lang_tbl) - 1;
182 while(lo < hi)
184 mid = (lo + hi) >> 1;
185 if(id < lang_tbl[mid].id) hi = mid;
186 else if(id > lang_tbl[mid].id) lo = mid + 1;
187 else return(mid);
190 return(id == lang_tbl[lo].id ? lo : 0);
193 CString FindLangFromId(WORD id)
195 return CString(lang_tbl[find_lang(id)].lang_long);
199 // CVobSubFile
202 CVobSubFile::CVobSubFile(CCritSec* pLock)
203 : CSubPicProviderImpl(pLock)
204 , m_sub(1024*1024)
208 CVobSubFile::~CVobSubFile()
214 bool CVobSubFile::Copy(CVobSubFile& vsf)
216 Close();
218 *(CVobSubSettings*)this = *(CVobSubSettings*)&vsf;
219 m_title = vsf.m_title;
220 m_iLang = vsf.m_iLang;
222 m_sub.SetLength(vsf.m_sub.GetLength());
223 m_sub.SeekToBegin();
225 for(int i = 0; i < 32; i++)
227 SubLang& src = vsf.m_langs[i];
228 SubLang& dst = m_langs[i];
230 dst.id = src.id;
231 dst.name = src.name;
232 dst.alt = src.alt;
234 for(size_t j = 0; j < src.subpos.GetCount(); j++)
236 SubPos& sp = src.subpos[j];
237 if(!sp.fValid) continue;
239 if(sp.filepos != vsf.m_sub.Seek(sp.filepos, CFile::begin))
240 continue;
242 sp.filepos = m_sub.GetPosition();
244 BYTE buff[2048];
245 vsf.m_sub.Read(buff, 2048);
246 m_sub.Write(buff, 2048);
248 WORD packetsize = (buff[buff[0x16]+0x18]<<8) | buff[buff[0x16]+0x19];
250 for(int k = 0, size, sizeleft = packetsize - 4;
251 k < packetsize - 4;
252 k += size, sizeleft -= size)
254 int hsize = buff[0x16]+0x18 + ((buff[0x15]&0x80) ? 4 : 0);
255 size = min(sizeleft, 2048 - hsize);
257 if(size != sizeleft)
259 while(vsf.m_sub.Read(buff, 2048))
261 if(!(buff[0x15]&0x80) && buff[buff[0x16]+0x17] == (i|0x20)) break;
264 m_sub.Write(buff, 2048);
268 dst.subpos.Add(sp);
272 m_sub.SetLength(m_sub.GetPosition());
274 return(true);
279 void CVobSubFile::TrimExtension(CString& fn)
281 int i = fn.ReverseFind('.');
282 if(i > 0)
284 CString ext = fn.Mid(i).MakeLower();
285 if(ext == _T(".ifo") || ext == _T(".idx") || ext == _T(".sub")
286 || ext == _T(".sst") || ext == _T(".son") || ext == _T(".rar"))
287 fn = fn.Left(i);
291 bool CVobSubFile::Open(CString fn)
293 TrimExtension(fn);
297 Close();
299 int ver;
300 if(!ReadIdx(fn + _T(".idx"), ver))
301 break;
303 if(ver < 6 && !ReadIfo(fn + _T(".ifo")))
304 break;
306 if(!ReadSub(fn + _T(".sub")) && !ReadRar(fn + _T(".rar")))
307 break;
309 m_title = fn;
311 for(int i = 0; i < 32; i++)
313 CAtlArray<SubPos>& sp = m_langs[i].subpos;
315 for(int j = 0; j < sp.GetCount(); j++)
317 sp[j].stop = sp[j].start;
318 sp[j].fForced = false;
320 int packetsize = 0, datasize = 0;
321 BYTE* buff = GetPacket(j, packetsize, datasize, i);
322 if(!buff) continue;
324 m_img.delay = j < (sp.GetCount()-1) ? sp[j+1].start - sp[j].start : 3000;
325 m_img.GetPacketInfo(buff, packetsize, datasize);
326 if(j < (sp.GetCount()-1)) m_img.delay = min(m_img.delay, sp[j+1].start - sp[j].start);
328 sp[j].stop = sp[j].start + m_img.delay;
329 sp[j].fForced = m_img.fForced;
331 if(j > 0 && sp[j-1].stop > sp[j].start)
332 sp[j-1].stop = sp[j].start;
334 delete [] buff;
338 return(true);
340 while(false);
342 Close();
344 return(false);
347 bool CVobSubFile::Save(CString fn, SubFormat sf)
349 TrimExtension(fn);
351 CVobSubFile vsf(NULL);
352 if(!vsf.Copy(*this))
353 return(false);
355 switch(sf)
357 case VobSub: return vsf.SaveVobSub(fn); break;
358 case WinSubMux: return vsf.SaveWinSubMux(fn); break;
359 case Scenarist: return vsf.SaveScenarist(fn); break;
360 case Maestro: return vsf.SaveMaestro(fn); break;
361 default: break;
364 return(false);
367 void CVobSubFile::Close()
369 InitSettings();
370 m_title.Empty();
371 m_sub.SetLength(0);
372 m_img.Invalidate();
373 m_iLang = -1;
374 for(int i = 0; i < 32; i++)
376 m_langs[i].id = 0;
377 m_langs[i].name.Empty();
378 m_langs[i].alt.Empty();
379 m_langs[i].subpos.RemoveAll();
385 bool CVobSubFile::ReadIdx(CString fn, int& ver)
387 CWebTextFile f;
388 if(!f.Open(fn))
389 return(false);
391 bool fError = false;
393 int id = -1, delay = 0, vobid = -1, cellid = -1;
394 __int64 celltimestamp = 0;
396 CString str;
397 for(int line = 0; !fError && f.ReadString(str); line++)
399 str.Trim();
401 if(line == 0)
403 TCHAR buff[] = _T("VobSub index file, v");
405 const TCHAR* s = str;
407 int i = str.Find(buff);
408 if(i < 0 || _stscanf(&s[i+_tcslen(buff)], _T("%d"), &ver) != 1
409 || ver > VOBSUBIDXVER)
411 AfxMessageBox(_T("Wrong file version!"));
412 fError = true;
413 continue;
416 else if(!str.GetLength())
418 continue;
420 else if(str[0] == _T('#'))
422 TCHAR buff[] = _T("Vob/Cell ID:");
424 const TCHAR* s = str;
426 int i = str.Find(buff);
427 if(i >= 0)
429 _stscanf(&s[i+_tcslen(buff)], _T("%d, %d (PTS: %d)"), &vobid, &cellid, &celltimestamp);
432 continue;
435 int i = str.Find(':');
436 if(i <= 0) continue;
438 CString entry = str.Left(i).MakeLower();
440 str = str.Mid(i+1);
441 str.Trim();
442 if(str.IsEmpty()) continue;
444 if(entry == _T("size"))
446 int x, y;
447 if(_stscanf(str, _T("%dx%d"), &x, &y) != 2) fError = true;
448 m_size.cx = x;
449 m_size.cy = y;
451 else if(entry == _T("org"))
453 if(_stscanf(str, _T("%d,%d"), &m_x, &m_y) != 2) fError = true;
454 else m_org = CPoint(m_x, m_y);
456 else if(entry == _T("scale"))
458 if(ver < 5)
460 int scale = 100;
461 if(_stscanf(str, _T("%d%%"), &scale) != 1) fError = true;
462 m_scale_x = m_scale_y = scale;
464 else
466 if(_stscanf(str, _T("%d%%,%d%%"), &m_scale_x, &m_scale_y) != 2) fError = true;
469 else if(entry == _T("alpha"))
471 if(_stscanf(str, _T("%d"), &m_alpha) != 1) fError = true;
473 else if(entry == _T("smooth"))
475 str.MakeLower();
477 if(str.Find(_T("old")) >= 0 || str.Find(_T("2")) >= 0) m_fSmooth = 2;
478 else if(str.Find(_T("on")) >= 0 || str.Find(_T("1")) >= 0) m_fSmooth = 1;
479 else if(str.Find(_T("off")) >= 0 || str.Find(_T("0")) >= 0) m_fSmooth = 0;
480 else fError = true;
482 else if(entry == _T("fadein/out"))
484 if(_stscanf(str, _T("%d,%d"), &m_fadein, &m_fadeout) != 2) fError = true;
486 else if(entry == _T("align"))
488 str.MakeLower();
490 int i = 0, j = 0;
491 for(CString token = str.Tokenize(_T(" "), i);
492 j < 3 && !fError && !token.IsEmpty();
493 token = str.Tokenize(_T(" "), i), j++)
495 if(j == 0)
497 if(token == _T("on") || token == _T("1")) m_fAlign = true;
498 else if(token == _T("off") || token == _T("0")) m_fAlign = false;
499 else fError = true;
501 else if(j == 1)
503 if(token == _T("at")) {j--; continue;}
505 if(token == _T("left")) m_alignhor = 0;
506 else if(token == _T("center")) m_alignhor = 1;
507 else if(token == _T("right")) m_alignhor = 2;
508 else fError = true;
510 else if(j == 2)
512 if(token == _T("top")) m_alignver = 0;
513 else if(token == _T("center")) m_alignver = 1;
514 else if(token == _T("bottom")) m_alignver = 2;
515 else fError = true;
519 else if(entry == _T("time offset"))
521 bool fNegative = false;
522 if(str[0] == '-') fNegative = true;
523 str.TrimLeft(_T("+-"));
525 TCHAR c;
526 int hh, mm, ss, ms;
527 int n = _stscanf(str, _T("%d%c%d%c%d%c%d"), &hh, &c, &mm, &c, &ss, &c, &ms);
529 m_toff = n == 1
530 ? hh * (fNegative ? -1 : 1)
531 : n == 4+3
532 ? (hh*60*60*1000 + mm*60*1000 + ss*1000 + ms) * (fNegative ? -1 : 1)
533 : fError = true, 0;
535 else if(entry == _T("forced subs"))
537 str.MakeLower();
539 if(str.Find(_T("on")) >= 0 || str.Find(_T("1")) >= 0) m_fOnlyShowForcedSubs = true;
540 else if(str.Find(_T("off")) >= 0 || str.Find(_T("0")) >= 0) m_fOnlyShowForcedSubs = false;
541 else fError = true;
543 else if(entry == _T("langidx"))
545 if(_stscanf(str, _T("%d"), &m_iLang) != 1) fError = true;
547 else if(entry == _T("palette"))
549 if(_stscanf(str, _T("%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x"),
550 &m_orgpal[0], &m_orgpal[1], &m_orgpal[2], &m_orgpal[3],
551 &m_orgpal[4], &m_orgpal[5], &m_orgpal[6], &m_orgpal[7],
552 &m_orgpal[8], &m_orgpal[9], &m_orgpal[10], &m_orgpal[11],
553 &m_orgpal[12], &m_orgpal[13], &m_orgpal[14], &m_orgpal[15]
554 ) != 16) fError = true;
556 else if(entry == _T("custom colors"))
558 str.MakeLower();
560 if(str.Find(_T("on")) == 0 || str.Find(_T("1")) == 0) m_fCustomPal = true;
561 else if(str.Find(_T("off")) == 0 || str.Find(_T("0")) == 0) m_fCustomPal = false;
562 else fError = true;
564 i = str.Find(_T("tridx:"));
565 if(i < 0) {fError = true; continue;}
566 str = str.Mid(i + (int)_tcslen(_T("tridx:")));
568 int tridx;
569 if(_stscanf(str, _T("%x"), &tridx) != 1) {fError = true; continue;}
570 tridx = ((tridx&0x1000)>>12) | ((tridx&0x100)>>7) | ((tridx&0x10)>>2) | ((tridx&1)<<3);
572 i = str.Find(_T("colors:"));
573 if(i < 0) {fError = true; continue;}
574 str = str.Mid(i + (int)_tcslen(_T("colors:")));
576 RGBQUAD pal[4];
577 if(_stscanf(str, _T("%x,%x,%x,%x"), &pal[0], &pal[1], &pal[2], &pal[3]) != 4) {fError = true; continue;}
579 SetCustomPal(pal, tridx);
581 else if(entry == _T("id"))
583 str.MakeLower();
585 int langid = ((str[0]&0xff)<<8)|(str[1]&0xff);
587 i = str.Find(_T("index:"));
588 if(i < 0) {fError = true; continue;}
589 str = str.Mid(i + (int)_tcslen(_T("index:")));
591 if(_stscanf(str, _T("%d"), &id) != 1 || id < 0 || id >= 32) {fError = true; continue;}
593 m_langs[id].id = langid;
594 m_langs[id].name = lang_tbl[find_lang(langid)].lang_long;
595 m_langs[id].alt = lang_tbl[find_lang(langid)].lang_long;
597 delay = 0;
599 else if(id >= 0 && entry == _T("alt"))
601 m_langs[id].alt = str;
603 else if(id >= 0 && entry == _T("delay"))
605 bool fNegative = false;
606 if(str[0] == '-') fNegative = true;
607 str.TrimLeft(_T("+-"));
609 TCHAR c;
610 int hh, mm, ss, ms;
611 if(_stscanf(str, _T("%d%c%d%c%d%c%d"), &hh, &c, &mm, &c, &ss, &c, &ms) != 4+3) {fError = true; continue;}
613 delay += (hh*60*60*1000 + mm*60*1000 + ss*1000 + ms) * (fNegative ? -1 : 1);
615 else if(id >= 0 && entry == _T("timestamp"))
617 SubPos sb;
619 sb.vobid = vobid;
620 sb.cellid = cellid;
621 sb.celltimestamp = celltimestamp;
622 sb.fValid = true;
624 bool fNegative = false;
625 if(str[0] == '-') fNegative = true;
626 str.TrimLeft(_T("+-"));
628 TCHAR c;
629 int hh, mm, ss, ms;
630 if(_stscanf(str, _T("%d%c%d%c%d%c%d"), &hh, &c, &mm, &c, &ss, &c, &ms) != 4+3) {fError = true; continue;}
632 sb.start = (hh*60*60*1000 + mm*60*1000 + ss*1000 + ms) * (fNegative ? -1 : 1) + delay;
634 i = str.Find(_T("filepos:"));
635 if(i < 0) {fError = true; continue;}
636 str = str.Mid(i + (int)_tcslen(_T("filepos:")));
638 if(_stscanf(str, _T("%I64x"), &sb.filepos) != 1) {fError = true; continue;}
640 if(delay < 0 && m_langs[id].subpos.GetCount() > 0)
642 __int64 ts = m_langs[id].subpos[m_langs[id].subpos.GetCount()-1].start;
644 if(sb.start < ts)
646 delay += (int)(ts - sb.start);
647 sb.start = ts;
651 m_langs[id].subpos.Add(sb);
653 else fError = true;
656 return(!fError);
659 bool CVobSubFile::ReadSub(CString fn)
661 CFile f;
662 if(!f.Open(fn, CFile::modeRead|CFile::typeBinary|CFile::shareDenyWrite))
663 return(false);
665 m_sub.SetLength(f.GetLength());
666 m_sub.SeekToBegin();
668 int len;
669 BYTE buff[2048];
670 while((len = f.Read(buff, sizeof(buff))) > 0 && *(DWORD*)buff == 0xba010000)
671 m_sub.Write(buff, len);
673 return(true);
676 static unsigned char* RARbuff = NULL;
677 static unsigned int RARpos = 0;
679 static int PASCAL MyProcessDataProc(unsigned char* Addr, int Size)
681 ASSERT(RARbuff);
683 memcpy(&RARbuff[RARpos], Addr, Size);
684 RARpos += Size;
686 return(1);
689 bool CVobSubFile::ReadRar(CString fn)
691 HMODULE h = LoadLibrary(_T("unrar.dll"));
692 if(!h) return(false);
694 RAROpenArchiveEx OpenArchiveEx = (RAROpenArchiveEx)GetProcAddress(h, "RAROpenArchiveEx");
695 RARCloseArchive CloseArchive = (RARCloseArchive)GetProcAddress(h, "RARCloseArchive");
696 RARReadHeaderEx ReadHeaderEx = (RARReadHeaderEx)GetProcAddress(h, "RARReadHeaderEx");
697 RARProcessFile ProcessFile = (RARProcessFile)GetProcAddress(h, "RARProcessFile");
698 RARSetChangeVolProc SetChangeVolProc = (RARSetChangeVolProc)GetProcAddress(h, "RARSetChangeVolProc");
699 RARSetProcessDataProc SetProcessDataProc = (RARSetProcessDataProc)GetProcAddress(h, "RARSetProcessDataProc");
700 RARSetPassword SetPassword = (RARSetPassword)GetProcAddress(h, "RARSetPassword");
702 if(!(OpenArchiveEx && CloseArchive && ReadHeaderEx && ProcessFile
703 && SetChangeVolProc && SetProcessDataProc && SetPassword))
705 FreeLibrary(h);
706 return(false);
709 struct RAROpenArchiveDataEx ArchiveDataEx;
710 memset(&ArchiveDataEx, 0, sizeof(ArchiveDataEx));
711 #ifdef UNICODE
712 ArchiveDataEx.ArcNameW = (LPTSTR)(LPCTSTR)fn;
713 char fnA[MAX_PATH];
714 if(wcstombs(fnA, fn, fn.GetLength()+1) == -1) fnA[0] = 0;
715 ArchiveDataEx.ArcName = fnA;
716 #else
717 ArchiveDataEx.ArcName = (LPTSTR)(LPCTSTR)fn;
718 #endif
719 ArchiveDataEx.OpenMode = RAR_OM_EXTRACT;
720 ArchiveDataEx.CmtBuf = 0;
721 HANDLE hrar = OpenArchiveEx(&ArchiveDataEx);
722 if(!hrar)
724 FreeLibrary(h);
725 return(false);
728 SetProcessDataProc(hrar, MyProcessDataProc);
730 struct RARHeaderDataEx HeaderDataEx;
731 HeaderDataEx.CmtBuf = NULL;
733 while(ReadHeaderEx(hrar, &HeaderDataEx) == 0)
735 #ifdef UNICODE
736 CString subfn(HeaderDataEx.FileNameW);
737 #else
738 CString subfn(HeaderDataEx.FileName);
739 #endif
741 if(!subfn.Right(4).CompareNoCase(_T(".sub")))
743 CAutoVectorPtr<BYTE> buff;
744 if(!buff.Allocate(HeaderDataEx.UnpSize))
746 CloseArchive(hrar);
747 FreeLibrary(h);
748 return(false);
751 RARbuff = buff;
752 RARpos = 0;
754 if(ProcessFile(hrar, RAR_TEST, NULL, NULL))
756 CloseArchive(hrar);
757 FreeLibrary(h);
759 return(false);
762 m_sub.SetLength(HeaderDataEx.UnpSize);
763 m_sub.SeekToBegin();
764 m_sub.Write(buff, HeaderDataEx.UnpSize);
765 m_sub.SeekToBegin();
767 RARbuff = NULL;
768 RARpos = 0;
770 break;
773 ProcessFile(hrar, RAR_SKIP, NULL, NULL);
776 CloseArchive(hrar);
777 FreeLibrary(h);
779 return(true);
782 #define ReadBEdw(var) \
783 f.Read(&((BYTE*)&var)[3], 1); \
784 f.Read(&((BYTE*)&var)[2], 1); \
785 f.Read(&((BYTE*)&var)[1], 1); \
786 f.Read(&((BYTE*)&var)[0], 1); \
788 bool CVobSubFile::ReadIfo(CString fn)
790 CFile f;
791 if(!f.Open(fn, CFile::modeRead|CFile::typeBinary|CFile::shareDenyWrite))
792 return(false);
794 /* PGC1 */
796 f.Seek(0xc0+0x0c, SEEK_SET);
798 DWORD pos;
799 ReadBEdw(pos);
801 f.Seek(pos*0x800 + 0x0c, CFile::begin);
803 DWORD offset;
804 ReadBEdw(offset);
806 /* Subpic palette */
808 f.Seek(pos*0x800 + offset + 0xa4, CFile::begin);
810 for(int i = 0; i < 16; i++)
812 BYTE y, u, v, tmp;
814 f.Read(&tmp, 1);
815 f.Read(&y, 1);
816 f.Read(&u, 1);
817 f.Read(&v, 1);
819 y = (y-16)*255/219;
821 m_orgpal[i].rgbRed = (BYTE)min(max(1.0*y + 1.4022*(u-128), 0), 255);
822 m_orgpal[i].rgbGreen = (BYTE)min(max(1.0*y - 0.3456*(u-128) - 0.7145*(v-128), 0), 255);
823 m_orgpal[i].rgbBlue = (BYTE)min(max(1.0*y + 1.7710*(v-128), 0) , 255);
826 return(true);
829 bool CVobSubFile::WriteIdx(CString fn)
831 CTextFile f;
832 if(!f.Save(fn, CTextFile::ASCII))
833 return(false);
835 CString str;
836 str.Format(_T("# VobSub index file, v%d (do not modify this line!)\n"), VOBSUBIDXVER);
838 f.WriteString(str);
839 f.WriteString(_T("# \n"));
840 f.WriteString(_T("# To repair desyncronization, you can insert gaps this way:\n"));
841 f.WriteString(_T("# (it usually happens after vob id changes)\n"));
842 f.WriteString(_T("# \n"));
843 f.WriteString(_T("#\t delay: [sign]hh:mm:ss:ms\n"));
844 f.WriteString(_T("# \n"));
845 f.WriteString(_T("# Where:\n"));
846 f.WriteString(_T("#\t [sign]: +, - (optional)\n"));
847 f.WriteString(_T("#\t hh: hours (0 <= hh)\n"));
848 f.WriteString(_T("#\t mm/ss: minutes/seconds (0 <= mm/ss <= 59)\n"));
849 f.WriteString(_T("#\t ms: milliseconds (0 <= ms <= 999)\n"));
850 f.WriteString(_T("# \n"));
851 f.WriteString(_T("#\t Note: You can't position a sub before the previous with a negative value.\n"));
852 f.WriteString(_T("# \n"));
853 f.WriteString(_T("# You can also modify timestamps or delete a few subs you don't like.\n"));
854 f.WriteString(_T("# Just make sure they stay in increasing order.\n"));
855 f.WriteString(_T("\n"));
856 f.WriteString(_T("\n"));
858 // Settings
860 f.WriteString(_T("# Settings\n\n"));
862 f.WriteString(_T("# Original frame size\n"));
863 str.Format(_T("size: %dx%d\n\n"), m_size.cx, m_size.cy);
864 f.WriteString(str);
866 f.WriteString(_T("# Origin, relative to the upper-left corner, can be overloaded by aligment\n"));
867 str.Format(_T("org: %d, %d\n\n"), m_x, m_y);
868 f.WriteString(str);
870 f.WriteString(_T("# Image scaling (hor,ver), origin is at the upper-left corner or at the alignment coord (x, y)\n"));
871 str.Format(_T("scale: %d%%, %d%%\n\n"), m_scale_x, m_scale_y);
872 f.WriteString(str);
874 f.WriteString(_T("# Alpha blending\n"));
875 str.Format(_T("alpha: %d%%\n\n"), m_alpha);
876 f.WriteString(str);
878 f.WriteString(_T("# Smoothing for very blocky images (use OLD for no filtering)\n"));
879 str.Format(_T("smooth: %s\n\n"), m_fSmooth == 0 ? _T("OFF") : m_fSmooth == 1 ? _T("ON") : _T("OLD"));
880 f.WriteString(str);
882 f.WriteString(_T("# In millisecs\n"));
883 str.Format(_T("fadein/out: %d, %d\n\n"), m_fadein, m_fadeout);
884 f.WriteString(str);
886 f.WriteString(_T("# Force subtitle placement relative to (org.x, org.y)\n"));
887 str.Format(_T("align: %s %s %s\n\n"),
888 m_fAlign ? _T("ON at") : _T("OFF at"),
889 m_alignhor == 0 ? _T("LEFT") : m_alignhor == 1 ? _T("CENTER") : m_alignhor == 2 ? _T("RIGHT") : _T(""),
890 m_alignver == 0 ? _T("TOP") : m_alignver == 1 ? _T("CENTER") : m_alignver == 2 ? _T("BOTTOM") : _T(""));
891 f.WriteString(str);
893 f.WriteString(_T("# For correcting non-progressive desync. (in millisecs or hh:mm:ss:ms)\n"));
894 f.WriteString(_T("# Note: Not effective in DirectVobSub, use \"delay: ... \" instead.\n"));
895 str.Format(_T("time offset: %d\n\n"), m_toff);
896 f.WriteString(str);
898 f.WriteString(_T("# ON: displays only forced subtitles, OFF: shows everything\n"));
899 str.Format(_T("forced subs: %s\n\n"), m_fOnlyShowForcedSubs ? _T("ON") : _T("OFF"));
900 f.WriteString(str);
902 f.WriteString(_T("# The original palette of the DVD\n"));
903 str.Format(_T("palette: %06x, %06x, %06x, %06x, %06x, %06x, %06x, %06x, %06x, %06x, %06x, %06x, %06x, %06x, %06x, %06x\n\n"),
904 *((unsigned int*)&m_orgpal[0])&0xffffff,
905 *((unsigned int*)&m_orgpal[1])&0xffffff,
906 *((unsigned int*)&m_orgpal[2])&0xffffff,
907 *((unsigned int*)&m_orgpal[3])&0xffffff,
908 *((unsigned int*)&m_orgpal[4])&0xffffff,
909 *((unsigned int*)&m_orgpal[5])&0xffffff,
910 *((unsigned int*)&m_orgpal[6])&0xffffff,
911 *((unsigned int*)&m_orgpal[7])&0xffffff,
912 *((unsigned int*)&m_orgpal[8])&0xffffff,
913 *((unsigned int*)&m_orgpal[9])&0xffffff,
914 *((unsigned int*)&m_orgpal[10])&0xffffff,
915 *((unsigned int*)&m_orgpal[11])&0xffffff,
916 *((unsigned int*)&m_orgpal[12])&0xffffff,
917 *((unsigned int*)&m_orgpal[13])&0xffffff,
918 *((unsigned int*)&m_orgpal[14])&0xffffff,
919 *((unsigned int*)&m_orgpal[15])&0xffffff);
920 f.WriteString(str);
922 int tridx = (!!(m_tridx&1))*0x1000 + (!!(m_tridx&2))*0x100 + (!!(m_tridx&4))*0x10 + (!!(m_tridx&8));
924 f.WriteString(_T("# Custom colors (transp idxs and the four colors)\n"));
925 str.Format(_T("custom colors: %s, tridx: %04x, colors: %06x, %06x, %06x, %06x\n\n"),
926 m_fCustomPal ? _T("ON") : _T("OFF"),
927 tridx,
928 *((unsigned int*)&m_cuspal[0])&0xffffff,
929 *((unsigned int*)&m_cuspal[1])&0xffffff,
930 *((unsigned int*)&m_cuspal[2])&0xffffff,
931 *((unsigned int*)&m_cuspal[3])&0xffffff);
932 f.WriteString(str);
934 f.WriteString(_T("# Language index in use\n"));
935 str.Format(_T("langidx: %d\n\n"), m_iLang);
936 f.WriteString(str);
938 // Subs
940 for(int i = 0; i < 32; i++)
942 SubLang& sl = m_langs[i];
944 CAtlArray<SubPos>& sp = sl.subpos;
945 if(sp.IsEmpty() && !sl.id) continue;
947 str.Format(_T("# %s\n"), sl.name);
948 f.WriteString(str);
950 ASSERT(sl.id);
951 if(!sl.id) sl.id = '--';
952 str.Format(_T("id: %c%c, index: %d\n"), sl.id>>8, sl.id&0xff, i);
953 f.WriteString(str);
955 str.Format(_T("# Decomment next line to activate alternative name in DirectVobSub / Windows Media Player 6.x\n"));
956 f.WriteString(str);
957 str.Format(_T("alt: %s\n"), sl.alt);
958 if(sl.name == sl.alt) str = _T("# ") + str;
959 f.WriteString(str);
961 char vobid = -1, cellid = -1;
963 for(size_t j = 0; j < sp.GetCount(); j++)
965 if(!sp[j].fValid) continue;
967 if(sp[j].vobid != vobid || sp[j].cellid != cellid)
969 str.Format(_T("# Vob/Cell ID: %d, %d (PTS: %d)\n"), sp[j].vobid, sp[j].cellid, sp[j].celltimestamp);
970 f.WriteString(str);
971 vobid = sp[j].vobid;
972 cellid = sp[j].cellid;
975 str.Format(_T("timestamp: %s%02d:%02d:%02d:%03d, filepos: %09I64x\n"),
976 sp[j].start < 0 ? _T("-") : _T(""),
977 abs(int((sp[j].start/1000/60/60)%60)),
978 abs(int((sp[j].start/1000/60)%60)),
979 abs(int((sp[j].start/1000)%60)),
980 abs(int((sp[j].start)%1000)),
981 sp[j].filepos);
982 f.WriteString(str);
985 f.WriteString(_T("\n"));
988 return(true);
991 bool CVobSubFile::WriteSub(CString fn)
993 CFile f;
994 if(!f.Open(fn, CFile::modeCreate|CFile::modeWrite|CFile::typeBinary|CFile::shareDenyWrite))
995 return(false);
997 if(m_sub.GetLength() == 0)
998 return(true); // nothing to do...
1000 m_sub.SeekToBegin();
1002 int len;
1003 BYTE buff[2048];
1004 while((len = m_sub.Read(buff, sizeof(buff))) > 0 && *(DWORD*)buff == 0xba010000)
1005 f.Write(buff, len);
1007 return(true);
1012 BYTE* CVobSubFile::GetPacket(int idx, int& packetsize, int& datasize, int iLang)
1014 BYTE* ret = NULL;
1016 if(iLang < 0 || iLang >= 32) iLang = m_iLang;
1017 CAtlArray<SubPos>& sp = m_langs[iLang].subpos;
1021 if(idx < 0 || idx >= sp.GetCount())
1022 break;
1024 if(m_sub.Seek(sp[idx].filepos, CFile::begin) != sp[idx].filepos)
1025 break;
1027 BYTE buff[0x800];
1028 if(sizeof(buff) != m_sub.Read(buff, sizeof(buff)))
1029 break;
1031 BYTE offset = buff[0x16];
1033 // let's check a few things to make sure...
1034 if(*(DWORD*)&buff[0x00] != 0xba010000
1035 || *(DWORD*)&buff[0x0e] != 0xbd010000
1036 || !(buff[0x15] & 0x80)
1037 || (buff[0x17] & 0xf0) != 0x20
1038 || (buff[buff[0x16] + 0x17] & 0xe0) != 0x20
1039 || (buff[buff[0x16] + 0x17] & 0x1f) != iLang)
1040 break;
1042 packetsize = (buff[buff[0x16] + 0x18] << 8) + buff[buff[0x16] + 0x19];
1043 datasize = (buff[buff[0x16] + 0x1a] << 8) + buff[buff[0x16] + 0x1b];
1045 ret = new BYTE[packetsize];
1046 if(!ret) break;
1048 int i = 0, sizeleft = packetsize;
1049 for(int size;
1050 i < packetsize;
1051 i += size, sizeleft -= size)
1053 int hsize = 0x18 + buff[0x16];
1054 size = min(sizeleft, 0x800 - hsize);
1055 memcpy(&ret[i], &buff[hsize], size);
1057 if(size != sizeleft)
1059 while(m_sub.Read(buff, sizeof(buff)))
1061 if(/*!(buff[0x15] & 0x80) &&*/ buff[buff[0x16] + 0x17] == (iLang|0x20))
1062 break;
1067 if(i != packetsize || sizeleft > 0)
1068 delete [] ret, ret = NULL;
1070 while(false);
1072 return(ret);
1075 bool CVobSubFile::GetFrame(int idx, int iLang)
1077 if(iLang < 0 || iLang >= 32) iLang = m_iLang;
1078 CAtlArray<SubPos>& sp = m_langs[iLang].subpos;
1080 if(idx < 0 || idx >= sp.GetCount())
1081 return(false);
1083 if(m_img.iLang != iLang || m_img.iIdx != idx)
1085 int packetsize = 0, datasize = 0;
1086 CAutoVectorPtr<BYTE> buff;
1087 buff.Attach(GetPacket(idx, packetsize, datasize, iLang));
1088 if(!buff || packetsize <= 0 || datasize <= 0) return(false);
1090 m_img.start = sp[idx].start;
1091 m_img.delay = idx < (sp.GetCount()-1)
1092 ? sp[idx+1].start - sp[idx].start
1093 : 3000;
1095 bool ret = m_img.Decode(buff, packetsize, datasize, m_fCustomPal, m_tridx, m_orgpal, m_cuspal, true);
1097 if(idx < (sp.GetCount()-1))
1098 m_img.delay = min(m_img.delay, sp[idx+1].start - m_img.start);
1100 if(!ret) return(false);
1102 m_img.iIdx = idx;
1103 m_img.iLang = iLang;
1106 return(m_fOnlyShowForcedSubs ? m_img.fForced : true);
1109 bool CVobSubFile::GetFrameByTimeStamp(__int64 time)
1111 return(GetFrame(GetFrameIdxByTimeStamp(time)));
1114 int CVobSubFile::GetFrameIdxByTimeStamp(__int64 time)
1116 if(m_iLang < 0 || m_iLang >= 32)
1117 return(-1);
1119 CAtlArray<SubPos>& sp = m_langs[m_iLang].subpos;
1121 int i = 0, j = (int)sp.GetCount() - 1, ret = -1;
1123 if(j >= 0 && time >= sp[j].start)
1124 return(j);
1126 while(i < j)
1128 int mid = (i + j) >> 1;
1129 int midstart = (int)sp[mid].start;
1131 if(time == midstart) {ret = mid; break;}
1132 else if(time < midstart) {ret = -1; if(j == mid) mid--; j = mid;}
1133 else if(time > midstart) {ret = mid; if(i == mid) mid++; i = mid;}
1136 return(ret);
1141 STDMETHODIMP CVobSubFile::NonDelegatingQueryInterface(REFIID riid, void** ppv)
1143 CheckPointer(ppv, E_POINTER);
1144 *ppv = NULL;
1146 return
1147 QI(IPersist)
1148 QI(ISubStream)
1149 QI(ISubPicProvider)
1150 __super::NonDelegatingQueryInterface(riid, ppv);
1153 // ISubPicProvider
1155 // TODO: return segments for the fade-in/out time (with animated set to "true" of course)
1157 STDMETHODIMP_(POSITION) CVobSubFile::GetStartPosition(REFERENCE_TIME rt, double fps)
1159 rt /= 10000;
1161 int i = GetFrameIdxByTimeStamp(rt);
1163 if(!GetFrame(i))
1164 return(NULL);
1166 if(rt >= (m_img.start + m_img.delay))
1168 if(!GetFrame(++i))
1169 return(NULL);
1172 return((POSITION)(i+1));
1175 STDMETHODIMP_(POSITION) CVobSubFile::GetNext(POSITION pos)
1177 int i = (int)pos;
1178 return(GetFrame(i) ? (POSITION)(i+1) : NULL);
1181 STDMETHODIMP_(REFERENCE_TIME) CVobSubFile::GetStart(POSITION pos, double fps)
1183 int i = (int)pos-1;
1184 return(GetFrame(i) ? 10000i64*m_img.start : 0);
1187 STDMETHODIMP_(REFERENCE_TIME) CVobSubFile::GetStop(POSITION pos, double fps)
1189 int i = (int)pos-1;
1190 return(GetFrame(i) ? 10000i64*(m_img.start + m_img.delay) : 0);
1193 STDMETHODIMP_(bool) CVobSubFile::IsAnimated(POSITION pos)
1195 return(false);
1198 STDMETHODIMP CVobSubFile::Render(SubPicDesc& spd, REFERENCE_TIME rt, double fps, RECT& bbox)
1200 if(spd.bpp != 32) return E_INVALIDARG;
1202 rt /= 10000;
1204 if(!GetFrame(GetFrameIdxByTimeStamp(rt)))
1205 return E_FAIL;
1207 if(rt >= (m_img.start + m_img.delay))
1208 return E_FAIL;
1210 return __super::Render(spd, bbox);
1213 // IPersist
1215 STDMETHODIMP CVobSubFile::GetClassID(CLSID* pClassID)
1217 return pClassID ? *pClassID = __uuidof(this), S_OK : E_POINTER;
1220 // ISubStream
1222 STDMETHODIMP_(int) CVobSubFile::GetStreamCount()
1224 int iStreamCount = 0;
1225 for(int i = 0; i < 32; i++)
1226 if(m_langs[i].subpos.GetCount()) iStreamCount++;
1227 return(iStreamCount);
1230 STDMETHODIMP CVobSubFile::GetStreamInfo(int iStream, WCHAR** ppName, LCID* pLCID)
1232 for(int i = 0; i < 32; i++)
1234 SubLang& sl = m_langs[i];
1236 if(sl.subpos.IsEmpty() || iStream-- > 0)
1237 continue;
1239 if(ppName)
1241 if(!(*ppName = (WCHAR*)CoTaskMemAlloc((sl.alt.GetLength()+1)*sizeof(WCHAR))))
1242 return E_OUTOFMEMORY;
1244 wcscpy(*ppName, CStringW(sl.alt));
1247 if(pLCID)
1249 *pLCID = 0; // TODO: make lcid out of "sl.id"
1252 return S_OK;
1255 return E_FAIL;
1258 STDMETHODIMP_(int) CVobSubFile::GetStream()
1260 int iStream = 0;
1262 for(int i = 0; i < m_iLang; i++)
1263 if(!m_langs[i].subpos.IsEmpty()) iStream++;
1265 return(iStream);
1268 STDMETHODIMP CVobSubFile::SetStream(int iStream)
1270 for(int i = 0; i < 32; i++)
1272 CAtlArray<SubPos>& sp = m_langs[i].subpos;
1274 if(sp.IsEmpty() || iStream-- > 0)
1275 continue;
1277 m_iLang = i;
1279 m_img.Invalidate();
1281 break;
1284 return iStream < 0 ? S_OK : E_FAIL;
1287 STDMETHODIMP CVobSubFile::Reload()
1289 CFileStatus s;
1290 if(!CFile::GetStatus(m_title + _T(".idx"), s)) return E_FAIL;
1291 return !m_title.IsEmpty() && Open(m_title) ? S_OK : E_FAIL;
1294 // StretchBlt
1296 static void PixelAtBiLinear(RGBQUAD& c, int x, int y, CVobSubImage& src)
1298 int w = src.rect.Width(),
1299 h = src.rect.Height();
1301 int x1 = (x >> 16), y1 = (y >> 16) * w,
1302 x2 = min(x1 + 1, w-1), y2 = min(y1 + w, (h-1)*w);
1304 RGBQUAD* ptr = src.lpPixels;
1306 RGBQUAD c11 = ptr[y1 + x1], c12 = ptr[y1 + x2],
1307 c21 = ptr[y2 + x1], c22 = ptr[y2 + x2];
1309 __int64 u2 = x & 0xffff,
1310 v2 = y & 0xffff,
1311 u1 = 0x10000 - u2,
1312 v1 = 0x10000 - v2;
1314 int v1u1 = int(v1*u1 >> 16) * c11.rgbReserved,
1315 v1u2 = int(v1*u2 >> 16) * c12.rgbReserved,
1316 v2u1 = int(v2*u1 >> 16) * c21.rgbReserved,
1317 v2u2 = int(v2*u2 >> 16) * c22.rgbReserved;
1319 c.rgbRed = (c11.rgbRed * v1u1 + c12.rgbRed * v1u2
1320 + c21.rgbRed * v2u1 + c22.rgbRed * v2u2) >> 24;
1321 c.rgbGreen = (c11.rgbGreen * v1u1 + c12.rgbGreen * v1u2
1322 + c21.rgbGreen * v2u1 + c22.rgbGreen * v2u2) >> 24;
1323 c.rgbBlue = (c11.rgbBlue * v1u1 + c12.rgbBlue * v1u2
1324 + c21.rgbBlue * v2u1 + c22.rgbBlue * v2u2) >> 24;
1325 c.rgbReserved = (v1u1 + v1u2
1326 + v2u1 + v2u2) >> 16;
1329 static void StretchBlt(SubPicDesc& spd, CRect dstrect, CVobSubImage& src)
1331 if(dstrect.IsRectEmpty()) return;
1333 if((dstrect & CRect(0, 0, spd.w, spd.h)).IsRectEmpty()) return;
1335 int sw = src.rect.Width(),
1336 sh = src.rect.Height(),
1337 dw = dstrect.Width(),
1338 dh = dstrect.Height();
1340 int srcx = 0,
1341 srcy = 0,
1342 srcdx = (sw << 16) / dw >> 1,
1343 srcdy = (sh << 16) / dh >> 1;
1345 if(dstrect.left < 0) {srcx = -dstrect.left * (srcdx<<1); dstrect.left = 0;}
1346 if(dstrect.top < 0) {srcy = -dstrect.top * (srcdy<<1); dstrect.top = 0;}
1347 if(dstrect.right > spd.w) {dstrect.right = spd.w;}
1348 if(dstrect.bottom > spd.h) {dstrect.bottom = spd.h;}
1350 if((dstrect & CRect(0, 0, spd.w, spd.h)).IsRectEmpty()) return;
1352 dw = dstrect.Width();
1353 dh = dstrect.Height();
1355 for(int y = dstrect.top; y < dstrect.bottom; y++, srcy += (srcdy<<1))
1357 RGBQUAD* ptr = (RGBQUAD*)&((BYTE*)spd.bits)[y*spd.pitch] + dstrect.left;
1358 RGBQUAD* endptr = ptr + dw;
1360 for(int sx = srcx; ptr < endptr; sx += (srcdx<<1), ptr++)
1362 // PixelAtBiLinear(*ptr, sx, srcy, src);
1363 ////
1364 RGBQUAD cc[4];
1366 PixelAtBiLinear(cc[0], sx, srcy, src);
1367 PixelAtBiLinear(cc[1], sx+srcdx, srcy, src);
1368 PixelAtBiLinear(cc[2], sx, srcy+srcdy, src);
1369 PixelAtBiLinear(cc[3], sx+srcdx, srcy+srcdy, src);
1371 ptr->rgbRed = (cc[0].rgbRed + cc[1].rgbRed + cc[2].rgbRed + cc[3].rgbRed) >> 2;
1372 ptr->rgbGreen = (cc[0].rgbGreen + cc[1].rgbGreen + cc[2].rgbGreen + cc[3].rgbGreen) >> 2;
1373 ptr->rgbBlue = (cc[0].rgbBlue + cc[1].rgbBlue + cc[2].rgbBlue + cc[3].rgbBlue) >> 2;
1374 ptr->rgbReserved = (cc[0].rgbReserved + cc[1].rgbReserved + cc[2].rgbReserved + cc[3].rgbReserved) >> 2;
1375 ////
1376 ptr->rgbRed = ptr->rgbRed * ptr->rgbReserved >> 8;
1377 ptr->rgbGreen = ptr->rgbGreen * ptr->rgbReserved >> 8;
1378 ptr->rgbBlue = ptr->rgbBlue * ptr->rgbReserved >> 8;
1379 ptr->rgbReserved = ~ptr->rgbReserved;
1386 // CVobSubSettings
1389 void CVobSubSettings::InitSettings()
1391 m_size.SetSize(720, 480);
1392 m_toff = m_x = m_y = 0;
1393 m_org.SetPoint(0, 0);
1394 m_scale_x = m_scale_y = m_alpha = 100;
1395 m_fadein = m_fadeout = 50;
1396 m_fSmooth = 0;
1397 m_fAlign = false;
1398 m_alignhor = m_alignver = 0;
1399 m_fOnlyShowForcedSubs = false;
1400 m_fCustomPal = false;
1401 m_tridx = 0;
1402 memset(m_orgpal, 0, sizeof(m_orgpal));
1403 memset(m_cuspal, 0, sizeof(m_cuspal));
1406 bool CVobSubSettings::GetCustomPal(RGBQUAD* cuspal, int& tridx)
1408 memcpy(cuspal, m_cuspal, sizeof(RGBQUAD)*4);
1409 tridx = m_tridx;
1410 return(m_fCustomPal);
1413 void CVobSubSettings::SetCustomPal(RGBQUAD* cuspal, int tridx)
1415 memcpy(m_cuspal, cuspal, sizeof(RGBQUAD)*4);
1416 m_tridx = tridx & 0xf;
1417 for(int i = 0; i < 4; i++) m_cuspal[i].rgbReserved = (tridx&(1<<i)) ? 0 : 0xff;
1418 m_img.Invalidate();
1421 void CVobSubSettings::GetDestrect(CRect& r)
1423 int w = MulDiv(m_img.rect.Width(), m_scale_x, 100);
1424 int h = MulDiv(m_img.rect.Height(), m_scale_y, 100);
1426 if(!m_fAlign)
1428 r.left = MulDiv(m_img.rect.left, m_scale_x, 100);
1429 r.right = MulDiv(m_img.rect.right, m_scale_x, 100);
1430 r.top = MulDiv(m_img.rect.top, m_scale_y, 100);
1431 r.bottom = MulDiv(m_img.rect.bottom, m_scale_y, 100);
1433 else
1435 switch(m_alignhor)
1437 case 0: r.left = 0; r.right = w; break; // left
1438 case 1: r.left = -(w>>1); r.right = -(w>>1) + w; break; // center
1439 case 2: r.left = -w; r.right = 0; break; // right
1440 default:
1441 r.left = MulDiv(m_img.rect.left, m_scale_x, 100);
1442 r.right = MulDiv(m_img.rect.right, m_scale_x, 100);
1443 break;
1446 switch(m_alignver)
1448 case 0: r.top = 0; r.bottom = h; break; // top
1449 case 1: r.top = -(h>>1); r.bottom = -(h>>1) + h; break; // center
1450 case 2: r.top = -h; r.bottom = 0; break; // bottom
1451 default:
1452 r.top = MulDiv(m_img.rect.top, m_scale_y, 100);
1453 r.bottom = MulDiv(m_img.rect.bottom, m_scale_y, 100);
1454 break;
1458 r += m_org;
1461 void CVobSubSettings::GetDestrect(CRect& r, int w, int h)
1463 GetDestrect(r);
1465 r.left = MulDiv(r.left, w, m_size.cx);
1466 r.right = MulDiv(r.right, w, m_size.cx);
1467 r.top = MulDiv(r.top, h, m_size.cy);
1468 r.bottom = MulDiv(r.bottom, h, m_size.cy);
1471 void CVobSubSettings::SetAlignment(bool fAlign, int x, int y, int hor, int ver)
1473 if(m_fAlign = fAlign)
1475 m_org.x = MulDiv(m_size.cx, x, 100);
1476 m_org.y = MulDiv(m_size.cy, y, 100);
1477 m_alignhor = min(max(hor, 0), 2);
1478 m_alignver = min(max(ver, 0), 2);
1480 else
1482 m_org.x = m_x;
1483 m_org.y = m_y;
1487 #include "RTS.h"
1489 HRESULT CVobSubSettings::Render(SubPicDesc& spd, RECT& bbox)
1491 CRect r;
1492 GetDestrect(r, spd.w, spd.h);
1493 StretchBlt(spd, r, m_img);
1495 CRenderedTextSubtitle rts(NULL);
1496 rts.CreateDefaultStyle(DEFAULT_CHARSET);
1497 rts.m_dstScreenSize.SetSize(m_size.cx, m_size.cy);
1498 CStringW assstr;
1499 m_img.Polygonize(assstr, false);
1500 REFERENCE_TIME rtStart = 10000i64*m_img.start, rtStop = 10000i64*(m_img.start+m_img.delay);
1501 rts.Add(assstr, true, rtStart, rtStop);
1502 rts.Render(spd, (rtStart+rtStop)/2, 25, r);
1504 r &= CRect(CPoint(0, 0), CSize(spd.w, spd.h));
1505 bbox = r;
1506 return !r.IsRectEmpty() ? S_OK : S_FALSE;
1509 /////////////////////////////////////////////////////////
1511 static bool CompressFile(CString fn)
1513 if(GetVersion() < 0)
1514 return(false);
1516 BOOL b = FALSE;
1518 HANDLE h = CreateFile(fn, GENERIC_WRITE|GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0);
1519 if(h != INVALID_HANDLE_VALUE)
1521 USHORT us = COMPRESSION_FORMAT_DEFAULT;
1522 DWORD nBytesReturned;
1523 b = DeviceIoControl(h, FSCTL_SET_COMPRESSION, (LPVOID)&us, 2, NULL, 0, (LPDWORD)&nBytesReturned, NULL);
1524 CloseHandle(h);
1527 return(!!b);
1530 bool CVobSubFile::SaveVobSub(CString fn)
1532 return WriteIdx(fn + _T(".idx")) && WriteSub(fn + _T(".sub"));
1535 bool CVobSubFile::SaveWinSubMux(CString fn)
1537 TrimExtension(fn);
1539 CStdioFile f;
1540 if(!f.Open(fn + _T(".sub"), CFile::modeCreate|CFile::modeWrite|CFile::typeText|CFile::shareDenyWrite))
1541 return(false);
1543 m_img.Invalidate();
1545 CAutoVectorPtr<BYTE> p4bpp;
1546 if(!p4bpp.Allocate(720*576/2))
1547 return(false);
1549 CAtlArray<SubPos>& sp = m_langs[m_iLang].subpos;
1550 for(int i = 0; i < sp.GetCount(); i++)
1552 if(!GetFrame(i)) continue;
1554 int pal[4] = {0, 1, 2, 3};
1556 for(int j = 0; j < 5; j++)
1558 if(j == 4 || !m_img.pal[j].tr)
1560 j &= 3;
1561 memset(p4bpp, (j<<4)|j, 720*576/2);
1562 pal[j] ^= pal[0], pal[0] ^= pal[j], pal[j] ^= pal[0];
1563 break;
1567 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};
1569 DWORD uipal[4+12];
1571 if(!m_fCustomPal)
1573 uipal[0] = *((DWORD*)&m_img.orgpal[m_img.pal[pal[0]].pal]);
1574 uipal[1] = *((DWORD*)&m_img.orgpal[m_img.pal[pal[1]].pal]);
1575 uipal[2] = *((DWORD*)&m_img.orgpal[m_img.pal[pal[2]].pal]);
1576 uipal[3] = *((DWORD*)&m_img.orgpal[m_img.pal[pal[3]].pal]);
1578 else
1580 uipal[0] = *((DWORD*)&m_img.cuspal[pal[0]]) & 0xffffff;
1581 uipal[1] = *((DWORD*)&m_img.cuspal[pal[1]]) & 0xffffff;
1582 uipal[2] = *((DWORD*)&m_img.cuspal[pal[2]]) & 0xffffff;
1583 uipal[3] = *((DWORD*)&m_img.cuspal[pal[3]]) & 0xffffff;
1586 CAtlMap<DWORD,BYTE> palmap;
1587 palmap[uipal[0]] = 0;
1588 palmap[uipal[1]] = 1;
1589 palmap[uipal[2]] = 2;
1590 palmap[uipal[3]] = 3;
1592 uipal[0] = 0xff; // blue background
1594 int w = m_img.rect.Width()-2;
1595 int h = m_img.rect.Height()-2;
1596 int pitch = (((w+1)>>1) + 3) & ~3;
1598 for(int y = 0; y < h; y++)
1600 DWORD* p = (DWORD*)&m_img.lpPixels[(y+1)*(w+2)+1];
1602 for(int x = 0; x < w; x++, p++)
1604 BYTE c = 0;
1606 if(*p & 0xff000000)
1608 DWORD uic = *p & 0xffffff;
1609 palmap.Lookup(uic, c);
1612 BYTE& c4bpp = p4bpp[(h-y-1)*pitch+(x>>1)];
1613 c4bpp = (x&1) ? ((c4bpp&0xf0)|c) : ((c4bpp&0x0f)|(c<<4));
1617 int t1 = m_img.start, t2 = t1 + m_img.delay /*+ (m_size.cy==480?(1000/29.97+1):(1000/25))*/;
1619 ASSERT(t2>t1);
1621 if(t2 <= 0) continue;
1622 if(t1 < 0) t1 = 0;
1624 CString bmpfn;
1625 bmpfn.Format(_T("%s_%06d.bmp"), fn, i+1);
1627 CString str;
1628 str.Format(_T("%s\t%02d:%02d:%02d:%02d %02d:%02d:%02d:%02d\t%03d %03d %03d %03d %d %d %d %d\n"),
1629 bmpfn,
1630 t1/1000/60/60, (t1/1000/60)%60, (t1/1000)%60, (t1%1000)/10,
1631 t2/1000/60/60, (t2/1000/60)%60, (t2/1000)%60, (t2%1000)/10,
1632 m_img.rect.Width(), m_img.rect.Height(), m_img.rect.left, m_img.rect.top,
1633 (tr[0]<<4)|tr[0], (tr[1]<<4)|tr[1], (tr[2]<<4)|tr[2], (tr[3]<<4)|tr[3]);
1634 f.WriteString(str);
1636 BITMAPFILEHEADER fhdr =
1638 0x4d42,
1639 sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + 16*sizeof(RGBQUAD) + pitch*h,
1640 0, 0,
1641 sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + 16*sizeof(RGBQUAD)
1644 BITMAPINFOHEADER ihdr =
1646 sizeof(BITMAPINFOHEADER),
1647 w, h, 1, 4, 0,
1649 pitch*h, 0,
1650 16, 4
1653 CFile bmp;
1654 if(bmp.Open(bmpfn, CFile::modeCreate|CFile::modeWrite|CFile::typeBinary|CFile::shareDenyWrite))
1656 bmp.Write(&fhdr, sizeof(fhdr));
1657 bmp.Write(&ihdr, sizeof(ihdr));
1658 bmp.Write(uipal, sizeof(RGBQUAD)*16);
1659 bmp.Write(p4bpp, pitch*h);
1660 bmp.Close();
1662 CompressFile(bmpfn);
1666 return(true);
1669 bool CVobSubFile::SaveScenarist(CString fn)
1671 TrimExtension(fn);
1673 CStdioFile f;
1674 if(!f.Open(fn + _T(".sst"), CFile::modeCreate|CFile::modeWrite|CFile::typeText|CFile::shareDenyWrite))
1675 return(false);
1677 m_img.Invalidate();
1679 fn.Replace('\\', '/');
1680 CString title = fn.Mid(fn.ReverseFind('/')+1);
1682 TCHAR buff[MAX_PATH], * pFilePart = buff;
1683 if(GetFullPathName(fn, MAX_PATH, buff, &pFilePart) == 0)
1684 return(false);
1686 CString fullpath = CString(buff).Left(pFilePart - buff);
1687 fullpath.TrimRight(_T("\\/"));
1688 if(fullpath.IsEmpty())
1689 return(false);
1691 CString str, str2;
1692 str += _T("st_format\t2\n");
1693 str += _T("Display_Start\t%s\n");
1694 str += _T("TV_Type\t\t%s\n");
1695 str += _T("Tape_Type\tNON_DROP\n");
1696 str += _T("Pixel_Area\t(0 %d)\n");
1697 str += _T("Directory\t%s\n");
1698 str += _T("Subtitle\t%s\n");
1699 str += _T("Display_Area\t(0 2 719 %d)\n");
1700 str += _T("Contrast\t(15 15 15 0)\n");
1701 str += _T("\n");
1702 str += _T("PA\t(0 0 255 - - - )\n");
1703 str += _T("E1\t(255 0 0 - - - )\n");
1704 str += _T("E2\t(0 0 0 - - - )\n");
1705 str += _T("BG\t(255 255 255 - - - )\n");
1706 str += _T("\n");
1707 str += _T("SP_NUMBER\tSTART\tEND\tFILE_NAME\n");
1708 str2.Format(str,
1709 !m_fOnlyShowForcedSubs ? _T("non_forced") : _T("forced"),
1710 m_size.cy == 480 ? _T("NTSC") : _T("PAL"),
1711 m_size.cy-3,
1712 fullpath,
1713 title,
1714 m_size.cy == 480 ? 479 : 574);
1716 f.WriteString(str2);
1718 f.Flush();
1720 RGBQUAD pal[16] =
1722 {255, 0, 0, 0},
1723 {0, 0, 255, 0},
1724 {0, 0, 0, 0},
1725 {255, 255, 255, 0},
1726 {0, 255, 0, 0},
1727 {255, 0, 255, 0},
1728 {0, 255, 255, 0},
1729 {125, 125, 0, 0},
1730 {125, 125, 125, 0},
1731 {225, 225, 225, 0},
1732 {0, 0, 125, 0},
1733 {0, 125, 0, 0},
1734 {125, 0, 0, 0},
1735 {255, 0, 222, 0},
1736 {0, 125, 222, 0},
1737 {125, 0, 125, 0},
1740 BITMAPFILEHEADER fhdr =
1742 0x4d42,
1743 sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + 16*sizeof(RGBQUAD) + 360*(m_size.cy-2),
1744 0, 0,
1745 sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + 16*sizeof(RGBQUAD)
1748 BITMAPINFOHEADER ihdr =
1750 sizeof(BITMAPINFOHEADER),
1751 720, m_size.cy-2, 1, 4, 0,
1752 360*(m_size.cy-2),
1753 0, 0,
1754 16, 4
1757 bool fCustomPal = m_fCustomPal;
1758 m_fCustomPal = true;
1759 RGBQUAD tempCusPal[4], newCusPal[4+12] = {{255, 0, 0, 0}, {0, 0, 255, 0}, {0, 0, 0, 0}, {255, 255, 255, 0}};
1760 memcpy(tempCusPal, m_cuspal, sizeof(tempCusPal));
1761 memcpy(m_cuspal, newCusPal, sizeof(m_cuspal));
1763 CAutoVectorPtr<BYTE> p4bpp;
1764 if(!p4bpp.Allocate((m_size.cy-2)*360))
1765 return(false);
1767 BYTE colormap[16];
1769 for(int i = 0; i < 16; i++)
1771 int idx = 0, maxdif = 255*255*3+1;
1773 for(int j = 0; j < 16 && maxdif; j++)
1775 int rdif = pal[j].rgbRed - m_orgpal[i].rgbRed;
1776 int gdif = pal[j].rgbGreen - m_orgpal[i].rgbGreen;
1777 int bdif = pal[j].rgbBlue - m_orgpal[i].rgbBlue;
1779 int dif = rdif*rdif + gdif*gdif + bdif*bdif;
1780 if(dif < maxdif) {maxdif = dif; idx = j;}
1783 colormap[i] = idx+1;
1786 int pc[4] = {1, 1, 1, 1}, pa[4] = {15, 15, 15, 0};
1788 CAtlArray<SubPos>& sp = m_langs[m_iLang].subpos;
1789 for(int i = 0, k = 0; i < sp.GetCount(); i++)
1791 if(!GetFrame(i)) continue;
1793 for(int j = 0; j < 5; j++)
1795 if(j == 4 || !m_img.pal[j].tr)
1797 j &= 3;
1798 memset(p4bpp, (j<<4)|j, (m_size.cy-2)*360);
1799 break;
1803 for(int y = max(m_img.rect.top+1, 2); y < m_img.rect.bottom-1; y++)
1805 ASSERT(m_size.cy-y-1 >= 0);
1806 if(m_size.cy-y-1 < 0) break;
1808 DWORD* p = (DWORD*)&m_img.lpPixels[(y-m_img.rect.top)*m_img.rect.Width()+1];
1810 for(int x = m_img.rect.left+1; x < m_img.rect.right-1; x++, p++)
1812 DWORD rgb = *p&0xffffff;
1813 BYTE c = rgb == 0x0000ff ? 0 : rgb == 0xff0000 ? 1 : rgb == 0x000000 ? 2 : 3;
1814 BYTE& c4bpp = p4bpp[(m_size.cy-y-1)*360+(x>>1)];
1815 c4bpp = (x&1) ? ((c4bpp&0xf0)|c) : ((c4bpp&0x0f)|(c<<4));
1819 CString bmpfn;
1820 bmpfn.Format(_T("%s_%04d.bmp"), fn, i+1);
1821 title = bmpfn.Mid(bmpfn.ReverseFind('/')+1);
1823 // E1, E2, P, Bg
1824 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]};
1825 c[0]^=c[1], c[1]^=c[0], c[0]^=c[1];
1827 if(memcmp(pc, c, sizeof(c)))
1829 memcpy(pc, c, sizeof(c));
1830 str.Format(_T("Color\t (%d %d %d %d)\n"), c[0], c[1], c[2], c[3]);
1831 f.WriteString(str);
1834 // E1, E2, P, Bg
1835 int a[4] = {m_img.pal[1].tr, m_img.pal[2].tr, m_img.pal[0].tr, m_img.pal[3].tr};
1836 a[0]^=a[1], a[1]^=a[0], a[0]^=a[1];
1838 if(memcmp(pa, a, sizeof(a)))
1840 memcpy(pa, a, sizeof(a));
1841 str.Format(_T("Contrast (%d %d %d %d)\n"), a[0], a[1], a[2], a[3]);
1842 f.WriteString(str);
1845 int t1 = sp[i].start;
1846 int h1 = t1/1000/60/60, m1 = (t1/1000/60)%60, s1 = (t1/1000)%60;
1847 int f1 = (int)((m_size.cy==480?29.97:25)*(t1%1000)/1000);
1849 int t2 = sp[i].stop;
1850 int h2 = t2/1000/60/60, m2 = (t2/1000/60)%60, s2 = (t2/1000)%60;
1851 int f2 = (int)((m_size.cy==480?29.97:25)*(t2%1000)/1000);
1853 if(t2 <= 0) continue;
1854 if(t1 < 0) t1 = 0;
1856 if(h1 == h2 && m1 == m2 && s1 == s2 && f1 == f2)
1858 f2++;
1859 if(f2 == (m_size.cy==480?30:25)) {f2 = 0; s2++; if(s2 == 60) {s2 = 0; m2++; if(m2 == 60) {m2 = 0; h2++;}}}
1862 if(i < sp.GetCount()-1)
1864 int t3 = sp[i+1].start;
1865 int h3 = t3/1000/60/60, m3 = (t3/1000/60)%60, s3 = (t3/1000)%60;
1866 int f3 = (int)((m_size.cy==480?29.97:25)*(t3%1000)/1000);
1868 if(h3 == h2 && m3 == m2 && s3 == s2 && f3 == f2)
1870 f2--;
1871 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--;}}}
1875 if(h1 == h2 && m1 == m2 && s1 == s2 && f1 == f2)
1876 continue;
1878 str.Format(_T("%04d\t%02d:%02d:%02d:%02d\t%02d:%02d:%02d:%02d\t%s\n"),
1879 ++k,
1880 h1, m1, s1, f1,
1881 h2, m2, s2, f2,
1882 title);
1883 f.WriteString(str);
1885 CFile bmp;
1886 if(bmp.Open(bmpfn, CFile::modeCreate|CFile::modeWrite|CFile::modeRead|CFile::typeBinary))
1888 bmp.Write(&fhdr, sizeof(fhdr));
1889 bmp.Write(&ihdr, sizeof(ihdr));
1890 bmp.Write(newCusPal, sizeof(RGBQUAD)*16);
1891 bmp.Write(p4bpp, 360*(m_size.cy-2));
1892 bmp.Close();
1894 CompressFile(bmpfn);
1898 m_fCustomPal = fCustomPal;
1899 memcpy(m_cuspal, tempCusPal, sizeof(m_cuspal));
1901 return(true);
1904 bool CVobSubFile::SaveMaestro(CString fn)
1906 TrimExtension(fn);
1908 CStdioFile f;
1909 if(!f.Open(fn + _T(".son"), CFile::modeCreate|CFile::modeWrite|CFile::typeText|CFile::shareDenyWrite))
1910 return(false);
1912 m_img.Invalidate();
1914 fn.Replace('\\', '/');
1915 CString title = fn.Mid(fn.ReverseFind('/')+1);
1917 TCHAR buff[MAX_PATH], * pFilePart = buff;
1918 if(GetFullPathName(fn, MAX_PATH, buff, &pFilePart) == 0)
1919 return(false);
1921 CString fullpath = CString(buff).Left(pFilePart - buff);
1922 fullpath.TrimRight(_T("\\/"));
1923 if(fullpath.IsEmpty())
1924 return(false);
1926 CString str, str2;
1927 str += _T("st_format\t2\n");
1928 str += _T("Display_Start\t%s\n");
1929 str += _T("TV_Type\t\t%s\n");
1930 str += _T("Tape_Type\tNON_DROP\n");
1931 str += _T("Pixel_Area\t(0 %d)\n");
1932 str += _T("Directory\t%s\n");
1933 str += _T("Subtitle\t%s\n");
1934 str += _T("Display_Area\t(0 2 719 %d)\n");
1935 str += _T("Contrast\t(15 15 15 0)\n");
1936 str += _T("\n");
1937 str += _T("SP_NUMBER\tSTART\tEND\tFILE_NAME\n");
1938 str2.Format(str,
1939 !m_fOnlyShowForcedSubs ? _T("non_forced") : _T("forced"),
1940 m_size.cy == 480 ? _T("NTSC") : _T("PAL"),
1941 m_size.cy-3,
1942 fullpath,
1943 title,
1944 m_size.cy == 480 ? 479 : 574);
1946 f.WriteString(str2);
1948 f.Flush();
1950 RGBQUAD pal[16] =
1952 {255, 0, 0, 0},
1953 {0, 0, 255, 0},
1954 {0, 0, 0, 0},
1955 {255, 255, 255, 0},
1956 {0, 255, 0, 0},
1957 {255, 0, 255, 0},
1958 {0, 255, 255, 0},
1959 {125, 125, 0, 0},
1960 {125, 125, 125, 0},
1961 {225, 225, 225, 0},
1962 {0, 0, 125, 0},
1963 {0, 125, 0, 0},
1964 {125, 0, 0, 0},
1965 {255, 0, 222, 0},
1966 {0, 125, 222, 0},
1967 {125, 0, 125, 0},
1970 BITMAPFILEHEADER fhdr =
1972 0x4d42,
1973 sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + 16*sizeof(RGBQUAD) + 360*(m_size.cy-2),
1974 0, 0,
1975 sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + 16*sizeof(RGBQUAD)
1978 BITMAPINFOHEADER ihdr =
1980 sizeof(BITMAPINFOHEADER),
1981 720, m_size.cy-2, 1, 4, 0,
1982 360*(m_size.cy-2),
1983 0, 0,
1984 16, 4
1987 bool fCustomPal = m_fCustomPal;
1988 m_fCustomPal = true;
1989 RGBQUAD tempCusPal[4], newCusPal[4+12] = {{255, 0, 0, 0}, {0, 0, 255, 0}, {0, 0, 0, 0}, {255, 255, 255, 0}};
1990 memcpy(tempCusPal, m_cuspal, sizeof(tempCusPal));
1991 memcpy(m_cuspal, newCusPal, sizeof(m_cuspal));
1993 CAutoVectorPtr<BYTE> p4bpp;
1994 if(!p4bpp.Allocate((m_size.cy-2)*360))
1995 return(false);
1997 BYTE colormap[16];
1998 for(int i = 0; i < 16; i++)
1999 colormap[i] = i;
2001 CFile spf;
2002 if(spf.Open(fn + _T(".spf"), CFile::modeCreate|CFile::modeWrite|CFile::typeBinary))
2004 for(int i = 0; i < 16; i++)
2006 COLORREF c = (m_orgpal[i].rgbBlue<<16) | (m_orgpal[i].rgbGreen<<8) | m_orgpal[i].rgbRed;
2007 spf.Write(&c, sizeof(COLORREF));
2010 spf.Close();
2013 int pc[4] = {1,1,1,1}, pa[4] = {15,15,15,0};
2015 CAtlArray<SubPos>& sp = m_langs[m_iLang].subpos;
2016 for(int i = 0, k = 0; i < sp.GetCount(); i++)
2018 if(!GetFrame(i)) continue;
2020 for(int j = 0; j < 5; j++)
2022 if(j == 4 || !m_img.pal[j].tr)
2024 j &= 3;
2025 memset(p4bpp, (j<<4)|j, (m_size.cy-2)*360);
2026 break;
2030 for(int y = max(m_img.rect.top+1, 2); y < m_img.rect.bottom-1; y++)
2032 ASSERT(m_size.cy-y-1 >= 0);
2033 if(m_size.cy-y-1 < 0) break;
2035 DWORD* p = (DWORD*)&m_img.lpPixels[(y-m_img.rect.top)*m_img.rect.Width()+1];
2037 for(int x = m_img.rect.left+1; x < m_img.rect.right-1; x++, p++)
2039 DWORD rgb = *p&0xffffff;
2040 BYTE c = rgb == 0x0000ff ? 0 : rgb == 0xff0000 ? 1 : rgb == 0x000000 ? 2 : 3;
2041 BYTE& c4bpp = p4bpp[(m_size.cy-y-1)*360+(x>>1)];
2042 c4bpp = (x&1) ? ((c4bpp&0xf0)|c) : ((c4bpp&0x0f)|(c<<4));
2046 CString bmpfn;
2047 bmpfn.Format(_T("%s_%04d.bmp"), fn, i+1);
2048 title = bmpfn.Mid(bmpfn.ReverseFind('/')+1);
2050 // E1, E2, P, Bg
2051 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]};
2053 if(memcmp(pc, c, sizeof(c)))
2055 memcpy(pc, c, sizeof(c));
2056 str.Format(_T("Color\t (%d %d %d %d)\n"), c[0], c[1], c[2], c[3]);
2057 f.WriteString(str);
2060 // E1, E2, P, Bg
2061 int a[4] = {m_img.pal[1].tr, m_img.pal[2].tr, m_img.pal[0].tr, m_img.pal[3].tr};
2063 if(memcmp(pa, a, sizeof(a)))
2065 memcpy(pa, a, sizeof(a));
2066 str.Format(_T("Contrast (%d %d %d %d)\n"), a[0], a[1], a[2], a[3]);
2067 f.WriteString(str);
2070 int t1 = sp[i].start;
2071 int h1 = t1/1000/60/60, m1 = (t1/1000/60)%60, s1 = (t1/1000)%60;
2072 int f1 = (int)((m_size.cy==480?29.97:25)*(t1%1000)/1000);
2074 int t2 = sp[i].stop;
2075 int h2 = t2/1000/60/60, m2 = (t2/1000/60)%60, s2 = (t2/1000)%60;
2076 int f2 = (int)((m_size.cy==480?29.97:25)*(t2%1000)/1000);
2078 if(t2 <= 0) continue;
2079 if(t1 < 0) t1 = 0;
2081 if(h1 == h2 && m1 == m2 && s1 == s2 && f1 == f2)
2083 f2++;
2084 if(f2 == (m_size.cy==480?30:25)) {f2 = 0; s2++; if(s2 == 60) {s2 = 0; m2++; if(m2 == 60) {m2 = 0; h2++;}}}
2087 if(i < sp.GetCount()-1)
2089 int t3 = sp[i+1].start;
2090 int h3 = t3/1000/60/60, m3 = (t3/1000/60)%60, s3 = (t3/1000)%60;
2091 int f3 = (int)((m_size.cy==480?29.97:25)*(t3%1000)/1000);
2093 if(h3 == h2 && m3 == m2 && s3 == s2 && f3 == f2)
2095 f2--;
2096 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--;}}}
2100 if(h1 == h2 && m1 == m2 && s1 == s2 && f1 == f2)
2101 continue;
2103 str.Format(_T("%04d\t%02d:%02d:%02d:%02d\t%02d:%02d:%02d:%02d\t%s\n"),
2104 ++k,
2105 h1, m1, s1, f1,
2106 h2, m2, s2, f2,
2107 title);
2108 f.WriteString(str);
2110 CFile bmp;
2111 if(bmp.Open(bmpfn, CFile::modeCreate|CFile::modeWrite|CFile::typeBinary))
2113 bmp.Write(&fhdr, sizeof(fhdr));
2114 bmp.Write(&ihdr, sizeof(ihdr));
2115 bmp.Write(newCusPal, sizeof(RGBQUAD)*16);
2116 bmp.Write(p4bpp, 360*(m_size.cy-2));
2117 bmp.Close();
2119 CompressFile(bmpfn);
2123 m_fCustomPal = fCustomPal;
2124 memcpy(m_cuspal, tempCusPal, sizeof(m_cuspal));
2126 return(true);
2130 // CVobSubStream
2133 CVobSubStream::CVobSubStream(CCritSec* pLock)
2134 : CSubPicProviderImpl(pLock)
2138 CVobSubStream::~CVobSubStream()
2142 void CVobSubStream::Open(CString name, BYTE* pData, int len)
2144 CAutoLock cAutoLock(&m_csSubPics);
2146 m_name = name;
2148 CAtlList<CString> lines;
2149 Explode(CString(CStringA((CHAR*)pData, len)), lines, '\n');
2150 while(lines.GetCount())
2152 CAtlList<CString> sl;
2153 Explode(lines.RemoveHead(), sl, ':', 2);
2154 if(sl.GetCount() != 2) continue;
2155 CString key = sl.GetHead();
2156 CString value = sl.GetTail();
2157 if(key == _T("size"))
2158 _stscanf(value, _T("%dx %d"), &m_size.cx, &m_size.cy);
2159 else if(key == _T("org"))
2160 _stscanf(value, _T("%d, %d"), &m_org.x, &m_org.y);
2161 else if(key == _T("scale"))
2162 _stscanf(value, _T("%d%%, %d%%"), &m_scale_x, &m_scale_y);
2163 else if(key == _T("alpha"))
2164 _stscanf(value, _T("%d%%"), &m_alpha);
2165 else if(key == _T("smooth"))
2166 m_fSmooth =
2167 value == _T("0") || value == _T("OFF") ? 0 :
2168 value == _T("1") || value == _T("ON") ? 1 :
2169 value == _T("2") || value == _T("OLD") ? 2 :
2171 else if(key == _T("align"))
2173 Explode(value, sl, ' ');
2174 if(sl.GetCount() == 4) sl.RemoveAt(sl.FindIndex(1));
2175 if(sl.GetCount() == 3)
2177 m_fAlign = sl.RemoveHead() == _T("ON");
2178 CString hor = sl.GetHead(), ver = sl.GetTail();
2179 m_alignhor = hor == _T("LEFT") ? 0 : hor == _T("CENTER") ? 1 : hor == _T("RIGHT") ? 2 : 1;
2180 m_alignver = ver == _T("TOP") ? 0 : ver == _T("CENTER") ? 1 : ver == _T("BOTTOM") ? 2 : 2;
2183 else if(key == _T("fade in/out"))
2184 _stscanf(value, _T("%d%, %d%"), &m_fadein, &m_fadeout);
2185 else if(key == _T("time offset"))
2186 m_toff = _tcstol(value, NULL, 10);
2187 else if(key == _T("forced subs"))
2188 m_fOnlyShowForcedSubs = value == _T("1") || value == _T("ON");
2189 else if(key == _T("palette"))
2191 Explode(value, sl, ',', 16);
2192 for(int i = 0; i < 16 && sl.GetCount(); i++)
2193 *(DWORD*)&m_orgpal[i] = _tcstol(sl.RemoveHead(), NULL, 16);
2195 else if(key == _T("custom colors"))
2197 m_fCustomPal = Explode(value, sl, ',', 3) == _T("ON");
2198 if(sl.GetCount() == 3)
2200 sl.RemoveHead();
2201 CAtlList<CString> tridx, colors;
2202 Explode(sl.RemoveHead(), tridx, ':', 2);
2203 if(tridx.RemoveHead() == _T("tridx"))
2205 TCHAR tr[4];
2206 _stscanf(tridx.RemoveHead(), _T("%c%c%c%c"), &tr[0], &tr[1], &tr[2], &tr[3]);
2207 for(int i = 0; i < 4; i++)
2208 m_tridx |= ((tr[i]=='1')?1:0)<<i;
2210 Explode(sl.RemoveHead(), colors, ':', 2);
2211 if(colors.RemoveHead() == _T("colors"))
2213 Explode(colors.RemoveHead(), colors, ',', 4);
2214 for(int i = 0; i < 4 && colors.GetCount(); i++)
2215 *(DWORD*)&m_cuspal[i] = _tcstol(colors.RemoveHead(), NULL, 16);
2222 void CVobSubStream::Add(REFERENCE_TIME tStart, REFERENCE_TIME tStop, BYTE* pData, int len)
2224 if(len <= 4 || ((pData[0]<<8)|pData[1]) != len) return;
2226 CVobSubImage vsi;
2227 vsi.GetPacketInfo(pData, (pData[0]<<8)|pData[1], (pData[2]<<8)|pData[3]);
2229 CAutoPtr<SubPic> p(new SubPic());
2230 p->tStart = tStart;
2231 p->tStop = vsi.delay > 0 ? (tStart + 10000i64*vsi.delay) : tStop;
2232 p->pData.SetCount(len);
2233 memcpy(p->pData.GetData(), pData, p->pData.GetCount());
2235 CAutoLock cAutoLock(&m_csSubPics);
2236 while(m_subpics.GetCount() && m_subpics.GetTail()->tStart >= tStart)
2238 m_subpics.RemoveTail();
2239 m_img.iIdx = -1;
2241 m_subpics.AddTail(p);
2244 void CVobSubStream::RemoveAll()
2246 CAutoLock cAutoLock(&m_csSubPics);
2247 m_subpics.RemoveAll();
2248 m_img.iIdx = -1;
2251 STDMETHODIMP CVobSubStream::NonDelegatingQueryInterface(REFIID riid, void** ppv)
2253 CheckPointer(ppv, E_POINTER);
2254 *ppv = NULL;
2256 return
2257 QI(IPersist)
2258 QI(ISubStream)
2259 QI(ISubPicProvider)
2260 __super::NonDelegatingQueryInterface(riid, ppv);
2263 // ISubPicProvider
2265 STDMETHODIMP_(POSITION) CVobSubStream::GetStartPosition(REFERENCE_TIME rt, double fps)
2267 CAutoLock cAutoLock(&m_csSubPics);
2268 POSITION pos = m_subpics.GetTailPosition();
2269 for(; pos; m_subpics.GetPrev(pos))
2271 SubPic* sp = m_subpics.GetAt(pos);
2272 if(sp->tStart <= rt)
2274 if(sp->tStop <= rt) m_subpics.GetNext(pos);
2275 break;
2278 return(pos);
2281 STDMETHODIMP_(POSITION) CVobSubStream::GetNext(POSITION pos)
2283 CAutoLock cAutoLock(&m_csSubPics);
2284 m_subpics.GetNext(pos);
2285 return pos;
2288 STDMETHODIMP_(REFERENCE_TIME) CVobSubStream::GetStart(POSITION pos, double fps)
2290 CAutoLock cAutoLock(&m_csSubPics);
2291 return m_subpics.GetAt(pos)->tStart;
2294 STDMETHODIMP_(REFERENCE_TIME) CVobSubStream::GetStop(POSITION pos, double fps)
2296 CAutoLock cAutoLock(&m_csSubPics);
2297 return m_subpics.GetAt(pos)->tStop;
2300 STDMETHODIMP_(bool) CVobSubStream::IsAnimated(POSITION pos)
2302 return(false);
2305 STDMETHODIMP CVobSubStream::Render(SubPicDesc& spd, REFERENCE_TIME rt, double fps, RECT& bbox)
2307 if(spd.bpp != 32) return E_INVALIDARG;
2309 POSITION pos = m_subpics.GetTailPosition();
2310 for(; pos; m_subpics.GetPrev(pos))
2312 SubPic* sp = m_subpics.GetAt(pos);
2313 if(sp->tStart <= rt && rt < sp->tStop)
2315 if(m_img.iIdx != (int)pos)
2317 BYTE* pData = sp->pData.GetData();
2318 m_img.Decode(
2319 pData, (pData[0]<<8)|pData[1], (pData[2]<<8)|pData[3],
2320 m_fCustomPal, m_tridx, m_orgpal, m_cuspal, true);
2321 m_img.iIdx = (int)pos;
2324 return __super::Render(spd, bbox);
2328 return E_FAIL;
2331 // IPersist
2333 STDMETHODIMP CVobSubStream::GetClassID(CLSID* pClassID)
2335 return pClassID ? *pClassID = __uuidof(this), S_OK : E_POINTER;
2338 // ISubStream
2340 STDMETHODIMP_(int) CVobSubStream::GetStreamCount()
2342 return 1;
2345 STDMETHODIMP CVobSubStream::GetStreamInfo(int i, WCHAR** ppName, LCID* pLCID)
2347 CAutoLock cAutoLock(&m_csSubPics);
2349 if(ppName)
2351 if(!(*ppName = (WCHAR*)CoTaskMemAlloc((m_name.GetLength()+1)*sizeof(WCHAR))))
2352 return E_OUTOFMEMORY;
2353 wcscpy(*ppName, CStringW(m_name));
2356 if(pLCID)
2358 *pLCID = 0; // TODO
2361 return S_OK;
2364 STDMETHODIMP_(int) CVobSubStream::GetStream()
2366 return 0;
2369 STDMETHODIMP CVobSubStream::SetStream(int iStream)
2371 return iStream == 0 ? S_OK : E_FAIL;