Fix SA1 open bus
[lsnes.git] / src / core / multitrack.cpp
blob75a59dca21a8a0156ff2442d0494c048059024b8
1 #include <string>
2 #include "core/command.hpp"
3 #include "core/controller.hpp"
4 #include "core/dispatch.hpp"
5 #include "core/keymapper.hpp"
6 #include "core/moviedata.hpp"
7 #include "core/multitrack.hpp"
8 #include "lua/internal.hpp"
10 void update_movie_state();
12 bool multitrack_edit::is_enabled()
14 return enabled;
17 void multitrack_edit::enable(bool state)
20 threads::alock h(mlock);
21 enabled = state;
22 controllerstate.clear();
24 update_movie_state();
27 void multitrack_edit::set(unsigned port, unsigned controller, state s)
30 threads::alock h(mlock);
31 controllerstate[std::make_pair(port, controller)] = s;
33 update_movie_state();
36 void multitrack_edit::set_and_notify(unsigned port, unsigned controller, state s)
38 if(!movb || !movb.get_movie().readonly_mode())
39 return;
40 set(port, controller, s);
41 notify_multitrack_change(port, controller, (int)s);
44 void multitrack_edit::rotate(bool forward)
46 if(!movb || !movb.get_movie().readonly_mode())
47 return;
48 std::vector<std::pair<unsigned, unsigned>> x;
49 for(unsigned i = 0;; i++) {
50 auto pcid = controls.lcid_to_pcid(i);
51 if(pcid.first < 0)
52 break;
53 x.push_back(std::make_pair(pcid.first, pcid.second));
55 auto old_controllerstate = controllerstate;
56 for(unsigned i = 0; i < x.size(); i++) {
57 state s = MT_PRESERVE;
58 if(old_controllerstate.count(x[i]))
59 s = old_controllerstate[x[i]];
60 unsigned i2;
61 if(forward) {
62 i2 = i + 1;
63 if(i2 >= x.size())
64 i2 = 0;
65 } else {
66 i2 = i - 1;
67 if(i2 >= x.size())
68 i2 = x.size() - 1;
70 controllerstate[x[i2]] = s;
71 notify_multitrack_change(x[i2].first, x[i2].second, s);
73 update_movie_state();
76 multitrack_edit::state multitrack_edit::get(unsigned port, unsigned controller)
78 threads::alock h(mlock);
79 auto key = std::make_pair(port, controller);
80 if(controllerstate.count(key))
81 return controllerstate[key];
82 return MT_PRESERVE;
85 void multitrack_edit::config_altered()
87 threads::alock h(mlock);
88 controllerstate.clear();
91 void multitrack_edit::process_frame(controller_frame& input)
93 if(!movb || !movb.get_movie().readonly_mode())
94 return;
95 threads::alock h(mlock);
96 bool any_need = false;
97 if(!enabled)
98 return;
99 for(auto i : controllerstate)
100 any_need = any_need || (i.second != MT_PRESERVE);
101 if(!any_need)
102 return; //No need to twiddle.
103 unsigned indices = input.get_index_count();
104 const port_type_set& portset = input.porttypes();
105 pollcounter_vector& p = movb.get_movie().get_pollcounters();
106 for(unsigned i = 0; i < indices; i++) {
107 port_index_triple t = portset.index_to_triple(i);
108 if(!t.valid)
109 continue;
110 auto key = std::make_pair(t.port, t.controller);
111 uint32_t pc = p.get_polls(i);
112 if(!controllerstate.count(key) || controllerstate[key] == MT_PRESERVE || (!t.port && !t.controller)) {
113 int16_t v = movb.get_movie().read_subframe_at_index(pc, t.port, t.controller, t.control);
114 input.axis3(t.port, t.controller, t.control, v);
115 } else {
116 int16_t v = movb.get_movie().read_subframe_at_index(pc, t.port, t.controller, t.control);
117 controllerstate[key];
118 const port_type& pt = portset.port_type(t.port);
119 auto pci = pt.controller_info->get(t.controller);
120 auto pb = pci ? pci->get(t.control) : NULL;
121 bool is_axis = (pb && pb->is_analog());
122 switch(controllerstate[key]) {
123 case MT_OR:
124 if(is_axis) {
125 int16_t v2 = input.axis3(t.port, t.controller, t.control);
126 if(v2)
127 v = v2;
128 } else
129 v |= input.axis3(t.port, t.controller, t.control);
130 break;
131 case MT_OVERWRITE:
132 v = input.axis3(t.port, t.controller, t.control);
133 break;
134 case MT_PRESERVE:
135 //No-op.
136 break;
137 case MT_XOR:
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;
146 movb.get_movie().write_subframe_at_index(pc, t.port, t.controller, t.control, v);
147 v = movb.get_movie().read_subframe_at_index(pc, t.port, t.controller, t.control);
148 input.axis3(t.port, t.controller, t.control, v);
153 bool multitrack_edit::any_records()
155 if(!movb || !movb.get_movie().readonly_mode())
156 return true;
157 threads::alock h(mlock);
158 bool any_need = false;
159 for(auto i : controllerstate)
160 any_need = any_need || (i.second != MT_PRESERVE);
161 return any_need;
164 namespace
166 command::fnptr<> rotate_forward(lsnes_cmd, "rotate-multitrack", "Rotate multitrack",
167 "Syntax: rotate-multitrack\nRotate multitrack\n",
168 []() throw(std::bad_alloc, std::runtime_error) {
169 multitrack_editor.rotate(true);
170 update_movie_state();
173 command::fnptr<> rotate_backward(lsnes_cmd, "rotate-multitrack-backwards", "Rotate multitrack backwards",
174 "Syntax: rotate-multitrack-backwards\nRotate multitrack backwards\n",
175 []() throw(std::bad_alloc, std::runtime_error) {
176 multitrack_editor.rotate(false);
177 update_movie_state();
180 command::fnptr<const std::string&> set_mt(lsnes_cmd, "set-multitrack", "Set multitrack mode",
181 "Syntax: set-multitrack <controller> <mode>\nSet multitrack mode\n",
182 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
183 regex_results r = regex("(.*)[ \t]+(.*)", args);
184 if(!r)
185 throw std::runtime_error("Bad arguments");
186 auto c = controller_by_name(r[1]);
187 if(c.first < 0)
188 throw std::runtime_error("No such controller");
189 if(r[2] == "keep")
190 multitrack_editor.set_and_notify(c.first, c.second, multitrack_edit::MT_PRESERVE);
191 else if(r[2] == "rewrite")
192 multitrack_editor.set_and_notify(c.first, c.second, multitrack_edit::MT_OVERWRITE);
193 else if(r[2] == "or")
194 multitrack_editor.set_and_notify(c.first, c.second, multitrack_edit::MT_OR);
195 else if(r[2] == "xor")
196 multitrack_editor.set_and_notify(c.first, c.second, multitrack_edit::MT_XOR);
197 else
198 throw std::runtime_error("Invalid mode (keep, rewrite, or, xor)");
199 update_movie_state();
202 keyboard::invbind _mtback(lsnes_mapper, "rotate-multitrack-backwards", "Multitrack‣Rotate backwards");
203 keyboard::invbind _mtfwd(lsnes_mapper, "rotate-multitrack", "Multitrack‣Rotate forward");
205 int multitrack_state(lua::state& L, lua::parameters& P)
207 unsigned port, controller;
209 P(port, controller);
211 auto s = multitrack_editor.get(port, controller);
212 switch(s) {
213 case multitrack_edit::MT_OR:
214 L.pushstring("or");
215 return 1;
216 case multitrack_edit::MT_OVERWRITE:
217 L.pushstring("rewrite");
218 return 1;
219 case multitrack_edit::MT_PRESERVE:
220 L.pushstring("keep");
221 return 1;
222 case multitrack_edit::MT_XOR:
223 L.pushstring("xor");
224 return 1;
225 default:
226 return 0;
230 lua::functions mtfn(lua_func_misc, "input", {
231 {"multitrack_state", multitrack_state},
235 multitrack_edit multitrack_editor;