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