/* * 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 "SubtitleFile.h" namespace ssf { SubtitleFile::SubtitleFile() { } SubtitleFile::~SubtitleFile() { } void SubtitleFile::Parse(InputStream& s) { m_segments.RemoveAll(); __super::Parse(s, s_predef); // TODO: check file.format == "ssf" and file.version == 1 CAtlList defs; GetRootRef()->GetChildDefs(defs, L"subtitle"); StringMapW offset; POSITION pos = defs.GetHeadPosition(); while(pos) { Definition* pDef = defs.GetNext(pos); try { Definition::Time time; if(pDef->GetAsTime(time, offset) && (*pDef)[L"@"].IsValue()) { m_segments.Insert(time.start.value, time.stop.value, pDef); } } catch(Exception&) { } } } void SubtitleFile::Append(InputStream& s, float start, float stop, bool fSetTime) { Reference* pRootRef = GetRootRef(); ParseDefs(s, pRootRef); CAtlList defs; GetNewDefs(defs); POSITION pos = defs.GetHeadPosition(); while(pos) { Definition* pDef = defs.GetNext(pos); if(pDef->m_parent == pRootRef && pDef->m_type == L"subtitle" && (*pDef)[L"@"].IsValue()) { m_segments.Insert(start, stop, pDef); if(fSetTime) { try { Definition::Time time; StringMapW offset; pDef->GetAsTime(time, offset); if(time.start.value == start && time.stop.value == stop) continue; } catch(Exception&) { } CStringW str; str.Format(L"%.3f", start); pDef->SetChildAsNumber(L"time.start", str, L"s"); str.Format(L"%.3f", stop); pDef->SetChildAsNumber(L"time.stop", str, L"s"); } } } Commit(); } bool SubtitleFile::Lookup(float at, CAutoPtrList& subs) { if(!subs.IsEmpty()) {ASSERT(0); return false;} CAtlList sis; m_segments.Lookup(at, sis); POSITION pos = sis.GetHeadPosition(); while(pos) { SegmentItem& si = sis.GetNext(pos); CAutoPtr s(new Subtitle(this)); if(s->Parse(si.pDef, si.start, si.stop, at)) { for(POSITION pos = subs.GetHeadPosition(); pos; subs.GetNext(pos)) { if(s->m_layer < subs.GetAt(pos)->m_layer) { subs.InsertBefore(pos, s); break; } } if(s) { subs.AddTail(s); } } } return !subs.IsEmpty(); } // SubtitleFile::Segment::Segment(float start, float stop, const SegmentItem* si) { m_start = start; m_stop = stop; if(si) AddTail(*si); } SubtitleFile::Segment::Segment(const Segment& s) { *this = s; } void SubtitleFile::Segment::operator = (const Segment& s) { m_start = s.m_start; m_stop = s.m_stop; RemoveAll(); AddTailList(&s); } // void SubtitleFile::SegmentList::RemoveAll() { __super::RemoveAll(); m_index.RemoveAll(); } void SubtitleFile::SegmentList::Insert(float start, float stop, Definition* pDef) { if(start >= stop) {ASSERT(0); return;} m_index.RemoveAll(); SegmentItem si = {pDef, start, stop}; if(IsEmpty()) { AddTail(Segment(start, stop, &si)); return; } Segment& head = GetHead(); Segment& tail = GetTail(); if(start >= tail.m_stop && stop > tail.m_stop) { if(start > tail.m_stop) AddTail(Segment(tail.m_stop, start)); AddTail(Segment(start, stop, &si)); } else if(start < head.m_start && stop <= head.m_start) { if(stop < head.m_start) AddHead(Segment(stop, head.m_start)); AddHead(Segment(start, stop, &si)); } else { if(start < head.m_start) { AddHead(Segment(start, head.m_start, &si)); start = head.m_start; } if(stop > tail.m_stop) { AddTail(Segment(tail.m_stop, stop, &si)); stop = tail.m_stop; } for(POSITION pos = GetHeadPosition(); pos; GetNext(pos)) { Segment& s = GetAt(pos); if(start >= s.m_stop) continue; if(stop <= s.m_start) break; if(s.m_start < start && start < s.m_stop) { Segment s2 = s; s2.m_start = start; InsertAfter(pos, s2); s.m_stop = start; } else if(s.m_start == start) { if(stop > s.m_stop) { start = s.m_stop; } else if(stop < s.m_stop) { Segment s2 = s; s2.m_start = stop; InsertAfter(pos, s2); s.m_stop = stop; } s.AddTail(si); } } } } size_t SubtitleFile::SegmentList::Index(bool fForce) { if(m_index.IsEmpty() || fForce) { m_index.RemoveAll(); POSITION pos = GetHeadPosition(); while(pos) m_index.Add(&GetNext(pos)); } return m_index.GetCount(); } void SubtitleFile::SegmentList::Lookup(float at, CAtlList& sis) { sis.RemoveAll(); size_t k; if(Lookup(at, k)) { sis.AddTailList(GetSegment(k)); } } bool SubtitleFile::SegmentList::Lookup(float at, size_t& k) { if(!Index()) return false; size_t i = 0, j = m_index.GetCount()-1; if(m_index[i]->m_start <= at && at < m_index[j]->m_stop) do { k = (i+j)/2; if(m_index[k]->m_start <= at && at < m_index[k]->m_stop) {return true;} else if(at < m_index[k]->m_start) {if(j == k) k--; j = k;} else if(at >= m_index[k]->m_stop) {if(i == k) k++; i = k;} } while(i <= j); return false; } const SubtitleFile::Segment* SubtitleFile::SegmentList::GetSegment(size_t k) { return 0 <= k && k < m_index.GetCount() ? m_index[k] : NULL; } // TODO: this should be overridable from outside LPCWSTR SubtitleFile::s_predef = L"color#white {a: 255; r: 255; g: 255; b: 255;}; \n" L"color#black {a: 255; r: 0; g: 0; b: 0;}; \n" L"color#gray {a: 255; r: 128; g: 128; b: 128;}; \n" L"color#red {a: 255; r: 255; g: 0; b: 0;}; \n" L"color#green {a: 255; r: 0; g: 255; b: 0;}; \n" L"color#blue {a: 255; r: 0; g: 0; b: 255;}; \n" L"color#cyan {a: 255; r: 0; g: 255; b: 255;}; \n" L"color#yellow {a: 255; r: 255; g: 255; b: 0;}; \n" L"color#magenta {a: 255; r: 255; g: 0; b: 255;}; \n" L" \n" L"align#topleft {v: \"top\"; h: \"left\";}; \n" L"align#topcenter {v: \"top\"; h: \"center\";}; \n" L"align#topright {v: \"top\"; h: \"right\";}; \n" L"align#middleleft {v: \"middle\"; h: \"left\";}; \n" L"align#middlecenter {v: \"middle\"; h: \"center\";}; \n" L"align#middleright {v: \"middle\"; h: \"right\";}; \n" L"align#bottomleft {v: \"bottom\"; h: \"left\";}; \n" L"align#bottomcenter {v: \"bottom\"; h: \"center\";}; \n" L"align#bottomright {v: \"bottom\"; h: \"right\";}; \n" L" \n" L"time#time {scale: 1;}; \n" L"time#startstop {start: \"start\"; stop: \"stop\";}; \n" L" \n" L"#b {font.weight: \"bold\"}; \n" L"#i {font.italic: \"true\"}; \n" L"#u {font.underline: \"true\"}; \n" L"#s {font.strikethrough: \"true\"}; \n" L" \n" L"#nobr {linebreak: \"none\"}; \n" L" \n" L"subtitle#subtitle \n" L"{ \n" L" frame \n" L" { \n" L" reference: \"video\"; \n" L" resolution: {cx: 640; cy: 480;}; \n" L" }; \n" L" \n" L" direction \n" L" { \n" L" primary: \"right\"; \n" L" secondary: \"down\"; \n" L" }; \n" L" \n" L" wrap: \"normal\"; \n" L" \n" L" layer: 0; \n" L" \n" L" style \n" L" { \n" L" linebreak: \"word\"; \n" L" \n" L" placement \n" L" { \n" L" clip: \"none\"; \n" L" margin: {t: 0; r: 0; b: 0; l: 0;}; \n" L" align: bottomcenter; \n" L" pos: \"auto\" \n" L" offset: {x: 0; y: 0;}; \n" L" angle: {x: 0; y: 0; z: 0;}; \n" L" org: \"auto\" \n" L" path: \"\"; \n" L" }; \n" L" \n" L" font \n" L" { \n" L" face: \"Arial\"; \n" L" size: 20; \n" L" weight: \"bold\"; \n" L" color: white; \n" L" underline: \"false\"; \n" L" strikethrough: \"false\"; \n" L" italic: \"false\"; \n" L" spacing: 0; \n" L" scale: {cx: 1; cy: 1;}; \n" L" kerning: \"true\"; \n" L" }; \n" L" \n" L" background \n" L" { \n" L" color: black; \n" L" size: 2; \n" L" type: \"outline\"; \n" L" blur: 0; \n" L" }; \n" L" \n" L" shadow \n" L" { \n" L" color: black {a: 128;}; \n" L" depth: 2; \n" L" angle: -45; \n" L" blur: 0; \n" L" }; \n" L" \n" L" fill \n" L" { \n" L" color: yellow; \n" L" width: 0; \n" L" }; \n" L" }; \n" L"}; \n"; }