2013-06-12 22:15:25 +03:00
|
|
|
#include "core/controller.hpp"
|
2012-01-09 21:55:55 +02:00
|
|
|
#include "core/controllerframe.hpp"
|
2012-01-11 23:07:31 +02:00
|
|
|
#include "core/dispatch.hpp"
|
|
|
|
#include "core/misc.hpp"
|
2013-03-14 00:15:43 +02:00
|
|
|
#include "core/moviedata.hpp"
|
2013-01-06 23:06:08 +02:00
|
|
|
#include "interface/romtype.hpp"
|
2012-01-09 21:55:55 +02:00
|
|
|
|
2012-01-11 17:31:25 +02:00
|
|
|
#include <cstdio>
|
2012-01-10 01:11:53 +02:00
|
|
|
#include <iostream>
|
|
|
|
|
2013-06-12 22:15:25 +03:00
|
|
|
void update_movie_state();
|
|
|
|
|
2012-01-09 21:55:55 +02:00
|
|
|
namespace
|
|
|
|
{
|
2012-10-11 20:06:40 +03:00
|
|
|
port_type_set dummytypes;
|
2012-01-11 23:07:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
controller_state::controller_state() throw()
|
|
|
|
{
|
2012-10-11 20:06:40 +03:00
|
|
|
types = &dummytypes;
|
2013-03-15 18:42:41 +02:00
|
|
|
tasinput_enaged = false;
|
2012-01-11 23:07:31 +02:00
|
|
|
}
|
|
|
|
|
2012-10-11 20:06:40 +03:00
|
|
|
std::pair<int,int> controller_state::lcid_to_pcid(unsigned lcid) throw()
|
2012-01-11 23:07:31 +02:00
|
|
|
{
|
2012-10-17 09:32:51 +03:00
|
|
|
if(lcid >= types->number_of_controllers())
|
|
|
|
return std::make_pair(-1, -1);
|
|
|
|
auto k = types->lcid_to_pcid(lcid);
|
|
|
|
return std::make_pair(k.first, k.second);
|
2012-01-11 23:07:31 +02:00
|
|
|
}
|
|
|
|
|
2012-10-19 19:53:15 +03:00
|
|
|
std::pair<int, int> controller_state::legacy_pcid_to_pair(unsigned pcid) throw()
|
|
|
|
{
|
|
|
|
if(pcid >= types->number_of_legacy_pcids())
|
|
|
|
return std::make_pair(-1, -1);
|
|
|
|
auto k = types->legacy_pcid_to_pair(pcid);
|
|
|
|
return std::make_pair(k.first, k.second);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-01-11 23:07:31 +02:00
|
|
|
controller_frame controller_state::get(uint64_t framenum) throw()
|
|
|
|
{
|
2013-03-14 00:15:43 +02:00
|
|
|
controller_frame tmp = _input ^ _framehold ^ _autohold;
|
|
|
|
for(auto i : _autofire)
|
|
|
|
if(i.second.eval_at(framenum))
|
|
|
|
tmp.axis2(i.first, tmp.axis2(i.first) ^ 1);
|
2013-03-15 18:42:41 +02:00
|
|
|
if(tasinput_enaged)
|
|
|
|
for(auto i : _tasinput) {
|
|
|
|
if(i.second.mode == 0 && i.second.state)
|
|
|
|
tmp.axis2(i.first, tmp.axis2(i.first) ^ 1);
|
|
|
|
else if(i.second.mode == 1)
|
|
|
|
tmp.axis2(i.first, i.second.state);
|
|
|
|
}
|
2013-06-12 22:15:25 +03:00
|
|
|
apply_macro(tmp);
|
2013-03-14 00:15:43 +02:00
|
|
|
return tmp;
|
2012-01-11 23:07:31 +02:00
|
|
|
}
|
|
|
|
|
2013-01-04 20:41:11 +02:00
|
|
|
void controller_state::analog(unsigned port, unsigned controller, unsigned control, short x) throw()
|
2012-01-11 23:07:31 +02:00
|
|
|
{
|
2013-01-04 20:41:11 +02:00
|
|
|
_input.axis3(port, controller, control, x);
|
2012-01-11 23:07:31 +02:00
|
|
|
}
|
|
|
|
|
2012-10-11 20:06:40 +03:00
|
|
|
void controller_state::autohold2(unsigned port, unsigned controller, unsigned pbid, bool newstate) throw()
|
2012-01-11 23:07:31 +02:00
|
|
|
{
|
2012-10-11 20:06:40 +03:00
|
|
|
_autohold.axis3(port, controller, pbid, newstate ? 1 : 0);
|
2013-07-07 13:54:56 +03:00
|
|
|
notify_autohold_update(port, controller, pbid, newstate);
|
2012-01-11 23:07:31 +02:00
|
|
|
}
|
|
|
|
|
2012-10-11 20:06:40 +03:00
|
|
|
bool controller_state::autohold2(unsigned port, unsigned controller, unsigned pbid) throw()
|
2012-01-11 23:07:31 +02:00
|
|
|
{
|
2012-10-11 20:06:40 +03:00
|
|
|
return (_autohold.axis3(port, controller, pbid) != 0);
|
2012-01-11 23:07:31 +02:00
|
|
|
}
|
|
|
|
|
2013-03-14 00:15:43 +02:00
|
|
|
void controller_state::autofire2(unsigned port, unsigned controller, unsigned pbid, unsigned duty, unsigned cyclelen)
|
|
|
|
throw()
|
|
|
|
{
|
|
|
|
unsigned idx = _input.porttypes().triple_to_index(port, controller, pbid);
|
|
|
|
if(duty) {
|
|
|
|
_autofire[idx].first_frame = movb.get_movie().get_current_frame();
|
|
|
|
_autofire[idx].duty = duty;
|
|
|
|
_autofire[idx].cyclelen = cyclelen;
|
|
|
|
} else
|
|
|
|
_autofire.erase(idx);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::pair<unsigned, unsigned> controller_state::autofire2(unsigned port, unsigned controller, unsigned pbid) throw()
|
|
|
|
{
|
|
|
|
unsigned idx = _input.porttypes().triple_to_index(port, controller, pbid);
|
|
|
|
if(!_autofire.count(idx))
|
|
|
|
return std::make_pair(0, 1);
|
|
|
|
else
|
|
|
|
return std::make_pair(_autofire[idx].duty, _autofire[idx].cyclelen);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool controller_state::autofire_info::eval_at(uint64_t frame)
|
|
|
|
{
|
|
|
|
if(frame < first_frame) {
|
|
|
|
uint64_t diff = first_frame - frame;
|
|
|
|
frame += ((diff / cyclelen) + 1) * cyclelen;
|
|
|
|
}
|
|
|
|
uint64_t diff2 = frame - first_frame;
|
|
|
|
return frame % cyclelen < duty;
|
|
|
|
}
|
|
|
|
|
2012-05-11 19:37:06 +03:00
|
|
|
void controller_state::reset_framehold() throw()
|
|
|
|
{
|
|
|
|
_framehold = _framehold.blank_frame();
|
|
|
|
}
|
|
|
|
|
2012-10-11 20:06:40 +03:00
|
|
|
void controller_state::framehold2(unsigned port, unsigned controller, unsigned pbid, bool newstate) throw()
|
2012-05-11 19:37:06 +03:00
|
|
|
{
|
2012-10-11 20:06:40 +03:00
|
|
|
_framehold.axis3(port, controller, pbid, newstate ? 1 : 0);
|
2012-05-11 19:37:06 +03:00
|
|
|
}
|
|
|
|
|
2012-10-11 20:06:40 +03:00
|
|
|
bool controller_state::framehold2(unsigned port, unsigned controller, unsigned pbid) throw()
|
2012-05-11 19:37:06 +03:00
|
|
|
{
|
2012-10-11 20:06:40 +03:00
|
|
|
return (_framehold.axis3(port, controller, pbid) != 0);
|
2012-05-11 19:37:06 +03:00
|
|
|
}
|
|
|
|
|
2012-10-11 20:06:40 +03:00
|
|
|
void controller_state::button2(unsigned port, unsigned controller, unsigned pbid, bool newstate) throw()
|
2012-01-11 23:07:31 +02:00
|
|
|
{
|
2012-10-11 20:06:40 +03:00
|
|
|
_input.axis3(port, controller, pbid, newstate ? 1 : 0);
|
2012-01-11 23:07:31 +02:00
|
|
|
}
|
|
|
|
|
2012-10-11 20:06:40 +03:00
|
|
|
bool controller_state::button2(unsigned port, unsigned controller, unsigned pbid) throw()
|
2012-01-11 23:07:31 +02:00
|
|
|
{
|
2012-10-11 20:06:40 +03:00
|
|
|
return (_input.axis3(port, controller, pbid) != 0);
|
2012-01-11 23:07:31 +02:00
|
|
|
}
|
|
|
|
|
2013-03-15 18:42:41 +02:00
|
|
|
void controller_state::tasinput(unsigned port, unsigned controller, unsigned pbid, int16_t state) throw()
|
|
|
|
{
|
|
|
|
unsigned idx = _input.porttypes().triple_to_index(port, controller, pbid);
|
|
|
|
if(!_tasinput.count(idx))
|
|
|
|
_tasinput[idx].mode = 0; //Just to be sure.
|
|
|
|
_tasinput[idx].state = state;
|
|
|
|
}
|
|
|
|
|
|
|
|
int16_t controller_state::tasinput(unsigned port, unsigned controller, unsigned pbid) throw()
|
|
|
|
{
|
|
|
|
unsigned idx = _input.porttypes().triple_to_index(port, controller, pbid);
|
|
|
|
return _tasinput.count(idx) ? _tasinput[idx].state : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void controller_state::tasinput_enable(bool enabled)
|
|
|
|
{
|
|
|
|
tasinput_enaged = enabled;
|
|
|
|
}
|
|
|
|
|
2013-01-04 20:41:11 +02:00
|
|
|
void reread_active_buttons();
|
2012-01-11 23:07:31 +02:00
|
|
|
|
2013-03-15 18:42:41 +02:00
|
|
|
void controller_state::reread_tasinput_mode(const port_type_set& ptype)
|
|
|
|
{
|
|
|
|
unsigned indices = ptype.indices();
|
|
|
|
_tasinput.clear();
|
|
|
|
for(unsigned i = 0; i < indices; i++) {
|
|
|
|
auto t = ptype.index_to_triple(i);
|
|
|
|
if(!t.valid)
|
|
|
|
continue;
|
|
|
|
//See what the heck that is...
|
|
|
|
const port_type& pt = ptype.port_type(t.port);
|
|
|
|
const port_controller_set& pci = *(pt.controller_info);
|
2013-06-15 19:27:03 +03:00
|
|
|
if(pci.controllers.size() <= t.controller)
|
2013-03-15 18:42:41 +02:00
|
|
|
continue;
|
|
|
|
const port_controller& pc = *(pci.controllers[t.controller]);
|
2013-06-15 19:27:03 +03:00
|
|
|
if(pc.buttons.size() <= t.control)
|
2013-03-15 18:42:41 +02:00
|
|
|
continue;
|
2013-06-15 19:27:03 +03:00
|
|
|
const port_controller_button& pcb = pc.buttons[t.control];
|
2013-03-15 18:42:41 +02:00
|
|
|
if(pcb.shadow)
|
|
|
|
continue;
|
|
|
|
if(pcb.type == port_controller_button::TYPE_BUTTON)
|
|
|
|
_tasinput[i].mode = 0;
|
|
|
|
else
|
|
|
|
_tasinput[i].mode = 1;
|
|
|
|
_tasinput[i].state = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-01-05 09:19:09 +02:00
|
|
|
void controller_state::set_ports(const port_type_set& ptype) throw(std::runtime_error)
|
2012-01-11 23:07:31 +02:00
|
|
|
{
|
2012-10-11 20:06:40 +03:00
|
|
|
const port_type_set* oldtype = types;
|
|
|
|
types = &ptype;
|
|
|
|
if(oldtype != types) {
|
|
|
|
_input.set_types(ptype);
|
|
|
|
_autohold.set_types(ptype);
|
|
|
|
_committed.set_types(ptype);
|
|
|
|
_framehold.set_types(ptype);
|
2012-01-11 23:07:31 +02:00
|
|
|
//The old autofire pattern no longer applies.
|
|
|
|
_autofire.clear();
|
2013-03-15 18:42:41 +02:00
|
|
|
reread_tasinput_mode(ptype);
|
2013-01-04 20:41:11 +02:00
|
|
|
_autohold = _autohold.blank_frame();
|
2012-01-11 23:07:31 +02:00
|
|
|
}
|
2013-01-04 20:41:11 +02:00
|
|
|
reread_active_buttons();
|
2013-07-07 13:54:56 +03:00
|
|
|
notify_autohold_reconfigure();
|
2012-01-11 23:07:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
controller_frame controller_state::get_blank() throw()
|
|
|
|
{
|
|
|
|
return _input.blank_frame();
|
|
|
|
}
|
|
|
|
|
2012-01-13 06:51:47 +02:00
|
|
|
controller_frame controller_state::get_committed() throw()
|
|
|
|
{
|
|
|
|
return _committed;
|
|
|
|
}
|
|
|
|
|
2013-04-05 20:06:00 +03:00
|
|
|
void controller_state::commit(controller_frame controls) throw()
|
2012-01-13 06:51:47 +02:00
|
|
|
{
|
|
|
|
_committed = controls;
|
|
|
|
}
|
2012-01-17 23:48:13 +02:00
|
|
|
|
2012-10-11 20:06:40 +03:00
|
|
|
bool controller_state::is_present(unsigned port, unsigned controller) throw()
|
2012-07-08 12:57:22 +03:00
|
|
|
{
|
2012-10-11 20:06:40 +03:00
|
|
|
return _input.is_present(port, controller);
|
2012-07-08 12:57:22 +03:00
|
|
|
}
|
2013-06-12 22:15:25 +03:00
|
|
|
|
|
|
|
void controller_state::erase_macro(const std::string& macro)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
umutex_class h(macro_lock);
|
|
|
|
if(!all_macros.count(macro))
|
|
|
|
return;
|
|
|
|
auto m = &all_macros[macro];
|
|
|
|
for(auto i = active_macros.begin(); i != active_macros.end(); i++) {
|
|
|
|
if(i->second == m) {
|
|
|
|
active_macros.erase(i);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
all_macros.erase(macro);
|
|
|
|
project_info* p = project_get();
|
|
|
|
if(p) {
|
|
|
|
p->macros.erase(macro);
|
|
|
|
project_flush(p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
load_macros(*this);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::set<std::string> controller_state::enumerate_macro()
|
|
|
|
{
|
|
|
|
umutex_class h(macro_lock);
|
|
|
|
std::set<std::string> r;
|
|
|
|
for(auto i : all_macros)
|
|
|
|
r.insert(i.first);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
controller_macro& controller_state::get_macro(const std::string& macro)
|
|
|
|
{
|
|
|
|
umutex_class h(macro_lock);
|
|
|
|
if(!all_macros.count(macro))
|
|
|
|
throw std::runtime_error("No such macro");
|
|
|
|
return all_macros[macro];
|
|
|
|
}
|
|
|
|
|
|
|
|
void controller_state::set_macro(const std::string& macro, const controller_macro& m)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
umutex_class h(macro_lock);
|
|
|
|
controller_macro* old = NULL;
|
|
|
|
if(all_macros.count(macro))
|
|
|
|
old = &all_macros[macro];
|
|
|
|
all_macros[macro] = m;
|
|
|
|
for(auto i = active_macros.begin(); i != active_macros.end(); i++) {
|
|
|
|
if(i->second == old) {
|
|
|
|
i->second = &all_macros[macro];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
project_info* p = project_get();
|
|
|
|
if(p) {
|
|
|
|
p->macros[macro] = all_macros[macro].serialize();
|
|
|
|
project_flush(p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
load_macros(*this);
|
|
|
|
}
|
|
|
|
|
|
|
|
void controller_state::apply_macro(controller_frame& f)
|
|
|
|
{
|
|
|
|
umutex_class h(macro_lock);
|
|
|
|
for(auto i : active_macros)
|
|
|
|
i.second->write(f, i.first);
|
|
|
|
}
|
|
|
|
|
|
|
|
void controller_state::advance_macros()
|
|
|
|
{
|
|
|
|
umutex_class h(macro_lock);
|
|
|
|
for(auto& i : active_macros)
|
|
|
|
i.first++;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::map<std::string, uint64_t> controller_state::get_macro_frames()
|
|
|
|
{
|
|
|
|
umutex_class h(macro_lock);
|
|
|
|
std::map<std::string, uint64_t> r;
|
|
|
|
for(auto i : active_macros) {
|
|
|
|
for(auto& j : all_macros)
|
|
|
|
if(i.second == &j.second) {
|
|
|
|
r[j.first] = i.first;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
void controller_state::set_macro_frames(const std::map<std::string, uint64_t>& f)
|
|
|
|
{
|
|
|
|
umutex_class h(macro_lock);
|
|
|
|
std::list<std::pair<uint64_t, controller_macro*>> new_active_macros;
|
|
|
|
for(auto i : f)
|
|
|
|
if(all_macros.count(i.first))
|
|
|
|
new_active_macros.push_back(std::make_pair(i.second, &all_macros[i.first]));
|
|
|
|
else
|
|
|
|
messages << "Warning: Can't find defintion for '" << i.first << "'" << std::endl;
|
|
|
|
std::swap(active_macros, new_active_macros);
|
|
|
|
}
|
|
|
|
|
|
|
|
void controller_state::rename_macro(const std::string& old, const std::string& newn)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
umutex_class h(macro_lock);
|
|
|
|
if(!all_macros.count(old))
|
|
|
|
throw std::runtime_error("Old macro doesn't exist");
|
|
|
|
if(all_macros.count(newn))
|
|
|
|
throw std::runtime_error("Target name already exists");
|
|
|
|
if(old == newn)
|
|
|
|
return;
|
|
|
|
all_macros[newn] = all_macros[old];
|
|
|
|
controller_macro* _old = &all_macros[old];
|
|
|
|
all_macros.erase(old);
|
|
|
|
for(auto i = active_macros.begin(); i != active_macros.end(); i++) {
|
|
|
|
if(i->second == _old) {
|
|
|
|
i->second = &all_macros[newn];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
project_info* p = project_get();
|
|
|
|
if(p) {
|
|
|
|
p->macros[newn] = p->macros[old];
|
|
|
|
p->macros.erase(old);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
load_macros(*this);
|
|
|
|
}
|
|
|
|
|
|
|
|
void controller_state::do_macro(const std::string& a, int mode) {
|
|
|
|
{
|
|
|
|
umutex_class h(macro_lock);
|
|
|
|
if(!all_macros.count(a)) {
|
|
|
|
if(mode & 1) messages << "No such macro '" << a << "'" << std::endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
controller_macro* m = &all_macros[a];
|
|
|
|
for(auto i = active_macros.begin(); i != active_macros.end(); i++) {
|
|
|
|
if(i->second == m) {
|
|
|
|
if(mode & 2) active_macros.erase(i);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(mode & 4) active_macros.push_back(std::make_pair(0, m));
|
|
|
|
}
|
|
|
|
end:
|
|
|
|
update_movie_state();
|
2013-07-07 13:54:56 +03:00
|
|
|
notify_status_update();
|
2013-06-12 22:15:25 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
std::set<std::string> controller_state::active_macro_set()
|
|
|
|
{
|
|
|
|
umutex_class h(macro_lock);
|
|
|
|
std::set<std::string> r;
|
|
|
|
for(auto i : active_macros) {
|
|
|
|
for(auto& j : all_macros)
|
|
|
|
if(i.second == &j.second) {
|
|
|
|
r.insert(j.first);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return r;
|
|
|
|
}
|