Fix performance issue with unsorted embedded ass file, see issue 23.
[xy_vsfilter.git] / src / subtitles / STS.cpp
blobc1a169527779e7623533c3c0ba51e8f66a5527dd
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 "xy_buffered_reader.h"
32 #include "text_reader.h"
33 #include <algorithm>
34 #include <vector>
35 #include "xy_logger.h"
37 // gathered from http://www.netwave.or.jp/~shikai/shikai/shcolor.htm
39 struct htmlcolor {TCHAR* name; DWORD color;} hmtlcolors[] =
41 {_T("white"), 0xffffff},
42 {_T("whitesmoke"), 0xf5f5f5},
43 {_T("ghostwhite"), 0xf8f8ff},
44 {_T("snow"), 0xfffafa},
45 {_T("gainsboro"), 0xdcdcdc},
46 {_T("lightgrey"), 0xd3d3d3},
47 {_T("silver"), 0xc0c0c0},
48 {_T("darkgray"), 0xa9a9a9},
49 {_T("gray"), 0x808080},
50 {_T("dimgray"), 0x696969},
51 {_T("lightslategray"), 0x778899},
52 {_T("slategray"), 0x708090},
53 {_T("darkslategray"), 0x2f4f4f},
54 {_T("black"), 0x000000},
56 {_T("azure"), 0xf0ffff},
57 {_T("aliceblue"), 0xf0f8ff},
58 {_T("mintcream"), 0xf5fffa},
59 {_T("honeydew"), 0xf0fff0},
60 {_T("lightcyan"), 0xe0ffff},
61 {_T("paleturqoise"), 0xafeeee},
62 {_T("powderblue"), 0xb0e0e6},
63 {_T("lightblue"), 0xadd8ed},
64 {_T("lightsteelblue"), 0xb0c4de},
65 {_T("skyblue"), 0x87ceeb},
66 {_T("lightskyblue"), 0x87cefa},
67 {_T("cyan"), 0x00ffff},
68 {_T("aqua"), 0x00ff80},
69 {_T("deepskyblue"), 0x00bfff},
70 {_T("aquamarine"), 0x7fffd4},
71 {_T("turquoise"), 0x40e0d0},
72 {_T("darkturquoise"), 0x00ced1},
73 {_T("lightseagreen"), 0x20b2aa},
74 {_T("mediumturquoise"), 0x40e0dd},
75 {_T("mediumaquamarine"), 0x66cdaa},
76 {_T("cadetblue"), 0x5f9ea0},
77 {_T("teal"), 0x008080},
78 {_T("darkcyan"), 0x008b8b},
79 {_T("comflowerblue"), 0x6495ed},
80 {_T("dodgerblue"), 0x1e90ff},
81 {_T("steelblue"), 0x4682b4},
82 {_T("royalblue"), 0x4169e1},
83 {_T("blue"), 0x0000ff},
84 {_T("mediumblue"), 0x0000cd},
85 {_T("mediumslateblue"), 0x7b68ee},
86 {_T("slateblue"), 0x6a5acd},
87 {_T("darkslateblue"), 0x483d8b},
88 {_T("darkblue"), 0x00008b},
89 {_T("midnightblue"), 0x191970},
90 {_T("navy"), 0x000080},
92 {_T("palegreen"), 0x98fb98},
93 {_T("lightgreen"), 0x90ee90},
94 {_T("mediumspringgreen"), 0x00fa9a},
95 {_T("springgreen"), 0x00ff7f},
96 {_T("chartreuse"), 0x7fff00},
97 {_T("lawngreen"), 0x7cfc00},
98 {_T("lime"), 0x00ff00},
99 {_T("limegreen"), 0x32cd32},
100 {_T("greenyellow"), 0xadff2f},
101 {_T("yellowgreen"), 0x9acd32},
102 {_T("darkseagreen"), 0x8fbc8f},
103 {_T("mediumseagreen"), 0x3cb371},
104 {_T("seagreen"), 0x2e8b57},
105 {_T("olivedrab"), 0x6b8e23},
106 {_T("forestgreen"), 0x228b22},
107 {_T("green"), 0x008000},
108 {_T("darkkhaki"), 0xbdb76b},
109 {_T("olive"), 0x808000},
110 {_T("darkolivegreen"), 0x556b2f},
111 {_T("darkgreen"), 0x006400},
113 {_T("floralwhite"), 0xfffaf0},
114 {_T("seashell"), 0xfff5ee},
115 {_T("ivory"), 0xfffff0},
116 {_T("beige"), 0xf5f5dc},
117 {_T("cornsilk"), 0xfff8dc},
118 {_T("lemonchiffon"), 0xfffacd},
119 {_T("lightyellow"), 0xffffe0},
120 {_T("lightgoldenrodyellow"), 0xfafad2},
121 {_T("papayawhip"), 0xffefd5},
122 {_T("blanchedalmond"), 0xffedcd},
123 {_T("palegoldenrod"), 0xeee8aa},
124 {_T("khaki"), 0xf0eb8c},
125 {_T("bisque"), 0xffe4c4},
126 {_T("moccasin"), 0xffe4b5},
127 {_T("navajowhite"), 0xffdead},
128 {_T("peachpuff"), 0xffdab9},
129 {_T("yellow"), 0xffff00},
130 {_T("gold"), 0xffd700},
131 {_T("wheat"), 0xf5deb3},
132 {_T("orange"), 0xffa500},
133 {_T("darkorange"), 0xff8c00},
135 {_T("oldlace"), 0xfdf5e6},
136 {_T("linen"), 0xfaf0e6},
137 {_T("antiquewhite"), 0xfaebd7},
138 {_T("lightsalmon"), 0xffa07a},
139 {_T("darksalmon"), 0xe9967a},
140 {_T("salmon"), 0xfa8072},
141 {_T("lightcoral"), 0xf08080},
142 {_T("indianred"), 0xcd5c5c},
143 {_T("coral"), 0xff7f50},
144 {_T("tomato"), 0xff6347},
145 {_T("orangered"), 0xff4500},
146 {_T("red"), 0xff0000},
147 {_T("crimson"), 0xdc143c},
148 {_T("firebrick"), 0xb22222},
149 {_T("maroon"), 0x800000},
150 {_T("darkred"), 0x8b0000},
152 {_T("lavender"), 0xe6e6fe},
153 {_T("lavenderblush"), 0xfff0f5},
154 {_T("mistyrose"), 0xffe4e1},
155 {_T("thistle"), 0xd8bfd8},
156 {_T("pink"), 0xffc0cb},
157 {_T("lightpink"), 0xffb6c1},
158 {_T("palevioletred"), 0xdb7093},
159 {_T("hotpink"), 0xff69b4},
160 {_T("fuchsia"), 0xff00ee},
161 {_T("magenta"), 0xff00ff},
162 {_T("mediumvioletred"), 0xc71585},
163 {_T("deeppink"), 0xff1493},
164 {_T("plum"), 0xdda0dd},
165 {_T("violet"), 0xee82ee},
166 {_T("orchid"), 0xda70d6},
167 {_T("mediumorchid"), 0xba55d3},
168 {_T("mediumpurple"), 0x9370db},
169 {_T("purple"), 0x9370db},
170 {_T("blueviolet"), 0x8a2be2},
171 {_T("darkviolet"), 0x9400d3},
172 {_T("darkorchid"), 0x9932cc},
174 {_T("tan"), 0xd2b48c},
175 {_T("burlywood"), 0xdeb887},
176 {_T("sandybrown"), 0xf4a460},
177 {_T("peru"), 0xcd853f},
178 {_T("goldenrod"), 0xdaa520},
179 {_T("darkgoldenrod"), 0xb8860b},
180 {_T("chocolate"), 0xd2691e},
181 {_T("rosybrown"), 0xbc8f8f},
182 {_T("sienna"), 0xa0522d},
183 {_T("saddlebrown"), 0x8b4513},
184 {_T("brown"), 0xa52a2a},
187 CHtmlColorMap::CHtmlColorMap()
189 for(int i = 0; i < countof(hmtlcolors); i++)
190 SetAt(hmtlcolors[i].name, hmtlcolors[i].color);
193 CHtmlColorMap g_colors;
195 CString g_default_style(_T("Default"));
199 BYTE CharSetList[] =
201 ANSI_CHARSET,
202 DEFAULT_CHARSET,
203 SYMBOL_CHARSET,
204 SHIFTJIS_CHARSET,
205 HANGEUL_CHARSET,
206 HANGUL_CHARSET,
207 GB2312_CHARSET,
208 CHINESEBIG5_CHARSET,
209 OEM_CHARSET,
210 JOHAB_CHARSET,
211 HEBREW_CHARSET,
212 ARABIC_CHARSET,
213 GREEK_CHARSET,
214 TURKISH_CHARSET,
215 VIETNAMESE_CHARSET,
216 THAI_CHARSET,
217 EASTEUROPE_CHARSET,
218 RUSSIAN_CHARSET,
219 MAC_CHARSET,
220 BALTIC_CHARSET
223 TCHAR* CharSetNames[] =
225 _T("ANSI"),
226 _T("DEFAULT"),
227 _T("SYMBOL"),
228 _T("SHIFTJIS"),
229 _T("HANGEUL"),
230 _T("HANGUL"),
231 _T("GB2312"),
232 _T("CHINESEBIG5"),
233 _T("OEM"),
234 _T("JOHAB"),
235 _T("HEBREW"),
236 _T("ARABIC"),
237 _T("GREEK"),
238 _T("TURKISH"),
239 _T("VIETNAMESE"),
240 _T("THAI"),
241 _T("EASTEUROPE"),
242 _T("RUSSIAN"),
243 _T("MAC"),
244 _T("BALTIC"),
247 int CharSetLen = countof(CharSetList);
249 static void LogSegments(const CAtlArray<STSSegment>& segments)
251 #ifdef __DO_LOG
252 for (int i=0;i<segments.GetCount();i++)
254 const STSSegment& s = segments[i];
255 XY_LOG_INFO(_T("\tsegments ")<<i<<_T(":")<<s.start<<_T(" ")
256 <<s.end<<_T(" ")<<s.subs.GetCount());
257 XY_LOG_INFO(_T("\tsubs: "));
258 for (int j=0;j<s.subs.GetCount();j++)
260 XY_LOG_INFO(_T("\t\t ")<<s.subs[j]);
263 #endif
268 static DWORD CharSetToCodePage(DWORD dwCharSet)
270 CHARSETINFO cs={0};
271 ::TranslateCharsetInfo((DWORD *)dwCharSet, &cs, TCI_SRCCHARSET);
272 return cs.ciACP;
275 int FindChar(CStringW str, WCHAR c, int pos, bool fUnicode, int CharSet)
277 if(fUnicode) return(str.Find(c, pos));
279 int fStyleMod = 0;
281 DWORD cp = CharSetToCodePage(CharSet);
282 int OrgCharSet = CharSet;
284 for(int i = 0, j = str.GetLength(), k; i < j; i++)
286 WCHAR c2 = str[i];
288 if(IsDBCSLeadByteEx(cp, (BYTE)c2)) i++;
289 else if(i >= pos)
291 if(c2 == c) return(i);
294 if(c2 == '{') fStyleMod++;
295 else if(fStyleMod > 0)
297 if(c2 == '}') fStyleMod--;
298 else if(c2 == 'e' && i >= 3 && i < j-1 && str.Mid(i-2, 3) == L"\\fe")
300 CharSet = 0;
301 for(k = i+1; _istdigit(str[k]); k++) CharSet = CharSet*10 + (str[k] - '0');
302 if(k == i+1) CharSet = OrgCharSet;
304 cp = CharSetToCodePage(CharSet);
309 return(-1);
312 int FindChar(CStringA str, char c, int pos, bool fUnicode, int CharSet)
314 ASSERT(!fUnicode);
316 return(FindChar(AToW(str), c, pos, false, CharSet));
319 static CStringW ToMBCS(CStringW str, DWORD CharSet)
321 CStringW ret;
323 DWORD cp = CharSetToCodePage(CharSet);
325 for(int i = 0, j = str.GetLength(); i < j; i++)
327 WCHAR wc = str.GetAt(i);
328 char c[8];
330 int len;
331 if((len = WideCharToMultiByte(cp, 0, &wc, 1, c, 8, NULL, NULL)) > 0)
333 for(int k = 0; k < len; k++)
334 ret += (WCHAR)(BYTE)c[k];
336 else
338 ret += '?';
342 return(ret);
345 static CStringW UnicodeSSAToMBCS(CStringW str, DWORD CharSet)
347 CStringW ret;
349 int OrgCharSet = CharSet;
351 for(int j = 0; j < str.GetLength(); )
353 j = str.Find('{', j);
354 if(j >= 0)
356 ret += ToMBCS(str.Left(j), CharSet);
357 str = str.Mid(j);
359 j = str.Find('}');
360 if(j < 0)
362 ret += ToMBCS(str, CharSet);
363 break;
365 else
367 int k = str.Find(L"\\fe");
368 if(k >= 0 && k < j)
370 CharSet = 0;
371 int l = k+3;
372 for(; _istdigit(str[l]); l++) CharSet = CharSet*10 + (str[l] - '0');
373 if(l == k+3) CharSet = OrgCharSet;
376 j++;
378 ret += ToMBCS(str.Left(j), OrgCharSet);
379 str = str.Mid(j);
380 j = 0;
383 else
385 ret += ToMBCS(str, CharSet);
386 break;
390 return(ret);
393 static CStringW ToUnicode(CStringW str, DWORD CharSet)
395 CStringW ret;
397 DWORD cp = CharSetToCodePage(CharSet);
399 for(int i = 0, j = str.GetLength(); i < j; i++)
401 WCHAR wc = str.GetAt(i);
402 char c = wc&0xff;
404 if(IsDBCSLeadByteEx(cp, (BYTE)wc))
406 i++;
408 if(i < j)
410 char cc[2];
411 cc[0] = c;
412 cc[1] = (char)str.GetAt(i);
414 MultiByteToWideChar(cp, 0, cc, 2, &wc, 1);
417 else
419 MultiByteToWideChar(cp, 0, &c, 1, &wc, 1);
422 ret += wc;
425 return(ret);
428 static CStringW MBCSSSAToUnicode(CStringW str, int CharSet)
430 CStringW ret;
432 int OrgCharSet = CharSet;
434 for(int j = 0; j < str.GetLength(); )
436 j = FindChar(str, '{', 0, false, CharSet);
438 if(j >= 0)
440 ret += ToUnicode(str.Left(j), CharSet);
441 str = str.Mid(j);
443 j = FindChar(str, '}', 0, false, CharSet);
445 if(j < 0)
447 ret += ToUnicode(str, CharSet);
448 break;
450 else
452 int k = str.Find(L"\\fe");
453 if(k >= 0 && k < j)
455 CharSet = 0;
456 int l = k+3;
457 for(; _istdigit(str[l]); l++) CharSet = CharSet*10 + (str[l] - '0');
458 if(l == k+3) CharSet = OrgCharSet;
461 j++;
463 ret += ToUnicode(str.Left(j), OrgCharSet);
464 str = str.Mid(j);
465 j = 0;
468 else
470 ret += ToUnicode(str, CharSet);
471 break;
475 return(ret);
478 CStringW RemoveSSATags(CStringW str, bool fUnicode, int CharSet)
480 str.Replace (L"{\\i1}", L"<i>");
481 str.Replace (L"{\\i}", L"</i>");
483 for(int i = 0, j; i < str.GetLength(); )
485 if((i = FindChar(str, '{', i, fUnicode, CharSet)) < 0) break;
486 if((j = FindChar(str, '}', i, fUnicode, CharSet)) < 0) break;
487 str.Delete(i, j-i+1);
490 str.Replace(L"\\N", L"\n");
491 str.Replace(L"\\n", L"\n");
492 str.Replace(L"\\h", L" ");
494 return(str);
499 static CStringW SubRipper2SSA(CStringW str, int CharSet)
501 str.Replace(L"<i>", L"{\\i1}");
502 str.Replace(L"</i>", L"{\\i}");
503 str.Replace(L"<b>", L"{\\b1}");
504 str.Replace(L"</b>", L"{\\b}");
505 str.Replace(L"<u>", L"{\\u1}");
506 str.Replace(L"</u>", L"{\\u}");
508 return(str);
511 static bool OpenSubRipper(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet)
513 int num = 0;
515 CStringW buff;
516 while(file->ReadString(buff))
518 buff.Trim();
519 if(buff.IsEmpty()) continue;
521 WCHAR sep;
522 int hh1, mm1, ss1, ms1, hh2, mm2, ss2, ms2;
523 int c = swscanf(buff, L"%d%c%d%c%d%c%d --> %d%c%d%c%d%c%d\n",
524 &hh1, &sep, &mm1, &sep, &ss1, &sep, &ms1,
525 &hh2, &sep, &mm2, &sep, &ss2, &sep, &ms2);
527 if(c == 1) // numbering
529 num = hh1;
531 else if(c == 14) // time info
533 CStringW str, tmp;
535 bool fFoundEmpty = false;
537 while(file->ReadString(tmp))
539 tmp.Trim();
540 if(tmp.IsEmpty()) fFoundEmpty = true;
542 int num2;
543 WCHAR c;
544 if(swscanf(tmp, L"%d%c", &num2, &c) == 1 && fFoundEmpty)
546 num = num2;
547 break;
550 str += tmp + '\n';
553 ret.Add(
554 SubRipper2SSA(str, CharSet),
555 file->IsUnicode(),
556 (((hh1*60 + mm1)*60) + ss1)*1000 + ms1,
557 (((hh2*60 + mm2)*60) + ss2)*1000 + ms2);
559 else if(c != EOF) // might be another format
561 return(false);
565 return(!ret.IsEmpty());
568 static bool OpenOldSubRipper(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet)
570 CStringW buff;
571 while(file->ReadString(buff))
573 buff.Trim();
574 if(buff.IsEmpty()) continue;
576 for(int i = 0; i < buff.GetLength(); i++)
578 if((i = FindChar(buff, '|', i, file->IsUnicode(), CharSet)) < 0) break;
579 buff.SetAt(i, '\n');
582 int hh1, mm1, ss1, hh2, mm2, ss2;
583 int c = swscanf(buff, L"{%d:%d:%d}{%d:%d:%d}", &hh1, &mm1, &ss1, &hh2, &mm2, &ss2);
585 if(c == 6)
587 ret.Add(
588 buff.Mid(buff.Find('}', buff.Find('}')+1)+1),
589 file->IsUnicode(),
590 (((hh1*60 + mm1)*60) + ss1)*1000,
591 (((hh2*60 + mm2)*60) + ss2)*1000);
593 else if(c != EOF) // might be another format
595 return(false);
599 return(!ret.IsEmpty());
602 static bool OpenSubViewer(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet)
604 STSStyle def;
605 CStringW font, color, size;
606 bool fBold, fItalic, fStriked, fUnderline;
608 CStringW buff;
609 while(file->ReadString(buff))
611 buff.Trim();
612 if(buff.IsEmpty()) continue;
614 if(buff[0] == '[')
616 for(int i = 0; i < buff.GetLength() && buff[i]== '['; )
618 int j = buff.Find(']', ++i);
619 if(j < i) break;
621 CStringW tag = buff.Mid(i,j-i);
622 tag.Trim();
623 tag.MakeLower();
625 i += j-i;
627 j = buff.Find('[', ++i);
628 if(j < 0) j = buff.GetLength();
630 CStringW param = buff.Mid(i,j-i);
631 param.Trim(L" \\t,");
633 i = j;
635 if(tag == L"font")
636 font = def.fontName.CompareNoCase(WToT(param)) ? param : L"";
637 else if(tag == L"colf")
638 color = def.colors[0] != wcstol(((LPCWSTR)param)+2, 0, 16) ? param : L"";
639 else if(tag == L"size")
640 size = def.fontSize != wcstol(param, 0, 10) ? param : L"";
641 else if(tag == L"style")
643 if(param.Find(L"no") >= 0)
645 fBold = fItalic = fStriked = fUnderline = false;
647 else
649 fBold = def.fontWeight < FW_BOLD && param.Find(L"bd") >= 0;
650 fItalic = def.fItalic && param.Find(L"it") >= 0;
651 fStriked = def.fStrikeOut && param.Find(L"st") >= 0;
652 fUnderline = def.fUnderline && param.Find(L"ud") >= 0;
657 continue;
660 WCHAR sep;
661 int hh1, mm1, ss1, hs1, hh2, mm2, ss2, hs2;
662 int c = swscanf(buff, L"%d:%d:%d%c%d,%d:%d:%d%c%d\n",
663 &hh1, &mm1, &ss1, &sep, &hs1, &hh2, &mm2, &ss2, &sep, &hs2);
665 if(c == 10)
667 CStringW str;
668 file->ReadString(str);
670 str.Replace(L"[br]", L"\\N");
672 CStringW prefix;
673 if(!font.IsEmpty()) prefix += L"\\fn" + font;
674 if(!color.IsEmpty()) prefix += L"\\c" + color;
675 if(!size.IsEmpty()) prefix += L"\\fs" + size;
676 if(fBold) prefix += L"\\b1";
677 if(fItalic) prefix += L"\\i1";
678 if(fStriked) prefix += L"\\s1";
679 if(fUnderline) prefix += L"\\u1";
680 if(!prefix.IsEmpty()) str = L"{" + prefix + L"}" + str;
682 ret.Add(str,
683 file->IsUnicode(),
684 (((hh1*60 + mm1)*60) + ss1)*1000 + hs1*10,
685 (((hh2*60 + mm2)*60) + ss2)*1000 + hs2*10);
687 else if(c != EOF) // might be another format
689 return(false);
693 return(!ret.IsEmpty());
696 static STSStyle* GetMicroDVDStyle(CString str, int CharSet)
698 STSStyle* ret = new STSStyle();
699 if(!ret) return(NULL);
701 for(int i = 0, len = str.GetLength(); i < len; i++)
703 int j = str.Find('{', i);
704 if(j < 0) j = len;
706 if(j >= len) break;
708 int k = str.Find('}', j);
709 if(k < 0) k = len;
711 CString code = str.Mid(j, k-j);
712 if(code.GetLength() > 2) code.SetAt(1, (TCHAR)towlower(code[1]));
714 if(!_tcsnicmp(code, _T("{c:$"), 4))
716 _stscanf(code, _T("{c:$%x"), &ret->colors[0]);
718 else if(!_tcsnicmp(code, _T("{f:"), 3))
720 ret->fontName = code.Mid(3);
722 else if(!_tcsnicmp(code, _T("{s:"), 3))
724 float f;
725 if(1 == _stscanf(code, _T("{s:%f"), &f))
726 ret->fontSize = f;
728 else if(!_tcsnicmp(code, _T("{h:"), 3))
730 _stscanf(code, _T("{h:%d"), &ret->charSet);
732 else if(!_tcsnicmp(code, _T("{y:"), 3))
734 code.MakeLower();
735 if(code.Find('b') >= 0) ret->fontWeight = FW_BOLD;
736 if(code.Find('i') >= 0) ret->fItalic = true;
737 if(code.Find('u') >= 0) ret->fUnderline = true;
738 if(code.Find('s') >= 0) ret->fStrikeOut = true;
740 else if(!_tcsnicmp(code, _T("{p:"), 3))
742 int p;
743 _stscanf(code, _T("{p:%d"), &p);
744 ret->scrAlignment = (p == 0) ? 8 : 2;
747 i = k;
750 return(ret);
753 static CStringW MicroDVD2SSA(CStringW str, bool fUnicode, int CharSet)
755 CStringW ret;
757 enum {COLOR=0, FONTNAME, FONTSIZE, FONTCHARSET, BOLD, ITALIC, UNDERLINE, STRIKEOUT};
758 bool fRestore[8];
759 int fRestoreLen = 8;
760 memset(fRestore, 0, sizeof(bool)*fRestoreLen);
762 for(int pos = 0, eol; pos < str.GetLength(); pos++)
764 if((eol = FindChar(str, '|', pos, fUnicode, CharSet)) < 0) eol = str.GetLength();
766 CStringW line = str.Mid(pos, eol-pos);
768 pos = eol;
770 for(int i = 0, j, k, len = line.GetLength(); i < len; i++)
772 if((j = FindChar(line, '{', i, fUnicode, CharSet)) < 0) j = str.GetLength();
774 ret += line.Mid(i, j-i);
776 if(j >= len) break;
778 if((k = FindChar(line, '}', j, fUnicode, CharSet)) < 0) k = len;
781 CStringW code = line.Mid(j, k-j);
783 if(!wcsnicmp(code, L"{c:$", 4))
785 fRestore[COLOR] = (iswupper(code[1]) == 0);
786 code.MakeLower();
788 int color;
789 swscanf(code, L"{c:$%x", &color);
790 code.Format(L"{\\c&H%x&}", color);
791 ret += code;
793 else if(!wcsnicmp(code, L"{f:", 3))
795 fRestore[FONTNAME] = (iswupper(code[1]) == 0);
797 code.Format(L"{\\fn%s}", code.Mid(3));
798 ret += code;
800 else if(!wcsnicmp(code, L"{s:", 3))
802 fRestore[FONTSIZE] = (iswupper(code[1]) == 0);
803 code.MakeLower();
805 float size;
806 swscanf(code, L"{s:%f", &size);
807 code.Format(L"{\\fs%f}", size);
808 ret += code;
810 else if(!wcsnicmp(code, L"{h:", 3))
812 fRestore[COLOR] = (_istupper(code[1]) == 0);
813 code.MakeLower();
815 int CharSet;
816 swscanf(code, L"{h:%d", &CharSet);
817 code.Format(L"{\\fe%d}", CharSet);
818 ret += code;
820 else if(!wcsnicmp(code, L"{y:", 3))
822 bool f = (_istupper(code[1]) == 0);
824 code.MakeLower();
826 ret += '{';
827 if(code.Find('b') >= 0) {ret += L"\\b1"; fRestore[BOLD] = f;}
828 if(code.Find('i') >= 0) {ret += L"\\i1"; fRestore[ITALIC] = f;}
829 if(code.Find('u') >= 0) {ret += L"\\u1"; fRestore[UNDERLINE] = f;}
830 if(code.Find('s') >= 0) {ret += L"\\s1"; fRestore[STRIKEOUT] = f;}
831 ret += '}';
833 else if(!wcsnicmp(code, L"{o:", 3))
835 code.MakeLower();
837 int x, y;
838 TCHAR c;
839 swscanf(code, L"{o:%d%c%d", &x, &c, &y);
840 code.Format(L"{\\move(%d,%d,0,0,0,0)}", x, y);
841 ret += code;
843 else ret += code;
846 i = k;
849 if(pos >= str.GetLength()) break;
851 for(int i = 0; i < fRestoreLen; i++)
853 if(fRestore[i])
855 switch(i)
857 case COLOR: ret += L"{\\c}"; break;
858 case FONTNAME: ret += L"{\\fn}"; break;
859 case FONTSIZE: ret += L"{\\fs}"; break;
860 case FONTCHARSET: ret += L"{\\fe}"; break;
861 case BOLD: ret += L"{\\b}"; break;
862 case ITALIC: ret += L"{\\i}"; break;
863 case UNDERLINE: ret += L"{\\u}"; break;
864 case STRIKEOUT: ret += L"{\\s}"; break;
865 default: break;
870 memset(fRestore, 0, sizeof(bool)*fRestoreLen);
872 ret += L"\\N";
875 return(ret);
878 static bool OpenMicroDVD(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet)
880 bool fCheck = false, fCheck2 = false;
882 CString style(_T("Default"));
884 CStringW buff;;
885 while(file->ReadString(buff))
887 buff.Trim();
888 if(buff.IsEmpty()) continue;
890 int start, end;
891 int c = swscanf(buff, L"{%d}{%d}", &start, &end);
893 if(c != 2) {c = swscanf(buff, L"{%d}{}", &start)+1; end = start + 60; fCheck = true;}
895 if(c != 2)
897 int i;
898 if(buff.Find('{') == 0 && (i = buff.Find('}')) > 1 && i < buff.GetLength())
900 if(STSStyle* s = GetMicroDVDStyle(WToT(buff.Mid(i+1)), CharSet))
902 style = buff.Mid(1, i-1);
903 style.MakeUpper();
904 if(style.GetLength()) {CString str = style.Mid(1); str.MakeLower(); style = style.Left(1) + str;}
905 ret.AddStyle(style, s);
906 CharSet = s->charSet;
907 continue;
912 if(c == 2)
914 if(fCheck2 && !ret.IsEmpty())
916 STSEntry& stse = ret.m_entries[ret.m_entries.GetCount()-1];
917 stse.end = min(stse.end, start);
918 fCheck2 = false;
921 ret.Add(
922 MicroDVD2SSA(buff.Mid(buff.Find('}', buff.Find('}')+1)+1), file->IsUnicode(), CharSet),
923 file->IsUnicode(),
924 start, end,
925 style);
927 if(fCheck)
929 fCheck = false;
930 fCheck2 = true;
933 else if(c != EOF) // might be another format
935 return(false);
939 return(!ret.IsEmpty());
942 static void ReplaceNoCase(CStringW& str, CStringW from, CStringW to)
944 CStringW lstr = str;
945 lstr.MakeLower();
947 int i, j, k;
949 for(i = 0, j = str.GetLength(); i < j; )
951 if((k = lstr.Find(from, i)) >= 0)
953 str.Delete(k, from.GetLength()); lstr.Delete(k, from.GetLength());
954 str.Insert(k, to); lstr.Insert(k, to);
955 i = k + to.GetLength();
956 j = str.GetLength();
958 else break;
962 static CStringW SMI2SSA(CStringW str, int CharSet)
964 ReplaceNoCase(str, L"&nbsp;", L" ");
965 ReplaceNoCase(str, L"&quot;", L"\"");
966 ReplaceNoCase(str, L"<br>", L"\\N");
967 ReplaceNoCase(str, L"<i>", L"{\\i1}");
968 ReplaceNoCase(str, L"</i>", L"{\\i}");
969 ReplaceNoCase(str, L"<b>", L"{\\b1}");
970 ReplaceNoCase(str, L"</b>", L"{\\b}");
972 CStringW lstr = str;
973 lstr.MakeLower();
975 // maven@maven.de
976 // now parse line
977 for(int i = 0, j = str.GetLength(); i < j; )
979 int k;
980 if((k = lstr.Find('<', i)) < 0) break;
982 int chars_inserted = 0;
984 int l = 1;
985 for(; k+l < j && lstr[k+l] != '>'; l++);
986 l++;
988 // Modified by Cookie Monster
989 if (lstr.Find(L"<font ", k) == k)
991 CStringW args = lstr.Mid(k+6, l-6); // delete "<font "
992 CStringW arg ;
994 args.Remove('\"'); args.Remove('#'); // may include 2 * " + #
995 arg.TrimLeft(); arg.TrimRight(L" >");
997 for (;;)
999 args.TrimLeft();
1000 arg = args.SpanExcluding(L" \t>");
1001 args = args.Mid(arg.GetLength());
1003 if(arg.IsEmpty())
1004 break;
1005 if (arg.Find(L"color=") == 0 )
1007 DWORD color;
1009 arg = arg.Mid(6); // delete "color="
1010 if ( arg.IsEmpty())
1011 continue;
1013 DWORD val;
1014 if(g_colors.Lookup(CString(arg), val))
1015 color = (DWORD)val;
1016 else if((color = wcstol(arg, NULL, 16) ) == 0)
1017 color = 0x00ffffff; // default is white
1019 arg.Format(L"%02x%02x%02x", color&0xff, (color>>8)&0xff, (color>>16)&0xff);
1020 lstr.Insert(k + l + chars_inserted, CStringW(L"{\\c&H") + arg + L"&}");
1021 str.Insert(k + l + chars_inserted, CStringW(L"{\\c&H") + arg + L"&}");
1022 chars_inserted += 5 + arg.GetLength() + 2;
1025 else if (arg.Find(_T("size=" )) == 0 )
1027 uint fsize;
1029 arg = arg.Mid(5); // delete "size="
1030 if ( arg.GetLength() == 0)
1031 continue;
1033 if ( fsize = _tcstol(arg, &tmp, 10) == 0 )
1034 continue;
1036 lstr.Insert(k + l + chars_inserted, CString(_T("{\\fs")) + arg + _T("&}"));
1037 str.Insert(k + l + chars_inserted, CString(_T("{\\fs")) + arg + _T("&}"));
1038 chars_inserted += 4 + arg.GetLength() + 2;
1044 // Original Code
1046 if (lstr.Find(L"<font color=", k) == k)
1048 CStringW arg = lstr.Mid(k+12, l-12); // may include 2 * " + #
1050 arg.Remove('\"');
1051 arg.Remove('#');
1052 arg.TrimLeft(); arg.TrimRight(L" >");
1054 if(arg.GetLength() > 0)
1056 DWORD color;
1058 CString key = WToT(arg);
1059 void* val;
1060 if(g_colors.Lookup(key, val)) color = (DWORD)val;
1061 else color = wcstol(arg, NULL, 16);
1063 arg.Format(L"%02x%02x%02x", color&0xff, (color>>8)&0xff, (color>>16)&0xff);
1066 lstr.Insert(k + l + chars_inserted, L"{\\c&H" + arg + L"&}");
1067 str.Insert(k + l + chars_inserted, L"{\\c&H" + arg + L"&}");
1068 chars_inserted += 5 + arg.GetLength() + 2;
1071 else if (lstr.Find(L"</font>", k) == k)
1073 lstr.Insert(k + l + chars_inserted, L"{\\c}");
1074 str.Insert(k + l + chars_inserted, L"{\\c}");
1075 chars_inserted += 4;
1078 str.Delete(k, l); lstr.Delete(k, l);
1079 i = k + chars_inserted;
1080 j = str.GetLength();
1083 return(str);
1086 static bool OpenSami(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet)
1088 CStringW buff, caption;
1090 ULONGLONG pos = file->GetPosition();
1092 bool fSAMI = false;
1094 while(file->ReadString(buff) && !fSAMI)
1096 if(buff.MakeUpper().Find(L"<SAMI>") >= 0) fSAMI = true;
1099 if(!fSAMI) return(false);
1101 file->Seek(pos, 0);
1103 bool fComment = false;
1105 int start_time = 0;
1107 while(file->ReadString(buff))
1109 buff.Trim();
1110 if(buff.IsEmpty()) continue;
1112 CStringW ubuff = buff;
1113 ubuff.MakeUpper();
1115 if(ubuff.Find(L"<!--") >= 0 || ubuff.Find(L"<TITLE>") >= 0)
1116 fComment = true;
1118 if(!fComment)
1120 int i;
1122 if((i = ubuff.Find(L"<SYNC START=")) >= 0)
1124 int time = 0;
1126 for(i = 12; i < ubuff.GetLength(); i++)
1128 if(ubuff[i] != '>' && ubuff[i] != 'M')
1130 if(iswdigit(ubuff[i]))
1132 time *= 10;
1133 time += ubuff[i] - 0x30;
1136 else break;
1139 ret.Add(
1140 SMI2SSA(caption, CharSet),
1141 file->IsUnicode(),
1142 start_time, time);
1144 start_time = time;
1145 caption.Empty();
1148 caption += buff;
1151 if(ubuff.Find(L"-->") >= 0 || ubuff.Find(L"</TITLE>") >= 0)
1152 fComment = false;
1155 ret.Add(
1156 SMI2SSA(caption, CharSet),
1157 file->IsUnicode(),
1158 start_time, MAXLONG);
1160 return(true);
1163 static bool OpenVPlayer(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet)
1165 CStringW buff;
1166 while(file->ReadString(buff))
1168 buff.Trim();
1169 if(buff.IsEmpty()) continue;
1171 for(int i = 0; i < buff.GetLength(); i++)
1173 if((i = FindChar(buff, '|', i, file->IsUnicode(), CharSet)) < 0) break;
1174 buff.SetAt(i, '\n');
1177 int hh, mm, ss;
1178 int c = swscanf(buff, L"%d:%d:%d:", &hh, &mm, &ss);
1180 if(c == 3)
1182 CStringW str = buff.Mid(buff.Find(':', buff.Find(':', buff.Find(':')+1)+1)+1);
1183 ret.Add(str,
1184 file->IsUnicode(),
1185 (((hh*60 + mm)*60) + ss)*1000,
1186 (((hh*60 + mm)*60) + ss)*1000 + 1000 + 50*str.GetLength());
1188 else if(c != EOF) // might be another format
1190 return(false);
1194 return(!ret.IsEmpty());
1197 inline CStringW GetStr(CStringW& buff, char sep = ',') //throw(...)
1199 buff.TrimLeft();
1201 int pos = buff.Find(sep);
1202 if(pos < 0)
1204 pos = buff.GetLength();
1205 if(pos < 1) throw 1;
1208 CStringW ret = buff.Left(pos);
1209 if(pos < buff.GetLength()) buff = buff.Mid(pos+1);
1211 return(ret);
1214 inline int GetInt(CStringW& buff, char sep = ',') //throw(...)
1216 CStringW str;
1218 str = GetStr(buff, sep);
1219 str.MakeLower();
1221 CStringW fmtstr = str.GetLength() > 2 && (str.Left(2) == L"&h" || str.Left(2) == L"0x")
1222 ? str = str.Mid(2), L"%x"
1223 : L"%d";
1225 int ret;
1226 if(swscanf(str, fmtstr, &ret) != 1) throw 1;
1228 return(ret);
1231 inline double GetFloat(CStringW& buff, char sep = ',') //throw(...)
1233 CStringW str;
1235 str = GetStr(buff, sep);
1236 str.MakeLower();
1238 float ret;
1239 if(swscanf(str, L"%f", &ret) != 1) throw 1;
1241 return((double)ret);
1244 inline CStringW::PCXSTR TryNextStr(CStringW::PXSTR * buff, WCHAR sep = WCHAR(','))
1246 CStringW::PXSTR start = NULL;
1247 CStringW::PXSTR ret = NULL;
1248 for(start=*buff; *start!=0 && *start==WCHAR(' '); start++) ;
1250 *buff=start;
1251 ret = start;
1253 for( ;*start!=0 && *start!=sep; start++) ;
1255 if(*start!=0)
1256 *buff = start+1;
1258 *start = 0;
1260 return(ret);
1263 inline int NextInt(CStringW::PXSTR * buff, WCHAR sep = WCHAR(',')) //throw(...)
1265 CStringW str;
1267 str = TryNextStr(buff, sep);
1268 str.MakeLower();
1270 CStringW fmtstr = str.GetLength() > 2 && (str.Left(2) == L"&h" || str.Left(2) == L"0x")
1271 ? str = str.Mid(2), L"%x"
1272 : L"%d";
1274 int ret;
1275 if(swscanf(str, fmtstr, &ret) != 1) throw 1;
1277 return(ret);
1280 inline double NextFloat(CStringW::PXSTR * buff, WCHAR sep = WCHAR(',')) //throw(...)
1282 CStringW str;
1284 str = TryNextStr(buff, sep);
1285 str.MakeLower();
1287 float ret;
1288 if(swscanf(str, L"%f", &ret) != 1) throw 1;
1290 return((double)ret);
1293 static bool LoadFont(CString& font)
1295 int len = font.GetLength();
1297 CAutoVectorPtr<BYTE> pData;
1298 if(len == 0 || (len&3) == 1 || !pData.Allocate(len))
1299 return(false);
1301 const TCHAR* s = font;
1302 const TCHAR* e = s + len;
1303 for(BYTE* p = pData; s < e; s++, p++) *p = *s - 33;
1305 for(int i = 0, j = 0, k = len&~3; i < k; i+=4, j+=3)
1307 pData[j+0] = ((pData[i+0]&63)<<2)|((pData[i+1]>>4)& 3);
1308 pData[j+1] = ((pData[i+1]&15)<<4)|((pData[i+2]>>2)&15);
1309 pData[j+2] = ((pData[i+2]& 3)<<6)|((pData[i+3]>>0)&63);
1312 int datalen = (len&~3)*3/4;
1314 if((len&3) == 2)
1316 pData[datalen++] = ((pData[(len&~3)+0]&63)<<2)|((pData[(len&~3)+1]>>4)&3);
1318 else if((len&3) == 3)
1320 pData[datalen++] = ((pData[(len&~3)+0]&63)<<2)|((pData[(len&~3)+1]>>4)& 3);
1321 pData[datalen++] = ((pData[(len&~3)+1]&15)<<4)|((pData[(len&~3)+2]>>2)&15);
1324 HANDLE hFont = INVALID_HANDLE_VALUE;
1326 if(HMODULE hModule = LoadLibrary(_T("GDI32.DLL")))
1328 typedef HANDLE (WINAPI *PAddFontMemResourceEx)( IN PVOID, IN DWORD, IN PVOID , IN DWORD*);
1329 if(PAddFontMemResourceEx f = (PAddFontMemResourceEx)GetProcAddress(hModule, "AddFontMemResourceEx"))
1331 DWORD cFonts;
1332 hFont = f(pData, datalen, NULL, &cFonts);
1335 FreeLibrary(hModule);
1338 if(hFont == INVALID_HANDLE_VALUE)
1340 TCHAR path[MAX_PATH];
1341 GetTempPath(MAX_PATH, path);
1343 DWORD chksum = 0;
1344 for(int i = 0, j = datalen>>2; i < j; i++)
1345 chksum += ((DWORD*)(BYTE*)pData)[i];
1347 CString fn;
1348 fn.Format(_T("%sfont%08x.ttf"), path, chksum);
1350 CFileStatus fs;
1351 if(!CFileGetStatus(fn, fs))
1353 CFile f;
1354 if(f.Open(fn, CFile::modeCreate|CFile::modeWrite|CFile::typeBinary|CFile::shareDenyWrite))
1356 f.Write(pData, datalen);
1357 f.Close();
1361 AddFontResource(fn);
1364 return(true);
1367 static bool LoadUUEFont(CTextFile* file)
1369 CString s, font;
1370 while(file->ReadString(s))
1372 s.Trim();
1373 if(s.IsEmpty() || s[0] == '[') break;
1374 if(s.Find(_T("fontname:")) == 0) {LoadFont(font); font.Empty(); continue;}
1376 font += s;
1379 if(!font.IsEmpty())
1380 LoadFont(font);
1382 return(true);
1385 static bool OpenSubStationAlpha(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet)
1387 using namespace xy_utils;
1388 bool fRet = false;
1390 int version = 3, sver = 3;
1392 CStringW buff;
1393 BufferedReader<CTextFile> buffered_reader;
1394 if(!buffered_reader.Init(file, file->GetLength()))
1396 DbgLog(( LOG_ERROR, 1, "init buffered_reader failed. buffsize:%d", file->GetLength() ));
1397 return false;
1399 TextReader<BufferedReader<CTextFile>> text_reader(buffered_reader, (TextReader<BufferedReader<CTextFile>>::Encoding)file->GetEncoding());
1401 while(text_reader.ReadLine(&buff))
1403 buff.Trim();
1404 if(buff.IsEmpty() || buff.GetAt(0) == ';') continue;
1406 CStringW entry;
1408 // try {
1409 entry = GetStr(buff, ':');
1410 // }
1411 // catch(...) {continue;}
1413 entry.MakeLower();
1415 if(entry == _T("dialogue"))
1419 CStringW::PXSTR __buff = buff.GetBuffer();
1420 int hh1, mm1, ss1, ms1_div10, hh2, mm2, ss2, ms2_div10, layer = 0;
1421 CString Style, Actor, Effect;
1422 CRect marginRect;
1424 if(version <= 4){TryNextStr(&__buff, '='); NextInt(&__buff);} /* Marked = */
1425 if(version >= 5)layer = NextInt(&__buff);
1426 hh1 = NextInt(&__buff, ':');
1427 mm1 = NextInt(&__buff, ':');
1428 ss1 = NextInt(&__buff, '.');
1429 ms1_div10 = NextInt(&__buff);
1430 hh2 = NextInt(&__buff, ':');
1431 mm2 = NextInt(&__buff, ':');
1432 ss2 = NextInt(&__buff, '.');
1433 ms2_div10 = NextInt(&__buff);
1434 Style = WToT(TryNextStr(&__buff));
1435 Actor = WToT(TryNextStr(&__buff));
1436 marginRect.left = NextInt(&__buff);
1437 marginRect.right = NextInt(&__buff);
1438 marginRect.top = marginRect.bottom = NextInt(&__buff);
1439 if(version >= 6)marginRect.bottom = NextInt(&__buff);
1440 Effect = WToT(TryNextStr(&__buff));
1442 CStringW buff2 = __buff;
1443 int len = min(Effect.GetLength(), buff2.GetLength());
1444 if(Effect.Left(len) == WToT(buff2.Left(len))) Effect.Empty();
1446 Style.TrimLeft('*');
1447 if(!Style.CompareNoCase(_T("Default"))) Style = _T("Default");
1449 ret.AddSTSEntryOnly(buff2,
1450 file->IsUnicode(),
1451 (((hh1*60 + mm1)*60) + ss1)*1000 + ms1_div10*10,
1452 (((hh2*60 + mm2)*60) + ss2)*1000 + ms2_div10*10,
1453 Style, Actor, Effect,
1454 marginRect,
1455 layer);
1457 catch(...)
1459 // ASSERT(0);
1460 // throw;
1461 return(false);
1464 else if(entry == L"[script info]")
1466 fRet = true;
1468 else if(entry == L"playresx")
1470 try {ret.m_dstScreenSize.cx = GetInt(buff);}
1471 catch(...) {ret.m_dstScreenSize = CSize(0, 0); return(false);}
1473 if(ret.m_dstScreenSize.cy <= 0)
1475 ret.m_dstScreenSize.cy = (ret.m_dstScreenSize.cx == 1280)
1476 ? 1024
1477 : ret.m_dstScreenSize.cx * 3 / 4;
1480 else if(entry == L"playresy")
1482 try {ret.m_dstScreenSize.cy = GetInt(buff);}
1483 catch(...) {ret.m_dstScreenSize = CSize(0, 0); return(false);}
1485 if(ret.m_dstScreenSize.cx <= 0)
1487 ret.m_dstScreenSize.cx = (ret.m_dstScreenSize.cy == 1024)
1488 ? 1280
1489 : ret.m_dstScreenSize.cy * 4 / 3;
1492 else if(entry == L"wrapstyle")
1494 try {ret.m_defaultWrapStyle = GetInt(buff);}
1495 catch(...) {ret.m_defaultWrapStyle = 1; return(false);}
1497 else if(entry == L"scripttype")
1499 if(buff.GetLength() >= 4 && !buff.Right(4).CompareNoCase(L"4.00")) version = sver = 4;
1500 else if(buff.GetLength() >= 5 && !buff.Right(5).CompareNoCase(L"4.00+")) version = sver = 5;
1501 else if(buff.GetLength() >= 6 && !buff.Right(6).CompareNoCase(L"4.00++")) version = sver = 6;
1503 else if(entry == L"collisions")
1505 buff = GetStr(buff);
1506 buff.MakeLower();
1507 ret.m_collisions = buff.Find(L"reverse") >= 0 ? 1 : 0;
1509 else if(entry == L"scaledborderandshadow")
1511 buff = GetStr(buff);
1512 buff.MakeLower();
1513 ret.m_fScaledBAS = buff.Find(L"yes") >= 0;
1515 else if(entry == L"[v4 styles]")
1517 fRet = true;
1518 sver = 4;
1520 else if(entry == L"[v4+ styles]")
1522 fRet = true;
1523 sver = 5;
1525 else if(entry == L"[v4++ styles]")
1527 fRet = true;
1528 sver = 6;
1530 else if(entry == L"style")
1532 STSStyle* style = new STSStyle;
1533 if(!style) return(false);
1537 CString StyleName;
1538 int alpha;
1539 CRect tmp_rect;
1541 StyleName = WToT(GetStr(buff));
1542 style->fontName = WToT(GetStr(buff));
1543 style->fontSize = GetFloat(buff);
1544 for(int i = 0; i < 4; i++) style->colors[i] = (COLORREF)GetInt(buff);
1545 style->fontWeight = !!GetInt(buff) ? FW_BOLD : FW_NORMAL;
1546 style->fItalic = !!GetInt(buff);
1547 if(sver >= 5) style->fUnderline = !!GetInt(buff);
1548 if(sver >= 5) style->fStrikeOut = !!GetInt(buff);
1549 if(sver >= 5) style->fontScaleX = GetFloat(buff);
1550 if(sver >= 5) style->fontScaleY = GetFloat(buff);
1551 if(sver >= 5) style->fontSpacing = GetFloat(buff);
1552 if(sver >= 5) style->fontAngleZ = GetFloat(buff);
1553 if(sver >= 4) style->borderStyle = GetInt(buff);
1554 style->outlineWidthX = style->outlineWidthY = GetFloat(buff);
1555 style->shadowDepthX = style->shadowDepthY = GetFloat(buff);
1556 style->scrAlignment = GetInt(buff);
1557 tmp_rect.left = GetInt(buff);
1558 tmp_rect.right = GetInt(buff);
1559 tmp_rect.top = tmp_rect.bottom = GetInt(buff);
1560 if(sver >= 6) tmp_rect.bottom = GetInt(buff);
1561 style->marginRect = tmp_rect;
1562 if(sver <= 4) alpha = GetInt(buff);
1563 style->charSet = GetInt(buff);
1564 if(sver >= 6) style->relativeTo = GetInt(buff);
1566 if(sver <= 4) style->colors[2] = style->colors[3]; // style->colors[2] is used for drawing the outline
1567 if(sver <= 4) alpha = max(min(alpha, 0xff), 0);
1568 if(sver <= 4) {for(int i = 0; i < 3; i++) style->alpha[i] = alpha; style->alpha[3] = 0x80;}
1569 if(sver >= 5) for(int i = 0; i < 4; i++) {style->alpha[i] = (BYTE)(style->colors[i]>>24); style->colors[i] &= 0xffffff;}
1570 if(sver >= 5) style->fontScaleX = max(style->fontScaleX, 0);
1571 if(sver >= 5) style->fontScaleY = max(style->fontScaleY, 0);
1572 if(sver >= 5) style->fontSpacing = max(style->fontSpacing, 0);
1573 style->fontAngleX = style->fontAngleY = 0;
1574 style->borderStyle = style->borderStyle == 1 ? 0 : style->borderStyle == 3 ? 1 : 0;
1575 style->outlineWidthX = max(style->outlineWidthX, 0);
1576 style->outlineWidthY = max(style->outlineWidthY, 0);
1577 style->shadowDepthX = max(style->shadowDepthX, 0);
1578 style->shadowDepthY = max(style->shadowDepthY, 0);
1579 if(sver <= 4) style->scrAlignment = (style->scrAlignment&4) ? ((style->scrAlignment&3)+6) // top
1580 : (style->scrAlignment&8) ? ((style->scrAlignment&3)+3) // mid
1581 : (style->scrAlignment&3); // bottom
1583 StyleName.TrimLeft('*');
1585 ret.AddStyle(StyleName, style);
1587 catch(...)
1589 delete style;
1590 return(false);
1593 else if(entry == L"[events]")
1595 fRet = true;
1597 // else if(entry == _T("dialogue"))
1598 // {
1599 // try
1600 // {
1601 // CStringW::PXSTR __buff = buff.GetBuffer();
1602 // int hh1, mm1, ss1, ms1_div10, hh2, mm2, ss2, ms2_div10, layer = 0;
1603 // CString Style, Actor, Effect;
1604 // CRect marginRect;
1606 //if(version <= 4){NextStr(&__buff, '='); NextInt(&__buff);} /* Marked = */
1607 //if(version >= 5)layer = NextInt(&__buff);
1608 // hh1 = NextInt(&__buff, ':');
1609 // mm1 = NextInt(&__buff, ':');
1610 // ss1 = NextInt(&__buff, '.');
1611 // ms1_div10 = NextInt(&__buff);
1612 // hh2 = NextInt(&__buff, ':');
1613 // mm2 = NextInt(&__buff, ':');
1614 // ss2 = NextInt(&__buff, '.');
1615 // ms2_div10 = NextInt(&__buff);
1616 // Style = WToT(NextStr(&__buff));
1617 // Actor = WToT(NextStr(&__buff));
1618 // marginRect.left = NextInt(&__buff);
1619 // marginRect.right = NextInt(&__buff);
1620 // marginRect.top = marginRect.bottom = NextInt(&__buff);
1621 //if(version >= 6)marginRect.bottom = NextInt(&__buff);
1622 // Effect = WToT(NextStr(&__buff));
1624 // int len = min(Effect.GetLength(), buff.GetLength());
1625 // if(Effect.Left(len) == WToT(buff.Left(len))) Effect.Empty();
1627 // Style.TrimLeft('*');
1628 // if(!Style.CompareNoCase(_T("Default"))) Style = _T("Default");
1630 // ret.AddSTSEntryOnly(__buff,
1631 // file->IsUnicode(),
1632 // (((hh1*60 + mm1)*60) + ss1)*1000 + ms1_div10*10,
1633 // (((hh2*60 + mm2)*60) + ss2)*1000 + ms2_div10*10,
1634 // Style, Actor, Effect,
1635 // marginRect,
1636 // layer);
1637 // }
1638 // catch(...)
1639 // {
1640 //// ASSERT(0);
1641 //// throw;
1642 // return(false);
1643 // }
1644 // }
1645 else if(entry == L"fontname")
1647 LoadUUEFont(file);
1650 // ret.Sort();
1651 return(fRet);
1654 static bool OpenXombieSub(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet)
1656 float version = 0;
1658 // CMapStringToPtr stylemap;
1660 CStringW buff;
1661 while(file->ReadString(buff))
1663 buff.Trim();
1664 if(buff.IsEmpty() || buff.GetAt(0) == ';') continue;
1666 CStringW entry;
1668 // try {
1669 entry = GetStr(buff, '=');
1670 // }
1671 // catch(...) {continue;}
1673 entry.MakeLower();
1675 if(entry == L"version")
1677 version = (float)GetFloat(buff);
1679 else if(entry == L"screenhorizontal")
1681 try {ret.m_dstScreenSize.cx = GetInt(buff);}
1682 catch(...) {ret.m_dstScreenSize = CSize(0, 0); return(false);}
1684 if(ret.m_dstScreenSize.cy <= 0)
1686 ret.m_dstScreenSize.cy = (ret.m_dstScreenSize.cx == 1280)
1687 ? 1024
1688 : ret.m_dstScreenSize.cx * 3 / 4;
1691 else if(entry == L"screenvertical")
1693 try {ret.m_dstScreenSize.cy = GetInt(buff);}
1694 catch(...) {ret.m_dstScreenSize = CSize(0, 0); return(false);}
1696 if(ret.m_dstScreenSize.cx <= 0)
1698 ret.m_dstScreenSize.cx = (ret.m_dstScreenSize.cy == 1024)
1699 ? 1280
1700 : ret.m_dstScreenSize.cy * 4 / 3;
1703 else if(entry == L"style")
1705 STSStyle* style = new STSStyle;
1706 if(!style) return(false);
1710 CString StyleName;
1711 CRect tmp_rect;
1713 StyleName = WToT(GetStr(buff)) + _T("_") + WToT(GetStr(buff));
1714 style->fontName = WToT(GetStr(buff));
1715 style->fontSize = GetFloat(buff);
1716 for(int i = 0; i < 4; i++) style->colors[i] = (COLORREF)GetInt(buff);
1717 for(int i = 0; i < 4; i++) style->alpha[i] = GetInt(buff);
1718 style->fontWeight = !!GetInt(buff) ? FW_BOLD : FW_NORMAL;
1719 style->fItalic = !!GetInt(buff);
1720 style->fUnderline = !!GetInt(buff);
1721 style->fStrikeOut = !!GetInt(buff);
1722 style->fBlur = !!GetInt(buff);
1723 style->fontScaleX = GetFloat(buff);
1724 style->fontScaleY = GetFloat(buff);
1725 style->fontSpacing = GetFloat(buff);
1726 style->fontAngleX = GetFloat(buff);
1727 style->fontAngleY = GetFloat(buff);
1728 style->fontAngleZ = GetFloat(buff);
1729 style->borderStyle = GetInt(buff);
1730 style->outlineWidthX = style->outlineWidthY = GetFloat(buff);
1731 style->shadowDepthX = style->shadowDepthY = GetFloat(buff);
1732 style->scrAlignment = GetInt(buff);
1734 tmp_rect.left = GetInt(buff);
1735 tmp_rect.right = GetInt(buff);
1736 tmp_rect.top = tmp_rect.bottom = GetInt(buff);
1737 style->marginRect = tmp_rect;
1739 style->charSet = GetInt(buff);
1741 style->fontScaleX = max(style->fontScaleX, 0);
1742 style->fontScaleY = max(style->fontScaleY, 0);
1743 style->fontSpacing = max(style->fontSpacing, 0);
1744 style->borderStyle = style->borderStyle == 1 ? 0 : style->borderStyle == 3 ? 1 : 0;
1745 style->outlineWidthX = max(style->outlineWidthX, 0);
1746 style->outlineWidthY = max(style->outlineWidthY, 0);
1747 style->shadowDepthX = max(style->shadowDepthX, 0);
1748 style->shadowDepthY = max(style->shadowDepthY, 0);
1750 ret.AddStyle(StyleName, style);
1752 catch(...)
1754 delete style;
1755 return(false);
1758 else if(entry == L"line")
1762 CString id;
1763 int hh1, mm1, ss1, ms1, hh2, mm2, ss2, ms2, layer = 0;
1764 CString Style, Actor;
1765 CRect marginRect;
1767 if(GetStr(buff) != L"D") continue;
1768 id = GetStr(buff);
1769 layer = GetInt(buff);
1770 hh1 = GetInt(buff, ':');
1771 mm1 = GetInt(buff, ':');
1772 ss1 = GetInt(buff, '.');
1773 ms1 = GetInt(buff);
1774 hh2 = GetInt(buff, ':');
1775 mm2 = GetInt(buff, ':');
1776 ss2 = GetInt(buff, '.');
1777 ms2 = GetInt(buff);
1778 Style = WToT(GetStr(buff)) + _T("_") + WToT(GetStr(buff));
1779 Actor = WToT(GetStr(buff));
1780 marginRect.left = GetInt(buff);
1781 marginRect.right = GetInt(buff);
1782 marginRect.top = marginRect.bottom = GetInt(buff);
1784 Style.TrimLeft('*');
1785 if(!Style.CompareNoCase(_T("Default"))) Style = _T("Default");
1787 ret.Add(buff,
1788 file->IsUnicode(),
1789 (((hh1*60 + mm1)*60) + ss1)*1000 + ms1,
1790 (((hh2*60 + mm2)*60) + ss2)*1000 + ms2,
1791 Style, Actor, _T(""),
1792 marginRect,
1793 layer);
1795 catch(...)
1797 return(false);
1800 else if(entry == L"fontname")
1802 LoadUUEFont(file);
1806 return(!ret.IsEmpty());
1809 #include "USFSubtitles.h"
1811 static bool OpenUSF(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet)
1813 CString str;
1814 while(file->ReadString(str))
1816 if(str.Find(_T("USFSubtitles")) >= 0)
1818 CUSFSubtitles usf;
1819 if(usf.Read(file->GetFilePath()) && usf.ConvertToSTS(ret))
1820 return(true);
1822 break;
1826 return(false);
1829 static CStringW MPL22SSA(CStringW str)
1831 CAtlList<CStringW> sl;
1832 Explode(str, sl, '|');
1833 POSITION pos = sl.GetHeadPosition();
1834 while(pos)
1836 CStringW& s = sl.GetNext(pos);
1837 if(s[0] == '/') {s = L"{\\i1}" + s.Mid(1) + L"{\\i0}";}
1839 str = Implode(sl, '\n');
1840 str.Replace(L"\n", L"\\N");
1841 return str;
1844 static bool OpenMPL2(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet)
1846 CStringW buff;;
1847 while(file->ReadString(buff))
1849 buff.Trim();
1850 if(buff.IsEmpty()) continue;
1852 int start, end;
1853 int c = swscanf(buff, L"[%d][%d]", &start, &end);
1855 if(c == 2)
1857 ret.Add(
1858 MPL22SSA(buff.Mid(buff.Find(']', buff.Find(']')+1)+1)),
1859 file->IsUnicode(),
1860 start*100, end*100);
1862 else if(c != EOF) // might be another format
1864 return(false);
1868 return(!ret.IsEmpty());
1871 typedef bool (*STSOpenFunct)(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet);
1873 static bool OpenRealText(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet);
1875 typedef struct {STSOpenFunct open; tmode mode;} OpenFunctStruct;
1877 static OpenFunctStruct OpenFuncts[] =
1879 OpenSubStationAlpha, TIME,
1880 OpenSubRipper, TIME,
1881 OpenOldSubRipper, TIME,
1882 OpenSubViewer, TIME,
1883 OpenMicroDVD, FRAME,
1884 OpenSami, TIME,
1885 OpenVPlayer, TIME,
1886 OpenXombieSub, TIME,
1887 OpenUSF, TIME,
1888 OpenMPL2, TIME,
1889 OpenRealText, TIME,
1892 static int nOpenFuncts = countof(OpenFuncts);
1896 CSimpleTextSubtitle::CSimpleTextSubtitle()
1898 m_mode = TIME;
1899 m_dstScreenSize = CSize(0, 0);
1900 m_defaultWrapStyle = 0;
1901 m_collisions = 0;
1902 m_fScaledBAS = false;
1903 m_encoding = CTextFile::ASCII;
1904 m_ePARCompensationType = EPCTDisabled;
1905 m_dPARCompensation = 1.0;
1908 CSimpleTextSubtitle::~CSimpleTextSubtitle()
1910 Empty();
1913 CSimpleTextSubtitle::CSimpleTextSubtitle(CSimpleTextSubtitle& sts)
1915 *this = sts;
1918 CSimpleTextSubtitle& CSimpleTextSubtitle::operator = (CSimpleTextSubtitle& sts)
1920 Empty();
1922 m_name = sts.m_name;
1923 m_mode = sts.m_mode;
1924 m_dstScreenSize = sts.m_dstScreenSize;
1925 m_defaultWrapStyle = sts.m_defaultWrapStyle;
1926 m_collisions = sts.m_collisions;
1927 m_fScaledBAS = sts.m_fScaledBAS;
1928 m_fSSA = sts.m_fSSA;
1929 m_fUsingAutoGeneratedDefaultStyle = sts.m_fUsingAutoGeneratedDefaultStyle;
1930 CopyStyles(sts.m_styles);
1931 m_segments.Copy(sts.m_segments);
1932 Copy(sts);
1934 return(*this);
1938 void CSimpleTextSubtitle::Copy(CSimpleTextSubtitle& sts)
1940 Empty();
1942 m_name = sts.m_name;
1943 m_mode = sts.m_mode;
1944 m_dstScreenSize = sts.m_dstScreenSize;
1945 m_defaultWrapStyle = sts.m_defaultWrapStyle;
1946 m_collisions = sts.m_collisions;
1947 m_fScaledBAS = sts.m_fScaledBAS;
1948 m_encoding = sts.m_encoding;
1949 m_fUsingAutoGeneratedDefaultStyle = sts.m_fUsingAutoGeneratedDefaultStyle;
1950 CopyStyles(sts.m_styles);
1951 m_segments.Copy(sts.m_segments);
1952 m_entries.Copy(sts.m_entries);
1955 void CSimpleTextSubtitle::Append(CSimpleTextSubtitle& sts, int timeoff)
1957 if(timeoff < 0)
1959 timeoff = m_entries.GetCount() > 0 ? m_entries.GetAt(m_entries.GetCount()-1).end : 0;
1962 for(int i = 0, j = m_entries.GetCount(); i < j; i++)
1964 if(m_entries.GetAt(i).start > timeoff)
1966 m_entries.RemoveAt(i, j - i);
1967 break;
1971 CopyStyles(sts.m_styles, true);
1973 for(int i = 0, j = sts.m_entries.GetCount(); i < j; i++)
1975 STSEntry stse = sts.m_entries.GetAt(i);
1976 stse.start += timeoff;
1977 stse.end += timeoff;
1978 stse.readorder += m_entries.GetCount();
1979 m_entries.Add(stse);
1982 CreateSegments();
1985 void CSTSStyleMap::Free()
1987 POSITION pos = GetStartPosition();
1988 while(pos)
1990 CString key;
1991 STSStyle* val;
1992 GetNextAssoc(pos, key, val);
1993 delete val;
1996 RemoveAll();
1999 bool CSimpleTextSubtitle::CopyStyles(const CSTSStyleMap& styles, bool fAppend)
2001 if(!fAppend) m_styles.Free();
2003 POSITION pos = styles.GetStartPosition();
2004 while(pos)
2006 CString key;
2007 STSStyle* val;
2008 styles.GetNextAssoc(pos, key, val);
2010 STSStyle* s = new STSStyle;
2011 if(!s) return(false);
2013 *s = *val;
2015 AddStyle(key, s);
2018 return(true);
2021 void CSimpleTextSubtitle::Empty()
2023 m_dstScreenSize = CSize(0, 0);
2024 m_styles.Free();
2025 m_segments.RemoveAll();
2026 m_entries.RemoveAll();
2029 void CSimpleTextSubtitle::Add(CStringW str, bool fUnicode, int start, int end,
2030 CString style, const CString& actor, const CString& effect, const CRect& marginRect, int layer, int readorder)
2032 XY_LOG_INFO(start<<_T(" ")<<end<<_T(" ")<<str.GetString()<<_T(" style:")<<style.GetString()
2033 <<_T(" Unicode:")<<fUnicode
2034 <<_T(" actor:")<<actor.GetString()<<_T(" effect:")<<effect.GetString()
2035 <<_T(" (l:")<<marginRect.left<<_T(",t:")<<marginRect.top<<_T(",r:")<<marginRect.right<<_T(",b:")<<marginRect.bottom
2036 <<_T(" layer:")<<layer<<_T(" readorder:")<<readorder
2037 <<_T(" entries:")<<m_entries.GetCount()<<_T(" seg:")<<m_segments.GetCount());
2039 if(start > end || str.Trim().IsEmpty() ) return;
2041 str.Remove('\r');
2042 str.Replace(L"\n", L"\\N");
2043 if(style.IsEmpty()) style = g_default_style;
2044 else if(style!=g_default_style)
2046 style.TrimLeft('*');
2049 STSEntry sub;
2050 sub.str = str;
2051 sub.fUnicode = fUnicode;
2052 sub.style = style;
2053 sub.actor = actor;
2054 sub.effect = effect;
2055 sub.marginRect = marginRect;
2056 sub.layer = layer;
2057 sub.start = start;
2058 sub.end = end;
2059 sub.readorder = readorder < 0 ? m_entries.GetCount() : readorder;
2060 int n = m_entries.Add(sub);
2062 int len = m_segments.GetCount();
2064 if(len == 0)
2066 STSSegment stss(start, end);
2067 stss.subs.Add(n);
2068 m_segments.Add(stss);
2070 else if(end <= m_segments[0].start)
2072 STSSegment stss(start, end);
2073 stss.subs.Add(n);
2074 m_segments.InsertAt(0, stss);
2076 else if(start >= m_segments[len-1].end)
2078 STSSegment stss(start, end);
2079 stss.subs.Add(n);
2080 m_segments.Add(stss);
2082 else
2084 if(start < m_segments[0].start)
2086 STSSegment stss(start, m_segments[0].start);
2087 stss.subs.Add(n);
2088 start = m_segments[0].start;
2089 m_segments.InsertAt(0, stss);
2092 for(size_t i = 0; i < m_segments.GetCount(); i++)
2094 STSSegment& s = m_segments[i];
2096 if(start >= s.end)
2098 continue;
2100 else if(end <= s.start)
2102 break;
2104 else if(s.start < start && start < s.end)
2106 STSSegment stss(s.start, start);
2107 stss.subs.Copy(s.subs);
2108 s.start = start;
2109 m_segments.InsertAt(i, stss);
2110 continue;
2112 if(start <= s.start && s.end <= end)
2114 for(int j = 0, k = s.subs.GetCount(); j <= k; j++)
2116 if(j == k || sub.readorder < m_entries.GetAt(s.subs[j]).readorder)
2118 s.subs.InsertAt(j, n);
2119 break;
2123 else if(s.start < end && end < s.end)
2125 STSSegment stss(s.start, end);
2126 stss.subs.Copy(s.subs);
2127 for(int j = 0, k = s.subs.GetCount(); j <= k; j++)
2129 if(j == k || sub.readorder < m_entries.GetAt(stss.subs[j]).readorder)
2131 stss.subs.InsertAt(j, n);
2132 break;
2135 s.start = end;
2136 m_segments.InsertAt(i, stss);
2140 if(end > m_segments[m_segments.GetCount()-1].end)
2142 STSSegment stss(m_segments[m_segments.GetCount()-1].end, end);
2143 stss.subs.Add(n);
2144 m_segments.Add(stss);
2148 str.Remove('\r');
2149 str.Replace(L"\n", L"\\N");
2150 if(style.IsEmpty()) style = _T("Default");
2152 int j = m_segments.GetCount();
2153 for(int i = j-1; i >= 0; i--)
2155 if(m_segments[i].end <= start)
2157 break;
2159 else if(m_segments[i].start >= start)
2161 m_segments.SetCount(m_segments.GetCount()-1);
2162 j--;
2164 else if(m_segments[i].end > start)
2166 if(i < j-1) m_segments.RemoveAt(i+1, j-i-1);
2167 m_segments[i].end = start;
2168 break;
2172 if(m_segments.GetCount() == 0 && j > 0)
2173 CSTSArray::RemoveAll();
2175 STSSegment stss(start, end);
2176 int len = m_entries.GetCount();
2177 stss.subs.Add(len);
2178 m_segments.Add(stss);
2180 STSEntry sub;
2181 sub.str = str;
2182 sub.fUnicode = fUnicode;
2183 sub.style = style;
2184 sub.actor = actor;
2185 sub.effect = effect;
2186 sub.marginRect = marginRect;
2187 sub.layer = layer;
2188 sub.start = start;
2189 sub.end = end;
2190 sub.readorder = m_entries.GetCount();
2191 CSTSArray::Add(sub);
2195 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*/ )
2197 if(str.Trim().IsEmpty() || start > end) return;
2199 str.Remove('\r');
2200 str.Replace(L"\n", L"\\N");
2201 if(style.IsEmpty()) style = _T("Default");
2202 style.TrimLeft('*');
2204 STSEntry sub;
2205 sub.str = str;
2206 sub.fUnicode = fUnicode;
2207 sub.style = style;
2208 sub.actor = actor;
2209 sub.effect = effect;
2210 sub.marginRect = marginRect;
2211 sub.layer = layer;
2212 sub.start = start;
2213 sub.end = end;
2214 sub.readorder = readorder < 0 ? m_entries.GetCount() : readorder;
2215 m_entries.Add(sub);
2216 return;
2219 STSStyle* CSimpleTextSubtitle::CreateDefaultStyle(int CharSet)
2221 STSStyle* ret = NULL;
2223 if(!m_styles.Lookup(g_default_style, ret))
2225 STSStyle* style = new STSStyle();
2226 style->charSet = CharSet;
2227 AddStyle(g_default_style, style);
2228 m_styles.Lookup(g_default_style, ret);
2230 m_fUsingAutoGeneratedDefaultStyle = true;
2232 else
2234 m_fUsingAutoGeneratedDefaultStyle = false;
2237 return ret;
2240 void CSimpleTextSubtitle::ChangeUnknownStylesToDefault()
2242 CAtlMap<CString, STSStyle*, CStringElementTraits<CString> > unknown;
2243 bool fReport = true;
2245 for(size_t i = 0; i < m_entries.GetCount(); i++)
2247 STSEntry& stse = m_entries.GetAt(i);
2249 STSStyle* val;
2250 if(!m_styles.Lookup(stse.style, val))
2252 if(!unknown.Lookup(stse.style, val))
2254 if(fReport)
2256 CString msg;
2257 msg.Format(_T("Unknown style found: \"%s\", changed to \"Default\"!\n\nPress Cancel to ignore further warnings."), stse.style);
2258 if(MessageBox(NULL, msg, _T("Warning"), MB_OKCANCEL|MB_ICONWARNING) != IDOK) fReport = false;
2261 unknown[stse.style] = NULL;
2264 stse.style = g_default_style;
2269 void CSimpleTextSubtitle::AddStyle(CString name, STSStyle* style)
2271 int i, j;
2273 if(name.IsEmpty()) name = g_default_style;
2275 STSStyle* val;
2276 if(m_styles.Lookup(name, val))
2278 if(*val == *style)
2280 delete style;
2281 return;
2283 const CString& name_str = name;
2285 int len = name_str.GetLength();
2287 for(i = len; i > 0 && _istdigit(name_str[i-1]); i--);
2289 int idx = 1;
2291 CString name2 = name_str;
2293 if(i < len && _stscanf(name_str.Right(len-i), _T("%d"), &idx) == 1)
2295 name2 = name_str.Left(i);
2298 idx++;
2300 CString name3;
2301 CString name3_str;
2304 name3_str.Format(_T("%s%d"), name2, idx);
2305 name3 = name3_str;
2306 idx++;
2308 while(m_styles.Lookup(name3));
2310 m_styles.RemoveKey(name);
2311 m_styles[name3] = val;
2313 for(i = 0, j = m_entries.GetCount(); i < j; i++)
2315 STSEntry& stse = m_entries.GetAt(i);
2316 if(stse.style == name) stse.style = name3;
2320 m_styles[name] = style;
2323 bool CSimpleTextSubtitle::SetDefaultStyle(STSStyle& s)
2325 DbgLog((LOG_TRACE, 3, "%s(%d): %s", __FILE__, __LINE__, __FUNCTION__));
2326 DbgLog((LOG_TRACE, 3, "\tm_styles count:%d", m_styles.GetCount()));
2327 STSStyle* val;
2328 if(!m_styles.Lookup(g_default_style, val)) return false;
2329 DbgLog((LOG_TRACE, 3, "\tm_styles Lookup Default succeed"));
2331 #ifdef DEBUG
2332 for(POSITION pos=m_styles.GetStartPosition(); pos!=NULL;)
2334 DbgLog((LOG_TRACE, 3, _T("\tm_styles[%s]"), (LPCTSTR)m_styles.GetNextKey(pos)));
2336 #endif
2338 *val = s;
2339 m_fUsingAutoGeneratedDefaultStyle = false;
2340 return true;
2343 bool CSimpleTextSubtitle::GetDefaultStyle(STSStyle& s)
2345 STSStyle* val;
2346 if(!m_styles.Lookup(g_default_style, val)) return false;
2347 s = *val;
2348 return true;
2351 void CSimpleTextSubtitle::ConvertToTimeBased(double fps)
2353 if(m_mode == TIME) return;
2355 for(int i = 0, j = m_entries.GetCount(); i < j; i++)
2357 STSEntry& stse = m_entries[i];
2358 stse.start = int(1.0 * stse.start * 1000 / fps + 0.5);
2359 stse.end = int(1.0 * stse.end * 1000 / fps + 0.5);
2362 m_mode = TIME;
2364 CreateSegments();
2367 void CSimpleTextSubtitle::ConvertToFrameBased(double fps)
2369 if(m_mode == FRAME) return;
2371 for(int i = 0, j = m_entries.GetCount(); i < j; i++)
2373 STSEntry& stse = m_entries[i];
2374 stse.start = int(1.0 * stse.start * fps / 1000 + 0.5);
2375 stse.end = int(1.0 * stse.end * fps / 1000 + 0.5);
2378 m_mode = FRAME;
2380 CreateSegments();
2383 int CSimpleTextSubtitle::SearchSub(int t, double fps)
2385 int i = 0, j = m_entries.GetCount() - 1, ret = -1;
2387 if(j >= 0 && t >= TranslateStart(j, fps))
2389 return(j);
2392 while(i < j)
2394 int mid = (i + j) >> 1;
2396 int midt = TranslateStart(mid, fps);
2398 if(t == midt)
2400 while(mid > 0 && t == TranslateStart(mid-1, fps)) mid--;
2401 ret = mid;
2402 break;
2404 else if(t < midt)
2406 ret = -1;
2407 if(j == mid) mid--;
2408 j = mid;
2410 else if(t > midt)
2412 ret = mid;
2413 if(i == mid) mid++;
2414 i = mid;
2418 return(ret);
2421 const STSSegment* CSimpleTextSubtitle::SearchSubs(int t, double fps, /*[out]*/ int* iSegment, int* nSegments)
2423 int segmentsCount = m_segments.GetCount();
2424 int i = 0, j = segmentsCount - 1;
2426 if(nSegments) *nSegments = segmentsCount;
2427 if(segmentsCount<=0)
2429 if(iSegment!=NULL)
2430 *iSegment = 0;
2431 return NULL;
2434 if(t >= TranslateSegmentEnd(j, fps))
2436 i = j;
2437 j++;
2439 if(t < TranslateSegmentEnd(i, fps))
2441 j = i;
2442 i--;
2445 while(i < j-1)
2447 int mid = (i + j) >> 1;
2449 int midt = TranslateSegmentEnd(mid, fps);
2451 if(t < midt)
2452 j=mid;
2453 else
2454 i=mid;
2456 if(iSegment!=NULL)
2457 *iSegment = j;
2458 if(j<segmentsCount)
2460 return &m_segments[j];
2462 return(NULL);
2465 STSSegment* CSimpleTextSubtitle::SearchSubs2(int t, double fps, /*[out]*/ int* iSegment, int* nSegments)
2467 int segmentsCount = m_segments.GetCount();
2468 int i = 0, j = segmentsCount - 1;
2470 if(iSegment) *iSegment = -1;
2471 if(nSegments) *nSegments = segmentsCount;
2473 if(segmentsCount<=0)
2475 if(iSegment) *iSegment = 0;
2476 return NULL;
2479 if(t >= TranslateSegmentEnd(j, fps))
2481 i = j;
2482 j++;
2484 if(t < TranslateSegmentEnd(i, fps))
2486 j = i;
2487 i--;
2490 while(i < j-1)
2492 int mid = (i + j) >> 1;
2494 int midt = TranslateSegmentEnd(mid, fps);
2496 if(t < midt)
2497 j=mid;
2498 else
2499 i=mid;
2501 if(j<segmentsCount && t>=TranslateSegmentStart(j, fps))
2503 if(iSegment) *iSegment = j;
2504 return &m_segments[j];
2506 return(NULL);
2511 int CSimpleTextSubtitle::TranslateStart(int i, double fps)
2513 return(i < 0 || m_entries.GetCount() <= i ? -1 :
2514 m_mode == TIME ? m_entries.GetAt(i).start :
2515 m_mode == FRAME ? (int)(m_entries.GetAt(i).start*1000/fps) :
2519 int CSimpleTextSubtitle::TranslateEnd(int i, double fps)
2521 return(i < 0 || m_entries.GetCount() <= i ? -1 :
2522 m_mode == TIME ? m_entries.GetAt(i).end :
2523 m_mode == FRAME ? (int)(m_entries.GetAt(i).end*1000/fps) :
2527 int CSimpleTextSubtitle::TranslateSegmentStart(int i, double fps)
2529 return(i < 0 || m_segments.GetCount() <= i ? -1 :
2530 m_mode == TIME ? m_segments[i].start :
2531 m_mode == FRAME ? (int)(m_segments[i].start*1000/fps) :
2535 int CSimpleTextSubtitle::TranslateSegmentEnd(int i, double fps)
2537 return(i < 0 || m_segments.GetCount() <= i ? -1 :
2538 m_mode == TIME ? m_segments[i].end :
2539 m_mode == FRAME ? (int)(m_segments[i].end*1000/fps) :
2543 void CSimpleTextSubtitle::TranslateSegmentStartEnd(int i, double fps, /*out*/int& start, /*out*/int& end)
2545 if(i < 0 || m_segments.GetCount() <= i)
2547 start=-1;
2548 end=-1;
2550 else
2552 if(m_mode == TIME)
2554 start = m_segments[i].start;
2555 end = m_segments[i].end;
2557 else //m_mode == FRAME
2559 start = (int)(m_segments[i].start*1000/fps);
2560 end = (int)(m_segments[i].end*1000/fps);
2565 STSStyle* CSimpleTextSubtitle::GetStyle(int i)
2567 STSStyle* style = NULL;
2568 m_styles.Lookup(m_entries.GetAt(i).style, style);
2570 STSStyle* defstyle = NULL;
2571 m_styles.Lookup(g_default_style, defstyle);
2573 if(!style)
2575 style = defstyle;
2578 ASSERT(style);
2580 return style;
2583 bool CSimpleTextSubtitle::GetStyle(int i, STSStyle* const stss)
2585 STSStyle* style = NULL;
2586 m_styles.Lookup(m_entries.GetAt(i).style, style);
2588 STSStyle* defstyle = NULL;
2589 m_styles.Lookup(g_default_style, defstyle);
2591 if(!style)
2593 if(!defstyle)
2595 defstyle = CreateDefaultStyle(DEFAULT_CHARSET);
2598 style = defstyle;
2601 if(!style) {ASSERT(0); return false;}
2603 *stss = *style;
2604 if(stss->relativeTo == 2 && defstyle)
2605 stss->relativeTo = defstyle->relativeTo;
2607 return true;
2610 int CSimpleTextSubtitle::GetCharSet(int i)
2612 STSStyle* style = GetStyle(i);
2613 return style!=NULL ? style->charSet : -1;
2616 bool CSimpleTextSubtitle::IsEntryUnicode(int i)
2618 return(m_entries.GetAt(i).fUnicode);
2621 void CSimpleTextSubtitle::ConvertUnicode(int i, bool fUnicode)
2623 STSEntry& stse = m_entries.GetAt(i);
2625 if(stse.fUnicode ^ fUnicode)
2627 int CharSet = GetCharSet(i);
2629 stse.str = fUnicode
2630 ? MBCSSSAToUnicode(stse.str, CharSet)
2631 : UnicodeSSAToMBCS(stse.str, CharSet);
2633 stse.fUnicode = fUnicode;
2637 CStringA CSimpleTextSubtitle::GetStrA(int i, bool fSSA)
2639 return(WToA(GetStrWA(i, fSSA)));
2642 CStringW CSimpleTextSubtitle::GetStrW(int i, bool fSSA)
2644 bool fUnicode = IsEntryUnicode(i);
2645 int CharSet = GetCharSet(i);
2647 CStringW str = m_entries.GetAt(i).str;
2649 if(!fUnicode)
2650 str = MBCSSSAToUnicode(str, CharSet);
2652 if(!fSSA)
2653 str = RemoveSSATags(str, fUnicode, CharSet);
2655 return(str);
2658 CStringW CSimpleTextSubtitle::GetStrWA(int i, bool fSSA)
2660 bool fUnicode = IsEntryUnicode(i);
2661 int CharSet = GetCharSet(i);
2663 CStringW str = m_entries.GetAt(i).str;
2665 if(fUnicode)
2666 str = UnicodeSSAToMBCS(str, CharSet);
2668 if(!fSSA)
2669 str = RemoveSSATags(str, fUnicode, CharSet);
2671 return(str);
2674 void CSimpleTextSubtitle::SetStr(int i, CStringA str, bool fUnicode)
2676 SetStr(i, AToW(str), false);
2679 void CSimpleTextSubtitle::SetStr(int i, CStringW str, bool fUnicode)
2681 STSEntry& stse = m_entries.GetAt(i);
2683 str.Replace(L"\n", L"\\N");
2685 if(stse.fUnicode && !fUnicode) stse.str = MBCSSSAToUnicode(str, GetCharSet(i));
2686 else if(!stse.fUnicode && fUnicode) stse.str = UnicodeSSAToMBCS(str, GetCharSet(i));
2687 else stse.str = str;
2690 static int comp1(const void* a, const void* b)
2692 int ret = ((STSEntry*)a)->start - ((STSEntry*)b)->start;
2693 if(ret == 0) ret = ((STSEntry*)a)->layer - ((STSEntry*)b)->layer;
2694 if(ret == 0) ret = ((STSEntry*)a)->readorder - ((STSEntry*)b)->readorder;
2695 return(ret);
2698 static int comp2(const void* a, const void* b)
2700 return(((STSEntry*)a)->readorder - ((STSEntry*)b)->readorder);
2703 void CSimpleTextSubtitle::Sort(bool fRestoreReadorder)
2705 qsort(m_entries.GetData(), m_entries.GetCount(), sizeof(STSEntry), !fRestoreReadorder ? comp1 : comp2);
2706 CreateSegments();
2709 static int intcomp(const void* i1, const void* i2)
2711 return(*((int*)i1) - *((int*)i2));
2714 void CSimpleTextSubtitle::CreateSegments()
2716 m_segments.RemoveAll();
2718 if(m_entries.GetCount()>0)
2720 size_t start, mid, end;
2721 CAtlArray<STSSegment> tempSegments;//if add to m_segments directly, then remove empty entities can be a
2722 //complex operation when having large segmentCount and lots of empty entities
2723 std::vector<int> breakpoints(2*m_entries.GetCount());
2724 for(size_t i = 0; i < m_entries.GetCount(); i++)
2726 STSEntry& stse = m_entries.GetAt(i);
2727 breakpoints[2*i]=stse.start;
2728 breakpoints[2*i+1]=stse.end;
2731 std::sort(breakpoints.begin(), breakpoints.end());
2733 int ptr = 1, prev = breakpoints[0];
2734 for(size_t i = breakpoints.size()-1; i > 0; i--, ptr++)
2736 if(breakpoints[ptr] != prev)
2738 tempSegments.Add(STSSegment(prev, breakpoints[ptr]));
2739 prev = breakpoints[ptr];
2743 size_t segmentCount = tempSegments.GetCount();
2745 for(size_t i = 0; i < m_entries.GetCount(); i++)
2747 STSEntry& stse = m_entries.GetAt(i);
2748 start = 0;
2749 end = segmentCount;
2750 while(start<end)
2752 mid = (start+end)>>1;
2753 if(tempSegments[mid].start < stse.start)
2755 start = mid+1;
2757 else
2759 end = mid;
2762 for(; start < tempSegments.GetCount() && tempSegments[start].end <= stse.end; start++)
2763 tempSegments[start].subs.Add(i);
2765 for(size_t i = 0; i < segmentCount; i++)
2766 if(tempSegments[i].subs.GetCount()>0)
2767 m_segments.Add(tempSegments[i]);
2769 OnChanged();
2771 for(i = 0, j = m_segments.GetCount(); i < j; i++)
2773 STSSegment& stss = m_segments[i];
2775 TRACE(_T("%d - %d"), stss.start, stss.end);
2777 for(int k = 0, l = stss.subs.GetCount(); k < l; k++)
2779 TRACE(_T(", %d"), stss.subs[k]);
2782 TRACE(_T("\n"));
2787 bool CSimpleTextSubtitle::Open(CString fn, int CharSet, CString name)
2789 Empty();
2791 CWebTextFile f;
2792 if(!f.Open(fn)) return(false);
2794 fn.Replace('\\', '/');
2795 if(name.IsEmpty())
2797 name = fn.Left(fn.ReverseFind('.'));
2798 name = name.Mid(name.ReverseFind('/')+1);
2799 name = name.Mid(name.ReverseFind('.')+1);
2802 return(Open(&f, CharSet, name));
2805 static int CountLines(CTextFile* f, ULONGLONG from, ULONGLONG to)
2807 int n = 0;
2808 CString s;
2809 f->Seek(from, 0);
2810 while(f->ReadString(s) && f->GetPosition() < to) n++;
2811 return(n);
2814 bool CSimpleTextSubtitle::Open(CTextFile* f, int CharSet, CString name)
2816 Empty();
2818 ULONGLONG pos = f->GetPosition();
2820 for(int i = 0; i < nOpenFuncts; i++)
2822 const TCHAR* func_name[]={
2823 TEXT("OpenSubStationAlpha"),
2824 TEXT("OpenSubRipper"),
2825 TEXT("OpenOldSubRipper"),
2826 TEXT("OpenSubViewer"),
2827 TEXT("OpenMicroDVD"),
2828 TEXT("OpenSami"),
2829 TEXT("OpenVPlayer"),
2830 TEXT("OpenXombieSub"),
2831 TEXT("OpenUSF"),
2832 TEXT("OpenMPL2"),
2833 TEXT("OpenRealText")};
2834 CAutoTiming t(func_name[i],0);
2836 if(!OpenFuncts[i].open(f, *this, CharSet) /*|| !GetCount()*/)
2838 if(m_entries.GetCount() > 0)
2840 int n = CountLines(f, pos, f->GetPosition());
2841 CString s;
2842 s.Format(_T("Syntax error at line %d!\t"), n+1);
2843 AfxMessageBox(s, MB_OK|MB_ICONERROR);
2844 Empty();
2845 break;
2848 f->Seek(pos, 0);
2849 Empty();
2850 continue;
2853 m_name = name;
2854 m_mode = OpenFuncts[i].mode;
2855 m_encoding = f->GetEncoding();
2856 m_path = f->GetFilePath();
2858 CWebTextFile f2;
2859 if(f2.Open(f->GetFilePath() + _T(".style")))
2860 OpenSubStationAlpha(&f2, *this, CharSet);
2862 // Sort();
2863 CreateSegments();
2865 CreateDefaultStyle(CharSet);
2867 ChangeUnknownStylesToDefault();
2869 if(m_dstScreenSize == CSize(0, 0)) m_dstScreenSize = CSize(384, 288);
2871 return(true);
2874 return(false);
2877 bool CSimpleTextSubtitle::Open(BYTE* data, int len, int CharSet, CString name)
2879 TCHAR path[MAX_PATH];
2880 if(!GetTempPath(MAX_PATH, path)) return(false);
2882 TCHAR fn[MAX_PATH];
2883 if(!GetTempFileName(path, _T("vs"), 0, fn)) return(false);
2885 FILE* tmp = _tfopen(fn, _T("wb"));
2886 if(!tmp) return(false);
2888 int i = 0;
2889 for(; i <= (len-1024); i += 1024) fwrite(&data[i], 1024, 1, tmp);
2890 if(len > i) fwrite(&data[i], len - i, 1, tmp);
2892 fclose(tmp);
2894 bool fRet = Open(fn, CharSet, name);
2896 _tremove(fn);
2898 return(fRet);
2901 bool CSimpleTextSubtitle::SaveAs(CString fn, exttype et, double fps, CTextFile::enc e)
2903 if(fn.Mid(fn.ReverseFind('.')+1).CompareNoCase(exttypestr[et]))
2905 if(fn[fn.GetLength()-1] != '.') fn += _T(".");
2906 fn += exttypestr[et];
2909 CTextFile f;
2910 if(!f.Save(fn, e))
2911 return(false);
2913 if(et == EXTSMI)
2915 CString str;
2917 str += _T("<SAMI>\n<HEAD>\n");
2918 str += _T("<STYLE TYPE=\"text/css\">\n");
2919 str += _T("<!--\n");
2920 str += _T("P {margin-left: 16pt; margin-right: 16pt; margin-bottom: 16pt; margin-top: 16pt;\n");
2921 str += _T(" text-align: center; font-size: 18pt; font-family: arial; font-weight: bold; color: #f0f0f0;}\n");
2922 str += _T(".UNKNOWNCC {Name:Unknown; lang:en-US; SAMIType:CC;}\n");
2923 str += _T("-->\n");
2924 str += _T("</STYLE>\n");
2925 str += _T("</HEAD>\n");
2926 str += _T("\n");
2927 str += _T("<BODY>\n");
2929 f.WriteString(str);
2931 else if(et == EXTSSA || et == EXTASS)
2933 CString str;
2935 str = _T("[Script Info]\n");
2936 str += (et == EXTSSA) ? _T("; This is a Sub Station Alpha v4 script.\n") : _T("; This is an Advanced Sub Station Alpha v4+ script.\n");
2937 str += _T("; For Sub Station Alpha info and downloads,\n");
2938 str += _T("; go to http://www.eswat.demon.co.uk/\n");
2939 str += _T("; or email kotus@eswat.demon.co.uk\n");
2940 str += _T("; \n");
2941 if(et == EXTASS)
2943 str += _T("; Advanced Sub Station Alpha script format developed by #Anime-Fansubs@EfNET\n");
2944 str += _T("; http://www.anime-fansubs.org\n");
2945 str += _T("; \n");
2946 str += _T("; For additional info and downloads go to http://gabest.org/\n");
2947 str += _T("; or email gabest@freemail.hu\n");
2948 str += _T("; \n");
2950 str += _T("; Note: This file was saved by Subresync.\n");
2951 str += _T("; \n");
2952 str += (et == EXTSSA) ? _T("ScriptType: v4.00\n") : _T("ScriptType: v4.00+\n");
2953 str += (m_collisions == 0) ? _T("Collisions: Normal\n") : _T("Collisions: Reverse\n");
2954 if(et == EXTASS && m_fScaledBAS) str += _T("ScaledBorderAndShadow: Yes\n");
2955 str += _T("PlayResX: %d\n");
2956 str += _T("PlayResY: %d\n");
2957 str += _T("Timer: 100.0000\n");
2958 str += _T("\n");
2959 str += (et == EXTSSA)
2960 ? _T("[V4 Styles]\nFormat: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, TertiaryColour, BackColour, Bold, Italic, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, AlphaLevel, Encoding\n")
2961 : _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");
2963 CString str2;
2964 str2.Format(str, m_dstScreenSize.cx, m_dstScreenSize.cy);
2965 f.WriteString(str2);
2967 str = (et == EXTSSA)
2968 ? _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")
2969 : _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");
2971 POSITION pos = m_styles.GetStartPosition();
2972 while(pos)
2974 CString key;
2975 STSStyle* s;
2976 m_styles.GetNextAssoc(pos, key, s);
2978 if(et == EXTSSA)
2980 CString str2;
2981 str2.Format(str, key,
2982 s->fontName, (int)s->fontSize,
2983 s->colors[0]&0xffffff,
2984 s->colors[1]&0xffffff,
2985 s->colors[2]&0xffffff,
2986 s->colors[3]&0xffffff,
2987 s->fontWeight > FW_NORMAL ? -1 : 0, s->fItalic ? -1 : 0,
2988 s->borderStyle == 0 ? 1 : s->borderStyle == 1 ? 3 : 0,
2989 (int)s->outlineWidthY, (int)s->shadowDepthY,
2990 s->scrAlignment <= 3 ? s->scrAlignment : s->scrAlignment <= 6 ? ((s->scrAlignment-3)|8) : s->scrAlignment <= 9 ? ((s->scrAlignment-6)|4) : 2,
2991 s->marginRect.get().left, s->marginRect.get().right, (s->marginRect.get().top + s->marginRect.get().bottom) / 2,
2992 s->alpha[0],
2993 s->charSet);
2994 f.WriteString(str2);
2996 else
2998 CString str2;
2999 str2.Format(str, key,
3000 s->fontName, (int)s->fontSize,
3001 (s->colors[0]&0xffffff) | (s->alpha[0]<<24),
3002 (s->colors[1]&0xffffff) | (s->alpha[1]<<24),
3003 (s->colors[2]&0xffffff) | (s->alpha[2]<<24),
3004 (s->colors[3]&0xffffff) | (s->alpha[3]<<24),
3005 s->fontWeight > FW_NORMAL ? -1 : 0,
3006 s->fItalic ? -1 : 0, s->fUnderline ? -1 : 0, s->fStrikeOut ? -1 : 0,
3007 (int)s->fontScaleX, (int)s->fontScaleY,
3008 (int)s->fontSpacing, (float)s->fontAngleZ,
3009 s->borderStyle == 0 ? 1 : s->borderStyle == 1 ? 3 : 0,
3010 (int)s->outlineWidthY, (int)s->shadowDepthY,
3011 s->scrAlignment,
3012 s->marginRect.get().left, s->marginRect.get().right, (s->marginRect.get().top + s->marginRect.get().bottom) / 2,
3013 s->charSet);
3014 f.WriteString(str2);
3018 if(m_entries.GetCount() > 0)
3020 str = _T("\n");
3021 str += _T("[Events]\n");
3022 str += (et == EXTSSA)
3023 ? _T("Format: Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\n")
3024 : _T("Format: Layer, Start, End, Style, Actor, MarginL, MarginR, MarginV, Effect, Text\n");
3025 f.WriteString(str);
3029 CStringW fmt =
3030 et == EXTSRT ? L"%d\n%02d:%02d:%02d,%03d --> %02d:%02d:%02d,%03d\n%s\n\n" :
3031 et == EXTSUB ? L"{%d}{%d}%s\n" :
3032 et == EXTSMI ? L"<SYNC Start=%d><P Class=UNKNOWNCC>\n%s\n<SYNC Start=%d><P Class=UNKNOWNCC>&nbsp;\n" :
3033 et == EXTPSB ? L"{%d:%02d:%02d}{%d:%02d:%02d}%s\n" :
3034 et == EXTSSA ? L"Dialogue: Marked=0,%d:%02d:%02d.%02d,%d:%02d:%02d.%02d,%s,%s,%04d,%04d,%04d,%s,%s\n" :
3035 et == EXTASS ? L"Dialogue: %d,%d:%02d:%02d.%02d,%d:%02d:%02d.%02d,%s,%s,%04d,%04d,%04d,%s,%s\n" :
3036 L"";
3037 // Sort(true);
3039 for(int i = 0, j = m_entries.GetCount(), k = 0; i < j; i++)
3041 STSEntry& stse = m_entries.GetAt(i);
3043 int t1 = TranslateStart(i, fps);
3044 if(t1 < 0) {k++; continue;}
3046 int t2 = TranslateEnd(i, fps);
3048 int hh1 = (t1/60/60/1000);
3049 int mm1 = (t1/60/1000)%60;
3050 int ss1 = (t1/1000)%60;
3051 int ms1 = (t1)%1000;
3052 int hh2 = (t2/60/60/1000);
3053 int mm2 = (t2/60/1000)%60;
3054 int ss2 = (t2/1000)%60;
3055 int ms2 = (t2)%1000;
3057 CStringW str = f.IsUnicode()
3058 ? GetStrW(i, et == EXTSSA || et == EXTASS)
3059 : GetStrWA(i, et == EXTSSA || et == EXTASS);
3061 CStringW str2;
3063 if(et == EXTSRT)
3065 str2.Format(fmt, i-k+1, hh1, mm1, ss1, ms1, hh2, mm2, ss2, ms2, str);
3067 else if(et == EXTSUB)
3069 str.Replace('\n', '|');
3070 str2.Format(fmt, int(t1*fps/1000), int(t2*fps/1000), str);
3072 else if(et == EXTSMI)
3074 str.Replace(L"\n", L"<br>");
3075 str2.Format(fmt, t1, str, t2);
3077 else if(et == EXTPSB)
3079 str.Replace('\n', '|');
3080 str2.Format(fmt, hh1, mm1, ss1, hh2, mm2, ss2, str);
3082 else if(et == EXTSSA)
3084 str.Replace(L"\n", L"\\N");
3085 str2.Format(fmt,
3086 hh1, mm1, ss1, ms1/10,
3087 hh2, mm2, ss2, ms2/10,
3088 TToW(stse.style), TToW(stse.actor),
3089 stse.marginRect.left, stse.marginRect.right, (stse.marginRect.top + stse.marginRect.bottom) / 2,
3090 TToW(stse.effect), str);
3092 else if(et == EXTASS)
3094 str.Replace(L"\n", L"\\N");
3095 str2.Format(fmt,
3096 stse.layer,
3097 hh1, mm1, ss1, ms1/10,
3098 hh2, mm2, ss2, ms2/10,
3099 TToW(stse.style), TToW(stse.actor),
3100 stse.marginRect.left, stse.marginRect.right, (stse.marginRect.top + stse.marginRect.bottom) / 2,
3101 TToW(stse.effect), str);
3104 f.WriteString(str2);
3107 // Sort();
3109 if(et == EXTSMI)
3111 f.WriteString(_T("</BODY>\n</SAMI>\n"));
3114 STSStyle* s;
3115 if(!m_fUsingAutoGeneratedDefaultStyle && m_styles.Lookup(g_default_style, s) && et != EXTSSA && et != EXTASS)
3117 CTextFile f;
3118 if(!f.Save(fn + _T(".style"), e))
3119 return(false);
3121 CString str, str2;
3123 str += _T("ScriptType: v4.00+\n");
3124 str += _T("PlayResX: %d\n");
3125 str += _T("PlayResY: %d\n");
3126 str += _T("\n");
3127 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");
3128 str2.Format(str, m_dstScreenSize.cx, m_dstScreenSize.cy);
3129 f.WriteString(str2);
3131 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");
3132 str2.Format(str,
3133 s->fontName, (int)s->fontSize,
3134 (s->colors[0]&0xffffff) | (s->alpha[0]<<24),
3135 (s->colors[1]&0xffffff) | (s->alpha[1]<<24),
3136 (s->colors[2]&0xffffff) | (s->alpha[2]<<24),
3137 (s->colors[3]&0xffffff) | (s->alpha[3]<<24),
3138 s->fontWeight > FW_NORMAL ? -1 : 0,
3139 s->fItalic ? -1 : 0, s->fUnderline ? -1 : 0, s->fStrikeOut ? -1 : 0,
3140 (int)s->fontScaleX, (int)s->fontScaleY,
3141 (int)s->fontSpacing, (float)s->fontAngleZ,
3142 s->borderStyle == 0 ? 1 : s->borderStyle == 1 ? 3 : 0,
3143 (int)s->outlineWidthY, (int)s->shadowDepthY,
3144 s->scrAlignment,
3145 s->marginRect.get().left, s->marginRect.get().right, (s->marginRect.get().top + s->marginRect.get().bottom) / 2,
3146 s->charSet);
3147 f.WriteString(str2);
3150 return(true);
3153 bool CSimpleTextSubtitle::IsEmpty()
3155 return m_entries.IsEmpty();
3158 void CSimpleTextSubtitle::RemoveAllEntries()
3160 m_entries.RemoveAll();
3163 ////////////////////////////////////////////////////////////////////
3165 bool STSStyleBase::operator==( const STSStyleBase& s ) const
3167 return charSet == s.charSet
3168 && fontName == s.fontName
3169 && fontSize == s.fontSize
3170 && fontWeight == s.fontWeight
3171 && fItalic == s.fItalic
3172 && fUnderline == s.fUnderline
3173 && fStrikeOut == s.fStrikeOut;
3176 STSStyle::STSStyle()
3178 SetDefault();
3181 void STSStyle::SetDefault()
3183 marginRect = CRect(20, 20, 20, 20);
3184 scrAlignment = 2;
3185 borderStyle = 0;
3186 outlineWidthX = outlineWidthY = 2;
3187 shadowDepthX = shadowDepthY = 3;
3188 colors[0] = 0x00ffffff;
3189 colors[1] = 0x0000ffff;
3190 colors[2] = 0x00000000;
3191 colors[3] = 0x00000000;
3192 alpha[0] = 0x00;
3193 alpha[1] = 0x00;
3194 alpha[2] = 0x00;
3195 alpha[3] = 0x80;
3196 charSet = DEFAULT_CHARSET;
3197 fontName = _T("Arial");
3198 fontSize = 18;
3199 fontScaleX = fontScaleY = 100;
3200 fontSpacing = 0;
3201 fontWeight = FW_BOLD;
3202 fItalic = false;
3203 fUnderline = false;
3204 fStrikeOut = false;
3205 fBlur = 0;
3206 fGaussianBlur = 0;
3207 fontShiftX = fontShiftY = fontAngleZ = fontAngleX = fontAngleY = 0;
3208 relativeTo = 2;
3211 bool STSStyle::operator == (const STSStyle& s)const
3213 return(marginRect == s.marginRect
3214 && scrAlignment == s.scrAlignment
3215 && borderStyle == s.borderStyle
3216 && outlineWidthX == s.outlineWidthX
3217 && outlineWidthY == s.outlineWidthY
3218 && shadowDepthX == s.shadowDepthX
3219 && shadowDepthY == s.shadowDepthY
3220 && *((int*)&colors[0]) == *((int*)&s.colors[0])
3221 && *((int*)&colors[1]) == *((int*)&s.colors[1])
3222 && *((int*)&colors[2]) == *((int*)&s.colors[2])
3223 && *((int*)&colors[3]) == *((int*)&s.colors[3])
3224 && alpha[0] == s.alpha[0]
3225 && alpha[1] == s.alpha[1]
3226 && alpha[2] == s.alpha[2]
3227 && alpha[3] == s.alpha[3]
3228 && fBlur == s.fBlur
3229 && fGaussianBlur == s.fGaussianBlur
3230 && relativeTo == s.relativeTo
3231 && IsFontStyleEqual(s));
3234 bool STSStyle::IsFontStyleEqual(const STSStyle& s) const
3236 return(
3237 charSet == s.charSet
3238 && fontName == s.fontName
3239 && fontSize == s.fontSize
3240 && fontScaleX == s.fontScaleX
3241 && fontScaleY == s.fontScaleY
3242 && fontSpacing == s.fontSpacing
3243 && fontWeight == s.fontWeight
3244 && fItalic == s.fItalic
3245 && fUnderline == s.fUnderline
3246 && fStrikeOut == s.fStrikeOut
3247 && fontAngleZ == s.fontAngleZ
3248 && fontAngleX == s.fontAngleX
3249 && fontAngleY == s.fontAngleY
3250 && fontShiftX == s.fontShiftX
3251 && fontShiftY == s.fontShiftY);
3254 void STSStyle::operator = (const LOGFONT& lf)
3256 charSet = lf.lfCharSet;
3257 fontName = lf.lfFaceName;
3258 HDC hDC = GetDC(0);
3259 fontSize = -MulDiv(lf.lfHeight, 72, GetDeviceCaps(hDC, LOGPIXELSY));
3260 ReleaseDC(0, hDC);
3261 // fontAngleZ = (float)(1.0*lf.lfEscapement/10);
3262 fontWeight = lf.lfWeight;
3263 fItalic = !!lf.lfItalic;
3264 fUnderline = !!lf.lfUnderline;
3265 fStrikeOut = !!lf.lfStrikeOut;
3268 LOGFONTA& operator <<= (LOGFONTA& lfa, const STSStyleBase& s)
3270 lfa.lfCharSet = s.charSet;
3271 strncpy_s(lfa.lfFaceName, LF_FACESIZE, CStringA(s.fontName), _TRUNCATE);
3272 HDC hDC = GetDC(0);
3273 lfa.lfHeight = -MulDiv((int)(s.fontSize+0.5), GetDeviceCaps(hDC, LOGPIXELSY), 72);
3274 ReleaseDC(0, hDC);
3275 lfa.lfWeight = s.fontWeight;
3276 lfa.lfItalic = s.fItalic?-1:0;
3277 lfa.lfUnderline = s.fUnderline?-1:0;
3278 lfa.lfStrikeOut = s.fStrikeOut?-1:0;
3279 return(lfa);
3282 LOGFONTW& operator <<= (LOGFONTW& lfw, const STSStyleBase& s)
3284 lfw.lfCharSet = s.charSet;
3285 wcsncpy_s(lfw.lfFaceName, LF_FACESIZE, CStringW(s.fontName), _TRUNCATE);
3286 HDC hDC = GetDC(0);
3287 lfw.lfHeight = -MulDiv((int)(s.fontSize+0.5), GetDeviceCaps(hDC, LOGPIXELSY), 72);
3288 ReleaseDC(0, hDC);
3289 lfw.lfWeight = s.fontWeight;
3290 lfw.lfItalic = s.fItalic?-1:0;
3291 lfw.lfUnderline = s.fUnderline?-1:0;
3292 lfw.lfStrikeOut = s.fStrikeOut?-1:0;
3293 return(lfw);
3296 CString& operator <<= (CString& style, const STSStyle& s)
3298 style.Format(_T("%d;%d;%d;%d;")
3299 _T("%d;%d;%f;%f;%f;%f;")
3300 _T("0x%06x;0x%06x;0x%06x;0x%06x;")
3301 _T("0x%02x;0x%02x;0x%02x;0x%02x;")
3302 _T("%d;")
3303 _T("%s;%f;%f;%f;%f;%d;")
3304 _T("%d;%d;%d;%d;%f;")
3305 _T("%f;%f;%f;")
3306 _T("%d"),
3307 s.marginRect.get().left, s.marginRect.get().right, s.marginRect.get().top, s.marginRect.get().bottom,
3308 s.scrAlignment, s.borderStyle,s.outlineWidthX, s.outlineWidthY, s.shadowDepthX, s.shadowDepthY,
3309 s.colors[0], s.colors[1], s.colors[2], s.colors[3],
3310 s.alpha[0], s.alpha[1], s.alpha[2], s.alpha[3],
3311 s.charSet,
3312 s.fontName,s.fontSize,s.fontScaleX, s.fontScaleY,s.fontSpacing,s.fontWeight,
3313 (int)s.fItalic, (int)s.fUnderline, (int)s.fStrikeOut, s.fBlur, s.fGaussianBlur,
3314 s.fontAngleZ, s.fontAngleX, s.fontAngleY,
3315 s.relativeTo);
3317 return(style);
3320 STSStyle& operator <<= (STSStyle& s, const CString& style)
3322 s.SetDefault();
3326 CStringW str = TToW(style);
3327 if(str.Find(';')>=0)
3329 CRect tmp_rect;
3330 tmp_rect.left = GetInt(str,';'); tmp_rect.right = GetInt(str,';'); tmp_rect.top = GetInt(str,';'); tmp_rect.bottom = GetInt(str,';');
3331 s.marginRect = tmp_rect;
3332 s.scrAlignment = GetInt(str,';'); s.borderStyle = GetInt(str,';');
3333 s.outlineWidthX = GetFloat(str,';'); s.outlineWidthY = GetFloat(str,';'); s.shadowDepthX = GetFloat(str,';'); s.shadowDepthY = GetFloat(str,';');
3334 for(int i = 0; i < 4; i++) s.colors[i] = (COLORREF)GetInt(str,';');
3335 for(int i = 0; i < 4; i++) s.alpha[i] = GetInt(str,';');
3336 s.charSet = GetInt(str,';');
3337 s.fontName = WToT(GetStr(str,';')); s.fontSize = GetFloat(str,';');
3338 s.fontScaleX = GetFloat(str,';'); s.fontScaleY = GetFloat(str,';');
3339 s.fontSpacing = GetFloat(str,';'); s.fontWeight = GetInt(str,';');
3340 s.fItalic = !!GetInt(str,';'); s.fUnderline = !!GetInt(str,';'); s.fStrikeOut = !!GetInt(str,';'); s.fBlur = GetInt(str,';'); s.fGaussianBlur = GetFloat(str,';');
3341 s.fontAngleZ = GetFloat(str,';'); s.fontAngleX = GetFloat(str,';'); s.fontAngleY = GetFloat(str,';');
3342 s.relativeTo = GetInt(str,';');
3345 catch(...)
3347 s.SetDefault();
3350 return(s);
3353 static bool OpenRealText(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet)
3355 wstring szFile;
3357 CStringW buff;
3358 while(file->ReadString(buff))
3360 buff.Trim();
3361 if(buff.IsEmpty()) continue;
3363 szFile += CStringW(_T("\n")) + buff.GetBuffer();
3366 CRealTextParser RealTextParser;
3367 if (!RealTextParser.ParseRealText(szFile))
3368 return false;
3370 CRealTextParser::Subtitles crRealText = RealTextParser.GetParsedSubtitles();
3372 for (map<pair<int, int>, wstring>::const_iterator i = crRealText.m_mapLines.begin();
3373 i != crRealText.m_mapLines.end();
3374 ++i)
3376 ret.Add(
3377 SubRipper2SSA(i->second.c_str(), CharSet),
3378 file->IsUnicode(),
3379 i->first.first,
3380 i->first.second);
3383 // std::wofstream wofsOut(L"c:/zzz.srt");
3384 // RealTextParser.OutputSRT(wofsOut);
3386 return(!ret.IsEmpty());