Allow binding commands to class instance
[lsnes.git] / src / core / subtitles.cpp
blobee4bce4f57d1f08e710151023175f41d70d60c59
1 #include "cmdhelp/subtitles.hpp"
2 #include "core/command.hpp"
3 #include "core/dispatch.hpp"
4 #include "core/framebuffer.hpp"
5 #include "core/instance.hpp"
6 #include "core/messages.hpp"
7 #include "core/movie.hpp"
8 #include "core/moviefile.hpp"
9 #include "core/subtitles.hpp"
10 #include "fonts/wrapper.hpp"
11 #include "library/string.hpp"
12 #include "lua/lua.hpp"
14 #include <fstream>
16 moviefile_subtiming::moviefile_subtiming(uint64_t _frame)
18 position_only = true;
19 frame = _frame;
20 length = 0;
23 moviefile_subtiming::moviefile_subtiming(uint64_t first, uint64_t _length)
25 position_only = false;
26 frame = first;
27 length = _length;
30 bool moviefile_subtiming::operator<(const moviefile_subtiming& a) const
32 //This goes in inverse order due to behaviour of lower_bound/upper_bound.
33 if(frame > a.frame)
34 return true;
35 if(frame < a.frame)
36 return false;
37 if(position_only && a.position_only)
38 return false;
39 //Position only compares greater than any of same frame.
40 if(position_only != a.position_only)
41 return position_only;
42 //Break ties on length.
43 return (length > a.length);
46 bool moviefile_subtiming::operator==(const moviefile_subtiming& a) const
48 if(frame != a.frame)
49 return false;
50 if(position_only && a.position_only)
51 return true;
52 if(position_only != a.position_only)
53 return false;
54 return (length != a.length);
57 bool moviefile_subtiming::inrange(uint64_t x) const
59 if(position_only)
60 return false;
61 return (x >= frame && x < frame + length);
64 uint64_t moviefile_subtiming::get_frame() const { return frame; }
65 uint64_t moviefile_subtiming::get_length() const { return length; }
67 namespace
69 std::string s_subescape(std::string x)
71 std::string y;
72 for(size_t i = 0; i < x.length(); i++) {
73 char ch = x[i];
74 if(ch == '\n')
75 y += "|";
76 else if(ch == '|')
77 y += "⎢";
78 else
79 y.append(1, ch);
81 return y;
84 struct render_object_subtitle : public framebuffer::object
86 render_object_subtitle(int32_t _x, int32_t _y, const std::string& _text) throw()
87 : x(_x), y(_y), text(_text), fg(0xFFFF80), bg(-1) {}
88 ~render_object_subtitle() throw() {}
89 template<bool X> void op(struct framebuffer::fb<X>& scr) throw()
91 main_font.render(scr, x, y, text, fg, bg, false, false);
93 void operator()(struct framebuffer::fb<true>& scr) throw() { op(scr); }
94 void operator()(struct framebuffer::fb<false>& scr) throw() { op(scr); }
95 void clone(framebuffer::queue& q) const throw(std::bad_alloc) { q.clone_helper(this); }
96 private:
97 int32_t x;
98 int32_t y;
99 std::string text;
100 framebuffer::color fg;
101 framebuffer::color bg;
105 subtitle_commentary::subtitle_commentary(movie_logic& _mlogic, emu_framebuffer& _fbuf, emulator_dispatch& _dispatch,
106 command::group& _cmd)
107 : mlogic(_mlogic), fbuf(_fbuf), edispatch(_dispatch), cmd(_cmd),
108 editsub(cmd, STUBS::editsub, [this](const std::string& a) { this->do_editsub(a); }),
109 listsub(cmd, STUBS::listsub, [this]() { this->do_listsub(); }),
110 savesub(cmd, STUBS::savesub, [this](command::arg_filename a) { this->do_savesub(a); })
114 std::string subtitle_commentary::s_escape(std::string x)
116 std::string y;
117 for(size_t i = 0; i < x.length(); i++) {
118 char ch = x[i];
119 if(ch == '\n')
120 y += "\\n";
121 else if(ch == '\\')
122 y += "\\";
123 else
124 y.append(1, ch);
126 return y;
129 std::string subtitle_commentary::s_unescape(std::string x)
131 bool escape = false;
132 std::string y;
133 for(size_t i = 0; i < x.length(); i++) {
134 char ch = x[i];
135 if(escape) {
136 if(ch == 'n')
137 y.append(1, '\n');
138 if(ch == '\\')
139 y.append(1, '\\');
140 escape = false;
141 } else {
142 if(ch == '\\')
143 escape = true;
144 else
145 y.append(1, ch);
148 return y;
151 void subtitle_commentary::render(lua::render_context& ctx)
153 if(!mlogic || mlogic.get_mfile().subtitles.empty())
154 return;
155 if(ctx.bottom_gap < 32)
156 ctx.bottom_gap = 32;
157 uint64_t curframe = mlogic.get_movie().get_current_frame() + 1;
158 moviefile_subtiming posmarker(curframe);
159 auto i = mlogic.get_mfile().subtitles.upper_bound(posmarker);
160 if(i != mlogic.get_mfile().subtitles.end() && i->first.inrange(curframe)) {
161 std::string subtxt = i->second;
162 int32_t y = ctx.height;
163 ctx.queue->create_add<render_object_subtitle>(0, y, subtxt);
167 std::set<std::pair<uint64_t, uint64_t>> subtitle_commentary::get_all()
169 std::set<std::pair<uint64_t, uint64_t>> r;
170 if(!mlogic)
171 return r;
172 for(auto i = mlogic.get_mfile().subtitles.rbegin(); i !=
173 mlogic.get_mfile().subtitles.rend(); i++)
174 r.insert(std::make_pair(i->first.get_frame(), i->first.get_length()));
175 return r;
178 std::string subtitle_commentary::get(uint64_t f, uint64_t l)
180 if(!mlogic)
181 return "";
182 moviefile_subtiming key(f, l);
183 if(!mlogic.get_mfile().subtitles.count(key))
184 return "";
185 else
186 return s_escape(mlogic.get_mfile().subtitles[key]);
189 void subtitle_commentary::set(uint64_t f, uint64_t l, const std::string& x)
191 if(!mlogic)
192 return;
193 moviefile_subtiming key(f, l);
194 if(x == "")
195 mlogic.get_mfile().subtitles.erase(key);
196 else
197 mlogic.get_mfile().subtitles[key] = s_unescape(x);
198 edispatch.subtitle_change();
199 fbuf.redraw_framebuffer();
202 void subtitle_commentary::do_editsub(const std::string& args)
204 auto r = regex("([0-9]+)[ \t]+([0-9]+)([ \t]+(.*))?", args, "Bad syntax");
205 uint64_t frame = parse_value<uint64_t>(r[1]);
206 uint64_t length = parse_value<uint64_t>(r[2]);
207 std::string text = r[4];
208 moviefile_subtiming key(frame, length);
209 if(text == "")
210 mlogic.get_mfile().subtitles.erase(key);
211 else
212 mlogic.get_mfile().subtitles[key] =
213 subtitle_commentary::s_unescape(text);
214 edispatch.subtitle_change();
215 fbuf.redraw_framebuffer();
218 void subtitle_commentary::do_listsub()
220 for(auto i = mlogic.get_mfile().subtitles.rbegin(); i !=
221 mlogic.get_mfile().subtitles.rend();
222 i++) {
223 messages << i->first.get_frame() << " " << i->first.get_length() << " "
224 << subtitle_commentary::s_escape(i->second) << std::endl;
228 void subtitle_commentary::do_savesub(const std::string& args)
230 if(mlogic.get_mfile().subtitles.empty())
231 return;
232 auto i = mlogic.get_mfile().subtitles.begin();
233 uint64_t lastframe = i->first.get_frame() + i->first.get_length();
234 std::ofstream y(std::string(args).c_str());
235 if(!y)
236 throw std::runtime_error("Can't open output file");
237 std::string lasttxt = "";
238 uint64_t since = 0;
239 for(uint64_t i = 1; i < lastframe; i++) {
240 moviefile_subtiming posmarker(i);
241 auto j = mlogic.get_mfile().subtitles.upper_bound(posmarker);
242 if(j == mlogic.get_mfile().subtitles.end())
243 continue;
244 if(lasttxt != j->second || !j->first.inrange(i)) {
245 if(lasttxt != "")
246 y << "{" << since << "}{" << i - 1 << "}" << s_subescape(lasttxt)
247 << std::endl;
248 since = i;
249 lasttxt = j->first.inrange(i) ? j->second : "";
252 if(lasttxt != "")
253 y << "{" << since << "}{" << lastframe - 1 << "}" << s_subescape(lasttxt)
254 << std::endl;
255 messages << "Saved subtitles to " << std::string(args) << std::endl;