/* * Copyright (C) 2003-2006 Gabest * http://www.gabest.org * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * http://www.gnu.org/copyleft/gpl.html * */ #include "stdafx.h" #include "STS.h" #include // gathered from http://www.netwave.or.jp/~shikai/shikai/shcolor.htm struct htmlcolor {TCHAR* name; DWORD color;} hmtlcolors[] = { {_T("white"), 0xffffff}, {_T("whitesmoke"), 0xf5f5f5}, {_T("ghostwhite"), 0xf8f8ff}, {_T("snow"), 0xfffafa}, {_T("gainsboro"), 0xdcdcdc}, {_T("lightgrey"), 0xd3d3d3}, {_T("silver"), 0xc0c0c0}, {_T("darkgray"), 0xa9a9a9}, {_T("gray"), 0x808080}, {_T("dimgray"), 0x696969}, {_T("lightslategray"), 0x778899}, {_T("slategray"), 0x708090}, {_T("darkslategray"), 0x2f4f4f}, {_T("black"), 0x000000}, {_T("azure"), 0xf0ffff}, {_T("aliceblue"), 0xf0f8ff}, {_T("mintcream"), 0xf5fffa}, {_T("honeydew"), 0xf0fff0}, {_T("lightcyan"), 0xe0ffff}, {_T("paleturqoise"), 0xafeeee}, {_T("powderblue"), 0xb0e0e6}, {_T("lightblue"), 0xadd8ed}, {_T("lightsteelblue"), 0xb0c4de}, {_T("skyblue"), 0x87ceeb}, {_T("lightskyblue"), 0x87cefa}, {_T("cyan"), 0x00ffff}, {_T("aqua"), 0x00ff80}, {_T("deepskyblue"), 0x00bfff}, {_T("aquamarine"), 0x7fffd4}, {_T("turquoise"), 0x40e0d0}, {_T("darkturquoise"), 0x00ced1}, {_T("lightseagreen"), 0x20b2aa}, {_T("mediumturquoise"), 0x40e0dd}, {_T("mediumaquamarine"), 0x66cdaa}, {_T("cadetblue"), 0x5f9ea0}, {_T("teal"), 0x008080}, {_T("darkcyan"), 0x008b8b}, {_T("comflowerblue"), 0x6495ed}, {_T("dodgerblue"), 0x1e90ff}, {_T("steelblue"), 0x4682b4}, {_T("royalblue"), 0x4169e1}, {_T("blue"), 0x0000ff}, {_T("mediumblue"), 0x0000cd}, {_T("mediumslateblue"), 0x7b68ee}, {_T("slateblue"), 0x6a5acd}, {_T("darkslateblue"), 0x483d8b}, {_T("darkblue"), 0x00008b}, {_T("midnightblue"), 0x191970}, {_T("navy"), 0x000080}, {_T("palegreen"), 0x98fb98}, {_T("lightgreen"), 0x90ee90}, {_T("mediumspringgreen"), 0x00fa9a}, {_T("springgreen"), 0x00ff7f}, {_T("chartreuse"), 0x7fff00}, {_T("lawngreen"), 0x7cfc00}, {_T("lime"), 0x00ff00}, {_T("limegreen"), 0x32cd32}, {_T("greenyellow"), 0xadff2f}, {_T("yellowgreen"), 0x9acd32}, {_T("darkseagreen"), 0x8fbc8f}, {_T("mediumseagreen"), 0x3cb371}, {_T("seagreen"), 0x2e8b57}, {_T("olivedrab"), 0x6b8e23}, {_T("forestgreen"), 0x228b22}, {_T("green"), 0x008000}, {_T("darkkhaki"), 0xbdb76b}, {_T("olive"), 0x808000}, {_T("darkolivegreen"), 0x556b2f}, {_T("darkgreen"), 0x006400}, {_T("floralwhite"), 0xfffaf0}, {_T("seashell"), 0xfff5ee}, {_T("ivory"), 0xfffff0}, {_T("beige"), 0xf5f5dc}, {_T("cornsilk"), 0xfff8dc}, {_T("lemonchiffon"), 0xfffacd}, {_T("lightyellow"), 0xffffe0}, {_T("lightgoldenrodyellow"), 0xfafad2}, {_T("papayawhip"), 0xffefd5}, {_T("blanchedalmond"), 0xffedcd}, {_T("palegoldenrod"), 0xeee8aa}, {_T("khaki"), 0xf0eb8c}, {_T("bisque"), 0xffe4c4}, {_T("moccasin"), 0xffe4b5}, {_T("navajowhite"), 0xffdead}, {_T("peachpuff"), 0xffdab9}, {_T("yellow"), 0xffff00}, {_T("gold"), 0xffd700}, {_T("wheat"), 0xf5deb3}, {_T("orange"), 0xffa500}, {_T("darkorange"), 0xff8c00}, {_T("oldlace"), 0xfdf5e6}, {_T("linen"), 0xfaf0e6}, {_T("antiquewhite"), 0xfaebd7}, {_T("lightsalmon"), 0xffa07a}, {_T("darksalmon"), 0xe9967a}, {_T("salmon"), 0xfa8072}, {_T("lightcoral"), 0xf08080}, {_T("indianred"), 0xcd5c5c}, {_T("coral"), 0xff7f50}, {_T("tomato"), 0xff6347}, {_T("orangered"), 0xff4500}, {_T("red"), 0xff0000}, {_T("crimson"), 0xdc143c}, {_T("firebrick"), 0xb22222}, {_T("maroon"), 0x800000}, {_T("darkred"), 0x8b0000}, {_T("lavender"), 0xe6e6fe}, {_T("lavenderblush"), 0xfff0f5}, {_T("mistyrose"), 0xffe4e1}, {_T("thistle"), 0xd8bfd8}, {_T("pink"), 0xffc0cb}, {_T("lightpink"), 0xffb6c1}, {_T("palevioletred"), 0xdb7093}, {_T("hotpink"), 0xff69b4}, {_T("fuchsia"), 0xff00ee}, {_T("magenta"), 0xff00ff}, {_T("mediumvioletred"), 0xc71585}, {_T("deeppink"), 0xff1493}, {_T("plum"), 0xdda0dd}, {_T("violet"), 0xee82ee}, {_T("orchid"), 0xda70d6}, {_T("mediumorchid"), 0xba55d3}, {_T("mediumpurple"), 0x9370db}, {_T("purple"), 0x9370db}, {_T("blueviolet"), 0x8a2be2}, {_T("darkviolet"), 0x9400d3}, {_T("darkorchid"), 0x9932cc}, {_T("tan"), 0xd2b48c}, {_T("burlywood"), 0xdeb887}, {_T("sandybrown"), 0xf4a460}, {_T("peru"), 0xcd853f}, {_T("goldenrod"), 0xdaa520}, {_T("darkgoldenrod"), 0xb8860b}, {_T("chocolate"), 0xd2691e}, {_T("rosybrown"), 0xbc8f8f}, {_T("sienna"), 0xa0522d}, {_T("saddlebrown"), 0x8b4513}, {_T("brown"), 0xa52a2a}, }; CHtmlColorMap::CHtmlColorMap() { for(int i = 0; i < countof(hmtlcolors); i++) SetAt(hmtlcolors[i].name, hmtlcolors[i].color); } CHtmlColorMap g_colors; // BYTE CharSetList[] = { ANSI_CHARSET, DEFAULT_CHARSET, SYMBOL_CHARSET, SHIFTJIS_CHARSET, HANGEUL_CHARSET, HANGUL_CHARSET, GB2312_CHARSET, CHINESEBIG5_CHARSET, OEM_CHARSET, JOHAB_CHARSET, HEBREW_CHARSET, ARABIC_CHARSET, GREEK_CHARSET, TURKISH_CHARSET, VIETNAMESE_CHARSET, THAI_CHARSET, EASTEUROPE_CHARSET, RUSSIAN_CHARSET, MAC_CHARSET, BALTIC_CHARSET }; TCHAR* CharSetNames[] = { _T("ANSI"), _T("DEFAULT"), _T("SYMBOL"), _T("SHIFTJIS"), _T("HANGEUL"), _T("HANGUL"), _T("GB2312"), _T("CHINESEBIG5"), _T("OEM"), _T("JOHAB"), _T("HEBREW"), _T("ARABIC"), _T("GREEK"), _T("TURKISH"), _T("VIETNAMESE"), _T("THAI"), _T("EASTEUROPE"), _T("RUSSIAN"), _T("MAC"), _T("BALTIC"), }; int CharSetLen = countof(CharSetList); // static DWORD CharSetToCodePage(DWORD dwCharSet) { CHARSETINFO cs={0}; ::TranslateCharsetInfo((DWORD *)dwCharSet, &cs, TCI_SRCCHARSET); return cs.ciACP; } int FindChar(CStringW str, WCHAR c, int pos, bool fUnicode, int CharSet) { if(fUnicode) return(str.Find(c, pos)); int fStyleMod = 0; DWORD cp = CharSetToCodePage(CharSet); int OrgCharSet = CharSet; for(int i = 0, j = str.GetLength(), k; i < j; i++) { WCHAR c2 = str[i]; if(IsDBCSLeadByteEx(cp, (BYTE)c2)) i++; else if(i >= pos) { if(c2 == c) return(i); } if(c2 == '{') fStyleMod++; else if(fStyleMod > 0) { if(c2 == '}') fStyleMod--; else if(c2 == 'e' && i >= 3 && i < j-1 && str.Mid(i-2, 3) == L"\\fe") { CharSet = 0; for(k = i+1; _istdigit(str[k]); k++) CharSet = CharSet*10 + (str[k] - '0'); if(k == i+1) CharSet = OrgCharSet; cp = CharSetToCodePage(CharSet); } } } return(-1); } /* int FindChar(CStringA str, char c, int pos, bool fUnicode, int CharSet) { ASSERT(!fUnicode); return(FindChar(AToW(str), c, pos, false, CharSet)); } */ static CStringW ToMBCS(CStringW str, DWORD CharSet) { CStringW ret; DWORD cp = CharSetToCodePage(CharSet); for(int i = 0, j = str.GetLength(); i < j; i++) { WCHAR wc = str.GetAt(i); char c[8]; int len; if((len = WideCharToMultiByte(cp, 0, &wc, 1, c, 8, NULL, NULL)) > 0) { for(int k = 0; k < len; k++) ret += (WCHAR)(BYTE)c[k]; } else { ret += '?'; } } return(ret); } static CStringW UnicodeSSAToMBCS(CStringW str, DWORD CharSet) { CStringW ret; int OrgCharSet = CharSet; for(int j = 0; j < str.GetLength(); ) { j = str.Find('{', j); if(j >= 0) { ret += ToMBCS(str.Left(j), CharSet); str = str.Mid(j); j = str.Find('}'); if(j < 0) { ret += ToMBCS(str, CharSet); break; } else { int k = str.Find(L"\\fe"); if(k >= 0 && k < j) { CharSet = 0; int l = k+3; for(; _istdigit(str[l]); l++) CharSet = CharSet*10 + (str[l] - '0'); if(l == k+3) CharSet = OrgCharSet; } j++; ret += ToMBCS(str.Left(j), OrgCharSet); str = str.Mid(j); j = 0; } } else { ret += ToMBCS(str, CharSet); break; } } return(ret); } static CStringW ToUnicode(CStringW str, DWORD CharSet) { CStringW ret; DWORD cp = CharSetToCodePage(CharSet); for(int i = 0, j = str.GetLength(); i < j; i++) { WCHAR wc = str.GetAt(i); char c = wc&0xff; if(IsDBCSLeadByteEx(cp, (BYTE)wc)) { i++; if(i < j) { char cc[2]; cc[0] = c; cc[1] = (char)str.GetAt(i); MultiByteToWideChar(cp, 0, cc, 2, &wc, 1); } } else { MultiByteToWideChar(cp, 0, &c, 1, &wc, 1); } ret += wc; } return(ret); } static CStringW MBCSSSAToUnicode(CStringW str, int CharSet) { CStringW ret; int OrgCharSet = CharSet; for(int j = 0; j < str.GetLength(); ) { j = FindChar(str, '{', 0, false, CharSet); if(j >= 0) { ret += ToUnicode(str.Left(j), CharSet); str = str.Mid(j); j = FindChar(str, '}', 0, false, CharSet); if(j < 0) { ret += ToUnicode(str, CharSet); break; } else { int k = str.Find(L"\\fe"); if(k >= 0 && k < j) { CharSet = 0; int l = k+3; for(; _istdigit(str[l]); l++) CharSet = CharSet*10 + (str[l] - '0'); if(l == k+3) CharSet = OrgCharSet; } j++; ret += ToUnicode(str.Left(j), OrgCharSet); str = str.Mid(j); j = 0; } } else { ret += ToUnicode(str, CharSet); break; } } return(ret); } CStringW RemoveSSATags(CStringW str, bool fUnicode, int CharSet) { for(int i = 0, j; i < str.GetLength(); ) { if((i = FindChar(str, '{', i, fUnicode, CharSet)) < 0) break; if((j = FindChar(str, '}', i, fUnicode, CharSet)) < 0) break; str.Delete(i, j-i+1); } str.Replace(L"\\N", L"\n"); str.Replace(L"\\n", L"\n"); str.Replace(L"\\h", L" "); return(str); } // static CStringW SubRipper2SSA(CStringW str, int CharSet) { str.Replace(L"", L"{\\i1}"); str.Replace(L"", L"{\\i}"); str.Replace(L"", L"{\\b1}"); str.Replace(L"", L"{\\b}"); str.Replace(L"", L"{\\u1}"); str.Replace(L"", L"{\\u}"); return(str); } static bool OpenSubRipper(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet) { int num = 0; CStringW buff; while(file->ReadString(buff)) { buff.Trim(); if(buff.IsEmpty()) continue; WCHAR sep; int hh1, mm1, ss1, ms1, hh2, mm2, ss2, ms2; int c = swscanf(buff, L"%d%c%d%c%d%c%d --> %d%c%d%c%d%c%d\n", &hh1, &sep, &mm1, &sep, &ss1, &sep, &ms1, &hh2, &sep, &mm2, &sep, &ss2, &sep, &ms2); if(c == 1) // numbering { num = hh1; } else if(c == 14) // time info { CStringW str, tmp; bool fFoundEmpty = false; while(file->ReadString(tmp)) { tmp.Trim(); if(tmp.IsEmpty()) fFoundEmpty = true; int num2; WCHAR c; if(swscanf(tmp, L"%d%c", &num2, &c) == 1 && fFoundEmpty) { num = num2; break; } str += tmp + '\n'; } ret.Add( SubRipper2SSA(str, CharSet), file->IsUnicode(), (((hh1*60 + mm1)*60) + ss1)*1000 + ms1, (((hh2*60 + mm2)*60) + ss2)*1000 + ms2); } else if(c != EOF) // might be another format { return(false); } } return(ret.GetCount() > 0); } static bool OpenOldSubRipper(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet) { CStringW buff; while(file->ReadString(buff)) { buff.Trim(); if(buff.IsEmpty()) continue; for(int i = 0; i < buff.GetLength(); i++) { if((i = FindChar(buff, '|', i, file->IsUnicode(), CharSet)) < 0) break; buff.SetAt(i, '\n'); } int hh1, mm1, ss1, hh2, mm2, ss2; int c = swscanf(buff, L"{%d:%d:%d}{%d:%d:%d}", &hh1, &mm1, &ss1, &hh2, &mm2, &ss2); if(c == 6) { ret.Add( buff.Mid(buff.Find('}', buff.Find('}')+1)+1), file->IsUnicode(), (((hh1*60 + mm1)*60) + ss1)*1000, (((hh2*60 + mm2)*60) + ss2)*1000); } else if(c != EOF) // might be another format { return(false); } } return(ret.GetCount() > 0); } static bool OpenSubViewer(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet) { STSStyle def; CStringW font, color, size; bool fBold, fItalic, fStriked, fUnderline; CStringW buff; while(file->ReadString(buff)) { buff.Trim(); if(buff.IsEmpty()) continue; if(buff[0] == '[') { for(int i = 0; i < buff.GetLength() && buff[i]== '['; ) { int j = buff.Find(']', ++i); if(j < i) break; CStringW tag = buff.Mid(i,j-i); tag.Trim(); tag.MakeLower(); i += j-i; j = buff.Find('[', ++i); if(j < 0) j = buff.GetLength(); CStringW param = buff.Mid(i,j-i); param.Trim(L" \\t,"); i = j; if(tag == L"font") font = def.fontName.CompareNoCase(WToT(param)) ? param : L""; else if(tag == L"colf") color = def.colors[0] != wcstol(((LPCWSTR)param)+2, 0, 16) ? param : L""; else if(tag == L"size") size = def.fontSize != wcstol(param, 0, 10) ? param : L""; else if(tag == L"style") { if(param.Find(L"no") >= 0) { fBold = fItalic = fStriked = fUnderline = false; } else { fBold = def.fontWeight < FW_BOLD && param.Find(L"bd") >= 0; fItalic = def.fItalic && param.Find(L"it") >= 0; fStriked = def.fStrikeOut && param.Find(L"st") >= 0; fUnderline = def.fUnderline && param.Find(L"ud") >= 0; } } } continue; } WCHAR sep; int hh1, mm1, ss1, hs1, hh2, mm2, ss2, hs2; int c = swscanf(buff, L"%d:%d:%d%c%d,%d:%d:%d%c%d\n", &hh1, &mm1, &ss1, &sep, &hs1, &hh2, &mm2, &ss2, &sep, &hs2); if(c == 10) { CStringW str; file->ReadString(str); str.Replace(L"[br]", L"\\N"); CStringW prefix; if(!font.IsEmpty()) prefix += L"\\fn" + font; if(!color.IsEmpty()) prefix += L"\\c" + color; if(!size.IsEmpty()) prefix += L"\\fs" + size; if(fBold) prefix += L"\\b1"; if(fItalic) prefix += L"\\i1"; if(fStriked) prefix += L"\\s1"; if(fUnderline) prefix += L"\\u1"; if(!prefix.IsEmpty()) str = L"{" + prefix + L"}" + str; ret.Add(str, file->IsUnicode(), (((hh1*60 + mm1)*60) + ss1)*1000 + hs1*10, (((hh2*60 + mm2)*60) + ss2)*1000 + hs2*10); } else if(c != EOF) // might be another format { return(false); } } return(ret.GetCount() > 0); } static STSStyle* GetMicroDVDStyle(CString str, int CharSet) { STSStyle* ret = new STSStyle(); if(!ret) return(NULL); for(int i = 0, len = str.GetLength(); i < len; i++) { int j = str.Find('{', i); if(j < 0) j = len; if(j >= len) break; int k = str.Find('}', j); if(k < 0) k = len; CString code = str.Mid(j, k-j); if(code.GetLength() > 2) code.SetAt(1, (TCHAR)towlower(code[1])); if(!_tcsnicmp(code, _T("{c:$"), 4)) { _stscanf(code, _T("{c:$%x"), &ret->colors[0]); } else if(!_tcsnicmp(code, _T("{f:"), 3)) { ret->fontName = code.Mid(3); } else if(!_tcsnicmp(code, _T("{s:"), 3)) { float f; if(1 == _stscanf(code, _T("{s:%f"), &f)) ret->fontSize = f; } else if(!_tcsnicmp(code, _T("{h:"), 3)) { _stscanf(code, _T("{h:%d"), &ret->charSet); } else if(!_tcsnicmp(code, _T("{y:"), 3)) { code.MakeLower(); if(code.Find('b') >= 0) ret->fontWeight = FW_BOLD; if(code.Find('i') >= 0) ret->fItalic = true; if(code.Find('u') >= 0) ret->fUnderline = true; if(code.Find('s') >= 0) ret->fStrikeOut = true; } else if(!_tcsnicmp(code, _T("{p:"), 3)) { int p; _stscanf(code, _T("{p:%d"), &p); ret->scrAlignment = (p == 0) ? 8 : 2; } i = k; } return(ret); } static CStringW MicroDVD2SSA(CStringW str, bool fUnicode, int CharSet) { CStringW ret; enum {COLOR=0, FONTNAME, FONTSIZE, FONTCHARSET, BOLD, ITALIC, UNDERLINE, STRIKEOUT}; bool fRestore[8]; int fRestoreLen = 8; memset(fRestore, 0, sizeof(bool)*fRestoreLen); for(int pos = 0, eol; pos < str.GetLength(); pos++) { if((eol = FindChar(str, '|', pos, fUnicode, CharSet)) < 0) eol = str.GetLength(); CStringW line = str.Mid(pos, eol-pos); pos = eol; for(int i = 0, j, k, len = line.GetLength(); i < len; i++) { if((j = FindChar(line, '{', i, fUnicode, CharSet)) < 0) j = str.GetLength(); ret += line.Mid(i, j-i); if(j >= len) break; if((k = FindChar(line, '}', j, fUnicode, CharSet)) < 0) k = len; { CStringW code = line.Mid(j, k-j); if(!wcsnicmp(code, L"{c:$", 4)) { fRestore[COLOR] = (iswupper(code[1]) == 0); code.MakeLower(); int color; swscanf(code, L"{c:$%x", &color); code.Format(L"{\\c&H%x&}", color); ret += code; } else if(!wcsnicmp(code, L"{f:", 3)) { fRestore[FONTNAME] = (iswupper(code[1]) == 0); code.Format(L"{\\fn%s}", code.Mid(3)); ret += code; } else if(!wcsnicmp(code, L"{s:", 3)) { fRestore[FONTSIZE] = (iswupper(code[1]) == 0); code.MakeLower(); float size; swscanf(code, L"{s:%f", &size); code.Format(L"{\\fs%f}", size); ret += code; } else if(!wcsnicmp(code, L"{h:", 3)) { fRestore[COLOR] = (_istupper(code[1]) == 0); code.MakeLower(); int CharSet; swscanf(code, L"{h:%d", &CharSet); code.Format(L"{\\fe%d}", CharSet); ret += code; } else if(!wcsnicmp(code, L"{y:", 3)) { bool f = (_istupper(code[1]) == 0); code.MakeLower(); ret += '{'; if(code.Find('b') >= 0) {ret += L"\\b1"; fRestore[BOLD] = f;} if(code.Find('i') >= 0) {ret += L"\\i1"; fRestore[ITALIC] = f;} if(code.Find('u') >= 0) {ret += L"\\u1"; fRestore[UNDERLINE] = f;} if(code.Find('s') >= 0) {ret += L"\\s1"; fRestore[STRIKEOUT] = f;} ret += '}'; } else if(!wcsnicmp(code, L"{o:", 3)) { code.MakeLower(); int x, y; TCHAR c; swscanf(code, L"{o:%d%c%d", &x, &c, &y); code.Format(L"{\\move(%d,%d,0,0,0,0)}", x, y); ret += code; } else ret += code; } i = k; } if(pos >= str.GetLength()) break; for(int i = 0; i < fRestoreLen; i++) { if(fRestore[i]) { switch(i) { case COLOR: ret += L"{\\c}"; break; case FONTNAME: ret += L"{\\fn}"; break; case FONTSIZE: ret += L"{\\fs}"; break; case FONTCHARSET: ret += L"{\\fe}"; break; case BOLD: ret += L"{\\b}"; break; case ITALIC: ret += L"{\\i}"; break; case UNDERLINE: ret += L"{\\u}"; break; case STRIKEOUT: ret += L"{\\s}"; break; default: break; } } } memset(fRestore, 0, sizeof(bool)*fRestoreLen); ret += L"\\N"; } return(ret); } static bool OpenMicroDVD(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet) { bool fCheck = false, fCheck2 = false; CString style(_T("Default")); CStringW buff;; while(file->ReadString(buff)) { buff.Trim(); if(buff.IsEmpty()) continue; int start, end; int c = swscanf(buff, L"{%d}{%d}", &start, &end); if(c != 2) {c = swscanf(buff, L"{%d}{}", &start)+1; end = start + 60; fCheck = true;} if(c != 2) { int i; if(buff.Find('{') == 0 && (i = buff.Find('}')) > 1 && i < buff.GetLength()) { if(STSStyle* s = GetMicroDVDStyle(WToT(buff.Mid(i+1)), CharSet)) { style = buff.Mid(1, i-1); style.MakeUpper(); if(style.GetLength()) {CString str = style.Mid(1); str.MakeLower(); style = style.Left(1) + str;} ret.AddStyle(style, s); CharSet = s->charSet; continue; } } } if(c == 2) { if(fCheck2 && ret.GetCount()) { STSEntry& stse = ret[ret.GetCount()-1]; stse.end = min(stse.end, start); fCheck2 = false; } ret.Add( MicroDVD2SSA(buff.Mid(buff.Find('}', buff.Find('}')+1)+1), file->IsUnicode(), CharSet), file->IsUnicode(), start, end, style); if(fCheck) { fCheck = false; fCheck2 = true; } } else if(c != EOF) // might be another format { return(false); } } return(ret.GetCount() > 0); } static void ReplaceNoCase(CStringW& str, CStringW from, CStringW to) { CStringW lstr = str; lstr.MakeLower(); int i, j, k; for(i = 0, j = str.GetLength(); i < j; ) { if((k = lstr.Find(from, i)) >= 0) { str.Delete(k, from.GetLength()); lstr.Delete(k, from.GetLength()); str.Insert(k, to); lstr.Insert(k, to); i = k + to.GetLength(); j = str.GetLength(); } else break; } } static CStringW SMI2SSA(CStringW str, int CharSet) { ReplaceNoCase(str, L" ", L" "); ReplaceNoCase(str, L""", L"\""); ReplaceNoCase(str, L"
", L"\\N"); ReplaceNoCase(str, L"", L"{\\i1}"); ReplaceNoCase(str, L"", L"{\\i}"); ReplaceNoCase(str, L"", L"{\\b1}"); ReplaceNoCase(str, L"", L"{\\b}"); CStringW lstr = str; lstr.MakeLower(); // maven@maven.de // now parse line for(int i = 0, j = str.GetLength(); i < j; ) { int k; if((k = lstr.Find('<', i)) < 0) break; int chars_inserted = 0; int l = 1; for(; k+l < j && lstr[k+l] != '>'; l++); l++; // Modified by Cookie Monster if (lstr.Find(L""); for (;;) { args.TrimLeft(); arg = args.SpanExcluding(L" \t>"); args = args.Mid(arg.GetLength()); if(arg.IsEmpty()) break; if (arg.Find(L"color=") == 0 ) { DWORD color; arg = arg.Mid(6); // delete "color=" if ( arg.IsEmpty()) continue; DWORD val; if(g_colors.Lookup(CString(arg), val)) color = (DWORD)val; else if((color = wcstol(arg, NULL, 16) ) == 0) color = 0x00ffffff; // default is white arg.Format(L"%02x%02x%02x", color&0xff, (color>>8)&0xff, (color>>16)&0xff); lstr.Insert(k + l + chars_inserted, CStringW(L"{\\c&H") + arg + L"&}"); str.Insert(k + l + chars_inserted, CStringW(L"{\\c&H") + arg + L"&}"); chars_inserted += 5 + arg.GetLength() + 2; } /* else if (arg.Find(_T("size=" )) == 0 ) { uint fsize; arg = arg.Mid(5); // delete "size=" if ( arg.GetLength() == 0) continue; if ( fsize = _tcstol(arg, &tmp, 10) == 0 ) continue; lstr.Insert(k + l + chars_inserted, CString(_T("{\\fs")) + arg + _T("&}")); str.Insert(k + l + chars_inserted, CString(_T("{\\fs")) + arg + _T("&}")); chars_inserted += 4 + arg.GetLength() + 2; } */ } } // Original Code /* if (lstr.Find(L""); if(arg.GetLength() > 0) { DWORD color; CString key = WToT(arg); void* val; if(g_colors.Lookup(key, val)) color = (DWORD)val; else color = wcstol(arg, NULL, 16); arg.Format(L"%02x%02x%02x", color&0xff, (color>>8)&0xff, (color>>16)&0xff); } lstr.Insert(k + l + chars_inserted, L"{\\c&H" + arg + L"&}"); str.Insert(k + l + chars_inserted, L"{\\c&H" + arg + L"&}"); chars_inserted += 5 + arg.GetLength() + 2; } */ else if (lstr.Find(L"", k) == k) { lstr.Insert(k + l + chars_inserted, L"{\\c}"); str.Insert(k + l + chars_inserted, L"{\\c}"); chars_inserted += 4; } str.Delete(k, l); lstr.Delete(k, l); i = k + chars_inserted; j = str.GetLength(); } return(str); } static bool OpenSami(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet) { CStringW buff, caption; ULONGLONG pos = file->GetPosition(); bool fSAMI = false; while(file->ReadString(buff) && !fSAMI) { if(buff.MakeUpper().Find(L"") >= 0) fSAMI = true; } if(!fSAMI) return(false); file->Seek(pos, 0); bool fComment = false; int start_time = 0; while(file->ReadString(buff)) { buff.Trim(); if(buff.IsEmpty()) continue; CStringW ubuff = buff; ubuff.MakeUpper(); if(ubuff.Find(L"") >= 0 || ubuff.Find(L"") >= 0) fComment = false; } ret.Add( SMI2SSA(caption, CharSet), file->IsUnicode(), start_time, MAXLONG); return(true); } static bool OpenVPlayer(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet) { CStringW buff; while(file->ReadString(buff)) { buff.Trim(); if(buff.IsEmpty()) continue; for(int i = 0; i < buff.GetLength(); i++) { if((i = FindChar(buff, '|', i, file->IsUnicode(), CharSet)) < 0) break; buff.SetAt(i, '\n'); } int hh, mm, ss; int c = swscanf(buff, L"%d:%d:%d:", &hh, &mm, &ss); if(c == 3) { CStringW str = buff.Mid(buff.Find(':', buff.Find(':', buff.Find(':')+1)+1)+1); ret.Add(str, file->IsUnicode(), (((hh*60 + mm)*60) + ss)*1000, (((hh*60 + mm)*60) + ss)*1000 + 1000 + 50*str.GetLength()); } else if(c != EOF) // might be another format { return(false); } } return(ret.GetCount() > 0); } CStringW GetStr(CStringW& buff, char sep = ',') //throw(...) { buff.TrimLeft(); int pos = buff.Find(sep); if(pos < 0) { pos = buff.GetLength(); if(pos < 1) throw 1; } CStringW ret = buff.Left(pos); if(pos < buff.GetLength()) buff = buff.Mid(pos+1); return(ret); } int GetInt(CStringW& buff, char sep = ',') //throw(...) { CStringW str; str = GetStr(buff, sep); str.MakeLower(); CStringW fmtstr = str.GetLength() > 2 && (str.Left(2) == L"&h" || str.Left(2) == L"0x") ? str = str.Mid(2), L"%x" : L"%d"; int ret; if(swscanf(str, fmtstr, &ret) != 1) throw 1; return(ret); } double GetFloat(CStringW& buff, char sep = ',') //throw(...) { CStringW str; str = GetStr(buff, sep); str.MakeLower(); float ret; if(swscanf(str, L"%f", &ret) != 1) throw 1; return((double)ret); } static bool LoadFont(CString& font) { int len = font.GetLength(); CAutoVectorPtr pData; if(len == 0 || (len&3) == 1 || !pData.Allocate(len)) return(false); const TCHAR* s = font; const TCHAR* e = s + len; for(BYTE* p = pData; s < e; s++, p++) *p = *s - 33; for(int i = 0, j = 0, k = len&~3; i < k; i+=4, j+=3) { pData[j+0] = ((pData[i+0]&63)<<2)|((pData[i+1]>>4)& 3); pData[j+1] = ((pData[i+1]&15)<<4)|((pData[i+2]>>2)&15); pData[j+2] = ((pData[i+2]& 3)<<6)|((pData[i+3]>>0)&63); } int datalen = (len&~3)*3/4; if((len&3) == 2) { pData[datalen++] = ((pData[(len&~3)+0]&63)<<2)|((pData[(len&~3)+1]>>4)&3); } else if((len&3) == 3) { pData[datalen++] = ((pData[(len&~3)+0]&63)<<2)|((pData[(len&~3)+1]>>4)& 3); pData[datalen++] = ((pData[(len&~3)+1]&15)<<4)|((pData[(len&~3)+2]>>2)&15); } HANDLE hFont = INVALID_HANDLE_VALUE; if(HMODULE hModule = LoadLibrary(_T("GDI32.DLL"))) { typedef HANDLE (WINAPI *PAddFontMemResourceEx)( IN PVOID, IN DWORD, IN PVOID , IN DWORD*); if(PAddFontMemResourceEx f = (PAddFontMemResourceEx)GetProcAddress(hModule, "AddFontMemResourceEx")) { DWORD cFonts; hFont = f(pData, datalen, NULL, &cFonts); } FreeLibrary(hModule); } if(hFont == INVALID_HANDLE_VALUE) { TCHAR path[MAX_PATH]; GetTempPath(MAX_PATH, path); DWORD chksum = 0; for(int i = 0, j = datalen>>2; i < j; i++) chksum += ((DWORD*)(BYTE*)pData)[i]; CString fn; fn.Format(_T("%sfont%08x.ttf"), path, chksum); CFileStatus fs; if(!CFileGetStatus(fn, fs)) { CFile f; if(f.Open(fn, CFile::modeCreate|CFile::modeWrite|CFile::typeBinary|CFile::shareDenyWrite)) { f.Write(pData, datalen); f.Close(); } } AddFontResource(fn); } return(true); } static bool LoadUUEFont(CTextFile* file) { CString s, font; while(file->ReadString(s)) { s.Trim(); if(s.IsEmpty() || s[0] == '[') break; if(s.Find(_T("fontname:")) == 0) {LoadFont(font); font.Empty(); continue;} font += s; } if(!font.IsEmpty()) LoadFont(font); return(true); } static bool OpenSubStationAlpha(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet) { bool fRet = false; bool firstLine = true; int version = 3, sver = 3; CStringW buff; while(file->ReadString(buff)) { buff.Trim(); if(buff.IsEmpty() || buff.GetAt(0) == ';') continue; CStringW entry; // try { entry = GetStr(buff, ':'); // } // catch(...) {continue;} entry.MakeLower(); if(entry == L"[script info]") { // [script info] must be the first line if (!firstLine) return false; fRet = true; } else if(entry == L"playresx") { try {ret.m_dstScreenSize.cx = GetInt(buff);} catch(...) {ret.m_dstScreenSize = CSize(0, 0); return(false);} if(ret.m_dstScreenSize.cy <= 0) { ret.m_dstScreenSize.cy = (ret.m_dstScreenSize.cx == 1280) ? 1024 : ret.m_dstScreenSize.cx * 3 / 4; } } else if(entry == L"playresy") { try {ret.m_dstScreenSize.cy = GetInt(buff);} catch(...) {ret.m_dstScreenSize = CSize(0, 0); return(false);} if(ret.m_dstScreenSize.cx <= 0) { ret.m_dstScreenSize.cx = (ret.m_dstScreenSize.cy == 1024) ? 1280 : ret.m_dstScreenSize.cy * 4 / 3; } } else if(entry == L"wrapstyle") { try {ret.m_defaultWrapStyle = GetInt(buff);} catch(...) {ret.m_defaultWrapStyle = 1; return(false);} } else if(entry == L"scripttype") { if(buff.GetLength() >= 4 && !buff.Right(4).CompareNoCase(L"4.00")) version = sver = 4; else if(buff.GetLength() >= 5 && !buff.Right(5).CompareNoCase(L"4.00+")) version = sver = 5; else if(buff.GetLength() >= 6 && !buff.Right(6).CompareNoCase(L"4.00++")) version = sver = 6; } else if(entry == L"collisions") { buff = GetStr(buff); buff.MakeLower(); ret.m_collisions = buff.Find(L"reverse") >= 0 ? 1 : 0; } else if(entry == L"scaledborderandshadow") { buff = GetStr(buff); buff.MakeLower(); ret.m_fScaledBAS = buff.Find(L"yes") >= 0; } else if(entry == L"[v4 styles]") { fRet = true; sver = 4; } else if(entry == L"[v4+ styles]") { fRet = true; sver = 5; } else if(entry == L"[v4++ styles]") { fRet = true; sver = 6; } else if(entry == L"style") { STSStyle* style = new STSStyle; if(!style) return(false); try { CString StyleName; int alpha; StyleName = WToT(GetStr(buff)); style->fontName = WToT(GetStr(buff)); style->fontSize = GetFloat(buff); for(int i = 0; i < 4; i++) style->colors[i] = (COLORREF)GetInt(buff); style->fontWeight = !!GetInt(buff) ? FW_BOLD : FW_NORMAL; style->fItalic = !!GetInt(buff); if(sver >= 5) style->fUnderline = !!GetInt(buff); if(sver >= 5) style->fStrikeOut = !!GetInt(buff); if(sver >= 5) style->fontScaleX = GetFloat(buff); if(sver >= 5) style->fontScaleY = GetFloat(buff); if(sver >= 5) style->fontSpacing = GetFloat(buff); if(sver >= 5) style->fontAngleZ = GetFloat(buff); if(sver >= 4) style->borderStyle = GetInt(buff); style->outlineWidth = GetFloat(buff); style->shadowDepth = GetFloat(buff); style->scrAlignment = GetInt(buff); style->marginRect.left = GetInt(buff); style->marginRect.right = GetInt(buff); style->marginRect.top = style->marginRect.bottom = GetInt(buff); if(sver >= 6) style->marginRect.bottom = GetInt(buff); if(sver <= 4) alpha = GetInt(buff); style->charSet = GetInt(buff); if(sver >= 6) style->relativeTo = GetInt(buff); if(sver <= 4) style->colors[2] = style->colors[3]; // style->colors[2] is used for drawing the outline if(sver <= 4) alpha = max(min(alpha, 0xff), 0); if(sver <= 4) {for(int i = 0; i < 3; i++) style->alpha[i] = alpha; style->alpha[3] = 0x80;} if(sver >= 5) for(int i = 0; i < 4; i++) {style->alpha[i] = (BYTE)(style->colors[i]>>24); style->colors[i] &= 0xffffff;} if(sver >= 5) style->fontScaleX = max(style->fontScaleX, 0); if(sver >= 5) style->fontScaleY = max(style->fontScaleY, 0); if(sver >= 5) style->fontSpacing = max(style->fontSpacing, 0); style->fontAngleX = style->fontAngleY = 0; style->borderStyle = style->borderStyle == 1 ? 0 : style->borderStyle == 3 ? 1 : 0; style->outlineWidth = max(style->outlineWidth, 0); style->shadowDepth = max(style->shadowDepth, 0); if(sver <= 4) style->scrAlignment = (style->scrAlignment&4) ? ((style->scrAlignment&3)+6) // top : (style->scrAlignment&8) ? ((style->scrAlignment&3)+3) // mid : (style->scrAlignment&3); // bottom StyleName.TrimLeft('*'); ret.AddStyle(StyleName, style); } catch(...) { delete style; return(false); } } else if(entry == L"[events]") { fRet = true; } else if(entry == _T("dialogue")) { try { int hh1, mm1, ss1, ms1_div10, hh2, mm2, ss2, ms2_div10, layer = 0; CString Style, Actor, Effect; CRect marginRect; if(version <= 4){GetStr(buff, '='); GetInt(buff);} /* Marked = */ if(version >= 5)layer = GetInt(buff); hh1 = GetInt(buff, ':'); mm1 = GetInt(buff, ':'); ss1 = GetInt(buff, '.'); ms1_div10 = GetInt(buff); hh2 = GetInt(buff, ':'); mm2 = GetInt(buff, ':'); ss2 = GetInt(buff, '.'); ms2_div10 = GetInt(buff); Style = WToT(GetStr(buff)); Actor = WToT(GetStr(buff)); marginRect.left = GetInt(buff); marginRect.right = GetInt(buff); marginRect.top = marginRect.bottom = GetInt(buff); if(version >= 6)marginRect.bottom = GetInt(buff); Effect = WToT(GetStr(buff)); int len = min(Effect.GetLength(), buff.GetLength()); if(Effect.Left(len) == WToT(buff.Left(len))) Effect.Empty(); Style.TrimLeft('*'); if(!Style.CompareNoCase(_T("Default"))) Style = _T("Default"); ret.Add(buff, file->IsUnicode(), (((hh1*60 + mm1)*60) + ss1)*1000 + ms1_div10*10, (((hh2*60 + mm2)*60) + ss2)*1000 + ms2_div10*10, Style, Actor, Effect, marginRect, layer); } catch(...) { return(false); } } else if(entry == L"fontname") { LoadUUEFont(file); } firstLine = false; } return(fRet); } static bool OpenXombieSub(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet) { float version = 0; // CMapStringToPtr stylemap; CStringW buff; while(file->ReadString(buff)) { buff.Trim(); if(buff.IsEmpty() || buff.GetAt(0) == ';') continue; CStringW entry; // try { entry = GetStr(buff, '='); // } // catch(...) {continue;} entry.MakeLower(); if(entry == L"version") { version = (float)GetFloat(buff); } else if(entry == L"screenhorizontal") { try {ret.m_dstScreenSize.cx = GetInt(buff);} catch(...) {ret.m_dstScreenSize = CSize(0, 0); return(false);} if(ret.m_dstScreenSize.cy <= 0) { ret.m_dstScreenSize.cy = (ret.m_dstScreenSize.cx == 1280) ? 1024 : ret.m_dstScreenSize.cx * 3 / 4; } } else if(entry == L"screenvertical") { try {ret.m_dstScreenSize.cy = GetInt(buff);} catch(...) {ret.m_dstScreenSize = CSize(0, 0); return(false);} if(ret.m_dstScreenSize.cx <= 0) { ret.m_dstScreenSize.cx = (ret.m_dstScreenSize.cy == 1024) ? 1280 : ret.m_dstScreenSize.cy * 4 / 3; } } else if(entry == L"style") { STSStyle* style = new STSStyle; if(!style) return(false); try { CString StyleName; StyleName = WToT(GetStr(buff)) + _T("_") + WToT(GetStr(buff)); style->fontName = WToT(GetStr(buff)); style->fontSize = GetFloat(buff); for(int i = 0; i < 4; i++) style->colors[i] = (COLORREF)GetInt(buff); for(int i = 0; i < 4; i++) style->alpha[i] = GetInt(buff); style->fontWeight = !!GetInt(buff) ? FW_BOLD : FW_NORMAL; style->fItalic = !!GetInt(buff); style->fUnderline = !!GetInt(buff); style->fStrikeOut = !!GetInt(buff); style->fBlur = !!GetInt(buff); style->fontScaleX = GetFloat(buff); style->fontScaleY = GetFloat(buff); style->fontSpacing = GetFloat(buff); style->fontAngleX = GetFloat(buff); style->fontAngleY = GetFloat(buff); style->fontAngleZ = GetFloat(buff); style->borderStyle = GetInt(buff); style->outlineWidth = GetFloat(buff); style->shadowDepth = GetFloat(buff); style->scrAlignment = GetInt(buff); style->marginRect.left = GetInt(buff); style->marginRect.right = GetInt(buff); style->marginRect.top = style->marginRect.bottom = GetInt(buff); style->charSet = GetInt(buff); style->fontScaleX = max(style->fontScaleX, 0); style->fontScaleY = max(style->fontScaleY, 0); style->fontSpacing = max(style->fontSpacing, 0); style->borderStyle = style->borderStyle == 1 ? 0 : style->borderStyle == 3 ? 1 : 0; style->outlineWidth = max(style->outlineWidth, 0); style->shadowDepth = max(style->shadowDepth, 0); ret.AddStyle(StyleName, style); } catch(...) { delete style; return(false); } } else if(entry == L"line") { try { CString id; int hh1, mm1, ss1, ms1, hh2, mm2, ss2, ms2, layer = 0; CString Style, Actor; CRect marginRect; if(GetStr(buff) != L"D") continue; id = GetStr(buff); layer = GetInt(buff); hh1 = GetInt(buff, ':'); mm1 = GetInt(buff, ':'); ss1 = GetInt(buff, '.'); ms1 = GetInt(buff); hh2 = GetInt(buff, ':'); mm2 = GetInt(buff, ':'); ss2 = GetInt(buff, '.'); ms2 = GetInt(buff); Style = WToT(GetStr(buff)) + _T("_") + WToT(GetStr(buff)); Actor = WToT(GetStr(buff)); marginRect.left = GetInt(buff); marginRect.right = GetInt(buff); marginRect.top = marginRect.bottom = GetInt(buff); Style.TrimLeft('*'); if(!Style.CompareNoCase(_T("Default"))) Style = _T("Default"); ret.Add(buff, file->IsUnicode(), (((hh1*60 + mm1)*60) + ss1)*1000 + ms1, (((hh2*60 + mm2)*60) + ss2)*1000 + ms2, Style, Actor, _T(""), marginRect, layer); } catch(...) { return(false); } } else if(entry == L"fontname") { LoadUUEFont(file); } } return(ret.GetCount() > 0); } #include "USFSubtitles.h" static bool OpenUSF(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet) { CString str; while(file->ReadString(str)) { if(str.Find(_T("USFSubtitles")) >= 0) { CUSFSubtitles usf; if(usf.Read(file->GetFilePath()) && usf.ConvertToSTS(ret)) return(true); break; } } return(false); } static CStringW MPL22SSA(CStringW str) { CAtlList sl; Explode(str, sl, '|'); POSITION pos = sl.GetHeadPosition(); while(pos) { CStringW& s = sl.GetNext(pos); if(s[0] == '/') {s = L"{\\i1}" + s.Mid(1) + L"{\\i0}";} } str = Implode(sl, '\n'); str.Replace(L"\n", L"\\N"); return str; } static bool OpenMPL2(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet) { CStringW buff;; while(file->ReadString(buff)) { buff.Trim(); if(buff.IsEmpty()) continue; int start, end; int c = swscanf(buff, L"[%d][%d]", &start, &end); if(c == 2) { ret.Add( MPL22SSA(buff.Mid(buff.Find(']', buff.Find(']')+1)+1)), file->IsUnicode(), start*100, end*100); } else if(c != EOF) // might be another format { return(false); } } return(ret.GetCount() > 0); } typedef bool (*STSOpenFunct)(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet); typedef struct {STSOpenFunct open; tmode mode;} OpenFunctStruct; static OpenFunctStruct OpenFuncts[] = { OpenSubRipper, TIME, OpenOldSubRipper, TIME, OpenSubViewer, TIME, OpenMicroDVD, FRAME, OpenSami, TIME, OpenVPlayer, TIME, OpenSubStationAlpha, TIME, OpenXombieSub, TIME, OpenUSF, TIME, OpenMPL2, TIME, }; static int nOpenFuncts = countof(OpenFuncts); // CSimpleTextSubtitle::CSimpleTextSubtitle() { m_mode = TIME; m_dstScreenSize = CSize(0, 0); m_defaultWrapStyle = 0; m_collisions = 0; m_fScaledBAS = false; m_encoding = CTextFile::ASCII; } CSimpleTextSubtitle::~CSimpleTextSubtitle() { Empty(); } /* CSimpleTextSubtitle::CSimpleTextSubtitle(CSimpleTextSubtitle& sts) { *this = sts; } CSimpleTextSubtitle& CSimpleTextSubtitle::operator = (CSimpleTextSubtitle& sts) { Empty(); m_name = sts.m_name; m_mode = sts.m_mode; m_dstScreenSize = sts.m_dstScreenSize; m_defaultWrapStyle = sts.m_defaultWrapStyle; m_collisions = sts.m_collisions; m_fScaledBAS = sts.m_fScaledBAS; m_fSSA = sts.m_fSSA; m_fUsingAutoGeneratedDefaultStyle = sts.m_fUsingAutoGeneratedDefaultStyle; CopyStyles(sts.m_styles); m_segments.Copy(sts.m_segments); Copy(sts); return(*this); } */ void CSimpleTextSubtitle::Copy(CSimpleTextSubtitle& sts) { Empty(); m_name = sts.m_name; m_mode = sts.m_mode; m_dstScreenSize = sts.m_dstScreenSize; m_defaultWrapStyle = sts.m_defaultWrapStyle; m_collisions = sts.m_collisions; m_fScaledBAS = sts.m_fScaledBAS; m_encoding = sts.m_encoding; m_fUsingAutoGeneratedDefaultStyle = sts.m_fUsingAutoGeneratedDefaultStyle; CopyStyles(sts.m_styles); m_segments.Copy(sts.m_segments); __super::Copy(sts); } void CSimpleTextSubtitle::Append(CSimpleTextSubtitle& sts, int timeoff) { if(timeoff < 0) { timeoff = GetCount() > 0 ? GetAt(GetCount()-1).end : 0; } for(int i = 0, j = GetCount(); i < j; i++) { if(GetAt(i).start > timeoff) { RemoveAt(i, j - i); break; } } CopyStyles(sts.m_styles, true); for(int i = 0, j = sts.GetCount(); i < j; i++) { STSEntry stse = sts.GetAt(i); stse.start += timeoff; stse.end += timeoff; stse.readorder += GetCount(); __super::Add(stse); } CreateSegments(); } void CSTSStyleMap::Free() { POSITION pos = GetStartPosition(); while(pos) { CString key; STSStyle* val; GetNextAssoc(pos, key, val); delete val; } RemoveAll(); } bool CSimpleTextSubtitle::CopyStyles(const CSTSStyleMap& styles, bool fAppend) { if(!fAppend) m_styles.Free(); POSITION pos = styles.GetStartPosition(); while(pos) { CString key; STSStyle* val; styles.GetNextAssoc(pos, key, val); STSStyle* s = new STSStyle; if(!s) return(false); *s = *val; AddStyle(key, s); } return(true); } void CSimpleTextSubtitle::Empty() { m_dstScreenSize = CSize(0, 0); m_styles.Free(); m_segments.RemoveAll(); RemoveAll(); } void CSimpleTextSubtitle::Add(CStringW str, bool fUnicode, int start, int end, CString style, CString actor, CString effect, CRect marginRect, int layer, int readorder) { if(str.Trim().IsEmpty() || start > end) return; str.Remove('\r'); str.Replace(L"\n", L"\\N"); if(style.IsEmpty()) style = _T("Default"); style.TrimLeft('*'); STSEntry sub; sub.str = str; sub.fUnicode = fUnicode; sub.style = style; sub.actor = actor; sub.effect = effect; sub.marginRect = marginRect; sub.layer = layer; sub.start = start; sub.end = end; sub.readorder = readorder < 0 ? GetCount() : readorder; int n = __super::Add(sub); int len = m_segments.GetCount(); if(len == 0) { STSSegment stss(start, end); stss.subs.Add(n); m_segments.Add(stss); } else if(end <= m_segments[0].start) { STSSegment stss(start, end); stss.subs.Add(n); m_segments.InsertAt(0, stss); } else if(start >= m_segments[len-1].end) { STSSegment stss(start, end); stss.subs.Add(n); m_segments.Add(stss); } else { if(start < m_segments[0].start) { STSSegment stss(start, m_segments[0].start); stss.subs.Add(n); start = m_segments[0].start; m_segments.InsertAt(0, stss); } for(int i = 0; i < m_segments.GetCount(); i++) { STSSegment& s = m_segments[i]; if(start >= s.end) continue; if(end <= s.start) break; if(s.start < start && start < s.end) { STSSegment stss(s.start, start); stss.subs.Copy(s.subs); s.start = start; m_segments.InsertAt(i, stss); continue; } if(start <= s.start && s.end <= end) { for(int j = 0, k = s.subs.GetCount(); j <= k; j++) { if(j == k || sub.readorder < GetAt(s.subs[j]).readorder) s.subs.InsertAt(j, n); } // s.subs.Add(n); } if(s.start < end && end < s.end) { STSSegment stss(s.start, end); stss.subs.Copy(s.subs); for(int j = 0, k = s.subs.GetCount(); j <= k; j++) { if(j == k || sub.readorder < GetAt(stss.subs[j]).readorder) stss.subs.InsertAt(j, n); } // stss.subs.Add(n); s.start = end; m_segments.InsertAt(i, stss); } } if(end > m_segments[m_segments.GetCount()-1].end) { STSSegment stss(m_segments[m_segments.GetCount()-1].end, end); stss.subs.Add(n); m_segments.Add(stss); } } /* str.Remove('\r'); str.Replace(L"\n", L"\\N"); if(style.IsEmpty()) style = _T("Default"); int j = m_segments.GetCount(); for(int i = j-1; i >= 0; i--) { if(m_segments[i].end <= start) { break; } else if(m_segments[i].start >= start) { m_segments.SetCount(m_segments.GetCount()-1); j--; } else if(m_segments[i].end > start) { if(i < j-1) m_segments.RemoveAt(i+1, j-i-1); m_segments[i].end = start; break; } } if(m_segments.GetCount() == 0 && j > 0) CSTSArray::RemoveAll(); STSSegment stss(start, end); int len = GetCount(); stss.subs.Add(len); m_segments.Add(stss); STSEntry sub; sub.str = str; sub.fUnicode = fUnicode; sub.style = style; sub.actor = actor; sub.effect = effect; sub.marginRect = marginRect; sub.layer = layer; sub.start = start; sub.end = end; sub.readorder = GetCount(); CSTSArray::Add(sub); */ } STSStyle* CSimpleTextSubtitle::CreateDefaultStyle(int CharSet) { CString def(_T("Default")); STSStyle* ret = NULL; if(!m_styles.Lookup(def, ret)) { STSStyle* style = new STSStyle(); style->charSet = CharSet; AddStyle(def, style); m_styles.Lookup(def, ret); m_fUsingAutoGeneratedDefaultStyle = true; } else { m_fUsingAutoGeneratedDefaultStyle = false; } return ret; } void CSimpleTextSubtitle::ChangeUnknownStylesToDefault() { CAtlMap > unknown; bool fReport = true; for(int i = 0; i < GetCount(); i++) { STSEntry& stse = GetAt(i); STSStyle* val; if(!m_styles.Lookup(stse.style, val)) { if(!unknown.Lookup(stse.style, val)) { if(fReport) { CString msg; msg.Format(_T("Unknown style found: \"%s\", changed to \"Default\"!\n\nPress Cancel to ignore further warnings."), stse.style); if(MessageBox(NULL, msg, _T("Warning"), MB_OKCANCEL|MB_ICONWARNING) != IDOK) fReport = false; } unknown[stse.style] = NULL; } stse.style = _T("Default"); } } } void CSimpleTextSubtitle::AddStyle(CString name, STSStyle* style) { int i, j; if(name.IsEmpty()) name = _T("Default"); STSStyle* val; if(m_styles.Lookup(name, val)) { if(*val == *style) { delete style; return; } int len = name.GetLength(); for(i = len; i > 0 && _istdigit(name[i-1]); i--); int idx = 1; CString name2 = name; if(i < len && _stscanf(name.Right(len-i), _T("%d"), &idx) == 1) { name2 = name.Left(i); } idx++; CString name3; do { name3.Format(_T("%s%d"), name2, idx); idx++; } while(m_styles.Lookup(name3)); m_styles.RemoveKey(name); m_styles[name3] = val; for(i = 0, j = GetCount(); i < j; i++) { STSEntry& stse = GetAt(i); if(stse.style == name) stse.style = name3; } } m_styles[name] = style; } bool CSimpleTextSubtitle::SetDefaultStyle(STSStyle& s) { STSStyle* val; if(!m_styles.Lookup(_T("Default"), val)) return false; *val = s; m_fUsingAutoGeneratedDefaultStyle = false; return true; } bool CSimpleTextSubtitle::GetDefaultStyle(STSStyle& s) { STSStyle* val; if(!m_styles.Lookup(_T("Default"), val)) return false; s = *val; return true; } void CSimpleTextSubtitle::ConvertToTimeBased(double fps) { if(m_mode == TIME) return; for(int i = 0, j = GetCount(); i < j; i++) { STSEntry& stse = (*this)[i]; stse.start = int(1.0 * stse.start * 1000 / fps + 0.5); stse.end = int(1.0 * stse.end * 1000 / fps + 0.5); } m_mode = TIME; CreateSegments(); } void CSimpleTextSubtitle::ConvertToFrameBased(double fps) { if(m_mode == FRAME) return; for(int i = 0, j = GetCount(); i < j; i++) { STSEntry& stse = (*this)[i]; stse.start = int(1.0 * stse.start * fps / 1000 + 0.5); stse.end = int(1.0 * stse.end * fps / 1000 + 0.5); } m_mode = FRAME; CreateSegments(); } int CSimpleTextSubtitle::SearchSub(int t, double fps) { int i = 0, j = GetCount() - 1, ret = -1; if(j >= 0 && t >= TranslateStart(j, fps)) { return(j); } while(i < j) { int mid = (i + j) >> 1; int midt = TranslateStart(mid, fps); if(t == midt) { while(mid > 0 && t == TranslateStart(mid-1, fps)) mid--; ret = mid; break; } else if(t < midt) { ret = -1; if(j == mid) mid--; j = mid; } else if(t > midt) { ret = mid; if(i == mid) mid++; i = mid; } } return(ret); } const STSSegment* CSimpleTextSubtitle::SearchSubs(int t, double fps, /*[out]*/ int* iSegment, int* nSegments) { int i = 0, j = m_segments.GetCount() - 1, ret = -1; if(nSegments) *nSegments = j+1; if(j >= 0 && t >= TranslateSegmentStart(j, fps) && t < TranslateSegmentEnd(j, fps)) { if(iSegment) *iSegment = j; return(&m_segments[j]); } if(j >= 0 && t >= TranslateSegmentEnd(j, fps)) { if(iSegment) *iSegment = j+1; return(NULL); } if(j > 0 && t < TranslateSegmentStart(i, fps)) { if(iSegment) *iSegment = -1; return(NULL); } while(i < j) { int mid = (i + j) >> 1; int midt = TranslateSegmentStart(mid, fps); if(t == midt) { ret = mid; break; } else if(t < midt) { ret = -1; if(j == mid) mid--; j = mid; } else if(t > midt) { ret = mid; if(i == mid) mid++; i = mid; } } if(0 <= ret && ret < m_segments.GetCount()) { if(iSegment) *iSegment = ret; } if(0 <= ret && ret < m_segments.GetCount() && m_segments[ret].subs.GetCount() > 0 && TranslateSegmentStart(ret, fps) <= t && t < TranslateSegmentEnd(ret, fps)) { return(&m_segments[ret]); } return(NULL); } int CSimpleTextSubtitle::TranslateStart(int i, double fps) { return(i < 0 || GetCount() <= i ? -1 : m_mode == TIME ? GetAt(i).start : m_mode == FRAME ? (int)(GetAt(i).start*1000/fps) : 0); } int CSimpleTextSubtitle::TranslateEnd(int i, double fps) { return(i < 0 || GetCount() <= i ? -1 : m_mode == TIME ? GetAt(i).end : m_mode == FRAME ? (int)(GetAt(i).end*1000/fps) : 0); } int CSimpleTextSubtitle::TranslateSegmentStart(int i, double fps) { return(i < 0 || m_segments.GetCount() <= i ? -1 : m_mode == TIME ? m_segments[i].start : m_mode == FRAME ? (int)(m_segments[i].start*1000/fps) : 0); } int CSimpleTextSubtitle::TranslateSegmentEnd(int i, double fps) { return(i < 0 || m_segments.GetCount() <= i ? -1 : m_mode == TIME ? m_segments[i].end : m_mode == FRAME ? (int)(m_segments[i].end*1000/fps) : 0); } STSStyle* CSimpleTextSubtitle::GetStyle(int i) { CString def = _T("Default"); STSStyle* style = NULL; m_styles.Lookup(GetAt(i).style, style); STSStyle* defstyle = NULL; m_styles.Lookup(def, defstyle); if(!style) { style = defstyle; } ASSERT(style); return style; } bool CSimpleTextSubtitle::GetStyle(int i, STSStyle& stss) { CString def = _T("Default"); STSStyle* style = NULL; m_styles.Lookup(GetAt(i).style, style); STSStyle* defstyle = NULL; m_styles.Lookup(def, defstyle); if(!style) { if(!defstyle) { defstyle = CreateDefaultStyle(DEFAULT_CHARSET); } style = defstyle; } if(!style) {ASSERT(0); return false;} stss = *style; if(stss.relativeTo == 2 && defstyle) stss.relativeTo = defstyle->relativeTo; return true; } int CSimpleTextSubtitle::GetCharSet(int i) { STSStyle stss; GetStyle(i, stss); return(stss.charSet); } bool CSimpleTextSubtitle::IsEntryUnicode(int i) { return(GetAt(i).fUnicode); } void CSimpleTextSubtitle::ConvertUnicode(int i, bool fUnicode) { STSEntry& stse = GetAt(i); if(stse.fUnicode ^ fUnicode) { int CharSet = GetCharSet(i); stse.str = fUnicode ? MBCSSSAToUnicode(stse.str, CharSet) : UnicodeSSAToMBCS(stse.str, CharSet); stse.fUnicode = fUnicode; } } CStringA CSimpleTextSubtitle::GetStrA(int i, bool fSSA) { return(WToA(GetStrWA(i, fSSA))); } CStringW CSimpleTextSubtitle::GetStrW(int i, bool fSSA) { bool fUnicode = IsEntryUnicode(i); int CharSet = GetCharSet(i); CStringW str = GetAt(i).str; if(!fUnicode) str = MBCSSSAToUnicode(str, CharSet); if(!fSSA) str = RemoveSSATags(str, fUnicode, CharSet); return(str); } CStringW CSimpleTextSubtitle::GetStrWA(int i, bool fSSA) { bool fUnicode = IsEntryUnicode(i); int CharSet = GetCharSet(i); CStringW str = GetAt(i).str; if(fUnicode) str = UnicodeSSAToMBCS(str, CharSet); if(!fSSA) str = RemoveSSATags(str, fUnicode, CharSet); return(str); } void CSimpleTextSubtitle::SetStr(int i, CStringA str, bool fUnicode) { SetStr(i, AToW(str), false); } void CSimpleTextSubtitle::SetStr(int i, CStringW str, bool fUnicode) { STSEntry& stse = GetAt(i); str.Replace(L"\n", L"\\N"); if(stse.fUnicode && !fUnicode) stse.str = MBCSSSAToUnicode(str, GetCharSet(i)); else if(!stse.fUnicode && fUnicode) stse.str = UnicodeSSAToMBCS(str, GetCharSet(i)); else stse.str = str; } static int comp1(const void* a, const void* b) { int ret = ((STSEntry*)a)->start - ((STSEntry*)b)->start; if(ret == 0) ret = ((STSEntry*)a)->layer - ((STSEntry*)b)->layer; if(ret == 0) ret = ((STSEntry*)a)->readorder - ((STSEntry*)b)->readorder; return(ret); } static int comp2(const void* a, const void* b) { return(((STSEntry*)a)->readorder - ((STSEntry*)b)->readorder); } void CSimpleTextSubtitle::Sort(bool fRestoreReadorder) { qsort(GetData(), GetCount(), sizeof(STSEntry), !fRestoreReadorder ? comp1 : comp2); CreateSegments(); } static int intcomp(const void* i1, const void* i2) { return(*((int*)i1) - *((int*)i2)); } void CSimpleTextSubtitle::CreateSegments() { m_segments.RemoveAll(); int i, j; CAtlArray breakpoints; for(i = 0; i < GetCount(); i++) { STSEntry& stse = GetAt(i); breakpoints.Add(stse.start); breakpoints.Add(stse.end); } qsort(breakpoints.GetData(), breakpoints.GetCount(), sizeof(int), intcomp); int* ptr = breakpoints.GetData(), prev = ptr ? *ptr : NULL; for(i = breakpoints.GetCount(); i > 0; i--, ptr++) { if(*ptr != prev) { m_segments.Add(STSSegment(prev, *ptr)); prev = *ptr; } } for(i = 0; i < GetCount(); i++) { STSEntry& stse = GetAt(i); for(j = 0; j < m_segments.GetCount() && m_segments[j].start < stse.start; j++); for(; j < m_segments.GetCount() && m_segments[j].end <= stse.end; j++) m_segments[j].subs.Add(i); } OnChanged(); /* for(i = 0, j = m_segments.GetCount(); i < j; i++) { STSSegment& stss = m_segments[i]; TRACE(_T("%d - %d"), stss.start, stss.end); for(int k = 0, l = stss.subs.GetCount(); k < l; k++) { TRACE(_T(", %d"), stss.subs[k]); } TRACE(_T("\n")); } */ } bool CSimpleTextSubtitle::Open(CString fn, int CharSet, CString name) { Empty(); CWebTextFile f; if(!f.Open(fn)) return(false); fn.Replace('\\', '/'); if(name.IsEmpty()) { name = fn.Left(fn.ReverseFind('.')); name = name.Mid(name.ReverseFind('/')+1); name = name.Mid(name.ReverseFind('.')+1); } return(Open(&f, CharSet, name)); } static int CountLines(CTextFile* f, ULONGLONG from, ULONGLONG to) { int n = 0; CString s; f->Seek(from, 0); while(f->ReadString(s) && f->GetPosition() < to) n++; return(n); } bool CSimpleTextSubtitle::Open(CTextFile* f, int CharSet, CString name) { Empty(); ULONGLONG pos = f->GetPosition(); for(int i = 0; i < nOpenFuncts; i++) { if(!OpenFuncts[i].open(f, *this, CharSet) /*|| !GetCount()*/) { if(GetCount() > 0) { int n = CountLines(f, pos, f->GetPosition()); CString s; s.Format(_T("Syntax error at line %d!\t"), n+1); AfxMessageBox(s, MB_OK|MB_ICONERROR); Empty(); break; } f->Seek(pos, 0); Empty(); continue; } m_name = name; m_mode = OpenFuncts[i].mode; m_encoding = f->GetEncoding(); m_path = f->GetFilePath(); // Sort(); CreateSegments(); CWebTextFile f2; if(f2.Open(f->GetFilePath() + _T(".style"))) OpenSubStationAlpha(&f2, *this, CharSet); CreateDefaultStyle(CharSet); ChangeUnknownStylesToDefault(); if(m_dstScreenSize == CSize(0, 0)) m_dstScreenSize = CSize(384, 288); return(true); } return(false); } bool CSimpleTextSubtitle::Open(BYTE* data, int len, int CharSet, CString name) { TCHAR path[MAX_PATH]; if(!GetTempPath(MAX_PATH, path)) return(false); TCHAR fn[MAX_PATH]; if(!GetTempFileName(path, _T("vs"), 0, fn)) return(false); FILE* tmp = _tfopen(fn, _T("wb")); if(!tmp) return(false); int i = 0; for(; i <= (len-1024); i += 1024) fwrite(&data[i], 1024, 1, tmp); if(len > i) fwrite(&data[i], len - i, 1, tmp); fclose(tmp); bool fRet = Open(fn, CharSet, name); _tremove(fn); return(fRet); } bool CSimpleTextSubtitle::SaveAs(CString fn, exttype et, double fps, CTextFile::enc e) { if(fn.Mid(fn.ReverseFind('.')+1).CompareNoCase(exttypestr[et])) { if(fn[fn.GetLength()-1] != '.') fn += _T("."); fn += exttypestr[et]; } CTextFile f; if(!f.Save(fn, e)) return(false); if(et == EXTSMI) { CString str; str += _T("\n\n"); str += _T("\n"); str += _T("\n"); str += _T("\n"); str += _T("\n"); f.WriteString(str); } else if(et == EXTSSA || et == EXTASS) { CString str; str = _T("[Script Info]\n"); str += (et == EXTSSA) ? _T("; This is a Sub Station Alpha v4 script.\n") : _T("; This is an Advanced Sub Station Alpha v4+ script.\n"); str += _T("; For Sub Station Alpha info and downloads,\n"); str += _T("; go to http://www.eswat.demon.co.uk/\n"); str += _T("; or email kotus@eswat.demon.co.uk\n"); str += _T("; \n"); if(et == EXTASS) { str += _T("; Advanced Sub Station Alpha script format developed by #Anime-Fansubs@EfNET\n"); str += _T("; http://www.anime-fansubs.org\n"); str += _T("; \n"); str += _T("; For additional info and downloads go to http://gabest.org/\n"); str += _T("; or email gabest@freemail.hu\n"); str += _T("; \n"); } str += _T("; Note: This file was saved by Subresync.\n"); str += _T("; \n"); str += (et == EXTSSA) ? _T("ScriptType: v4.00\n") : _T("ScriptType: v4.00+\n"); str += (m_collisions == 0) ? _T("Collisions: Normal\n") : _T("Collisions: Reverse\n"); if(et == EXTASS && m_fScaledBAS) str += _T("ScaledBorderAndShadow: Yes\n"); str += _T("PlayResX: %d\n"); str += _T("PlayResY: %d\n"); str += _T("Timer: 100.0000\n"); str += _T("\n"); str += (et == EXTSSA) ? _T("[V4 Styles]\nFormat: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, TertiaryColour, BackColour, Bold, Italic, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, AlphaLevel, Encoding\n") : _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"); CString str2; str2.Format(str, m_dstScreenSize.cx, m_dstScreenSize.cy); f.WriteString(str2); str = (et == EXTSSA) ? _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") : _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"); POSITION pos = m_styles.GetStartPosition(); while(pos) { CString key; STSStyle* s; m_styles.GetNextAssoc(pos, key, s); if(et == EXTSSA) { CString str2; str2.Format(str, key, s->fontName, (int)s->fontSize, s->colors[0]&0xffffff, s->colors[1]&0xffffff, s->colors[2]&0xffffff, s->colors[3]&0xffffff, s->fontWeight > FW_NORMAL ? -1 : 0, s->fItalic ? -1 : 0, s->borderStyle == 0 ? 1 : s->borderStyle == 1 ? 3 : 0, (int)s->outlineWidth, (int)s->shadowDepth, s->scrAlignment <= 3 ? s->scrAlignment : s->scrAlignment <= 6 ? ((s->scrAlignment-3)|8) : s->scrAlignment <= 9 ? ((s->scrAlignment-6)|4) : 2, s->marginRect.left, s->marginRect.right, (s->marginRect.top + s->marginRect.bottom) / 2, s->alpha[0], s->charSet); f.WriteString(str2); } else { CString str2; str2.Format(str, key, s->fontName, (int)s->fontSize, (s->colors[0]&0xffffff) | (s->alpha[0]<<24), (s->colors[1]&0xffffff) | (s->alpha[1]<<24), (s->colors[2]&0xffffff) | (s->alpha[2]<<24), (s->colors[3]&0xffffff) | (s->alpha[3]<<24), s->fontWeight > FW_NORMAL ? -1 : 0, s->fItalic ? -1 : 0, s->fUnderline ? -1 : 0, s->fStrikeOut ? -1 : 0, (int)s->fontScaleX, (int)s->fontScaleY, (int)s->fontSpacing, (float)s->fontAngleZ, s->borderStyle == 0 ? 1 : s->borderStyle == 1 ? 3 : 0, (int)s->outlineWidth, (int)s->shadowDepth, s->scrAlignment, s->marginRect.left, s->marginRect.right, (s->marginRect.top + s->marginRect.bottom) / 2, s->charSet); f.WriteString(str2); } } if(GetCount() > 0) { str = _T("\n"); str += _T("[Events]\n"); str += (et == EXTSSA) ? _T("Format: Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\n") : _T("Format: Layer, Start, End, Style, Actor, MarginL, MarginR, MarginV, Effect, Text\n"); f.WriteString(str); } } CStringW fmt = et == EXTSRT ? L"%d\n%02d:%02d:%02d,%03d --> %02d:%02d:%02d,%03d\n%s\n\n" : et == EXTSUB ? L"{%d}{%d}%s\n" : et == EXTSMI ? L"

\n%s\n

 \n" : et == EXTPSB ? L"{%d:%02d:%02d}{%d:%02d:%02d}%s\n" : et == EXTSSA ? L"Dialogue: Marked=0,%d:%02d:%02d.%02d,%d:%02d:%02d.%02d,%s,%s,%04d,%04d,%04d,%s,%s\n" : et == EXTASS ? L"Dialogue: %d,%d:%02d:%02d.%02d,%d:%02d:%02d.%02d,%s,%s,%04d,%04d,%04d,%s,%s\n" : L""; // Sort(true); for(int i = 0, j = GetCount(), k = 0; i < j; i++) { STSEntry& stse = GetAt(i); int t1 = TranslateStart(i, fps); if(t1 < 0) {k++; continue;} int t2 = TranslateEnd(i, fps); int hh1 = (t1/60/60/1000); int mm1 = (t1/60/1000)%60; int ss1 = (t1/1000)%60; int ms1 = (t1)%1000; int hh2 = (t2/60/60/1000); int mm2 = (t2/60/1000)%60; int ss2 = (t2/1000)%60; int ms2 = (t2)%1000; CStringW str = f.IsUnicode() ? GetStrW(i, et == EXTSSA || et == EXTASS) : GetStrWA(i, et == EXTSSA || et == EXTASS); CStringW str2; if(et == EXTSRT) { str2.Format(fmt, i-k+1, hh1, mm1, ss1, ms1, hh2, mm2, ss2, ms2, str); } else if(et == EXTSUB) { str.Replace('\n', '|'); str2.Format(fmt, int(t1*fps/1000), int(t2*fps/1000), str); } else if(et == EXTSMI) { str.Replace(L"\n", L"
"); str2.Format(fmt, t1, str, t2); } else if(et == EXTPSB) { str.Replace('\n', '|'); str2.Format(fmt, hh1, mm1, ss1, hh2, mm2, ss2, str); } else if(et == EXTSSA) { str.Replace(L"\n", L"\\N"); str2.Format(fmt, hh1, mm1, ss1, ms1/10, hh2, mm2, ss2, ms2/10, TToW(stse.style), TToW(stse.actor), stse.marginRect.left, stse.marginRect.right, (stse.marginRect.top + stse.marginRect.bottom) / 2, TToW(stse.effect), str); } else if(et == EXTASS) { str.Replace(L"\n", L"\\N"); str2.Format(fmt, stse.layer, hh1, mm1, ss1, ms1/10, hh2, mm2, ss2, ms2/10, TToW(stse.style), TToW(stse.actor), stse.marginRect.left, stse.marginRect.right, (stse.marginRect.top + stse.marginRect.bottom) / 2, TToW(stse.effect), str); } f.WriteString(str2); } // Sort(); if(et == EXTSMI) { f.WriteString(_T("\n\n")); } STSStyle* s; if(!m_fUsingAutoGeneratedDefaultStyle && m_styles.Lookup(_T("Default"), s) && et != EXTSSA && et != EXTASS) { CTextFile f; if(!f.Save(fn + _T(".style"), e)) return(false); CString str, str2; str += _T("ScriptType: v4.00+\n"); str += _T("PlayResX: %d\n"); str += _T("PlayResY: %d\n"); str += _T("\n"); 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"); str2.Format(str, m_dstScreenSize.cx, m_dstScreenSize.cy); f.WriteString(str2); 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"); str2.Format(str, s->fontName, (int)s->fontSize, (s->colors[0]&0xffffff) | (s->alpha[0]<<24), (s->colors[1]&0xffffff) | (s->alpha[1]<<24), (s->colors[2]&0xffffff) | (s->alpha[2]<<24), (s->colors[3]&0xffffff) | (s->alpha[3]<<24), s->fontWeight > FW_NORMAL ? -1 : 0, s->fItalic ? -1 : 0, s->fUnderline ? -1 : 0, s->fStrikeOut ? -1 : 0, (int)s->fontScaleX, (int)s->fontScaleY, (int)s->fontSpacing, (float)s->fontAngleZ, s->borderStyle == 0 ? 1 : s->borderStyle == 1 ? 3 : 0, (int)s->outlineWidth, (int)s->shadowDepth, s->scrAlignment, s->marginRect.left, s->marginRect.right, (s->marginRect.top + s->marginRect.bottom) / 2, s->charSet); f.WriteString(str2); } return(true); } //////////////////////////////////////////////////////////////////// STSStyle::STSStyle() { SetDefault(); } void STSStyle::SetDefault() { marginRect = CRect(20, 20, 20, 20); scrAlignment = 2; borderStyle = 0; outlineWidth = 2; shadowDepth = 3; colors[0] = 0x00ffffff; colors[1] = 0x0000ffff; colors[2] = 0x00000000; colors[3] = 0x00000000; alpha[0] = 0x00; alpha[1] = 0x00; alpha[2] = 0x00; alpha[3] = 0x80; charSet = DEFAULT_CHARSET; fontName = _T("Arial"); fontSize = 18; fontScaleX = fontScaleY = 100; fontSpacing = 0; fontWeight = FW_BOLD; fItalic = false; fUnderline = false; fStrikeOut = false; fBlur = false; fontShiftX = fontShiftY = fontAngleZ = fontAngleX = fontAngleY = 0; relativeTo = 2; } bool STSStyle::operator == (STSStyle& s) { return(marginRect == s.marginRect && scrAlignment == s.scrAlignment && borderStyle == s.borderStyle && outlineWidth == s.outlineWidth && shadowDepth == s.shadowDepth && *((int*)&colors[0]) == *((int*)&s.colors[0]) && *((int*)&colors[1]) == *((int*)&s.colors[1]) && *((int*)&colors[2]) == *((int*)&s.colors[2]) && *((int*)&colors[3]) == *((int*)&s.colors[3]) && alpha[0] == s.alpha[0] && alpha[1] == s.alpha[1] && alpha[2] == s.alpha[2] && alpha[3] == s.alpha[3] && fBlur == s.fBlur && relativeTo == s.relativeTo && IsFontStyleEqual(s)); } bool STSStyle::IsFontStyleEqual(STSStyle& s) { return( charSet == s.charSet && fontName == s.fontName && fontSize == s.fontSize && fontScaleX == s.fontScaleX && fontScaleY == s.fontScaleY && fontSpacing == s.fontSpacing && fontWeight == s.fontWeight && fItalic == s.fItalic && fUnderline == s.fUnderline && fStrikeOut == s.fStrikeOut && fontAngleZ == s.fontAngleZ && fontAngleX == s.fontAngleX && fontAngleY == s.fontAngleY); } void STSStyle::operator = (LOGFONT& lf) { charSet = lf.lfCharSet; fontName = lf.lfFaceName; HDC hDC = GetDC(0); //fontSize = -MulDiv(lf.lfHeight, 72, GetDeviceCaps(hDC, LOGPIXELSY)); fontSize = lf.lfHeight; ReleaseDC(0, hDC); // fontAngleZ = (float)(1.0*lf.lfEscapement/10); fontWeight = lf.lfWeight; fItalic = !!lf.lfItalic; fUnderline = !!lf.lfUnderline; fStrikeOut = !!lf.lfStrikeOut; } LOGFONTA& operator <<= (LOGFONTA& lfa, STSStyle& s) { lfa.lfCharSet = s.charSet; strncpy_s(lfa.lfFaceName, LF_FACESIZE, CStringA(s.fontName), _TRUNCATE); HDC hDC = GetDC(0); //lfa.lfHeight = -MulDiv((int)(s.fontSize+0.5), GetDeviceCaps(hDC, LOGPIXELSY), 72); lfa.lfHeight = (LONG)(s.fontSize + 0.5); ReleaseDC(0, hDC); lfa.lfWeight = s.fontWeight; lfa.lfItalic = s.fItalic?-1:0; lfa.lfUnderline = s.fUnderline?-1:0; lfa.lfStrikeOut = s.fStrikeOut?-1:0; return(lfa); } LOGFONTW& operator <<= (LOGFONTW& lfw, STSStyle& s) { lfw.lfCharSet = s.charSet; wcsncpy_s(lfw.lfFaceName, LF_FACESIZE, CStringW(s.fontName), _TRUNCATE); HDC hDC = GetDC(0); //lfw.lfHeight = -MulDiv((int)(s.fontSize+0.5), GetDeviceCaps(hDC, LOGPIXELSY), 72); lfw.lfHeight = (LONG)(s.fontSize + 0.5); ReleaseDC(0, hDC); lfw.lfWeight = s.fontWeight; lfw.lfItalic = s.fItalic?-1:0; lfw.lfUnderline = s.fUnderline?-1:0; lfw.lfStrikeOut = s.fStrikeOut?-1:0; return(lfw); } CString& operator <<= (CString& style, STSStyle& s) { style.Format(_T("%d,%d,%d,%d,%d,%d,%f,%f,0x%06x,0x%06x,0x%06x,0x%06x,0x%02x,0x%02x,0x%02x,0x%02x,%d,%s,%f,%f,%f,%f,%d,%d,%d,%d,%d,%f,%f,%f,%d"), s.marginRect.left,s.marginRect.right,s.marginRect.top,s.marginRect.bottom, s.scrAlignment, s.borderStyle, s.outlineWidth, s.shadowDepth, s.colors[0], s.colors[1], s.colors[2], s.colors[3], s.alpha[0], s.alpha[1], s.alpha[2], s.alpha[3], s.charSet, s.fontName, s.fontSize, s.fontScaleX, s.fontScaleY, s.fontSpacing, s.fontWeight, (int)s.fItalic, (int)s.fUnderline, (int)s.fStrikeOut, (int)s.fBlur, s.fontAngleZ, s.fontAngleX, s.fontAngleY, s.relativeTo); return(style); } STSStyle& operator <<= (STSStyle& s, CString& style) { s.SetDefault(); try { CStringW str = TToW(style); s.marginRect.left = GetInt(str); s.marginRect.right = GetInt(str); s.marginRect.top = GetInt(str); s.marginRect.bottom = GetInt(str); s.scrAlignment = GetInt(str); s.borderStyle = GetInt(str); s.outlineWidth = GetFloat(str); s.shadowDepth = GetFloat(str); for(int i = 0; i < 4; i++) s.colors[i] = (COLORREF)GetInt(str); for(int i = 0; i < 4; i++) s.alpha[i] = GetInt(str); s.charSet = GetInt(str); s.fontName = WToT(GetStr(str)); s.fontSize = GetFloat(str); s.fontScaleX = GetFloat(str); s.fontScaleY = GetFloat(str); s.fontSpacing = GetFloat(str); s.fontWeight = GetInt(str); s.fItalic = !!GetInt(str); s.fUnderline = !!GetInt(str); s.fStrikeOut = !!GetInt(str); s.fBlur = !!GetInt(str); s.fontAngleZ = GetFloat(str); s.fontAngleX = GetFloat(str); s.fontAngleY = GetFloat(str); s.relativeTo = GetInt(str); } catch(...) { s.SetDefault(); } return(s); }