Aegisub/vsfilter/subtitles/STS.cpp
Niels Martin Hansen 13654219c2 Implemented four new tags: \xbord \ybord \xshad \yshad
Allows setting the border in X and Y direction separately, and the shadow displacement in X and Y direction separately.
The \xshad and \yshad tags also allow negative shadow displacement values. Negative shadow is still not allowed in any of the traditional locations.

Originally committed to SVN as r2278.
2008-07-22 21:42:36 +00:00

3042 lines
69 KiB
C++

/*
* 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 <atlbase.h>
// 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"<i>", L"{\\i1}");
str.Replace(L"</i>", L"{\\i}");
str.Replace(L"<b>", L"{\\b1}");
str.Replace(L"</b>", L"{\\b}");
str.Replace(L"<u>", L"{\\u1}");
str.Replace(L"</u>", 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"&nbsp;", L" ");
ReplaceNoCase(str, L"&quot;", L"\"");
ReplaceNoCase(str, L"<br>", L"\\N");
ReplaceNoCase(str, L"<i>", L"{\\i1}");
ReplaceNoCase(str, L"</i>", L"{\\i}");
ReplaceNoCase(str, L"<b>", L"{\\b1}");
ReplaceNoCase(str, L"</b>", 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"<font ", k) == k)
{
CStringW args = lstr.Mid(k+6, l-6); // delete "<font "
CStringW arg ;
args.Remove('\"'); args.Remove('#'); // may include 2 * " + #
arg.TrimLeft(); arg.TrimRight(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"<font color=", k) == k)
{
CStringW arg = lstr.Mid(k+12, l-12); // may include 2 * " + #
arg.Remove('\"');
arg.Remove('#');
arg.TrimLeft(); arg.TrimRight(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"</font>", 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"<SAMI>") >= 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"<TITLE>") >= 0)
fComment = true;
if(!fComment)
{
int i;
if((i = ubuff.Find(L"<SYNC START=")) >= 0)
{
int time = 0;
for(i = 12; i < ubuff.GetLength(); i++)
{
if(ubuff[i] != '>' && ubuff[i] != 'M')
{
if(iswdigit(ubuff[i]))
{
time *= 10;
time += ubuff[i] - 0x30;
}
}
else break;
}
ret.Add(
SMI2SSA(caption, CharSet),
file->IsUnicode(),
start_time, time);
start_time = time;
caption.Empty();
}
caption += buff;
}
if(ubuff.Find(L"-->") >= 0 || ubuff.Find(L"</TITLE>") >= 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<BYTE> 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->outlineWidthX = style->outlineWidthY = GetFloat(buff);
style->shadowDepthX = style->shadowDepthY = 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->outlineWidthX = max(style->outlineWidthX, 0);
style->outlineWidthY = max(style->outlineWidthY, 0);
style->shadowDepthX = max(style->shadowDepthX, 0);
style->shadowDepthY = max(style->shadowDepthY, 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->outlineWidthX = style->outlineWidthY = GetFloat(buff);
style->shadowDepthX = style->shadowDepthY = 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->outlineWidthX = max(style->outlineWidthX, 0);
style->outlineWidthY = max(style->outlineWidthY, 0);
style->shadowDepthX = max(style->shadowDepthX, 0);
style->shadowDepthY = max(style->shadowDepthY, 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<CStringW> 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<CString, STSStyle*, CStringElementTraits<CString> > 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<int> 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("<SAMI>\n<HEAD>\n");
str += _T("<STYLE TYPE=\"text/css\">\n");
str += _T("<!--\n");
str += _T("P {margin-left: 16pt; margin-right: 16pt; margin-bottom: 16pt; margin-top: 16pt;\n");
str += _T(" text-align: center; font-size: 18pt; font-family: arial; font-weight: bold; color: #f0f0f0;}\n");
str += _T(".UNKNOWNCC {Name:Unknown; lang:en-US; SAMIType:CC;}\n");
str += _T("-->\n");
str += _T("</STYLE>\n");
str += _T("</HEAD>\n");
str += _T("\n");
str += _T("<BODY>\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->outlineWidthY, (int)s->shadowDepthY,
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->outlineWidthY, (int)s->shadowDepthY,
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"<SYNC Start=%d><P Class=UNKNOWNCC>\n%s\n<SYNC Start=%d><P Class=UNKNOWNCC>&nbsp;\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"<br>");
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("</BODY>\n</SAMI>\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->outlineWidthY, (int)s->shadowDepthY,
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;
outlineWidthX = outlineWidthY = 2;
shadowDepthX = shadowDepthY = 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
&& outlineWidthX == s.outlineWidthX
&& outlineWidthY == s.outlineWidthY
&& shadowDepthX == s.shadowDepthX
&& shadowDepthY == s.shadowDepthY
&& *((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.outlineWidthY, s.shadowDepthY,
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.outlineWidthX = s.outlineWidthY = GetFloat(str); s.shadowDepthX = s.shadowDepthY = 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);
}