Merge with MPC-HC 6d1472b2f18266d92e5bc068667de348c0cd3b3b.
[xy_vsfilter.git] / src / subtitles / STS.cpp
blob231ffef357b1c54a90fb49356252b39fe5480f8e
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.
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 "STS.h"
24 #include <atlbase.h>
26 #include "RealTextParser.h"
27 #include <fstream>
29 #include "CAutoTiming.h"
31 #include <algorithm>
32 #include <vector>
33 #include "xy_logger.h"
35 // gathered from http://www.netwave.or.jp/~shikai/shikai/shcolor.htm
37 struct htmlcolor {TCHAR* name; DWORD color;} hmtlcolors[] =
39 {_T("white"), 0xffffff},
40 {_T("whitesmoke"), 0xf5f5f5},
41 {_T("ghostwhite"), 0xf8f8ff},
42 {_T("snow"), 0xfffafa},
43 {_T("gainsboro"), 0xdcdcdc},
44 {_T("lightgrey"), 0xd3d3d3},
45 {_T("silver"), 0xc0c0c0},
46 {_T("darkgray"), 0xa9a9a9},
47 {_T("gray"), 0x808080},
48 {_T("dimgray"), 0x696969},
49 {_T("lightslategray"), 0x778899},
50 {_T("slategray"), 0x708090},
51 {_T("darkslategray"), 0x2f4f4f},
52 {_T("black"), 0x000000},
54 {_T("azure"), 0xf0ffff},
55 {_T("aliceblue"), 0xf0f8ff},
56 {_T("mintcream"), 0xf5fffa},
57 {_T("honeydew"), 0xf0fff0},
58 {_T("lightcyan"), 0xe0ffff},
59 {_T("paleturqoise"), 0xafeeee},
60 {_T("powderblue"), 0xb0e0e6},
61 {_T("lightblue"), 0xadd8ed},
62 {_T("lightsteelblue"), 0xb0c4de},
63 {_T("skyblue"), 0x87ceeb},
64 {_T("lightskyblue"), 0x87cefa},
65 {_T("cyan"), 0x00ffff},
66 {_T("aqua"), 0x00ff80},
67 {_T("deepskyblue"), 0x00bfff},
68 {_T("aquamarine"), 0x7fffd4},
69 {_T("turquoise"), 0x40e0d0},
70 {_T("darkturquoise"), 0x00ced1},
71 {_T("lightseagreen"), 0x20b2aa},
72 {_T("mediumturquoise"), 0x40e0dd},
73 {_T("mediumaquamarine"), 0x66cdaa},
74 {_T("cadetblue"), 0x5f9ea0},
75 {_T("teal"), 0x008080},
76 {_T("darkcyan"), 0x008b8b},
77 {_T("comflowerblue"), 0x6495ed},
78 {_T("dodgerblue"), 0x1e90ff},
79 {_T("steelblue"), 0x4682b4},
80 {_T("royalblue"), 0x4169e1},
81 {_T("blue"), 0x0000ff},
82 {_T("mediumblue"), 0x0000cd},
83 {_T("mediumslateblue"), 0x7b68ee},
84 {_T("slateblue"), 0x6a5acd},
85 {_T("darkslateblue"), 0x483d8b},
86 {_T("darkblue"), 0x00008b},
87 {_T("midnightblue"), 0x191970},
88 {_T("navy"), 0x000080},
90 {_T("palegreen"), 0x98fb98},
91 {_T("lightgreen"), 0x90ee90},
92 {_T("mediumspringgreen"), 0x00fa9a},
93 {_T("springgreen"), 0x00ff7f},
94 {_T("chartreuse"), 0x7fff00},
95 {_T("lawngreen"), 0x7cfc00},
96 {_T("lime"), 0x00ff00},
97 {_T("limegreen"), 0x32cd32},
98 {_T("greenyellow"), 0xadff2f},
99 {_T("yellowgreen"), 0x9acd32},
100 {_T("darkseagreen"), 0x8fbc8f},
101 {_T("mediumseagreen"), 0x3cb371},
102 {_T("seagreen"), 0x2e8b57},
103 {_T("olivedrab"), 0x6b8e23},
104 {_T("forestgreen"), 0x228b22},
105 {_T("green"), 0x008000},
106 {_T("darkkhaki"), 0xbdb76b},
107 {_T("olive"), 0x808000},
108 {_T("darkolivegreen"), 0x556b2f},
109 {_T("darkgreen"), 0x006400},
111 {_T("floralwhite"), 0xfffaf0},
112 {_T("seashell"), 0xfff5ee},
113 {_T("ivory"), 0xfffff0},
114 {_T("beige"), 0xf5f5dc},
115 {_T("cornsilk"), 0xfff8dc},
116 {_T("lemonchiffon"), 0xfffacd},
117 {_T("lightyellow"), 0xffffe0},
118 {_T("lightgoldenrodyellow"), 0xfafad2},
119 {_T("papayawhip"), 0xffefd5},
120 {_T("blanchedalmond"), 0xffedcd},
121 {_T("palegoldenrod"), 0xeee8aa},
122 {_T("khaki"), 0xf0eb8c},
123 {_T("bisque"), 0xffe4c4},
124 {_T("moccasin"), 0xffe4b5},
125 {_T("navajowhite"), 0xffdead},
126 {_T("peachpuff"), 0xffdab9},
127 {_T("yellow"), 0xffff00},
128 {_T("gold"), 0xffd700},
129 {_T("wheat"), 0xf5deb3},
130 {_T("orange"), 0xffa500},
131 {_T("darkorange"), 0xff8c00},
133 {_T("oldlace"), 0xfdf5e6},
134 {_T("linen"), 0xfaf0e6},
135 {_T("antiquewhite"), 0xfaebd7},
136 {_T("lightsalmon"), 0xffa07a},
137 {_T("darksalmon"), 0xe9967a},
138 {_T("salmon"), 0xfa8072},
139 {_T("lightcoral"), 0xf08080},
140 {_T("indianred"), 0xcd5c5c},
141 {_T("coral"), 0xff7f50},
142 {_T("tomato"), 0xff6347},
143 {_T("orangered"), 0xff4500},
144 {_T("red"), 0xff0000},
145 {_T("crimson"), 0xdc143c},
146 {_T("firebrick"), 0xb22222},
147 {_T("maroon"), 0x800000},
148 {_T("darkred"), 0x8b0000},
150 {_T("lavender"), 0xe6e6fe},
151 {_T("lavenderblush"), 0xfff0f5},
152 {_T("mistyrose"), 0xffe4e1},
153 {_T("thistle"), 0xd8bfd8},
154 {_T("pink"), 0xffc0cb},
155 {_T("lightpink"), 0xffb6c1},
156 {_T("palevioletred"), 0xdb7093},
157 {_T("hotpink"), 0xff69b4},
158 {_T("fuchsia"), 0xff00ee},
159 {_T("magenta"), 0xff00ff},
160 {_T("mediumvioletred"), 0xc71585},
161 {_T("deeppink"), 0xff1493},
162 {_T("plum"), 0xdda0dd},
163 {_T("violet"), 0xee82ee},
164 {_T("orchid"), 0xda70d6},
165 {_T("mediumorchid"), 0xba55d3},
166 {_T("mediumpurple"), 0x9370db},
167 {_T("purple"), 0x9370db},
168 {_T("blueviolet"), 0x8a2be2},
169 {_T("darkviolet"), 0x9400d3},
170 {_T("darkorchid"), 0x9932cc},
172 {_T("tan"), 0xd2b48c},
173 {_T("burlywood"), 0xdeb887},
174 {_T("sandybrown"), 0xf4a460},
175 {_T("peru"), 0xcd853f},
176 {_T("goldenrod"), 0xdaa520},
177 {_T("darkgoldenrod"), 0xb8860b},
178 {_T("chocolate"), 0xd2691e},
179 {_T("rosybrown"), 0xbc8f8f},
180 {_T("sienna"), 0xa0522d},
181 {_T("saddlebrown"), 0x8b4513},
182 {_T("brown"), 0xa52a2a},
185 CHtmlColorMap::CHtmlColorMap()
187 for(int i = 0; i < countof(hmtlcolors); i++)
188 SetAt(hmtlcolors[i].name, hmtlcolors[i].color);
191 CHtmlColorMap g_colors;
193 CString g_default_style(_T("Default"));
197 BYTE CharSetList[] =
199 ANSI_CHARSET,
200 DEFAULT_CHARSET,
201 SYMBOL_CHARSET,
202 SHIFTJIS_CHARSET,
203 HANGEUL_CHARSET,
204 HANGUL_CHARSET,
205 GB2312_CHARSET,
206 CHINESEBIG5_CHARSET,
207 OEM_CHARSET,
208 JOHAB_CHARSET,
209 HEBREW_CHARSET,
210 ARABIC_CHARSET,
211 GREEK_CHARSET,
212 TURKISH_CHARSET,
213 VIETNAMESE_CHARSET,
214 THAI_CHARSET,
215 EASTEUROPE_CHARSET,
216 RUSSIAN_CHARSET,
217 MAC_CHARSET,
218 BALTIC_CHARSET
221 TCHAR* CharSetNames[] =
223 _T("ANSI"),
224 _T("DEFAULT"),
225 _T("SYMBOL"),
226 _T("SHIFTJIS"),
227 _T("HANGEUL"),
228 _T("HANGUL"),
229 _T("GB2312"),
230 _T("CHINESEBIG5"),
231 _T("OEM"),
232 _T("JOHAB"),
233 _T("HEBREW"),
234 _T("ARABIC"),
235 _T("GREEK"),
236 _T("TURKISH"),
237 _T("VIETNAMESE"),
238 _T("THAI"),
239 _T("EASTEUROPE"),
240 _T("RUSSIAN"),
241 _T("MAC"),
242 _T("BALTIC"),
245 int CharSetLen = countof(CharSetList);
247 static void LogSegments(const CAtlArray<STSSegment>& segments)
249 #ifdef __DO_LOG
250 for (int i=0;i<segments.GetCount();i++)
252 const STSSegment& s = segments[i];
253 XY_LOG_INFO(_T("\tsegments ")<<i<<_T(":")<<s.start<<_T(" ")
254 <<s.end<<_T(" ")<<s.subs.GetCount());
255 XY_LOG_INFO(_T("\tsubs: "));
256 for (int j=0;j<s.subs.GetCount();j++)
258 XY_LOG_INFO(_T("\t\t ")<<s.subs[j]);
261 #endif
266 static DWORD CharSetToCodePage(DWORD dwCharSet)
268 CHARSETINFO cs={0};
269 ::TranslateCharsetInfo((DWORD *)dwCharSet, &cs, TCI_SRCCHARSET);
270 return cs.ciACP;
273 int FindChar(CStringW str, WCHAR c, int pos, bool fUnicode, int CharSet)
275 if(fUnicode) return(str.Find(c, pos));
277 int fStyleMod = 0;
279 DWORD cp = CharSetToCodePage(CharSet);
280 int OrgCharSet = CharSet;
282 for(int i = 0, j = str.GetLength(), k; i < j; i++)
284 WCHAR c2 = str[i];
286 if(IsDBCSLeadByteEx(cp, (BYTE)c2)) i++;
287 else if(i >= pos)
289 if(c2 == c) return(i);
292 if(c2 == '{') fStyleMod++;
293 else if(fStyleMod > 0)
295 if(c2 == '}') fStyleMod--;
296 else if(c2 == 'e' && i >= 3 && i < j-1 && str.Mid(i-2, 3) == L"\\fe")
298 CharSet = 0;
299 for(k = i+1; _istdigit(str[k]); k++) CharSet = CharSet*10 + (str[k] - '0');
300 if(k == i+1) CharSet = OrgCharSet;
302 cp = CharSetToCodePage(CharSet);
307 return(-1);
310 int FindChar(CStringA str, char c, int pos, bool fUnicode, int CharSet)
312 ASSERT(!fUnicode);
314 return(FindChar(AToW(str), c, pos, false, CharSet));
317 static CStringW ToMBCS(CStringW str, DWORD CharSet)
319 CStringW ret;
321 DWORD cp = CharSetToCodePage(CharSet);
323 for(int i = 0, j = str.GetLength(); i < j; i++)
325 WCHAR wc = str.GetAt(i);
326 char c[8];
328 int len;
329 if((len = WideCharToMultiByte(cp, 0, &wc, 1, c, 8, NULL, NULL)) > 0)
331 for(int k = 0; k < len; k++)
332 ret += (WCHAR)(BYTE)c[k];
334 else
336 ret += '?';
340 return(ret);
343 static CStringW UnicodeSSAToMBCS(CStringW str, DWORD CharSet)
345 CStringW ret;
347 int OrgCharSet = CharSet;
349 for(int j = 0; j < str.GetLength(); )
351 j = str.Find('{', j);
352 if(j >= 0)
354 ret += ToMBCS(str.Left(j), CharSet);
355 str = str.Mid(j);
357 j = str.Find('}');
358 if(j < 0)
360 ret += ToMBCS(str, CharSet);
361 break;
363 else
365 int k = str.Find(L"\\fe");
366 if(k >= 0 && k < j)
368 CharSet = 0;
369 int l = k+3;
370 for(; _istdigit(str[l]); l++) CharSet = CharSet*10 + (str[l] - '0');
371 if(l == k+3) CharSet = OrgCharSet;
374 j++;
376 ret += ToMBCS(str.Left(j), OrgCharSet);
377 str = str.Mid(j);
378 j = 0;
381 else
383 ret += ToMBCS(str, CharSet);
384 break;
388 return(ret);
391 static CStringW ToUnicode(CStringW str, DWORD CharSet)
393 CStringW ret;
395 DWORD cp = CharSetToCodePage(CharSet);
397 for(int i = 0, j = str.GetLength(); i < j; i++)
399 WCHAR wc = str.GetAt(i);
400 char c = wc&0xff;
402 if(IsDBCSLeadByteEx(cp, (BYTE)wc))
404 i++;
406 if(i < j)
408 char cc[2];
409 cc[0] = c;
410 cc[1] = (char)str.GetAt(i);
412 MultiByteToWideChar(cp, 0, cc, 2, &wc, 1);
415 else
417 MultiByteToWideChar(cp, 0, &c, 1, &wc, 1);
420 ret += wc;
423 return(ret);
426 static CStringW MBCSSSAToUnicode(CStringW str, int CharSet)
428 CStringW ret;
430 int OrgCharSet = CharSet;
432 for(int j = 0; j < str.GetLength(); )
434 j = FindChar(str, '{', 0, false, CharSet);
436 if(j >= 0)
438 ret += ToUnicode(str.Left(j), CharSet);
439 str = str.Mid(j);
441 j = FindChar(str, '}', 0, false, CharSet);
443 if(j < 0)
445 ret += ToUnicode(str, CharSet);
446 break;
448 else
450 int k = str.Find(L"\\fe");
451 if(k >= 0 && k < j)
453 CharSet = 0;
454 int l = k+3;
455 for(; _istdigit(str[l]); l++) CharSet = CharSet*10 + (str[l] - '0');
456 if(l == k+3) CharSet = OrgCharSet;
459 j++;
461 ret += ToUnicode(str.Left(j), OrgCharSet);
462 str = str.Mid(j);
463 j = 0;
466 else
468 ret += ToUnicode(str, CharSet);
469 break;
473 return(ret);
476 CStringW RemoveSSATags(CStringW str, bool fUnicode, int CharSet)
478 str.Replace (L"{\\i1}", L"<i>");
479 str.Replace (L"{\\i}", L"</i>");
481 for(int i = 0, j; i < str.GetLength(); )
483 if((i = FindChar(str, '{', i, fUnicode, CharSet)) < 0) break;
484 if((j = FindChar(str, '}', i, fUnicode, CharSet)) < 0) break;
485 str.Delete(i, j-i+1);
488 str.Replace(L"\\N", L"\n");
489 str.Replace(L"\\n", L"\n");
490 str.Replace(L"\\h", L" ");
492 return(str);
497 static CStringW SubRipper2SSA(CStringW str, int CharSet)
499 str.Replace(L"<i>", L"{\\i1}");
500 str.Replace(L"</i>", L"{\\i}");
501 str.Replace(L"<b>", L"{\\b1}");
502 str.Replace(L"</b>", L"{\\b}");
503 str.Replace(L"<u>", L"{\\u1}");
504 str.Replace(L"</u>", L"{\\u}");
506 return(str);
509 static bool OpenSubRipper(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet)
511 int num = 0;
513 CStringW buff;
514 while(file->ReadString(buff))
516 buff.Trim();
517 if(buff.IsEmpty()) continue;
519 WCHAR sep;
520 int hh1, mm1, ss1, ms1, hh2, mm2, ss2, ms2;
521 int c = swscanf(buff, L"%d%c%d%c%d%c%d --> %d%c%d%c%d%c%d\n",
522 &hh1, &sep, &mm1, &sep, &ss1, &sep, &ms1,
523 &hh2, &sep, &mm2, &sep, &ss2, &sep, &ms2);
525 if(c == 1) // numbering
527 num = hh1;
529 else if(c == 14) // time info
531 CStringW str, tmp;
533 bool fFoundEmpty = false;
535 while(file->ReadString(tmp))
537 tmp.Trim();
538 if(tmp.IsEmpty()) fFoundEmpty = true;
540 int num2;
541 WCHAR c;
542 if(swscanf(tmp, L"%d%c", &num2, &c) == 1 && fFoundEmpty)
544 num = num2;
545 break;
548 str += tmp + '\n';
551 ret.Add(
552 SubRipper2SSA(str, CharSet),
553 file->IsUnicode(),
554 (((hh1*60 + mm1)*60) + ss1)*1000 + ms1,
555 (((hh2*60 + mm2)*60) + ss2)*1000 + ms2);
557 else if(c != EOF) // might be another format
559 return(false);
563 return(!ret.IsEmpty());
566 static bool OpenOldSubRipper(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet)
568 CStringW buff;
569 while(file->ReadString(buff))
571 buff.Trim();
572 if(buff.IsEmpty()) continue;
574 for(int i = 0; i < buff.GetLength(); i++)
576 if((i = FindChar(buff, '|', i, file->IsUnicode(), CharSet)) < 0) break;
577 buff.SetAt(i, '\n');
580 int hh1, mm1, ss1, hh2, mm2, ss2;
581 int c = swscanf(buff, L"{%d:%d:%d}{%d:%d:%d}", &hh1, &mm1, &ss1, &hh2, &mm2, &ss2);
583 if(c == 6)
585 ret.Add(
586 buff.Mid(buff.Find('}', buff.Find('}')+1)+1),
587 file->IsUnicode(),
588 (((hh1*60 + mm1)*60) + ss1)*1000,
589 (((hh2*60 + mm2)*60) + ss2)*1000);
591 else if(c != EOF) // might be another format
593 return(false);
597 return(!ret.IsEmpty());
600 static bool OpenSubViewer(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet)
602 STSStyle def;
603 CStringW font, color, size;
604 bool fBold, fItalic, fStriked, fUnderline;
606 CStringW buff;
607 while(file->ReadString(buff))
609 buff.Trim();
610 if(buff.IsEmpty()) continue;
612 if(buff[0] == '[')
614 for(int i = 0; i < buff.GetLength() && buff[i]== '['; )
616 int j = buff.Find(']', ++i);
617 if(j < i) break;
619 CStringW tag = buff.Mid(i,j-i);
620 tag.Trim();
621 tag.MakeLower();
623 i += j-i;
625 j = buff.Find('[', ++i);
626 if(j < 0) j = buff.GetLength();
628 CStringW param = buff.Mid(i,j-i);
629 param.Trim(L" \\t,");
631 i = j;
633 if(tag == L"font")
634 font = def.fontName.CompareNoCase(WToT(param)) ? param : L"";
635 else if(tag == L"colf")
636 color = def.colors[0] != wcstol(((LPCWSTR)param)+2, 0, 16) ? param : L"";
637 else if(tag == L"size")
638 size = def.fontSize != wcstol(param, 0, 10) ? param : L"";
639 else if(tag == L"style")
641 if(param.Find(L"no") >= 0)
643 fBold = fItalic = fStriked = fUnderline = false;
645 else
647 fBold = def.fontWeight < FW_BOLD && param.Find(L"bd") >= 0;
648 fItalic = def.fItalic && param.Find(L"it") >= 0;
649 fStriked = def.fStrikeOut && param.Find(L"st") >= 0;
650 fUnderline = def.fUnderline && param.Find(L"ud") >= 0;
655 continue;
658 WCHAR sep;
659 int hh1, mm1, ss1, hs1, hh2, mm2, ss2, hs2;
660 int c = swscanf(buff, L"%d:%d:%d%c%d,%d:%d:%d%c%d\n",
661 &hh1, &mm1, &ss1, &sep, &hs1, &hh2, &mm2, &ss2, &sep, &hs2);
663 if(c == 10)
665 CStringW str;
666 file->ReadString(str);
668 str.Replace(L"[br]", L"\\N");
670 CStringW prefix;
671 if(!font.IsEmpty()) prefix += L"\\fn" + font;
672 if(!color.IsEmpty()) prefix += L"\\c" + color;
673 if(!size.IsEmpty()) prefix += L"\\fs" + size;
674 if(fBold) prefix += L"\\b1";
675 if(fItalic) prefix += L"\\i1";
676 if(fStriked) prefix += L"\\s1";
677 if(fUnderline) prefix += L"\\u1";
678 if(!prefix.IsEmpty()) str = L"{" + prefix + L"}" + str;
680 ret.Add(str,
681 file->IsUnicode(),
682 (((hh1*60 + mm1)*60) + ss1)*1000 + hs1*10,
683 (((hh2*60 + mm2)*60) + ss2)*1000 + hs2*10);
685 else if(c != EOF) // might be another format
687 return(false);
691 return(!ret.IsEmpty());
694 static STSStyle* GetMicroDVDStyle(CString str, int CharSet)
696 STSStyle* ret = new STSStyle();
697 if(!ret) return(NULL);
699 for(int i = 0, len = str.GetLength(); i < len; i++)
701 int j = str.Find('{', i);
702 if(j < 0) j = len;
704 if(j >= len) break;
706 int k = str.Find('}', j);
707 if(k < 0) k = len;
709 CString code = str.Mid(j, k-j);
710 if(code.GetLength() > 2) code.SetAt(1, (TCHAR)towlower(code[1]));
712 if(!_tcsnicmp(code, _T("{c:$"), 4))
714 _stscanf(code, _T("{c:$%x"), &ret->colors[0]);
716 else if(!_tcsnicmp(code, _T("{f:"), 3))
718 ret->fontName = code.Mid(3);
720 else if(!_tcsnicmp(code, _T("{s:"), 3))
722 float f;
723 if(1 == _stscanf(code, _T("{s:%f"), &f))
724 ret->fontSize = f;
726 else if(!_tcsnicmp(code, _T("{h:"), 3))
728 _stscanf(code, _T("{h:%d"), &ret->charSet);
730 else if(!_tcsnicmp(code, _T("{y:"), 3))
732 code.MakeLower();
733 if(code.Find('b') >= 0) ret->fontWeight = FW_BOLD;
734 if(code.Find('i') >= 0) ret->fItalic = true;
735 if(code.Find('u') >= 0) ret->fUnderline = true;
736 if(code.Find('s') >= 0) ret->fStrikeOut = true;
738 else if(!_tcsnicmp(code, _T("{p:"), 3))
740 int p;
741 _stscanf(code, _T("{p:%d"), &p);
742 ret->scrAlignment = (p == 0) ? 8 : 2;
745 i = k;
748 return(ret);
751 static CStringW MicroDVD2SSA(CStringW str, bool fUnicode, int CharSet)
753 CStringW ret;
755 enum {COLOR=0, FONTNAME, FONTSIZE, FONTCHARSET, BOLD, ITALIC, UNDERLINE, STRIKEOUT};
756 bool fRestore[8];
757 int fRestoreLen = 8;
758 memset(fRestore, 0, sizeof(bool)*fRestoreLen);
760 for(int pos = 0, eol; pos < str.GetLength(); pos++)
762 if((eol = FindChar(str, '|', pos, fUnicode, CharSet)) < 0) eol = str.GetLength();
764 CStringW line = str.Mid(pos, eol-pos);
766 pos = eol;
768 for(int i = 0, j, k, len = line.GetLength(); i < len; i++)
770 if((j = FindChar(line, '{', i, fUnicode, CharSet)) < 0) j = str.GetLength();
772 ret += line.Mid(i, j-i);
774 if(j >= len) break;
776 if((k = FindChar(line, '}', j, fUnicode, CharSet)) < 0) k = len;
779 CStringW code = line.Mid(j, k-j);
781 if(!wcsnicmp(code, L"{c:$", 4))
783 fRestore[COLOR] = (iswupper(code[1]) == 0);
784 code.MakeLower();
786 int color;
787 swscanf(code, L"{c:$%x", &color);
788 code.Format(L"{\\c&H%x&}", color);
789 ret += code;
791 else if(!wcsnicmp(code, L"{f:", 3))
793 fRestore[FONTNAME] = (iswupper(code[1]) == 0);
795 code.Format(L"{\\fn%s}", code.Mid(3));
796 ret += code;
798 else if(!wcsnicmp(code, L"{s:", 3))
800 fRestore[FONTSIZE] = (iswupper(code[1]) == 0);
801 code.MakeLower();
803 float size;
804 swscanf(code, L"{s:%f", &size);
805 code.Format(L"{\\fs%f}", size);
806 ret += code;
808 else if(!wcsnicmp(code, L"{h:", 3))
810 fRestore[COLOR] = (_istupper(code[1]) == 0);
811 code.MakeLower();
813 int CharSet;
814 swscanf(code, L"{h:%d", &CharSet);
815 code.Format(L"{\\fe%d}", CharSet);
816 ret += code;
818 else if(!wcsnicmp(code, L"{y:", 3))
820 bool f = (_istupper(code[1]) == 0);
822 code.MakeLower();
824 ret += '{';
825 if(code.Find('b') >= 0) {ret += L"\\b1"; fRestore[BOLD] = f;}
826 if(code.Find('i') >= 0) {ret += L"\\i1"; fRestore[ITALIC] = f;}
827 if(code.Find('u') >= 0) {ret += L"\\u1"; fRestore[UNDERLINE] = f;}
828 if(code.Find('s') >= 0) {ret += L"\\s1"; fRestore[STRIKEOUT] = f;}
829 ret += '}';
831 else if(!wcsnicmp(code, L"{o:", 3))
833 code.MakeLower();
835 int x, y;
836 TCHAR c;
837 swscanf(code, L"{o:%d%c%d", &x, &c, &y);
838 code.Format(L"{\\move(%d,%d,0,0,0,0)}", x, y);
839 ret += code;
841 else ret += code;
844 i = k;
847 if(pos >= str.GetLength()) break;
849 for(int i = 0; i < fRestoreLen; i++)
851 if(fRestore[i])
853 switch(i)
855 case COLOR: ret += L"{\\c}"; break;
856 case FONTNAME: ret += L"{\\fn}"; break;
857 case FONTSIZE: ret += L"{\\fs}"; break;
858 case FONTCHARSET: ret += L"{\\fe}"; break;
859 case BOLD: ret += L"{\\b}"; break;
860 case ITALIC: ret += L"{\\i}"; break;
861 case UNDERLINE: ret += L"{\\u}"; break;
862 case STRIKEOUT: ret += L"{\\s}"; break;
863 default: break;
868 memset(fRestore, 0, sizeof(bool)*fRestoreLen);
870 ret += L"\\N";
873 return(ret);
876 static bool OpenMicroDVD(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet)
878 bool fCheck = false, fCheck2 = false;
880 CString style(_T("Default"));
882 CStringW buff;;
883 while(file->ReadString(buff))
885 buff.Trim();
886 if(buff.IsEmpty()) continue;
888 int start, end;
889 int c = swscanf(buff, L"{%d}{%d}", &start, &end);
891 if(c != 2) {c = swscanf(buff, L"{%d}{}", &start)+1; end = start + 60; fCheck = true;}
893 if(c != 2)
895 int i;
896 if(buff.Find('{') == 0 && (i = buff.Find('}')) > 1 && i < buff.GetLength())
898 if(STSStyle* s = GetMicroDVDStyle(WToT(buff.Mid(i+1)), CharSet))
900 style = buff.Mid(1, i-1);
901 style.MakeUpper();
902 if(style.GetLength()) {CString str = style.Mid(1); str.MakeLower(); style = style.Left(1) + str;}
903 ret.AddStyle(style, s);
904 CharSet = s->charSet;
905 continue;
910 if(c == 2)
912 if(fCheck2 && !ret.IsEmpty())
914 STSEntry& stse = ret.m_entries[ret.m_entries.GetCount()-1];
915 stse.end = min(stse.end, start);
916 fCheck2 = false;
919 ret.Add(
920 MicroDVD2SSA(buff.Mid(buff.Find('}', buff.Find('}')+1)+1), file->IsUnicode(), CharSet),
921 file->IsUnicode(),
922 start, end,
923 style);
925 if(fCheck)
927 fCheck = false;
928 fCheck2 = true;
931 else if(c != EOF) // might be another format
933 return(false);
937 return(!ret.IsEmpty());
940 static void ReplaceNoCase(CStringW& str, CStringW from, CStringW to)
942 CStringW lstr = str;
943 lstr.MakeLower();
945 int i, j, k;
947 for(i = 0, j = str.GetLength(); i < j; )
949 if((k = lstr.Find(from, i)) >= 0)
951 str.Delete(k, from.GetLength()); lstr.Delete(k, from.GetLength());
952 str.Insert(k, to); lstr.Insert(k, to);
953 i = k + to.GetLength();
954 j = str.GetLength();
956 else break;
960 static CStringW SMI2SSA(CStringW str, int CharSet)
962 ReplaceNoCase(str, L"&nbsp;", L" ");
963 ReplaceNoCase(str, L"&quot;", L"\"");
964 ReplaceNoCase(str, L"<br>", L"\\N");
965 ReplaceNoCase(str, L"<i>", L"{\\i1}");
966 ReplaceNoCase(str, L"</i>", L"{\\i}");
967 ReplaceNoCase(str, L"<b>", L"{\\b1}");
968 ReplaceNoCase(str, L"</b>", L"{\\b}");
970 CStringW lstr = str;
971 lstr.MakeLower();
973 // maven@maven.de
974 // now parse line
975 for(int i = 0, j = str.GetLength(); i < j; )
977 int k;
978 if((k = lstr.Find('<', i)) < 0) break;
980 int chars_inserted = 0;
982 int l = 1;
983 for(; k+l < j && lstr[k+l] != '>'; l++);
984 l++;
986 // Modified by Cookie Monster
987 if (lstr.Find(L"<font ", k) == k)
989 CStringW args = lstr.Mid(k+6, l-6); // delete "<font "
990 CStringW arg ;
992 args.Remove('\"'); args.Remove('#'); // may include 2 * " + #
993 arg.TrimLeft(); arg.TrimRight(L" >");
995 for (;;)
997 args.TrimLeft();
998 arg = args.SpanExcluding(L" \t>");
999 args = args.Mid(arg.GetLength());
1001 if(arg.IsEmpty())
1002 break;
1003 if (arg.Find(L"color=") == 0 )
1005 DWORD color;
1007 arg = arg.Mid(6); // delete "color="
1008 if ( arg.IsEmpty())
1009 continue;
1011 DWORD val;
1012 if(g_colors.Lookup(CString(arg), val))
1013 color = (DWORD)val;
1014 else if((color = wcstol(arg, NULL, 16) ) == 0)
1015 color = 0x00ffffff; // default is white
1017 arg.Format(L"%02x%02x%02x", color&0xff, (color>>8)&0xff, (color>>16)&0xff);
1018 lstr.Insert(k + l + chars_inserted, CStringW(L"{\\c&H") + arg + L"&}");
1019 str.Insert(k + l + chars_inserted, CStringW(L"{\\c&H") + arg + L"&}");
1020 chars_inserted += 5 + arg.GetLength() + 2;
1023 else if (arg.Find(_T("size=" )) == 0 )
1025 uint fsize;
1027 arg = arg.Mid(5); // delete "size="
1028 if ( arg.GetLength() == 0)
1029 continue;
1031 if ( fsize = _tcstol(arg, &tmp, 10) == 0 )
1032 continue;
1034 lstr.Insert(k + l + chars_inserted, CString(_T("{\\fs")) + arg + _T("&}"));
1035 str.Insert(k + l + chars_inserted, CString(_T("{\\fs")) + arg + _T("&}"));
1036 chars_inserted += 4 + arg.GetLength() + 2;
1042 // Original Code
1044 if (lstr.Find(L"<font color=", k) == k)
1046 CStringW arg = lstr.Mid(k+12, l-12); // may include 2 * " + #
1048 arg.Remove('\"');
1049 arg.Remove('#');
1050 arg.TrimLeft(); arg.TrimRight(L" >");
1052 if(arg.GetLength() > 0)
1054 DWORD color;
1056 CString key = WToT(arg);
1057 void* val;
1058 if(g_colors.Lookup(key, val)) color = (DWORD)val;
1059 else color = wcstol(arg, NULL, 16);
1061 arg.Format(L"%02x%02x%02x", color&0xff, (color>>8)&0xff, (color>>16)&0xff);
1064 lstr.Insert(k + l + chars_inserted, L"{\\c&H" + arg + L"&}");
1065 str.Insert(k + l + chars_inserted, L"{\\c&H" + arg + L"&}");
1066 chars_inserted += 5 + arg.GetLength() + 2;
1069 else if (lstr.Find(L"</font>", k) == k)
1071 lstr.Insert(k + l + chars_inserted, L"{\\c}");
1072 str.Insert(k + l + chars_inserted, L"{\\c}");
1073 chars_inserted += 4;
1076 str.Delete(k, l); lstr.Delete(k, l);
1077 i = k + chars_inserted;
1078 j = str.GetLength();
1081 return(str);
1084 static bool OpenSami(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet)
1086 CStringW buff, caption;
1088 ULONGLONG pos = file->GetPosition();
1090 bool fSAMI = false;
1092 while(file->ReadString(buff) && !fSAMI)
1094 if(buff.MakeUpper().Find(L"<SAMI>") >= 0) fSAMI = true;
1097 if(!fSAMI) return(false);
1099 file->Seek(pos, 0);
1101 bool fComment = false;
1103 int start_time = 0;
1105 while(file->ReadString(buff))
1107 buff.Trim();
1108 if(buff.IsEmpty()) continue;
1110 CStringW ubuff = buff;
1111 ubuff.MakeUpper();
1113 if(ubuff.Find(L"<!--") >= 0 || ubuff.Find(L"<TITLE>") >= 0)
1114 fComment = true;
1116 if(!fComment)
1118 int i;
1120 if((i = ubuff.Find(L"<SYNC START=")) >= 0)
1122 int time = 0;
1124 for(i = 12; i < ubuff.GetLength(); i++)
1126 if(ubuff[i] != '>' && ubuff[i] != 'M')
1128 if(iswdigit(ubuff[i]))
1130 time *= 10;
1131 time += ubuff[i] - 0x30;
1134 else break;
1137 ret.Add(
1138 SMI2SSA(caption, CharSet),
1139 file->IsUnicode(),
1140 start_time, time);
1142 start_time = time;
1143 caption.Empty();
1146 caption += buff;
1149 if(ubuff.Find(L"-->") >= 0 || ubuff.Find(L"</TITLE>") >= 0)
1150 fComment = false;
1153 ret.Add(
1154 SMI2SSA(caption, CharSet),
1155 file->IsUnicode(),
1156 start_time, MAXLONG);
1158 return(true);
1161 static bool OpenVPlayer(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet)
1163 CStringW buff;
1164 while(file->ReadString(buff))
1166 buff.Trim();
1167 if(buff.IsEmpty()) continue;
1169 for(int i = 0; i < buff.GetLength(); i++)
1171 if((i = FindChar(buff, '|', i, file->IsUnicode(), CharSet)) < 0) break;
1172 buff.SetAt(i, '\n');
1175 int hh, mm, ss;
1176 int c = swscanf(buff, L"%d:%d:%d:", &hh, &mm, &ss);
1178 if(c == 3)
1180 CStringW str = buff.Mid(buff.Find(':', buff.Find(':', buff.Find(':')+1)+1)+1);
1181 ret.Add(str,
1182 file->IsUnicode(),
1183 (((hh*60 + mm)*60) + ss)*1000,
1184 (((hh*60 + mm)*60) + ss)*1000 + 1000 + 50*str.GetLength());
1186 else if(c != EOF) // might be another format
1188 return(false);
1192 return(!ret.IsEmpty());
1195 inline CStringW GetStr(CStringW& buff, char sep = ',') //throw(...)
1197 buff.TrimLeft();
1199 int pos = buff.Find(sep);
1200 if(pos < 0)
1202 pos = buff.GetLength();
1203 if(pos < 1) throw 1;
1206 CStringW ret = buff.Left(pos);
1207 if(pos < buff.GetLength()) buff = buff.Mid(pos+1);
1209 return(ret);
1212 inline int GetInt(CStringW& buff, char sep = ',') //throw(...)
1214 CStringW str;
1216 str = GetStr(buff, sep);
1217 str.MakeLower();
1219 CStringW fmtstr = str.GetLength() > 2 && (str.Left(2) == L"&h" || str.Left(2) == L"0x")
1220 ? str = str.Mid(2), L"%x"
1221 : L"%d";
1223 int ret;
1224 if(swscanf(str, fmtstr, &ret) != 1) throw 1;
1226 return(ret);
1229 inline double GetFloat(CStringW& buff, char sep = ',') //throw(...)
1231 CStringW str;
1233 str = GetStr(buff, sep);
1234 str.MakeLower();
1236 float ret;
1237 if(swscanf(str, L"%f", &ret) != 1) throw 1;
1239 return((double)ret);
1242 inline CStringW::PCXSTR TryNextStr(CStringW::PXSTR * buff, WCHAR sep = WCHAR(','))
1244 CStringW::PXSTR start = NULL;
1245 CStringW::PXSTR ret = NULL;
1246 for(start=*buff; *start!=0 && *start==WCHAR(' '); start++) ;
1248 *buff=start;
1249 ret = start;
1251 for( ;*start!=0 && *start!=sep; start++) ;
1253 if(*start!=0)
1254 *buff = start+1;
1256 *start = 0;
1258 return(ret);
1261 inline int NextInt(CStringW::PXSTR * buff, WCHAR sep = WCHAR(',')) //throw(...)
1263 CStringW str;
1265 str = TryNextStr(buff, sep);
1266 str.MakeLower();
1268 CStringW fmtstr = str.GetLength() > 2 && (str.Left(2) == L"&h" || str.Left(2) == L"0x")
1269 ? str = str.Mid(2), L"%x"
1270 : L"%d";
1272 int ret;
1273 if(swscanf(str, fmtstr, &ret) != 1) throw 1;
1275 return(ret);
1278 inline double NextFloat(CStringW::PXSTR * buff, WCHAR sep = WCHAR(',')) //throw(...)
1280 CStringW str;
1282 str = TryNextStr(buff, sep);
1283 str.MakeLower();
1285 float ret;
1286 if(swscanf(str, L"%f", &ret) != 1) throw 1;
1288 return((double)ret);
1291 static bool LoadFont(CString& font)
1293 int len = font.GetLength();
1295 CAutoVectorPtr<BYTE> pData;
1296 if(len == 0 || (len&3) == 1 || !pData.Allocate(len))
1297 return(false);
1299 const TCHAR* s = font;
1300 const TCHAR* e = s + len;
1301 for(BYTE* p = pData; s < e; s++, p++) *p = *s - 33;
1303 for(int i = 0, j = 0, k = len&~3; i < k; i+=4, j+=3)
1305 pData[j+0] = ((pData[i+0]&63)<<2)|((pData[i+1]>>4)& 3);
1306 pData[j+1] = ((pData[i+1]&15)<<4)|((pData[i+2]>>2)&15);
1307 pData[j+2] = ((pData[i+2]& 3)<<6)|((pData[i+3]>>0)&63);
1310 int datalen = (len&~3)*3/4;
1312 if((len&3) == 2)
1314 pData[datalen++] = ((pData[(len&~3)+0]&63)<<2)|((pData[(len&~3)+1]>>4)&3);
1316 else if((len&3) == 3)
1318 pData[datalen++] = ((pData[(len&~3)+0]&63)<<2)|((pData[(len&~3)+1]>>4)& 3);
1319 pData[datalen++] = ((pData[(len&~3)+1]&15)<<4)|((pData[(len&~3)+2]>>2)&15);
1322 HANDLE hFont = INVALID_HANDLE_VALUE;
1324 if(HMODULE hModule = LoadLibrary(_T("GDI32.DLL")))
1326 typedef HANDLE (WINAPI *PAddFontMemResourceEx)( IN PVOID, IN DWORD, IN PVOID , IN DWORD*);
1327 if(PAddFontMemResourceEx f = (PAddFontMemResourceEx)GetProcAddress(hModule, "AddFontMemResourceEx"))
1329 DWORD cFonts;
1330 hFont = f(pData, datalen, NULL, &cFonts);
1333 FreeLibrary(hModule);
1336 if(hFont == INVALID_HANDLE_VALUE)
1338 TCHAR path[MAX_PATH];
1339 GetTempPath(MAX_PATH, path);
1341 DWORD chksum = 0;
1342 for(int i = 0, j = datalen>>2; i < j; i++)
1343 chksum += ((DWORD*)(BYTE*)pData)[i];
1345 CString fn;
1346 fn.Format(_T("%sfont%08x.ttf"), path, chksum);
1348 CFileStatus fs;
1349 if(!CFileGetStatus(fn, fs))
1351 CFile f;
1352 if(f.Open(fn, CFile::modeCreate|CFile::modeWrite|CFile::typeBinary|CFile::shareDenyWrite))
1354 f.Write(pData, datalen);
1355 f.Close();
1359 AddFontResource(fn);
1362 return(true);
1365 static bool LoadUUEFont(CTextFile* file)
1367 CString s, font;
1368 while(file->ReadString(s))
1370 s.Trim();
1371 if(s.IsEmpty() || s[0] == '[') break;
1372 if(s.Find(_T("fontname:")) == 0) {LoadFont(font); font.Empty(); continue;}
1374 font += s;
1377 if(!font.IsEmpty())
1378 LoadFont(font);
1380 return(true);
1383 static bool OpenSubStationAlpha(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet)
1385 bool fRet = false;
1387 int version = 3, sver = 3;
1389 CStringW buff;
1390 while(file->ReadString(buff))
1392 buff.Trim();
1393 if(buff.IsEmpty() || buff.GetAt(0) == ';') continue;
1395 CStringW entry;
1397 // try {
1398 entry = GetStr(buff, ':');
1399 // }
1400 // catch(...) {continue;}
1402 entry.MakeLower();
1404 if(entry == L"dialogue")
1408 CStringW::PXSTR __buff = buff.GetBuffer();
1409 int hh1, mm1, ss1, ms1_div10, hh2, mm2, ss2, ms2_div10, layer = 0;
1410 CString Style, Actor, Effect;
1411 CRect marginRect;
1413 if(version <= 4){TryNextStr(&__buff, '='); NextInt(&__buff);} /* Marked = */
1414 if(version >= 5)layer = NextInt(&__buff);
1415 hh1 = NextInt(&__buff, ':');
1416 mm1 = NextInt(&__buff, ':');
1417 ss1 = NextInt(&__buff, '.');
1418 ms1_div10 = NextInt(&__buff);
1419 hh2 = NextInt(&__buff, ':');
1420 mm2 = NextInt(&__buff, ':');
1421 ss2 = NextInt(&__buff, '.');
1422 ms2_div10 = NextInt(&__buff);
1423 Style = WToT(TryNextStr(&__buff));
1424 Actor = WToT(TryNextStr(&__buff));
1425 marginRect.left = NextInt(&__buff);
1426 marginRect.right = NextInt(&__buff);
1427 marginRect.top = marginRect.bottom = NextInt(&__buff);
1428 if(version >= 6)marginRect.bottom = NextInt(&__buff);
1429 Effect = WToT(TryNextStr(&__buff));
1431 CStringW buff2 = __buff;
1432 int len = min(Effect.GetLength(), buff2.GetLength());
1433 if(Effect.Left(len) == WToT(buff2.Left(len))) Effect.Empty();
1435 Style.TrimLeft('*');
1436 if(!Style.CompareNoCase(_T("Default"))) Style = _T("Default");
1438 ret.AddSTSEntryOnly(buff2,
1439 file->IsUnicode(),
1440 (((hh1*60 + mm1)*60) + ss1)*1000 + ms1_div10*10,
1441 (((hh2*60 + mm2)*60) + ss2)*1000 + ms2_div10*10,
1442 Style, Actor, Effect,
1443 marginRect,
1444 layer);
1446 catch(...)
1448 // ASSERT(0);
1449 // throw;
1450 return(false);
1453 else if(entry == L"[script info]")
1455 fRet = true;
1457 else if(entry == L"playresx")
1459 try {ret.m_dstScreenSize.cx = GetInt(buff);}
1460 catch(...) {ret.m_dstScreenSize = CSize(0, 0); return(false);}
1462 if(ret.m_dstScreenSize.cy <= 0)
1464 ret.m_dstScreenSize.cy = (ret.m_dstScreenSize.cx == 1280)
1465 ? 1024
1466 : ret.m_dstScreenSize.cx * 3 / 4;
1469 else if(entry == L"playresy")
1471 try {ret.m_dstScreenSize.cy = GetInt(buff);}
1472 catch(...) {ret.m_dstScreenSize = CSize(0, 0); return(false);}
1474 if(ret.m_dstScreenSize.cx <= 0)
1476 ret.m_dstScreenSize.cx = (ret.m_dstScreenSize.cy == 1024)
1477 ? 1280
1478 : ret.m_dstScreenSize.cy * 4 / 3;
1481 else if(entry == L"wrapstyle")
1483 try {ret.m_defaultWrapStyle = GetInt(buff);}
1484 catch(...) {ret.m_defaultWrapStyle = 1; return(false);}
1486 else if(entry == L"scripttype")
1488 if(buff.GetLength() >= 4 && !buff.Right(4).CompareNoCase(L"4.00")) version = sver = 4;
1489 else if(buff.GetLength() >= 5 && !buff.Right(5).CompareNoCase(L"4.00+")) version = sver = 5;
1490 else if(buff.GetLength() >= 6 && !buff.Right(6).CompareNoCase(L"4.00++")) version = sver = 6;
1492 else if(entry == L"collisions")
1494 buff = GetStr(buff);
1495 buff.MakeLower();
1496 ret.m_collisions = buff.Find(L"reverse") >= 0 ? 1 : 0;
1498 else if(entry == L"scaledborderandshadow")
1500 buff = GetStr(buff);
1501 buff.MakeLower();
1502 ret.m_fScaledBAS = buff.Find(L"yes") >= 0;
1504 else if(entry == L"[v4 styles]")
1506 fRet = true;
1507 sver = 4;
1509 else if(entry == L"[v4+ styles]")
1511 fRet = true;
1512 sver = 5;
1514 else if(entry == L"[v4++ styles]")
1516 fRet = true;
1517 sver = 6;
1519 else if(entry == L"style")
1521 STSStyle* style = new STSStyle;
1522 if(!style) return(false);
1526 CString StyleName;
1527 int alpha;
1528 CRect tmp_rect;
1530 StyleName = WToT(GetStr(buff));
1531 style->fontName = WToT(GetStr(buff));
1532 style->fontSize = GetFloat(buff);
1533 for(int i = 0; i < 4; i++) style->colors[i] = (COLORREF)GetInt(buff);
1534 style->fontWeight = !!GetInt(buff) ? FW_BOLD : FW_NORMAL;
1535 style->fItalic = !!GetInt(buff);
1536 if(sver >= 5) style->fUnderline = !!GetInt(buff);
1537 if(sver >= 5) style->fStrikeOut = !!GetInt(buff);
1538 if(sver >= 5) style->fontScaleX = GetFloat(buff);
1539 if(sver >= 5) style->fontScaleY = GetFloat(buff);
1540 if(sver >= 5) style->fontSpacing = GetFloat(buff);
1541 if(sver >= 5) style->fontAngleZ = GetFloat(buff);
1542 if(sver >= 4) style->borderStyle = GetInt(buff);
1543 style->outlineWidthX = style->outlineWidthY = GetFloat(buff);
1544 style->shadowDepthX = style->shadowDepthY = GetFloat(buff);
1545 style->scrAlignment = GetInt(buff);
1546 tmp_rect.left = GetInt(buff);
1547 tmp_rect.right = GetInt(buff);
1548 tmp_rect.top = tmp_rect.bottom = GetInt(buff);
1549 if(sver >= 6) tmp_rect.bottom = GetInt(buff);
1550 style->marginRect = tmp_rect;
1551 if(sver <= 4) alpha = GetInt(buff);
1552 style->charSet = GetInt(buff);
1553 if(sver >= 6) style->relativeTo = GetInt(buff);
1555 if(sver <= 4) style->colors[2] = style->colors[3]; // style->colors[2] is used for drawing the outline
1556 if(sver <= 4) alpha = max(min(alpha, 0xff), 0);
1557 if(sver <= 4) {for(int i = 0; i < 3; i++) style->alpha[i] = alpha; style->alpha[3] = 0x80;}
1558 if(sver >= 5) for(int i = 0; i < 4; i++) {style->alpha[i] = (BYTE)(style->colors[i]>>24); style->colors[i] &= 0xffffff;}
1559 if(sver >= 5) style->fontScaleX = max(style->fontScaleX, 0);
1560 if(sver >= 5) style->fontScaleY = max(style->fontScaleY, 0);
1561 if(sver >= 5) style->fontSpacing = max(style->fontSpacing, 0);
1562 style->fontAngleX = style->fontAngleY = 0;
1563 style->borderStyle = style->borderStyle == 1 ? 0 : style->borderStyle == 3 ? 1 : 0;
1564 style->outlineWidthX = max(style->outlineWidthX, 0);
1565 style->outlineWidthY = max(style->outlineWidthY, 0);
1566 style->shadowDepthX = max(style->shadowDepthX, 0);
1567 style->shadowDepthY = max(style->shadowDepthY, 0);
1568 if(sver <= 4) style->scrAlignment = (style->scrAlignment&4) ? ((style->scrAlignment&3)+6) // top
1569 : (style->scrAlignment&8) ? ((style->scrAlignment&3)+3) // mid
1570 : (style->scrAlignment&3); // bottom
1572 StyleName.TrimLeft('*');
1574 ret.AddStyle(StyleName, style);
1576 catch(...)
1578 delete style;
1579 return(false);
1582 else if(entry == L"[events]")
1584 fRet = true;
1586 else if(entry == L"fontname")
1588 LoadUUEFont(file);
1590 else if(entry == L"ycbcr matrix")
1592 buff = GetStr(buff);
1593 buff.MakeLower();
1594 if (buff=="none")
1596 ret.m_eYCbCrMatrix = CSimpleTextSubtitle::YCbCrMatrix_AUTO;
1597 ret.m_eYCbCrRange = CSimpleTextSubtitle::YCbCrRange_AUTO;
1599 else if (buff=="tv.601")
1601 ret.m_eYCbCrMatrix = CSimpleTextSubtitle::YCbCrMatrix_BT601;
1602 ret.m_eYCbCrRange = CSimpleTextSubtitle::YCbCrRange_TV;
1604 else if (buff=="tv.709")
1606 ret.m_eYCbCrMatrix = CSimpleTextSubtitle::YCbCrMatrix_BT709;
1607 ret.m_eYCbCrRange = CSimpleTextSubtitle::YCbCrRange_TV;
1609 else if (buff=="pc.601")
1611 ret.m_eYCbCrMatrix = CSimpleTextSubtitle::YCbCrMatrix_BT601;
1612 ret.m_eYCbCrRange = CSimpleTextSubtitle::YCbCrRange_PC;
1614 else if (buff=="pc.709")
1616 ret.m_eYCbCrMatrix = CSimpleTextSubtitle::YCbCrMatrix_BT709;
1617 ret.m_eYCbCrRange = CSimpleTextSubtitle::YCbCrRange_PC;
1621 // ret.Sort();
1622 return(fRet);
1625 static bool OpenXombieSub(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet)
1627 float version = 0;
1629 // CMapStringToPtr stylemap;
1631 CStringW buff;
1632 while(file->ReadString(buff))
1634 buff.Trim();
1635 if(buff.IsEmpty() || buff.GetAt(0) == ';') continue;
1637 CStringW entry;
1639 // try {
1640 entry = GetStr(buff, '=');
1641 // }
1642 // catch(...) {continue;}
1644 entry.MakeLower();
1646 if(entry == L"version")
1648 version = (float)GetFloat(buff);
1650 else if(entry == L"screenhorizontal")
1652 try {ret.m_dstScreenSize.cx = GetInt(buff);}
1653 catch(...) {ret.m_dstScreenSize = CSize(0, 0); return(false);}
1655 if(ret.m_dstScreenSize.cy <= 0)
1657 ret.m_dstScreenSize.cy = (ret.m_dstScreenSize.cx == 1280)
1658 ? 1024
1659 : ret.m_dstScreenSize.cx * 3 / 4;
1662 else if(entry == L"screenvertical")
1664 try {ret.m_dstScreenSize.cy = GetInt(buff);}
1665 catch(...) {ret.m_dstScreenSize = CSize(0, 0); return(false);}
1667 if(ret.m_dstScreenSize.cx <= 0)
1669 ret.m_dstScreenSize.cx = (ret.m_dstScreenSize.cy == 1024)
1670 ? 1280
1671 : ret.m_dstScreenSize.cy * 4 / 3;
1674 else if(entry == L"style")
1676 STSStyle* style = new STSStyle;
1677 if(!style) return(false);
1681 CString StyleName;
1682 CRect tmp_rect;
1684 StyleName = WToT(GetStr(buff)) + _T("_") + WToT(GetStr(buff));
1685 style->fontName = WToT(GetStr(buff));
1686 style->fontSize = GetFloat(buff);
1687 for(int i = 0; i < 4; i++) style->colors[i] = (COLORREF)GetInt(buff);
1688 for(int i = 0; i < 4; i++) style->alpha[i] = GetInt(buff);
1689 style->fontWeight = !!GetInt(buff) ? FW_BOLD : FW_NORMAL;
1690 style->fItalic = !!GetInt(buff);
1691 style->fUnderline = !!GetInt(buff);
1692 style->fStrikeOut = !!GetInt(buff);
1693 style->fBlur = !!GetInt(buff);
1694 style->fontScaleX = GetFloat(buff);
1695 style->fontScaleY = GetFloat(buff);
1696 style->fontSpacing = GetFloat(buff);
1697 style->fontAngleX = GetFloat(buff);
1698 style->fontAngleY = GetFloat(buff);
1699 style->fontAngleZ = GetFloat(buff);
1700 style->borderStyle = GetInt(buff);
1701 style->outlineWidthX = style->outlineWidthY = GetFloat(buff);
1702 style->shadowDepthX = style->shadowDepthY = GetFloat(buff);
1703 style->scrAlignment = GetInt(buff);
1705 tmp_rect.left = GetInt(buff);
1706 tmp_rect.right = GetInt(buff);
1707 tmp_rect.top = tmp_rect.bottom = GetInt(buff);
1708 style->marginRect = tmp_rect;
1710 style->charSet = GetInt(buff);
1712 style->fontScaleX = max(style->fontScaleX, 0);
1713 style->fontScaleY = max(style->fontScaleY, 0);
1714 style->fontSpacing = max(style->fontSpacing, 0);
1715 style->borderStyle = style->borderStyle == 1 ? 0 : style->borderStyle == 3 ? 1 : 0;
1716 style->outlineWidthX = max(style->outlineWidthX, 0);
1717 style->outlineWidthY = max(style->outlineWidthY, 0);
1718 style->shadowDepthX = max(style->shadowDepthX, 0);
1719 style->shadowDepthY = max(style->shadowDepthY, 0);
1721 ret.AddStyle(StyleName, style);
1723 catch(...)
1725 delete style;
1726 return(false);
1729 else if(entry == L"line")
1733 CString id;
1734 int hh1, mm1, ss1, ms1, hh2, mm2, ss2, ms2, layer = 0;
1735 CString Style, Actor;
1736 CRect marginRect;
1738 if(GetStr(buff) != L"D") continue;
1739 id = GetStr(buff);
1740 layer = GetInt(buff);
1741 hh1 = GetInt(buff, ':');
1742 mm1 = GetInt(buff, ':');
1743 ss1 = GetInt(buff, '.');
1744 ms1 = GetInt(buff);
1745 hh2 = GetInt(buff, ':');
1746 mm2 = GetInt(buff, ':');
1747 ss2 = GetInt(buff, '.');
1748 ms2 = GetInt(buff);
1749 Style = WToT(GetStr(buff)) + _T("_") + WToT(GetStr(buff));
1750 Actor = WToT(GetStr(buff));
1751 marginRect.left = GetInt(buff);
1752 marginRect.right = GetInt(buff);
1753 marginRect.top = marginRect.bottom = GetInt(buff);
1755 Style.TrimLeft('*');
1756 if(!Style.CompareNoCase(_T("Default"))) Style = _T("Default");
1758 ret.Add(buff,
1759 file->IsUnicode(),
1760 (((hh1*60 + mm1)*60) + ss1)*1000 + ms1,
1761 (((hh2*60 + mm2)*60) + ss2)*1000 + ms2,
1762 Style, Actor, _T(""),
1763 marginRect,
1764 layer);
1766 catch(...)
1768 return(false);
1771 else if(entry == L"fontname")
1773 LoadUUEFont(file);
1777 return(!ret.IsEmpty());
1780 #include "USFSubtitles.h"
1782 static bool OpenUSF(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet)
1784 CString str;
1785 while(file->ReadString(str))
1787 if(str.Find(_T("USFSubtitles")) >= 0)
1789 CUSFSubtitles usf;
1790 if(usf.Read(file->GetFilePath()) && usf.ConvertToSTS(ret))
1791 return(true);
1793 break;
1797 return(false);
1800 static CStringW MPL22SSA(CStringW str)
1802 CAtlList<CStringW> sl;
1803 Explode(str, sl, '|');
1804 POSITION pos = sl.GetHeadPosition();
1805 while(pos)
1807 CStringW& s = sl.GetNext(pos);
1808 if(s[0] == '/') {s = L"{\\i1}" + s.Mid(1) + L"{\\i0}";}
1810 str = Implode(sl, '\n');
1811 str.Replace(L"\n", L"\\N");
1812 return str;
1815 static bool OpenMPL2(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet)
1817 CStringW buff;;
1818 while(file->ReadString(buff))
1820 buff.Trim();
1821 if(buff.IsEmpty()) continue;
1823 int start, end;
1824 int c = swscanf(buff, L"[%d][%d]", &start, &end);
1826 if(c == 2)
1828 ret.Add(
1829 MPL22SSA(buff.Mid(buff.Find(']', buff.Find(']')+1)+1)),
1830 file->IsUnicode(),
1831 start*100, end*100);
1833 else if(c != EOF) // might be another format
1835 return(false);
1839 return(!ret.IsEmpty());
1842 typedef bool (*STSOpenFunct)(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet);
1844 static bool OpenRealText(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet);
1846 typedef struct {STSOpenFunct open; tmode mode;} OpenFunctStruct;
1848 static OpenFunctStruct OpenFuncts[] =
1850 OpenSubStationAlpha, TIME,
1851 OpenSubRipper, TIME,
1852 OpenOldSubRipper, TIME,
1853 OpenSubViewer, TIME,
1854 OpenMicroDVD, FRAME,
1855 OpenSami, TIME,
1856 OpenVPlayer, TIME,
1857 OpenXombieSub, TIME,
1858 OpenUSF, TIME,
1859 OpenMPL2, TIME,
1860 OpenRealText, TIME,
1863 static int nOpenFuncts = countof(OpenFuncts);
1867 CSimpleTextSubtitle::CSimpleTextSubtitle()
1869 m_mode = TIME;
1870 m_dstScreenSize = CSize(0, 0);
1871 m_defaultWrapStyle = 0;
1872 m_collisions = 0;
1873 m_fScaledBAS = false;
1874 m_encoding = CTextFile::ASCII;
1875 m_ePARCompensationType = EPCTDisabled;
1876 m_dPARCompensation = 1.0;
1877 m_eYCbCrMatrix = YCbCrMatrix_BT601;
1878 m_eYCbCrRange = YCbCrRange_TV;
1881 CSimpleTextSubtitle::~CSimpleTextSubtitle()
1883 Empty();
1886 CSimpleTextSubtitle::CSimpleTextSubtitle(CSimpleTextSubtitle& sts)
1888 *this = sts;
1891 CSimpleTextSubtitle& CSimpleTextSubtitle::operator = (CSimpleTextSubtitle& sts)
1893 Empty();
1895 m_name = sts.m_name;
1896 m_mode = sts.m_mode;
1897 m_dstScreenSize = sts.m_dstScreenSize;
1898 m_defaultWrapStyle = sts.m_defaultWrapStyle;
1899 m_collisions = sts.m_collisions;
1900 m_fScaledBAS = sts.m_fScaledBAS;
1901 m_fSSA = sts.m_fSSA;
1902 m_fUsingAutoGeneratedDefaultStyle = sts.m_fUsingAutoGeneratedDefaultStyle;
1903 CopyStyles(sts.m_styles);
1904 m_segments.Copy(sts.m_segments);
1905 Copy(sts);
1907 return(*this);
1911 void CSimpleTextSubtitle::Copy(CSimpleTextSubtitle& sts)
1913 Empty();
1915 m_name = sts.m_name;
1916 m_mode = sts.m_mode;
1917 m_dstScreenSize = sts.m_dstScreenSize;
1918 m_defaultWrapStyle = sts.m_defaultWrapStyle;
1919 m_collisions = sts.m_collisions;
1920 m_fScaledBAS = sts.m_fScaledBAS;
1921 m_encoding = sts.m_encoding;
1922 m_fUsingAutoGeneratedDefaultStyle = sts.m_fUsingAutoGeneratedDefaultStyle;
1923 CopyStyles(sts.m_styles);
1924 m_segments.Copy(sts.m_segments);
1925 m_entries.Copy(sts.m_entries);
1928 void CSimpleTextSubtitle::Append(CSimpleTextSubtitle& sts, int timeoff)
1930 if(timeoff < 0)
1932 timeoff = m_entries.GetCount() > 0 ? m_entries.GetAt(m_entries.GetCount()-1).end : 0;
1935 for(int i = 0, j = m_entries.GetCount(); i < j; i++)
1937 if(m_entries.GetAt(i).start > timeoff)
1939 m_entries.RemoveAt(i, j - i);
1940 break;
1944 CopyStyles(sts.m_styles, true);
1946 for(int i = 0, j = sts.m_entries.GetCount(); i < j; i++)
1948 STSEntry stse = sts.m_entries.GetAt(i);
1949 stse.start += timeoff;
1950 stse.end += timeoff;
1951 stse.readorder += m_entries.GetCount();
1952 m_entries.Add(stse);
1955 CreateSegments();
1958 void CSTSStyleMap::Free()
1960 POSITION pos = GetStartPosition();
1961 while(pos)
1963 CString key;
1964 STSStyle* val;
1965 GetNextAssoc(pos, key, val);
1966 delete val;
1969 RemoveAll();
1972 bool CSimpleTextSubtitle::CopyStyles(const CSTSStyleMap& styles, bool fAppend)
1974 if(!fAppend) m_styles.Free();
1976 POSITION pos = styles.GetStartPosition();
1977 while(pos)
1979 CString key;
1980 STSStyle* val;
1981 styles.GetNextAssoc(pos, key, val);
1983 STSStyle* s = new STSStyle;
1984 if(!s) return(false);
1986 *s = *val;
1988 AddStyle(key, s);
1991 return(true);
1994 void CSimpleTextSubtitle::Empty()
1996 m_dstScreenSize = CSize(0, 0);
1997 m_styles.Free();
1998 m_segments.RemoveAll();
1999 m_entries.RemoveAll();
2002 void CSimpleTextSubtitle::Add(CStringW str, bool fUnicode, int start, int end,
2003 CString style, const CString& actor, const CString& effect, const CRect& marginRect, int layer, int readorder)
2005 XY_LOG_INFO(start<<_T(" ")<<end<<_T(" ")<<str.GetString()<<_T(" style:")<<style.GetString()
2006 <<_T(" Unicode:")<<fUnicode
2007 <<_T(" actor:")<<actor.GetString()<<_T(" effect:")<<effect.GetString()
2008 <<_T(" (l:")<<marginRect.left<<_T(",t:")<<marginRect.top<<_T(",r:")<<marginRect.right<<_T(",b:")<<marginRect.bottom
2009 <<_T(" layer:")<<layer<<_T(" readorder:")<<readorder
2010 <<_T(" entries:")<<m_entries.GetCount()<<_T(" seg:")<<m_segments.GetCount());
2012 if(start > end || str.Trim().IsEmpty() ) return;
2014 str.Remove('\r');
2015 str.Replace(L"\n", L"\\N");
2016 if(style.IsEmpty()) style = g_default_style;
2017 else if(style!=g_default_style)
2019 style.TrimLeft('*');
2022 STSEntry sub;
2023 sub.str = str;
2024 sub.fUnicode = fUnicode;
2025 sub.style = style;
2026 sub.actor = actor;
2027 sub.effect = effect;
2028 sub.marginRect = marginRect;
2029 sub.layer = layer;
2030 sub.start = start;
2031 sub.end = end;
2032 sub.readorder = readorder < 0 ? m_entries.GetCount() : readorder;
2033 int n = m_entries.Add(sub);
2035 int len = m_segments.GetCount();
2037 if(len == 0)
2039 STSSegment stss(start, end);
2040 stss.subs.Add(n);
2041 m_segments.Add(stss);
2043 else if(end <= m_segments[0].start)
2045 STSSegment stss(start, end);
2046 stss.subs.Add(n);
2047 m_segments.InsertAt(0, stss);
2049 else if(start >= m_segments[len-1].end)
2051 STSSegment stss(start, end);
2052 stss.subs.Add(n);
2053 m_segments.Add(stss);
2055 else
2057 if(start < m_segments[0].start)
2059 STSSegment stss(start, m_segments[0].start);
2060 stss.subs.Add(n);
2061 start = m_segments[0].start;
2062 m_segments.InsertAt(0, stss);
2065 for(size_t i = 0; i < m_segments.GetCount(); i++)
2067 STSSegment& s = m_segments[i];
2069 if(start >= s.end)
2071 continue;
2073 else if(end <= s.start)
2075 break;
2077 else if(s.start < start && start < s.end)
2079 STSSegment stss(s.start, start);
2080 stss.subs.Copy(s.subs);
2081 s.start = start;
2082 m_segments.InsertAt(i, stss);
2083 continue;
2085 if(start <= s.start && s.end <= end)
2087 for(int j = 0, k = s.subs.GetCount(); j <= k; j++)
2089 if(j == k || sub.readorder < m_entries.GetAt(s.subs[j]).readorder)
2091 s.subs.InsertAt(j, n);
2092 break;
2096 else if(s.start < end && end < s.end)
2098 STSSegment stss(s.start, end);
2099 stss.subs.Copy(s.subs);
2100 for(int j = 0, k = s.subs.GetCount(); j <= k; j++)
2102 if(j == k || sub.readorder < m_entries.GetAt(stss.subs[j]).readorder)
2104 stss.subs.InsertAt(j, n);
2105 break;
2108 s.start = end;
2109 m_segments.InsertAt(i, stss);
2113 if(end > m_segments[m_segments.GetCount()-1].end)
2115 STSSegment stss(m_segments[m_segments.GetCount()-1].end, end);
2116 stss.subs.Add(n);
2117 m_segments.Add(stss);
2121 str.Remove('\r');
2122 str.Replace(L"\n", L"\\N");
2123 if(style.IsEmpty()) style = _T("Default");
2125 int j = m_segments.GetCount();
2126 for(int i = j-1; i >= 0; i--)
2128 if(m_segments[i].end <= start)
2130 break;
2132 else if(m_segments[i].start >= start)
2134 m_segments.SetCount(m_segments.GetCount()-1);
2135 j--;
2137 else if(m_segments[i].end > start)
2139 if(i < j-1) m_segments.RemoveAt(i+1, j-i-1);
2140 m_segments[i].end = start;
2141 break;
2145 if(m_segments.GetCount() == 0 && j > 0)
2146 CSTSArray::RemoveAll();
2148 STSSegment stss(start, end);
2149 int len = m_entries.GetCount();
2150 stss.subs.Add(len);
2151 m_segments.Add(stss);
2153 STSEntry sub;
2154 sub.str = str;
2155 sub.fUnicode = fUnicode;
2156 sub.style = style;
2157 sub.actor = actor;
2158 sub.effect = effect;
2159 sub.marginRect = marginRect;
2160 sub.layer = layer;
2161 sub.start = start;
2162 sub.end = end;
2163 sub.readorder = m_entries.GetCount();
2164 CSTSArray::Add(sub);
2168 void CSimpleTextSubtitle::AddSTSEntryOnly( CStringW str, bool fUnicode, int start, int end, CString style /*= _T("Default")*/, const CString& actor /*= _T("")*/, const CString& effect /*= _T("")*/, const CRect& marginRect /*= CRect(0,0,0,0)*/, int layer /*= 0*/, int readorder /*= -1*/ )
2170 if(str.Trim().IsEmpty() || start > end) return;
2172 str.Remove('\r');
2173 str.Replace(L"\n", L"\\N");
2174 if(style.IsEmpty()) style = _T("Default");
2175 style.TrimLeft('*');
2177 STSEntry sub;
2178 sub.str = str;
2179 sub.fUnicode = fUnicode;
2180 sub.style = style;
2181 sub.actor = actor;
2182 sub.effect = effect;
2183 sub.marginRect = marginRect;
2184 sub.layer = layer;
2185 sub.start = start;
2186 sub.end = end;
2187 sub.readorder = readorder < 0 ? m_entries.GetCount() : readorder;
2188 m_entries.Add(sub);
2189 return;
2192 STSStyle* CSimpleTextSubtitle::CreateDefaultStyle(int CharSet)
2194 STSStyle* ret = NULL;
2196 if(!m_styles.Lookup(g_default_style, ret))
2198 STSStyle* style = new STSStyle();
2199 style->charSet = CharSet;
2200 AddStyle(g_default_style, style);
2201 m_styles.Lookup(g_default_style, ret);
2203 m_fUsingAutoGeneratedDefaultStyle = true;
2205 else
2207 m_fUsingAutoGeneratedDefaultStyle = false;
2210 return ret;
2213 void CSimpleTextSubtitle::ChangeUnknownStylesToDefault()
2215 CAtlMap<CString, STSStyle*, CStringElementTraits<CString> > unknown;
2216 bool fReport = true;
2218 for(size_t i = 0; i < m_entries.GetCount(); i++)
2220 STSEntry& stse = m_entries.GetAt(i);
2222 STSStyle* val;
2223 if(!m_styles.Lookup(stse.style, val))
2225 if(!unknown.Lookup(stse.style, val))
2227 if(fReport)
2229 CString msg;
2230 msg.Format(_T("Unknown style found: \"%s\", changed to \"Default\"!\n\nPress Cancel to ignore further warnings."), stse.style);
2231 if(MessageBox(NULL, msg, _T("Warning"), MB_OKCANCEL|MB_ICONWARNING) != IDOK) fReport = false;
2234 unknown[stse.style] = NULL;
2237 stse.style = g_default_style;
2242 void CSimpleTextSubtitle::AddStyle(CString name, STSStyle* style)
2244 int i, j;
2246 if(name.IsEmpty()) name = g_default_style;
2248 STSStyle* val;
2249 if(m_styles.Lookup(name, val))
2251 if(*val == *style)
2253 delete style;
2254 return;
2256 const CString& name_str = name;
2258 int len = name_str.GetLength();
2260 for(i = len; i > 0 && _istdigit(name_str[i-1]); i--);
2262 int idx = 1;
2264 CString name2 = name_str;
2266 if(i < len && _stscanf(name_str.Right(len-i), _T("%d"), &idx) == 1)
2268 name2 = name_str.Left(i);
2271 idx++;
2273 CString name3;
2274 CString name3_str;
2277 name3_str.Format(_T("%s%d"), name2, idx);
2278 name3 = name3_str;
2279 idx++;
2281 while(m_styles.Lookup(name3));
2283 m_styles.RemoveKey(name);
2284 m_styles[name3] = val;
2286 for(i = 0, j = m_entries.GetCount(); i < j; i++)
2288 STSEntry& stse = m_entries.GetAt(i);
2289 if(stse.style == name) stse.style = name3;
2293 m_styles[name] = style;
2296 bool CSimpleTextSubtitle::SetDefaultStyle(STSStyle& s)
2298 DbgLog((LOG_TRACE, 3, "%s(%d): %s", __FILE__, __LINE__, __FUNCTION__));
2299 DbgLog((LOG_TRACE, 3, "\tm_styles count:%d", m_styles.GetCount()));
2300 STSStyle* val;
2301 if(!m_styles.Lookup(g_default_style, val)) return false;
2302 DbgLog((LOG_TRACE, 3, "\tm_styles Lookup Default succeed"));
2304 #ifdef DEBUG
2305 for(POSITION pos=m_styles.GetStartPosition(); pos!=NULL;)
2307 DbgLog((LOG_TRACE, 3, _T("\tm_styles[%s]"), (LPCTSTR)m_styles.GetNextKey(pos)));
2309 #endif
2311 *val = s;
2312 m_fUsingAutoGeneratedDefaultStyle = false;
2313 return true;
2316 bool CSimpleTextSubtitle::GetDefaultStyle(STSStyle& s)
2318 STSStyle* val;
2319 if(!m_styles.Lookup(g_default_style, val)) return false;
2320 s = *val;
2321 return true;
2324 void CSimpleTextSubtitle::ConvertToTimeBased(double fps)
2326 if(m_mode == TIME) return;
2328 for(int i = 0, j = m_entries.GetCount(); i < j; i++)
2330 STSEntry& stse = m_entries[i];
2331 stse.start = int(1.0 * stse.start * 1000 / fps + 0.5);
2332 stse.end = int(1.0 * stse.end * 1000 / fps + 0.5);
2335 m_mode = TIME;
2337 CreateSegments();
2340 void CSimpleTextSubtitle::ConvertToFrameBased(double fps)
2342 if(m_mode == FRAME) return;
2344 for(int i = 0, j = m_entries.GetCount(); i < j; i++)
2346 STSEntry& stse = m_entries[i];
2347 stse.start = int(1.0 * stse.start * fps / 1000 + 0.5);
2348 stse.end = int(1.0 * stse.end * fps / 1000 + 0.5);
2351 m_mode = FRAME;
2353 CreateSegments();
2356 int CSimpleTextSubtitle::SearchSub(int t, double fps)
2358 int i = 0, j = m_entries.GetCount() - 1, ret = -1;
2360 if(j >= 0 && t >= TranslateStart(j, fps))
2362 return(j);
2365 while(i < j)
2367 int mid = (i + j) >> 1;
2369 int midt = TranslateStart(mid, fps);
2371 if(t == midt)
2373 while(mid > 0 && t == TranslateStart(mid-1, fps)) mid--;
2374 ret = mid;
2375 break;
2377 else if(t < midt)
2379 ret = -1;
2380 if(j == mid) mid--;
2381 j = mid;
2383 else if(t > midt)
2385 ret = mid;
2386 if(i == mid) mid++;
2387 i = mid;
2391 return(ret);
2394 const STSSegment* CSimpleTextSubtitle::SearchSubs(int t, double fps, /*[out]*/ int* iSegment, int* nSegments)
2396 int segmentsCount = m_segments.GetCount();
2397 int i = 0, j = segmentsCount - 1;
2399 if(nSegments) *nSegments = segmentsCount;
2400 if(segmentsCount<=0)
2402 if(iSegment!=NULL)
2403 *iSegment = 0;
2404 return NULL;
2407 if(t >= TranslateSegmentEnd(j, fps))
2409 i = j;
2410 j++;
2412 if(t < TranslateSegmentEnd(i, fps))
2414 j = i;
2415 i--;
2418 while(i < j-1)
2420 int mid = (i + j) >> 1;
2422 int midt = TranslateSegmentEnd(mid, fps);
2424 if(t < midt)
2425 j=mid;
2426 else
2427 i=mid;
2429 if(iSegment!=NULL)
2430 *iSegment = j;
2431 if(j<segmentsCount)
2433 return &m_segments[j];
2435 return(NULL);
2438 STSSegment* CSimpleTextSubtitle::SearchSubs2(int t, double fps, /*[out]*/ int* iSegment, int* nSegments)
2440 int segmentsCount = m_segments.GetCount();
2441 int i = 0, j = segmentsCount - 1;
2443 if(iSegment) *iSegment = -1;
2444 if(nSegments) *nSegments = segmentsCount;
2446 if(segmentsCount<=0)
2448 if(iSegment) *iSegment = 0;
2449 return NULL;
2452 if(t >= TranslateSegmentEnd(j, fps))
2454 i = j;
2455 j++;
2457 if(t < TranslateSegmentEnd(i, fps))
2459 j = i;
2460 i--;
2463 while(i < j-1)
2465 int mid = (i + j) >> 1;
2467 int midt = TranslateSegmentEnd(mid, fps);
2469 if(t < midt)
2470 j=mid;
2471 else
2472 i=mid;
2474 if(j<segmentsCount && t>=TranslateSegmentStart(j, fps))
2476 if(iSegment) *iSegment = j;
2477 return &m_segments[j];
2479 return(NULL);
2484 int CSimpleTextSubtitle::TranslateStart(int i, double fps)
2486 return(i < 0 || m_entries.GetCount() <= i ? -1 :
2487 m_mode == TIME ? m_entries.GetAt(i).start :
2488 m_mode == FRAME ? (int)(m_entries.GetAt(i).start*1000/fps) :
2492 int CSimpleTextSubtitle::TranslateEnd(int i, double fps)
2494 return(i < 0 || m_entries.GetCount() <= i ? -1 :
2495 m_mode == TIME ? m_entries.GetAt(i).end :
2496 m_mode == FRAME ? (int)(m_entries.GetAt(i).end*1000/fps) :
2500 int CSimpleTextSubtitle::TranslateSegmentStart(int i, double fps)
2502 return(i < 0 || m_segments.GetCount() <= i ? -1 :
2503 m_mode == TIME ? m_segments[i].start :
2504 m_mode == FRAME ? (int)(m_segments[i].start*1000/fps) :
2508 int CSimpleTextSubtitle::TranslateSegmentEnd(int i, double fps)
2510 return(i < 0 || m_segments.GetCount() <= i ? -1 :
2511 m_mode == TIME ? m_segments[i].end :
2512 m_mode == FRAME ? (int)(m_segments[i].end*1000/fps) :
2516 void CSimpleTextSubtitle::TranslateSegmentStartEnd(int i, double fps, /*out*/int& start, /*out*/int& end)
2518 if(i < 0 || m_segments.GetCount() <= i)
2520 start=-1;
2521 end=-1;
2523 else
2525 if(m_mode == TIME)
2527 start = m_segments[i].start;
2528 end = m_segments[i].end;
2530 else //m_mode == FRAME
2532 start = (int)(m_segments[i].start*1000/fps);
2533 end = (int)(m_segments[i].end*1000/fps);
2538 STSStyle* CSimpleTextSubtitle::GetStyle(int i)
2540 STSStyle* style = NULL;
2541 m_styles.Lookup(m_entries.GetAt(i).style, style);
2543 STSStyle* defstyle = NULL;
2544 m_styles.Lookup(g_default_style, defstyle);
2546 if(!style)
2548 style = defstyle;
2551 ASSERT(style);
2553 return style;
2556 bool CSimpleTextSubtitle::GetStyle(int i, STSStyle* const stss)
2558 STSStyle* style = NULL;
2559 m_styles.Lookup(m_entries.GetAt(i).style, style);
2561 STSStyle* defstyle = NULL;
2562 m_styles.Lookup(g_default_style, defstyle);
2564 if(!style)
2566 if(!defstyle)
2568 defstyle = CreateDefaultStyle(DEFAULT_CHARSET);
2571 style = defstyle;
2574 if(!style) {ASSERT(0); return false;}
2576 *stss = *style;
2577 if(stss->relativeTo == 2 && defstyle)
2578 stss->relativeTo = defstyle->relativeTo;
2580 return true;
2583 int CSimpleTextSubtitle::GetCharSet(int i)
2585 STSStyle* style = GetStyle(i);
2586 return style!=NULL ? style->charSet : -1;
2589 bool CSimpleTextSubtitle::IsEntryUnicode(int i)
2591 return(m_entries.GetAt(i).fUnicode);
2594 void CSimpleTextSubtitle::ConvertUnicode(int i, bool fUnicode)
2596 STSEntry& stse = m_entries.GetAt(i);
2598 if(stse.fUnicode ^ fUnicode)
2600 int CharSet = GetCharSet(i);
2602 stse.str = fUnicode
2603 ? MBCSSSAToUnicode(stse.str, CharSet)
2604 : UnicodeSSAToMBCS(stse.str, CharSet);
2606 stse.fUnicode = fUnicode;
2610 CStringA CSimpleTextSubtitle::GetStrA(int i, bool fSSA)
2612 return(WToA(GetStrWA(i, fSSA)));
2615 CStringW CSimpleTextSubtitle::GetStrW(int i, bool fSSA)
2617 bool fUnicode = IsEntryUnicode(i);
2618 int CharSet = GetCharSet(i);
2620 CStringW str = m_entries.GetAt(i).str;
2622 if(!fUnicode)
2623 str = MBCSSSAToUnicode(str, CharSet);
2625 if(!fSSA)
2626 str = RemoveSSATags(str, fUnicode, CharSet);
2628 return(str);
2631 CStringW CSimpleTextSubtitle::GetStrWA(int i, bool fSSA)
2633 bool fUnicode = IsEntryUnicode(i);
2634 int CharSet = GetCharSet(i);
2636 CStringW str = m_entries.GetAt(i).str;
2638 if(fUnicode)
2639 str = UnicodeSSAToMBCS(str, CharSet);
2641 if(!fSSA)
2642 str = RemoveSSATags(str, fUnicode, CharSet);
2644 return(str);
2647 void CSimpleTextSubtitle::SetStr(int i, CStringA str, bool fUnicode)
2649 SetStr(i, AToW(str), false);
2652 void CSimpleTextSubtitle::SetStr(int i, CStringW str, bool fUnicode)
2654 STSEntry& stse = m_entries.GetAt(i);
2656 str.Replace(L"\n", L"\\N");
2658 if(stse.fUnicode && !fUnicode) stse.str = MBCSSSAToUnicode(str, GetCharSet(i));
2659 else if(!stse.fUnicode && fUnicode) stse.str = UnicodeSSAToMBCS(str, GetCharSet(i));
2660 else stse.str = str;
2663 static int comp1(const void* a, const void* b)
2665 int ret = ((STSEntry*)a)->start - ((STSEntry*)b)->start;
2666 if(ret == 0) ret = ((STSEntry*)a)->layer - ((STSEntry*)b)->layer;
2667 if(ret == 0) ret = ((STSEntry*)a)->readorder - ((STSEntry*)b)->readorder;
2668 return(ret);
2671 static int comp2(const void* a, const void* b)
2673 return(((STSEntry*)a)->readorder - ((STSEntry*)b)->readorder);
2676 void CSimpleTextSubtitle::Sort(bool fRestoreReadorder)
2678 qsort(m_entries.GetData(), m_entries.GetCount(), sizeof(STSEntry), !fRestoreReadorder ? comp1 : comp2);
2679 CreateSegments();
2682 static int intcomp(const void* i1, const void* i2)
2684 return(*((int*)i1) - *((int*)i2));
2687 void CSimpleTextSubtitle::CreateSegments()
2689 m_segments.RemoveAll();
2691 if(m_entries.GetCount()>0)
2693 size_t start, mid, end;
2694 CAtlArray<STSSegment> tempSegments;//if add to m_segments directly, then remove empty entities can be a
2695 //complex operation when having large segmentCount and lots of empty entities
2696 std::vector<int> breakpoints(2*m_entries.GetCount());
2697 for(size_t i = 0; i < m_entries.GetCount(); i++)
2699 STSEntry& stse = m_entries.GetAt(i);
2700 breakpoints[2*i]=stse.start;
2701 breakpoints[2*i+1]=stse.end;
2704 std::sort(breakpoints.begin(), breakpoints.end());
2706 int ptr = 1, prev = breakpoints[0];
2707 for(size_t i = breakpoints.size()-1; i > 0; i--, ptr++)
2709 if(breakpoints[ptr] != prev)
2711 tempSegments.Add(STSSegment(prev, breakpoints[ptr]));
2712 prev = breakpoints[ptr];
2716 size_t segmentCount = tempSegments.GetCount();
2718 for(size_t i = 0; i < m_entries.GetCount(); i++)
2720 STSEntry& stse = m_entries.GetAt(i);
2721 start = 0;
2722 end = segmentCount;
2723 while(start<end)
2725 mid = (start+end)>>1;
2726 if(tempSegments[mid].start < stse.start)
2728 start = mid+1;
2730 else
2732 end = mid;
2735 for(; start < tempSegments.GetCount() && tempSegments[start].end <= stse.end; start++)
2736 tempSegments[start].subs.Add(i);
2738 for(size_t i = 0; i < segmentCount; i++)
2739 if(tempSegments[i].subs.GetCount()>0)
2740 m_segments.Add(tempSegments[i]);
2742 OnChanged();
2744 for(i = 0, j = m_segments.GetCount(); i < j; i++)
2746 STSSegment& stss = m_segments[i];
2748 TRACE(_T("%d - %d"), stss.start, stss.end);
2750 for(int k = 0, l = stss.subs.GetCount(); k < l; k++)
2752 TRACE(_T(", %d"), stss.subs[k]);
2755 TRACE(_T("\n"));
2760 bool CSimpleTextSubtitle::Open(CString fn, int CharSet, CString name)
2762 Empty();
2764 CWebTextFile f(CTextFile::UTF8);
2765 if(!f.Open(fn)) return(false);
2767 fn.Replace('\\', '/');
2768 if(name.IsEmpty())
2770 name = fn.Left(fn.ReverseFind('.'));
2771 name = name.Mid(name.ReverseFind('/')+1);
2772 name = name.Mid(name.ReverseFind('.')+1);
2775 return(Open(&f, CharSet, name));
2778 static int CountLines(CTextFile* f, ULONGLONG from, ULONGLONG to)
2780 int n = 0;
2781 CString s;
2782 f->Seek(from, 0);
2783 while(f->ReadString(s) && f->GetPosition() < to) n++;
2784 return(n);
2787 bool CSimpleTextSubtitle::Open(CTextFile* f, int CharSet, CString name)
2789 Empty();
2791 ULONGLONG pos = f->GetPosition();
2793 for(int i = 0; i < nOpenFuncts; i++)
2795 const TCHAR* func_name[]={
2796 TEXT("OpenSubStationAlpha"),
2797 TEXT("OpenSubRipper"),
2798 TEXT("OpenOldSubRipper"),
2799 TEXT("OpenSubViewer"),
2800 TEXT("OpenMicroDVD"),
2801 TEXT("OpenSami"),
2802 TEXT("OpenVPlayer"),
2803 TEXT("OpenXombieSub"),
2804 TEXT("OpenUSF"),
2805 TEXT("OpenMPL2"),
2806 TEXT("OpenRealText")};
2807 CAutoTiming t(func_name[i],0);
2809 if(!OpenFuncts[i].open(f, *this, CharSet) /*|| !GetCount()*/)
2811 if(m_entries.GetCount() > 0)
2813 int n = CountLines(f, pos, f->GetPosition());
2814 CString s;
2815 s.Format(_T("Syntax error at line %d!\t"), n+1);
2816 AfxMessageBox(s, MB_OK|MB_ICONERROR);
2817 Empty();
2818 break;
2821 f->Seek(pos, 0);
2822 Empty();
2823 continue;
2826 m_name = name;
2827 m_mode = OpenFuncts[i].mode;
2828 m_encoding = f->GetEncoding();
2829 m_path = f->GetFilePath();
2831 CWebTextFile f2(CTextFile::UTF8);
2832 if(f2.Open(f->GetFilePath() + _T(".style")))
2833 OpenSubStationAlpha(&f2, *this, CharSet);
2835 // Sort();
2836 CreateSegments();
2838 CreateDefaultStyle(CharSet);
2840 ChangeUnknownStylesToDefault();
2842 if(m_dstScreenSize == CSize(0, 0)) m_dstScreenSize = CSize(384, 288);
2844 return(true);
2847 return(false);
2850 bool CSimpleTextSubtitle::Open(BYTE* data, int len, int CharSet, CString name)
2852 TCHAR path[MAX_PATH];
2853 if(!GetTempPath(MAX_PATH, path)) return(false);
2855 TCHAR fn[MAX_PATH];
2856 if(!GetTempFileName(path, _T("vs"), 0, fn)) return(false);
2858 FILE* tmp = _tfopen(fn, _T("wb"));
2859 if(!tmp) return(false);
2861 int i = 0;
2862 for(; i <= (len-1024); i += 1024) fwrite(&data[i], 1024, 1, tmp);
2863 if(len > i) fwrite(&data[i], len - i, 1, tmp);
2865 fclose(tmp);
2867 bool fRet = Open(fn, CharSet, name);
2869 _tremove(fn);
2871 return(fRet);
2874 bool CSimpleTextSubtitle::SaveAs(CString fn, exttype et, double fps, CTextFile::enc e)
2876 if(fn.Mid(fn.ReverseFind('.')+1).CompareNoCase(exttypestr[et]))
2878 if(fn[fn.GetLength()-1] != '.') fn += _T(".");
2879 fn += exttypestr[et];
2882 CTextFile f;
2883 if(!f.Save(fn, e))
2884 return(false);
2886 if(et == EXTSMI)
2888 CString str;
2890 str += _T("<SAMI>\n<HEAD>\n");
2891 str += _T("<STYLE TYPE=\"text/css\">\n");
2892 str += _T("<!--\n");
2893 str += _T("P {margin-left: 16pt; margin-right: 16pt; margin-bottom: 16pt; margin-top: 16pt;\n");
2894 str += _T(" text-align: center; font-size: 18pt; font-family: arial; font-weight: bold; color: #f0f0f0;}\n");
2895 str += _T(".UNKNOWNCC {Name:Unknown; lang:en-US; SAMIType:CC;}\n");
2896 str += _T("-->\n");
2897 str += _T("</STYLE>\n");
2898 str += _T("</HEAD>\n");
2899 str += _T("\n");
2900 str += _T("<BODY>\n");
2902 f.WriteString(str);
2904 else if(et == EXTSSA || et == EXTASS)
2906 CString str;
2908 str = _T("[Script Info]\n");
2909 str += (et == EXTSSA) ? _T("; This is a Sub Station Alpha v4 script.\n") : _T("; This is an Advanced Sub Station Alpha v4+ script.\n");
2910 str += _T("; For Sub Station Alpha info and downloads,\n");
2911 str += _T("; go to http://www.eswat.demon.co.uk/\n");
2912 str += _T("; or email kotus@eswat.demon.co.uk\n");
2913 str += _T("; \n");
2914 if(et == EXTASS)
2916 str += _T("; Advanced Sub Station Alpha script format developed by #Anime-Fansubs@EfNET\n");
2917 str += _T("; http://www.anime-fansubs.org\n");
2918 str += _T("; \n");
2919 str += _T("; For additional info and downloads go to http://gabest.org/\n");
2920 str += _T("; or email gabest@freemail.hu\n");
2921 str += _T("; \n");
2923 str += _T("; Note: This file was saved by Subresync.\n");
2924 str += _T("; \n");
2925 str += (et == EXTSSA) ? _T("ScriptType: v4.00\n") : _T("ScriptType: v4.00+\n");
2926 str += (m_collisions == 0) ? _T("Collisions: Normal\n") : _T("Collisions: Reverse\n");
2927 if(et == EXTASS && m_fScaledBAS) str += _T("ScaledBorderAndShadow: Yes\n");
2928 str += _T("PlayResX: %d\n");
2929 str += _T("PlayResY: %d\n");
2930 str += _T("Timer: 100.0000\n");
2931 str += _T("\n");
2932 str += (et == EXTSSA)
2933 ? _T("[V4 Styles]\nFormat: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, TertiaryColour, BackColour, Bold, Italic, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, AlphaLevel, Encoding\n")
2934 : _T("[V4+ Styles]\nFormat: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding\n");
2936 CString str2;
2937 str2.Format(str, m_dstScreenSize.cx, m_dstScreenSize.cy);
2938 f.WriteString(str2);
2940 str = (et == EXTSSA)
2941 ? _T("Style: %s,%s,%d,&H%06x,&H%06x,&H%06x,&H%06x,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n")
2942 : _T("Style: %s,%s,%d,&H%08x,&H%08x,&H%08x,&H%08x,%d,%d,%d,%d,%d,%d,%d,%.2f,%d,%d,%d,%d,%d,%d,%d,%d\n");
2944 POSITION pos = m_styles.GetStartPosition();
2945 while(pos)
2947 CString key;
2948 STSStyle* s;
2949 m_styles.GetNextAssoc(pos, key, s);
2951 if(et == EXTSSA)
2953 CString str2;
2954 str2.Format(str, key,
2955 s->fontName, (int)s->fontSize,
2956 s->colors[0]&0xffffff,
2957 s->colors[1]&0xffffff,
2958 s->colors[2]&0xffffff,
2959 s->colors[3]&0xffffff,
2960 s->fontWeight > FW_NORMAL ? -1 : 0, s->fItalic ? -1 : 0,
2961 s->borderStyle == 0 ? 1 : s->borderStyle == 1 ? 3 : 0,
2962 (int)s->outlineWidthY, (int)s->shadowDepthY,
2963 s->scrAlignment <= 3 ? s->scrAlignment : s->scrAlignment <= 6 ? ((s->scrAlignment-3)|8) : s->scrAlignment <= 9 ? ((s->scrAlignment-6)|4) : 2,
2964 s->marginRect.get().left, s->marginRect.get().right, (s->marginRect.get().top + s->marginRect.get().bottom) / 2,
2965 s->alpha[0],
2966 s->charSet);
2967 f.WriteString(str2);
2969 else
2971 CString str2;
2972 str2.Format(str, key,
2973 s->fontName, (int)s->fontSize,
2974 (s->colors[0]&0xffffff) | (s->alpha[0]<<24),
2975 (s->colors[1]&0xffffff) | (s->alpha[1]<<24),
2976 (s->colors[2]&0xffffff) | (s->alpha[2]<<24),
2977 (s->colors[3]&0xffffff) | (s->alpha[3]<<24),
2978 s->fontWeight > FW_NORMAL ? -1 : 0,
2979 s->fItalic ? -1 : 0, s->fUnderline ? -1 : 0, s->fStrikeOut ? -1 : 0,
2980 (int)s->fontScaleX, (int)s->fontScaleY,
2981 (int)s->fontSpacing, (float)s->fontAngleZ,
2982 s->borderStyle == 0 ? 1 : s->borderStyle == 1 ? 3 : 0,
2983 (int)s->outlineWidthY, (int)s->shadowDepthY,
2984 s->scrAlignment,
2985 s->marginRect.get().left, s->marginRect.get().right, (s->marginRect.get().top + s->marginRect.get().bottom) / 2,
2986 s->charSet);
2987 f.WriteString(str2);
2991 if(m_entries.GetCount() > 0)
2993 str = _T("\n");
2994 str += _T("[Events]\n");
2995 str += (et == EXTSSA)
2996 ? _T("Format: Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\n")
2997 : _T("Format: Layer, Start, End, Style, Actor, MarginL, MarginR, MarginV, Effect, Text\n");
2998 f.WriteString(str);
3002 CStringW fmt =
3003 et == EXTSRT ? L"%d\n%02d:%02d:%02d,%03d --> %02d:%02d:%02d,%03d\n%s\n\n" :
3004 et == EXTSUB ? L"{%d}{%d}%s\n" :
3005 et == EXTSMI ? L"<SYNC Start=%d><P Class=UNKNOWNCC>\n%s\n<SYNC Start=%d><P Class=UNKNOWNCC>&nbsp;\n" :
3006 et == EXTPSB ? L"{%d:%02d:%02d}{%d:%02d:%02d}%s\n" :
3007 et == EXTSSA ? L"Dialogue: Marked=0,%d:%02d:%02d.%02d,%d:%02d:%02d.%02d,%s,%s,%04d,%04d,%04d,%s,%s\n" :
3008 et == EXTASS ? L"Dialogue: %d,%d:%02d:%02d.%02d,%d:%02d:%02d.%02d,%s,%s,%04d,%04d,%04d,%s,%s\n" :
3009 L"";
3010 // Sort(true);
3012 for(int i = 0, j = m_entries.GetCount(), k = 0; i < j; i++)
3014 STSEntry& stse = m_entries.GetAt(i);
3016 int t1 = TranslateStart(i, fps);
3017 if(t1 < 0) {k++; continue;}
3019 int t2 = TranslateEnd(i, fps);
3021 int hh1 = (t1/60/60/1000);
3022 int mm1 = (t1/60/1000)%60;
3023 int ss1 = (t1/1000)%60;
3024 int ms1 = (t1)%1000;
3025 int hh2 = (t2/60/60/1000);
3026 int mm2 = (t2/60/1000)%60;
3027 int ss2 = (t2/1000)%60;
3028 int ms2 = (t2)%1000;
3030 CStringW str = f.IsUnicode()
3031 ? GetStrW(i, et == EXTSSA || et == EXTASS)
3032 : GetStrWA(i, et == EXTSSA || et == EXTASS);
3034 CStringW str2;
3036 if(et == EXTSRT)
3038 str2.Format(fmt, i-k+1, hh1, mm1, ss1, ms1, hh2, mm2, ss2, ms2, str);
3040 else if(et == EXTSUB)
3042 str.Replace('\n', '|');
3043 str2.Format(fmt, int(t1*fps/1000), int(t2*fps/1000), str);
3045 else if(et == EXTSMI)
3047 str.Replace(L"\n", L"<br>");
3048 str2.Format(fmt, t1, str, t2);
3050 else if(et == EXTPSB)
3052 str.Replace('\n', '|');
3053 str2.Format(fmt, hh1, mm1, ss1, hh2, mm2, ss2, str);
3055 else if(et == EXTSSA)
3057 str.Replace(L"\n", L"\\N");
3058 str2.Format(fmt,
3059 hh1, mm1, ss1, ms1/10,
3060 hh2, mm2, ss2, ms2/10,
3061 TToW(stse.style), TToW(stse.actor),
3062 stse.marginRect.left, stse.marginRect.right, (stse.marginRect.top + stse.marginRect.bottom) / 2,
3063 TToW(stse.effect), str);
3065 else if(et == EXTASS)
3067 str.Replace(L"\n", L"\\N");
3068 str2.Format(fmt,
3069 stse.layer,
3070 hh1, mm1, ss1, ms1/10,
3071 hh2, mm2, ss2, ms2/10,
3072 TToW(stse.style), TToW(stse.actor),
3073 stse.marginRect.left, stse.marginRect.right, (stse.marginRect.top + stse.marginRect.bottom) / 2,
3074 TToW(stse.effect), str);
3077 f.WriteString(str2);
3080 // Sort();
3082 if(et == EXTSMI)
3084 f.WriteString(_T("</BODY>\n</SAMI>\n"));
3087 STSStyle* s;
3088 if(!m_fUsingAutoGeneratedDefaultStyle && m_styles.Lookup(g_default_style, s) && et != EXTSSA && et != EXTASS)
3090 CTextFile f;
3091 if(!f.Save(fn + _T(".style"), e))
3092 return(false);
3094 CString str, str2;
3096 str += _T("ScriptType: v4.00+\n");
3097 str += _T("PlayResX: %d\n");
3098 str += _T("PlayResY: %d\n");
3099 str += _T("\n");
3100 str += _T("[V4+ Styles]\nFormat: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding\n");
3101 str2.Format(str, m_dstScreenSize.cx, m_dstScreenSize.cy);
3102 f.WriteString(str2);
3104 str = _T("Style: Default,%s,%d,&H%08x,&H%08x,&H%08x,&H%08x,%d,%d,%d,%d,%d,%d,%d,%.2f,%d,%d,%d,%d,%d,%d,%d,%d\n");
3105 str2.Format(str,
3106 s->fontName, (int)s->fontSize,
3107 (s->colors[0]&0xffffff) | (s->alpha[0]<<24),
3108 (s->colors[1]&0xffffff) | (s->alpha[1]<<24),
3109 (s->colors[2]&0xffffff) | (s->alpha[2]<<24),
3110 (s->colors[3]&0xffffff) | (s->alpha[3]<<24),
3111 s->fontWeight > FW_NORMAL ? -1 : 0,
3112 s->fItalic ? -1 : 0, s->fUnderline ? -1 : 0, s->fStrikeOut ? -1 : 0,
3113 (int)s->fontScaleX, (int)s->fontScaleY,
3114 (int)s->fontSpacing, (float)s->fontAngleZ,
3115 s->borderStyle == 0 ? 1 : s->borderStyle == 1 ? 3 : 0,
3116 (int)s->outlineWidthY, (int)s->shadowDepthY,
3117 s->scrAlignment,
3118 s->marginRect.get().left, s->marginRect.get().right, (s->marginRect.get().top + s->marginRect.get().bottom) / 2,
3119 s->charSet);
3120 f.WriteString(str2);
3123 return(true);
3126 bool CSimpleTextSubtitle::IsEmpty()
3128 return m_entries.IsEmpty();
3131 void CSimpleTextSubtitle::RemoveAllEntries()
3133 m_entries.RemoveAll();
3136 ////////////////////////////////////////////////////////////////////
3138 bool STSStyleBase::operator==( const STSStyleBase& s ) const
3140 return charSet == s.charSet
3141 && fontName == s.fontName
3142 && fontSize == s.fontSize
3143 && fontWeight == s.fontWeight
3144 && fItalic == s.fItalic
3145 && fUnderline == s.fUnderline
3146 && fStrikeOut == s.fStrikeOut;
3149 STSStyle::STSStyle()
3151 SetDefault();
3154 void STSStyle::SetDefault()
3156 marginRect = CRect(20, 20, 20, 20);
3157 scrAlignment = 2;
3158 borderStyle = 0;
3159 outlineWidthX = outlineWidthY = 2;
3160 shadowDepthX = shadowDepthY = 3;
3161 colors[0] = 0x00ffffff;
3162 colors[1] = 0x0000ffff;
3163 colors[2] = 0x00000000;
3164 colors[3] = 0x00000000;
3165 alpha[0] = 0x00;
3166 alpha[1] = 0x00;
3167 alpha[2] = 0x00;
3168 alpha[3] = 0x80;
3169 charSet = DEFAULT_CHARSET;
3170 fontName = _T("Arial");
3171 fontSize = 18;
3172 fontScaleX = fontScaleY = 100;
3173 fontSpacing = 0;
3174 fontWeight = FW_BOLD;
3175 fItalic = false;
3176 fUnderline = false;
3177 fStrikeOut = false;
3178 fBlur = 0;
3179 fGaussianBlur = 0;
3180 fontShiftX = fontShiftY = fontAngleZ = fontAngleX = fontAngleY = 0;
3181 relativeTo = 2;
3184 bool STSStyle::operator == (const STSStyle& s)const
3186 return(marginRect == s.marginRect
3187 && scrAlignment == s.scrAlignment
3188 && borderStyle == s.borderStyle
3189 && outlineWidthX == s.outlineWidthX
3190 && outlineWidthY == s.outlineWidthY
3191 && shadowDepthX == s.shadowDepthX
3192 && shadowDepthY == s.shadowDepthY
3193 && *((int*)&colors[0]) == *((int*)&s.colors[0])
3194 && *((int*)&colors[1]) == *((int*)&s.colors[1])
3195 && *((int*)&colors[2]) == *((int*)&s.colors[2])
3196 && *((int*)&colors[3]) == *((int*)&s.colors[3])
3197 && alpha[0] == s.alpha[0]
3198 && alpha[1] == s.alpha[1]
3199 && alpha[2] == s.alpha[2]
3200 && alpha[3] == s.alpha[3]
3201 && fBlur == s.fBlur
3202 && fGaussianBlur == s.fGaussianBlur
3203 && relativeTo == s.relativeTo
3204 && IsFontStyleEqual(s));
3207 bool STSStyle::IsFontStyleEqual(const STSStyle& s) const
3209 return(
3210 charSet == s.charSet
3211 && fontName == s.fontName
3212 && fontSize == s.fontSize
3213 && fontScaleX == s.fontScaleX
3214 && fontScaleY == s.fontScaleY
3215 && fontSpacing == s.fontSpacing
3216 && fontWeight == s.fontWeight
3217 && fItalic == s.fItalic
3218 && fUnderline == s.fUnderline
3219 && fStrikeOut == s.fStrikeOut
3220 && fontAngleZ == s.fontAngleZ
3221 && fontAngleX == s.fontAngleX
3222 && fontAngleY == s.fontAngleY
3223 && fontShiftX == s.fontShiftX
3224 && fontShiftY == s.fontShiftY);
3227 void STSStyle::operator = (const LOGFONT& lf)
3229 charSet = lf.lfCharSet;
3230 fontName = lf.lfFaceName;
3231 HDC hDC = GetDC(0);
3232 fontSize = -MulDiv(lf.lfHeight, 72, GetDeviceCaps(hDC, LOGPIXELSY));
3233 ReleaseDC(0, hDC);
3234 // fontAngleZ = (float)(1.0*lf.lfEscapement/10);
3235 fontWeight = lf.lfWeight;
3236 fItalic = !!lf.lfItalic;
3237 fUnderline = !!lf.lfUnderline;
3238 fStrikeOut = !!lf.lfStrikeOut;
3241 LOGFONTA& operator <<= (LOGFONTA& lfa, const STSStyleBase& s)
3243 lfa.lfCharSet = s.charSet;
3244 strncpy_s(lfa.lfFaceName, LF_FACESIZE, CStringA(s.fontName), _TRUNCATE);
3245 HDC hDC = GetDC(0);
3246 lfa.lfHeight = -MulDiv((int)(s.fontSize+0.5), GetDeviceCaps(hDC, LOGPIXELSY), 72);
3247 ReleaseDC(0, hDC);
3248 lfa.lfWeight = s.fontWeight;
3249 lfa.lfItalic = s.fItalic?-1:0;
3250 lfa.lfUnderline = s.fUnderline?-1:0;
3251 lfa.lfStrikeOut = s.fStrikeOut?-1:0;
3252 return(lfa);
3255 LOGFONTW& operator <<= (LOGFONTW& lfw, const STSStyleBase& s)
3257 lfw.lfCharSet = s.charSet;
3258 wcsncpy_s(lfw.lfFaceName, LF_FACESIZE, CStringW(s.fontName), _TRUNCATE);
3259 HDC hDC = GetDC(0);
3260 lfw.lfHeight = -MulDiv((int)(s.fontSize+0.5), GetDeviceCaps(hDC, LOGPIXELSY), 72);
3261 ReleaseDC(0, hDC);
3262 lfw.lfWeight = s.fontWeight;
3263 lfw.lfItalic = s.fItalic?-1:0;
3264 lfw.lfUnderline = s.fUnderline?-1:0;
3265 lfw.lfStrikeOut = s.fStrikeOut?-1:0;
3266 return(lfw);
3269 CString& operator <<= (CString& style, const STSStyle& s)
3271 style.Format(_T("%d;%d;%d;%d;")
3272 _T("%d;%d;%f;%f;%f;%f;")
3273 _T("0x%06x;0x%06x;0x%06x;0x%06x;")
3274 _T("0x%02x;0x%02x;0x%02x;0x%02x;")
3275 _T("%d;")
3276 _T("%s;%f;%f;%f;%f;%d;")
3277 _T("%d;%d;%d;%f;%f;")
3278 _T("%f;%f;%f;")
3279 _T("%d"),
3280 s.marginRect.get().left, s.marginRect.get().right, s.marginRect.get().top, s.marginRect.get().bottom,
3281 s.scrAlignment, s.borderStyle,s.outlineWidthX, s.outlineWidthY, s.shadowDepthX, s.shadowDepthY,
3282 s.colors[0], s.colors[1], s.colors[2], s.colors[3],
3283 s.alpha[0], s.alpha[1], s.alpha[2], s.alpha[3],
3284 s.charSet,
3285 s.fontName,s.fontSize,s.fontScaleX, s.fontScaleY,s.fontSpacing,s.fontWeight,
3286 (int)s.fItalic, (int)s.fUnderline, (int)s.fStrikeOut, s.fBlur, s.fGaussianBlur,
3287 s.fontAngleZ, s.fontAngleX, s.fontAngleY,
3288 s.relativeTo);
3290 return(style);
3293 STSStyle& operator <<= (STSStyle& s, const CString& style)
3295 s.SetDefault();
3299 CStringW str = TToW(style);
3300 if(str.Find(';')>=0)
3302 CRect tmp_rect;
3303 tmp_rect.left = GetInt(str,';'); tmp_rect.right = GetInt(str,';'); tmp_rect.top = GetInt(str,';'); tmp_rect.bottom = GetInt(str,';');
3304 s.marginRect = tmp_rect;
3305 s.scrAlignment = GetInt(str,';'); s.borderStyle = GetInt(str,';');
3306 s.outlineWidthX = GetFloat(str,';'); s.outlineWidthY = GetFloat(str,';'); s.shadowDepthX = GetFloat(str,';'); s.shadowDepthY = GetFloat(str,';');
3307 for(int i = 0; i < 4; i++) s.colors[i] = (COLORREF)GetInt(str,';');
3308 for(int i = 0; i < 4; i++) s.alpha[i] = GetInt(str,';');
3309 s.charSet = GetInt(str,';');
3310 s.fontName = WToT(GetStr(str,';')); s.fontSize = GetFloat(str,';');
3311 s.fontScaleX = GetFloat(str,';'); s.fontScaleY = GetFloat(str,';');
3312 s.fontSpacing = GetFloat(str,';'); s.fontWeight = GetInt(str,';');
3313 s.fItalic = !!GetInt(str,';'); s.fUnderline = !!GetInt(str,';'); s.fStrikeOut = !!GetInt(str,';'); s.fBlur = GetFloat(str,';'); s.fGaussianBlur = GetFloat(str,';');
3314 s.fontAngleZ = GetFloat(str,';'); s.fontAngleX = GetFloat(str,';'); s.fontAngleY = GetFloat(str,';');
3315 s.relativeTo = GetInt(str,';');
3318 catch(...)
3320 s.SetDefault();
3323 return(s);
3326 static bool OpenRealText(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet)
3328 wstring szFile;
3330 CStringW buff;
3331 while(file->ReadString(buff))
3333 buff.Trim();
3334 if(buff.IsEmpty()) continue;
3336 szFile += CStringW(_T("\n")) + buff.GetBuffer();
3339 CRealTextParser RealTextParser;
3340 if (!RealTextParser.ParseRealText(szFile))
3341 return false;
3343 CRealTextParser::Subtitles crRealText = RealTextParser.GetParsedSubtitles();
3345 for (map<pair<int, int>, wstring>::const_iterator i = crRealText.m_mapLines.begin();
3346 i != crRealText.m_mapLines.end();
3347 ++i)
3349 ret.Add(
3350 SubRipper2SSA(i->second.c_str(), CharSet),
3351 file->IsUnicode(),
3352 i->first.first,
3353 i->first.second);
3356 // std::wofstream wofsOut(L"c:/zzz.srt");
3357 // RealTextParser.OutputSRT(wofsOut);
3359 return(!ret.IsEmpty());