Make various instance stuff to take references to other instance objs
[lsnes.git] / src / core / subtitles.cpp
blob6f0f113c9883cd8481165dc6bf13e1fed0a6dd5f
1 #include "core/command.hpp"
2 #include "core/dispatch.hpp"
3 #include "core/framebuffer.hpp"
4 #include "core/instance.hpp"
5 #include "core/moviedata.hpp"
6 #include "core/subtitles.hpp"
7 #include "core/window.hpp"
8 #include "library/string.hpp"
9 #include "fonts/wrapper.hpp"
10 #include <fstream>
12 moviefile_subtiming::moviefile_subtiming(uint64_t _frame)
14 position_only = true;
15 frame = _frame;
16 length = 0;
19 moviefile_subtiming::moviefile_subtiming(uint64_t first, uint64_t _length)
21 position_only = false;
22 frame = first;
23 length = _length;
26 bool moviefile_subtiming::operator<(const moviefile_subtiming& a) const
28 //This goes in inverse order due to behaviour of lower_bound/upper_bound.
29 if(frame > a.frame)
30 return true;
31 if(frame < a.frame)
32 return false;
33 if(position_only && a.position_only)
34 return false;
35 //Position only compares greater than any of same frame.
36 if(position_only != a.position_only)
37 return position_only;
38 //Break ties on length.
39 return (length > a.length);
42 bool moviefile_subtiming::operator==(const moviefile_subtiming& a) const
44 if(frame != a.frame)
45 return false;
46 if(position_only && a.position_only)
47 return true;
48 if(position_only != a.position_only)
49 return false;
50 return (length != a.length);
53 bool moviefile_subtiming::inrange(uint64_t x) const
55 if(position_only)
56 return false;
57 return (x >= frame && x < frame + length);
60 uint64_t moviefile_subtiming::get_frame() const { return frame; }
61 uint64_t moviefile_subtiming::get_length() const { return length; }
63 namespace
65 std::string s_subescape(std::string x)
67 std::string y;
68 for(size_t i = 0; i < x.length(); i++) {
69 char ch = x[i];
70 if(ch == '\n')
71 y += "|";
72 else if(ch == '|')
73 y += "⎢";
74 else
75 y.append(1, ch);
77 return y;
80 struct render_object_subtitle : public framebuffer::object
82 render_object_subtitle(int32_t _x, int32_t _y, const std::string& _text) throw()
83 : x(_x), y(_y), text(_text), fg(0xFFFF80), bg(-1) {}
84 ~render_object_subtitle() throw() {}
85 template<bool X> void op(struct framebuffer::fb<X>& scr) throw()
87 main_font.render(scr, x, y, text, fg, bg, false, false);
89 void operator()(struct framebuffer::fb<true>& scr) throw() { op(scr); }
90 void operator()(struct framebuffer::fb<false>& scr) throw() { op(scr); }
91 void clone(framebuffer::queue& q) const throw(std::bad_alloc) { q.clone_helper(this); }
92 private:
93 int32_t x;
94 int32_t y;
95 std::string text;
96 framebuffer::color fg;
97 framebuffer::color bg;
101 command::fnptr<const std::string&> edit_subtitle(lsnes_cmds, "edit-subtitle", "Edit a subtitle",
102 "Syntax: edit-subtitle <first> <length> <text>\nAdd/Edit subtitle\n"
103 "Syntax: edit-subtitle <first> <length>\nADelete subtitle\n",
104 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
105 auto r = regex("([0-9]+)[ \t]+([0-9]+)([ \t]+(.*))?", args, "Bad syntax");
106 uint64_t frame = parse_value<uint64_t>(r[1]);
107 uint64_t length = parse_value<uint64_t>(r[2]);
108 std::string text = r[4];
109 moviefile_subtiming key(frame, length);
110 if(text == "")
111 CORE().mlogic.get_mfile().subtitles.erase(key);
112 else
113 CORE().mlogic.get_mfile().subtitles[key] =
114 subtitle_commentary::s_unescape(text);
115 notify_subtitle_change();
116 CORE().fbuf.redraw_framebuffer();
119 command::fnptr<> list_subtitle(lsnes_cmds, "list-subtitle", "List the subtitles",
120 "Syntax: list-subtitle\nList the subtitles.\n",
121 []() throw(std::bad_alloc, std::runtime_error) {
122 for(auto i = CORE().mlogic.get_mfile().subtitles.rbegin(); i !=
123 CORE().mlogic.get_mfile().subtitles.rend();
124 i++) {
125 messages << i->first.get_frame() << " " << i->first.get_length() << " "
126 << subtitle_commentary::s_escape(i->second) << std::endl;
130 command::fnptr<command::arg_filename> save_s(lsnes_cmds, "save-subtitle", "Save subtitles in .sub format",
131 "Syntax: save-subtitle <file>\nSaves subtitles in .sub format to <file>\n",
132 [](command::arg_filename args) throw(std::bad_alloc, std::runtime_error) {
133 if(CORE().mlogic.get_mfile().subtitles.empty())
134 return;
135 auto i = CORE().mlogic.get_mfile().subtitles.begin();
136 uint64_t lastframe = i->first.get_frame() + i->first.get_length();
137 std::ofstream y(std::string(args).c_str());
138 if(!y)
139 throw std::runtime_error("Can't open output file");
140 std::string lasttxt = "";
141 uint64_t since = 0;
142 for(uint64_t i = 1; i < lastframe; i++) {
143 moviefile_subtiming posmarker(i);
144 auto j = CORE().mlogic.get_mfile().subtitles.upper_bound(posmarker);
145 if(j == CORE().mlogic.get_mfile().subtitles.end())
146 continue;
147 if(lasttxt != j->second || !j->first.inrange(i)) {
148 if(lasttxt != "")
149 y << "{" << since << "}{" << i - 1 << "}" << s_subescape(lasttxt)
150 << std::endl;
151 since = i;
152 lasttxt = j->first.inrange(i) ? j->second : "";
155 if(lasttxt != "")
156 y << "{" << since << "}{" << lastframe - 1 << "}" << s_subescape(lasttxt)
157 << std::endl;
158 messages << "Saved subtitles to " << std::string(args) << std::endl;
162 subtitle_commentary::subtitle_commentary(movie_logic& _mlogic, emu_framebuffer& _fbuf)
163 : mlogic(_mlogic), fbuf(_fbuf)
167 std::string subtitle_commentary::s_escape(std::string x)
169 std::string y;
170 for(size_t i = 0; i < x.length(); i++) {
171 char ch = x[i];
172 if(ch == '\n')
173 y += "\\n";
174 else if(ch == '\\')
175 y += "\\";
176 else
177 y.append(1, ch);
179 return y;
182 std::string subtitle_commentary::s_unescape(std::string x)
184 bool escape = false;
185 std::string y;
186 for(size_t i = 0; i < x.length(); i++) {
187 char ch = x[i];
188 if(escape) {
189 if(ch == 'n')
190 y.append(1, '\n');
191 if(ch == '\\')
192 y.append(1, '\\');
193 escape = false;
194 } else {
195 if(ch == '\\')
196 escape = true;
197 else
198 y.append(1, ch);
201 return y;
204 void subtitle_commentary::render(lua::render_context& ctx)
206 if(!mlogic || mlogic.get_mfile().subtitles.empty())
207 return;
208 if(ctx.bottom_gap < 32)
209 ctx.bottom_gap = 32;
210 uint64_t curframe = mlogic.get_movie().get_current_frame() + 1;
211 moviefile_subtiming posmarker(curframe);
212 auto i = mlogic.get_mfile().subtitles.upper_bound(posmarker);
213 if(i != mlogic.get_mfile().subtitles.end() && i->first.inrange(curframe)) {
214 std::string subtxt = i->second;
215 int32_t y = ctx.height;
216 ctx.queue->create_add<render_object_subtitle>(0, y, subtxt);
220 std::set<std::pair<uint64_t, uint64_t>> subtitle_commentary::get_all()
222 std::set<std::pair<uint64_t, uint64_t>> r;
223 if(!mlogic)
224 return r;
225 for(auto i = mlogic.get_mfile().subtitles.rbegin(); i !=
226 mlogic.get_mfile().subtitles.rend(); i++)
227 r.insert(std::make_pair(i->first.get_frame(), i->first.get_length()));
228 return r;
231 std::string subtitle_commentary::get(uint64_t f, uint64_t l)
233 if(!mlogic)
234 return "";
235 moviefile_subtiming key(f, l);
236 if(!mlogic.get_mfile().subtitles.count(key))
237 return "";
238 else
239 return s_escape(mlogic.get_mfile().subtitles[key]);
242 void subtitle_commentary::set(uint64_t f, uint64_t l, const std::string& x)
244 if(!mlogic)
245 return;
246 moviefile_subtiming key(f, l);
247 if(x == "")
248 mlogic.get_mfile().subtitles.erase(key);
249 else
250 mlogic.get_mfile().subtitles[key] = s_unescape(x);
251 notify_subtitle_change();
252 fbuf.redraw_framebuffer();