Allow binding commands to class instance
[lsnes.git] / src / core / multitrack.cpp
blob9c871a95874677e41f02153b756fc5d6144c415d
1 #include "cmdhelp/multitrack.hpp"
2 #include "core/command.hpp"
3 #include "core/controller.hpp"
4 #include "core/dispatch.hpp"
5 #include "core/emustatus.hpp"
6 #include "core/instance.hpp"
7 #include "core/keymapper.hpp"
8 #include "core/movie.hpp"
9 #include "core/multitrack.hpp"
10 #include "lua/internal.hpp"
12 #include <string>
14 multitrack_edit::multitrack_edit(movie_logic& _mlogic, controller_state& _controls, emulator_dispatch& _dispatch,
15 status_updater& _supdater, button_mapping& _buttons, command::group& _cmd)
16 : mlogic(_mlogic), controls(_controls), edispatch(_dispatch), supdater(_supdater), buttons(_buttons),
17 cmd(_cmd),
18 mt_f(cmd, STUBS::multitrackf, [this]() { this->do_mt_fwd(); }),
19 mt_b(cmd, STUBS::multitrackb, [this]() { this->do_mt_bw(); }),
20 mt_s(cmd, STUBS::multitracks, [this](const std::string& a) { this->do_mt_set(a); })
24 bool multitrack_edit::is_enabled()
26 return enabled;
29 void multitrack_edit::enable(bool state)
32 threads::alock h(mlock);
33 enabled = state;
34 controllerstate.clear();
36 supdater.update();
39 void multitrack_edit::set(unsigned port, unsigned controller, state s)
42 threads::alock h(mlock);
43 controllerstate[std::make_pair(port, controller)] = s;
45 supdater.update();
48 void multitrack_edit::set_and_notify(unsigned port, unsigned controller, state s)
50 if(!mlogic || !mlogic.get_movie().readonly_mode())
51 return;
52 set(port, controller, s);
53 edispatch.multitrack_change(port, controller, (int)s);
56 void multitrack_edit::rotate(bool forward)
58 if(!mlogic || !mlogic.get_movie().readonly_mode())
59 return;
60 std::vector<std::pair<unsigned, unsigned>> x;
61 for(unsigned i = 0;; i++) {
62 auto pcid = controls.lcid_to_pcid(i);
63 if(pcid.first < 0)
64 break;
65 x.push_back(std::make_pair(pcid.first, pcid.second));
67 auto old_controllerstate = controllerstate;
68 for(unsigned i = 0; i < x.size(); i++) {
69 state s = MT_PRESERVE;
70 if(old_controllerstate.count(x[i]))
71 s = old_controllerstate[x[i]];
72 unsigned i2;
73 if(forward) {
74 i2 = i + 1;
75 if(i2 >= x.size())
76 i2 = 0;
77 } else {
78 i2 = i - 1;
79 if(i2 >= x.size())
80 i2 = x.size() - 1;
82 controllerstate[x[i2]] = s;
83 edispatch.multitrack_change(x[i2].first, x[i2].second, s);
85 supdater.update();
88 multitrack_edit::state multitrack_edit::get(unsigned port, unsigned controller)
90 threads::alock h(mlock);
91 auto key = std::make_pair(port, controller);
92 if(controllerstate.count(key))
93 return controllerstate[key];
94 return MT_PRESERVE;
97 void multitrack_edit::config_altered()
99 threads::alock h(mlock);
100 controllerstate.clear();
103 void multitrack_edit::process_frame(portctrl::frame& input)
105 if(!mlogic || !mlogic.get_movie().readonly_mode())
106 return;
107 threads::alock h(mlock);
108 bool any_need = false;
109 if(!enabled)
110 return;
111 for(auto i : controllerstate)
112 any_need = any_need || (i.second != MT_PRESERVE);
113 if(!any_need)
114 return; //No need to twiddle.
115 unsigned indices = input.get_index_count();
116 const portctrl::type_set& portset = input.porttypes();
117 portctrl::counters& p = mlogic.get_movie().get_pollcounters();
118 for(unsigned i = 0; i < indices; i++) {
119 portctrl::index_triple t = portset.index_to_triple(i);
120 if(!t.valid)
121 continue;
122 auto key = std::make_pair(t.port, t.controller);
123 uint32_t pc = p.get_polls(i);
124 if(!controllerstate.count(key) || controllerstate[key] == MT_PRESERVE || (!t.port && !t.controller)) {
125 int16_t v = mlogic.get_movie().read_subframe_at_index(pc, t.port, t.controller,
126 t.control);
127 input.axis3(t.port, t.controller, t.control, v);
128 } else {
129 int16_t v = mlogic.get_movie().read_subframe_at_index(pc, t.port, t.controller,
130 t.control);
131 controllerstate[key];
132 const portctrl::type& pt = portset.port_type(t.port);
133 auto pci = pt.controller_info->get(t.controller);
134 auto pb = pci ? pci->get(t.control) : NULL;
135 bool is_axis = (pb && pb->is_analog());
136 switch(controllerstate[key]) {
137 case MT_OR:
138 if(is_axis) {
139 int16_t v2 = input.axis3(t.port, t.controller, t.control);
140 if(v2)
141 v = v2;
142 } else
143 v |= input.axis3(t.port, t.controller, t.control);
144 break;
145 case MT_OVERWRITE:
146 v = input.axis3(t.port, t.controller, t.control);
147 break;
148 case MT_PRESERVE:
149 //No-op.
150 break;
151 case MT_XOR:
152 if(is_axis) {
153 int16_t v2 = input.axis3(t.port, t.controller, t.control);
154 if(v2)
155 v = v2;
156 } else
157 v ^= input.axis3(t.port, t.controller, t.control);
158 break;
160 mlogic.get_movie().write_subframe_at_index(pc, t.port, t.controller, t.control,
162 v = mlogic.get_movie().read_subframe_at_index(pc, t.port, t.controller,
163 t.control);
164 input.axis3(t.port, t.controller, t.control, v);
169 bool multitrack_edit::any_records()
171 if(!mlogic || !mlogic.get_movie().readonly_mode())
172 return true;
173 threads::alock h(mlock);
174 bool any_need = false;
175 for(auto i : controllerstate)
176 any_need = any_need || (i.second != MT_PRESERVE);
177 return any_need;
180 void multitrack_edit::do_mt_set(const std::string& args)
182 regex_results r = regex("(.*)[ \t]+(.*)", args);
183 if(!r)
184 throw std::runtime_error("Bad arguments");
185 auto c = buttons.byname(r[1]);
186 if(c.first < 0)
187 throw std::runtime_error("No such controller");
188 if(r[2] == "keep")
189 set_and_notify(c.first, c.second, multitrack_edit::MT_PRESERVE);
190 else if(r[2] == "rewrite")
191 set_and_notify(c.first, c.second, multitrack_edit::MT_OVERWRITE);
192 else if(r[2] == "or")
193 set_and_notify(c.first, c.second, multitrack_edit::MT_OR);
194 else if(r[2] == "xor")
195 set_and_notify(c.first, c.second, multitrack_edit::MT_XOR);
196 else
197 throw std::runtime_error("Invalid mode (keep, rewrite, or, xor)");
198 supdater.update();
201 void multitrack_edit::do_mt_fwd()
203 rotate(true);
204 supdater.update();
207 void multitrack_edit::do_mt_bw()
209 rotate(false);
210 supdater.update();
213 namespace
215 int multitrack_state(lua::state& L, lua::parameters& P)
217 unsigned port, controller;
219 P(port, controller);
221 auto s = CORE().mteditor->get(port, controller);
222 switch(s) {
223 case multitrack_edit::MT_OR:
224 L.pushstring("or");
225 return 1;
226 case multitrack_edit::MT_OVERWRITE:
227 L.pushstring("rewrite");
228 return 1;
229 case multitrack_edit::MT_PRESERVE:
230 L.pushstring("keep");
231 return 1;
232 case multitrack_edit::MT_XOR:
233 L.pushstring("xor");
234 return 1;
235 default:
236 return 0;
240 lua::functions LUA_mtfn(lua_func_misc, "input", {
241 {"multitrack_state", multitrack_state},