Roll back some changes to fix a codepage issue.
[xy_vsfilter.git] / src / subtitles / STS.cpp
blobd31b580f734ffb1c7d27be40769a9164467d421a
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 == _T("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 == _T("dialogue"))
1587 // {
1588 // try
1589 // {
1590 // CStringW::PXSTR __buff = buff.GetBuffer();
1591 // int hh1, mm1, ss1, ms1_div10, hh2, mm2, ss2, ms2_div10, layer = 0;
1592 // CString Style, Actor, Effect;
1593 // CRect marginRect;
1595 //if(version <= 4){NextStr(&__buff, '='); NextInt(&__buff);} /* Marked = */
1596 //if(version >= 5)layer = NextInt(&__buff);
1597 // hh1 = NextInt(&__buff, ':');
1598 // mm1 = NextInt(&__buff, ':');
1599 // ss1 = NextInt(&__buff, '.');
1600 // ms1_div10 = NextInt(&__buff);
1601 // hh2 = NextInt(&__buff, ':');
1602 // mm2 = NextInt(&__buff, ':');
1603 // ss2 = NextInt(&__buff, '.');
1604 // ms2_div10 = NextInt(&__buff);
1605 // Style = WToT(NextStr(&__buff));
1606 // Actor = WToT(NextStr(&__buff));
1607 // marginRect.left = NextInt(&__buff);
1608 // marginRect.right = NextInt(&__buff);
1609 // marginRect.top = marginRect.bottom = NextInt(&__buff);
1610 //if(version >= 6)marginRect.bottom = NextInt(&__buff);
1611 // Effect = WToT(NextStr(&__buff));
1613 // int len = min(Effect.GetLength(), buff.GetLength());
1614 // if(Effect.Left(len) == WToT(buff.Left(len))) Effect.Empty();
1616 // Style.TrimLeft('*');
1617 // if(!Style.CompareNoCase(_T("Default"))) Style = _T("Default");
1619 // ret.AddSTSEntryOnly(__buff,
1620 // file->IsUnicode(),
1621 // (((hh1*60 + mm1)*60) + ss1)*1000 + ms1_div10*10,
1622 // (((hh2*60 + mm2)*60) + ss2)*1000 + ms2_div10*10,
1623 // Style, Actor, Effect,
1624 // marginRect,
1625 // layer);
1626 // }
1627 // catch(...)
1628 // {
1629 //// ASSERT(0);
1630 //// throw;
1631 // return(false);
1632 // }
1633 // }
1634 else if(entry == L"fontname")
1636 LoadUUEFont(file);
1639 // ret.Sort();
1640 return(fRet);
1643 static bool OpenXombieSub(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet)
1645 float version = 0;
1647 // CMapStringToPtr stylemap;
1649 CStringW buff;
1650 while(file->ReadString(buff))
1652 buff.Trim();
1653 if(buff.IsEmpty() || buff.GetAt(0) == ';') continue;
1655 CStringW entry;
1657 // try {
1658 entry = GetStr(buff, '=');
1659 // }
1660 // catch(...) {continue;}
1662 entry.MakeLower();
1664 if(entry == L"version")
1666 version = (float)GetFloat(buff);
1668 else if(entry == L"screenhorizontal")
1670 try {ret.m_dstScreenSize.cx = GetInt(buff);}
1671 catch(...) {ret.m_dstScreenSize = CSize(0, 0); return(false);}
1673 if(ret.m_dstScreenSize.cy <= 0)
1675 ret.m_dstScreenSize.cy = (ret.m_dstScreenSize.cx == 1280)
1676 ? 1024
1677 : ret.m_dstScreenSize.cx * 3 / 4;
1680 else if(entry == L"screenvertical")
1682 try {ret.m_dstScreenSize.cy = GetInt(buff);}
1683 catch(...) {ret.m_dstScreenSize = CSize(0, 0); return(false);}
1685 if(ret.m_dstScreenSize.cx <= 0)
1687 ret.m_dstScreenSize.cx = (ret.m_dstScreenSize.cy == 1024)
1688 ? 1280
1689 : ret.m_dstScreenSize.cy * 4 / 3;
1692 else if(entry == L"style")
1694 STSStyle* style = new STSStyle;
1695 if(!style) return(false);
1699 CString StyleName;
1700 CRect tmp_rect;
1702 StyleName = WToT(GetStr(buff)) + _T("_") + WToT(GetStr(buff));
1703 style->fontName = WToT(GetStr(buff));
1704 style->fontSize = GetFloat(buff);
1705 for(int i = 0; i < 4; i++) style->colors[i] = (COLORREF)GetInt(buff);
1706 for(int i = 0; i < 4; i++) style->alpha[i] = GetInt(buff);
1707 style->fontWeight = !!GetInt(buff) ? FW_BOLD : FW_NORMAL;
1708 style->fItalic = !!GetInt(buff);
1709 style->fUnderline = !!GetInt(buff);
1710 style->fStrikeOut = !!GetInt(buff);
1711 style->fBlur = !!GetInt(buff);
1712 style->fontScaleX = GetFloat(buff);
1713 style->fontScaleY = GetFloat(buff);
1714 style->fontSpacing = GetFloat(buff);
1715 style->fontAngleX = GetFloat(buff);
1716 style->fontAngleY = GetFloat(buff);
1717 style->fontAngleZ = GetFloat(buff);
1718 style->borderStyle = GetInt(buff);
1719 style->outlineWidthX = style->outlineWidthY = GetFloat(buff);
1720 style->shadowDepthX = style->shadowDepthY = GetFloat(buff);
1721 style->scrAlignment = GetInt(buff);
1723 tmp_rect.left = GetInt(buff);
1724 tmp_rect.right = GetInt(buff);
1725 tmp_rect.top = tmp_rect.bottom = GetInt(buff);
1726 style->marginRect = tmp_rect;
1728 style->charSet = GetInt(buff);
1730 style->fontScaleX = max(style->fontScaleX, 0);
1731 style->fontScaleY = max(style->fontScaleY, 0);
1732 style->fontSpacing = max(style->fontSpacing, 0);
1733 style->borderStyle = style->borderStyle == 1 ? 0 : style->borderStyle == 3 ? 1 : 0;
1734 style->outlineWidthX = max(style->outlineWidthX, 0);
1735 style->outlineWidthY = max(style->outlineWidthY, 0);
1736 style->shadowDepthX = max(style->shadowDepthX, 0);
1737 style->shadowDepthY = max(style->shadowDepthY, 0);
1739 ret.AddStyle(StyleName, style);
1741 catch(...)
1743 delete style;
1744 return(false);
1747 else if(entry == L"line")
1751 CString id;
1752 int hh1, mm1, ss1, ms1, hh2, mm2, ss2, ms2, layer = 0;
1753 CString Style, Actor;
1754 CRect marginRect;
1756 if(GetStr(buff) != L"D") continue;
1757 id = GetStr(buff);
1758 layer = GetInt(buff);
1759 hh1 = GetInt(buff, ':');
1760 mm1 = GetInt(buff, ':');
1761 ss1 = GetInt(buff, '.');
1762 ms1 = GetInt(buff);
1763 hh2 = GetInt(buff, ':');
1764 mm2 = GetInt(buff, ':');
1765 ss2 = GetInt(buff, '.');
1766 ms2 = GetInt(buff);
1767 Style = WToT(GetStr(buff)) + _T("_") + WToT(GetStr(buff));
1768 Actor = WToT(GetStr(buff));
1769 marginRect.left = GetInt(buff);
1770 marginRect.right = GetInt(buff);
1771 marginRect.top = marginRect.bottom = GetInt(buff);
1773 Style.TrimLeft('*');
1774 if(!Style.CompareNoCase(_T("Default"))) Style = _T("Default");
1776 ret.Add(buff,
1777 file->IsUnicode(),
1778 (((hh1*60 + mm1)*60) + ss1)*1000 + ms1,
1779 (((hh2*60 + mm2)*60) + ss2)*1000 + ms2,
1780 Style, Actor, _T(""),
1781 marginRect,
1782 layer);
1784 catch(...)
1786 return(false);
1789 else if(entry == L"fontname")
1791 LoadUUEFont(file);
1795 return(!ret.IsEmpty());
1798 #include "USFSubtitles.h"
1800 static bool OpenUSF(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet)
1802 CString str;
1803 while(file->ReadString(str))
1805 if(str.Find(_T("USFSubtitles")) >= 0)
1807 CUSFSubtitles usf;
1808 if(usf.Read(file->GetFilePath()) && usf.ConvertToSTS(ret))
1809 return(true);
1811 break;
1815 return(false);
1818 static CStringW MPL22SSA(CStringW str)
1820 CAtlList<CStringW> sl;
1821 Explode(str, sl, '|');
1822 POSITION pos = sl.GetHeadPosition();
1823 while(pos)
1825 CStringW& s = sl.GetNext(pos);
1826 if(s[0] == '/') {s = L"{\\i1}" + s.Mid(1) + L"{\\i0}";}
1828 str = Implode(sl, '\n');
1829 str.Replace(L"\n", L"\\N");
1830 return str;
1833 static bool OpenMPL2(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet)
1835 CStringW buff;;
1836 while(file->ReadString(buff))
1838 buff.Trim();
1839 if(buff.IsEmpty()) continue;
1841 int start, end;
1842 int c = swscanf(buff, L"[%d][%d]", &start, &end);
1844 if(c == 2)
1846 ret.Add(
1847 MPL22SSA(buff.Mid(buff.Find(']', buff.Find(']')+1)+1)),
1848 file->IsUnicode(),
1849 start*100, end*100);
1851 else if(c != EOF) // might be another format
1853 return(false);
1857 return(!ret.IsEmpty());
1860 typedef bool (*STSOpenFunct)(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet);
1862 static bool OpenRealText(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet);
1864 typedef struct {STSOpenFunct open; tmode mode;} OpenFunctStruct;
1866 static OpenFunctStruct OpenFuncts[] =
1868 OpenSubStationAlpha, TIME,
1869 OpenSubRipper, TIME,
1870 OpenOldSubRipper, TIME,
1871 OpenSubViewer, TIME,
1872 OpenMicroDVD, FRAME,
1873 OpenSami, TIME,
1874 OpenVPlayer, TIME,
1875 OpenXombieSub, TIME,
1876 OpenUSF, TIME,
1877 OpenMPL2, TIME,
1878 OpenRealText, TIME,
1881 static int nOpenFuncts = countof(OpenFuncts);
1885 CSimpleTextSubtitle::CSimpleTextSubtitle()
1887 m_mode = TIME;
1888 m_dstScreenSize = CSize(0, 0);
1889 m_defaultWrapStyle = 0;
1890 m_collisions = 0;
1891 m_fScaledBAS = false;
1892 m_encoding = CTextFile::ASCII;
1893 m_ePARCompensationType = EPCTDisabled;
1894 m_dPARCompensation = 1.0;
1897 CSimpleTextSubtitle::~CSimpleTextSubtitle()
1899 Empty();
1902 CSimpleTextSubtitle::CSimpleTextSubtitle(CSimpleTextSubtitle& sts)
1904 *this = sts;
1907 CSimpleTextSubtitle& CSimpleTextSubtitle::operator = (CSimpleTextSubtitle& sts)
1909 Empty();
1911 m_name = sts.m_name;
1912 m_mode = sts.m_mode;
1913 m_dstScreenSize = sts.m_dstScreenSize;
1914 m_defaultWrapStyle = sts.m_defaultWrapStyle;
1915 m_collisions = sts.m_collisions;
1916 m_fScaledBAS = sts.m_fScaledBAS;
1917 m_fSSA = sts.m_fSSA;
1918 m_fUsingAutoGeneratedDefaultStyle = sts.m_fUsingAutoGeneratedDefaultStyle;
1919 CopyStyles(sts.m_styles);
1920 m_segments.Copy(sts.m_segments);
1921 Copy(sts);
1923 return(*this);
1927 void CSimpleTextSubtitle::Copy(CSimpleTextSubtitle& sts)
1929 Empty();
1931 m_name = sts.m_name;
1932 m_mode = sts.m_mode;
1933 m_dstScreenSize = sts.m_dstScreenSize;
1934 m_defaultWrapStyle = sts.m_defaultWrapStyle;
1935 m_collisions = sts.m_collisions;
1936 m_fScaledBAS = sts.m_fScaledBAS;
1937 m_encoding = sts.m_encoding;
1938 m_fUsingAutoGeneratedDefaultStyle = sts.m_fUsingAutoGeneratedDefaultStyle;
1939 CopyStyles(sts.m_styles);
1940 m_segments.Copy(sts.m_segments);
1941 m_entries.Copy(sts.m_entries);
1944 void CSimpleTextSubtitle::Append(CSimpleTextSubtitle& sts, int timeoff)
1946 if(timeoff < 0)
1948 timeoff = m_entries.GetCount() > 0 ? m_entries.GetAt(m_entries.GetCount()-1).end : 0;
1951 for(int i = 0, j = m_entries.GetCount(); i < j; i++)
1953 if(m_entries.GetAt(i).start > timeoff)
1955 m_entries.RemoveAt(i, j - i);
1956 break;
1960 CopyStyles(sts.m_styles, true);
1962 for(int i = 0, j = sts.m_entries.GetCount(); i < j; i++)
1964 STSEntry stse = sts.m_entries.GetAt(i);
1965 stse.start += timeoff;
1966 stse.end += timeoff;
1967 stse.readorder += m_entries.GetCount();
1968 m_entries.Add(stse);
1971 CreateSegments();
1974 void CSTSStyleMap::Free()
1976 POSITION pos = GetStartPosition();
1977 while(pos)
1979 CString key;
1980 STSStyle* val;
1981 GetNextAssoc(pos, key, val);
1982 delete val;
1985 RemoveAll();
1988 bool CSimpleTextSubtitle::CopyStyles(const CSTSStyleMap& styles, bool fAppend)
1990 if(!fAppend) m_styles.Free();
1992 POSITION pos = styles.GetStartPosition();
1993 while(pos)
1995 CString key;
1996 STSStyle* val;
1997 styles.GetNextAssoc(pos, key, val);
1999 STSStyle* s = new STSStyle;
2000 if(!s) return(false);
2002 *s = *val;
2004 AddStyle(key, s);
2007 return(true);
2010 void CSimpleTextSubtitle::Empty()
2012 m_dstScreenSize = CSize(0, 0);
2013 m_styles.Free();
2014 m_segments.RemoveAll();
2015 m_entries.RemoveAll();
2018 void CSimpleTextSubtitle::Add(CStringW str, bool fUnicode, int start, int end,
2019 CString style, const CString& actor, const CString& effect, const CRect& marginRect, int layer, int readorder)
2021 XY_LOG_INFO(start<<_T(" ")<<end<<_T(" ")<<str.GetString()<<_T(" style:")<<style.GetString()
2022 <<_T(" Unicode:")<<fUnicode
2023 <<_T(" actor:")<<actor.GetString()<<_T(" effect:")<<effect.GetString()
2024 <<_T(" (l:")<<marginRect.left<<_T(",t:")<<marginRect.top<<_T(",r:")<<marginRect.right<<_T(",b:")<<marginRect.bottom
2025 <<_T(" layer:")<<layer<<_T(" readorder:")<<readorder
2026 <<_T(" entries:")<<m_entries.GetCount()<<_T(" seg:")<<m_segments.GetCount());
2028 if(start > end || str.Trim().IsEmpty() ) return;
2030 str.Remove('\r');
2031 str.Replace(L"\n", L"\\N");
2032 if(style.IsEmpty()) style = g_default_style;
2033 else if(style!=g_default_style)
2035 style.TrimLeft('*');
2038 STSEntry sub;
2039 sub.str = str;
2040 sub.fUnicode = fUnicode;
2041 sub.style = style;
2042 sub.actor = actor;
2043 sub.effect = effect;
2044 sub.marginRect = marginRect;
2045 sub.layer = layer;
2046 sub.start = start;
2047 sub.end = end;
2048 sub.readorder = readorder < 0 ? m_entries.GetCount() : readorder;
2049 int n = m_entries.Add(sub);
2051 int len = m_segments.GetCount();
2053 if(len == 0)
2055 STSSegment stss(start, end);
2056 stss.subs.Add(n);
2057 m_segments.Add(stss);
2059 else if(end <= m_segments[0].start)
2061 STSSegment stss(start, end);
2062 stss.subs.Add(n);
2063 m_segments.InsertAt(0, stss);
2065 else if(start >= m_segments[len-1].end)
2067 STSSegment stss(start, end);
2068 stss.subs.Add(n);
2069 m_segments.Add(stss);
2071 else
2073 if(start < m_segments[0].start)
2075 STSSegment stss(start, m_segments[0].start);
2076 stss.subs.Add(n);
2077 start = m_segments[0].start;
2078 m_segments.InsertAt(0, stss);
2081 for(size_t i = 0; i < m_segments.GetCount(); i++)
2083 STSSegment& s = m_segments[i];
2085 if(start >= s.end)
2087 continue;
2089 else if(end <= s.start)
2091 break;
2093 else if(s.start < start && start < s.end)
2095 STSSegment stss(s.start, start);
2096 stss.subs.Copy(s.subs);
2097 s.start = start;
2098 m_segments.InsertAt(i, stss);
2099 continue;
2101 if(start <= s.start && s.end <= end)
2103 for(int j = 0, k = s.subs.GetCount(); j <= k; j++)
2105 if(j == k || sub.readorder < m_entries.GetAt(s.subs[j]).readorder)
2107 s.subs.InsertAt(j, n);
2108 break;
2112 else if(s.start < end && end < s.end)
2114 STSSegment stss(s.start, end);
2115 stss.subs.Copy(s.subs);
2116 for(int j = 0, k = s.subs.GetCount(); j <= k; j++)
2118 if(j == k || sub.readorder < m_entries.GetAt(stss.subs[j]).readorder)
2120 stss.subs.InsertAt(j, n);
2121 break;
2124 s.start = end;
2125 m_segments.InsertAt(i, stss);
2129 if(end > m_segments[m_segments.GetCount()-1].end)
2131 STSSegment stss(m_segments[m_segments.GetCount()-1].end, end);
2132 stss.subs.Add(n);
2133 m_segments.Add(stss);
2137 str.Remove('\r');
2138 str.Replace(L"\n", L"\\N");
2139 if(style.IsEmpty()) style = _T("Default");
2141 int j = m_segments.GetCount();
2142 for(int i = j-1; i >= 0; i--)
2144 if(m_segments[i].end <= start)
2146 break;
2148 else if(m_segments[i].start >= start)
2150 m_segments.SetCount(m_segments.GetCount()-1);
2151 j--;
2153 else if(m_segments[i].end > start)
2155 if(i < j-1) m_segments.RemoveAt(i+1, j-i-1);
2156 m_segments[i].end = start;
2157 break;
2161 if(m_segments.GetCount() == 0 && j > 0)
2162 CSTSArray::RemoveAll();
2164 STSSegment stss(start, end);
2165 int len = m_entries.GetCount();
2166 stss.subs.Add(len);
2167 m_segments.Add(stss);
2169 STSEntry sub;
2170 sub.str = str;
2171 sub.fUnicode = fUnicode;
2172 sub.style = style;
2173 sub.actor = actor;
2174 sub.effect = effect;
2175 sub.marginRect = marginRect;
2176 sub.layer = layer;
2177 sub.start = start;
2178 sub.end = end;
2179 sub.readorder = m_entries.GetCount();
2180 CSTSArray::Add(sub);
2184 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*/ )
2186 if(str.Trim().IsEmpty() || start > end) return;
2188 str.Remove('\r');
2189 str.Replace(L"\n", L"\\N");
2190 if(style.IsEmpty()) style = _T("Default");
2191 style.TrimLeft('*');
2193 STSEntry sub;
2194 sub.str = str;
2195 sub.fUnicode = fUnicode;
2196 sub.style = style;
2197 sub.actor = actor;
2198 sub.effect = effect;
2199 sub.marginRect = marginRect;
2200 sub.layer = layer;
2201 sub.start = start;
2202 sub.end = end;
2203 sub.readorder = readorder < 0 ? m_entries.GetCount() : readorder;
2204 m_entries.Add(sub);
2205 return;
2208 STSStyle* CSimpleTextSubtitle::CreateDefaultStyle(int CharSet)
2210 STSStyle* ret = NULL;
2212 if(!m_styles.Lookup(g_default_style, ret))
2214 STSStyle* style = new STSStyle();
2215 style->charSet = CharSet;
2216 AddStyle(g_default_style, style);
2217 m_styles.Lookup(g_default_style, ret);
2219 m_fUsingAutoGeneratedDefaultStyle = true;
2221 else
2223 m_fUsingAutoGeneratedDefaultStyle = false;
2226 return ret;
2229 void CSimpleTextSubtitle::ChangeUnknownStylesToDefault()
2231 CAtlMap<CString, STSStyle*, CStringElementTraits<CString> > unknown;
2232 bool fReport = true;
2234 for(size_t i = 0; i < m_entries.GetCount(); i++)
2236 STSEntry& stse = m_entries.GetAt(i);
2238 STSStyle* val;
2239 if(!m_styles.Lookup(stse.style, val))
2241 if(!unknown.Lookup(stse.style, val))
2243 if(fReport)
2245 CString msg;
2246 msg.Format(_T("Unknown style found: \"%s\", changed to \"Default\"!\n\nPress Cancel to ignore further warnings."), stse.style);
2247 if(MessageBox(NULL, msg, _T("Warning"), MB_OKCANCEL|MB_ICONWARNING) != IDOK) fReport = false;
2250 unknown[stse.style] = NULL;
2253 stse.style = g_default_style;
2258 void CSimpleTextSubtitle::AddStyle(CString name, STSStyle* style)
2260 int i, j;
2262 if(name.IsEmpty()) name = g_default_style;
2264 STSStyle* val;
2265 if(m_styles.Lookup(name, val))
2267 if(*val == *style)
2269 delete style;
2270 return;
2272 const CString& name_str = name;
2274 int len = name_str.GetLength();
2276 for(i = len; i > 0 && _istdigit(name_str[i-1]); i--);
2278 int idx = 1;
2280 CString name2 = name_str;
2282 if(i < len && _stscanf(name_str.Right(len-i), _T("%d"), &idx) == 1)
2284 name2 = name_str.Left(i);
2287 idx++;
2289 CString name3;
2290 CString name3_str;
2293 name3_str.Format(_T("%s%d"), name2, idx);
2294 name3 = name3_str;
2295 idx++;
2297 while(m_styles.Lookup(name3));
2299 m_styles.RemoveKey(name);
2300 m_styles[name3] = val;
2302 for(i = 0, j = m_entries.GetCount(); i < j; i++)
2304 STSEntry& stse = m_entries.GetAt(i);
2305 if(stse.style == name) stse.style = name3;
2309 m_styles[name] = style;
2312 bool CSimpleTextSubtitle::SetDefaultStyle(STSStyle& s)
2314 DbgLog((LOG_TRACE, 3, "%s(%d): %s", __FILE__, __LINE__, __FUNCTION__));
2315 DbgLog((LOG_TRACE, 3, "\tm_styles count:%d", m_styles.GetCount()));
2316 STSStyle* val;
2317 if(!m_styles.Lookup(g_default_style, val)) return false;
2318 DbgLog((LOG_TRACE, 3, "\tm_styles Lookup Default succeed"));
2320 #ifdef DEBUG
2321 for(POSITION pos=m_styles.GetStartPosition(); pos!=NULL;)
2323 DbgLog((LOG_TRACE, 3, _T("\tm_styles[%s]"), (LPCTSTR)m_styles.GetNextKey(pos)));
2325 #endif
2327 *val = s;
2328 m_fUsingAutoGeneratedDefaultStyle = false;
2329 return true;
2332 bool CSimpleTextSubtitle::GetDefaultStyle(STSStyle& s)
2334 STSStyle* val;
2335 if(!m_styles.Lookup(g_default_style, val)) return false;
2336 s = *val;
2337 return true;
2340 void CSimpleTextSubtitle::ConvertToTimeBased(double fps)
2342 if(m_mode == TIME) 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 * 1000 / fps + 0.5);
2348 stse.end = int(1.0 * stse.end * 1000 / fps + 0.5);
2351 m_mode = TIME;
2353 CreateSegments();
2356 void CSimpleTextSubtitle::ConvertToFrameBased(double fps)
2358 if(m_mode == FRAME) return;
2360 for(int i = 0, j = m_entries.GetCount(); i < j; i++)
2362 STSEntry& stse = m_entries[i];
2363 stse.start = int(1.0 * stse.start * fps / 1000 + 0.5);
2364 stse.end = int(1.0 * stse.end * fps / 1000 + 0.5);
2367 m_mode = FRAME;
2369 CreateSegments();
2372 int CSimpleTextSubtitle::SearchSub(int t, double fps)
2374 int i = 0, j = m_entries.GetCount() - 1, ret = -1;
2376 if(j >= 0 && t >= TranslateStart(j, fps))
2378 return(j);
2381 while(i < j)
2383 int mid = (i + j) >> 1;
2385 int midt = TranslateStart(mid, fps);
2387 if(t == midt)
2389 while(mid > 0 && t == TranslateStart(mid-1, fps)) mid--;
2390 ret = mid;
2391 break;
2393 else if(t < midt)
2395 ret = -1;
2396 if(j == mid) mid--;
2397 j = mid;
2399 else if(t > midt)
2401 ret = mid;
2402 if(i == mid) mid++;
2403 i = mid;
2407 return(ret);
2410 const STSSegment* CSimpleTextSubtitle::SearchSubs(int t, double fps, /*[out]*/ int* iSegment, int* nSegments)
2412 int segmentsCount = m_segments.GetCount();
2413 int i = 0, j = segmentsCount - 1;
2415 if(nSegments) *nSegments = segmentsCount;
2416 if(segmentsCount<=0)
2418 if(iSegment!=NULL)
2419 *iSegment = 0;
2420 return NULL;
2423 if(t >= TranslateSegmentEnd(j, fps))
2425 i = j;
2426 j++;
2428 if(t < TranslateSegmentEnd(i, fps))
2430 j = i;
2431 i--;
2434 while(i < j-1)
2436 int mid = (i + j) >> 1;
2438 int midt = TranslateSegmentEnd(mid, fps);
2440 if(t < midt)
2441 j=mid;
2442 else
2443 i=mid;
2445 if(iSegment!=NULL)
2446 *iSegment = j;
2447 if(j<segmentsCount)
2449 return &m_segments[j];
2451 return(NULL);
2454 STSSegment* CSimpleTextSubtitle::SearchSubs2(int t, double fps, /*[out]*/ int* iSegment, int* nSegments)
2456 int segmentsCount = m_segments.GetCount();
2457 int i = 0, j = segmentsCount - 1;
2459 if(iSegment) *iSegment = -1;
2460 if(nSegments) *nSegments = segmentsCount;
2462 if(segmentsCount<=0)
2464 if(iSegment) *iSegment = 0;
2465 return NULL;
2468 if(t >= TranslateSegmentEnd(j, fps))
2470 i = j;
2471 j++;
2473 if(t < TranslateSegmentEnd(i, fps))
2475 j = i;
2476 i--;
2479 while(i < j-1)
2481 int mid = (i + j) >> 1;
2483 int midt = TranslateSegmentEnd(mid, fps);
2485 if(t < midt)
2486 j=mid;
2487 else
2488 i=mid;
2490 if(j<segmentsCount && t>=TranslateSegmentStart(j, fps))
2492 if(iSegment) *iSegment = j;
2493 return &m_segments[j];
2495 return(NULL);
2500 int CSimpleTextSubtitle::TranslateStart(int i, double fps)
2502 return(i < 0 || m_entries.GetCount() <= i ? -1 :
2503 m_mode == TIME ? m_entries.GetAt(i).start :
2504 m_mode == FRAME ? (int)(m_entries.GetAt(i).start*1000/fps) :
2508 int CSimpleTextSubtitle::TranslateEnd(int i, double fps)
2510 return(i < 0 || m_entries.GetCount() <= i ? -1 :
2511 m_mode == TIME ? m_entries.GetAt(i).end :
2512 m_mode == FRAME ? (int)(m_entries.GetAt(i).end*1000/fps) :
2516 int CSimpleTextSubtitle::TranslateSegmentStart(int i, double fps)
2518 return(i < 0 || m_segments.GetCount() <= i ? -1 :
2519 m_mode == TIME ? m_segments[i].start :
2520 m_mode == FRAME ? (int)(m_segments[i].start*1000/fps) :
2524 int CSimpleTextSubtitle::TranslateSegmentEnd(int i, double fps)
2526 return(i < 0 || m_segments.GetCount() <= i ? -1 :
2527 m_mode == TIME ? m_segments[i].end :
2528 m_mode == FRAME ? (int)(m_segments[i].end*1000/fps) :
2532 void CSimpleTextSubtitle::TranslateSegmentStartEnd(int i, double fps, /*out*/int& start, /*out*/int& end)
2534 if(i < 0 || m_segments.GetCount() <= i)
2536 start=-1;
2537 end=-1;
2539 else
2541 if(m_mode == TIME)
2543 start = m_segments[i].start;
2544 end = m_segments[i].end;
2546 else //m_mode == FRAME
2548 start = (int)(m_segments[i].start*1000/fps);
2549 end = (int)(m_segments[i].end*1000/fps);
2554 STSStyle* CSimpleTextSubtitle::GetStyle(int i)
2556 STSStyle* style = NULL;
2557 m_styles.Lookup(m_entries.GetAt(i).style, style);
2559 STSStyle* defstyle = NULL;
2560 m_styles.Lookup(g_default_style, defstyle);
2562 if(!style)
2564 style = defstyle;
2567 ASSERT(style);
2569 return style;
2572 bool CSimpleTextSubtitle::GetStyle(int i, STSStyle* const stss)
2574 STSStyle* style = NULL;
2575 m_styles.Lookup(m_entries.GetAt(i).style, style);
2577 STSStyle* defstyle = NULL;
2578 m_styles.Lookup(g_default_style, defstyle);
2580 if(!style)
2582 if(!defstyle)
2584 defstyle = CreateDefaultStyle(DEFAULT_CHARSET);
2587 style = defstyle;
2590 if(!style) {ASSERT(0); return false;}
2592 *stss = *style;
2593 if(stss->relativeTo == 2 && defstyle)
2594 stss->relativeTo = defstyle->relativeTo;
2596 return true;
2599 int CSimpleTextSubtitle::GetCharSet(int i)
2601 STSStyle* style = GetStyle(i);
2602 return style!=NULL ? style->charSet : -1;
2605 bool CSimpleTextSubtitle::IsEntryUnicode(int i)
2607 return(m_entries.GetAt(i).fUnicode);
2610 void CSimpleTextSubtitle::ConvertUnicode(int i, bool fUnicode)
2612 STSEntry& stse = m_entries.GetAt(i);
2614 if(stse.fUnicode ^ fUnicode)
2616 int CharSet = GetCharSet(i);
2618 stse.str = fUnicode
2619 ? MBCSSSAToUnicode(stse.str, CharSet)
2620 : UnicodeSSAToMBCS(stse.str, CharSet);
2622 stse.fUnicode = fUnicode;
2626 CStringA CSimpleTextSubtitle::GetStrA(int i, bool fSSA)
2628 return(WToA(GetStrWA(i, fSSA)));
2631 CStringW CSimpleTextSubtitle::GetStrW(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 = MBCSSSAToUnicode(str, CharSet);
2641 if(!fSSA)
2642 str = RemoveSSATags(str, fUnicode, CharSet);
2644 return(str);
2647 CStringW CSimpleTextSubtitle::GetStrWA(int i, bool fSSA)
2649 bool fUnicode = IsEntryUnicode(i);
2650 int CharSet = GetCharSet(i);
2652 CStringW str = m_entries.GetAt(i).str;
2654 if(fUnicode)
2655 str = UnicodeSSAToMBCS(str, CharSet);
2657 if(!fSSA)
2658 str = RemoveSSATags(str, fUnicode, CharSet);
2660 return(str);
2663 void CSimpleTextSubtitle::SetStr(int i, CStringA str, bool fUnicode)
2665 SetStr(i, AToW(str), false);
2668 void CSimpleTextSubtitle::SetStr(int i, CStringW str, bool fUnicode)
2670 STSEntry& stse = m_entries.GetAt(i);
2672 str.Replace(L"\n", L"\\N");
2674 if(stse.fUnicode && !fUnicode) stse.str = MBCSSSAToUnicode(str, GetCharSet(i));
2675 else if(!stse.fUnicode && fUnicode) stse.str = UnicodeSSAToMBCS(str, GetCharSet(i));
2676 else stse.str = str;
2679 static int comp1(const void* a, const void* b)
2681 int ret = ((STSEntry*)a)->start - ((STSEntry*)b)->start;
2682 if(ret == 0) ret = ((STSEntry*)a)->layer - ((STSEntry*)b)->layer;
2683 if(ret == 0) ret = ((STSEntry*)a)->readorder - ((STSEntry*)b)->readorder;
2684 return(ret);
2687 static int comp2(const void* a, const void* b)
2689 return(((STSEntry*)a)->readorder - ((STSEntry*)b)->readorder);
2692 void CSimpleTextSubtitle::Sort(bool fRestoreReadorder)
2694 qsort(m_entries.GetData(), m_entries.GetCount(), sizeof(STSEntry), !fRestoreReadorder ? comp1 : comp2);
2695 CreateSegments();
2698 static int intcomp(const void* i1, const void* i2)
2700 return(*((int*)i1) - *((int*)i2));
2703 void CSimpleTextSubtitle::CreateSegments()
2705 m_segments.RemoveAll();
2707 if(m_entries.GetCount()>0)
2709 size_t start, mid, end;
2710 CAtlArray<STSSegment> tempSegments;//if add to m_segments directly, then remove empty entities can be a
2711 //complex operation when having large segmentCount and lots of empty entities
2712 std::vector<int> breakpoints(2*m_entries.GetCount());
2713 for(size_t i = 0; i < m_entries.GetCount(); i++)
2715 STSEntry& stse = m_entries.GetAt(i);
2716 breakpoints[2*i]=stse.start;
2717 breakpoints[2*i+1]=stse.end;
2720 std::sort(breakpoints.begin(), breakpoints.end());
2722 int ptr = 1, prev = breakpoints[0];
2723 for(size_t i = breakpoints.size()-1; i > 0; i--, ptr++)
2725 if(breakpoints[ptr] != prev)
2727 tempSegments.Add(STSSegment(prev, breakpoints[ptr]));
2728 prev = breakpoints[ptr];
2732 size_t segmentCount = tempSegments.GetCount();
2734 for(size_t i = 0; i < m_entries.GetCount(); i++)
2736 STSEntry& stse = m_entries.GetAt(i);
2737 start = 0;
2738 end = segmentCount;
2739 while(start<end)
2741 mid = (start+end)>>1;
2742 if(tempSegments[mid].start < stse.start)
2744 start = mid+1;
2746 else
2748 end = mid;
2751 for(; start < tempSegments.GetCount() && tempSegments[start].end <= stse.end; start++)
2752 tempSegments[start].subs.Add(i);
2754 for(size_t i = 0; i < segmentCount; i++)
2755 if(tempSegments[i].subs.GetCount()>0)
2756 m_segments.Add(tempSegments[i]);
2758 OnChanged();
2760 for(i = 0, j = m_segments.GetCount(); i < j; i++)
2762 STSSegment& stss = m_segments[i];
2764 TRACE(_T("%d - %d"), stss.start, stss.end);
2766 for(int k = 0, l = stss.subs.GetCount(); k < l; k++)
2768 TRACE(_T(", %d"), stss.subs[k]);
2771 TRACE(_T("\n"));
2776 bool CSimpleTextSubtitle::Open(CString fn, int CharSet, CString name)
2778 Empty();
2780 CWebTextFile f;
2781 if(!f.Open(fn)) return(false);
2783 fn.Replace('\\', '/');
2784 if(name.IsEmpty())
2786 name = fn.Left(fn.ReverseFind('.'));
2787 name = name.Mid(name.ReverseFind('/')+1);
2788 name = name.Mid(name.ReverseFind('.')+1);
2791 return(Open(&f, CharSet, name));
2794 static int CountLines(CTextFile* f, ULONGLONG from, ULONGLONG to)
2796 int n = 0;
2797 CString s;
2798 f->Seek(from, 0);
2799 while(f->ReadString(s) && f->GetPosition() < to) n++;
2800 return(n);
2803 bool CSimpleTextSubtitle::Open(CTextFile* f, int CharSet, CString name)
2805 Empty();
2807 ULONGLONG pos = f->GetPosition();
2809 for(int i = 0; i < nOpenFuncts; i++)
2811 const TCHAR* func_name[]={
2812 TEXT("OpenSubStationAlpha"),
2813 TEXT("OpenSubRipper"),
2814 TEXT("OpenOldSubRipper"),
2815 TEXT("OpenSubViewer"),
2816 TEXT("OpenMicroDVD"),
2817 TEXT("OpenSami"),
2818 TEXT("OpenVPlayer"),
2819 TEXT("OpenXombieSub"),
2820 TEXT("OpenUSF"),
2821 TEXT("OpenMPL2"),
2822 TEXT("OpenRealText")};
2823 CAutoTiming t(func_name[i],0);
2825 if(!OpenFuncts[i].open(f, *this, CharSet) /*|| !GetCount()*/)
2827 if(m_entries.GetCount() > 0)
2829 int n = CountLines(f, pos, f->GetPosition());
2830 CString s;
2831 s.Format(_T("Syntax error at line %d!\t"), n+1);
2832 AfxMessageBox(s, MB_OK|MB_ICONERROR);
2833 Empty();
2834 break;
2837 f->Seek(pos, 0);
2838 Empty();
2839 continue;
2842 m_name = name;
2843 m_mode = OpenFuncts[i].mode;
2844 m_encoding = f->GetEncoding();
2845 m_path = f->GetFilePath();
2847 CWebTextFile f2;
2848 if(f2.Open(f->GetFilePath() + _T(".style")))
2849 OpenSubStationAlpha(&f2, *this, CharSet);
2851 // Sort();
2852 CreateSegments();
2854 CreateDefaultStyle(CharSet);
2856 ChangeUnknownStylesToDefault();
2858 if(m_dstScreenSize == CSize(0, 0)) m_dstScreenSize = CSize(384, 288);
2860 return(true);
2863 return(false);
2866 bool CSimpleTextSubtitle::Open(BYTE* data, int len, int CharSet, CString name)
2868 TCHAR path[MAX_PATH];
2869 if(!GetTempPath(MAX_PATH, path)) return(false);
2871 TCHAR fn[MAX_PATH];
2872 if(!GetTempFileName(path, _T("vs"), 0, fn)) return(false);
2874 FILE* tmp = _tfopen(fn, _T("wb"));
2875 if(!tmp) return(false);
2877 int i = 0;
2878 for(; i <= (len-1024); i += 1024) fwrite(&data[i], 1024, 1, tmp);
2879 if(len > i) fwrite(&data[i], len - i, 1, tmp);
2881 fclose(tmp);
2883 bool fRet = Open(fn, CharSet, name);
2885 _tremove(fn);
2887 return(fRet);
2890 bool CSimpleTextSubtitle::SaveAs(CString fn, exttype et, double fps, CTextFile::enc e)
2892 if(fn.Mid(fn.ReverseFind('.')+1).CompareNoCase(exttypestr[et]))
2894 if(fn[fn.GetLength()-1] != '.') fn += _T(".");
2895 fn += exttypestr[et];
2898 CTextFile f;
2899 if(!f.Save(fn, e))
2900 return(false);
2902 if(et == EXTSMI)
2904 CString str;
2906 str += _T("<SAMI>\n<HEAD>\n");
2907 str += _T("<STYLE TYPE=\"text/css\">\n");
2908 str += _T("<!--\n");
2909 str += _T("P {margin-left: 16pt; margin-right: 16pt; margin-bottom: 16pt; margin-top: 16pt;\n");
2910 str += _T(" text-align: center; font-size: 18pt; font-family: arial; font-weight: bold; color: #f0f0f0;}\n");
2911 str += _T(".UNKNOWNCC {Name:Unknown; lang:en-US; SAMIType:CC;}\n");
2912 str += _T("-->\n");
2913 str += _T("</STYLE>\n");
2914 str += _T("</HEAD>\n");
2915 str += _T("\n");
2916 str += _T("<BODY>\n");
2918 f.WriteString(str);
2920 else if(et == EXTSSA || et == EXTASS)
2922 CString str;
2924 str = _T("[Script Info]\n");
2925 str += (et == EXTSSA) ? _T("; This is a Sub Station Alpha v4 script.\n") : _T("; This is an Advanced Sub Station Alpha v4+ script.\n");
2926 str += _T("; For Sub Station Alpha info and downloads,\n");
2927 str += _T("; go to http://www.eswat.demon.co.uk/\n");
2928 str += _T("; or email kotus@eswat.demon.co.uk\n");
2929 str += _T("; \n");
2930 if(et == EXTASS)
2932 str += _T("; Advanced Sub Station Alpha script format developed by #Anime-Fansubs@EfNET\n");
2933 str += _T("; http://www.anime-fansubs.org\n");
2934 str += _T("; \n");
2935 str += _T("; For additional info and downloads go to http://gabest.org/\n");
2936 str += _T("; or email gabest@freemail.hu\n");
2937 str += _T("; \n");
2939 str += _T("; Note: This file was saved by Subresync.\n");
2940 str += _T("; \n");
2941 str += (et == EXTSSA) ? _T("ScriptType: v4.00\n") : _T("ScriptType: v4.00+\n");
2942 str += (m_collisions == 0) ? _T("Collisions: Normal\n") : _T("Collisions: Reverse\n");
2943 if(et == EXTASS && m_fScaledBAS) str += _T("ScaledBorderAndShadow: Yes\n");
2944 str += _T("PlayResX: %d\n");
2945 str += _T("PlayResY: %d\n");
2946 str += _T("Timer: 100.0000\n");
2947 str += _T("\n");
2948 str += (et == EXTSSA)
2949 ? _T("[V4 Styles]\nFormat: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, TertiaryColour, BackColour, Bold, Italic, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, AlphaLevel, Encoding\n")
2950 : _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");
2952 CString str2;
2953 str2.Format(str, m_dstScreenSize.cx, m_dstScreenSize.cy);
2954 f.WriteString(str2);
2956 str = (et == EXTSSA)
2957 ? _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")
2958 : _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");
2960 POSITION pos = m_styles.GetStartPosition();
2961 while(pos)
2963 CString key;
2964 STSStyle* s;
2965 m_styles.GetNextAssoc(pos, key, s);
2967 if(et == EXTSSA)
2969 CString str2;
2970 str2.Format(str, key,
2971 s->fontName, (int)s->fontSize,
2972 s->colors[0]&0xffffff,
2973 s->colors[1]&0xffffff,
2974 s->colors[2]&0xffffff,
2975 s->colors[3]&0xffffff,
2976 s->fontWeight > FW_NORMAL ? -1 : 0, s->fItalic ? -1 : 0,
2977 s->borderStyle == 0 ? 1 : s->borderStyle == 1 ? 3 : 0,
2978 (int)s->outlineWidthY, (int)s->shadowDepthY,
2979 s->scrAlignment <= 3 ? s->scrAlignment : s->scrAlignment <= 6 ? ((s->scrAlignment-3)|8) : s->scrAlignment <= 9 ? ((s->scrAlignment-6)|4) : 2,
2980 s->marginRect.get().left, s->marginRect.get().right, (s->marginRect.get().top + s->marginRect.get().bottom) / 2,
2981 s->alpha[0],
2982 s->charSet);
2983 f.WriteString(str2);
2985 else
2987 CString str2;
2988 str2.Format(str, key,
2989 s->fontName, (int)s->fontSize,
2990 (s->colors[0]&0xffffff) | (s->alpha[0]<<24),
2991 (s->colors[1]&0xffffff) | (s->alpha[1]<<24),
2992 (s->colors[2]&0xffffff) | (s->alpha[2]<<24),
2993 (s->colors[3]&0xffffff) | (s->alpha[3]<<24),
2994 s->fontWeight > FW_NORMAL ? -1 : 0,
2995 s->fItalic ? -1 : 0, s->fUnderline ? -1 : 0, s->fStrikeOut ? -1 : 0,
2996 (int)s->fontScaleX, (int)s->fontScaleY,
2997 (int)s->fontSpacing, (float)s->fontAngleZ,
2998 s->borderStyle == 0 ? 1 : s->borderStyle == 1 ? 3 : 0,
2999 (int)s->outlineWidthY, (int)s->shadowDepthY,
3000 s->scrAlignment,
3001 s->marginRect.get().left, s->marginRect.get().right, (s->marginRect.get().top + s->marginRect.get().bottom) / 2,
3002 s->charSet);
3003 f.WriteString(str2);
3007 if(m_entries.GetCount() > 0)
3009 str = _T("\n");
3010 str += _T("[Events]\n");
3011 str += (et == EXTSSA)
3012 ? _T("Format: Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\n")
3013 : _T("Format: Layer, Start, End, Style, Actor, MarginL, MarginR, MarginV, Effect, Text\n");
3014 f.WriteString(str);
3018 CStringW fmt =
3019 et == EXTSRT ? L"%d\n%02d:%02d:%02d,%03d --> %02d:%02d:%02d,%03d\n%s\n\n" :
3020 et == EXTSUB ? L"{%d}{%d}%s\n" :
3021 et == EXTSMI ? L"<SYNC Start=%d><P Class=UNKNOWNCC>\n%s\n<SYNC Start=%d><P Class=UNKNOWNCC>&nbsp;\n" :
3022 et == EXTPSB ? L"{%d:%02d:%02d}{%d:%02d:%02d}%s\n" :
3023 et == EXTSSA ? L"Dialogue: Marked=0,%d:%02d:%02d.%02d,%d:%02d:%02d.%02d,%s,%s,%04d,%04d,%04d,%s,%s\n" :
3024 et == EXTASS ? L"Dialogue: %d,%d:%02d:%02d.%02d,%d:%02d:%02d.%02d,%s,%s,%04d,%04d,%04d,%s,%s\n" :
3025 L"";
3026 // Sort(true);
3028 for(int i = 0, j = m_entries.GetCount(), k = 0; i < j; i++)
3030 STSEntry& stse = m_entries.GetAt(i);
3032 int t1 = TranslateStart(i, fps);
3033 if(t1 < 0) {k++; continue;}
3035 int t2 = TranslateEnd(i, fps);
3037 int hh1 = (t1/60/60/1000);
3038 int mm1 = (t1/60/1000)%60;
3039 int ss1 = (t1/1000)%60;
3040 int ms1 = (t1)%1000;
3041 int hh2 = (t2/60/60/1000);
3042 int mm2 = (t2/60/1000)%60;
3043 int ss2 = (t2/1000)%60;
3044 int ms2 = (t2)%1000;
3046 CStringW str = f.IsUnicode()
3047 ? GetStrW(i, et == EXTSSA || et == EXTASS)
3048 : GetStrWA(i, et == EXTSSA || et == EXTASS);
3050 CStringW str2;
3052 if(et == EXTSRT)
3054 str2.Format(fmt, i-k+1, hh1, mm1, ss1, ms1, hh2, mm2, ss2, ms2, str);
3056 else if(et == EXTSUB)
3058 str.Replace('\n', '|');
3059 str2.Format(fmt, int(t1*fps/1000), int(t2*fps/1000), str);
3061 else if(et == EXTSMI)
3063 str.Replace(L"\n", L"<br>");
3064 str2.Format(fmt, t1, str, t2);
3066 else if(et == EXTPSB)
3068 str.Replace('\n', '|');
3069 str2.Format(fmt, hh1, mm1, ss1, hh2, mm2, ss2, str);
3071 else if(et == EXTSSA)
3073 str.Replace(L"\n", L"\\N");
3074 str2.Format(fmt,
3075 hh1, mm1, ss1, ms1/10,
3076 hh2, mm2, ss2, ms2/10,
3077 TToW(stse.style), TToW(stse.actor),
3078 stse.marginRect.left, stse.marginRect.right, (stse.marginRect.top + stse.marginRect.bottom) / 2,
3079 TToW(stse.effect), str);
3081 else if(et == EXTASS)
3083 str.Replace(L"\n", L"\\N");
3084 str2.Format(fmt,
3085 stse.layer,
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);
3093 f.WriteString(str2);
3096 // Sort();
3098 if(et == EXTSMI)
3100 f.WriteString(_T("</BODY>\n</SAMI>\n"));
3103 STSStyle* s;
3104 if(!m_fUsingAutoGeneratedDefaultStyle && m_styles.Lookup(g_default_style, s) && et != EXTSSA && et != EXTASS)
3106 CTextFile f;
3107 if(!f.Save(fn + _T(".style"), e))
3108 return(false);
3110 CString str, str2;
3112 str += _T("ScriptType: v4.00+\n");
3113 str += _T("PlayResX: %d\n");
3114 str += _T("PlayResY: %d\n");
3115 str += _T("\n");
3116 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");
3117 str2.Format(str, m_dstScreenSize.cx, m_dstScreenSize.cy);
3118 f.WriteString(str2);
3120 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");
3121 str2.Format(str,
3122 s->fontName, (int)s->fontSize,
3123 (s->colors[0]&0xffffff) | (s->alpha[0]<<24),
3124 (s->colors[1]&0xffffff) | (s->alpha[1]<<24),
3125 (s->colors[2]&0xffffff) | (s->alpha[2]<<24),
3126 (s->colors[3]&0xffffff) | (s->alpha[3]<<24),
3127 s->fontWeight > FW_NORMAL ? -1 : 0,
3128 s->fItalic ? -1 : 0, s->fUnderline ? -1 : 0, s->fStrikeOut ? -1 : 0,
3129 (int)s->fontScaleX, (int)s->fontScaleY,
3130 (int)s->fontSpacing, (float)s->fontAngleZ,
3131 s->borderStyle == 0 ? 1 : s->borderStyle == 1 ? 3 : 0,
3132 (int)s->outlineWidthY, (int)s->shadowDepthY,
3133 s->scrAlignment,
3134 s->marginRect.get().left, s->marginRect.get().right, (s->marginRect.get().top + s->marginRect.get().bottom) / 2,
3135 s->charSet);
3136 f.WriteString(str2);
3139 return(true);
3142 bool CSimpleTextSubtitle::IsEmpty()
3144 return m_entries.IsEmpty();
3147 void CSimpleTextSubtitle::RemoveAllEntries()
3149 m_entries.RemoveAll();
3152 ////////////////////////////////////////////////////////////////////
3154 bool STSStyleBase::operator==( const STSStyleBase& s ) const
3156 return charSet == s.charSet
3157 && fontName == s.fontName
3158 && fontSize == s.fontSize
3159 && fontWeight == s.fontWeight
3160 && fItalic == s.fItalic
3161 && fUnderline == s.fUnderline
3162 && fStrikeOut == s.fStrikeOut;
3165 STSStyle::STSStyle()
3167 SetDefault();
3170 void STSStyle::SetDefault()
3172 marginRect = CRect(20, 20, 20, 20);
3173 scrAlignment = 2;
3174 borderStyle = 0;
3175 outlineWidthX = outlineWidthY = 2;
3176 shadowDepthX = shadowDepthY = 3;
3177 colors[0] = 0x00ffffff;
3178 colors[1] = 0x0000ffff;
3179 colors[2] = 0x00000000;
3180 colors[3] = 0x00000000;
3181 alpha[0] = 0x00;
3182 alpha[1] = 0x00;
3183 alpha[2] = 0x00;
3184 alpha[3] = 0x80;
3185 charSet = DEFAULT_CHARSET;
3186 fontName = _T("Arial");
3187 fontSize = 18;
3188 fontScaleX = fontScaleY = 100;
3189 fontSpacing = 0;
3190 fontWeight = FW_BOLD;
3191 fItalic = false;
3192 fUnderline = false;
3193 fStrikeOut = false;
3194 fBlur = 0;
3195 fGaussianBlur = 0;
3196 fontShiftX = fontShiftY = fontAngleZ = fontAngleX = fontAngleY = 0;
3197 relativeTo = 2;
3200 bool STSStyle::operator == (const STSStyle& s)const
3202 return(marginRect == s.marginRect
3203 && scrAlignment == s.scrAlignment
3204 && borderStyle == s.borderStyle
3205 && outlineWidthX == s.outlineWidthX
3206 && outlineWidthY == s.outlineWidthY
3207 && shadowDepthX == s.shadowDepthX
3208 && shadowDepthY == s.shadowDepthY
3209 && *((int*)&colors[0]) == *((int*)&s.colors[0])
3210 && *((int*)&colors[1]) == *((int*)&s.colors[1])
3211 && *((int*)&colors[2]) == *((int*)&s.colors[2])
3212 && *((int*)&colors[3]) == *((int*)&s.colors[3])
3213 && alpha[0] == s.alpha[0]
3214 && alpha[1] == s.alpha[1]
3215 && alpha[2] == s.alpha[2]
3216 && alpha[3] == s.alpha[3]
3217 && fBlur == s.fBlur
3218 && fGaussianBlur == s.fGaussianBlur
3219 && relativeTo == s.relativeTo
3220 && IsFontStyleEqual(s));
3223 bool STSStyle::IsFontStyleEqual(const STSStyle& s) const
3225 return(
3226 charSet == s.charSet
3227 && fontName == s.fontName
3228 && fontSize == s.fontSize
3229 && fontScaleX == s.fontScaleX
3230 && fontScaleY == s.fontScaleY
3231 && fontSpacing == s.fontSpacing
3232 && fontWeight == s.fontWeight
3233 && fItalic == s.fItalic
3234 && fUnderline == s.fUnderline
3235 && fStrikeOut == s.fStrikeOut
3236 && fontAngleZ == s.fontAngleZ
3237 && fontAngleX == s.fontAngleX
3238 && fontAngleY == s.fontAngleY
3239 && fontShiftX == s.fontShiftX
3240 && fontShiftY == s.fontShiftY);
3243 void STSStyle::operator = (const LOGFONT& lf)
3245 charSet = lf.lfCharSet;
3246 fontName = lf.lfFaceName;
3247 HDC hDC = GetDC(0);
3248 fontSize = -MulDiv(lf.lfHeight, 72, GetDeviceCaps(hDC, LOGPIXELSY));
3249 ReleaseDC(0, hDC);
3250 // fontAngleZ = (float)(1.0*lf.lfEscapement/10);
3251 fontWeight = lf.lfWeight;
3252 fItalic = !!lf.lfItalic;
3253 fUnderline = !!lf.lfUnderline;
3254 fStrikeOut = !!lf.lfStrikeOut;
3257 LOGFONTA& operator <<= (LOGFONTA& lfa, const STSStyleBase& s)
3259 lfa.lfCharSet = s.charSet;
3260 strncpy_s(lfa.lfFaceName, LF_FACESIZE, CStringA(s.fontName), _TRUNCATE);
3261 HDC hDC = GetDC(0);
3262 lfa.lfHeight = -MulDiv((int)(s.fontSize+0.5), GetDeviceCaps(hDC, LOGPIXELSY), 72);
3263 ReleaseDC(0, hDC);
3264 lfa.lfWeight = s.fontWeight;
3265 lfa.lfItalic = s.fItalic?-1:0;
3266 lfa.lfUnderline = s.fUnderline?-1:0;
3267 lfa.lfStrikeOut = s.fStrikeOut?-1:0;
3268 return(lfa);
3271 LOGFONTW& operator <<= (LOGFONTW& lfw, const STSStyleBase& s)
3273 lfw.lfCharSet = s.charSet;
3274 wcsncpy_s(lfw.lfFaceName, LF_FACESIZE, CStringW(s.fontName), _TRUNCATE);
3275 HDC hDC = GetDC(0);
3276 lfw.lfHeight = -MulDiv((int)(s.fontSize+0.5), GetDeviceCaps(hDC, LOGPIXELSY), 72);
3277 ReleaseDC(0, hDC);
3278 lfw.lfWeight = s.fontWeight;
3279 lfw.lfItalic = s.fItalic?-1:0;
3280 lfw.lfUnderline = s.fUnderline?-1:0;
3281 lfw.lfStrikeOut = s.fStrikeOut?-1:0;
3282 return(lfw);
3285 CString& operator <<= (CString& style, const STSStyle& s)
3287 style.Format(_T("%d;%d;%d;%d;")
3288 _T("%d;%d;%f;%f;%f;%f;")
3289 _T("0x%06x;0x%06x;0x%06x;0x%06x;")
3290 _T("0x%02x;0x%02x;0x%02x;0x%02x;")
3291 _T("%d;")
3292 _T("%s;%f;%f;%f;%f;%d;")
3293 _T("%d;%d;%d;%d;%f;")
3294 _T("%f;%f;%f;")
3295 _T("%d"),
3296 s.marginRect.get().left, s.marginRect.get().right, s.marginRect.get().top, s.marginRect.get().bottom,
3297 s.scrAlignment, s.borderStyle,s.outlineWidthX, s.outlineWidthY, s.shadowDepthX, s.shadowDepthY,
3298 s.colors[0], s.colors[1], s.colors[2], s.colors[3],
3299 s.alpha[0], s.alpha[1], s.alpha[2], s.alpha[3],
3300 s.charSet,
3301 s.fontName,s.fontSize,s.fontScaleX, s.fontScaleY,s.fontSpacing,s.fontWeight,
3302 (int)s.fItalic, (int)s.fUnderline, (int)s.fStrikeOut, s.fBlur, s.fGaussianBlur,
3303 s.fontAngleZ, s.fontAngleX, s.fontAngleY,
3304 s.relativeTo);
3306 return(style);
3309 STSStyle& operator <<= (STSStyle& s, const CString& style)
3311 s.SetDefault();
3315 CStringW str = TToW(style);
3316 if(str.Find(';')>=0)
3318 CRect tmp_rect;
3319 tmp_rect.left = GetInt(str,';'); tmp_rect.right = GetInt(str,';'); tmp_rect.top = GetInt(str,';'); tmp_rect.bottom = GetInt(str,';');
3320 s.marginRect = tmp_rect;
3321 s.scrAlignment = GetInt(str,';'); s.borderStyle = GetInt(str,';');
3322 s.outlineWidthX = GetFloat(str,';'); s.outlineWidthY = GetFloat(str,';'); s.shadowDepthX = GetFloat(str,';'); s.shadowDepthY = GetFloat(str,';');
3323 for(int i = 0; i < 4; i++) s.colors[i] = (COLORREF)GetInt(str,';');
3324 for(int i = 0; i < 4; i++) s.alpha[i] = GetInt(str,';');
3325 s.charSet = GetInt(str,';');
3326 s.fontName = WToT(GetStr(str,';')); s.fontSize = GetFloat(str,';');
3327 s.fontScaleX = GetFloat(str,';'); s.fontScaleY = GetFloat(str,';');
3328 s.fontSpacing = GetFloat(str,';'); s.fontWeight = GetInt(str,';');
3329 s.fItalic = !!GetInt(str,';'); s.fUnderline = !!GetInt(str,';'); s.fStrikeOut = !!GetInt(str,';'); s.fBlur = GetInt(str,';'); s.fGaussianBlur = GetFloat(str,';');
3330 s.fontAngleZ = GetFloat(str,';'); s.fontAngleX = GetFloat(str,';'); s.fontAngleY = GetFloat(str,';');
3331 s.relativeTo = GetInt(str,';');
3334 catch(...)
3336 s.SetDefault();
3339 return(s);
3342 static bool OpenRealText(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet)
3344 wstring szFile;
3346 CStringW buff;
3347 while(file->ReadString(buff))
3349 buff.Trim();
3350 if(buff.IsEmpty()) continue;
3352 szFile += CStringW(_T("\n")) + buff.GetBuffer();
3355 CRealTextParser RealTextParser;
3356 if (!RealTextParser.ParseRealText(szFile))
3357 return false;
3359 CRealTextParser::Subtitles crRealText = RealTextParser.GetParsedSubtitles();
3361 for (map<pair<int, int>, wstring>::const_iterator i = crRealText.m_mapLines.begin();
3362 i != crRealText.m_mapLines.end();
3363 ++i)
3365 ret.Add(
3366 SubRipper2SSA(i->second.c_str(), CharSet),
3367 file->IsUnicode(),
3368 i->first.first,
3369 i->first.second);
3372 // std::wofstream wofsOut(L"c:/zzz.srt");
3373 // RealTextParser.OutputSRT(wofsOut);
3375 return(!ret.IsEmpty());