1/*
2 * Copyright 2010, Stephan A��mus <superstippi@gmx.de>. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "SubTitlesSRT.h"
8
9#include <new>
10
11#include <stdlib.h>
12
13#include <File.h>
14
15#include "FileReadWrite.h"
16
17
18SubTitlesSRT::SubTitlesSRT(BFile* file, const char* name)
19	:
20	SubTitles(),
21	fName(name),
22	fSubTitles(64)
23{
24	if (file == NULL)
25		return;
26	if (file->InitCheck() != B_OK)
27		return;
28
29	FileReadWrite lineProvider(file);
30	BString line;
31	enum {
32		EXPECT_SEQUENCE_NUMBER = 0,
33		EXPECT_TIME_CODE,
34		EXPECT_TEXT
35	};
36
37	SubTitle subTitle;
38	int32 lastSequenceNumber = 0;
39	int32 currentLine = 0;
40
41	int32 state = EXPECT_SEQUENCE_NUMBER;
42	while (lineProvider.Next(line)) {
43		line.RemoveAll("\n");
44		line.RemoveAll("\r");
45		switch (state) {
46			case EXPECT_SEQUENCE_NUMBER:
47			{
48				if (line.IsEmpty())
49					continue;
50
51				line.Trim();
52				int32 sequenceNumber = atoi(line.String());
53				if (sequenceNumber != lastSequenceNumber + 1) {
54					fprintf(stderr, "Warning: Wrong sequence number in SRT "
55						"file: %ld, expected: %ld, line %ld\n", sequenceNumber,
56						lastSequenceNumber + 1, currentLine);
57				}
58				state = EXPECT_TIME_CODE;
59				lastSequenceNumber = sequenceNumber;
60				break;
61			}
62
63			case EXPECT_TIME_CODE:
64			{
65				line.Trim();
66				int32 separatorPos = line.FindFirst(" --> ");
67				if (separatorPos < 0) {
68					fprintf(stderr, "Error: Time code expected on line %ld, "
69						"got '%s'\n", currentLine, line.String());
70					return;
71				}
72				BString timeCode(line.String(), separatorPos);
73				if (separatorPos != 12) {
74					fprintf(stderr, "Warning: Time code broken on line %ld "
75						"(%s)?\n", currentLine, timeCode.String());
76				}
77				int hours;
78				int minutes;
79				int seconds;
80				int milliSeconds;
81				if (sscanf(timeCode.String(), "%d:%d:%d,%d", &hours, &minutes,
82					&seconds, &milliSeconds) != 4) {
83					fprintf(stderr, "Error: Failed to parse start time on "
84						"line %ld\n", currentLine);
85					return;
86				}
87				subTitle.startTime = (bigtime_t)hours * 60 * 60 * 1000000LL
88					+ (bigtime_t)minutes * 60 * 1000000LL
89					+ (bigtime_t)seconds * 1000000LL
90					+ (bigtime_t)milliSeconds * 1000;
91
92				int32 endTimePos = separatorPos + 5;
93				timeCode.SetTo(line.String() + endTimePos);
94				if (sscanf(timeCode.String(), "%d:%d:%d,%d", &hours, &minutes,
95					&seconds, &milliSeconds) != 4) {
96					fprintf(stderr, "Error: Failed to parse end time on "
97						"line %ld\n", currentLine);
98					return;
99				}
100				bigtime_t endTime = (bigtime_t)hours * 60 * 60 * 1000000LL
101					+ (bigtime_t)minutes * 60 * 1000000LL
102					+ (bigtime_t)seconds * 1000000LL
103					+ (bigtime_t)milliSeconds * 1000;
104
105				subTitle.duration = endTime - subTitle.startTime;
106
107				state = EXPECT_TEXT;
108				break;
109			}
110
111			case EXPECT_TEXT:
112				if (line.IsEmpty()) {
113					int32 index = _IndexFor(subTitle.startTime);
114					SubTitle* clone = new(std::nothrow) SubTitle(subTitle);
115					if (clone == NULL || !fSubTitles.AddItem(clone, index)) {
116						delete clone;
117						return;
118					}
119					subTitle.text = "";
120					subTitle.placement = BPoint(-1, -1);
121					subTitle.startTime = 0;
122					subTitle.duration = 0;
123
124					state = EXPECT_SEQUENCE_NUMBER;
125				} else
126					subTitle.text << line << '\n';
127				break;
128		}
129		line.SetTo("");
130		currentLine++;
131	}
132}
133
134
135SubTitlesSRT::~SubTitlesSRT()
136{
137	for (int32 i = fSubTitles.CountItems() - 1; i >= 0; i--)
138		delete reinterpret_cast<SubTitle*>(fSubTitles.ItemAtFast(i));
139}
140
141
142const char*
143SubTitlesSRT::Name() const
144{
145	return fName.String();
146}
147
148
149const SubTitle*
150SubTitlesSRT::SubTitleAt(bigtime_t time) const
151{
152	int32 index = _IndexFor(time);
153	SubTitle* subTitle
154		= reinterpret_cast<SubTitle*>(fSubTitles.ItemAt(index));
155	if (subTitle != NULL && subTitle->startTime > time)
156		subTitle = reinterpret_cast<SubTitle*>(fSubTitles.ItemAt(index - 1));
157	if (subTitle != NULL && subTitle->startTime <= time
158		&& subTitle->startTime + subTitle->duration > time) {
159		return subTitle;
160	}
161	return NULL;
162}
163
164
165int32
166SubTitlesSRT::_IndexFor(bigtime_t startTime) const
167{
168	// binary search index
169	int32 lower = 0;
170	int32 upper = fSubTitles.CountItems();
171	while (lower < upper) {
172		int32 mid = (lower + upper) / 2;
173		SubTitle* subTitle = reinterpret_cast<SubTitle*>(
174			fSubTitles.ItemAtFast(mid));
175		if (startTime < subTitle->startTime)
176			upper = mid;
177		else
178			lower = mid + 1;
179	}
180	return lower;
181}
182
183
184
185