1 // Copyright (c) 2014, Thomas Goyne <plorkyeran@aegisub.org>
2 //
3 // Permission to use, copy, modify, and distribute this software for any
4 // purpose with or without fee is hereby granted, provided that the above
5 // copyright notice and this permission notice appear in all copies.
6 //
7 // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10 // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12 // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13 // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14 //
15 // Aegisub Project http://www.aegisub.org/
16 
17 #include <libaegisub/ass/time.h>
18 #include <libaegisub/ass/smpte.h>
19 
20 #include <libaegisub/format.h>
21 #include <libaegisub/util.h>
22 
23 #include <algorithm>
24 #include <boost/algorithm/string/classification.hpp>
25 #include <boost/algorithm/string/split.hpp>
26 #include <boost/range/adaptor/filtered.hpp>
27 
28 namespace agi {
Time(int time)29 Time::Time(int time) : time(util::mid(0, time, 10 * 60 * 60 * 1000 - 1)) { }
30 
Time(std::string const & text)31 Time::Time(std::string const& text) {
32 	int after_decimal = -1;
33 	int current = 0;
34 	for (char c : text | boost::adaptors::filtered(boost::is_any_of(",.0123456789:"))) {
35 		if (c == ':') {
36 			time = time * 60 + current;
37 			current = 0;
38 		}
39 		else if (c == '.' || c == ',') {
40 			time = (time * 60 + current) * 1000;
41 			current = 0;
42 			after_decimal = 100;
43 		}
44 		else if (after_decimal < 0) {
45 			current *= 10;
46 			current += c - '0';
47 		}
48 		else {
49 			time += (c - '0') * after_decimal;
50 			after_decimal /= 10;
51 		}
52 	}
53 
54 	// Never saw a decimal, so convert now to ms
55 	if (after_decimal < 0)
56 		time = (time * 60 + current) * 1000;
57 
58 	// Limit to the valid range
59 	time = util::mid(0, time, 10 * 60 * 60 * 1000 - 1);
60 }
61 
GetAssFormatted(bool msPrecision) const62 std::string Time::GetAssFormatted(bool msPrecision) const {
63 	std::string ret(10 + msPrecision, ':');
64 	ret[0] = '0' + GetTimeHours();
65 	ret[2] = '0' + (time % (60 * 60 * 1000)) / (60 * 1000 * 10);
66 	ret[3] = '0' + (time % (10 * 60 * 1000)) / (60 * 1000);
67 	ret[5] = '0' + (time % (60 * 1000)) / (1000 * 10);
68 	ret[6] = '0' + (time % (10 * 1000)) / 1000;
69 	ret[7] = '.';
70 	ret[8] = '0' + (time % 1000) / 100;
71 	ret[9] = '0' + (time % 100) / 10;
72 	if (msPrecision)
73 		ret[10] = '0' + time % 10;
74 	return ret;
75 }
76 
GetTimeHours() const77 int Time::GetTimeHours() const { return time / 3600000; }
GetTimeMinutes() const78 int Time::GetTimeMinutes() const { return (time % 3600000) / 60000; }
GetTimeSeconds() const79 int Time::GetTimeSeconds() const { return (time % 60000) / 1000; }
GetTimeMiliseconds() const80 int Time::GetTimeMiliseconds() const { return (time % 1000); }
GetTimeCentiseconds() const81 int Time::GetTimeCentiseconds() const { return (time % 1000) / 10; }
82 
SmpteFormatter(vfr::Framerate fps,std::string sep)83 SmpteFormatter::SmpteFormatter(vfr::Framerate fps, std::string sep)
84 : fps(std::move(fps))
85 , sep(std::move(sep))
86 {
87 }
88 
ToSMPTE(Time time) const89 std::string SmpteFormatter::ToSMPTE(Time time) const {
90 	int h=0, m=0, s=0, f=0;
91 	fps.SmpteAtTime(time, &h, &m, &s, &f);
92 	return format("%02d%s%02d%s%02d%s%02d", h, sep, m, sep, s, sep, f);
93 }
94 
FromSMPTE(std::string const & str) const95 Time SmpteFormatter::FromSMPTE(std::string const& str) const {
96 	std::vector<std::string> toks;
97 	boost::split(toks, str, boost::is_any_of(sep));
98 	if (toks.size() != 4) return 0;
99 
100 	int h, m, s, f;
101 	util::try_parse(toks[0], &h);
102 	util::try_parse(toks[1], &m);
103 	util::try_parse(toks[2], &s);
104 	util::try_parse(toks[3], &f);
105 	return fps.TimeAtSmpte(h, m, s, f);
106 }
107 }
108