chore: CRLF to LF
This commit is contained in:
+82
-82
@@ -1,82 +1,82 @@
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include <winsock2.h>
|
||||
|
||||
#include "util/rc4.h"
|
||||
|
||||
#include "module.h"
|
||||
#include "websocket.h"
|
||||
#include "serial.h"
|
||||
|
||||
namespace api {
|
||||
|
||||
struct ClientState {
|
||||
SOCKADDR_IN address;
|
||||
SOCKET socket;
|
||||
bool close = false;
|
||||
std::vector<Module*> modules;
|
||||
std::string password;
|
||||
bool password_change = false;
|
||||
util::RC4 *cipher = nullptr;
|
||||
};
|
||||
|
||||
class Controller {
|
||||
private:
|
||||
|
||||
// configuration
|
||||
const static int server_backlog = 16;
|
||||
const static int server_receive_buffer_size = 64 * 1024;
|
||||
const static int server_message_buffer_max_size = 64 * 1024;
|
||||
const static int server_worker_count = 2;
|
||||
const static int server_connection_limit = 4096;
|
||||
|
||||
// settings
|
||||
unsigned short port;
|
||||
std::string password;
|
||||
bool pretty;
|
||||
|
||||
// server
|
||||
WebSocketController *websocket;
|
||||
std::vector<SerialController *> serial;
|
||||
std::vector<std::thread> server_workers;
|
||||
std::vector<std::thread> server_handlers;
|
||||
std::mutex server_handlers_m;
|
||||
std::vector<api::ClientState *> client_states;
|
||||
std::mutex client_states_m;
|
||||
SOCKET server;
|
||||
void server_worker();
|
||||
void connection_handler(ClientState client_state);
|
||||
|
||||
public:
|
||||
|
||||
// state
|
||||
bool server_running;
|
||||
|
||||
// constructor / destructor
|
||||
Controller(unsigned short port, std::string password, bool pretty);
|
||||
~Controller();
|
||||
|
||||
void listen_serial(std::string port, DWORD baud);
|
||||
|
||||
bool process_request(ClientState *state, std::vector<char> *in, std::vector<char> *out);
|
||||
bool process_request(ClientState *state, const char *in, size_t in_size, std::vector<char> *out);
|
||||
static void process_password_change(ClientState *state);
|
||||
|
||||
void init_state(ClientState *state);
|
||||
static void free_state(ClientState *state);
|
||||
|
||||
void free_socket();
|
||||
void obtain_client_states(std::vector<ClientState> *output);
|
||||
|
||||
std::string get_ip_address(sockaddr_in addr);
|
||||
|
||||
inline const std::string &get_password() const {
|
||||
return this->password;
|
||||
}
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include <winsock2.h>
|
||||
|
||||
#include "util/rc4.h"
|
||||
|
||||
#include "module.h"
|
||||
#include "websocket.h"
|
||||
#include "serial.h"
|
||||
|
||||
namespace api {
|
||||
|
||||
struct ClientState {
|
||||
SOCKADDR_IN address;
|
||||
SOCKET socket;
|
||||
bool close = false;
|
||||
std::vector<Module*> modules;
|
||||
std::string password;
|
||||
bool password_change = false;
|
||||
util::RC4 *cipher = nullptr;
|
||||
};
|
||||
|
||||
class Controller {
|
||||
private:
|
||||
|
||||
// configuration
|
||||
const static int server_backlog = 16;
|
||||
const static int server_receive_buffer_size = 64 * 1024;
|
||||
const static int server_message_buffer_max_size = 64 * 1024;
|
||||
const static int server_worker_count = 2;
|
||||
const static int server_connection_limit = 4096;
|
||||
|
||||
// settings
|
||||
unsigned short port;
|
||||
std::string password;
|
||||
bool pretty;
|
||||
|
||||
// server
|
||||
WebSocketController *websocket;
|
||||
std::vector<SerialController *> serial;
|
||||
std::vector<std::thread> server_workers;
|
||||
std::vector<std::thread> server_handlers;
|
||||
std::mutex server_handlers_m;
|
||||
std::vector<api::ClientState *> client_states;
|
||||
std::mutex client_states_m;
|
||||
SOCKET server;
|
||||
void server_worker();
|
||||
void connection_handler(ClientState client_state);
|
||||
|
||||
public:
|
||||
|
||||
// state
|
||||
bool server_running;
|
||||
|
||||
// constructor / destructor
|
||||
Controller(unsigned short port, std::string password, bool pretty);
|
||||
~Controller();
|
||||
|
||||
void listen_serial(std::string port, DWORD baud);
|
||||
|
||||
bool process_request(ClientState *state, std::vector<char> *in, std::vector<char> *out);
|
||||
bool process_request(ClientState *state, const char *in, size_t in_size, std::vector<char> *out);
|
||||
static void process_password_change(ClientState *state);
|
||||
|
||||
void init_state(ClientState *state);
|
||||
static void free_state(ClientState *state);
|
||||
|
||||
void free_socket();
|
||||
void obtain_client_states(std::vector<ClientState> *output);
|
||||
|
||||
std::string get_ip_address(sockaddr_in addr);
|
||||
|
||||
inline const std::string &get_password() const {
|
||||
return this->password;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
+43
-43
@@ -1,43 +1,43 @@
|
||||
#include <utility>
|
||||
|
||||
#include "util/logging.h"
|
||||
|
||||
#include "module.h"
|
||||
|
||||
using namespace rapidjson;
|
||||
|
||||
namespace api {
|
||||
|
||||
// logging setting
|
||||
bool LOGGING = false;
|
||||
|
||||
Module::Module(std::string name, bool password_force) {
|
||||
this->name = std::move(name);
|
||||
this->password_force = password_force;
|
||||
}
|
||||
|
||||
void Module::handle(Request &req, Response &res) {
|
||||
|
||||
// log module access
|
||||
if (LOGGING)
|
||||
log_info("api::" + this->name, "handling request");
|
||||
|
||||
// find function
|
||||
auto pos = functions.find(req.function);
|
||||
if (pos == functions.end())
|
||||
return error_function_unknown(res);
|
||||
|
||||
// call function
|
||||
pos->second(req, res);
|
||||
}
|
||||
|
||||
void Module::error(Response &res, std::string err) {
|
||||
|
||||
// log the warning
|
||||
log_warning("api::" + this->name, "error: {}", err);
|
||||
|
||||
// add error to response
|
||||
Value val(err.c_str(), res.doc()->GetAllocator());
|
||||
res.add_error(val);
|
||||
}
|
||||
}
|
||||
#include <utility>
|
||||
|
||||
#include "util/logging.h"
|
||||
|
||||
#include "module.h"
|
||||
|
||||
using namespace rapidjson;
|
||||
|
||||
namespace api {
|
||||
|
||||
// logging setting
|
||||
bool LOGGING = false;
|
||||
|
||||
Module::Module(std::string name, bool password_force) {
|
||||
this->name = std::move(name);
|
||||
this->password_force = password_force;
|
||||
}
|
||||
|
||||
void Module::handle(Request &req, Response &res) {
|
||||
|
||||
// log module access
|
||||
if (LOGGING)
|
||||
log_info("api::" + this->name, "handling request");
|
||||
|
||||
// find function
|
||||
auto pos = functions.find(req.function);
|
||||
if (pos == functions.end())
|
||||
return error_function_unknown(res);
|
||||
|
||||
// call function
|
||||
pos->second(req, res);
|
||||
}
|
||||
|
||||
void Module::error(Response &res, std::string err) {
|
||||
|
||||
// log the warning
|
||||
log_warning("api::" + this->name, "error: {}", err);
|
||||
|
||||
// add error to response
|
||||
Value val(err.c_str(), res.doc()->GetAllocator());
|
||||
res.add_error(val);
|
||||
}
|
||||
}
|
||||
|
||||
+67
-67
@@ -1,67 +1,67 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <external/robin_hood.h>
|
||||
|
||||
#include "response.h"
|
||||
#include "request.h"
|
||||
|
||||
namespace api {
|
||||
|
||||
// logging setting
|
||||
extern bool LOGGING;
|
||||
|
||||
// callback
|
||||
typedef std::function<void(Request &, Response &)> ModuleFunctionCallback;
|
||||
|
||||
class Module {
|
||||
protected:
|
||||
|
||||
// map of available functions
|
||||
robin_hood::unordered_map<std::string, ModuleFunctionCallback> functions;
|
||||
|
||||
// default constructor
|
||||
explicit Module(std::string name, bool password_force=false);
|
||||
|
||||
public:
|
||||
|
||||
// virtual deconstructor
|
||||
virtual ~Module() = default;
|
||||
|
||||
// name of the module (should match namespace)
|
||||
std::string name;
|
||||
bool password_force;
|
||||
|
||||
// the magic
|
||||
void handle(Request &req, Response &res);
|
||||
|
||||
/*
|
||||
* Error definitions.
|
||||
*/
|
||||
|
||||
void error(Response &res, std::string err);
|
||||
void error_type(Response &res, const std::string &field, const std::string &type) {
|
||||
std::ostringstream s;
|
||||
s << field << " must be a " << type;
|
||||
error(res, s.str());
|
||||
};
|
||||
void error_size(Response &res, const std::string &field, size_t size) {
|
||||
std::ostringstream s;
|
||||
s << field << " must be of size " << size;
|
||||
error(res, s.str());
|
||||
}
|
||||
void error_unknown(Response &res, const std::string &field, const std::string &name) {
|
||||
std::ostringstream s;
|
||||
s << "Unknown " << field << ": " << name;
|
||||
error(res, s.str());
|
||||
}
|
||||
|
||||
#define ERR(name, err) void error_##name(Response &res) { error(res, err); }
|
||||
ERR(function_unknown, "Unknown function.");
|
||||
ERR(params_insufficient, "Insufficient number of parameters.");
|
||||
#undef ERR
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <external/robin_hood.h>
|
||||
|
||||
#include "response.h"
|
||||
#include "request.h"
|
||||
|
||||
namespace api {
|
||||
|
||||
// logging setting
|
||||
extern bool LOGGING;
|
||||
|
||||
// callback
|
||||
typedef std::function<void(Request &, Response &)> ModuleFunctionCallback;
|
||||
|
||||
class Module {
|
||||
protected:
|
||||
|
||||
// map of available functions
|
||||
robin_hood::unordered_map<std::string, ModuleFunctionCallback> functions;
|
||||
|
||||
// default constructor
|
||||
explicit Module(std::string name, bool password_force=false);
|
||||
|
||||
public:
|
||||
|
||||
// virtual deconstructor
|
||||
virtual ~Module() = default;
|
||||
|
||||
// name of the module (should match namespace)
|
||||
std::string name;
|
||||
bool password_force;
|
||||
|
||||
// the magic
|
||||
void handle(Request &req, Response &res);
|
||||
|
||||
/*
|
||||
* Error definitions.
|
||||
*/
|
||||
|
||||
void error(Response &res, std::string err);
|
||||
void error_type(Response &res, const std::string &field, const std::string &type) {
|
||||
std::ostringstream s;
|
||||
s << field << " must be a " << type;
|
||||
error(res, s.str());
|
||||
};
|
||||
void error_size(Response &res, const std::string &field, size_t size) {
|
||||
std::ostringstream s;
|
||||
s << field << " must be of size " << size;
|
||||
error(res, s.str());
|
||||
}
|
||||
void error_unknown(Response &res, const std::string &field, const std::string &name) {
|
||||
std::ostringstream s;
|
||||
s << "Unknown " << field << ": " << name;
|
||||
error(res, s.str());
|
||||
}
|
||||
|
||||
#define ERR(name, err) void error_##name(Response &res) { error(res, err); }
|
||||
ERR(function_unknown, "Unknown function.");
|
||||
ERR(params_insufficient, "Insufficient number of parameters.");
|
||||
#undef ERR
|
||||
};
|
||||
}
|
||||
|
||||
+177
-177
@@ -1,177 +1,177 @@
|
||||
#include "analogs.h"
|
||||
#include <functional>
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "cfg/analog.h"
|
||||
#include "launcher/launcher.h"
|
||||
#include "games/io.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace rapidjson;
|
||||
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
Analogs::Analogs() : Module("analogs") {
|
||||
functions["read"] = std::bind(&Analogs::read, this, _1, _2);
|
||||
functions["write"] = std::bind(&Analogs::write, this, _1, _2);
|
||||
functions["write_reset"] = std::bind(&Analogs::write_reset, this, _1, _2);
|
||||
analogs = games::get_analogs(eamuse_get_game());
|
||||
}
|
||||
|
||||
/**
|
||||
* read()
|
||||
*/
|
||||
void Analogs::read(api::Request &req, Response &res) {
|
||||
|
||||
// check analog cache
|
||||
if (!analogs) {
|
||||
return;
|
||||
}
|
||||
|
||||
// add state for each analog
|
||||
for (auto &analog : *this->analogs) {
|
||||
Value state(kArrayType);
|
||||
Value analog_name(analog.getName().c_str(), res.doc()->GetAllocator());
|
||||
Value analog_state(GameAPI::Analogs::getState(RI_MGR, analog));
|
||||
Value analog_enabled(analog.override_enabled);
|
||||
state.PushBack(analog_name, res.doc()->GetAllocator());
|
||||
state.PushBack(analog_state, res.doc()->GetAllocator());
|
||||
state.PushBack(analog_enabled, res.doc()->GetAllocator());
|
||||
res.add_data(state);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* write([name: str, state: float], ...)
|
||||
*/
|
||||
void Analogs::write(Request &req, Response &res) {
|
||||
|
||||
// check analog cache
|
||||
if (!analogs)
|
||||
return;
|
||||
|
||||
// loop parameters
|
||||
for (Value ¶m : req.params.GetArray()) {
|
||||
|
||||
// check params
|
||||
if (!param.IsArray()) {
|
||||
error(res, "parameters must be arrays");
|
||||
return;
|
||||
}
|
||||
if (param.Size() < 2) {
|
||||
error_params_insufficient(res);
|
||||
continue;
|
||||
}
|
||||
if (!param[0].IsString()) {
|
||||
error_type(res, "name", "string");
|
||||
continue;
|
||||
}
|
||||
if (!param[1].IsFloat() && !param[1].IsInt()) {
|
||||
error_type(res, "state", "float");
|
||||
continue;
|
||||
}
|
||||
|
||||
// get params
|
||||
auto analog_name = param[0].GetString();
|
||||
auto analog_state = param[1].GetFloat();
|
||||
|
||||
// write analog state
|
||||
if (!this->write_analog(analog_name, analog_state)) {
|
||||
error_unknown(res, "analog", analog_name);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* write_reset()
|
||||
* write_reset([name: str], ...)
|
||||
*/
|
||||
void Analogs::write_reset(Request &req, Response &res) {
|
||||
|
||||
// check analog cache
|
||||
if (!analogs)
|
||||
return;
|
||||
|
||||
// get params
|
||||
auto params = req.params.GetArray();
|
||||
|
||||
// write_reset()
|
||||
if (params.Size() == 0) {
|
||||
if (analogs != nullptr) {
|
||||
for (auto &analog : *this->analogs) {
|
||||
analog.override_enabled = false;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// loop parameters
|
||||
for (Value ¶m : req.params.GetArray()) {
|
||||
|
||||
// check params
|
||||
if (!param.IsArray()) {
|
||||
error(res, "parameters must be arrays");
|
||||
return;
|
||||
}
|
||||
if (param.Size() < 1) {
|
||||
error_params_insufficient(res);
|
||||
continue;
|
||||
}
|
||||
if (!param[0].IsString()) {
|
||||
error_type(res, "name", "string");
|
||||
continue;
|
||||
}
|
||||
|
||||
// get params
|
||||
auto analog_name = param[0].GetString();
|
||||
|
||||
// write analog state
|
||||
if (!this->write_analog_reset(analog_name)) {
|
||||
error_unknown(res, "analog", analog_name);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Analogs::write_analog(std::string name, float state) {
|
||||
|
||||
// check analog cache
|
||||
if (!this->analogs) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// find analog
|
||||
for (auto &analog : *this->analogs) {
|
||||
if (analog.getName() == name) {
|
||||
analog.override_state = CLAMP(state, 0.f, 1.f);
|
||||
analog.override_enabled = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// unknown analog
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Analogs::write_analog_reset(std::string name) {
|
||||
|
||||
// check analog cache
|
||||
if (!analogs) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// find analog
|
||||
for (auto &analog : *this->analogs) {
|
||||
if (analog.getName() == name) {
|
||||
analog.override_enabled = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// unknown analog
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#include "analogs.h"
|
||||
#include <functional>
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "cfg/analog.h"
|
||||
#include "launcher/launcher.h"
|
||||
#include "games/io.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace rapidjson;
|
||||
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
Analogs::Analogs() : Module("analogs") {
|
||||
functions["read"] = std::bind(&Analogs::read, this, _1, _2);
|
||||
functions["write"] = std::bind(&Analogs::write, this, _1, _2);
|
||||
functions["write_reset"] = std::bind(&Analogs::write_reset, this, _1, _2);
|
||||
analogs = games::get_analogs(eamuse_get_game());
|
||||
}
|
||||
|
||||
/**
|
||||
* read()
|
||||
*/
|
||||
void Analogs::read(api::Request &req, Response &res) {
|
||||
|
||||
// check analog cache
|
||||
if (!analogs) {
|
||||
return;
|
||||
}
|
||||
|
||||
// add state for each analog
|
||||
for (auto &analog : *this->analogs) {
|
||||
Value state(kArrayType);
|
||||
Value analog_name(analog.getName().c_str(), res.doc()->GetAllocator());
|
||||
Value analog_state(GameAPI::Analogs::getState(RI_MGR, analog));
|
||||
Value analog_enabled(analog.override_enabled);
|
||||
state.PushBack(analog_name, res.doc()->GetAllocator());
|
||||
state.PushBack(analog_state, res.doc()->GetAllocator());
|
||||
state.PushBack(analog_enabled, res.doc()->GetAllocator());
|
||||
res.add_data(state);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* write([name: str, state: float], ...)
|
||||
*/
|
||||
void Analogs::write(Request &req, Response &res) {
|
||||
|
||||
// check analog cache
|
||||
if (!analogs)
|
||||
return;
|
||||
|
||||
// loop parameters
|
||||
for (Value ¶m : req.params.GetArray()) {
|
||||
|
||||
// check params
|
||||
if (!param.IsArray()) {
|
||||
error(res, "parameters must be arrays");
|
||||
return;
|
||||
}
|
||||
if (param.Size() < 2) {
|
||||
error_params_insufficient(res);
|
||||
continue;
|
||||
}
|
||||
if (!param[0].IsString()) {
|
||||
error_type(res, "name", "string");
|
||||
continue;
|
||||
}
|
||||
if (!param[1].IsFloat() && !param[1].IsInt()) {
|
||||
error_type(res, "state", "float");
|
||||
continue;
|
||||
}
|
||||
|
||||
// get params
|
||||
auto analog_name = param[0].GetString();
|
||||
auto analog_state = param[1].GetFloat();
|
||||
|
||||
// write analog state
|
||||
if (!this->write_analog(analog_name, analog_state)) {
|
||||
error_unknown(res, "analog", analog_name);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* write_reset()
|
||||
* write_reset([name: str], ...)
|
||||
*/
|
||||
void Analogs::write_reset(Request &req, Response &res) {
|
||||
|
||||
// check analog cache
|
||||
if (!analogs)
|
||||
return;
|
||||
|
||||
// get params
|
||||
auto params = req.params.GetArray();
|
||||
|
||||
// write_reset()
|
||||
if (params.Size() == 0) {
|
||||
if (analogs != nullptr) {
|
||||
for (auto &analog : *this->analogs) {
|
||||
analog.override_enabled = false;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// loop parameters
|
||||
for (Value ¶m : req.params.GetArray()) {
|
||||
|
||||
// check params
|
||||
if (!param.IsArray()) {
|
||||
error(res, "parameters must be arrays");
|
||||
return;
|
||||
}
|
||||
if (param.Size() < 1) {
|
||||
error_params_insufficient(res);
|
||||
continue;
|
||||
}
|
||||
if (!param[0].IsString()) {
|
||||
error_type(res, "name", "string");
|
||||
continue;
|
||||
}
|
||||
|
||||
// get params
|
||||
auto analog_name = param[0].GetString();
|
||||
|
||||
// write analog state
|
||||
if (!this->write_analog_reset(analog_name)) {
|
||||
error_unknown(res, "analog", analog_name);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Analogs::write_analog(std::string name, float state) {
|
||||
|
||||
// check analog cache
|
||||
if (!this->analogs) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// find analog
|
||||
for (auto &analog : *this->analogs) {
|
||||
if (analog.getName() == name) {
|
||||
analog.override_state = CLAMP(state, 0.f, 1.f);
|
||||
analog.override_enabled = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// unknown analog
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Analogs::write_analog_reset(std::string name) {
|
||||
|
||||
// check analog cache
|
||||
if (!analogs) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// find analog
|
||||
for (auto &analog : *this->analogs) {
|
||||
if (analog.getName() == name) {
|
||||
analog.override_enabled = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// unknown analog
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
+28
-28
@@ -1,28 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
#include "cfg/api.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class Analogs : public Module {
|
||||
public:
|
||||
Analogs();
|
||||
|
||||
private:
|
||||
|
||||
// state
|
||||
std::vector<Analog> *analogs;
|
||||
|
||||
// function definitions
|
||||
void read(Request &req, Response &res);
|
||||
void write(Request &req, Response &res);
|
||||
void write_reset(Request &req, Response &res);
|
||||
|
||||
// helper
|
||||
bool write_analog(std::string name, float state);
|
||||
bool write_analog_reset(std::string name);
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
#include "cfg/api.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class Analogs : public Module {
|
||||
public:
|
||||
Analogs();
|
||||
|
||||
private:
|
||||
|
||||
// state
|
||||
std::vector<Analog> *analogs;
|
||||
|
||||
// function definitions
|
||||
void read(Request &req, Response &res);
|
||||
void write(Request &req, Response &res);
|
||||
void write_reset(Request &req, Response &res);
|
||||
|
||||
// helper
|
||||
bool write_analog(std::string name, float state);
|
||||
bool write_analog_reset(std::string name);
|
||||
};
|
||||
}
|
||||
|
||||
+28
-28
@@ -1,28 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
#include "cfg/api.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class Buttons : public Module {
|
||||
public:
|
||||
Buttons();
|
||||
|
||||
private:
|
||||
|
||||
// state
|
||||
std::vector<Button> *buttons;
|
||||
|
||||
// function definitions
|
||||
void read(Request &req, Response &res);
|
||||
void write(Request &req, Response &res);
|
||||
void write_reset(Request &req, Response &res);
|
||||
|
||||
// helper
|
||||
bool write_button(std::string name, float state);
|
||||
bool write_button_reset(std::string name);
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
#include "cfg/api.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class Buttons : public Module {
|
||||
public:
|
||||
Buttons();
|
||||
|
||||
private:
|
||||
|
||||
// state
|
||||
std::vector<Button> *buttons;
|
||||
|
||||
// function definitions
|
||||
void read(Request &req, Response &res);
|
||||
void write(Request &req, Response &res);
|
||||
void write_reset(Request &req, Response &res);
|
||||
|
||||
// helper
|
||||
bool write_button(std::string name, float state);
|
||||
bool write_button_reset(std::string name);
|
||||
};
|
||||
}
|
||||
|
||||
+82
-82
@@ -1,82 +1,82 @@
|
||||
#include "capture.h"
|
||||
#include <functional>
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "hooks/graphics/graphics.h"
|
||||
#include "util/crypt.h"
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace rapidjson;
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
static thread_local std::vector<uint8_t> CAPTURE_BUFFER;
|
||||
|
||||
Capture::Capture() : Module("capture") {
|
||||
functions["get_screens"] = std::bind(&Capture::get_screens, this, _1, _2);
|
||||
functions["get_jpg"] = std::bind(&Capture::get_jpg, this, _1, _2);
|
||||
}
|
||||
|
||||
/**
|
||||
* get_screens()
|
||||
*/
|
||||
void Capture::get_screens(Request &req, Response &res) {
|
||||
|
||||
// aquire screens
|
||||
std::vector<int> screens;
|
||||
graphics_screens_get(screens);
|
||||
|
||||
// add screens to response
|
||||
for (auto &screen : screens) {
|
||||
res.add_data(screen);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get_jpg([screen=0, quality=70, downscale=0, divide=1])
|
||||
* screen: uint specifying the window
|
||||
* quality: uint in range [0, 100]
|
||||
* reduce: uint for dividing image size
|
||||
*/
|
||||
void Capture::get_jpg(Request &req, Response &res) {
|
||||
CAPTURE_BUFFER.reserve(1024 * 128);
|
||||
|
||||
// settings
|
||||
int screen = 0;
|
||||
int quality = 70;
|
||||
int divide = 1;
|
||||
if (req.params.Size() > 0 && req.params[0].IsUint())
|
||||
screen = req.params[0].GetUint();
|
||||
if (req.params.Size() > 1 && req.params[1].IsUint())
|
||||
quality = req.params[1].GetUint();
|
||||
if (req.params.Size() > 2 && req.params[2].IsUint())
|
||||
divide = req.params[2].GetUint();
|
||||
|
||||
// receive JPEG data
|
||||
uint64_t timestamp = 0;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
graphics_capture_trigger(screen);
|
||||
bool success = graphics_capture_receive_jpeg(screen, [] (uint8_t byte) {
|
||||
CAPTURE_BUFFER.push_back(byte);
|
||||
}, true, quality, true, divide, ×tamp, &width, &height);
|
||||
if (!success) {
|
||||
return;
|
||||
}
|
||||
|
||||
// encode to base64
|
||||
auto encoded = crypt::base64_encode(
|
||||
CAPTURE_BUFFER.data(),
|
||||
CAPTURE_BUFFER.size());
|
||||
|
||||
// clear buffer
|
||||
CAPTURE_BUFFER.clear();
|
||||
|
||||
// add data to response
|
||||
Value data;
|
||||
data.SetString(encoded.c_str(), encoded.length(), res.doc()->GetAllocator());
|
||||
res.add_data(timestamp);
|
||||
res.add_data(width);
|
||||
res.add_data(height);
|
||||
res.add_data(data);
|
||||
}
|
||||
}
|
||||
#include "capture.h"
|
||||
#include <functional>
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "hooks/graphics/graphics.h"
|
||||
#include "util/crypt.h"
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace rapidjson;
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
static thread_local std::vector<uint8_t> CAPTURE_BUFFER;
|
||||
|
||||
Capture::Capture() : Module("capture") {
|
||||
functions["get_screens"] = std::bind(&Capture::get_screens, this, _1, _2);
|
||||
functions["get_jpg"] = std::bind(&Capture::get_jpg, this, _1, _2);
|
||||
}
|
||||
|
||||
/**
|
||||
* get_screens()
|
||||
*/
|
||||
void Capture::get_screens(Request &req, Response &res) {
|
||||
|
||||
// aquire screens
|
||||
std::vector<int> screens;
|
||||
graphics_screens_get(screens);
|
||||
|
||||
// add screens to response
|
||||
for (auto &screen : screens) {
|
||||
res.add_data(screen);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get_jpg([screen=0, quality=70, downscale=0, divide=1])
|
||||
* screen: uint specifying the window
|
||||
* quality: uint in range [0, 100]
|
||||
* reduce: uint for dividing image size
|
||||
*/
|
||||
void Capture::get_jpg(Request &req, Response &res) {
|
||||
CAPTURE_BUFFER.reserve(1024 * 128);
|
||||
|
||||
// settings
|
||||
int screen = 0;
|
||||
int quality = 70;
|
||||
int divide = 1;
|
||||
if (req.params.Size() > 0 && req.params[0].IsUint())
|
||||
screen = req.params[0].GetUint();
|
||||
if (req.params.Size() > 1 && req.params[1].IsUint())
|
||||
quality = req.params[1].GetUint();
|
||||
if (req.params.Size() > 2 && req.params[2].IsUint())
|
||||
divide = req.params[2].GetUint();
|
||||
|
||||
// receive JPEG data
|
||||
uint64_t timestamp = 0;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
graphics_capture_trigger(screen);
|
||||
bool success = graphics_capture_receive_jpeg(screen, [] (uint8_t byte) {
|
||||
CAPTURE_BUFFER.push_back(byte);
|
||||
}, true, quality, true, divide, ×tamp, &width, &height);
|
||||
if (!success) {
|
||||
return;
|
||||
}
|
||||
|
||||
// encode to base64
|
||||
auto encoded = crypt::base64_encode(
|
||||
CAPTURE_BUFFER.data(),
|
||||
CAPTURE_BUFFER.size());
|
||||
|
||||
// clear buffer
|
||||
CAPTURE_BUFFER.clear();
|
||||
|
||||
// add data to response
|
||||
Value data;
|
||||
data.SetString(encoded.c_str(), encoded.length(), res.doc()->GetAllocator());
|
||||
res.add_data(timestamp);
|
||||
res.add_data(width);
|
||||
res.add_data(height);
|
||||
res.add_data(data);
|
||||
}
|
||||
}
|
||||
|
||||
+18
-18
@@ -1,18 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class Capture : public Module {
|
||||
public:
|
||||
Capture();
|
||||
|
||||
private:
|
||||
|
||||
// function definitions
|
||||
void get_screens(Request &req, Response &res);
|
||||
void get_jpg(Request &req, Response &res);
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class Capture : public Module {
|
||||
public:
|
||||
Capture();
|
||||
|
||||
private:
|
||||
|
||||
// function definitions
|
||||
void get_screens(Request &req, Response &res);
|
||||
void get_jpg(Request &req, Response &res);
|
||||
};
|
||||
}
|
||||
|
||||
+17
-17
@@ -1,17 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class Card : public Module {
|
||||
public:
|
||||
Card();
|
||||
|
||||
private:
|
||||
|
||||
// function definitions
|
||||
void insert(Request &req, Response &res);
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class Card : public Module {
|
||||
public:
|
||||
Card();
|
||||
|
||||
private:
|
||||
|
||||
// function definitions
|
||||
void insert(Request &req, Response &res);
|
||||
};
|
||||
}
|
||||
|
||||
+20
-20
@@ -1,20 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class Coin : public Module {
|
||||
public:
|
||||
Coin();
|
||||
|
||||
private:
|
||||
|
||||
// function definitions
|
||||
void get(Request &req, Response &res);
|
||||
void set(Request &req, Response &res);
|
||||
void insert(Request &req, Response &res);
|
||||
void blocker_get(Request &req, Response &res);
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class Coin : public Module {
|
||||
public:
|
||||
Coin();
|
||||
|
||||
private:
|
||||
|
||||
// function definitions
|
||||
void get(Request &req, Response &res);
|
||||
void set(Request &req, Response &res);
|
||||
void insert(Request &req, Response &res);
|
||||
void blocker_get(Request &req, Response &res);
|
||||
};
|
||||
}
|
||||
|
||||
+180
-180
@@ -1,180 +1,180 @@
|
||||
#include "control.h"
|
||||
|
||||
#include <csignal>
|
||||
#include <functional>
|
||||
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "launcher/shutdown.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/crypt.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace rapidjson;
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
struct SignalMapping {
|
||||
int signum;
|
||||
const char* name;
|
||||
};
|
||||
static SignalMapping SIGNAL_MAPPINGS[] = {
|
||||
{ SIGABRT, "SIGABRT" },
|
||||
{ SIGFPE, "SIGFPE" },
|
||||
{ SIGILL, "SIGILL" },
|
||||
{ SIGINT, "SIGINT" },
|
||||
{ SIGSEGV, "SIGSEGV" },
|
||||
{ SIGTERM, "SIGTERM" },
|
||||
};
|
||||
|
||||
static inline bool acquire_shutdown_privs() {
|
||||
|
||||
// check if already acquired
|
||||
static bool acquired = false;
|
||||
if (acquired)
|
||||
return true;
|
||||
|
||||
// get process token
|
||||
HANDLE hToken;
|
||||
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
|
||||
return false;
|
||||
|
||||
// get the LUID for the shutdown privilege
|
||||
TOKEN_PRIVILEGES tkp;
|
||||
LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid);
|
||||
tkp.PrivilegeCount = 1;
|
||||
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
||||
|
||||
// get the shutdown privilege for this process
|
||||
AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES) NULL, 0);
|
||||
|
||||
// check for error
|
||||
bool success = GetLastError() == ERROR_SUCCESS;
|
||||
if (success)
|
||||
acquired = true;
|
||||
return success;
|
||||
}
|
||||
|
||||
Control::Control() : Module("control", true) {
|
||||
functions["raise"] = std::bind(&Control::raise, this, _1, _2);
|
||||
functions["exit"] = std::bind(&Control::exit, this, _1, _2);
|
||||
functions["restart"] = std::bind(&Control::restart, this, _1, _2);
|
||||
functions["session_refresh"] = std::bind(&Control::session_refresh, this, _1, _2);
|
||||
functions["shutdown"] = std::bind(&Control::shutdown, this, _1, _2);
|
||||
functions["reboot"] = std::bind(&Control::reboot, this, _1, _2);
|
||||
}
|
||||
|
||||
/**
|
||||
* raise(signal: str)
|
||||
*/
|
||||
void Control::raise(Request &req, Response &res) {
|
||||
|
||||
// check args
|
||||
if (req.params.Size() < 1)
|
||||
return error_params_insufficient(res);
|
||||
if (!req.params[0].IsString())
|
||||
return error_type(res, "signal", "string");
|
||||
|
||||
// get signal
|
||||
auto signal_str = req.params[0].GetString();
|
||||
int signal_val = -1;
|
||||
for (auto mapping : SIGNAL_MAPPINGS) {
|
||||
if (_stricmp(mapping.name, signal_str) == 0) {
|
||||
signal_val = mapping.signum;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// check if not found
|
||||
if (signal_val < 0)
|
||||
return error_unknown(res, "signal", signal_str);
|
||||
|
||||
// raise signal
|
||||
if (::raise(signal_val))
|
||||
return error(res, "Failed to raise signo " + to_string(signal_val));
|
||||
}
|
||||
|
||||
/**
|
||||
* exit()
|
||||
* exit(code: int)
|
||||
*/
|
||||
void Control::exit(Request &req, Response &res) {
|
||||
|
||||
// exit()
|
||||
if (req.params.Size() == 0) {
|
||||
launcher::shutdown();
|
||||
}
|
||||
|
||||
// check code
|
||||
if (!req.params[0].IsInt())
|
||||
return error_type(res, "code", "int");
|
||||
|
||||
// exit
|
||||
launcher::shutdown(req.params[0].GetInt());
|
||||
}
|
||||
|
||||
/**
|
||||
* restart()
|
||||
*/
|
||||
void Control::restart(Request &req, Response &res) {
|
||||
|
||||
// restart launcher
|
||||
launcher::restart();
|
||||
}
|
||||
|
||||
/**
|
||||
* session_refresh()
|
||||
*/
|
||||
void Control::session_refresh(Request &req, Response &res) {
|
||||
|
||||
// generate new password
|
||||
uint8_t password_bin[128];
|
||||
crypt::random_bytes(password_bin, std::size(password_bin));
|
||||
std::string password = bin2hex(&password_bin[0], std::size(password_bin));
|
||||
|
||||
// add to response
|
||||
Value password_val(password.c_str(), res.doc()->GetAllocator());
|
||||
res.add_data(password_val);
|
||||
|
||||
// change password
|
||||
res.password_change(password);
|
||||
}
|
||||
|
||||
/**
|
||||
* shutdown()
|
||||
*/
|
||||
void Control::shutdown(Request &req, Response &res) {
|
||||
|
||||
// acquire privileges
|
||||
if (!acquire_shutdown_privs())
|
||||
return error(res, "Unable to acquire shutdown privileges");
|
||||
|
||||
// exit windows
|
||||
if (!ExitWindowsEx(EWX_POWEROFF | EWX_FORCE,
|
||||
SHTDN_REASON_MAJOR_APPLICATION |
|
||||
SHTDN_REASON_MINOR_MAINTENANCE))
|
||||
return error(res, "Unable to shutdown system");
|
||||
|
||||
// terminate this process
|
||||
launcher::shutdown(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* reboot()
|
||||
*/
|
||||
void Control::reboot(Request &req, Response &res) {
|
||||
|
||||
// acquire privileges
|
||||
if (!acquire_shutdown_privs())
|
||||
return error(res, "Unable to acquire shutdown privileges");
|
||||
|
||||
// exit windows
|
||||
if (!ExitWindowsEx(EWX_REBOOT | EWX_FORCE,
|
||||
SHTDN_REASON_MAJOR_APPLICATION |
|
||||
SHTDN_REASON_MINOR_MAINTENANCE))
|
||||
return error(res, "Unable to reboot system");
|
||||
|
||||
// terminate this process
|
||||
launcher::shutdown(0);
|
||||
}
|
||||
}
|
||||
#include "control.h"
|
||||
|
||||
#include <csignal>
|
||||
#include <functional>
|
||||
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "launcher/shutdown.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/crypt.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace rapidjson;
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
struct SignalMapping {
|
||||
int signum;
|
||||
const char* name;
|
||||
};
|
||||
static SignalMapping SIGNAL_MAPPINGS[] = {
|
||||
{ SIGABRT, "SIGABRT" },
|
||||
{ SIGFPE, "SIGFPE" },
|
||||
{ SIGILL, "SIGILL" },
|
||||
{ SIGINT, "SIGINT" },
|
||||
{ SIGSEGV, "SIGSEGV" },
|
||||
{ SIGTERM, "SIGTERM" },
|
||||
};
|
||||
|
||||
static inline bool acquire_shutdown_privs() {
|
||||
|
||||
// check if already acquired
|
||||
static bool acquired = false;
|
||||
if (acquired)
|
||||
return true;
|
||||
|
||||
// get process token
|
||||
HANDLE hToken;
|
||||
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
|
||||
return false;
|
||||
|
||||
// get the LUID for the shutdown privilege
|
||||
TOKEN_PRIVILEGES tkp;
|
||||
LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid);
|
||||
tkp.PrivilegeCount = 1;
|
||||
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
||||
|
||||
// get the shutdown privilege for this process
|
||||
AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES) NULL, 0);
|
||||
|
||||
// check for error
|
||||
bool success = GetLastError() == ERROR_SUCCESS;
|
||||
if (success)
|
||||
acquired = true;
|
||||
return success;
|
||||
}
|
||||
|
||||
Control::Control() : Module("control", true) {
|
||||
functions["raise"] = std::bind(&Control::raise, this, _1, _2);
|
||||
functions["exit"] = std::bind(&Control::exit, this, _1, _2);
|
||||
functions["restart"] = std::bind(&Control::restart, this, _1, _2);
|
||||
functions["session_refresh"] = std::bind(&Control::session_refresh, this, _1, _2);
|
||||
functions["shutdown"] = std::bind(&Control::shutdown, this, _1, _2);
|
||||
functions["reboot"] = std::bind(&Control::reboot, this, _1, _2);
|
||||
}
|
||||
|
||||
/**
|
||||
* raise(signal: str)
|
||||
*/
|
||||
void Control::raise(Request &req, Response &res) {
|
||||
|
||||
// check args
|
||||
if (req.params.Size() < 1)
|
||||
return error_params_insufficient(res);
|
||||
if (!req.params[0].IsString())
|
||||
return error_type(res, "signal", "string");
|
||||
|
||||
// get signal
|
||||
auto signal_str = req.params[0].GetString();
|
||||
int signal_val = -1;
|
||||
for (auto mapping : SIGNAL_MAPPINGS) {
|
||||
if (_stricmp(mapping.name, signal_str) == 0) {
|
||||
signal_val = mapping.signum;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// check if not found
|
||||
if (signal_val < 0)
|
||||
return error_unknown(res, "signal", signal_str);
|
||||
|
||||
// raise signal
|
||||
if (::raise(signal_val))
|
||||
return error(res, "Failed to raise signo " + to_string(signal_val));
|
||||
}
|
||||
|
||||
/**
|
||||
* exit()
|
||||
* exit(code: int)
|
||||
*/
|
||||
void Control::exit(Request &req, Response &res) {
|
||||
|
||||
// exit()
|
||||
if (req.params.Size() == 0) {
|
||||
launcher::shutdown();
|
||||
}
|
||||
|
||||
// check code
|
||||
if (!req.params[0].IsInt())
|
||||
return error_type(res, "code", "int");
|
||||
|
||||
// exit
|
||||
launcher::shutdown(req.params[0].GetInt());
|
||||
}
|
||||
|
||||
/**
|
||||
* restart()
|
||||
*/
|
||||
void Control::restart(Request &req, Response &res) {
|
||||
|
||||
// restart launcher
|
||||
launcher::restart();
|
||||
}
|
||||
|
||||
/**
|
||||
* session_refresh()
|
||||
*/
|
||||
void Control::session_refresh(Request &req, Response &res) {
|
||||
|
||||
// generate new password
|
||||
uint8_t password_bin[128];
|
||||
crypt::random_bytes(password_bin, std::size(password_bin));
|
||||
std::string password = bin2hex(&password_bin[0], std::size(password_bin));
|
||||
|
||||
// add to response
|
||||
Value password_val(password.c_str(), res.doc()->GetAllocator());
|
||||
res.add_data(password_val);
|
||||
|
||||
// change password
|
||||
res.password_change(password);
|
||||
}
|
||||
|
||||
/**
|
||||
* shutdown()
|
||||
*/
|
||||
void Control::shutdown(Request &req, Response &res) {
|
||||
|
||||
// acquire privileges
|
||||
if (!acquire_shutdown_privs())
|
||||
return error(res, "Unable to acquire shutdown privileges");
|
||||
|
||||
// exit windows
|
||||
if (!ExitWindowsEx(EWX_POWEROFF | EWX_FORCE,
|
||||
SHTDN_REASON_MAJOR_APPLICATION |
|
||||
SHTDN_REASON_MINOR_MAINTENANCE))
|
||||
return error(res, "Unable to shutdown system");
|
||||
|
||||
// terminate this process
|
||||
launcher::shutdown(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* reboot()
|
||||
*/
|
||||
void Control::reboot(Request &req, Response &res) {
|
||||
|
||||
// acquire privileges
|
||||
if (!acquire_shutdown_privs())
|
||||
return error(res, "Unable to acquire shutdown privileges");
|
||||
|
||||
// exit windows
|
||||
if (!ExitWindowsEx(EWX_REBOOT | EWX_FORCE,
|
||||
SHTDN_REASON_MAJOR_APPLICATION |
|
||||
SHTDN_REASON_MINOR_MAINTENANCE))
|
||||
return error(res, "Unable to reboot system");
|
||||
|
||||
// terminate this process
|
||||
launcher::shutdown(0);
|
||||
}
|
||||
}
|
||||
|
||||
+22
-22
@@ -1,22 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class Control : public Module {
|
||||
public:
|
||||
Control();
|
||||
|
||||
private:
|
||||
|
||||
// function definitions
|
||||
void raise(Request &req, Response &res);
|
||||
void exit(Request &req, Response &res);
|
||||
void restart(Request &req, Response &res);
|
||||
void session_refresh(Request &req, Response &res);
|
||||
void shutdown(Request &req, Response &res);
|
||||
void reboot(Request &req, Response &res);
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class Control : public Module {
|
||||
public:
|
||||
Control();
|
||||
|
||||
private:
|
||||
|
||||
// function definitions
|
||||
void raise(Request &req, Response &res);
|
||||
void exit(Request &req, Response &res);
|
||||
void restart(Request &req, Response &res);
|
||||
void session_refresh(Request &req, Response &res);
|
||||
void shutdown(Request &req, Response &res);
|
||||
void reboot(Request &req, Response &res);
|
||||
};
|
||||
}
|
||||
|
||||
+19
-19
@@ -1,19 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class Info : public Module {
|
||||
public:
|
||||
Info();
|
||||
|
||||
private:
|
||||
|
||||
// function definitions
|
||||
void avs(Request &req, Response &res);
|
||||
void launcher(Request &req, Response &res);
|
||||
void memory(Request &req, Response &res);
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class Info : public Module {
|
||||
public:
|
||||
Info();
|
||||
|
||||
private:
|
||||
|
||||
// function definitions
|
||||
void avs(Request &req, Response &res);
|
||||
void launcher(Request &req, Response &res);
|
||||
void memory(Request &req, Response &res);
|
||||
};
|
||||
}
|
||||
|
||||
+176
-176
@@ -1,176 +1,176 @@
|
||||
#include "keypads.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "avs/game.h"
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "misc/eamuse.h"
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace rapidjson;
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
struct KeypadMapping {
|
||||
char character;
|
||||
uint16_t state;
|
||||
};
|
||||
|
||||
static KeypadMapping KEYPAD_MAPPINGS[] = {
|
||||
{ '0', 1 << EAM_IO_KEYPAD_0 },
|
||||
{ '1', 1 << EAM_IO_KEYPAD_1 },
|
||||
{ '2', 1 << EAM_IO_KEYPAD_2 },
|
||||
{ '3', 1 << EAM_IO_KEYPAD_3 },
|
||||
{ '4', 1 << EAM_IO_KEYPAD_4 },
|
||||
{ '5', 1 << EAM_IO_KEYPAD_5 },
|
||||
{ '6', 1 << EAM_IO_KEYPAD_6 },
|
||||
{ '7', 1 << EAM_IO_KEYPAD_7 },
|
||||
{ '8', 1 << EAM_IO_KEYPAD_8 },
|
||||
{ '9', 1 << EAM_IO_KEYPAD_9 },
|
||||
{ 'A', 1 << EAM_IO_KEYPAD_00 },
|
||||
{ 'D', 1 << EAM_IO_KEYPAD_DECIMAL },
|
||||
};
|
||||
|
||||
Keypads::Keypads() : Module("keypads") {
|
||||
functions["write"] = std::bind(&Keypads::write, this, _1, _2);
|
||||
functions["set"] = std::bind(&Keypads::set, this, _1, _2);
|
||||
functions["get"] = std::bind(&Keypads::get, this, _1, _2);
|
||||
}
|
||||
|
||||
/**
|
||||
* write(keypad: uint, input: str)
|
||||
*/
|
||||
void Keypads::write(Request &req, Response &res) {
|
||||
|
||||
// check params
|
||||
if (req.params.Size() < 2) {
|
||||
return error_params_insufficient(res);
|
||||
}
|
||||
if (!req.params[0].IsUint()) {
|
||||
return error_type(res, "keypad", "uint");
|
||||
}
|
||||
if (!req.params[1].IsString()) {
|
||||
return error_type(res, "input", "string");
|
||||
}
|
||||
|
||||
// get params
|
||||
auto keypad = req.params[0].GetUint();
|
||||
auto input = std::string(req.params[1].GetString());
|
||||
|
||||
// process all chars
|
||||
for (auto c : input) {
|
||||
uint16_t state = 0;
|
||||
|
||||
// find mapping
|
||||
bool mapping_found = false;
|
||||
for (auto &mapping : KEYPAD_MAPPINGS) {
|
||||
if (_strnicmp(&mapping.character, &c, 1) == 0) {
|
||||
state |= mapping.state;
|
||||
mapping_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// check for error
|
||||
if (!mapping_found) {
|
||||
return error_unknown(res, "char", std::string("") + c);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write input to keypad.
|
||||
* We try to make sure it was accepted by waiting a bit more than two frames.
|
||||
*/
|
||||
DWORD sleep_time = 70;
|
||||
if (avs::game::is_model("MDX")) {
|
||||
|
||||
// cuz fuck DDR
|
||||
sleep_time = 150;
|
||||
}
|
||||
|
||||
// set
|
||||
eamuse_set_keypad_overrides(keypad, state);
|
||||
Sleep(sleep_time);
|
||||
|
||||
// unset
|
||||
eamuse_set_keypad_overrides(keypad, 0);
|
||||
Sleep(sleep_time);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* set(keypad: uint, key: char, ...)
|
||||
*/
|
||||
void Keypads::set(Request &req, Response &res) {
|
||||
|
||||
// check keypad
|
||||
if (req.params.Size() < 1) {
|
||||
return error_params_insufficient(res);
|
||||
}
|
||||
if (!req.params[0].IsUint()) {
|
||||
return error_type(res, "keypad", "uint");
|
||||
}
|
||||
auto keypad = req.params[0].GetUint();
|
||||
|
||||
// iterate params
|
||||
uint16_t state = 0;
|
||||
auto params = req.params.GetArray();
|
||||
for (size_t i = 1; i < params.Size(); i++) {
|
||||
auto ¶m = params[i];
|
||||
|
||||
// check key
|
||||
if (!param.IsString()) {
|
||||
error_type(res, "key", "char");
|
||||
}
|
||||
if (param.GetStringLength() < 1) {
|
||||
error_size(res, "key", 1);
|
||||
}
|
||||
|
||||
// find mapping
|
||||
auto key = param.GetString();
|
||||
bool mapping_found = false;
|
||||
for (auto &mapping : KEYPAD_MAPPINGS) {
|
||||
if (_strnicmp(&mapping.character, key, 1) == 0) {
|
||||
state |= mapping.state;
|
||||
mapping_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// check for error
|
||||
if (!mapping_found) {
|
||||
return error_unknown(res, "key", key);
|
||||
}
|
||||
}
|
||||
|
||||
// set keypad state
|
||||
eamuse_set_keypad_overrides(keypad, state);
|
||||
}
|
||||
|
||||
/**
|
||||
* get(keypad: uint)
|
||||
*/
|
||||
void Keypads::get(Request &req, Response &res) {
|
||||
|
||||
// check keypad
|
||||
if (req.params.Size() < 1) {
|
||||
return error_params_insufficient(res);
|
||||
}
|
||||
if (!req.params[0].IsUint()) {
|
||||
return error_type(res, "keypad", "uint");
|
||||
}
|
||||
auto keypad = req.params[0].GetUint();
|
||||
|
||||
// get keypad state
|
||||
auto state = eamuse_get_keypad_state(keypad);
|
||||
|
||||
// add keys to response
|
||||
for (auto &mapping : KEYPAD_MAPPINGS) {
|
||||
if (state & mapping.state) {
|
||||
Value val(&mapping.character, 1);
|
||||
res.add_data(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#include "keypads.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "avs/game.h"
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "misc/eamuse.h"
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace rapidjson;
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
struct KeypadMapping {
|
||||
char character;
|
||||
uint16_t state;
|
||||
};
|
||||
|
||||
static KeypadMapping KEYPAD_MAPPINGS[] = {
|
||||
{ '0', 1 << EAM_IO_KEYPAD_0 },
|
||||
{ '1', 1 << EAM_IO_KEYPAD_1 },
|
||||
{ '2', 1 << EAM_IO_KEYPAD_2 },
|
||||
{ '3', 1 << EAM_IO_KEYPAD_3 },
|
||||
{ '4', 1 << EAM_IO_KEYPAD_4 },
|
||||
{ '5', 1 << EAM_IO_KEYPAD_5 },
|
||||
{ '6', 1 << EAM_IO_KEYPAD_6 },
|
||||
{ '7', 1 << EAM_IO_KEYPAD_7 },
|
||||
{ '8', 1 << EAM_IO_KEYPAD_8 },
|
||||
{ '9', 1 << EAM_IO_KEYPAD_9 },
|
||||
{ 'A', 1 << EAM_IO_KEYPAD_00 },
|
||||
{ 'D', 1 << EAM_IO_KEYPAD_DECIMAL },
|
||||
};
|
||||
|
||||
Keypads::Keypads() : Module("keypads") {
|
||||
functions["write"] = std::bind(&Keypads::write, this, _1, _2);
|
||||
functions["set"] = std::bind(&Keypads::set, this, _1, _2);
|
||||
functions["get"] = std::bind(&Keypads::get, this, _1, _2);
|
||||
}
|
||||
|
||||
/**
|
||||
* write(keypad: uint, input: str)
|
||||
*/
|
||||
void Keypads::write(Request &req, Response &res) {
|
||||
|
||||
// check params
|
||||
if (req.params.Size() < 2) {
|
||||
return error_params_insufficient(res);
|
||||
}
|
||||
if (!req.params[0].IsUint()) {
|
||||
return error_type(res, "keypad", "uint");
|
||||
}
|
||||
if (!req.params[1].IsString()) {
|
||||
return error_type(res, "input", "string");
|
||||
}
|
||||
|
||||
// get params
|
||||
auto keypad = req.params[0].GetUint();
|
||||
auto input = std::string(req.params[1].GetString());
|
||||
|
||||
// process all chars
|
||||
for (auto c : input) {
|
||||
uint16_t state = 0;
|
||||
|
||||
// find mapping
|
||||
bool mapping_found = false;
|
||||
for (auto &mapping : KEYPAD_MAPPINGS) {
|
||||
if (_strnicmp(&mapping.character, &c, 1) == 0) {
|
||||
state |= mapping.state;
|
||||
mapping_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// check for error
|
||||
if (!mapping_found) {
|
||||
return error_unknown(res, "char", std::string("") + c);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write input to keypad.
|
||||
* We try to make sure it was accepted by waiting a bit more than two frames.
|
||||
*/
|
||||
DWORD sleep_time = 70;
|
||||
if (avs::game::is_model("MDX")) {
|
||||
|
||||
// cuz fuck DDR
|
||||
sleep_time = 150;
|
||||
}
|
||||
|
||||
// set
|
||||
eamuse_set_keypad_overrides(keypad, state);
|
||||
Sleep(sleep_time);
|
||||
|
||||
// unset
|
||||
eamuse_set_keypad_overrides(keypad, 0);
|
||||
Sleep(sleep_time);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* set(keypad: uint, key: char, ...)
|
||||
*/
|
||||
void Keypads::set(Request &req, Response &res) {
|
||||
|
||||
// check keypad
|
||||
if (req.params.Size() < 1) {
|
||||
return error_params_insufficient(res);
|
||||
}
|
||||
if (!req.params[0].IsUint()) {
|
||||
return error_type(res, "keypad", "uint");
|
||||
}
|
||||
auto keypad = req.params[0].GetUint();
|
||||
|
||||
// iterate params
|
||||
uint16_t state = 0;
|
||||
auto params = req.params.GetArray();
|
||||
for (size_t i = 1; i < params.Size(); i++) {
|
||||
auto ¶m = params[i];
|
||||
|
||||
// check key
|
||||
if (!param.IsString()) {
|
||||
error_type(res, "key", "char");
|
||||
}
|
||||
if (param.GetStringLength() < 1) {
|
||||
error_size(res, "key", 1);
|
||||
}
|
||||
|
||||
// find mapping
|
||||
auto key = param.GetString();
|
||||
bool mapping_found = false;
|
||||
for (auto &mapping : KEYPAD_MAPPINGS) {
|
||||
if (_strnicmp(&mapping.character, key, 1) == 0) {
|
||||
state |= mapping.state;
|
||||
mapping_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// check for error
|
||||
if (!mapping_found) {
|
||||
return error_unknown(res, "key", key);
|
||||
}
|
||||
}
|
||||
|
||||
// set keypad state
|
||||
eamuse_set_keypad_overrides(keypad, state);
|
||||
}
|
||||
|
||||
/**
|
||||
* get(keypad: uint)
|
||||
*/
|
||||
void Keypads::get(Request &req, Response &res) {
|
||||
|
||||
// check keypad
|
||||
if (req.params.Size() < 1) {
|
||||
return error_params_insufficient(res);
|
||||
}
|
||||
if (!req.params[0].IsUint()) {
|
||||
return error_type(res, "keypad", "uint");
|
||||
}
|
||||
auto keypad = req.params[0].GetUint();
|
||||
|
||||
// get keypad state
|
||||
auto state = eamuse_get_keypad_state(keypad);
|
||||
|
||||
// add keys to response
|
||||
for (auto &mapping : KEYPAD_MAPPINGS) {
|
||||
if (state & mapping.state) {
|
||||
Value val(&mapping.character, 1);
|
||||
res.add_data(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+19
-19
@@ -1,19 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class Keypads : public Module {
|
||||
public:
|
||||
Keypads();
|
||||
|
||||
private:
|
||||
|
||||
// function definitions
|
||||
void write(Request &req, Response &res);
|
||||
void set(Request &req, Response &res);
|
||||
void get(Request &req, Response &res);
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class Keypads : public Module {
|
||||
public:
|
||||
Keypads();
|
||||
|
||||
private:
|
||||
|
||||
// function definitions
|
||||
void write(Request &req, Response &res);
|
||||
void set(Request &req, Response &res);
|
||||
void get(Request &req, Response &res);
|
||||
};
|
||||
}
|
||||
|
||||
+36
-36
@@ -1,36 +1,36 @@
|
||||
#include "lcd.h"
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "games/shared/lcdhandle.h"
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace rapidjson;
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
LCD::LCD() : Module("lcd") {
|
||||
functions["info"] = std::bind(&LCD::info, this, _1, _2);
|
||||
}
|
||||
|
||||
/*
|
||||
* info()
|
||||
*/
|
||||
void LCD::info(Request &req, Response &res) {
|
||||
|
||||
// get allocator
|
||||
auto &alloc = res.doc()->GetAllocator();
|
||||
|
||||
// build info object
|
||||
Value info(kObjectType);
|
||||
info.AddMember("enabled", games::shared::LCD_ENABLED, alloc);
|
||||
info.AddMember("csm", StringRef(games::shared::LCD_CSM.c_str()), alloc);
|
||||
info.AddMember("bri", games::shared::LCD_BRI, alloc);
|
||||
info.AddMember("con", games::shared::LCD_CON, alloc);
|
||||
info.AddMember("bl", games::shared::LCD_BL, alloc);
|
||||
info.AddMember("red", games::shared::LCD_RED, alloc);
|
||||
info.AddMember("green", games::shared::LCD_GREEN, alloc);
|
||||
info.AddMember("blue", games::shared::LCD_BLUE, alloc);
|
||||
|
||||
// add info object
|
||||
res.add_data(info);
|
||||
}
|
||||
}
|
||||
#include "lcd.h"
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "games/shared/lcdhandle.h"
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace rapidjson;
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
LCD::LCD() : Module("lcd") {
|
||||
functions["info"] = std::bind(&LCD::info, this, _1, _2);
|
||||
}
|
||||
|
||||
/*
|
||||
* info()
|
||||
*/
|
||||
void LCD::info(Request &req, Response &res) {
|
||||
|
||||
// get allocator
|
||||
auto &alloc = res.doc()->GetAllocator();
|
||||
|
||||
// build info object
|
||||
Value info(kObjectType);
|
||||
info.AddMember("enabled", games::shared::LCD_ENABLED, alloc);
|
||||
info.AddMember("csm", StringRef(games::shared::LCD_CSM.c_str()), alloc);
|
||||
info.AddMember("bri", games::shared::LCD_BRI, alloc);
|
||||
info.AddMember("con", games::shared::LCD_CON, alloc);
|
||||
info.AddMember("bl", games::shared::LCD_BL, alloc);
|
||||
info.AddMember("red", games::shared::LCD_RED, alloc);
|
||||
info.AddMember("green", games::shared::LCD_GREEN, alloc);
|
||||
info.AddMember("blue", games::shared::LCD_BLUE, alloc);
|
||||
|
||||
// add info object
|
||||
res.add_data(info);
|
||||
}
|
||||
}
|
||||
|
||||
+17
-17
@@ -1,17 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class LCD : public Module {
|
||||
public:
|
||||
LCD();
|
||||
|
||||
private:
|
||||
|
||||
// function definitions
|
||||
void info(Request &req, Response &res);
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class LCD : public Module {
|
||||
public:
|
||||
LCD();
|
||||
|
||||
private:
|
||||
|
||||
// function definitions
|
||||
void info(Request &req, Response &res);
|
||||
};
|
||||
}
|
||||
|
||||
+179
-179
@@ -1,179 +1,179 @@
|
||||
#include "lights.h"
|
||||
#include <functional>
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "cfg/light.h"
|
||||
#include "launcher/launcher.h"
|
||||
#include "games/io.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace rapidjson;
|
||||
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
Lights::Lights() : Module("lights") {
|
||||
functions["read"] = std::bind(&Lights::read, this, _1, _2);
|
||||
functions["write"] = std::bind(&Lights::write, this, _1, _2);
|
||||
functions["write_reset"] = std::bind(&Lights::write_reset, this, _1, _2);
|
||||
lights = games::get_lights(eamuse_get_game());
|
||||
}
|
||||
|
||||
/**
|
||||
* read()
|
||||
*/
|
||||
void Lights::read(api::Request &req, Response &res) {
|
||||
|
||||
// check light cache
|
||||
if (!this->lights) {
|
||||
return;
|
||||
}
|
||||
|
||||
// add state for each light
|
||||
for (auto &light : *this->lights) {
|
||||
Value state(kArrayType);
|
||||
Value light_name(light.getName().c_str(), res.doc()->GetAllocator());
|
||||
Value light_state(GameAPI::Lights::readLight(RI_MGR, light));
|
||||
Value light_enabled(light.override_enabled);
|
||||
state.PushBack(light_name, res.doc()->GetAllocator());
|
||||
state.PushBack(light_state, res.doc()->GetAllocator());
|
||||
state.PushBack(light_enabled, res.doc()->GetAllocator());
|
||||
res.add_data(state);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* write([name: str, state: float], ...)
|
||||
*/
|
||||
void Lights::write(Request &req, Response &res) {
|
||||
|
||||
// check light cache
|
||||
if (!this->lights) {
|
||||
return;
|
||||
}
|
||||
|
||||
// loop parameters
|
||||
for (Value ¶m : req.params.GetArray()) {
|
||||
|
||||
// check params
|
||||
if (!param.IsArray()) {
|
||||
error(res, "parameters must be arrays");
|
||||
return;
|
||||
}
|
||||
if (param.Size() < 2) {
|
||||
error_params_insufficient(res);
|
||||
continue;
|
||||
}
|
||||
if (!param[0].IsString()) {
|
||||
error_type(res, "name", "string");
|
||||
continue;
|
||||
}
|
||||
if (!param[1].IsFloat() && !param[1].IsInt()) {
|
||||
error_type(res, "state", "float");
|
||||
continue;
|
||||
}
|
||||
|
||||
// get params
|
||||
auto light_name = param[0].GetString();
|
||||
auto light_state = param[1].GetFloat();
|
||||
|
||||
// write light state
|
||||
if (!this->write_light(light_name, light_state)) {
|
||||
error_unknown(res, "light", light_name);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* write_reset()
|
||||
* write_reset([name: str], ...)
|
||||
*/
|
||||
void Lights::write_reset(Request &req, Response &res) {
|
||||
|
||||
// check light cache
|
||||
if (!this->lights) {
|
||||
return;
|
||||
}
|
||||
|
||||
// get params
|
||||
auto params = req.params.GetArray();
|
||||
|
||||
// write_reset()
|
||||
if (params.Size() == 0) {
|
||||
if (lights != nullptr) {
|
||||
for (auto &light : *this->lights) {
|
||||
light.override_enabled = false;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// loop parameters
|
||||
for (Value ¶m : req.params.GetArray()) {
|
||||
|
||||
// check params
|
||||
if (!param.IsArray()) {
|
||||
error(res, "parameters must be arrays");
|
||||
return;
|
||||
}
|
||||
if (param.Size() < 1) {
|
||||
error_params_insufficient(res);
|
||||
continue;
|
||||
}
|
||||
if (!param[0].IsString()) {
|
||||
error_type(res, "name", "string");
|
||||
continue;
|
||||
}
|
||||
|
||||
// get params
|
||||
auto light_name = param[0].GetString();
|
||||
|
||||
// write analog state
|
||||
if (!this->write_light_reset(light_name)) {
|
||||
error_unknown(res, "analog", light_name);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Lights::write_light(std::string name, float state) {
|
||||
|
||||
// check light cache
|
||||
if (!this->lights) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// find light
|
||||
for (auto &light : *this->lights) {
|
||||
if (light.getName() == name) {
|
||||
light.override_state = CLAMP(state, 0.f, 1.f);
|
||||
light.override_enabled = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// unknown light
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Lights::write_light_reset(std::string name) {
|
||||
|
||||
// check light cache
|
||||
if (!this->lights) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// find light
|
||||
for (auto &light : *this->lights) {
|
||||
if (light.getName() == name) {
|
||||
light.override_enabled = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// unknown light
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#include "lights.h"
|
||||
#include <functional>
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "cfg/light.h"
|
||||
#include "launcher/launcher.h"
|
||||
#include "games/io.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace rapidjson;
|
||||
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
Lights::Lights() : Module("lights") {
|
||||
functions["read"] = std::bind(&Lights::read, this, _1, _2);
|
||||
functions["write"] = std::bind(&Lights::write, this, _1, _2);
|
||||
functions["write_reset"] = std::bind(&Lights::write_reset, this, _1, _2);
|
||||
lights = games::get_lights(eamuse_get_game());
|
||||
}
|
||||
|
||||
/**
|
||||
* read()
|
||||
*/
|
||||
void Lights::read(api::Request &req, Response &res) {
|
||||
|
||||
// check light cache
|
||||
if (!this->lights) {
|
||||
return;
|
||||
}
|
||||
|
||||
// add state for each light
|
||||
for (auto &light : *this->lights) {
|
||||
Value state(kArrayType);
|
||||
Value light_name(light.getName().c_str(), res.doc()->GetAllocator());
|
||||
Value light_state(GameAPI::Lights::readLight(RI_MGR, light));
|
||||
Value light_enabled(light.override_enabled);
|
||||
state.PushBack(light_name, res.doc()->GetAllocator());
|
||||
state.PushBack(light_state, res.doc()->GetAllocator());
|
||||
state.PushBack(light_enabled, res.doc()->GetAllocator());
|
||||
res.add_data(state);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* write([name: str, state: float], ...)
|
||||
*/
|
||||
void Lights::write(Request &req, Response &res) {
|
||||
|
||||
// check light cache
|
||||
if (!this->lights) {
|
||||
return;
|
||||
}
|
||||
|
||||
// loop parameters
|
||||
for (Value ¶m : req.params.GetArray()) {
|
||||
|
||||
// check params
|
||||
if (!param.IsArray()) {
|
||||
error(res, "parameters must be arrays");
|
||||
return;
|
||||
}
|
||||
if (param.Size() < 2) {
|
||||
error_params_insufficient(res);
|
||||
continue;
|
||||
}
|
||||
if (!param[0].IsString()) {
|
||||
error_type(res, "name", "string");
|
||||
continue;
|
||||
}
|
||||
if (!param[1].IsFloat() && !param[1].IsInt()) {
|
||||
error_type(res, "state", "float");
|
||||
continue;
|
||||
}
|
||||
|
||||
// get params
|
||||
auto light_name = param[0].GetString();
|
||||
auto light_state = param[1].GetFloat();
|
||||
|
||||
// write light state
|
||||
if (!this->write_light(light_name, light_state)) {
|
||||
error_unknown(res, "light", light_name);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* write_reset()
|
||||
* write_reset([name: str], ...)
|
||||
*/
|
||||
void Lights::write_reset(Request &req, Response &res) {
|
||||
|
||||
// check light cache
|
||||
if (!this->lights) {
|
||||
return;
|
||||
}
|
||||
|
||||
// get params
|
||||
auto params = req.params.GetArray();
|
||||
|
||||
// write_reset()
|
||||
if (params.Size() == 0) {
|
||||
if (lights != nullptr) {
|
||||
for (auto &light : *this->lights) {
|
||||
light.override_enabled = false;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// loop parameters
|
||||
for (Value ¶m : req.params.GetArray()) {
|
||||
|
||||
// check params
|
||||
if (!param.IsArray()) {
|
||||
error(res, "parameters must be arrays");
|
||||
return;
|
||||
}
|
||||
if (param.Size() < 1) {
|
||||
error_params_insufficient(res);
|
||||
continue;
|
||||
}
|
||||
if (!param[0].IsString()) {
|
||||
error_type(res, "name", "string");
|
||||
continue;
|
||||
}
|
||||
|
||||
// get params
|
||||
auto light_name = param[0].GetString();
|
||||
|
||||
// write analog state
|
||||
if (!this->write_light_reset(light_name)) {
|
||||
error_unknown(res, "analog", light_name);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Lights::write_light(std::string name, float state) {
|
||||
|
||||
// check light cache
|
||||
if (!this->lights) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// find light
|
||||
for (auto &light : *this->lights) {
|
||||
if (light.getName() == name) {
|
||||
light.override_state = CLAMP(state, 0.f, 1.f);
|
||||
light.override_enabled = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// unknown light
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Lights::write_light_reset(std::string name) {
|
||||
|
||||
// check light cache
|
||||
if (!this->lights) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// find light
|
||||
for (auto &light : *this->lights) {
|
||||
if (light.getName() == name) {
|
||||
light.override_enabled = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// unknown light
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
+28
-28
@@ -1,28 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
#include "cfg/api.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class Lights : public Module {
|
||||
public:
|
||||
Lights();
|
||||
|
||||
private:
|
||||
|
||||
// state
|
||||
std::vector<Light> *lights;
|
||||
|
||||
// function definitions
|
||||
void read(Request &req, Response &res);
|
||||
void write(Request &req, Response &res);
|
||||
void write_reset(Request &req, Response &res);
|
||||
|
||||
// helper
|
||||
bool write_light(std::string name, float state);
|
||||
bool write_light_reset(std::string name);
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
#include "cfg/api.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class Lights : public Module {
|
||||
public:
|
||||
Lights();
|
||||
|
||||
private:
|
||||
|
||||
// state
|
||||
std::vector<Light> *lights;
|
||||
|
||||
// function definitions
|
||||
void read(Request &req, Response &res);
|
||||
void write(Request &req, Response &res);
|
||||
void write_reset(Request &req, Response &res);
|
||||
|
||||
// helper
|
||||
bool write_light(std::string name, float state);
|
||||
bool write_light_reset(std::string name);
|
||||
};
|
||||
}
|
||||
|
||||
+233
-233
@@ -1,233 +1,233 @@
|
||||
#include "memory.h"
|
||||
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "util/fileutils.h"
|
||||
#include "util/libutils.h"
|
||||
#include "util/memutils.h"
|
||||
#include "util/sigscan.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace rapidjson;
|
||||
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
// global lock to prevent simultaneous access to memory
|
||||
static std::mutex MEMORY_LOCK;
|
||||
|
||||
Memory::Memory() : Module("memory", true) {
|
||||
functions["write"] = std::bind(&Memory::write, this, _1, _2);
|
||||
functions["read"] = std::bind(&Memory::read, this, _1, _2);
|
||||
functions["signature"] = std::bind(&Memory::signature, this, _1, _2);
|
||||
}
|
||||
|
||||
/**
|
||||
* write(dll_name: str, data: hex, offset: uint)
|
||||
*/
|
||||
void Memory::write(Request &req, Response &res) {
|
||||
std::lock_guard<std::mutex> lock(MEMORY_LOCK);
|
||||
|
||||
// check params
|
||||
if (req.params.Size() < 3) {
|
||||
return error_params_insufficient(res);
|
||||
}
|
||||
if (!req.params[0].IsString()) {
|
||||
return error_type(res, "dll_name", "str");
|
||||
}
|
||||
if (!req.params[1].IsString() || (req.params[1].GetStringLength() & 1)) {
|
||||
return error_type(res, "data", "hex string");
|
||||
}
|
||||
if (!req.params[2].IsUint()) {
|
||||
return error_type(res, "offset", "uint");
|
||||
}
|
||||
|
||||
// get params
|
||||
auto dll_name = req.params[0].GetString();
|
||||
auto dll_path = MODULE_PATH / dll_name;
|
||||
auto data = req.params[1].GetString();
|
||||
intptr_t offset = req.params[2].GetUint();
|
||||
|
||||
// convert data to bin
|
||||
size_t data_bin_size = strlen(data) / 2;
|
||||
auto data_bin = std::make_unique<uint8_t[]>(data_bin_size);
|
||||
hex2bin(data, data_bin.get());
|
||||
|
||||
// check if file exists in modules
|
||||
if (!fileutils::file_exists(dll_path)) {
|
||||
return error(res, "Couldn't find " + dll_path.string());
|
||||
}
|
||||
|
||||
// get module
|
||||
auto module = libutils::try_module(dll_name);
|
||||
if (!module) {
|
||||
return error(res, "Couldn't find module.");
|
||||
}
|
||||
|
||||
// convert offset to RVA
|
||||
offset = libutils::offset2rva(dll_path, offset);
|
||||
if (offset == ~0) {
|
||||
return error(res, "Couldn't convert offset to RVA.");
|
||||
}
|
||||
|
||||
// get module information
|
||||
MODULEINFO module_info {};
|
||||
if (!GetModuleInformation(GetCurrentProcess(), module, &module_info, sizeof(MODULEINFO))) {
|
||||
return error(res, "Couldn't get module information.");
|
||||
}
|
||||
|
||||
// check bounds
|
||||
if (offset + data_bin_size >= (size_t) module_info.lpBaseOfDll + module_info.SizeOfImage) {
|
||||
return error(res, "Data out of bounds.");
|
||||
}
|
||||
auto data_pos = reinterpret_cast<uint8_t *>(module_info.lpBaseOfDll) + offset;
|
||||
|
||||
// replace data
|
||||
memutils::VProtectGuard guard(data_pos, data_bin_size);
|
||||
memcpy(data_pos, data_bin.get(), data_bin_size);
|
||||
}
|
||||
|
||||
/**
|
||||
* read(dll_name: str, offset: uint, size: uint)
|
||||
*/
|
||||
void Memory::read(Request &req, Response &res) {
|
||||
std::lock_guard<std::mutex> lock(MEMORY_LOCK);
|
||||
|
||||
// check params
|
||||
if (req.params.Size() < 3) {
|
||||
return error_params_insufficient(res);
|
||||
}
|
||||
if (!req.params[0].IsString()) {
|
||||
return error_type(res, "dll_name", "str");
|
||||
}
|
||||
if (!req.params[1].IsUint()) {
|
||||
return error_type(res, "offset", "uint");
|
||||
}
|
||||
if (!req.params[2].IsUint()) {
|
||||
return error_type(res, "size", "uint");
|
||||
}
|
||||
|
||||
// get params
|
||||
auto dll_name = req.params[0].GetString();
|
||||
auto dll_path = MODULE_PATH / dll_name;
|
||||
intptr_t offset = req.params[1].GetUint();
|
||||
auto size = req.params[2].GetUint();
|
||||
|
||||
// check if file exists in modules
|
||||
if (!fileutils::file_exists(dll_path)) {
|
||||
return error(res, "Couldn't find " + dll_path.string());
|
||||
}
|
||||
|
||||
// get module
|
||||
auto module = libutils::try_module(dll_name);
|
||||
if (!module) {
|
||||
return error(res, "Couldn't find module.");
|
||||
}
|
||||
|
||||
// convert offset to RVA
|
||||
offset = libutils::offset2rva(dll_path, offset);
|
||||
if (offset == ~0) {
|
||||
return error(res, "Couldn't convert offset to RVA.");
|
||||
}
|
||||
|
||||
// get module information
|
||||
MODULEINFO module_info {};
|
||||
if (!GetModuleInformation(GetCurrentProcess(), module, &module_info, sizeof(MODULEINFO))) {
|
||||
return error(res, "Couldn't get module information.");
|
||||
}
|
||||
|
||||
// check bounds
|
||||
auto max = offset + size;
|
||||
if ((size_t) max >= (size_t) module_info.lpBaseOfDll + module_info.SizeOfImage) {
|
||||
return error(res, "Data out of bounds.");
|
||||
}
|
||||
|
||||
// read memory to hex (without virtual protect)
|
||||
std::string hex = bin2hex((uint8_t*) module_info.lpBaseOfDll + offset, size);
|
||||
Value hex_val(hex.c_str(), res.doc()->GetAllocator());
|
||||
res.add_data(hex_val);
|
||||
}
|
||||
|
||||
/**
|
||||
* signature(
|
||||
* dll_name: str,
|
||||
* signature: hex,
|
||||
* replacement: hex,
|
||||
* offset: uint,
|
||||
* usage: uint)
|
||||
*
|
||||
* Both signature and replacement will ignore bytes specified as "??" in the hex string.
|
||||
* The offset specifies the offset between the found signature and the position to write the replacement to.
|
||||
* The resulting integer is the file offset where the replacement was written to.
|
||||
*/
|
||||
void Memory::signature(Request &req, Response &res) {
|
||||
std::lock_guard<std::mutex> lock(MEMORY_LOCK);
|
||||
|
||||
// check params
|
||||
if (req.params.Size() < 5) {
|
||||
return error_params_insufficient(res);
|
||||
}
|
||||
if (!req.params[0].IsString()) {
|
||||
return error_type(res, "dll_name", "string");
|
||||
}
|
||||
if (!req.params[1].IsString() || (req.params[1].GetStringLength() & 1)) {
|
||||
return error_type(res, "signature", "hex string");
|
||||
}
|
||||
if (!req.params[2].IsString() || (req.params[2].GetStringLength() & 1)) {
|
||||
return error_type(res, "replacement", "hex string");
|
||||
}
|
||||
if (!req.params[3].IsUint()) {
|
||||
return error_type(res, "offset", "uint");
|
||||
}
|
||||
if (!req.params[4].IsUint()) {
|
||||
return error_type(res, "usage", "uint");
|
||||
}
|
||||
|
||||
// get params
|
||||
auto dll_name = req.params[0].GetString();
|
||||
auto dll_path = MODULE_PATH / dll_name;
|
||||
auto signature = req.params[1].GetString();
|
||||
auto replacement = req.params[2].GetString();
|
||||
auto offset = req.params[3].GetUint();
|
||||
auto usage = req.params[4].GetUint();
|
||||
|
||||
// check if file exists in modules
|
||||
if (!fileutils::file_exists(dll_path)) {
|
||||
return error(res, "Couldn't find " + dll_path.string());
|
||||
}
|
||||
|
||||
// get module
|
||||
auto module = libutils::try_module(dll_name);
|
||||
if (!module) {
|
||||
return error(res, "Couldn't find module.");
|
||||
}
|
||||
|
||||
// execute
|
||||
auto result = replace_pattern(
|
||||
module,
|
||||
signature,
|
||||
replacement,
|
||||
offset,
|
||||
usage
|
||||
);
|
||||
|
||||
// check result
|
||||
if (!result) {
|
||||
return error(res, std::string("Pattern not found in memory of ") + dll_name);
|
||||
}
|
||||
|
||||
// convert to offset
|
||||
auto rva = result - reinterpret_cast<intptr_t>(module);
|
||||
result = libutils::rva2offset(dll_path, rva);
|
||||
if (result == -1) {
|
||||
return error(res, "Couldn't convert RVA to file offset.");
|
||||
}
|
||||
|
||||
// add result
|
||||
Value result_val(result);
|
||||
res.add_data(result_val);
|
||||
}
|
||||
}
|
||||
#include "memory.h"
|
||||
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "util/fileutils.h"
|
||||
#include "util/libutils.h"
|
||||
#include "util/memutils.h"
|
||||
#include "util/sigscan.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace rapidjson;
|
||||
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
// global lock to prevent simultaneous access to memory
|
||||
static std::mutex MEMORY_LOCK;
|
||||
|
||||
Memory::Memory() : Module("memory", true) {
|
||||
functions["write"] = std::bind(&Memory::write, this, _1, _2);
|
||||
functions["read"] = std::bind(&Memory::read, this, _1, _2);
|
||||
functions["signature"] = std::bind(&Memory::signature, this, _1, _2);
|
||||
}
|
||||
|
||||
/**
|
||||
* write(dll_name: str, data: hex, offset: uint)
|
||||
*/
|
||||
void Memory::write(Request &req, Response &res) {
|
||||
std::lock_guard<std::mutex> lock(MEMORY_LOCK);
|
||||
|
||||
// check params
|
||||
if (req.params.Size() < 3) {
|
||||
return error_params_insufficient(res);
|
||||
}
|
||||
if (!req.params[0].IsString()) {
|
||||
return error_type(res, "dll_name", "str");
|
||||
}
|
||||
if (!req.params[1].IsString() || (req.params[1].GetStringLength() & 1)) {
|
||||
return error_type(res, "data", "hex string");
|
||||
}
|
||||
if (!req.params[2].IsUint()) {
|
||||
return error_type(res, "offset", "uint");
|
||||
}
|
||||
|
||||
// get params
|
||||
auto dll_name = req.params[0].GetString();
|
||||
auto dll_path = MODULE_PATH / dll_name;
|
||||
auto data = req.params[1].GetString();
|
||||
intptr_t offset = req.params[2].GetUint();
|
||||
|
||||
// convert data to bin
|
||||
size_t data_bin_size = strlen(data) / 2;
|
||||
auto data_bin = std::make_unique<uint8_t[]>(data_bin_size);
|
||||
hex2bin(data, data_bin.get());
|
||||
|
||||
// check if file exists in modules
|
||||
if (!fileutils::file_exists(dll_path)) {
|
||||
return error(res, "Couldn't find " + dll_path.string());
|
||||
}
|
||||
|
||||
// get module
|
||||
auto module = libutils::try_module(dll_name);
|
||||
if (!module) {
|
||||
return error(res, "Couldn't find module.");
|
||||
}
|
||||
|
||||
// convert offset to RVA
|
||||
offset = libutils::offset2rva(dll_path, offset);
|
||||
if (offset == ~0) {
|
||||
return error(res, "Couldn't convert offset to RVA.");
|
||||
}
|
||||
|
||||
// get module information
|
||||
MODULEINFO module_info {};
|
||||
if (!GetModuleInformation(GetCurrentProcess(), module, &module_info, sizeof(MODULEINFO))) {
|
||||
return error(res, "Couldn't get module information.");
|
||||
}
|
||||
|
||||
// check bounds
|
||||
if (offset + data_bin_size >= (size_t) module_info.lpBaseOfDll + module_info.SizeOfImage) {
|
||||
return error(res, "Data out of bounds.");
|
||||
}
|
||||
auto data_pos = reinterpret_cast<uint8_t *>(module_info.lpBaseOfDll) + offset;
|
||||
|
||||
// replace data
|
||||
memutils::VProtectGuard guard(data_pos, data_bin_size);
|
||||
memcpy(data_pos, data_bin.get(), data_bin_size);
|
||||
}
|
||||
|
||||
/**
|
||||
* read(dll_name: str, offset: uint, size: uint)
|
||||
*/
|
||||
void Memory::read(Request &req, Response &res) {
|
||||
std::lock_guard<std::mutex> lock(MEMORY_LOCK);
|
||||
|
||||
// check params
|
||||
if (req.params.Size() < 3) {
|
||||
return error_params_insufficient(res);
|
||||
}
|
||||
if (!req.params[0].IsString()) {
|
||||
return error_type(res, "dll_name", "str");
|
||||
}
|
||||
if (!req.params[1].IsUint()) {
|
||||
return error_type(res, "offset", "uint");
|
||||
}
|
||||
if (!req.params[2].IsUint()) {
|
||||
return error_type(res, "size", "uint");
|
||||
}
|
||||
|
||||
// get params
|
||||
auto dll_name = req.params[0].GetString();
|
||||
auto dll_path = MODULE_PATH / dll_name;
|
||||
intptr_t offset = req.params[1].GetUint();
|
||||
auto size = req.params[2].GetUint();
|
||||
|
||||
// check if file exists in modules
|
||||
if (!fileutils::file_exists(dll_path)) {
|
||||
return error(res, "Couldn't find " + dll_path.string());
|
||||
}
|
||||
|
||||
// get module
|
||||
auto module = libutils::try_module(dll_name);
|
||||
if (!module) {
|
||||
return error(res, "Couldn't find module.");
|
||||
}
|
||||
|
||||
// convert offset to RVA
|
||||
offset = libutils::offset2rva(dll_path, offset);
|
||||
if (offset == ~0) {
|
||||
return error(res, "Couldn't convert offset to RVA.");
|
||||
}
|
||||
|
||||
// get module information
|
||||
MODULEINFO module_info {};
|
||||
if (!GetModuleInformation(GetCurrentProcess(), module, &module_info, sizeof(MODULEINFO))) {
|
||||
return error(res, "Couldn't get module information.");
|
||||
}
|
||||
|
||||
// check bounds
|
||||
auto max = offset + size;
|
||||
if ((size_t) max >= (size_t) module_info.lpBaseOfDll + module_info.SizeOfImage) {
|
||||
return error(res, "Data out of bounds.");
|
||||
}
|
||||
|
||||
// read memory to hex (without virtual protect)
|
||||
std::string hex = bin2hex((uint8_t*) module_info.lpBaseOfDll + offset, size);
|
||||
Value hex_val(hex.c_str(), res.doc()->GetAllocator());
|
||||
res.add_data(hex_val);
|
||||
}
|
||||
|
||||
/**
|
||||
* signature(
|
||||
* dll_name: str,
|
||||
* signature: hex,
|
||||
* replacement: hex,
|
||||
* offset: uint,
|
||||
* usage: uint)
|
||||
*
|
||||
* Both signature and replacement will ignore bytes specified as "??" in the hex string.
|
||||
* The offset specifies the offset between the found signature and the position to write the replacement to.
|
||||
* The resulting integer is the file offset where the replacement was written to.
|
||||
*/
|
||||
void Memory::signature(Request &req, Response &res) {
|
||||
std::lock_guard<std::mutex> lock(MEMORY_LOCK);
|
||||
|
||||
// check params
|
||||
if (req.params.Size() < 5) {
|
||||
return error_params_insufficient(res);
|
||||
}
|
||||
if (!req.params[0].IsString()) {
|
||||
return error_type(res, "dll_name", "string");
|
||||
}
|
||||
if (!req.params[1].IsString() || (req.params[1].GetStringLength() & 1)) {
|
||||
return error_type(res, "signature", "hex string");
|
||||
}
|
||||
if (!req.params[2].IsString() || (req.params[2].GetStringLength() & 1)) {
|
||||
return error_type(res, "replacement", "hex string");
|
||||
}
|
||||
if (!req.params[3].IsUint()) {
|
||||
return error_type(res, "offset", "uint");
|
||||
}
|
||||
if (!req.params[4].IsUint()) {
|
||||
return error_type(res, "usage", "uint");
|
||||
}
|
||||
|
||||
// get params
|
||||
auto dll_name = req.params[0].GetString();
|
||||
auto dll_path = MODULE_PATH / dll_name;
|
||||
auto signature = req.params[1].GetString();
|
||||
auto replacement = req.params[2].GetString();
|
||||
auto offset = req.params[3].GetUint();
|
||||
auto usage = req.params[4].GetUint();
|
||||
|
||||
// check if file exists in modules
|
||||
if (!fileutils::file_exists(dll_path)) {
|
||||
return error(res, "Couldn't find " + dll_path.string());
|
||||
}
|
||||
|
||||
// get module
|
||||
auto module = libutils::try_module(dll_name);
|
||||
if (!module) {
|
||||
return error(res, "Couldn't find module.");
|
||||
}
|
||||
|
||||
// execute
|
||||
auto result = replace_pattern(
|
||||
module,
|
||||
signature,
|
||||
replacement,
|
||||
offset,
|
||||
usage
|
||||
);
|
||||
|
||||
// check result
|
||||
if (!result) {
|
||||
return error(res, std::string("Pattern not found in memory of ") + dll_name);
|
||||
}
|
||||
|
||||
// convert to offset
|
||||
auto rva = result - reinterpret_cast<intptr_t>(module);
|
||||
result = libutils::rva2offset(dll_path, rva);
|
||||
if (result == -1) {
|
||||
return error(res, "Couldn't convert RVA to file offset.");
|
||||
}
|
||||
|
||||
// add result
|
||||
Value result_val(result);
|
||||
res.add_data(result_val);
|
||||
}
|
||||
}
|
||||
|
||||
+19
-19
@@ -1,19 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class Memory : public Module {
|
||||
public:
|
||||
Memory();
|
||||
|
||||
private:
|
||||
|
||||
// function definitions
|
||||
void write(Request &req, Response &res);
|
||||
void read(Request &req, Response &res);
|
||||
void signature(Request &req, Response &res);
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class Memory : public Module {
|
||||
public:
|
||||
Memory();
|
||||
|
||||
private:
|
||||
|
||||
// function definitions
|
||||
void write(Request &req, Response &res);
|
||||
void read(Request &req, Response &res);
|
||||
void signature(Request &req, Response &res);
|
||||
};
|
||||
}
|
||||
|
||||
+55
-55
@@ -1,55 +1,55 @@
|
||||
#include "request.h"
|
||||
#include "../util/logging.h"
|
||||
#include "module.h"
|
||||
|
||||
using namespace rapidjson;
|
||||
|
||||
namespace api {
|
||||
|
||||
Request::Request(rapidjson::Document &document) {
|
||||
Value::MemberIterator it;
|
||||
this->parse_error = false;
|
||||
|
||||
// get ID
|
||||
it = document.FindMember("id");
|
||||
if (it == document.MemberEnd() || !(*it).value.IsUint64()) {
|
||||
log_warning("api", "Request ID is invalid");
|
||||
this->parse_error = true;
|
||||
return;
|
||||
}
|
||||
this->id = (*it).value.GetUint64();
|
||||
|
||||
// get module
|
||||
it = document.FindMember("module");
|
||||
if (it == document.MemberEnd() || !(*it).value.IsString()) {
|
||||
log_warning("api", "Request module is invalid");
|
||||
this->parse_error = true;
|
||||
return;
|
||||
}
|
||||
this->module = (*it).value.GetString();
|
||||
|
||||
// get function
|
||||
it = document.FindMember("function");
|
||||
if (it == document.MemberEnd() || !(*it).value.IsString()) {
|
||||
log_warning("api", "Request function is invalid");
|
||||
this->parse_error = true;
|
||||
return;
|
||||
}
|
||||
this->function = (*it).value.GetString();
|
||||
|
||||
// get params
|
||||
it = document.FindMember("params");
|
||||
if (it == document.MemberEnd() || !(*it).value.IsArray()) {
|
||||
log_warning("api", "Request params is invalid");
|
||||
this->parse_error = true;
|
||||
return;
|
||||
}
|
||||
this->params = document["params"];
|
||||
|
||||
// log request
|
||||
if (LOGGING) {
|
||||
log_info("api", "new request > id: {}, module: {}, function: {}",
|
||||
this->id, this->module, this->function);
|
||||
}
|
||||
}
|
||||
}
|
||||
#include "request.h"
|
||||
#include "../util/logging.h"
|
||||
#include "module.h"
|
||||
|
||||
using namespace rapidjson;
|
||||
|
||||
namespace api {
|
||||
|
||||
Request::Request(rapidjson::Document &document) {
|
||||
Value::MemberIterator it;
|
||||
this->parse_error = false;
|
||||
|
||||
// get ID
|
||||
it = document.FindMember("id");
|
||||
if (it == document.MemberEnd() || !(*it).value.IsUint64()) {
|
||||
log_warning("api", "Request ID is invalid");
|
||||
this->parse_error = true;
|
||||
return;
|
||||
}
|
||||
this->id = (*it).value.GetUint64();
|
||||
|
||||
// get module
|
||||
it = document.FindMember("module");
|
||||
if (it == document.MemberEnd() || !(*it).value.IsString()) {
|
||||
log_warning("api", "Request module is invalid");
|
||||
this->parse_error = true;
|
||||
return;
|
||||
}
|
||||
this->module = (*it).value.GetString();
|
||||
|
||||
// get function
|
||||
it = document.FindMember("function");
|
||||
if (it == document.MemberEnd() || !(*it).value.IsString()) {
|
||||
log_warning("api", "Request function is invalid");
|
||||
this->parse_error = true;
|
||||
return;
|
||||
}
|
||||
this->function = (*it).value.GetString();
|
||||
|
||||
// get params
|
||||
it = document.FindMember("params");
|
||||
if (it == document.MemberEnd() || !(*it).value.IsArray()) {
|
||||
log_warning("api", "Request params is invalid");
|
||||
this->parse_error = true;
|
||||
return;
|
||||
}
|
||||
this->params = document["params"];
|
||||
|
||||
// log request
|
||||
if (LOGGING) {
|
||||
log_info("api", "new request > id: {}, module: {}, function: {}",
|
||||
this->id, this->module, this->function);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+21
-21
@@ -1,21 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "external/rapidjson/document.h"
|
||||
|
||||
namespace api {
|
||||
|
||||
class Request {
|
||||
public:
|
||||
uint64_t id;
|
||||
std::string module;
|
||||
std::string function;
|
||||
rapidjson::Value params;
|
||||
bool parse_error;
|
||||
|
||||
Request(rapidjson::Document &document);
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "external/rapidjson/document.h"
|
||||
|
||||
namespace api {
|
||||
|
||||
class Request {
|
||||
public:
|
||||
uint64_t id;
|
||||
std::string module;
|
||||
std::string function;
|
||||
rapidjson::Value params;
|
||||
bool parse_error;
|
||||
|
||||
Request(rapidjson::Document &document);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# SpiceAPI Arduino Library
|
||||
This library is still a bit experimental and might contain bugs.
|
||||
|
||||
To use this library, it's recommended to just copy the Arduino project and start from that.
|
||||
# SpiceAPI Arduino Library
|
||||
This library is still a bit experimental and might contain bugs.
|
||||
|
||||
To use this library, it's recommended to just copy the Arduino project and start from that.
|
||||
|
||||
@@ -1,65 +1,65 @@
|
||||
#ifndef SPICEAPI_RC4_H
|
||||
#define SPICEAPI_RC4_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
namespace spiceapi {
|
||||
|
||||
class RC4 {
|
||||
private:
|
||||
uint8_t s_box[256];
|
||||
size_t a = 0, b = 0;
|
||||
|
||||
public:
|
||||
|
||||
RC4(uint8_t *key, size_t key_size);
|
||||
|
||||
void crypt(uint8_t *data, size_t size);
|
||||
};
|
||||
}
|
||||
|
||||
spiceapi::RC4::RC4(uint8_t *key, size_t key_size) {
|
||||
|
||||
// initialize S-BOX
|
||||
for (size_t i = 0; i < sizeof(s_box); i++)
|
||||
s_box[i] = (uint8_t) i;
|
||||
|
||||
// check key size
|
||||
if (!key_size)
|
||||
return;
|
||||
|
||||
// KSA
|
||||
size_t j = 0;
|
||||
for (size_t i = 0; i < sizeof(s_box); i++) {
|
||||
|
||||
// update
|
||||
j = (j + s_box[i] + key[i % key_size]) % sizeof(s_box);
|
||||
|
||||
// swap
|
||||
auto tmp = s_box[i];
|
||||
s_box[i] = s_box[j];
|
||||
s_box[j] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
void spiceapi::RC4::crypt(uint8_t *data, size_t size) {
|
||||
|
||||
// iterate all bytes
|
||||
for (size_t pos = 0; pos < size; pos++) {
|
||||
|
||||
// update
|
||||
a = (a + 1) % sizeof(s_box);
|
||||
b = (b + s_box[a]) % sizeof(s_box);
|
||||
|
||||
// swap
|
||||
auto tmp = s_box[a];
|
||||
s_box[a] = s_box[b];
|
||||
s_box[b] = tmp;
|
||||
|
||||
// crypt
|
||||
data[pos] ^= s_box[(s_box[a] + s_box[b]) % sizeof(s_box)];
|
||||
}
|
||||
}
|
||||
|
||||
#endif //SPICEAPI_RC4_H
|
||||
#ifndef SPICEAPI_RC4_H
|
||||
#define SPICEAPI_RC4_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
namespace spiceapi {
|
||||
|
||||
class RC4 {
|
||||
private:
|
||||
uint8_t s_box[256];
|
||||
size_t a = 0, b = 0;
|
||||
|
||||
public:
|
||||
|
||||
RC4(uint8_t *key, size_t key_size);
|
||||
|
||||
void crypt(uint8_t *data, size_t size);
|
||||
};
|
||||
}
|
||||
|
||||
spiceapi::RC4::RC4(uint8_t *key, size_t key_size) {
|
||||
|
||||
// initialize S-BOX
|
||||
for (size_t i = 0; i < sizeof(s_box); i++)
|
||||
s_box[i] = (uint8_t) i;
|
||||
|
||||
// check key size
|
||||
if (!key_size)
|
||||
return;
|
||||
|
||||
// KSA
|
||||
size_t j = 0;
|
||||
for (size_t i = 0; i < sizeof(s_box); i++) {
|
||||
|
||||
// update
|
||||
j = (j + s_box[i] + key[i % key_size]) % sizeof(s_box);
|
||||
|
||||
// swap
|
||||
auto tmp = s_box[i];
|
||||
s_box[i] = s_box[j];
|
||||
s_box[j] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
void spiceapi::RC4::crypt(uint8_t *data, size_t size) {
|
||||
|
||||
// iterate all bytes
|
||||
for (size_t pos = 0; pos < size; pos++) {
|
||||
|
||||
// update
|
||||
a = (a + 1) % sizeof(s_box);
|
||||
b = (b + s_box[a]) % sizeof(s_box);
|
||||
|
||||
// swap
|
||||
auto tmp = s_box[a];
|
||||
s_box[a] = s_box[b];
|
||||
s_box[b] = tmp;
|
||||
|
||||
// crypt
|
||||
data[pos] ^= s_box[(s_box[a] + s_box[b]) % sizeof(s_box)];
|
||||
}
|
||||
}
|
||||
|
||||
#endif //SPICEAPI_RC4_H
|
||||
|
||||
@@ -1,172 +1,172 @@
|
||||
/*
|
||||
* SpiceAPI Arduino Example Project
|
||||
*
|
||||
* To enable it in SpiceTools, use "-api 1337 -apipass changeme -apiserial COM1" or similar.
|
||||
*/
|
||||
|
||||
/*
|
||||
* SpiceAPI Wrapper Buffer Sizes
|
||||
*
|
||||
* They should be as big as possible to be able to create/parse
|
||||
* some of the bigger requests/responses. Due to dynamic memory
|
||||
* limitations of some weaker devices, if you set them too high
|
||||
* you will probably experience crashes/bugs/problems, one
|
||||
* example would be "Request ID is invalid" in the log.
|
||||
*/
|
||||
#define SPICEAPI_WRAPPER_BUFFER_SIZE 256
|
||||
#define SPICEAPI_WRAPPER_BUFFER_SIZE_STR 256
|
||||
|
||||
/*
|
||||
* WiFi Support
|
||||
* Uncomment to enable the wireless API interface.
|
||||
*/
|
||||
//#define ENABLE_WIFI
|
||||
|
||||
/*
|
||||
* WiFi Settings
|
||||
* You can ignore these if you don't plan on using WiFi
|
||||
*/
|
||||
#ifdef ENABLE_WIFI
|
||||
#include <ESP8266WiFi.h>
|
||||
WiFiClient client;
|
||||
#define SPICEAPI_INTERFACE client
|
||||
#define SPICEAPI_INTERFACE_WIFICLIENT
|
||||
#define SPICEAPI_INTERFACE_WIFICLIENT_HOST "192.168.178.143"
|
||||
#define SPICEAPI_INTERFACE_WIFICLIENT_PORT 1337
|
||||
#define WIFI_SSID "MySSID"
|
||||
#define WIFI_PASS "MyWifiPassword"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This is the interface a serial connection will use.
|
||||
* You can change this to another Serial port, e.g. with an
|
||||
* Arduino Mega you can use Serial1/Serial2/Serial3.
|
||||
*/
|
||||
#ifndef ENABLE_WIFI
|
||||
#define SPICEAPI_INTERFACE Serial
|
||||
#endif
|
||||
|
||||
/*
|
||||
* SpiceAPI Includes
|
||||
*
|
||||
* If you have the JSON strings beforehands or want to craft them
|
||||
* manually, you don't have to import the wrappers at all and can
|
||||
* use Connection::request to send and receive raw JSON strings.
|
||||
*/
|
||||
#include "connection.h"
|
||||
#include "wrappers.h"
|
||||
|
||||
/*
|
||||
* This global object represents the API connection.
|
||||
* The first parameter is the buffer size of the JSON string
|
||||
* we're receiving. So a size of 512 will only be able to
|
||||
* hold a JSON of 512 characters maximum.
|
||||
*
|
||||
* An empty password string means no password is being used.
|
||||
* This is the recommended when using Serial only.
|
||||
*/
|
||||
spiceapi::Connection CON(512, "changeme");
|
||||
|
||||
void setup() {
|
||||
|
||||
#ifdef ENABLE_WIFI
|
||||
|
||||
/*
|
||||
* When using WiFi, we can use the Serial interface for debugging.
|
||||
* You can open Serial Monitor and see what IP it gets assigned to.
|
||||
*/
|
||||
Serial.begin(57600);
|
||||
|
||||
// set WiFi mode to station (disables integrated AP)
|
||||
WiFi.mode(WIFI_STA);
|
||||
|
||||
// now try connecting to our Router/AP
|
||||
Serial.print("Connecting");
|
||||
WiFi.begin(WIFI_SSID, WIFI_PASS);
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
}
|
||||
|
||||
// print debug info over serial
|
||||
Serial.print("\nLocal IP: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
* Since the API makes use of the Serial module, we need to
|
||||
* set it up using our preferred baud rate manually.
|
||||
*/
|
||||
SPICEAPI_INTERFACE.begin(57600);
|
||||
while (!SPICEAPI_INTERFACE);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
/*
|
||||
* Here's a few tests/examples on how to make use of the wrappers.
|
||||
*/
|
||||
|
||||
// insert cards for P1/P2
|
||||
spiceapi::card_insert(CON, 0, "E004012345678901");
|
||||
spiceapi::card_insert(CON, 1, "E004012345678902");
|
||||
|
||||
// insert a single coin / multiple coins
|
||||
spiceapi::coin_insert(CON);
|
||||
spiceapi::coin_insert(CON, 3);
|
||||
|
||||
// get the IIDX led ticker text
|
||||
char ticker[9];
|
||||
if (spiceapi::iidx_ticker_get(CON, ticker)) {
|
||||
// if a function returns true, that means success
|
||||
// now we can do something with the ticker as if it was a string
|
||||
//Serial1.println(ticker);
|
||||
}
|
||||
|
||||
// get AVS info
|
||||
spiceapi::InfoAvs avs_info {};
|
||||
if (spiceapi::info_avs(CON, avs_info)) {
|
||||
//Serial1.println(avs_info.model);
|
||||
}
|
||||
|
||||
// enter some keys on P1 keypad (blocks until sequence is entered fully)
|
||||
spiceapi::keypads_write(CON, 0, "1234");
|
||||
|
||||
// get light states
|
||||
spiceapi::LightState lights[8];
|
||||
size_t lights_size = spiceapi::lights_read(CON, lights, 8);
|
||||
for (size_t i = 0; i < lights_size; i++) {
|
||||
auto &light = lights[i];
|
||||
//Serial1.println(light.name);
|
||||
//Serial1.println(light.value);
|
||||
|
||||
// modify value to full bright
|
||||
light.value = 1.f;
|
||||
}
|
||||
|
||||
// send back modified light states
|
||||
spiceapi::lights_write(CON, lights, lights_size);
|
||||
|
||||
// refresh session (generates new crypt key, not that important for serial)
|
||||
spiceapi::control_session_refresh(CON);
|
||||
|
||||
// you can also manually send requests without the wrappers
|
||||
// this avoids json generation, but you still need to parse it in some way
|
||||
const char *answer_json = CON.request(
|
||||
"{"
|
||||
"\"id\": 0,"
|
||||
"\"module\":\"coin\","
|
||||
"\"function\":\"insert\","
|
||||
"\"params\":[]"
|
||||
"}"
|
||||
);
|
||||
|
||||
/*
|
||||
* For more functions/information, just check out wrappers.h yourself.
|
||||
* Have fun :)
|
||||
*/
|
||||
delay(5000);
|
||||
}
|
||||
/*
|
||||
* SpiceAPI Arduino Example Project
|
||||
*
|
||||
* To enable it in SpiceTools, use "-api 1337 -apipass changeme -apiserial COM1" or similar.
|
||||
*/
|
||||
|
||||
/*
|
||||
* SpiceAPI Wrapper Buffer Sizes
|
||||
*
|
||||
* They should be as big as possible to be able to create/parse
|
||||
* some of the bigger requests/responses. Due to dynamic memory
|
||||
* limitations of some weaker devices, if you set them too high
|
||||
* you will probably experience crashes/bugs/problems, one
|
||||
* example would be "Request ID is invalid" in the log.
|
||||
*/
|
||||
#define SPICEAPI_WRAPPER_BUFFER_SIZE 256
|
||||
#define SPICEAPI_WRAPPER_BUFFER_SIZE_STR 256
|
||||
|
||||
/*
|
||||
* WiFi Support
|
||||
* Uncomment to enable the wireless API interface.
|
||||
*/
|
||||
//#define ENABLE_WIFI
|
||||
|
||||
/*
|
||||
* WiFi Settings
|
||||
* You can ignore these if you don't plan on using WiFi
|
||||
*/
|
||||
#ifdef ENABLE_WIFI
|
||||
#include <ESP8266WiFi.h>
|
||||
WiFiClient client;
|
||||
#define SPICEAPI_INTERFACE client
|
||||
#define SPICEAPI_INTERFACE_WIFICLIENT
|
||||
#define SPICEAPI_INTERFACE_WIFICLIENT_HOST "192.168.178.143"
|
||||
#define SPICEAPI_INTERFACE_WIFICLIENT_PORT 1337
|
||||
#define WIFI_SSID "MySSID"
|
||||
#define WIFI_PASS "MyWifiPassword"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This is the interface a serial connection will use.
|
||||
* You can change this to another Serial port, e.g. with an
|
||||
* Arduino Mega you can use Serial1/Serial2/Serial3.
|
||||
*/
|
||||
#ifndef ENABLE_WIFI
|
||||
#define SPICEAPI_INTERFACE Serial
|
||||
#endif
|
||||
|
||||
/*
|
||||
* SpiceAPI Includes
|
||||
*
|
||||
* If you have the JSON strings beforehands or want to craft them
|
||||
* manually, you don't have to import the wrappers at all and can
|
||||
* use Connection::request to send and receive raw JSON strings.
|
||||
*/
|
||||
#include "connection.h"
|
||||
#include "wrappers.h"
|
||||
|
||||
/*
|
||||
* This global object represents the API connection.
|
||||
* The first parameter is the buffer size of the JSON string
|
||||
* we're receiving. So a size of 512 will only be able to
|
||||
* hold a JSON of 512 characters maximum.
|
||||
*
|
||||
* An empty password string means no password is being used.
|
||||
* This is the recommended when using Serial only.
|
||||
*/
|
||||
spiceapi::Connection CON(512, "changeme");
|
||||
|
||||
void setup() {
|
||||
|
||||
#ifdef ENABLE_WIFI
|
||||
|
||||
/*
|
||||
* When using WiFi, we can use the Serial interface for debugging.
|
||||
* You can open Serial Monitor and see what IP it gets assigned to.
|
||||
*/
|
||||
Serial.begin(57600);
|
||||
|
||||
// set WiFi mode to station (disables integrated AP)
|
||||
WiFi.mode(WIFI_STA);
|
||||
|
||||
// now try connecting to our Router/AP
|
||||
Serial.print("Connecting");
|
||||
WiFi.begin(WIFI_SSID, WIFI_PASS);
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
}
|
||||
|
||||
// print debug info over serial
|
||||
Serial.print("\nLocal IP: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
* Since the API makes use of the Serial module, we need to
|
||||
* set it up using our preferred baud rate manually.
|
||||
*/
|
||||
SPICEAPI_INTERFACE.begin(57600);
|
||||
while (!SPICEAPI_INTERFACE);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
/*
|
||||
* Here's a few tests/examples on how to make use of the wrappers.
|
||||
*/
|
||||
|
||||
// insert cards for P1/P2
|
||||
spiceapi::card_insert(CON, 0, "E004012345678901");
|
||||
spiceapi::card_insert(CON, 1, "E004012345678902");
|
||||
|
||||
// insert a single coin / multiple coins
|
||||
spiceapi::coin_insert(CON);
|
||||
spiceapi::coin_insert(CON, 3);
|
||||
|
||||
// get the IIDX led ticker text
|
||||
char ticker[9];
|
||||
if (spiceapi::iidx_ticker_get(CON, ticker)) {
|
||||
// if a function returns true, that means success
|
||||
// now we can do something with the ticker as if it was a string
|
||||
//Serial1.println(ticker);
|
||||
}
|
||||
|
||||
// get AVS info
|
||||
spiceapi::InfoAvs avs_info {};
|
||||
if (spiceapi::info_avs(CON, avs_info)) {
|
||||
//Serial1.println(avs_info.model);
|
||||
}
|
||||
|
||||
// enter some keys on P1 keypad (blocks until sequence is entered fully)
|
||||
spiceapi::keypads_write(CON, 0, "1234");
|
||||
|
||||
// get light states
|
||||
spiceapi::LightState lights[8];
|
||||
size_t lights_size = spiceapi::lights_read(CON, lights, 8);
|
||||
for (size_t i = 0; i < lights_size; i++) {
|
||||
auto &light = lights[i];
|
||||
//Serial1.println(light.name);
|
||||
//Serial1.println(light.value);
|
||||
|
||||
// modify value to full bright
|
||||
light.value = 1.f;
|
||||
}
|
||||
|
||||
// send back modified light states
|
||||
spiceapi::lights_write(CON, lights, lights_size);
|
||||
|
||||
// refresh session (generates new crypt key, not that important for serial)
|
||||
spiceapi::control_session_refresh(CON);
|
||||
|
||||
// you can also manually send requests without the wrappers
|
||||
// this avoids json generation, but you still need to parse it in some way
|
||||
const char *answer_json = CON.request(
|
||||
"{"
|
||||
"\"id\": 0,"
|
||||
"\"module\":\"coin\","
|
||||
"\"function\":\"insert\","
|
||||
"\"params\":[]"
|
||||
"}"
|
||||
);
|
||||
|
||||
/*
|
||||
* For more functions/information, just check out wrappers.h yourself.
|
||||
* Have fun :)
|
||||
*/
|
||||
delay(5000);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,8 @@
|
||||
# SpiceAPI C++ Library
|
||||
This library is still a bit experimental and might contain bugs.
|
||||
|
||||
To include it into your project, it's recommended to just copy the
|
||||
files into your source directory.
|
||||
|
||||
To use the wrappers, RapidJSON is required and you might need to
|
||||
adjust the include paths for your project's build.
|
||||
# SpiceAPI C++ Library
|
||||
This library is still a bit experimental and might contain bugs.
|
||||
|
||||
To include it into your project, it's recommended to just copy the
|
||||
files into your source directory.
|
||||
|
||||
To use the wrappers, RapidJSON is required and you might need to
|
||||
adjust the include paths for your project's build.
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
For more information, please refer to <http://unlicense.org/>
|
||||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
For more information, please refer to <http://unlicense.org/>
|
||||
|
||||
@@ -1,181 +1,181 @@
|
||||
#include <iostream>
|
||||
#include <ws2tcpip.h>
|
||||
#include "connection.h"
|
||||
|
||||
namespace spiceapi {
|
||||
|
||||
// settings
|
||||
static const size_t RECEIVE_BUFFER_SIZE = 64 * 1024;
|
||||
static const int RECEIVE_TIMEOUT = 1000;
|
||||
}
|
||||
|
||||
spiceapi::Connection::Connection(std::string host, uint16_t port, std::string password) {
|
||||
this->host = host;
|
||||
this->port = port;
|
||||
this->password = password;
|
||||
this->socket = INVALID_SOCKET;
|
||||
this->cipher = nullptr;
|
||||
|
||||
// WSA startup
|
||||
WSADATA wsa_data;
|
||||
int error = WSAStartup(MAKEWORD(2, 2), &wsa_data);
|
||||
if (error) {
|
||||
std::cerr << "Failed to start WSA: " << error << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
spiceapi::Connection::~Connection() {
|
||||
|
||||
// clean up
|
||||
if (this->cipher != nullptr)
|
||||
delete this->cipher;
|
||||
|
||||
// cleanup WSA
|
||||
WSACleanup();
|
||||
}
|
||||
|
||||
void spiceapi::Connection::cipher_alloc() {
|
||||
|
||||
// delete old cipher
|
||||
if (this->cipher != nullptr) {
|
||||
delete this->cipher;
|
||||
this->cipher = nullptr;
|
||||
}
|
||||
|
||||
// create new cipher if password is set
|
||||
if (this->password.length() > 0) {
|
||||
this->cipher = new RC4(
|
||||
(uint8_t *) this->password.c_str(),
|
||||
strlen(this->password.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
bool spiceapi::Connection::check() {
|
||||
int result = 0;
|
||||
|
||||
// check if socket is invalid
|
||||
if (this->socket == INVALID_SOCKET) {
|
||||
|
||||
// get all addresses
|
||||
addrinfo *addr_list;
|
||||
addrinfo hints{};
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = IPPROTO_TCP;
|
||||
if ((result = getaddrinfo(
|
||||
this->host.c_str(),
|
||||
std::to_string(this->port).c_str(),
|
||||
&hints,
|
||||
&addr_list))) {
|
||||
std::cerr << "getaddrinfo failed: " << result << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// check all addresses
|
||||
for (addrinfo *addr = addr_list; addr != NULL; addr = addr->ai_next) {
|
||||
|
||||
// try open socket
|
||||
this->socket = ::socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
|
||||
if (this->socket == INVALID_SOCKET) {
|
||||
std::cerr << "socket failed: " << WSAGetLastError() << std::endl;
|
||||
freeaddrinfo(addr_list);
|
||||
return false;
|
||||
}
|
||||
|
||||
// try connect
|
||||
result = connect(this->socket, addr->ai_addr, (int) addr->ai_addrlen);
|
||||
if (result == SOCKET_ERROR) {
|
||||
closesocket(this->socket);
|
||||
this->socket = INVALID_SOCKET;
|
||||
continue;
|
||||
}
|
||||
|
||||
// configure socket
|
||||
int opt_val;
|
||||
opt_val = 1;
|
||||
setsockopt(this->socket, IPPROTO_TCP, TCP_NODELAY, (const char*) &opt_val, sizeof(opt_val));
|
||||
opt_val = RECEIVE_TIMEOUT;
|
||||
setsockopt(this->socket, SOL_SOCKET, SO_RCVTIMEO, (const char*) &opt_val, sizeof(opt_val));
|
||||
|
||||
// connection successful
|
||||
this->cipher_alloc();
|
||||
break;
|
||||
}
|
||||
|
||||
// check if successful
|
||||
freeaddrinfo(addr_list);
|
||||
if (this->socket == INVALID_SOCKET) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// socket probably still valid
|
||||
return true;
|
||||
}
|
||||
|
||||
void spiceapi::Connection::change_pass(std::string password) {
|
||||
this->password = password;
|
||||
this->cipher_alloc();
|
||||
}
|
||||
|
||||
std::string spiceapi::Connection::request(std::string json) {
|
||||
|
||||
// check connection
|
||||
if (!this->check())
|
||||
return "";
|
||||
|
||||
// crypt
|
||||
auto json_len = strlen(json.c_str()) + 1;
|
||||
uint8_t* json_data = new uint8_t[json_len];
|
||||
memcpy(json_data, json.c_str(), json_len);
|
||||
if (this->cipher != nullptr)
|
||||
this->cipher->crypt(json_data, json_len);
|
||||
|
||||
// send
|
||||
auto send_result = send(this->socket, (const char*) json_data, (int) json_len, 0);
|
||||
delete[] json_data;
|
||||
if (send_result == SOCKET_ERROR || send_result < (int) json_len) {
|
||||
closesocket(this->socket);
|
||||
this->socket = INVALID_SOCKET;
|
||||
return "";
|
||||
}
|
||||
|
||||
// receive
|
||||
uint8_t receive_data[RECEIVE_BUFFER_SIZE];
|
||||
size_t receive_data_len = 0;
|
||||
int receive_result;
|
||||
while ((receive_result = recv(
|
||||
this->socket,
|
||||
(char*) &receive_data[receive_data_len],
|
||||
sizeof(receive_data) - receive_data_len, 0)) > 0) {
|
||||
|
||||
// check for buffer overflow
|
||||
if (receive_data_len + receive_result >= sizeof(receive_data)) {
|
||||
closesocket(this->socket);
|
||||
this->socket = INVALID_SOCKET;
|
||||
return "";
|
||||
}
|
||||
|
||||
// crypt
|
||||
if (this->cipher != nullptr)
|
||||
this->cipher->crypt(&receive_data[receive_data_len], (size_t) receive_result);
|
||||
|
||||
// increase received data length
|
||||
receive_data_len += receive_result;
|
||||
|
||||
// check for message end
|
||||
if (receive_data[receive_data_len - 1] == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
// return resulting json
|
||||
if (receive_data_len > 0) {
|
||||
return std::string((const char *) &receive_data[0], receive_data_len - 1);
|
||||
} else {
|
||||
|
||||
// receive error
|
||||
this->socket = INVALID_SOCKET;
|
||||
return "";
|
||||
}
|
||||
}
|
||||
#include <iostream>
|
||||
#include <ws2tcpip.h>
|
||||
#include "connection.h"
|
||||
|
||||
namespace spiceapi {
|
||||
|
||||
// settings
|
||||
static const size_t RECEIVE_BUFFER_SIZE = 64 * 1024;
|
||||
static const int RECEIVE_TIMEOUT = 1000;
|
||||
}
|
||||
|
||||
spiceapi::Connection::Connection(std::string host, uint16_t port, std::string password) {
|
||||
this->host = host;
|
||||
this->port = port;
|
||||
this->password = password;
|
||||
this->socket = INVALID_SOCKET;
|
||||
this->cipher = nullptr;
|
||||
|
||||
// WSA startup
|
||||
WSADATA wsa_data;
|
||||
int error = WSAStartup(MAKEWORD(2, 2), &wsa_data);
|
||||
if (error) {
|
||||
std::cerr << "Failed to start WSA: " << error << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
spiceapi::Connection::~Connection() {
|
||||
|
||||
// clean up
|
||||
if (this->cipher != nullptr)
|
||||
delete this->cipher;
|
||||
|
||||
// cleanup WSA
|
||||
WSACleanup();
|
||||
}
|
||||
|
||||
void spiceapi::Connection::cipher_alloc() {
|
||||
|
||||
// delete old cipher
|
||||
if (this->cipher != nullptr) {
|
||||
delete this->cipher;
|
||||
this->cipher = nullptr;
|
||||
}
|
||||
|
||||
// create new cipher if password is set
|
||||
if (this->password.length() > 0) {
|
||||
this->cipher = new RC4(
|
||||
(uint8_t *) this->password.c_str(),
|
||||
strlen(this->password.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
bool spiceapi::Connection::check() {
|
||||
int result = 0;
|
||||
|
||||
// check if socket is invalid
|
||||
if (this->socket == INVALID_SOCKET) {
|
||||
|
||||
// get all addresses
|
||||
addrinfo *addr_list;
|
||||
addrinfo hints{};
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = IPPROTO_TCP;
|
||||
if ((result = getaddrinfo(
|
||||
this->host.c_str(),
|
||||
std::to_string(this->port).c_str(),
|
||||
&hints,
|
||||
&addr_list))) {
|
||||
std::cerr << "getaddrinfo failed: " << result << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// check all addresses
|
||||
for (addrinfo *addr = addr_list; addr != NULL; addr = addr->ai_next) {
|
||||
|
||||
// try open socket
|
||||
this->socket = ::socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
|
||||
if (this->socket == INVALID_SOCKET) {
|
||||
std::cerr << "socket failed: " << WSAGetLastError() << std::endl;
|
||||
freeaddrinfo(addr_list);
|
||||
return false;
|
||||
}
|
||||
|
||||
// try connect
|
||||
result = connect(this->socket, addr->ai_addr, (int) addr->ai_addrlen);
|
||||
if (result == SOCKET_ERROR) {
|
||||
closesocket(this->socket);
|
||||
this->socket = INVALID_SOCKET;
|
||||
continue;
|
||||
}
|
||||
|
||||
// configure socket
|
||||
int opt_val;
|
||||
opt_val = 1;
|
||||
setsockopt(this->socket, IPPROTO_TCP, TCP_NODELAY, (const char*) &opt_val, sizeof(opt_val));
|
||||
opt_val = RECEIVE_TIMEOUT;
|
||||
setsockopt(this->socket, SOL_SOCKET, SO_RCVTIMEO, (const char*) &opt_val, sizeof(opt_val));
|
||||
|
||||
// connection successful
|
||||
this->cipher_alloc();
|
||||
break;
|
||||
}
|
||||
|
||||
// check if successful
|
||||
freeaddrinfo(addr_list);
|
||||
if (this->socket == INVALID_SOCKET) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// socket probably still valid
|
||||
return true;
|
||||
}
|
||||
|
||||
void spiceapi::Connection::change_pass(std::string password) {
|
||||
this->password = password;
|
||||
this->cipher_alloc();
|
||||
}
|
||||
|
||||
std::string spiceapi::Connection::request(std::string json) {
|
||||
|
||||
// check connection
|
||||
if (!this->check())
|
||||
return "";
|
||||
|
||||
// crypt
|
||||
auto json_len = strlen(json.c_str()) + 1;
|
||||
uint8_t* json_data = new uint8_t[json_len];
|
||||
memcpy(json_data, json.c_str(), json_len);
|
||||
if (this->cipher != nullptr)
|
||||
this->cipher->crypt(json_data, json_len);
|
||||
|
||||
// send
|
||||
auto send_result = send(this->socket, (const char*) json_data, (int) json_len, 0);
|
||||
delete[] json_data;
|
||||
if (send_result == SOCKET_ERROR || send_result < (int) json_len) {
|
||||
closesocket(this->socket);
|
||||
this->socket = INVALID_SOCKET;
|
||||
return "";
|
||||
}
|
||||
|
||||
// receive
|
||||
uint8_t receive_data[RECEIVE_BUFFER_SIZE];
|
||||
size_t receive_data_len = 0;
|
||||
int receive_result;
|
||||
while ((receive_result = recv(
|
||||
this->socket,
|
||||
(char*) &receive_data[receive_data_len],
|
||||
sizeof(receive_data) - receive_data_len, 0)) > 0) {
|
||||
|
||||
// check for buffer overflow
|
||||
if (receive_data_len + receive_result >= sizeof(receive_data)) {
|
||||
closesocket(this->socket);
|
||||
this->socket = INVALID_SOCKET;
|
||||
return "";
|
||||
}
|
||||
|
||||
// crypt
|
||||
if (this->cipher != nullptr)
|
||||
this->cipher->crypt(&receive_data[receive_data_len], (size_t) receive_result);
|
||||
|
||||
// increase received data length
|
||||
receive_data_len += receive_result;
|
||||
|
||||
// check for message end
|
||||
if (receive_data[receive_data_len - 1] == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
// return resulting json
|
||||
if (receive_data_len > 0) {
|
||||
return std::string((const char *) &receive_data[0], receive_data_len - 1);
|
||||
} else {
|
||||
|
||||
// receive error
|
||||
this->socket = INVALID_SOCKET;
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,31 +1,31 @@
|
||||
#ifndef SPICEAPI_CONNECTION_H
|
||||
#define SPICEAPI_CONNECTION_H
|
||||
|
||||
#include <string>
|
||||
#include <winsock2.h>
|
||||
#include "rc4.h"
|
||||
|
||||
namespace spiceapi {
|
||||
|
||||
class Connection {
|
||||
private:
|
||||
std::string host;
|
||||
uint16_t port;
|
||||
std::string password;
|
||||
SOCKET socket;
|
||||
RC4* cipher;
|
||||
|
||||
void cipher_alloc();
|
||||
|
||||
public:
|
||||
Connection(std::string host, uint16_t port, std::string password = "");
|
||||
~Connection();
|
||||
|
||||
bool check();
|
||||
void change_pass(std::string password);
|
||||
std::string request(std::string json);
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif //SPICEAPI_CONNECTION_H
|
||||
#ifndef SPICEAPI_CONNECTION_H
|
||||
#define SPICEAPI_CONNECTION_H
|
||||
|
||||
#include <string>
|
||||
#include <winsock2.h>
|
||||
#include "rc4.h"
|
||||
|
||||
namespace spiceapi {
|
||||
|
||||
class Connection {
|
||||
private:
|
||||
std::string host;
|
||||
uint16_t port;
|
||||
std::string password;
|
||||
SOCKET socket;
|
||||
RC4* cipher;
|
||||
|
||||
void cipher_alloc();
|
||||
|
||||
public:
|
||||
Connection(std::string host, uint16_t port, std::string password = "");
|
||||
~Connection();
|
||||
|
||||
bool check();
|
||||
void change_pass(std::string password);
|
||||
std::string request(std::string json);
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif //SPICEAPI_CONNECTION_H
|
||||
|
||||
@@ -1,45 +1,45 @@
|
||||
#include "rc4.h"
|
||||
#include <iterator>
|
||||
|
||||
spiceapi::RC4::RC4(uint8_t *key, size_t key_size) {
|
||||
|
||||
// initialize S-BOX
|
||||
for (size_t i = 0; i < std::size(s_box); i++)
|
||||
s_box[i] = (uint8_t) i;
|
||||
|
||||
// check key size
|
||||
if (!key_size)
|
||||
return;
|
||||
|
||||
// KSA
|
||||
size_t j = 0;
|
||||
for (size_t i = 0; i < std::size(s_box); i++) {
|
||||
|
||||
// update
|
||||
j = (j + s_box[i] + key[i % key_size]) % std::size(s_box);
|
||||
|
||||
// swap
|
||||
auto tmp = s_box[i];
|
||||
s_box[i] = s_box[j];
|
||||
s_box[j] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
void spiceapi::RC4::crypt(uint8_t *data, size_t size) {
|
||||
|
||||
// iterate all bytes
|
||||
for (size_t pos = 0; pos < size; pos++) {
|
||||
|
||||
// update
|
||||
a = (a + 1) % std::size(s_box);
|
||||
b = (b + s_box[a]) % std::size(s_box);
|
||||
|
||||
// swap
|
||||
auto tmp = s_box[a];
|
||||
s_box[a] = s_box[b];
|
||||
s_box[b] = tmp;
|
||||
|
||||
// crypt
|
||||
data[pos] ^= s_box[(s_box[a] + s_box[b]) % std::size(s_box)];
|
||||
}
|
||||
}
|
||||
#include "rc4.h"
|
||||
#include <iterator>
|
||||
|
||||
spiceapi::RC4::RC4(uint8_t *key, size_t key_size) {
|
||||
|
||||
// initialize S-BOX
|
||||
for (size_t i = 0; i < std::size(s_box); i++)
|
||||
s_box[i] = (uint8_t) i;
|
||||
|
||||
// check key size
|
||||
if (!key_size)
|
||||
return;
|
||||
|
||||
// KSA
|
||||
size_t j = 0;
|
||||
for (size_t i = 0; i < std::size(s_box); i++) {
|
||||
|
||||
// update
|
||||
j = (j + s_box[i] + key[i % key_size]) % std::size(s_box);
|
||||
|
||||
// swap
|
||||
auto tmp = s_box[i];
|
||||
s_box[i] = s_box[j];
|
||||
s_box[j] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
void spiceapi::RC4::crypt(uint8_t *data, size_t size) {
|
||||
|
||||
// iterate all bytes
|
||||
for (size_t pos = 0; pos < size; pos++) {
|
||||
|
||||
// update
|
||||
a = (a + 1) % std::size(s_box);
|
||||
b = (b + s_box[a]) % std::size(s_box);
|
||||
|
||||
// swap
|
||||
auto tmp = s_box[a];
|
||||
s_box[a] = s_box[b];
|
||||
s_box[b] = tmp;
|
||||
|
||||
// crypt
|
||||
data[pos] ^= s_box[(s_box[a] + s_box[b]) % std::size(s_box)];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
#ifndef SPICEAPI_RC4_H
|
||||
#define SPICEAPI_RC4_H
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace spiceapi {
|
||||
|
||||
class RC4 {
|
||||
private:
|
||||
uint8_t s_box[256];
|
||||
size_t a = 0, b = 0;
|
||||
|
||||
public:
|
||||
|
||||
RC4(uint8_t *key, size_t key_size);
|
||||
|
||||
void crypt(uint8_t *data, size_t size);
|
||||
};
|
||||
}
|
||||
|
||||
#endif //SPICEAPI_RC4_H
|
||||
#ifndef SPICEAPI_RC4_H
|
||||
#define SPICEAPI_RC4_H
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace spiceapi {
|
||||
|
||||
class RC4 {
|
||||
private:
|
||||
uint8_t s_box[256];
|
||||
size_t a = 0, b = 0;
|
||||
|
||||
public:
|
||||
|
||||
RC4(uint8_t *key, size_t key_size);
|
||||
|
||||
void crypt(uint8_t *data, size_t size);
|
||||
};
|
||||
}
|
||||
|
||||
#endif //SPICEAPI_RC4_H
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,104 +1,104 @@
|
||||
#ifndef SPICEAPI_WRAPPERS_H
|
||||
#define SPICEAPI_WRAPPERS_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include "connection.h"
|
||||
|
||||
namespace spiceapi {
|
||||
|
||||
struct AnalogState {
|
||||
std::string name;
|
||||
float value;
|
||||
};
|
||||
|
||||
struct ButtonState {
|
||||
std::string name;
|
||||
float value;
|
||||
};
|
||||
|
||||
struct LightState {
|
||||
std::string name;
|
||||
float value;
|
||||
};
|
||||
|
||||
struct InfoAvs {
|
||||
std::string model, dest, spec, rev, ext;
|
||||
};
|
||||
|
||||
struct InfoLauncher {
|
||||
std::string version;
|
||||
std::string compile_date, compile_time, system_time;
|
||||
std::vector<std::string> args;
|
||||
};
|
||||
|
||||
struct InfoMemory {
|
||||
uint64_t mem_total, mem_total_used, mem_used;
|
||||
uint64_t vmem_total, vmem_total_used, vmem_used;
|
||||
};
|
||||
|
||||
struct TouchState {
|
||||
uint64_t id;
|
||||
int64_t x, y;
|
||||
};
|
||||
|
||||
struct LCDInfo {
|
||||
bool enabled;
|
||||
std::string csm;
|
||||
uint8_t bri, con, bl, red, green, blue;
|
||||
};
|
||||
|
||||
uint64_t msg_gen_id();
|
||||
|
||||
bool analogs_read(Connection &con, std::vector<AnalogState> &states);
|
||||
bool analogs_write(Connection &con, std::vector<AnalogState> &states);
|
||||
bool analogs_write_reset(Connection &con, std::vector<AnalogState> &states);
|
||||
|
||||
bool buttons_read(Connection &con, std::vector<ButtonState> &states);
|
||||
bool buttons_write(Connection &con, std::vector<ButtonState> &states);
|
||||
bool buttons_write_reset(Connection &con, std::vector<ButtonState> &states);
|
||||
|
||||
bool card_insert(Connection &con, size_t index, const char *card_id);
|
||||
|
||||
bool coin_get(Connection &con, int &coins);
|
||||
bool coin_set(Connection &con, int coins);
|
||||
bool coin_insert(Connection &con, int coins=1);
|
||||
bool coin_blocker_get(Connection &con, bool &closed);
|
||||
|
||||
bool control_raise(Connection &con, const char *signal);
|
||||
bool control_exit(Connection &con);
|
||||
bool control_exit(Connection &con, int exit_code);
|
||||
bool control_restart(Connection &con);
|
||||
bool control_session_refresh(Connection &con);
|
||||
bool control_shutdown(Connection &con);
|
||||
bool control_reboot(Connection &con);
|
||||
|
||||
bool iidx_ticker_get(Connection &con, char *ticker);
|
||||
bool iidx_ticker_set(Connection &con, const char *ticker);
|
||||
bool iidx_ticker_reset(Connection &con);
|
||||
|
||||
bool info_avs(Connection &con, InfoAvs &info);
|
||||
bool info_launcher(Connection &con, InfoLauncher &info);
|
||||
bool info_memory(Connection &con, InfoMemory &info);
|
||||
|
||||
bool keypads_write(Connection &con, unsigned int keypad, const char *input);
|
||||
bool keypads_set(Connection &con, unsigned int keypad, std::vector<char> &keys);
|
||||
bool keypads_get(Connection &con, unsigned int keypad, std::vector<char> &keys);
|
||||
|
||||
bool lights_read(Connection &con, std::vector<LightState> &states);
|
||||
bool lights_write(Connection &con, std::vector<LightState> &states);
|
||||
bool lights_write_reset(Connection &con, std::vector<LightState> &states);
|
||||
|
||||
bool memory_write(Connection &con, const char *dll_name, const char *hex, uint32_t offset);
|
||||
bool memory_read(Connection &con, const char *dll_name, uint32_t offset, uint32_t size, std::string &hex);
|
||||
bool memory_signature(Connection &con, const char *dll_name, const char *signature, const char *replacement,
|
||||
uint32_t offset, uint32_t usage, uint32_t &file_offset);
|
||||
|
||||
bool touch_read(Connection &con, std::vector<TouchState> &states);
|
||||
bool touch_write(Connection &con, std::vector<TouchState> &states);
|
||||
bool touch_write_reset(Connection &con, std::vector<TouchState> &states);
|
||||
|
||||
bool lcd_info(Connection &con, LCDInfo &info);
|
||||
}
|
||||
|
||||
#endif //SPICEAPI_WRAPPERS_H
|
||||
#ifndef SPICEAPI_WRAPPERS_H
|
||||
#define SPICEAPI_WRAPPERS_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include "connection.h"
|
||||
|
||||
namespace spiceapi {
|
||||
|
||||
struct AnalogState {
|
||||
std::string name;
|
||||
float value;
|
||||
};
|
||||
|
||||
struct ButtonState {
|
||||
std::string name;
|
||||
float value;
|
||||
};
|
||||
|
||||
struct LightState {
|
||||
std::string name;
|
||||
float value;
|
||||
};
|
||||
|
||||
struct InfoAvs {
|
||||
std::string model, dest, spec, rev, ext;
|
||||
};
|
||||
|
||||
struct InfoLauncher {
|
||||
std::string version;
|
||||
std::string compile_date, compile_time, system_time;
|
||||
std::vector<std::string> args;
|
||||
};
|
||||
|
||||
struct InfoMemory {
|
||||
uint64_t mem_total, mem_total_used, mem_used;
|
||||
uint64_t vmem_total, vmem_total_used, vmem_used;
|
||||
};
|
||||
|
||||
struct TouchState {
|
||||
uint64_t id;
|
||||
int64_t x, y;
|
||||
};
|
||||
|
||||
struct LCDInfo {
|
||||
bool enabled;
|
||||
std::string csm;
|
||||
uint8_t bri, con, bl, red, green, blue;
|
||||
};
|
||||
|
||||
uint64_t msg_gen_id();
|
||||
|
||||
bool analogs_read(Connection &con, std::vector<AnalogState> &states);
|
||||
bool analogs_write(Connection &con, std::vector<AnalogState> &states);
|
||||
bool analogs_write_reset(Connection &con, std::vector<AnalogState> &states);
|
||||
|
||||
bool buttons_read(Connection &con, std::vector<ButtonState> &states);
|
||||
bool buttons_write(Connection &con, std::vector<ButtonState> &states);
|
||||
bool buttons_write_reset(Connection &con, std::vector<ButtonState> &states);
|
||||
|
||||
bool card_insert(Connection &con, size_t index, const char *card_id);
|
||||
|
||||
bool coin_get(Connection &con, int &coins);
|
||||
bool coin_set(Connection &con, int coins);
|
||||
bool coin_insert(Connection &con, int coins=1);
|
||||
bool coin_blocker_get(Connection &con, bool &closed);
|
||||
|
||||
bool control_raise(Connection &con, const char *signal);
|
||||
bool control_exit(Connection &con);
|
||||
bool control_exit(Connection &con, int exit_code);
|
||||
bool control_restart(Connection &con);
|
||||
bool control_session_refresh(Connection &con);
|
||||
bool control_shutdown(Connection &con);
|
||||
bool control_reboot(Connection &con);
|
||||
|
||||
bool iidx_ticker_get(Connection &con, char *ticker);
|
||||
bool iidx_ticker_set(Connection &con, const char *ticker);
|
||||
bool iidx_ticker_reset(Connection &con);
|
||||
|
||||
bool info_avs(Connection &con, InfoAvs &info);
|
||||
bool info_launcher(Connection &con, InfoLauncher &info);
|
||||
bool info_memory(Connection &con, InfoMemory &info);
|
||||
|
||||
bool keypads_write(Connection &con, unsigned int keypad, const char *input);
|
||||
bool keypads_set(Connection &con, unsigned int keypad, std::vector<char> &keys);
|
||||
bool keypads_get(Connection &con, unsigned int keypad, std::vector<char> &keys);
|
||||
|
||||
bool lights_read(Connection &con, std::vector<LightState> &states);
|
||||
bool lights_write(Connection &con, std::vector<LightState> &states);
|
||||
bool lights_write_reset(Connection &con, std::vector<LightState> &states);
|
||||
|
||||
bool memory_write(Connection &con, const char *dll_name, const char *hex, uint32_t offset);
|
||||
bool memory_read(Connection &con, const char *dll_name, uint32_t offset, uint32_t size, std::string &hex);
|
||||
bool memory_signature(Connection &con, const char *dll_name, const char *signature, const char *replacement,
|
||||
uint32_t offset, uint32_t usage, uint32_t &file_offset);
|
||||
|
||||
bool touch_read(Connection &con, std::vector<TouchState> &states);
|
||||
bool touch_write(Connection &con, std::vector<TouchState> &states);
|
||||
bool touch_write_reset(Connection &con, std::vector<TouchState> &states);
|
||||
|
||||
bool lcd_info(Connection &con, LCDInfo &info);
|
||||
}
|
||||
|
||||
#endif //SPICEAPI_WRAPPERS_H
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
For more information, please refer to <http://unlicense.org/>
|
||||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
For more information, please refer to <http://unlicense.org/>
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
library spiceapi;
|
||||
import 'dart:convert';
|
||||
import 'dart:async';
|
||||
import 'dart:math';
|
||||
import 'dart:html';
|
||||
import 'dart:typed_data';
|
||||
part "src/connection.dart";
|
||||
part "src/request.dart";
|
||||
part "src/response.dart";
|
||||
part "src/exceptions.dart";
|
||||
part "src/rc4.dart";
|
||||
part "src/wrappers/analogs.dart";
|
||||
part "src/wrappers/buttons.dart";
|
||||
part "src/wrappers/card.dart";
|
||||
part "src/wrappers/coin.dart";
|
||||
part "src/wrappers/control.dart";
|
||||
part "src/wrappers/info.dart";
|
||||
part "src/wrappers/keypads.dart";
|
||||
part "src/wrappers/lights.dart";
|
||||
part "src/wrappers/memory.dart";
|
||||
part "src/wrappers/iidx.dart";
|
||||
part "src/wrappers/touch.dart";
|
||||
library spiceapi;
|
||||
import 'dart:convert';
|
||||
import 'dart:async';
|
||||
import 'dart:math';
|
||||
import 'dart:html';
|
||||
import 'dart:typed_data';
|
||||
part "src/connection.dart";
|
||||
part "src/request.dart";
|
||||
part "src/response.dart";
|
||||
part "src/exceptions.dart";
|
||||
part "src/rc4.dart";
|
||||
part "src/wrappers/analogs.dart";
|
||||
part "src/wrappers/buttons.dart";
|
||||
part "src/wrappers/card.dart";
|
||||
part "src/wrappers/coin.dart";
|
||||
part "src/wrappers/control.dart";
|
||||
part "src/wrappers/info.dart";
|
||||
part "src/wrappers/keypads.dart";
|
||||
part "src/wrappers/lights.dart";
|
||||
part "src/wrappers/memory.dart";
|
||||
part "src/wrappers/iidx.dart";
|
||||
part "src/wrappers/touch.dart";
|
||||
|
||||
@@ -1,193 +1,193 @@
|
||||
part of spiceapi;
|
||||
|
||||
|
||||
class Connection {
|
||||
|
||||
// settings
|
||||
static const _TIMEOUT = Duration(seconds: 2);
|
||||
static const _BUFFER_SIZE = 1024 * 8;
|
||||
|
||||
// state
|
||||
final String host, pass;
|
||||
final int port;
|
||||
var resource;
|
||||
List<int> _dataBuffer;
|
||||
StreamController<Response> _responses;
|
||||
StreamController<Connection> _connections;
|
||||
WebSocket _socket;
|
||||
RC4 _cipher;
|
||||
bool _disposed = false;
|
||||
|
||||
Connection(this.host, this.port, this.pass,
|
||||
{this.resource, bool refreshSession=true}) {
|
||||
|
||||
// initialize
|
||||
_dataBuffer = List<int>();
|
||||
_responses = StreamController<Response>.broadcast();
|
||||
_connections = StreamController<Connection>.broadcast();
|
||||
if (pass.length > 0)
|
||||
_cipher = RC4(utf8.encode(pass));
|
||||
|
||||
// initialize socket
|
||||
this._socket = WebSocket("ws://$host:${port + 1}");
|
||||
this._socket.binaryType = "arraybuffer";
|
||||
|
||||
// listen to events
|
||||
this._socket.onOpen.listen((e) async {
|
||||
|
||||
// refresh session
|
||||
bool error = false;
|
||||
if (refreshSession) {
|
||||
try {
|
||||
await controlRefreshSession(this);
|
||||
} on Error {
|
||||
error = true;
|
||||
} on TimeoutException {
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
|
||||
// mark as connected
|
||||
if (!this._connections.isClosed)
|
||||
this._connections.add(this);
|
||||
if (error)
|
||||
this.dispose();
|
||||
});
|
||||
|
||||
this._socket.onMessage.listen((e) {
|
||||
|
||||
// get data
|
||||
var data = e.data;
|
||||
if (data is ByteBuffer)
|
||||
data = data.asUint8List();
|
||||
|
||||
// check type
|
||||
if (data is List<int>) {
|
||||
|
||||
// cipher
|
||||
if (_cipher != null)
|
||||
_cipher.crypt(data);
|
||||
|
||||
// add data to buffer
|
||||
_dataBuffer.addAll(data);
|
||||
|
||||
// check buffer size
|
||||
if (_dataBuffer.length > _BUFFER_SIZE) {
|
||||
this.dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
// check for completed message
|
||||
for (int i = 0; i < _dataBuffer.length; i++) {
|
||||
if (_dataBuffer[i] == 0) {
|
||||
|
||||
// get message data and remove from buffer
|
||||
var msgData = List<int>.from(_dataBuffer.getRange(0, i));
|
||||
_dataBuffer.removeRange(0, i + 1);
|
||||
|
||||
// check data length
|
||||
if (msgData.length > 0) {
|
||||
|
||||
// convert to JSON
|
||||
var msgStr = utf8.decode(msgData, allowMalformed: false);
|
||||
|
||||
// build response
|
||||
var res = Response.fromJson(msgStr);
|
||||
this._responses.add(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this._socket.onClose.listen((e) {
|
||||
this.dispose();
|
||||
});
|
||||
|
||||
this._socket.onError.listen((e) {
|
||||
this.dispose();
|
||||
});
|
||||
}
|
||||
|
||||
void changePass(String pass) {
|
||||
if (pass.length > 0)
|
||||
_cipher = RC4(utf8.encode(pass));
|
||||
else
|
||||
_cipher = null;
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
if (_socket != null)
|
||||
_socket.close();
|
||||
_socket = null;
|
||||
if (_responses != null)
|
||||
_responses.close();
|
||||
if (_connections != null)
|
||||
_connections.close();
|
||||
this._disposed = true;
|
||||
this.free();
|
||||
}
|
||||
|
||||
bool isDisposed() {
|
||||
return this._disposed;
|
||||
}
|
||||
|
||||
void free() {
|
||||
|
||||
// release optional resource
|
||||
if (this.resource != null) {
|
||||
this.resource.release();
|
||||
this.resource = null;
|
||||
}
|
||||
}
|
||||
|
||||
bool isFree() {
|
||||
return this.resource == null;
|
||||
}
|
||||
|
||||
Future<Connection> onConnect() {
|
||||
return _connections.stream.first;
|
||||
}
|
||||
|
||||
bool isValid() {
|
||||
return this._socket != null && !this._disposed;
|
||||
}
|
||||
|
||||
Future<Response> request(Request req) {
|
||||
|
||||
// add response listener
|
||||
var res = _awaitResponse(req._id);
|
||||
|
||||
// write request
|
||||
_writeRequest(req);
|
||||
|
||||
// return future response
|
||||
return res.then((res) {
|
||||
|
||||
// validate first
|
||||
res.validate();
|
||||
|
||||
// return it
|
||||
return res;
|
||||
});
|
||||
}
|
||||
|
||||
void _writeRequest(Request req) async {
|
||||
|
||||
// convert to JSON
|
||||
var json = req.toJson() + "\x00";
|
||||
var jsonEncoded = utf8.encode(json);
|
||||
|
||||
// cipher
|
||||
if (_cipher != null)
|
||||
_cipher.crypt(jsonEncoded);
|
||||
|
||||
// write to socket
|
||||
this._socket.sendByteBuffer(Int8List.fromList(jsonEncoded).buffer);
|
||||
}
|
||||
|
||||
Future<Response> _awaitResponse(int id) {
|
||||
return _responses.stream.timeout(_TIMEOUT).firstWhere(
|
||||
(res) => res._id == id, orElse: null);
|
||||
}
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
|
||||
class Connection {
|
||||
|
||||
// settings
|
||||
static const _TIMEOUT = Duration(seconds: 2);
|
||||
static const _BUFFER_SIZE = 1024 * 8;
|
||||
|
||||
// state
|
||||
final String host, pass;
|
||||
final int port;
|
||||
var resource;
|
||||
List<int> _dataBuffer;
|
||||
StreamController<Response> _responses;
|
||||
StreamController<Connection> _connections;
|
||||
WebSocket _socket;
|
||||
RC4 _cipher;
|
||||
bool _disposed = false;
|
||||
|
||||
Connection(this.host, this.port, this.pass,
|
||||
{this.resource, bool refreshSession=true}) {
|
||||
|
||||
// initialize
|
||||
_dataBuffer = List<int>();
|
||||
_responses = StreamController<Response>.broadcast();
|
||||
_connections = StreamController<Connection>.broadcast();
|
||||
if (pass.length > 0)
|
||||
_cipher = RC4(utf8.encode(pass));
|
||||
|
||||
// initialize socket
|
||||
this._socket = WebSocket("ws://$host:${port + 1}");
|
||||
this._socket.binaryType = "arraybuffer";
|
||||
|
||||
// listen to events
|
||||
this._socket.onOpen.listen((e) async {
|
||||
|
||||
// refresh session
|
||||
bool error = false;
|
||||
if (refreshSession) {
|
||||
try {
|
||||
await controlRefreshSession(this);
|
||||
} on Error {
|
||||
error = true;
|
||||
} on TimeoutException {
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
|
||||
// mark as connected
|
||||
if (!this._connections.isClosed)
|
||||
this._connections.add(this);
|
||||
if (error)
|
||||
this.dispose();
|
||||
});
|
||||
|
||||
this._socket.onMessage.listen((e) {
|
||||
|
||||
// get data
|
||||
var data = e.data;
|
||||
if (data is ByteBuffer)
|
||||
data = data.asUint8List();
|
||||
|
||||
// check type
|
||||
if (data is List<int>) {
|
||||
|
||||
// cipher
|
||||
if (_cipher != null)
|
||||
_cipher.crypt(data);
|
||||
|
||||
// add data to buffer
|
||||
_dataBuffer.addAll(data);
|
||||
|
||||
// check buffer size
|
||||
if (_dataBuffer.length > _BUFFER_SIZE) {
|
||||
this.dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
// check for completed message
|
||||
for (int i = 0; i < _dataBuffer.length; i++) {
|
||||
if (_dataBuffer[i] == 0) {
|
||||
|
||||
// get message data and remove from buffer
|
||||
var msgData = List<int>.from(_dataBuffer.getRange(0, i));
|
||||
_dataBuffer.removeRange(0, i + 1);
|
||||
|
||||
// check data length
|
||||
if (msgData.length > 0) {
|
||||
|
||||
// convert to JSON
|
||||
var msgStr = utf8.decode(msgData, allowMalformed: false);
|
||||
|
||||
// build response
|
||||
var res = Response.fromJson(msgStr);
|
||||
this._responses.add(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this._socket.onClose.listen((e) {
|
||||
this.dispose();
|
||||
});
|
||||
|
||||
this._socket.onError.listen((e) {
|
||||
this.dispose();
|
||||
});
|
||||
}
|
||||
|
||||
void changePass(String pass) {
|
||||
if (pass.length > 0)
|
||||
_cipher = RC4(utf8.encode(pass));
|
||||
else
|
||||
_cipher = null;
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
if (_socket != null)
|
||||
_socket.close();
|
||||
_socket = null;
|
||||
if (_responses != null)
|
||||
_responses.close();
|
||||
if (_connections != null)
|
||||
_connections.close();
|
||||
this._disposed = true;
|
||||
this.free();
|
||||
}
|
||||
|
||||
bool isDisposed() {
|
||||
return this._disposed;
|
||||
}
|
||||
|
||||
void free() {
|
||||
|
||||
// release optional resource
|
||||
if (this.resource != null) {
|
||||
this.resource.release();
|
||||
this.resource = null;
|
||||
}
|
||||
}
|
||||
|
||||
bool isFree() {
|
||||
return this.resource == null;
|
||||
}
|
||||
|
||||
Future<Connection> onConnect() {
|
||||
return _connections.stream.first;
|
||||
}
|
||||
|
||||
bool isValid() {
|
||||
return this._socket != null && !this._disposed;
|
||||
}
|
||||
|
||||
Future<Response> request(Request req) {
|
||||
|
||||
// add response listener
|
||||
var res = _awaitResponse(req._id);
|
||||
|
||||
// write request
|
||||
_writeRequest(req);
|
||||
|
||||
// return future response
|
||||
return res.then((res) {
|
||||
|
||||
// validate first
|
||||
res.validate();
|
||||
|
||||
// return it
|
||||
return res;
|
||||
});
|
||||
}
|
||||
|
||||
void _writeRequest(Request req) async {
|
||||
|
||||
// convert to JSON
|
||||
var json = req.toJson() + "\x00";
|
||||
var jsonEncoded = utf8.encode(json);
|
||||
|
||||
// cipher
|
||||
if (_cipher != null)
|
||||
_cipher.crypt(jsonEncoded);
|
||||
|
||||
// write to socket
|
||||
this._socket.sendByteBuffer(Int8List.fromList(jsonEncoded).buffer);
|
||||
}
|
||||
|
||||
Future<Response> _awaitResponse(int id) {
|
||||
return _responses.stream.timeout(_TIMEOUT).firstWhere(
|
||||
(res) => res._id == id, orElse: null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
part of spiceapi;
|
||||
|
||||
class APIError implements Exception {
|
||||
String cause;
|
||||
APIError(this.cause);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return this.cause;
|
||||
}
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
class APIError implements Exception {
|
||||
String cause;
|
||||
APIError(this.cause);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return this.cause;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,48 +1,48 @@
|
||||
part of spiceapi;
|
||||
|
||||
class RC4 {
|
||||
|
||||
// state
|
||||
int _a = 0;
|
||||
int _b = 0;
|
||||
List<int> _sBox = List<int>(256);
|
||||
|
||||
RC4(List<int> key) {
|
||||
|
||||
// init sBox
|
||||
for (int i = 0; i < 256; i++) {
|
||||
_sBox[i] = i;
|
||||
}
|
||||
|
||||
// process key
|
||||
int j = 0;
|
||||
for (int i = 0; i < 256; i++) {
|
||||
|
||||
// update
|
||||
j = (j + _sBox[i] + key[i % key.length]) % 256;
|
||||
|
||||
// swap
|
||||
var tmp = _sBox[i];
|
||||
_sBox[i] = _sBox[j];
|
||||
_sBox[j] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
void crypt(List<int> inData) {
|
||||
for (int i = 0; i < inData.length; i++) {
|
||||
|
||||
// update
|
||||
_a = (_a + 1) % 256;
|
||||
_b = (_b + _sBox[_a]) % 256;
|
||||
|
||||
// swap
|
||||
var tmp = _sBox[_a];
|
||||
_sBox[_a] = _sBox[_b];
|
||||
_sBox[_b] = tmp;
|
||||
|
||||
// crypt
|
||||
inData[i] ^= _sBox[(_sBox[_a] + _sBox[_b]) % 256];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
class RC4 {
|
||||
|
||||
// state
|
||||
int _a = 0;
|
||||
int _b = 0;
|
||||
List<int> _sBox = List<int>(256);
|
||||
|
||||
RC4(List<int> key) {
|
||||
|
||||
// init sBox
|
||||
for (int i = 0; i < 256; i++) {
|
||||
_sBox[i] = i;
|
||||
}
|
||||
|
||||
// process key
|
||||
int j = 0;
|
||||
for (int i = 0; i < 256; i++) {
|
||||
|
||||
// update
|
||||
j = (j + _sBox[i] + key[i % key.length]) % 256;
|
||||
|
||||
// swap
|
||||
var tmp = _sBox[i];
|
||||
_sBox[i] = _sBox[j];
|
||||
_sBox[j] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
void crypt(List<int> inData) {
|
||||
for (int i = 0; i < inData.length; i++) {
|
||||
|
||||
// update
|
||||
_a = (_a + 1) % 256;
|
||||
_b = (_b + _sBox[_a]) % 256;
|
||||
|
||||
// swap
|
||||
var tmp = _sBox[_a];
|
||||
_sBox[_a] = _sBox[_b];
|
||||
_sBox[_b] = tmp;
|
||||
|
||||
// crypt
|
||||
inData[i] ^= _sBox[(_sBox[_a] + _sBox[_b]) % 256];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,45 +1,45 @@
|
||||
part of spiceapi;
|
||||
|
||||
class Request {
|
||||
|
||||
static int _lastID = 0;
|
||||
|
||||
// contents
|
||||
int _id;
|
||||
String _module;
|
||||
String _function;
|
||||
List _params;
|
||||
|
||||
Request(String module, String function, {id}) {
|
||||
|
||||
// automatic ID iteration
|
||||
if (id == null) {
|
||||
if (++_lastID >= pow(2, 32))
|
||||
_lastID = 1;
|
||||
id = _lastID;
|
||||
} else
|
||||
_lastID = id;
|
||||
|
||||
// build contents
|
||||
this._id = id;
|
||||
this._module = module;
|
||||
this._function = function;
|
||||
this._params = List();
|
||||
}
|
||||
|
||||
String toJson() {
|
||||
return jsonEncode(
|
||||
{
|
||||
"id": this._id,
|
||||
"module": this._module,
|
||||
"function": this._function,
|
||||
"params": this._params,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void addParam(param) {
|
||||
this._params.add(param);
|
||||
}
|
||||
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
class Request {
|
||||
|
||||
static int _lastID = 0;
|
||||
|
||||
// contents
|
||||
int _id;
|
||||
String _module;
|
||||
String _function;
|
||||
List _params;
|
||||
|
||||
Request(String module, String function, {id}) {
|
||||
|
||||
// automatic ID iteration
|
||||
if (id == null) {
|
||||
if (++_lastID >= pow(2, 32))
|
||||
_lastID = 1;
|
||||
id = _lastID;
|
||||
} else
|
||||
_lastID = id;
|
||||
|
||||
// build contents
|
||||
this._id = id;
|
||||
this._module = module;
|
||||
this._function = function;
|
||||
this._params = List();
|
||||
}
|
||||
|
||||
String toJson() {
|
||||
return jsonEncode(
|
||||
{
|
||||
"id": this._id,
|
||||
"module": this._module,
|
||||
"function": this._function,
|
||||
"params": this._params,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void addParam(param) {
|
||||
this._params.add(param);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
part of spiceapi;
|
||||
|
||||
class Response {
|
||||
|
||||
String _json;
|
||||
int _id;
|
||||
List _errors;
|
||||
List _data;
|
||||
|
||||
Response.fromJson(String json) {
|
||||
this._json = json;
|
||||
var obj = jsonDecode(json);
|
||||
this._id = obj["id"];
|
||||
this._errors = obj["errors"];
|
||||
this._data = obj["data"];
|
||||
}
|
||||
|
||||
void validate() {
|
||||
|
||||
// check for errors
|
||||
if (_errors.length > 0) {
|
||||
// TODO: add all errors
|
||||
throw APIError(_errors[0].toString());
|
||||
}
|
||||
}
|
||||
|
||||
List getData() {
|
||||
return _data;
|
||||
}
|
||||
|
||||
String toJson() {
|
||||
return _json;
|
||||
}
|
||||
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
class Response {
|
||||
|
||||
String _json;
|
||||
int _id;
|
||||
List _errors;
|
||||
List _data;
|
||||
|
||||
Response.fromJson(String json) {
|
||||
this._json = json;
|
||||
var obj = jsonDecode(json);
|
||||
this._id = obj["id"];
|
||||
this._errors = obj["errors"];
|
||||
this._data = obj["data"];
|
||||
}
|
||||
|
||||
void validate() {
|
||||
|
||||
// check for errors
|
||||
if (_errors.length > 0) {
|
||||
// TODO: add all errors
|
||||
throw APIError(_errors[0].toString());
|
||||
}
|
||||
}
|
||||
|
||||
List getData() {
|
||||
return _data;
|
||||
}
|
||||
|
||||
String toJson() {
|
||||
return _json;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,54 +1,54 @@
|
||||
part of spiceapi;
|
||||
|
||||
class AnalogState {
|
||||
String name;
|
||||
double state;
|
||||
bool active;
|
||||
|
||||
AnalogState(this.name, this.state);
|
||||
AnalogState._fromRead(this.name, this.state, this.active);
|
||||
}
|
||||
|
||||
Future<List<AnalogState>> analogsRead(Connection con) {
|
||||
var req = Request("analogs", "read");
|
||||
return con.request(req).then((res) {
|
||||
|
||||
// build states list
|
||||
List<AnalogState> states = [];
|
||||
for (List state in res.getData()) {
|
||||
states.add(AnalogState._fromRead(
|
||||
state[0],
|
||||
state[1],
|
||||
state[2],
|
||||
));
|
||||
}
|
||||
|
||||
// return it
|
||||
return states;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> analogsWrite(Connection con, List<AnalogState> states) {
|
||||
var req = Request("analogs", "write");
|
||||
|
||||
// add params
|
||||
for (var state in states) {
|
||||
var obj = [
|
||||
state.name,
|
||||
state.state
|
||||
];
|
||||
req.addParam(obj);
|
||||
}
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> analogsWriteReset(Connection con, List<String> names) {
|
||||
var req = Request("analogs", "write_reset");
|
||||
|
||||
// add params
|
||||
for (var name in names)
|
||||
req.addParam(name);
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
class AnalogState {
|
||||
String name;
|
||||
double state;
|
||||
bool active;
|
||||
|
||||
AnalogState(this.name, this.state);
|
||||
AnalogState._fromRead(this.name, this.state, this.active);
|
||||
}
|
||||
|
||||
Future<List<AnalogState>> analogsRead(Connection con) {
|
||||
var req = Request("analogs", "read");
|
||||
return con.request(req).then((res) {
|
||||
|
||||
// build states list
|
||||
List<AnalogState> states = [];
|
||||
for (List state in res.getData()) {
|
||||
states.add(AnalogState._fromRead(
|
||||
state[0],
|
||||
state[1],
|
||||
state[2],
|
||||
));
|
||||
}
|
||||
|
||||
// return it
|
||||
return states;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> analogsWrite(Connection con, List<AnalogState> states) {
|
||||
var req = Request("analogs", "write");
|
||||
|
||||
// add params
|
||||
for (var state in states) {
|
||||
var obj = [
|
||||
state.name,
|
||||
state.state
|
||||
];
|
||||
req.addParam(obj);
|
||||
}
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> analogsWriteReset(Connection con, List<String> names) {
|
||||
var req = Request("analogs", "write_reset");
|
||||
|
||||
// add params
|
||||
for (var name in names)
|
||||
req.addParam(name);
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
@@ -1,54 +1,54 @@
|
||||
part of spiceapi;
|
||||
|
||||
class ButtonState {
|
||||
String name;
|
||||
double state;
|
||||
bool active;
|
||||
|
||||
ButtonState(this.name, this.state);
|
||||
ButtonState._fromRead(this.name, this.state, this.active);
|
||||
}
|
||||
|
||||
Future<List<ButtonState>> buttonsRead(Connection con) {
|
||||
var req = Request("buttons", "read");
|
||||
return con.request(req).then((res) {
|
||||
|
||||
// build states list
|
||||
List<ButtonState> states = [];
|
||||
for (List state in res.getData()) {
|
||||
states.add(ButtonState._fromRead(
|
||||
state[0],
|
||||
state[1],
|
||||
state[2],
|
||||
));
|
||||
}
|
||||
|
||||
// return it
|
||||
return states;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> buttonsWrite(Connection con, List<ButtonState> states) {
|
||||
var req = Request("buttons", "write");
|
||||
|
||||
// add params
|
||||
for (var state in states) {
|
||||
var obj = [
|
||||
state.name,
|
||||
state.state
|
||||
];
|
||||
req.addParam(obj);
|
||||
}
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> buttonsWriteReset(Connection con, List<String> names) {
|
||||
var req = Request("buttons", "write_reset");
|
||||
|
||||
// add params
|
||||
for (var name in names)
|
||||
req.addParam(name);
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
class ButtonState {
|
||||
String name;
|
||||
double state;
|
||||
bool active;
|
||||
|
||||
ButtonState(this.name, this.state);
|
||||
ButtonState._fromRead(this.name, this.state, this.active);
|
||||
}
|
||||
|
||||
Future<List<ButtonState>> buttonsRead(Connection con) {
|
||||
var req = Request("buttons", "read");
|
||||
return con.request(req).then((res) {
|
||||
|
||||
// build states list
|
||||
List<ButtonState> states = [];
|
||||
for (List state in res.getData()) {
|
||||
states.add(ButtonState._fromRead(
|
||||
state[0],
|
||||
state[1],
|
||||
state[2],
|
||||
));
|
||||
}
|
||||
|
||||
// return it
|
||||
return states;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> buttonsWrite(Connection con, List<ButtonState> states) {
|
||||
var req = Request("buttons", "write");
|
||||
|
||||
// add params
|
||||
for (var state in states) {
|
||||
var obj = [
|
||||
state.name,
|
||||
state.state
|
||||
];
|
||||
req.addParam(obj);
|
||||
}
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> buttonsWriteReset(Connection con, List<String> names) {
|
||||
var req = Request("buttons", "write_reset");
|
||||
|
||||
// add params
|
||||
for (var name in names)
|
||||
req.addParam(name);
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
part of spiceapi;
|
||||
|
||||
Future<void> cardInsert(Connection con, int unit, String cardID) {
|
||||
var req = Request("card", "insert");
|
||||
req.addParam(unit);
|
||||
req.addParam(cardID);
|
||||
return con.request(req);
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
Future<void> cardInsert(Connection con, int unit, String cardID) {
|
||||
var req = Request("card", "insert");
|
||||
req.addParam(unit);
|
||||
req.addParam(cardID);
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
part of spiceapi;
|
||||
|
||||
Future<int> coinGet(Connection con) {
|
||||
var req = Request("coin", "get");
|
||||
return con.request(req).then((res) {
|
||||
return res.getData()[0];
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> coinSet(Connection con, int amount) {
|
||||
var req = Request("coin", "set");
|
||||
req.addParam(amount);
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> coinInsert(Connection con, [int amount=1]) {
|
||||
var req = Request("coin", "insert");
|
||||
if (amount != 1)
|
||||
req.addParam(amount);
|
||||
return con.request(req);
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
Future<int> coinGet(Connection con) {
|
||||
var req = Request("coin", "get");
|
||||
return con.request(req).then((res) {
|
||||
return res.getData()[0];
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> coinSet(Connection con, int amount) {
|
||||
var req = Request("coin", "set");
|
||||
req.addParam(amount);
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> coinInsert(Connection con, [int amount=1]) {
|
||||
var req = Request("coin", "insert");
|
||||
if (amount != 1)
|
||||
req.addParam(amount);
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
@@ -1,36 +1,36 @@
|
||||
part of spiceapi;
|
||||
|
||||
Future<void> controlRaise(Connection con, String signal) {
|
||||
var req = Request("control", "raise");
|
||||
req.addParam(signal);
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> controlExit(Connection con, int code) {
|
||||
var req = Request("control", "exit");
|
||||
req.addParam(code);
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> controlRestart(Connection con) {
|
||||
var req = Request("control", "restart");
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> controlRefreshSession(Connection con) {
|
||||
var rnd = new Random();
|
||||
var req = Request("control", "session_refresh", id: rnd.nextInt(pow(2, 32)));
|
||||
return con.request(req).then((res) {
|
||||
con.changePass(res.getData()[0]);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> controlShutdown(Connection con) {
|
||||
var req = Request("control", "shutdown");
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> controlReboot(Connection con) {
|
||||
var req = Request("control", "reboot");
|
||||
return con.request(req);
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
Future<void> controlRaise(Connection con, String signal) {
|
||||
var req = Request("control", "raise");
|
||||
req.addParam(signal);
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> controlExit(Connection con, int code) {
|
||||
var req = Request("control", "exit");
|
||||
req.addParam(code);
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> controlRestart(Connection con) {
|
||||
var req = Request("control", "restart");
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> controlRefreshSession(Connection con) {
|
||||
var rnd = new Random();
|
||||
var req = Request("control", "session_refresh", id: rnd.nextInt(pow(2, 32)));
|
||||
return con.request(req).then((res) {
|
||||
con.changePass(res.getData()[0]);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> controlShutdown(Connection con) {
|
||||
var req = Request("control", "shutdown");
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> controlReboot(Connection con) {
|
||||
var req = Request("control", "reboot");
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
part of spiceapi;
|
||||
|
||||
Future<String> iidxTickerGet(Connection con) {
|
||||
var req = Request("iidx", "ticker_get");
|
||||
return con.request(req).then((res) {
|
||||
return res.getData()[0];
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> iidxTickerSet(Connection con, String text) {
|
||||
var req = Request("iidx", "ticker_set");
|
||||
req.addParam(text);
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> iidxTickerReset(Connection con) {
|
||||
var req = Request("iidx", "ticker_reset");
|
||||
return con.request(req);
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
Future<String> iidxTickerGet(Connection con) {
|
||||
var req = Request("iidx", "ticker_get");
|
||||
return con.request(req).then((res) {
|
||||
return res.getData()[0];
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> iidxTickerSet(Connection con, String text) {
|
||||
var req = Request("iidx", "ticker_set");
|
||||
req.addParam(text);
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> iidxTickerReset(Connection con) {
|
||||
var req = Request("iidx", "ticker_reset");
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
part of spiceapi;
|
||||
|
||||
Future<Map> infoAVS(Connection con) {
|
||||
var req = Request("info", "avs");
|
||||
return con.request(req).then((res) {
|
||||
return res.getData()[0];
|
||||
});
|
||||
}
|
||||
|
||||
Future<Map> infoLauncher(Connection con) {
|
||||
var req = Request("info", "launcher");
|
||||
return con.request(req).then((res) {
|
||||
return res.getData()[0];
|
||||
});
|
||||
}
|
||||
|
||||
Future<Map> infoMemory(Connection con) {
|
||||
var req = Request("info", "memory");
|
||||
return con.request(req).then((res) {
|
||||
return res.getData()[0];
|
||||
});
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
Future<Map> infoAVS(Connection con) {
|
||||
var req = Request("info", "avs");
|
||||
return con.request(req).then((res) {
|
||||
return res.getData()[0];
|
||||
});
|
||||
}
|
||||
|
||||
Future<Map> infoLauncher(Connection con) {
|
||||
var req = Request("info", "launcher");
|
||||
return con.request(req).then((res) {
|
||||
return res.getData()[0];
|
||||
});
|
||||
}
|
||||
|
||||
Future<Map> infoMemory(Connection con) {
|
||||
var req = Request("info", "memory");
|
||||
return con.request(req).then((res) {
|
||||
return res.getData()[0];
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
part of spiceapi;
|
||||
|
||||
Future<void> keypadsWrite(Connection con, int unit, String input) {
|
||||
var req = Request("keypads", "write");
|
||||
req.addParam(unit);
|
||||
req.addParam(input);
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> keypadsSet(Connection con, int unit, String buttons) {
|
||||
var req = Request("keypads", "set");
|
||||
req.addParam(unit);
|
||||
for (int i = 0; i < buttons.length; i++)
|
||||
req.addParam(buttons[i]);
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<String> keypadsGet(Connection con, int unit) {
|
||||
var req = Request("keypads", "get");
|
||||
req.addParam(unit);
|
||||
return con.request(req).then((res) {
|
||||
String buttons = "";
|
||||
for (var obj in res.getData()) {
|
||||
buttons += obj;
|
||||
}
|
||||
return buttons;
|
||||
});
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
Future<void> keypadsWrite(Connection con, int unit, String input) {
|
||||
var req = Request("keypads", "write");
|
||||
req.addParam(unit);
|
||||
req.addParam(input);
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> keypadsSet(Connection con, int unit, String buttons) {
|
||||
var req = Request("keypads", "set");
|
||||
req.addParam(unit);
|
||||
for (int i = 0; i < buttons.length; i++)
|
||||
req.addParam(buttons[i]);
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<String> keypadsGet(Connection con, int unit) {
|
||||
var req = Request("keypads", "get");
|
||||
req.addParam(unit);
|
||||
return con.request(req).then((res) {
|
||||
String buttons = "";
|
||||
for (var obj in res.getData()) {
|
||||
buttons += obj;
|
||||
}
|
||||
return buttons;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,54 +1,54 @@
|
||||
part of spiceapi;
|
||||
|
||||
class LightState {
|
||||
String name;
|
||||
double state;
|
||||
bool active;
|
||||
|
||||
LightState(this.name, this.state);
|
||||
LightState._fromRead(this.name, this.state, this.active);
|
||||
}
|
||||
|
||||
Future<List<LightState>> lightsRead(Connection con) {
|
||||
var req = Request("lights", "read");
|
||||
return con.request(req).then((res) {
|
||||
|
||||
// build states list
|
||||
List<LightState> states = [];
|
||||
for (List state in res.getData()) {
|
||||
states.add(LightState._fromRead(
|
||||
state[0],
|
||||
state[1],
|
||||
state[2],
|
||||
));
|
||||
}
|
||||
|
||||
// return it
|
||||
return states;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> lightsWrite(Connection con, List<LightState> states) {
|
||||
var req = Request("lights", "write");
|
||||
|
||||
// add params
|
||||
for (var state in states) {
|
||||
var obj = [
|
||||
state.name,
|
||||
state.state
|
||||
];
|
||||
req.addParam(obj);
|
||||
}
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> lightsWriteReset(Connection con, List<String> names) {
|
||||
var req = Request("lights", "write_reset");
|
||||
|
||||
// add params
|
||||
for (var name in names)
|
||||
req.addParam(name);
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
class LightState {
|
||||
String name;
|
||||
double state;
|
||||
bool active;
|
||||
|
||||
LightState(this.name, this.state);
|
||||
LightState._fromRead(this.name, this.state, this.active);
|
||||
}
|
||||
|
||||
Future<List<LightState>> lightsRead(Connection con) {
|
||||
var req = Request("lights", "read");
|
||||
return con.request(req).then((res) {
|
||||
|
||||
// build states list
|
||||
List<LightState> states = [];
|
||||
for (List state in res.getData()) {
|
||||
states.add(LightState._fromRead(
|
||||
state[0],
|
||||
state[1],
|
||||
state[2],
|
||||
));
|
||||
}
|
||||
|
||||
// return it
|
||||
return states;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> lightsWrite(Connection con, List<LightState> states) {
|
||||
var req = Request("lights", "write");
|
||||
|
||||
// add params
|
||||
for (var state in states) {
|
||||
var obj = [
|
||||
state.name,
|
||||
state.state
|
||||
];
|
||||
req.addParam(obj);
|
||||
}
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> lightsWriteReset(Connection con, List<String> names) {
|
||||
var req = Request("lights", "write_reset");
|
||||
|
||||
// add params
|
||||
for (var name in names)
|
||||
req.addParam(name);
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
part of spiceapi;
|
||||
|
||||
Future<void> memoryWrite(Connection con,
|
||||
String dllName, String data, int offset) {
|
||||
var req = Request("memory", "write");
|
||||
req.addParam(dllName);
|
||||
req.addParam(data);
|
||||
req.addParam(offset);
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<String> memoryRead(Connection con,
|
||||
String dllName, int offset, int size) {
|
||||
var req = Request("memory", "read");
|
||||
req.addParam(dllName);
|
||||
req.addParam(offset);
|
||||
req.addParam(size);
|
||||
return con.request(req).then((res) {
|
||||
return res.getData()[0];
|
||||
});
|
||||
}
|
||||
|
||||
Future<int> memorySignature(Connection con,
|
||||
String dllName, String signature, String replacement,
|
||||
int offset, int usage) {
|
||||
var req = Request("memory", "signature");
|
||||
req.addParam(dllName);
|
||||
req.addParam(signature);
|
||||
req.addParam(replacement);
|
||||
req.addParam(offset);
|
||||
req.addParam(usage);
|
||||
return con.request(req).then((res) {
|
||||
return res.getData()[0];
|
||||
});
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
Future<void> memoryWrite(Connection con,
|
||||
String dllName, String data, int offset) {
|
||||
var req = Request("memory", "write");
|
||||
req.addParam(dllName);
|
||||
req.addParam(data);
|
||||
req.addParam(offset);
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<String> memoryRead(Connection con,
|
||||
String dllName, int offset, int size) {
|
||||
var req = Request("memory", "read");
|
||||
req.addParam(dllName);
|
||||
req.addParam(offset);
|
||||
req.addParam(size);
|
||||
return con.request(req).then((res) {
|
||||
return res.getData()[0];
|
||||
});
|
||||
}
|
||||
|
||||
Future<int> memorySignature(Connection con,
|
||||
String dllName, String signature, String replacement,
|
||||
int offset, int usage) {
|
||||
var req = Request("memory", "signature");
|
||||
req.addParam(dllName);
|
||||
req.addParam(signature);
|
||||
req.addParam(replacement);
|
||||
req.addParam(offset);
|
||||
req.addParam(usage);
|
||||
return con.request(req).then((res) {
|
||||
return res.getData()[0];
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,53 +1,53 @@
|
||||
part of spiceapi;
|
||||
|
||||
class TouchState {
|
||||
int id;
|
||||
int x, y;
|
||||
|
||||
TouchState(this.id, this.x, this.y);
|
||||
}
|
||||
|
||||
Future<List<TouchState>> touchRead(Connection con) {
|
||||
var req = Request("touch", "read");
|
||||
return con.request(req).then((res) {
|
||||
|
||||
// build states list
|
||||
List<TouchState> states = [];
|
||||
for (List state in res.getData()) {
|
||||
states.add(TouchState(
|
||||
state[0],
|
||||
state[1],
|
||||
state[2],
|
||||
));
|
||||
}
|
||||
|
||||
// return it
|
||||
return states;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> touchWrite(Connection con, List<TouchState> states) {
|
||||
var req = Request("touch", "write");
|
||||
|
||||
// add params
|
||||
for (var state in states) {
|
||||
var obj = [
|
||||
state.id,
|
||||
state.x,
|
||||
state.y
|
||||
];
|
||||
req.addParam(obj);
|
||||
}
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> touchWriteReset(Connection con, List<int> touchIDs) {
|
||||
var req = Request("touch", "write_reset");
|
||||
|
||||
// add params
|
||||
for (var id in touchIDs)
|
||||
req.addParam(id);
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
class TouchState {
|
||||
int id;
|
||||
int x, y;
|
||||
|
||||
TouchState(this.id, this.x, this.y);
|
||||
}
|
||||
|
||||
Future<List<TouchState>> touchRead(Connection con) {
|
||||
var req = Request("touch", "read");
|
||||
return con.request(req).then((res) {
|
||||
|
||||
// build states list
|
||||
List<TouchState> states = [];
|
||||
for (List state in res.getData()) {
|
||||
states.add(TouchState(
|
||||
state[0],
|
||||
state[1],
|
||||
state[2],
|
||||
));
|
||||
}
|
||||
|
||||
// return it
|
||||
return states;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> touchWrite(Connection con, List<TouchState> states) {
|
||||
var req = Request("touch", "write");
|
||||
|
||||
// add params
|
||||
for (var state in states) {
|
||||
var obj = [
|
||||
state.id,
|
||||
state.x,
|
||||
state.y
|
||||
];
|
||||
req.addParam(obj);
|
||||
}
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> touchWriteReset(Connection con, List<int> touchIDs) {
|
||||
var req = Request("touch", "write_reset");
|
||||
|
||||
// add params
|
||||
for (var id in touchIDs)
|
||||
req.addParam(id);
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
For more information, please refer to <http://unlicense.org/>
|
||||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
For more information, please refer to <http://unlicense.org/>
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
library spiceapi;
|
||||
import 'dart:io';
|
||||
import 'dart:convert';
|
||||
import 'dart:async';
|
||||
import 'dart:math';
|
||||
import 'dart:typed_data';
|
||||
part "src/connection.dart";
|
||||
part "src/request.dart";
|
||||
part "src/response.dart";
|
||||
part "src/exceptions.dart";
|
||||
part "src/rc4.dart";
|
||||
part "src/wrappers/analogs.dart";
|
||||
part "src/wrappers/buttons.dart";
|
||||
part "src/wrappers/capture.dart";
|
||||
part "src/wrappers/card.dart";
|
||||
part "src/wrappers/coin.dart";
|
||||
part "src/wrappers/control.dart";
|
||||
part "src/wrappers/info.dart";
|
||||
part "src/wrappers/keypads.dart";
|
||||
part "src/wrappers/lights.dart";
|
||||
part "src/wrappers/memory.dart";
|
||||
part "src/wrappers/iidx.dart";
|
||||
part "src/wrappers/touch.dart";
|
||||
library spiceapi;
|
||||
import 'dart:io';
|
||||
import 'dart:convert';
|
||||
import 'dart:async';
|
||||
import 'dart:math';
|
||||
import 'dart:typed_data';
|
||||
part "src/connection.dart";
|
||||
part "src/request.dart";
|
||||
part "src/response.dart";
|
||||
part "src/exceptions.dart";
|
||||
part "src/rc4.dart";
|
||||
part "src/wrappers/analogs.dart";
|
||||
part "src/wrappers/buttons.dart";
|
||||
part "src/wrappers/capture.dart";
|
||||
part "src/wrappers/card.dart";
|
||||
part "src/wrappers/coin.dart";
|
||||
part "src/wrappers/control.dart";
|
||||
part "src/wrappers/info.dart";
|
||||
part "src/wrappers/keypads.dart";
|
||||
part "src/wrappers/lights.dart";
|
||||
part "src/wrappers/memory.dart";
|
||||
part "src/wrappers/iidx.dart";
|
||||
part "src/wrappers/touch.dart";
|
||||
|
||||
@@ -1,192 +1,192 @@
|
||||
part of spiceapi;
|
||||
|
||||
|
||||
class Connection {
|
||||
|
||||
// settings
|
||||
static const _TIMEOUT = Duration(seconds: 3);
|
||||
static const _BUFFER_SIZE = 1024 * 1024 * 8;
|
||||
|
||||
// state
|
||||
final String host, pass;
|
||||
final int port;
|
||||
var resource;
|
||||
List<int> _dataBuffer;
|
||||
StreamController<Response> _responses;
|
||||
StreamController<Connection> _connections;
|
||||
Socket _socket;
|
||||
RC4 _cipher;
|
||||
bool _disposed = false;
|
||||
|
||||
Connection(this.host, this.port, this.pass,
|
||||
{this.resource, bool refreshSession=true}) {
|
||||
|
||||
// initialize
|
||||
_dataBuffer = List<int>();
|
||||
_responses = StreamController<Response>.broadcast();
|
||||
_connections = StreamController<Connection>.broadcast();
|
||||
if (pass.length > 0)
|
||||
_cipher = RC4(utf8.encode(pass));
|
||||
|
||||
// connect
|
||||
Socket.connect(host, port, timeout: _TIMEOUT).then((socket) async {
|
||||
|
||||
// remember socket
|
||||
this._socket = socket;
|
||||
|
||||
// listen to data
|
||||
socket.listen((data) {
|
||||
|
||||
// cipher
|
||||
if (_cipher != null)
|
||||
_cipher.crypt(data);
|
||||
|
||||
// add data to buffer
|
||||
_dataBuffer.addAll(data);
|
||||
|
||||
// check buffer size
|
||||
if (_dataBuffer.length > _BUFFER_SIZE) {
|
||||
socket.destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
// check for completed message
|
||||
for (int i = 0; i < _dataBuffer.length; i++) {
|
||||
if (_dataBuffer[i] == 0) {
|
||||
|
||||
// get message data and remove from buffer
|
||||
var msgData = List<int>.from(_dataBuffer.getRange(0, i));
|
||||
_dataBuffer.removeRange(0, i + 1);
|
||||
|
||||
// check data length
|
||||
if (msgData.length > 0) {
|
||||
|
||||
// convert to JSON
|
||||
var msgStr = utf8.decode(msgData, allowMalformed: false);
|
||||
|
||||
// build response
|
||||
var res = Response.fromJson(msgStr);
|
||||
this._responses.add(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}, onError: (e) {
|
||||
|
||||
// dispose on listen error
|
||||
this.dispose();
|
||||
|
||||
}, onDone: () {
|
||||
|
||||
// dispose on listen done
|
||||
this.dispose();
|
||||
|
||||
});
|
||||
|
||||
// refresh session
|
||||
bool error = false;
|
||||
if (refreshSession) {
|
||||
try {
|
||||
await controlRefreshSession(this);
|
||||
} on Error {
|
||||
error = true;
|
||||
} on TimeoutException {
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
|
||||
// mark as connected
|
||||
if (!this._connections.isClosed)
|
||||
this._connections.add(this);
|
||||
if (error)
|
||||
this.dispose();
|
||||
|
||||
}, onError: (e) {
|
||||
|
||||
// dispose on connection error
|
||||
this.dispose();
|
||||
});
|
||||
}
|
||||
|
||||
void changePass(String pass) {
|
||||
if (pass.length > 0)
|
||||
_cipher = RC4(utf8.encode(pass));
|
||||
else
|
||||
_cipher = null;
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
if (_socket != null)
|
||||
_socket.destroy();
|
||||
if (_responses != null)
|
||||
_responses.close();
|
||||
if (_connections != null)
|
||||
_connections.close();
|
||||
this._disposed = true;
|
||||
this.free();
|
||||
}
|
||||
|
||||
bool isDisposed() {
|
||||
return this._disposed;
|
||||
}
|
||||
|
||||
void free() {
|
||||
|
||||
// release optional resource
|
||||
if (this.resource != null) {
|
||||
this.resource.release();
|
||||
this.resource = null;
|
||||
}
|
||||
}
|
||||
|
||||
bool isFree() {
|
||||
return this.resource == null;
|
||||
}
|
||||
|
||||
Future<Connection> onConnect() {
|
||||
return _connections.stream.first;
|
||||
}
|
||||
|
||||
bool isValid() {
|
||||
return this._socket != null && !this._disposed;
|
||||
}
|
||||
|
||||
Future<Response> request(Request req) {
|
||||
|
||||
// add response listener
|
||||
var res = _awaitResponse(req._id);
|
||||
|
||||
// write request
|
||||
_writeRequest(req);
|
||||
|
||||
// return future response
|
||||
return res.then((res) {
|
||||
|
||||
// validate first
|
||||
res.validate();
|
||||
|
||||
// return it
|
||||
return res;
|
||||
});
|
||||
}
|
||||
|
||||
void _writeRequest(Request req) async {
|
||||
|
||||
// convert to JSON
|
||||
var json = req.toJson() + "\x00";
|
||||
var jsonEncoded = utf8.encode(json);
|
||||
|
||||
// cipher
|
||||
if (_cipher != null)
|
||||
_cipher.crypt(jsonEncoded);
|
||||
|
||||
// write to socket
|
||||
this._socket.add(jsonEncoded);
|
||||
}
|
||||
|
||||
Future<Response> _awaitResponse(int id) {
|
||||
return _responses.stream.timeout(_TIMEOUT).firstWhere(
|
||||
(res) => res._id == id, orElse: null);
|
||||
}
|
||||
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
|
||||
class Connection {
|
||||
|
||||
// settings
|
||||
static const _TIMEOUT = Duration(seconds: 3);
|
||||
static const _BUFFER_SIZE = 1024 * 1024 * 8;
|
||||
|
||||
// state
|
||||
final String host, pass;
|
||||
final int port;
|
||||
var resource;
|
||||
List<int> _dataBuffer;
|
||||
StreamController<Response> _responses;
|
||||
StreamController<Connection> _connections;
|
||||
Socket _socket;
|
||||
RC4 _cipher;
|
||||
bool _disposed = false;
|
||||
|
||||
Connection(this.host, this.port, this.pass,
|
||||
{this.resource, bool refreshSession=true}) {
|
||||
|
||||
// initialize
|
||||
_dataBuffer = List<int>();
|
||||
_responses = StreamController<Response>.broadcast();
|
||||
_connections = StreamController<Connection>.broadcast();
|
||||
if (pass.length > 0)
|
||||
_cipher = RC4(utf8.encode(pass));
|
||||
|
||||
// connect
|
||||
Socket.connect(host, port, timeout: _TIMEOUT).then((socket) async {
|
||||
|
||||
// remember socket
|
||||
this._socket = socket;
|
||||
|
||||
// listen to data
|
||||
socket.listen((data) {
|
||||
|
||||
// cipher
|
||||
if (_cipher != null)
|
||||
_cipher.crypt(data);
|
||||
|
||||
// add data to buffer
|
||||
_dataBuffer.addAll(data);
|
||||
|
||||
// check buffer size
|
||||
if (_dataBuffer.length > _BUFFER_SIZE) {
|
||||
socket.destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
// check for completed message
|
||||
for (int i = 0; i < _dataBuffer.length; i++) {
|
||||
if (_dataBuffer[i] == 0) {
|
||||
|
||||
// get message data and remove from buffer
|
||||
var msgData = List<int>.from(_dataBuffer.getRange(0, i));
|
||||
_dataBuffer.removeRange(0, i + 1);
|
||||
|
||||
// check data length
|
||||
if (msgData.length > 0) {
|
||||
|
||||
// convert to JSON
|
||||
var msgStr = utf8.decode(msgData, allowMalformed: false);
|
||||
|
||||
// build response
|
||||
var res = Response.fromJson(msgStr);
|
||||
this._responses.add(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}, onError: (e) {
|
||||
|
||||
// dispose on listen error
|
||||
this.dispose();
|
||||
|
||||
}, onDone: () {
|
||||
|
||||
// dispose on listen done
|
||||
this.dispose();
|
||||
|
||||
});
|
||||
|
||||
// refresh session
|
||||
bool error = false;
|
||||
if (refreshSession) {
|
||||
try {
|
||||
await controlRefreshSession(this);
|
||||
} on Error {
|
||||
error = true;
|
||||
} on TimeoutException {
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
|
||||
// mark as connected
|
||||
if (!this._connections.isClosed)
|
||||
this._connections.add(this);
|
||||
if (error)
|
||||
this.dispose();
|
||||
|
||||
}, onError: (e) {
|
||||
|
||||
// dispose on connection error
|
||||
this.dispose();
|
||||
});
|
||||
}
|
||||
|
||||
void changePass(String pass) {
|
||||
if (pass.length > 0)
|
||||
_cipher = RC4(utf8.encode(pass));
|
||||
else
|
||||
_cipher = null;
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
if (_socket != null)
|
||||
_socket.destroy();
|
||||
if (_responses != null)
|
||||
_responses.close();
|
||||
if (_connections != null)
|
||||
_connections.close();
|
||||
this._disposed = true;
|
||||
this.free();
|
||||
}
|
||||
|
||||
bool isDisposed() {
|
||||
return this._disposed;
|
||||
}
|
||||
|
||||
void free() {
|
||||
|
||||
// release optional resource
|
||||
if (this.resource != null) {
|
||||
this.resource.release();
|
||||
this.resource = null;
|
||||
}
|
||||
}
|
||||
|
||||
bool isFree() {
|
||||
return this.resource == null;
|
||||
}
|
||||
|
||||
Future<Connection> onConnect() {
|
||||
return _connections.stream.first;
|
||||
}
|
||||
|
||||
bool isValid() {
|
||||
return this._socket != null && !this._disposed;
|
||||
}
|
||||
|
||||
Future<Response> request(Request req) {
|
||||
|
||||
// add response listener
|
||||
var res = _awaitResponse(req._id);
|
||||
|
||||
// write request
|
||||
_writeRequest(req);
|
||||
|
||||
// return future response
|
||||
return res.then((res) {
|
||||
|
||||
// validate first
|
||||
res.validate();
|
||||
|
||||
// return it
|
||||
return res;
|
||||
});
|
||||
}
|
||||
|
||||
void _writeRequest(Request req) async {
|
||||
|
||||
// convert to JSON
|
||||
var json = req.toJson() + "\x00";
|
||||
var jsonEncoded = utf8.encode(json);
|
||||
|
||||
// cipher
|
||||
if (_cipher != null)
|
||||
_cipher.crypt(jsonEncoded);
|
||||
|
||||
// write to socket
|
||||
this._socket.add(jsonEncoded);
|
||||
}
|
||||
|
||||
Future<Response> _awaitResponse(int id) {
|
||||
return _responses.stream.timeout(_TIMEOUT).firstWhere(
|
||||
(res) => res._id == id, orElse: null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
part of spiceapi;
|
||||
|
||||
class APIError implements Exception {
|
||||
String cause;
|
||||
APIError(this.cause);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return this.cause;
|
||||
}
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
class APIError implements Exception {
|
||||
String cause;
|
||||
APIError(this.cause);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return this.cause;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,48 +1,48 @@
|
||||
part of spiceapi;
|
||||
|
||||
class RC4 {
|
||||
|
||||
// state
|
||||
int _a = 0;
|
||||
int _b = 0;
|
||||
List<int> _sBox = List<int>(256);
|
||||
|
||||
RC4(List<int> key) {
|
||||
|
||||
// init sBox
|
||||
for (int i = 0; i < 256; i++) {
|
||||
_sBox[i] = i;
|
||||
}
|
||||
|
||||
// process key
|
||||
int j = 0;
|
||||
for (int i = 0; i < 256; i++) {
|
||||
|
||||
// update
|
||||
j = (j + _sBox[i] + key[i % key.length]) % 256;
|
||||
|
||||
// swap
|
||||
var tmp = _sBox[i];
|
||||
_sBox[i] = _sBox[j];
|
||||
_sBox[j] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
void crypt(List<int> inData) {
|
||||
for (int i = 0; i < inData.length; i++) {
|
||||
|
||||
// update
|
||||
_a = (_a + 1) % 256;
|
||||
_b = (_b + _sBox[_a]) % 256;
|
||||
|
||||
// swap
|
||||
var tmp = _sBox[_a];
|
||||
_sBox[_a] = _sBox[_b];
|
||||
_sBox[_b] = tmp;
|
||||
|
||||
// crypt
|
||||
inData[i] ^= _sBox[(_sBox[_a] + _sBox[_b]) % 256];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
class RC4 {
|
||||
|
||||
// state
|
||||
int _a = 0;
|
||||
int _b = 0;
|
||||
List<int> _sBox = List<int>(256);
|
||||
|
||||
RC4(List<int> key) {
|
||||
|
||||
// init sBox
|
||||
for (int i = 0; i < 256; i++) {
|
||||
_sBox[i] = i;
|
||||
}
|
||||
|
||||
// process key
|
||||
int j = 0;
|
||||
for (int i = 0; i < 256; i++) {
|
||||
|
||||
// update
|
||||
j = (j + _sBox[i] + key[i % key.length]) % 256;
|
||||
|
||||
// swap
|
||||
var tmp = _sBox[i];
|
||||
_sBox[i] = _sBox[j];
|
||||
_sBox[j] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
void crypt(List<int> inData) {
|
||||
for (int i = 0; i < inData.length; i++) {
|
||||
|
||||
// update
|
||||
_a = (_a + 1) % 256;
|
||||
_b = (_b + _sBox[_a]) % 256;
|
||||
|
||||
// swap
|
||||
var tmp = _sBox[_a];
|
||||
_sBox[_a] = _sBox[_b];
|
||||
_sBox[_b] = tmp;
|
||||
|
||||
// crypt
|
||||
inData[i] ^= _sBox[(_sBox[_a] + _sBox[_b]) % 256];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,45 +1,45 @@
|
||||
part of spiceapi;
|
||||
|
||||
class Request {
|
||||
|
||||
static int _lastID = 0;
|
||||
|
||||
// contents
|
||||
int _id;
|
||||
String _module;
|
||||
String _function;
|
||||
List _params;
|
||||
|
||||
Request(String module, String function, {id}) {
|
||||
|
||||
// automatic ID iteration
|
||||
if (id == null) {
|
||||
if (++_lastID >= pow(2, 32))
|
||||
_lastID = 1;
|
||||
id = _lastID;
|
||||
} else
|
||||
_lastID = id;
|
||||
|
||||
// build contents
|
||||
this._id = id;
|
||||
this._module = module;
|
||||
this._function = function;
|
||||
this._params = List();
|
||||
}
|
||||
|
||||
String toJson() {
|
||||
return jsonEncode(
|
||||
{
|
||||
"id": this._id,
|
||||
"module": this._module,
|
||||
"function": this._function,
|
||||
"params": this._params,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void addParam(param) {
|
||||
this._params.add(param);
|
||||
}
|
||||
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
class Request {
|
||||
|
||||
static int _lastID = 0;
|
||||
|
||||
// contents
|
||||
int _id;
|
||||
String _module;
|
||||
String _function;
|
||||
List _params;
|
||||
|
||||
Request(String module, String function, {id}) {
|
||||
|
||||
// automatic ID iteration
|
||||
if (id == null) {
|
||||
if (++_lastID >= pow(2, 32))
|
||||
_lastID = 1;
|
||||
id = _lastID;
|
||||
} else
|
||||
_lastID = id;
|
||||
|
||||
// build contents
|
||||
this._id = id;
|
||||
this._module = module;
|
||||
this._function = function;
|
||||
this._params = List();
|
||||
}
|
||||
|
||||
String toJson() {
|
||||
return jsonEncode(
|
||||
{
|
||||
"id": this._id,
|
||||
"module": this._module,
|
||||
"function": this._function,
|
||||
"params": this._params,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void addParam(param) {
|
||||
this._params.add(param);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
part of spiceapi;
|
||||
|
||||
class Response {
|
||||
|
||||
String _json;
|
||||
int _id;
|
||||
List _errors;
|
||||
List _data;
|
||||
|
||||
Response.fromJson(String json) {
|
||||
this._json = json;
|
||||
var obj = jsonDecode(json);
|
||||
this._id = obj["id"];
|
||||
this._errors = obj["errors"];
|
||||
this._data = obj["data"];
|
||||
}
|
||||
|
||||
void validate() {
|
||||
|
||||
// check for errors
|
||||
if (_errors.length > 0) {
|
||||
// TODO: add all errors
|
||||
throw APIError(_errors[0].toString());
|
||||
}
|
||||
}
|
||||
|
||||
List getData() {
|
||||
return _data;
|
||||
}
|
||||
|
||||
String toJson() {
|
||||
return _json;
|
||||
}
|
||||
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
class Response {
|
||||
|
||||
String _json;
|
||||
int _id;
|
||||
List _errors;
|
||||
List _data;
|
||||
|
||||
Response.fromJson(String json) {
|
||||
this._json = json;
|
||||
var obj = jsonDecode(json);
|
||||
this._id = obj["id"];
|
||||
this._errors = obj["errors"];
|
||||
this._data = obj["data"];
|
||||
}
|
||||
|
||||
void validate() {
|
||||
|
||||
// check for errors
|
||||
if (_errors.length > 0) {
|
||||
// TODO: add all errors
|
||||
throw APIError(_errors[0].toString());
|
||||
}
|
||||
}
|
||||
|
||||
List getData() {
|
||||
return _data;
|
||||
}
|
||||
|
||||
String toJson() {
|
||||
return _json;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,54 +1,54 @@
|
||||
part of spiceapi;
|
||||
|
||||
class AnalogState {
|
||||
String name;
|
||||
double state;
|
||||
bool active;
|
||||
|
||||
AnalogState(this.name, this.state);
|
||||
AnalogState._fromRead(this.name, this.state, this.active);
|
||||
}
|
||||
|
||||
Future<List<AnalogState>> analogsRead(Connection con) {
|
||||
var req = Request("analogs", "read");
|
||||
return con.request(req).then((res) {
|
||||
|
||||
// build states list
|
||||
List<AnalogState> states = [];
|
||||
for (List state in res.getData()) {
|
||||
states.add(AnalogState._fromRead(
|
||||
state[0],
|
||||
state[1],
|
||||
state[2],
|
||||
));
|
||||
}
|
||||
|
||||
// return it
|
||||
return states;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> analogsWrite(Connection con, List<AnalogState> states) {
|
||||
var req = Request("analogs", "write");
|
||||
|
||||
// add params
|
||||
for (var state in states) {
|
||||
var obj = [
|
||||
state.name,
|
||||
state.state
|
||||
];
|
||||
req.addParam(obj);
|
||||
}
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> analogsWriteReset(Connection con, List<String> names) {
|
||||
var req = Request("analogs", "write_reset");
|
||||
|
||||
// add params
|
||||
for (var name in names)
|
||||
req.addParam(name);
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
class AnalogState {
|
||||
String name;
|
||||
double state;
|
||||
bool active;
|
||||
|
||||
AnalogState(this.name, this.state);
|
||||
AnalogState._fromRead(this.name, this.state, this.active);
|
||||
}
|
||||
|
||||
Future<List<AnalogState>> analogsRead(Connection con) {
|
||||
var req = Request("analogs", "read");
|
||||
return con.request(req).then((res) {
|
||||
|
||||
// build states list
|
||||
List<AnalogState> states = [];
|
||||
for (List state in res.getData()) {
|
||||
states.add(AnalogState._fromRead(
|
||||
state[0],
|
||||
state[1],
|
||||
state[2],
|
||||
));
|
||||
}
|
||||
|
||||
// return it
|
||||
return states;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> analogsWrite(Connection con, List<AnalogState> states) {
|
||||
var req = Request("analogs", "write");
|
||||
|
||||
// add params
|
||||
for (var state in states) {
|
||||
var obj = [
|
||||
state.name,
|
||||
state.state
|
||||
];
|
||||
req.addParam(obj);
|
||||
}
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> analogsWriteReset(Connection con, List<String> names) {
|
||||
var req = Request("analogs", "write_reset");
|
||||
|
||||
// add params
|
||||
for (var name in names)
|
||||
req.addParam(name);
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
@@ -1,54 +1,54 @@
|
||||
part of spiceapi;
|
||||
|
||||
class ButtonState {
|
||||
String name;
|
||||
double state;
|
||||
bool active;
|
||||
|
||||
ButtonState(this.name, this.state);
|
||||
ButtonState._fromRead(this.name, this.state, this.active);
|
||||
}
|
||||
|
||||
Future<List<ButtonState>> buttonsRead(Connection con) {
|
||||
var req = Request("buttons", "read");
|
||||
return con.request(req).then((res) {
|
||||
|
||||
// build states list
|
||||
List<ButtonState> states = [];
|
||||
for (List state in res.getData()) {
|
||||
states.add(ButtonState._fromRead(
|
||||
state[0],
|
||||
state[1],
|
||||
state[2],
|
||||
));
|
||||
}
|
||||
|
||||
// return it
|
||||
return states;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> buttonsWrite(Connection con, List<ButtonState> states) {
|
||||
var req = Request("buttons", "write");
|
||||
|
||||
// add params
|
||||
for (var state in states) {
|
||||
var obj = [
|
||||
state.name,
|
||||
state.state
|
||||
];
|
||||
req.addParam(obj);
|
||||
}
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> buttonsWriteReset(Connection con, List<String> names) {
|
||||
var req = Request("buttons", "write_reset");
|
||||
|
||||
// add params
|
||||
for (var name in names)
|
||||
req.addParam(name);
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
class ButtonState {
|
||||
String name;
|
||||
double state;
|
||||
bool active;
|
||||
|
||||
ButtonState(this.name, this.state);
|
||||
ButtonState._fromRead(this.name, this.state, this.active);
|
||||
}
|
||||
|
||||
Future<List<ButtonState>> buttonsRead(Connection con) {
|
||||
var req = Request("buttons", "read");
|
||||
return con.request(req).then((res) {
|
||||
|
||||
// build states list
|
||||
List<ButtonState> states = [];
|
||||
for (List state in res.getData()) {
|
||||
states.add(ButtonState._fromRead(
|
||||
state[0],
|
||||
state[1],
|
||||
state[2],
|
||||
));
|
||||
}
|
||||
|
||||
// return it
|
||||
return states;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> buttonsWrite(Connection con, List<ButtonState> states) {
|
||||
var req = Request("buttons", "write");
|
||||
|
||||
// add params
|
||||
for (var state in states) {
|
||||
var obj = [
|
||||
state.name,
|
||||
state.state
|
||||
];
|
||||
req.addParam(obj);
|
||||
}
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> buttonsWriteReset(Connection con, List<String> names) {
|
||||
var req = Request("buttons", "write_reset");
|
||||
|
||||
// add params
|
||||
for (var name in names)
|
||||
req.addParam(name);
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
@@ -1,38 +1,38 @@
|
||||
part of spiceapi;
|
||||
|
||||
class CaptureData {
|
||||
int timestamp;
|
||||
int width, height;
|
||||
Uint8List data;
|
||||
}
|
||||
|
||||
var _base64DecoderInstance = Base64Decoder();
|
||||
|
||||
Future<List> captureGetScreens(Connection con) {
|
||||
var req = Request("capture", "get_screens");
|
||||
return con.request(req).then((res) {
|
||||
return res.getData();
|
||||
});
|
||||
}
|
||||
|
||||
Future<CaptureData> captureGetJPG(Connection con, {
|
||||
int screen = 0,
|
||||
int quality = 60,
|
||||
int divide = 1,
|
||||
}) {
|
||||
var req = Request("capture", "get_jpg");
|
||||
req.addParam(screen);
|
||||
req.addParam(quality);
|
||||
req.addParam(divide);
|
||||
return con.request(req).then((res) {
|
||||
var captureData = CaptureData();
|
||||
var data = res.getData();
|
||||
if (data.length > 0) captureData.timestamp = data[0];
|
||||
if (data.length > 1) captureData.width = data[1];
|
||||
if (data.length > 2) captureData.height = data[2];
|
||||
if (data.length > 3) {
|
||||
captureData.data = _base64DecoderInstance.convert(data[3]);
|
||||
}
|
||||
return captureData;
|
||||
});
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
class CaptureData {
|
||||
int timestamp;
|
||||
int width, height;
|
||||
Uint8List data;
|
||||
}
|
||||
|
||||
var _base64DecoderInstance = Base64Decoder();
|
||||
|
||||
Future<List> captureGetScreens(Connection con) {
|
||||
var req = Request("capture", "get_screens");
|
||||
return con.request(req).then((res) {
|
||||
return res.getData();
|
||||
});
|
||||
}
|
||||
|
||||
Future<CaptureData> captureGetJPG(Connection con, {
|
||||
int screen = 0,
|
||||
int quality = 60,
|
||||
int divide = 1,
|
||||
}) {
|
||||
var req = Request("capture", "get_jpg");
|
||||
req.addParam(screen);
|
||||
req.addParam(quality);
|
||||
req.addParam(divide);
|
||||
return con.request(req).then((res) {
|
||||
var captureData = CaptureData();
|
||||
var data = res.getData();
|
||||
if (data.length > 0) captureData.timestamp = data[0];
|
||||
if (data.length > 1) captureData.width = data[1];
|
||||
if (data.length > 2) captureData.height = data[2];
|
||||
if (data.length > 3) {
|
||||
captureData.data = _base64DecoderInstance.convert(data[3]);
|
||||
}
|
||||
return captureData;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
part of spiceapi;
|
||||
|
||||
Future<void> cardInsert(Connection con, int unit, String cardID) {
|
||||
var req = Request("card", "insert");
|
||||
req.addParam(unit);
|
||||
req.addParam(cardID);
|
||||
return con.request(req);
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
Future<void> cardInsert(Connection con, int unit, String cardID) {
|
||||
var req = Request("card", "insert");
|
||||
req.addParam(unit);
|
||||
req.addParam(cardID);
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
part of spiceapi;
|
||||
|
||||
Future<int> coinGet(Connection con) {
|
||||
var req = Request("coin", "get");
|
||||
return con.request(req).then((res) {
|
||||
return res.getData()[0];
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> coinSet(Connection con, int amount) {
|
||||
var req = Request("coin", "set");
|
||||
req.addParam(amount);
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> coinInsert(Connection con, [int amount=1]) {
|
||||
var req = Request("coin", "insert");
|
||||
if (amount != 1)
|
||||
req.addParam(amount);
|
||||
return con.request(req);
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
Future<int> coinGet(Connection con) {
|
||||
var req = Request("coin", "get");
|
||||
return con.request(req).then((res) {
|
||||
return res.getData()[0];
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> coinSet(Connection con, int amount) {
|
||||
var req = Request("coin", "set");
|
||||
req.addParam(amount);
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> coinInsert(Connection con, [int amount=1]) {
|
||||
var req = Request("coin", "insert");
|
||||
if (amount != 1)
|
||||
req.addParam(amount);
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
@@ -1,36 +1,36 @@
|
||||
part of spiceapi;
|
||||
|
||||
Future<void> controlRaise(Connection con, String signal) {
|
||||
var req = Request("control", "raise");
|
||||
req.addParam(signal);
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> controlExit(Connection con, int code) {
|
||||
var req = Request("control", "exit");
|
||||
req.addParam(code);
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> controlRestart(Connection con) {
|
||||
var req = Request("control", "restart");
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> controlRefreshSession(Connection con) {
|
||||
var rnd = new Random();
|
||||
var req = Request("control", "session_refresh", id: rnd.nextInt(pow(2, 32)));
|
||||
return con.request(req).then((res) {
|
||||
con.changePass(res.getData()[0]);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> controlShutdown(Connection con) {
|
||||
var req = Request("control", "shutdown");
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> controlReboot(Connection con) {
|
||||
var req = Request("control", "reboot");
|
||||
return con.request(req);
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
Future<void> controlRaise(Connection con, String signal) {
|
||||
var req = Request("control", "raise");
|
||||
req.addParam(signal);
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> controlExit(Connection con, int code) {
|
||||
var req = Request("control", "exit");
|
||||
req.addParam(code);
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> controlRestart(Connection con) {
|
||||
var req = Request("control", "restart");
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> controlRefreshSession(Connection con) {
|
||||
var rnd = new Random();
|
||||
var req = Request("control", "session_refresh", id: rnd.nextInt(pow(2, 32)));
|
||||
return con.request(req).then((res) {
|
||||
con.changePass(res.getData()[0]);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> controlShutdown(Connection con) {
|
||||
var req = Request("control", "shutdown");
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> controlReboot(Connection con) {
|
||||
var req = Request("control", "reboot");
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
part of spiceapi;
|
||||
|
||||
Future<String> iidxTickerGet(Connection con) {
|
||||
var req = Request("iidx", "ticker_get");
|
||||
return con.request(req).then((res) {
|
||||
return res.getData()[0];
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> iidxTickerSet(Connection con, String text) {
|
||||
var req = Request("iidx", "ticker_set");
|
||||
req.addParam(text);
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> iidxTickerReset(Connection con) {
|
||||
var req = Request("iidx", "ticker_reset");
|
||||
return con.request(req);
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
Future<String> iidxTickerGet(Connection con) {
|
||||
var req = Request("iidx", "ticker_get");
|
||||
return con.request(req).then((res) {
|
||||
return res.getData()[0];
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> iidxTickerSet(Connection con, String text) {
|
||||
var req = Request("iidx", "ticker_set");
|
||||
req.addParam(text);
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> iidxTickerReset(Connection con) {
|
||||
var req = Request("iidx", "ticker_reset");
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
part of spiceapi;
|
||||
|
||||
Future<Map> infoAVS(Connection con) {
|
||||
var req = Request("info", "avs");
|
||||
return con.request(req).then((res) {
|
||||
return res.getData()[0];
|
||||
});
|
||||
}
|
||||
|
||||
Future<Map> infoLauncher(Connection con) {
|
||||
var req = Request("info", "launcher");
|
||||
return con.request(req).then((res) {
|
||||
return res.getData()[0];
|
||||
});
|
||||
}
|
||||
|
||||
Future<Map> infoMemory(Connection con) {
|
||||
var req = Request("info", "memory");
|
||||
return con.request(req).then((res) {
|
||||
return res.getData()[0];
|
||||
});
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
Future<Map> infoAVS(Connection con) {
|
||||
var req = Request("info", "avs");
|
||||
return con.request(req).then((res) {
|
||||
return res.getData()[0];
|
||||
});
|
||||
}
|
||||
|
||||
Future<Map> infoLauncher(Connection con) {
|
||||
var req = Request("info", "launcher");
|
||||
return con.request(req).then((res) {
|
||||
return res.getData()[0];
|
||||
});
|
||||
}
|
||||
|
||||
Future<Map> infoMemory(Connection con) {
|
||||
var req = Request("info", "memory");
|
||||
return con.request(req).then((res) {
|
||||
return res.getData()[0];
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
part of spiceapi;
|
||||
|
||||
Future<void> keypadsWrite(Connection con, int unit, String input) {
|
||||
var req = Request("keypads", "write");
|
||||
req.addParam(unit);
|
||||
req.addParam(input);
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> keypadsSet(Connection con, int unit, String buttons) {
|
||||
var req = Request("keypads", "set");
|
||||
req.addParam(unit);
|
||||
for (int i = 0; i < buttons.length; i++)
|
||||
req.addParam(buttons[i]);
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<String> keypadsGet(Connection con, int unit) {
|
||||
var req = Request("keypads", "get");
|
||||
req.addParam(unit);
|
||||
return con.request(req).then((res) {
|
||||
String buttons = "";
|
||||
for (var obj in res.getData()) {
|
||||
buttons += obj;
|
||||
}
|
||||
return buttons;
|
||||
});
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
Future<void> keypadsWrite(Connection con, int unit, String input) {
|
||||
var req = Request("keypads", "write");
|
||||
req.addParam(unit);
|
||||
req.addParam(input);
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> keypadsSet(Connection con, int unit, String buttons) {
|
||||
var req = Request("keypads", "set");
|
||||
req.addParam(unit);
|
||||
for (int i = 0; i < buttons.length; i++)
|
||||
req.addParam(buttons[i]);
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<String> keypadsGet(Connection con, int unit) {
|
||||
var req = Request("keypads", "get");
|
||||
req.addParam(unit);
|
||||
return con.request(req).then((res) {
|
||||
String buttons = "";
|
||||
for (var obj in res.getData()) {
|
||||
buttons += obj;
|
||||
}
|
||||
return buttons;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,54 +1,54 @@
|
||||
part of spiceapi;
|
||||
|
||||
class LightState {
|
||||
String name;
|
||||
double state;
|
||||
bool active;
|
||||
|
||||
LightState(this.name, this.state);
|
||||
LightState._fromRead(this.name, this.state, this.active);
|
||||
}
|
||||
|
||||
Future<List<LightState>> lightsRead(Connection con) {
|
||||
var req = Request("lights", "read");
|
||||
return con.request(req).then((res) {
|
||||
|
||||
// build states list
|
||||
List<LightState> states = [];
|
||||
for (List state in res.getData()) {
|
||||
states.add(LightState._fromRead(
|
||||
state[0],
|
||||
state[1],
|
||||
state[2],
|
||||
));
|
||||
}
|
||||
|
||||
// return it
|
||||
return states;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> lightsWrite(Connection con, List<LightState> states) {
|
||||
var req = Request("lights", "write");
|
||||
|
||||
// add params
|
||||
for (var state in states) {
|
||||
var obj = [
|
||||
state.name,
|
||||
state.state
|
||||
];
|
||||
req.addParam(obj);
|
||||
}
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> lightsWriteReset(Connection con, List<String> names) {
|
||||
var req = Request("lights", "write_reset");
|
||||
|
||||
// add params
|
||||
for (var name in names)
|
||||
req.addParam(name);
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
class LightState {
|
||||
String name;
|
||||
double state;
|
||||
bool active;
|
||||
|
||||
LightState(this.name, this.state);
|
||||
LightState._fromRead(this.name, this.state, this.active);
|
||||
}
|
||||
|
||||
Future<List<LightState>> lightsRead(Connection con) {
|
||||
var req = Request("lights", "read");
|
||||
return con.request(req).then((res) {
|
||||
|
||||
// build states list
|
||||
List<LightState> states = [];
|
||||
for (List state in res.getData()) {
|
||||
states.add(LightState._fromRead(
|
||||
state[0],
|
||||
state[1],
|
||||
state[2],
|
||||
));
|
||||
}
|
||||
|
||||
// return it
|
||||
return states;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> lightsWrite(Connection con, List<LightState> states) {
|
||||
var req = Request("lights", "write");
|
||||
|
||||
// add params
|
||||
for (var state in states) {
|
||||
var obj = [
|
||||
state.name,
|
||||
state.state
|
||||
];
|
||||
req.addParam(obj);
|
||||
}
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> lightsWriteReset(Connection con, List<String> names) {
|
||||
var req = Request("lights", "write_reset");
|
||||
|
||||
// add params
|
||||
for (var name in names)
|
||||
req.addParam(name);
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
part of spiceapi;
|
||||
|
||||
Future<void> memoryWrite(Connection con,
|
||||
String dllName, String data, int offset) {
|
||||
var req = Request("memory", "write");
|
||||
req.addParam(dllName);
|
||||
req.addParam(data);
|
||||
req.addParam(offset);
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<String> memoryRead(Connection con,
|
||||
String dllName, int offset, int size) {
|
||||
var req = Request("memory", "read");
|
||||
req.addParam(dllName);
|
||||
req.addParam(offset);
|
||||
req.addParam(size);
|
||||
return con.request(req).then((res) {
|
||||
return res.getData()[0];
|
||||
});
|
||||
}
|
||||
|
||||
Future<int> memorySignature(Connection con,
|
||||
String dllName, String signature, String replacement,
|
||||
int offset, int usage) {
|
||||
var req = Request("memory", "signature");
|
||||
req.addParam(dllName);
|
||||
req.addParam(signature);
|
||||
req.addParam(replacement);
|
||||
req.addParam(offset);
|
||||
req.addParam(usage);
|
||||
return con.request(req).then((res) {
|
||||
return res.getData()[0];
|
||||
});
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
Future<void> memoryWrite(Connection con,
|
||||
String dllName, String data, int offset) {
|
||||
var req = Request("memory", "write");
|
||||
req.addParam(dllName);
|
||||
req.addParam(data);
|
||||
req.addParam(offset);
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<String> memoryRead(Connection con,
|
||||
String dllName, int offset, int size) {
|
||||
var req = Request("memory", "read");
|
||||
req.addParam(dllName);
|
||||
req.addParam(offset);
|
||||
req.addParam(size);
|
||||
return con.request(req).then((res) {
|
||||
return res.getData()[0];
|
||||
});
|
||||
}
|
||||
|
||||
Future<int> memorySignature(Connection con,
|
||||
String dllName, String signature, String replacement,
|
||||
int offset, int usage) {
|
||||
var req = Request("memory", "signature");
|
||||
req.addParam(dllName);
|
||||
req.addParam(signature);
|
||||
req.addParam(replacement);
|
||||
req.addParam(offset);
|
||||
req.addParam(usage);
|
||||
return con.request(req).then((res) {
|
||||
return res.getData()[0];
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,68 +1,68 @@
|
||||
part of spiceapi;
|
||||
|
||||
class TouchState {
|
||||
int id;
|
||||
int x, y;
|
||||
bool active = true;
|
||||
bool updated = true;
|
||||
|
||||
TouchState(this.id, this.x, this.y);
|
||||
}
|
||||
|
||||
Future<List<TouchState>> touchRead(Connection con) {
|
||||
var req = Request("touch", "read");
|
||||
return con.request(req).then((res) {
|
||||
|
||||
// build states list
|
||||
List<TouchState> states = [];
|
||||
for (List state in res.getData()) {
|
||||
states.add(TouchState(
|
||||
state[0],
|
||||
state[1],
|
||||
state[2],
|
||||
));
|
||||
}
|
||||
|
||||
// return it
|
||||
return states;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> touchWrite(Connection con, List<TouchState> states) async {
|
||||
if (states.isEmpty) return;
|
||||
var req = Request("touch", "write");
|
||||
|
||||
// add params
|
||||
for (var state in states) {
|
||||
var obj = [
|
||||
state.id,
|
||||
state.x,
|
||||
state.y
|
||||
];
|
||||
req.addParam(obj);
|
||||
}
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> touchWriteReset(Connection con, List<TouchState> states) async {
|
||||
if (states.isEmpty) return;
|
||||
var req = Request("touch", "write_reset");
|
||||
|
||||
// add params
|
||||
for (var state in states)
|
||||
req.addParam(state.id);
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> touchWriteResetIDs(Connection con, List<int> touchIDs) async {
|
||||
if (touchIDs.isEmpty) return;
|
||||
var req = Request("touch", "write_reset");
|
||||
|
||||
// add params
|
||||
for (var id in touchIDs)
|
||||
req.addParam(id);
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
part of spiceapi;
|
||||
|
||||
class TouchState {
|
||||
int id;
|
||||
int x, y;
|
||||
bool active = true;
|
||||
bool updated = true;
|
||||
|
||||
TouchState(this.id, this.x, this.y);
|
||||
}
|
||||
|
||||
Future<List<TouchState>> touchRead(Connection con) {
|
||||
var req = Request("touch", "read");
|
||||
return con.request(req).then((res) {
|
||||
|
||||
// build states list
|
||||
List<TouchState> states = [];
|
||||
for (List state in res.getData()) {
|
||||
states.add(TouchState(
|
||||
state[0],
|
||||
state[1],
|
||||
state[2],
|
||||
));
|
||||
}
|
||||
|
||||
// return it
|
||||
return states;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> touchWrite(Connection con, List<TouchState> states) async {
|
||||
if (states.isEmpty) return;
|
||||
var req = Request("touch", "write");
|
||||
|
||||
// add params
|
||||
for (var state in states) {
|
||||
var obj = [
|
||||
state.id,
|
||||
state.x,
|
||||
state.y
|
||||
];
|
||||
req.addParam(obj);
|
||||
}
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> touchWriteReset(Connection con, List<TouchState> states) async {
|
||||
if (states.isEmpty) return;
|
||||
var req = Request("touch", "write_reset");
|
||||
|
||||
// add params
|
||||
for (var state in states)
|
||||
req.addParam(state.id);
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
Future<void> touchWriteResetIDs(Connection con, List<int> touchIDs) async {
|
||||
if (touchIDs.isEmpty) return;
|
||||
var req = Request("touch", "write_reset");
|
||||
|
||||
// add params
|
||||
for (var id in touchIDs)
|
||||
req.addParam(id);
|
||||
|
||||
return con.request(req);
|
||||
}
|
||||
|
||||
+116
-116
@@ -1,116 +1,116 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
pip-wheel-metadata/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# celery beat schedule file
|
||||
celerybeat-schedule
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
pip-wheel-metadata/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# celery beat schedule file
|
||||
celerybeat-schedule
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
For more information, please refer to <http://unlicense.org/>
|
||||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
For more information, please refer to <http://unlicense.org/>
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
from .connection import Connection
|
||||
from .request import Request
|
||||
from .analogs import *
|
||||
from .buttons import *
|
||||
from .card import *
|
||||
from .coin import *
|
||||
from .control import *
|
||||
from .exceptions import *
|
||||
from .iidx import *
|
||||
from .info import *
|
||||
from .keypads import *
|
||||
from .lights import *
|
||||
from .memory import *
|
||||
from .touch import *
|
||||
from .connection import Connection
|
||||
from .request import Request
|
||||
from .analogs import *
|
||||
from .buttons import *
|
||||
from .card import *
|
||||
from .coin import *
|
||||
from .control import *
|
||||
from .exceptions import *
|
||||
from .iidx import *
|
||||
from .info import *
|
||||
from .keypads import *
|
||||
from .lights import *
|
||||
from .memory import *
|
||||
from .touch import *
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
from .connection import Connection
|
||||
from .request import Request
|
||||
|
||||
|
||||
def analogs_read(con: Connection):
|
||||
res = con.request(Request("analogs", "read"))
|
||||
return res.get_data()
|
||||
|
||||
|
||||
def analogs_write(con: Connection, analog_state_list):
|
||||
req = Request("analogs", "write")
|
||||
for state in analog_state_list:
|
||||
req.add_param(state)
|
||||
con.request(req)
|
||||
|
||||
|
||||
def analogs_write_reset(con: Connection, analog_names=None):
|
||||
req = Request("analogs", "write_reset")
|
||||
|
||||
# reset all analogs
|
||||
if not analog_names:
|
||||
con.request(req)
|
||||
return
|
||||
|
||||
# reset specified analogs
|
||||
for analog_name in analog_names:
|
||||
req.add_param(analog_name)
|
||||
con.request(req)
|
||||
from .connection import Connection
|
||||
from .request import Request
|
||||
|
||||
|
||||
def analogs_read(con: Connection):
|
||||
res = con.request(Request("analogs", "read"))
|
||||
return res.get_data()
|
||||
|
||||
|
||||
def analogs_write(con: Connection, analog_state_list):
|
||||
req = Request("analogs", "write")
|
||||
for state in analog_state_list:
|
||||
req.add_param(state)
|
||||
con.request(req)
|
||||
|
||||
|
||||
def analogs_write_reset(con: Connection, analog_names=None):
|
||||
req = Request("analogs", "write_reset")
|
||||
|
||||
# reset all analogs
|
||||
if not analog_names:
|
||||
con.request(req)
|
||||
return
|
||||
|
||||
# reset specified analogs
|
||||
for analog_name in analog_names:
|
||||
req.add_param(analog_name)
|
||||
con.request(req)
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
from .connection import Connection
|
||||
from .request import Request
|
||||
|
||||
|
||||
def buttons_read(con: Connection):
|
||||
res = con.request(Request("buttons", "read"))
|
||||
return res.get_data()
|
||||
|
||||
|
||||
def buttons_write(con: Connection, button_state_list):
|
||||
req = Request("buttons", "write")
|
||||
for state in button_state_list:
|
||||
req.add_param(state)
|
||||
con.request(req)
|
||||
|
||||
|
||||
def buttons_write_reset(con: Connection, button_names=None):
|
||||
req = Request("buttons", "write_reset")
|
||||
|
||||
# reset all buttons
|
||||
if not button_names:
|
||||
con.request(req)
|
||||
return
|
||||
|
||||
# reset specified buttons
|
||||
for button_name in button_names:
|
||||
req.add_param(button_name)
|
||||
con.request(req)
|
||||
from .connection import Connection
|
||||
from .request import Request
|
||||
|
||||
|
||||
def buttons_read(con: Connection):
|
||||
res = con.request(Request("buttons", "read"))
|
||||
return res.get_data()
|
||||
|
||||
|
||||
def buttons_write(con: Connection, button_state_list):
|
||||
req = Request("buttons", "write")
|
||||
for state in button_state_list:
|
||||
req.add_param(state)
|
||||
con.request(req)
|
||||
|
||||
|
||||
def buttons_write_reset(con: Connection, button_names=None):
|
||||
req = Request("buttons", "write_reset")
|
||||
|
||||
# reset all buttons
|
||||
if not button_names:
|
||||
con.request(req)
|
||||
return
|
||||
|
||||
# reset specified buttons
|
||||
for button_name in button_names:
|
||||
req.add_param(button_name)
|
||||
con.request(req)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
from .connection import Connection
|
||||
from .request import Request
|
||||
|
||||
|
||||
def card_insert(con: Connection, unit: int, card_id: str):
|
||||
req = Request("card", "insert")
|
||||
req.add_param(unit)
|
||||
req.add_param(card_id)
|
||||
con.request(req)
|
||||
from .connection import Connection
|
||||
from .request import Request
|
||||
|
||||
|
||||
def card_insert(con: Connection, unit: int, card_id: str):
|
||||
req = Request("card", "insert")
|
||||
req.add_param(unit)
|
||||
req.add_param(card_id)
|
||||
con.request(req)
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
from .connection import Connection
|
||||
from .request import Request
|
||||
|
||||
|
||||
def coin_get(con: Connection):
|
||||
res = con.request(Request("coin", "get"))
|
||||
return res.get_data()[0]
|
||||
|
||||
|
||||
def coin_set(con: Connection, amount: int):
|
||||
req = Request("coin", "set")
|
||||
req.add_param(amount)
|
||||
con.request(req)
|
||||
|
||||
|
||||
def coin_insert(con: Connection, amount=1):
|
||||
req = Request("coin", "insert")
|
||||
if amount != 1:
|
||||
req.add_param(amount)
|
||||
con.request(req)
|
||||
from .connection import Connection
|
||||
from .request import Request
|
||||
|
||||
|
||||
def coin_get(con: Connection):
|
||||
res = con.request(Request("coin", "get"))
|
||||
return res.get_data()[0]
|
||||
|
||||
|
||||
def coin_set(con: Connection, amount: int):
|
||||
req = Request("coin", "set")
|
||||
req.add_param(amount)
|
||||
con.request(req)
|
||||
|
||||
|
||||
def coin_insert(con: Connection, amount=1):
|
||||
req = Request("coin", "insert")
|
||||
if amount != 1:
|
||||
req.add_param(amount)
|
||||
con.request(req)
|
||||
|
||||
@@ -1,139 +1,139 @@
|
||||
import os
|
||||
import socket
|
||||
from .request import Request
|
||||
from .response import Response
|
||||
from .rc4 import rc4
|
||||
from .exceptions import MalformedRequestException, APIError
|
||||
|
||||
|
||||
class Connection:
|
||||
""" Container for managing a single connection to the API server.
|
||||
"""
|
||||
|
||||
def __init__(self, host: str, port: int, password: str):
|
||||
"""Default constructor.
|
||||
|
||||
:param host: the host string to connect to
|
||||
:param port: the port of the host
|
||||
:param password: the connection password string
|
||||
"""
|
||||
self.host = host
|
||||
self.port = port
|
||||
self.password = password
|
||||
self.socket = None
|
||||
self.cipher = None
|
||||
self.reconnect()
|
||||
|
||||
def reconnect(self, refresh_session=True):
|
||||
"""Reconnect to the server.
|
||||
|
||||
This opens a new connection and closes the previous one, if existing.
|
||||
"""
|
||||
|
||||
# close old socket
|
||||
self.close()
|
||||
|
||||
# create new socket
|
||||
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.socket.settimeout(3)
|
||||
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
||||
self.socket.connect((self.host, self.port))
|
||||
|
||||
# cipher
|
||||
self.change_password(self.password)
|
||||
|
||||
# refresh session
|
||||
if refresh_session:
|
||||
from .control import control_session_refresh
|
||||
control_session_refresh(self)
|
||||
|
||||
def change_password(self, password):
|
||||
"""Allows to change the password on the fly.
|
||||
|
||||
The cipher will be rebuilt.
|
||||
"""
|
||||
if len(password) > 0:
|
||||
self.cipher = rc4(password.encode("UTF-8"))
|
||||
else:
|
||||
self.cipher = None
|
||||
|
||||
def close(self):
|
||||
"""Close the active connection, if existing."""
|
||||
|
||||
# check if socket is existing
|
||||
if self.socket:
|
||||
|
||||
# close and delete socket
|
||||
self.socket.close()
|
||||
self.socket = None
|
||||
|
||||
def request(self, request: Request):
|
||||
"""Send a request to the server and receive the answer.
|
||||
|
||||
:param request: request object
|
||||
:return: response object
|
||||
"""
|
||||
|
||||
# check if disconnected
|
||||
if not self.socket:
|
||||
raise RuntimeError("No active connection.")
|
||||
|
||||
# build data
|
||||
data = request.to_json().encode("UTF-8") + b"\x00"
|
||||
if self.cipher:
|
||||
data_list = list(data)
|
||||
data_cipher = []
|
||||
for b in data_list:
|
||||
data_cipher.append(b ^ next(self.cipher))
|
||||
data = bytes(data_cipher)
|
||||
|
||||
# send request
|
||||
if os.name != 'nt':
|
||||
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_QUICKACK, 1)
|
||||
self.socket.send(data)
|
||||
|
||||
# get answer
|
||||
answer_data = []
|
||||
while not len(answer_data) or answer_data[-1] != 0:
|
||||
|
||||
# receive data
|
||||
if os.name != 'nt':
|
||||
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_QUICKACK, 1)
|
||||
receive_data = self.socket.recv(4096)
|
||||
|
||||
# check length
|
||||
if len(receive_data):
|
||||
|
||||
# check cipher
|
||||
if self.cipher:
|
||||
|
||||
# add decrypted data
|
||||
for b in receive_data:
|
||||
answer_data.append(int(b ^ next(self.cipher)))
|
||||
else:
|
||||
|
||||
# add plaintext
|
||||
for b in receive_data:
|
||||
answer_data.append(int(b))
|
||||
else:
|
||||
raise RuntimeError("Connection was closed.")
|
||||
|
||||
# check for empty response
|
||||
if len(answer_data) <= 1:
|
||||
|
||||
# empty response means the JSON couldn't be parsed
|
||||
raise MalformedRequestException()
|
||||
|
||||
# build response
|
||||
response = Response(bytes(answer_data[:-1]).decode("UTF-8"))
|
||||
if len(response.get_errors()):
|
||||
raise APIError(response.get_errors())
|
||||
|
||||
# check ID
|
||||
req_id = request.get_id()
|
||||
res_id = response.get_id()
|
||||
if req_id != res_id:
|
||||
raise RuntimeError(f"Unexpected response ID: {res_id} (expected {req_id})")
|
||||
|
||||
# return response object
|
||||
return response
|
||||
import os
|
||||
import socket
|
||||
from .request import Request
|
||||
from .response import Response
|
||||
from .rc4 import rc4
|
||||
from .exceptions import MalformedRequestException, APIError
|
||||
|
||||
|
||||
class Connection:
|
||||
""" Container for managing a single connection to the API server.
|
||||
"""
|
||||
|
||||
def __init__(self, host: str, port: int, password: str):
|
||||
"""Default constructor.
|
||||
|
||||
:param host: the host string to connect to
|
||||
:param port: the port of the host
|
||||
:param password: the connection password string
|
||||
"""
|
||||
self.host = host
|
||||
self.port = port
|
||||
self.password = password
|
||||
self.socket = None
|
||||
self.cipher = None
|
||||
self.reconnect()
|
||||
|
||||
def reconnect(self, refresh_session=True):
|
||||
"""Reconnect to the server.
|
||||
|
||||
This opens a new connection and closes the previous one, if existing.
|
||||
"""
|
||||
|
||||
# close old socket
|
||||
self.close()
|
||||
|
||||
# create new socket
|
||||
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.socket.settimeout(3)
|
||||
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
||||
self.socket.connect((self.host, self.port))
|
||||
|
||||
# cipher
|
||||
self.change_password(self.password)
|
||||
|
||||
# refresh session
|
||||
if refresh_session:
|
||||
from .control import control_session_refresh
|
||||
control_session_refresh(self)
|
||||
|
||||
def change_password(self, password):
|
||||
"""Allows to change the password on the fly.
|
||||
|
||||
The cipher will be rebuilt.
|
||||
"""
|
||||
if len(password) > 0:
|
||||
self.cipher = rc4(password.encode("UTF-8"))
|
||||
else:
|
||||
self.cipher = None
|
||||
|
||||
def close(self):
|
||||
"""Close the active connection, if existing."""
|
||||
|
||||
# check if socket is existing
|
||||
if self.socket:
|
||||
|
||||
# close and delete socket
|
||||
self.socket.close()
|
||||
self.socket = None
|
||||
|
||||
def request(self, request: Request):
|
||||
"""Send a request to the server and receive the answer.
|
||||
|
||||
:param request: request object
|
||||
:return: response object
|
||||
"""
|
||||
|
||||
# check if disconnected
|
||||
if not self.socket:
|
||||
raise RuntimeError("No active connection.")
|
||||
|
||||
# build data
|
||||
data = request.to_json().encode("UTF-8") + b"\x00"
|
||||
if self.cipher:
|
||||
data_list = list(data)
|
||||
data_cipher = []
|
||||
for b in data_list:
|
||||
data_cipher.append(b ^ next(self.cipher))
|
||||
data = bytes(data_cipher)
|
||||
|
||||
# send request
|
||||
if os.name != 'nt':
|
||||
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_QUICKACK, 1)
|
||||
self.socket.send(data)
|
||||
|
||||
# get answer
|
||||
answer_data = []
|
||||
while not len(answer_data) or answer_data[-1] != 0:
|
||||
|
||||
# receive data
|
||||
if os.name != 'nt':
|
||||
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_QUICKACK, 1)
|
||||
receive_data = self.socket.recv(4096)
|
||||
|
||||
# check length
|
||||
if len(receive_data):
|
||||
|
||||
# check cipher
|
||||
if self.cipher:
|
||||
|
||||
# add decrypted data
|
||||
for b in receive_data:
|
||||
answer_data.append(int(b ^ next(self.cipher)))
|
||||
else:
|
||||
|
||||
# add plaintext
|
||||
for b in receive_data:
|
||||
answer_data.append(int(b))
|
||||
else:
|
||||
raise RuntimeError("Connection was closed.")
|
||||
|
||||
# check for empty response
|
||||
if len(answer_data) <= 1:
|
||||
|
||||
# empty response means the JSON couldn't be parsed
|
||||
raise MalformedRequestException()
|
||||
|
||||
# build response
|
||||
response = Response(bytes(answer_data[:-1]).decode("UTF-8"))
|
||||
if len(response.get_errors()):
|
||||
raise APIError(response.get_errors())
|
||||
|
||||
# check ID
|
||||
req_id = request.get_id()
|
||||
res_id = response.get_id()
|
||||
if req_id != res_id:
|
||||
raise RuntimeError(f"Unexpected response ID: {res_id} (expected {req_id})")
|
||||
|
||||
# return response object
|
||||
return response
|
||||
|
||||
@@ -1,51 +1,51 @@
|
||||
import random
|
||||
from .connection import Connection
|
||||
from .request import Request
|
||||
|
||||
|
||||
def control_raise(con: Connection, signal: str):
|
||||
req = Request("control", "raise")
|
||||
req.add_param(signal)
|
||||
con.request(req)
|
||||
|
||||
|
||||
def control_exit(con: Connection, code=None):
|
||||
req = Request("control", "exit")
|
||||
if code:
|
||||
req.add_param(code)
|
||||
try:
|
||||
con.request(req)
|
||||
except RuntimeError:
|
||||
pass # we expect the connection to get killed
|
||||
|
||||
|
||||
def control_restart(con: Connection):
|
||||
req = Request("control", "restart")
|
||||
try:
|
||||
con.request(req)
|
||||
except RuntimeError:
|
||||
pass # we expect the connection to get killed
|
||||
|
||||
|
||||
def control_session_refresh(con: Connection):
|
||||
res = con.request(Request("control", "session_refresh", req_id=random.randint(1, 2**64)))
|
||||
|
||||
# apply new password
|
||||
password = res.get_data()[0]
|
||||
con.change_password(password)
|
||||
|
||||
|
||||
def control_shutdown(con: Connection):
|
||||
req = Request("control", "shutdown")
|
||||
try:
|
||||
con.request(req)
|
||||
except RuntimeError:
|
||||
pass # we expect the connection to get killed
|
||||
|
||||
|
||||
def control_reboot(con: Connection):
|
||||
req = Request("control", "reboot")
|
||||
try:
|
||||
con.request(req)
|
||||
except RuntimeError:
|
||||
pass # we expect the connection to get killed
|
||||
import random
|
||||
from .connection import Connection
|
||||
from .request import Request
|
||||
|
||||
|
||||
def control_raise(con: Connection, signal: str):
|
||||
req = Request("control", "raise")
|
||||
req.add_param(signal)
|
||||
con.request(req)
|
||||
|
||||
|
||||
def control_exit(con: Connection, code=None):
|
||||
req = Request("control", "exit")
|
||||
if code:
|
||||
req.add_param(code)
|
||||
try:
|
||||
con.request(req)
|
||||
except RuntimeError:
|
||||
pass # we expect the connection to get killed
|
||||
|
||||
|
||||
def control_restart(con: Connection):
|
||||
req = Request("control", "restart")
|
||||
try:
|
||||
con.request(req)
|
||||
except RuntimeError:
|
||||
pass # we expect the connection to get killed
|
||||
|
||||
|
||||
def control_session_refresh(con: Connection):
|
||||
res = con.request(Request("control", "session_refresh", req_id=random.randint(1, 2**64)))
|
||||
|
||||
# apply new password
|
||||
password = res.get_data()[0]
|
||||
con.change_password(password)
|
||||
|
||||
|
||||
def control_shutdown(con: Connection):
|
||||
req = Request("control", "shutdown")
|
||||
try:
|
||||
con.request(req)
|
||||
except RuntimeError:
|
||||
pass # we expect the connection to get killed
|
||||
|
||||
|
||||
def control_reboot(con: Connection):
|
||||
req = Request("control", "reboot")
|
||||
try:
|
||||
con.request(req)
|
||||
except RuntimeError:
|
||||
pass # we expect the connection to get killed
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
|
||||
|
||||
class APIError(Exception):
|
||||
|
||||
def __init__(self, errors):
|
||||
super().__init__("\r\n".join(errors))
|
||||
|
||||
|
||||
class MalformedRequestException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class APIError(Exception):
|
||||
|
||||
def __init__(self, errors):
|
||||
super().__init__("\r\n".join(errors))
|
||||
|
||||
|
||||
class MalformedRequestException(Exception):
|
||||
pass
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
from .connection import Connection
|
||||
from .request import Request
|
||||
|
||||
|
||||
def iidx_ticker_get(con: Connection):
|
||||
res = con.request(Request("iidx", "ticker_get"))
|
||||
return res.get_data()
|
||||
|
||||
|
||||
def iidx_ticker_set(con: Connection, text: str):
|
||||
req = Request("iidx", "ticker_set")
|
||||
req.add_param(text)
|
||||
con.request(req)
|
||||
|
||||
|
||||
def iidx_ticker_reset(con: Connection):
|
||||
req = Request("iidx", "ticker_reset")
|
||||
con.request(req)
|
||||
from .connection import Connection
|
||||
from .request import Request
|
||||
|
||||
|
||||
def iidx_ticker_get(con: Connection):
|
||||
res = con.request(Request("iidx", "ticker_get"))
|
||||
return res.get_data()
|
||||
|
||||
|
||||
def iidx_ticker_set(con: Connection, text: str):
|
||||
req = Request("iidx", "ticker_set")
|
||||
req.add_param(text)
|
||||
con.request(req)
|
||||
|
||||
|
||||
def iidx_ticker_reset(con: Connection):
|
||||
req = Request("iidx", "ticker_reset")
|
||||
con.request(req)
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
from .connection import Connection
|
||||
from .request import Request
|
||||
|
||||
|
||||
def info_avs(con: Connection):
|
||||
res = con.request(Request("info", "avs"))
|
||||
return res.get_data()[0]
|
||||
|
||||
|
||||
def info_launcher(con: Connection):
|
||||
res = con.request(Request("info", "launcher"))
|
||||
return res.get_data()[0]
|
||||
|
||||
|
||||
def info_memory(con: Connection):
|
||||
res = con.request(Request("info", "memory"))
|
||||
return res.get_data()[0]
|
||||
from .connection import Connection
|
||||
from .request import Request
|
||||
|
||||
|
||||
def info_avs(con: Connection):
|
||||
res = con.request(Request("info", "avs"))
|
||||
return res.get_data()[0]
|
||||
|
||||
|
||||
def info_launcher(con: Connection):
|
||||
res = con.request(Request("info", "launcher"))
|
||||
return res.get_data()[0]
|
||||
|
||||
|
||||
def info_memory(con: Connection):
|
||||
res = con.request(Request("info", "memory"))
|
||||
return res.get_data()[0]
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
from .connection import Connection
|
||||
from .request import Request
|
||||
|
||||
|
||||
def keypads_write(con: Connection, keypad: int, input_values: str):
|
||||
req = Request("keypads", "write")
|
||||
req.add_param(keypad)
|
||||
req.add_param(input_values)
|
||||
con.request(req)
|
||||
|
||||
|
||||
def keypads_set(con: Connection, keypad: int, input_values: str):
|
||||
req = Request("keypads", "set")
|
||||
req.add_param(keypad)
|
||||
for value in input_values:
|
||||
req.add_param(value)
|
||||
con.request(req)
|
||||
|
||||
|
||||
def keypads_get(con: Connection, keypad: int):
|
||||
req = Request("keypads", "get")
|
||||
req.add_param(keypad)
|
||||
res = con.request(req)
|
||||
return res.get_data()
|
||||
from .connection import Connection
|
||||
from .request import Request
|
||||
|
||||
|
||||
def keypads_write(con: Connection, keypad: int, input_values: str):
|
||||
req = Request("keypads", "write")
|
||||
req.add_param(keypad)
|
||||
req.add_param(input_values)
|
||||
con.request(req)
|
||||
|
||||
|
||||
def keypads_set(con: Connection, keypad: int, input_values: str):
|
||||
req = Request("keypads", "set")
|
||||
req.add_param(keypad)
|
||||
for value in input_values:
|
||||
req.add_param(value)
|
||||
con.request(req)
|
||||
|
||||
|
||||
def keypads_get(con: Connection, keypad: int):
|
||||
req = Request("keypads", "get")
|
||||
req.add_param(keypad)
|
||||
res = con.request(req)
|
||||
return res.get_data()
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
from .connection import Connection
|
||||
from .request import Request
|
||||
|
||||
|
||||
def lights_read(con: Connection):
|
||||
res = con.request(Request("lights", "read"))
|
||||
return res.get_data()
|
||||
|
||||
|
||||
def lights_write(con: Connection, light_state_list):
|
||||
req = Request("lights", "write")
|
||||
for state in light_state_list:
|
||||
req.add_param(state)
|
||||
con.request(req)
|
||||
|
||||
|
||||
def lights_write_reset(con: Connection, light_names=None):
|
||||
req = Request("lights", "write_reset")
|
||||
|
||||
# reset all lights
|
||||
if not light_names:
|
||||
con.request(req)
|
||||
return
|
||||
|
||||
# reset specified lights
|
||||
for light_name in light_names:
|
||||
req.add_param(light_name)
|
||||
con.request(req)
|
||||
from .connection import Connection
|
||||
from .request import Request
|
||||
|
||||
|
||||
def lights_read(con: Connection):
|
||||
res = con.request(Request("lights", "read"))
|
||||
return res.get_data()
|
||||
|
||||
|
||||
def lights_write(con: Connection, light_state_list):
|
||||
req = Request("lights", "write")
|
||||
for state in light_state_list:
|
||||
req.add_param(state)
|
||||
con.request(req)
|
||||
|
||||
|
||||
def lights_write_reset(con: Connection, light_names=None):
|
||||
req = Request("lights", "write_reset")
|
||||
|
||||
# reset all lights
|
||||
if not light_names:
|
||||
con.request(req)
|
||||
return
|
||||
|
||||
# reset specified lights
|
||||
for light_name in light_names:
|
||||
req.add_param(light_name)
|
||||
con.request(req)
|
||||
|
||||
@@ -1,31 +1,31 @@
|
||||
from .connection import Connection
|
||||
from .request import Request
|
||||
|
||||
|
||||
def memory_write(con: Connection, dll_name: str, data: str, offset: int):
|
||||
req = Request("memory", "write")
|
||||
req.add_param(dll_name)
|
||||
req.add_param(data)
|
||||
req.add_param(offset)
|
||||
con.request(req)
|
||||
|
||||
|
||||
def memory_read(con: Connection, dll_name: str, offset: int, size: int):
|
||||
req = Request("memory", "read")
|
||||
req.add_param(dll_name)
|
||||
req.add_param(offset)
|
||||
req.add_param(size)
|
||||
res = con.request(req)
|
||||
return res.get_data()[0]
|
||||
|
||||
|
||||
def memory_signature(con: Connection, dll_name: str, signature: str,
|
||||
replacement: str, offset: int, usage: int):
|
||||
req = Request("memory", "signature")
|
||||
req.add_param(dll_name)
|
||||
req.add_param(signature)
|
||||
req.add_param(replacement)
|
||||
req.add_param(offset)
|
||||
req.add_param(usage)
|
||||
res = con.request(req)
|
||||
return res.get_data()[0]
|
||||
from .connection import Connection
|
||||
from .request import Request
|
||||
|
||||
|
||||
def memory_write(con: Connection, dll_name: str, data: str, offset: int):
|
||||
req = Request("memory", "write")
|
||||
req.add_param(dll_name)
|
||||
req.add_param(data)
|
||||
req.add_param(offset)
|
||||
con.request(req)
|
||||
|
||||
|
||||
def memory_read(con: Connection, dll_name: str, offset: int, size: int):
|
||||
req = Request("memory", "read")
|
||||
req.add_param(dll_name)
|
||||
req.add_param(offset)
|
||||
req.add_param(size)
|
||||
res = con.request(req)
|
||||
return res.get_data()[0]
|
||||
|
||||
|
||||
def memory_signature(con: Connection, dll_name: str, signature: str,
|
||||
replacement: str, offset: int, usage: int):
|
||||
req = Request("memory", "signature")
|
||||
req.add_param(dll_name)
|
||||
req.add_param(signature)
|
||||
req.add_param(replacement)
|
||||
req.add_param(offset)
|
||||
req.add_param(usage)
|
||||
res = con.request(req)
|
||||
return res.get_data()[0]
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
|
||||
|
||||
def rc4_ksa(key):
|
||||
n = len(key)
|
||||
j = 0
|
||||
s_box = list(range(256))
|
||||
for i in range(256):
|
||||
j = (j + s_box[i] + key[i % n]) % 256
|
||||
s_box[i], s_box[j] = s_box[j], s_box[i]
|
||||
return s_box
|
||||
|
||||
|
||||
def rc4_prga(s_box):
|
||||
i = 0
|
||||
j = 0
|
||||
while True:
|
||||
i = (i + 1) % 256
|
||||
j = (j + s_box[i]) % 256
|
||||
s_box[i], s_box[j] = s_box[j], s_box[i]
|
||||
yield s_box[(s_box[i] + s_box[j]) % 256]
|
||||
|
||||
|
||||
def rc4(key):
|
||||
return rc4_prga(rc4_ksa(key))
|
||||
|
||||
|
||||
def rc4_ksa(key):
|
||||
n = len(key)
|
||||
j = 0
|
||||
s_box = list(range(256))
|
||||
for i in range(256):
|
||||
j = (j + s_box[i] + key[i % n]) % 256
|
||||
s_box[i], s_box[j] = s_box[j], s_box[i]
|
||||
return s_box
|
||||
|
||||
|
||||
def rc4_prga(s_box):
|
||||
i = 0
|
||||
j = 0
|
||||
while True:
|
||||
i = (i + 1) % 256
|
||||
j = (j + s_box[i]) % 256
|
||||
s_box[i], s_box[j] = s_box[j], s_box[i]
|
||||
yield s_box[(s_box[i] + s_box[j]) % 256]
|
||||
|
||||
|
||||
def rc4(key):
|
||||
return rc4_prga(rc4_ksa(key))
|
||||
|
||||
@@ -1,63 +1,63 @@
|
||||
import json
|
||||
from threading import Lock
|
||||
|
||||
|
||||
class Request:
|
||||
|
||||
# global ID pool
|
||||
GLOBAL_ID = 1
|
||||
GLOBAL_ID_LOCK = Lock()
|
||||
|
||||
def __init__(self, module: str, function: str, req_id=None):
|
||||
|
||||
# use global ID
|
||||
with Request.GLOBAL_ID_LOCK:
|
||||
if req_id is None:
|
||||
|
||||
# reset at max value
|
||||
Request.GLOBAL_ID += 1
|
||||
if Request.GLOBAL_ID >= 2 ** 64:
|
||||
Request.GLOBAL_ID = 1
|
||||
|
||||
# get ID and increase by one
|
||||
req_id = Request.GLOBAL_ID
|
||||
|
||||
else:
|
||||
|
||||
# carry over ID
|
||||
Request.GLOBAL_ID = req_id
|
||||
|
||||
# remember ID
|
||||
self._id = req_id
|
||||
|
||||
# build data dict
|
||||
self.data = {
|
||||
"id": req_id,
|
||||
"module": module,
|
||||
"function": function,
|
||||
"params": []
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def from_json(request_json: str):
|
||||
req = Request("", "", 0)
|
||||
req.data = json.loads(request_json)
|
||||
req._id = req.data["id"]
|
||||
return req
|
||||
|
||||
def get_id(self):
|
||||
return self._id
|
||||
|
||||
def to_json(self):
|
||||
return json.dumps(
|
||||
self.data,
|
||||
ensure_ascii=False,
|
||||
check_circular=False,
|
||||
allow_nan=False,
|
||||
indent=None,
|
||||
separators=(",", ":"),
|
||||
sort_keys=False
|
||||
)
|
||||
|
||||
def add_param(self, param):
|
||||
self.data["params"].append(param)
|
||||
import json
|
||||
from threading import Lock
|
||||
|
||||
|
||||
class Request:
|
||||
|
||||
# global ID pool
|
||||
GLOBAL_ID = 1
|
||||
GLOBAL_ID_LOCK = Lock()
|
||||
|
||||
def __init__(self, module: str, function: str, req_id=None):
|
||||
|
||||
# use global ID
|
||||
with Request.GLOBAL_ID_LOCK:
|
||||
if req_id is None:
|
||||
|
||||
# reset at max value
|
||||
Request.GLOBAL_ID += 1
|
||||
if Request.GLOBAL_ID >= 2 ** 64:
|
||||
Request.GLOBAL_ID = 1
|
||||
|
||||
# get ID and increase by one
|
||||
req_id = Request.GLOBAL_ID
|
||||
|
||||
else:
|
||||
|
||||
# carry over ID
|
||||
Request.GLOBAL_ID = req_id
|
||||
|
||||
# remember ID
|
||||
self._id = req_id
|
||||
|
||||
# build data dict
|
||||
self.data = {
|
||||
"id": req_id,
|
||||
"module": module,
|
||||
"function": function,
|
||||
"params": []
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def from_json(request_json: str):
|
||||
req = Request("", "", 0)
|
||||
req.data = json.loads(request_json)
|
||||
req._id = req.data["id"]
|
||||
return req
|
||||
|
||||
def get_id(self):
|
||||
return self._id
|
||||
|
||||
def to_json(self):
|
||||
return json.dumps(
|
||||
self.data,
|
||||
ensure_ascii=False,
|
||||
check_circular=False,
|
||||
allow_nan=False,
|
||||
indent=None,
|
||||
separators=(",", ":"),
|
||||
sort_keys=False
|
||||
)
|
||||
|
||||
def add_param(self, param):
|
||||
self.data["params"].append(param)
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
import json
|
||||
|
||||
|
||||
class Response:
|
||||
|
||||
def __init__(self, response_json: str):
|
||||
self._res = json.loads(response_json)
|
||||
self._id = self._res["id"]
|
||||
self._errors = self._res["errors"]
|
||||
self._data = self._res["data"]
|
||||
|
||||
def to_json(self):
|
||||
return json.dumps(
|
||||
self._res,
|
||||
ensure_ascii=True,
|
||||
check_circular=False,
|
||||
allow_nan=False,
|
||||
indent=2,
|
||||
separators=(",", ": "),
|
||||
sort_keys=False
|
||||
)
|
||||
|
||||
def get_id(self):
|
||||
return self._id
|
||||
|
||||
def get_errors(self):
|
||||
return self._errors
|
||||
|
||||
def get_data(self):
|
||||
return self._data
|
||||
import json
|
||||
|
||||
|
||||
class Response:
|
||||
|
||||
def __init__(self, response_json: str):
|
||||
self._res = json.loads(response_json)
|
||||
self._id = self._res["id"]
|
||||
self._errors = self._res["errors"]
|
||||
self._data = self._res["data"]
|
||||
|
||||
def to_json(self):
|
||||
return json.dumps(
|
||||
self._res,
|
||||
ensure_ascii=True,
|
||||
check_circular=False,
|
||||
allow_nan=False,
|
||||
indent=2,
|
||||
separators=(",", ": "),
|
||||
sort_keys=False
|
||||
)
|
||||
|
||||
def get_id(self):
|
||||
return self._id
|
||||
|
||||
def get_errors(self):
|
||||
return self._errors
|
||||
|
||||
def get_data(self):
|
||||
return self._data
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
from .connection import Connection
|
||||
from .request import Request
|
||||
|
||||
|
||||
def touch_read(con: Connection):
|
||||
res = con.request(Request("touch", "read"))
|
||||
return res.get_data()
|
||||
|
||||
|
||||
def touch_write(con: Connection, touch_points):
|
||||
req = Request("touch", "write")
|
||||
for state in touch_points:
|
||||
req.add_param(state)
|
||||
con.request(req)
|
||||
|
||||
|
||||
def touch_write_reset(con: Connection, touch_ids):
|
||||
req = Request("touch", "write_reset")
|
||||
for touch_id in touch_ids:
|
||||
req.add_param(touch_id)
|
||||
con.request(req)
|
||||
from .connection import Connection
|
||||
from .request import Request
|
||||
|
||||
|
||||
def touch_read(con: Connection):
|
||||
res = con.request(Request("touch", "read"))
|
||||
return res.get_data()
|
||||
|
||||
|
||||
def touch_write(con: Connection, touch_points):
|
||||
req = Request("touch", "write")
|
||||
for state in touch_points:
|
||||
req.add_param(state)
|
||||
con.request(req)
|
||||
|
||||
|
||||
def touch_write_reset(con: Connection, touch_ids):
|
||||
req = Request("touch", "write_reset")
|
||||
for touch_id in touch_ids:
|
||||
req.add_param(touch_id)
|
||||
con.request(req)
|
||||
|
||||
+574
-574
File diff suppressed because it is too large
Load Diff
@@ -1,52 +1,52 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import binascii
|
||||
import spiceapi
|
||||
import argparse
|
||||
|
||||
|
||||
def patch_string(con, dll_name: str, find: str, replace: str):
|
||||
while True:
|
||||
try:
|
||||
|
||||
# replace first result
|
||||
address = spiceapi.memory_signature(
|
||||
con, dll_name,
|
||||
binascii.hexlify(bytes(find, "utf-8")).decode("utf-8"),
|
||||
binascii.hexlify(bytes(replace, "utf-8")).decode("utf-8"),
|
||||
0, 0)
|
||||
|
||||
# print findings
|
||||
print("{}: {} = {} => {}".format(
|
||||
dll_name,
|
||||
hex(address),
|
||||
find,
|
||||
replace))
|
||||
|
||||
except spiceapi.APIError:
|
||||
|
||||
# this happens when the signature wasn't found anymore
|
||||
break
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
# parse args
|
||||
parser = argparse.ArgumentParser(description="SpiceAPI string replacer")
|
||||
parser.add_argument("host", type=str, help="The host to connect to")
|
||||
parser.add_argument("port", type=int, help="The port the host is using")
|
||||
parser.add_argument("password", type=str, help="The pass the host is using")
|
||||
parser.add_argument("dll", type=str, help="The DLL to patch")
|
||||
parser.add_argument("find", type=str, help="The string to find")
|
||||
parser.add_argument("replace", type=str, help="The string to replace with")
|
||||
args = parser.parse_args()
|
||||
|
||||
# connect
|
||||
con = spiceapi.Connection(host=args.host, port=args.port, password=args.password)
|
||||
|
||||
# replace the string
|
||||
patch_string(con, args.dll, args.find, args.replace)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import binascii
|
||||
import spiceapi
|
||||
import argparse
|
||||
|
||||
|
||||
def patch_string(con, dll_name: str, find: str, replace: str):
|
||||
while True:
|
||||
try:
|
||||
|
||||
# replace first result
|
||||
address = spiceapi.memory_signature(
|
||||
con, dll_name,
|
||||
binascii.hexlify(bytes(find, "utf-8")).decode("utf-8"),
|
||||
binascii.hexlify(bytes(replace, "utf-8")).decode("utf-8"),
|
||||
0, 0)
|
||||
|
||||
# print findings
|
||||
print("{}: {} = {} => {}".format(
|
||||
dll_name,
|
||||
hex(address),
|
||||
find,
|
||||
replace))
|
||||
|
||||
except spiceapi.APIError:
|
||||
|
||||
# this happens when the signature wasn't found anymore
|
||||
break
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
# parse args
|
||||
parser = argparse.ArgumentParser(description="SpiceAPI string replacer")
|
||||
parser.add_argument("host", type=str, help="The host to connect to")
|
||||
parser.add_argument("port", type=int, help="The port the host is using")
|
||||
parser.add_argument("password", type=str, help="The pass the host is using")
|
||||
parser.add_argument("dll", type=str, help="The DLL to patch")
|
||||
parser.add_argument("find", type=str, help="The string to find")
|
||||
parser.add_argument("replace", type=str, help="The string to replace with")
|
||||
args = parser.parse_args()
|
||||
|
||||
# connect
|
||||
con = spiceapi.Connection(host=args.host, port=args.port, password=args.password)
|
||||
|
||||
# replace the string
|
||||
patch_string(con, args.dll, args.find, args.replace)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
+51
-51
@@ -1,51 +1,51 @@
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "external/rapidjson/writer.h"
|
||||
#include "external/rapidjson/prettywriter.h"
|
||||
|
||||
#include "util/logging.h"
|
||||
|
||||
#include "response.h"
|
||||
|
||||
using namespace api;
|
||||
|
||||
Response::Response(uint64_t id) {
|
||||
|
||||
// load template
|
||||
document.Parse(
|
||||
"{"
|
||||
"\"id\": -1,"
|
||||
"\"errors\": [],"
|
||||
"\"data\": []"
|
||||
"}"
|
||||
);
|
||||
|
||||
// check for error
|
||||
auto error = document.GetParseError();
|
||||
if (error)
|
||||
log_warning("api", "response template parse error: {}", error);
|
||||
|
||||
// set ID
|
||||
document["id"].SetUint64(id);
|
||||
|
||||
// get fields
|
||||
this->errors = document["errors"];
|
||||
this->data = document["data"];
|
||||
}
|
||||
|
||||
std::string Response::get_string(bool pretty) {
|
||||
|
||||
// apply errors and data
|
||||
this->document["errors"] = this->errors;
|
||||
this->document["data"] = this->data;
|
||||
|
||||
// generate string
|
||||
rapidjson::StringBuffer sb;
|
||||
if (pretty) {
|
||||
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(sb);
|
||||
this->document.Accept(writer);
|
||||
} else {
|
||||
rapidjson::Writer<rapidjson::StringBuffer> writer(sb);
|
||||
this->document.Accept(writer);
|
||||
}
|
||||
return std::string(sb.GetString());
|
||||
}
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "external/rapidjson/writer.h"
|
||||
#include "external/rapidjson/prettywriter.h"
|
||||
|
||||
#include "util/logging.h"
|
||||
|
||||
#include "response.h"
|
||||
|
||||
using namespace api;
|
||||
|
||||
Response::Response(uint64_t id) {
|
||||
|
||||
// load template
|
||||
document.Parse(
|
||||
"{"
|
||||
"\"id\": -1,"
|
||||
"\"errors\": [],"
|
||||
"\"data\": []"
|
||||
"}"
|
||||
);
|
||||
|
||||
// check for error
|
||||
auto error = document.GetParseError();
|
||||
if (error)
|
||||
log_warning("api", "response template parse error: {}", error);
|
||||
|
||||
// set ID
|
||||
document["id"].SetUint64(id);
|
||||
|
||||
// get fields
|
||||
this->errors = document["errors"];
|
||||
this->data = document["data"];
|
||||
}
|
||||
|
||||
std::string Response::get_string(bool pretty) {
|
||||
|
||||
// apply errors and data
|
||||
this->document["errors"] = this->errors;
|
||||
this->document["data"] = this->data;
|
||||
|
||||
// generate string
|
||||
rapidjson::StringBuffer sb;
|
||||
if (pretty) {
|
||||
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(sb);
|
||||
this->document.Accept(writer);
|
||||
} else {
|
||||
rapidjson::Writer<rapidjson::StringBuffer> writer(sb);
|
||||
this->document.Accept(writer);
|
||||
}
|
||||
return std::string(sb.GetString());
|
||||
}
|
||||
|
||||
+37
-37
@@ -1,37 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include "external/rapidjson/document.h"
|
||||
|
||||
namespace api {
|
||||
|
||||
class Response {
|
||||
private:
|
||||
rapidjson::Document document;
|
||||
rapidjson::Value errors;
|
||||
rapidjson::Value data;
|
||||
|
||||
public:
|
||||
std::string password;
|
||||
bool password_changed = false;
|
||||
|
||||
Response(uint64_t id);
|
||||
|
||||
template <class T> void add_error(T& error) {
|
||||
this->errors.PushBack(error, document.GetAllocator());
|
||||
};
|
||||
template <class T> void add_data(T& data) {
|
||||
this->data.PushBack(data, document.GetAllocator());
|
||||
}
|
||||
|
||||
std::string get_string(bool pretty=false);
|
||||
|
||||
inline rapidjson::Document* doc() {
|
||||
return &document;
|
||||
}
|
||||
|
||||
inline void password_change(std::string password) {
|
||||
this->password = password;
|
||||
this->password_changed = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include "external/rapidjson/document.h"
|
||||
|
||||
namespace api {
|
||||
|
||||
class Response {
|
||||
private:
|
||||
rapidjson::Document document;
|
||||
rapidjson::Value errors;
|
||||
rapidjson::Value data;
|
||||
|
||||
public:
|
||||
std::string password;
|
||||
bool password_changed = false;
|
||||
|
||||
Response(uint64_t id);
|
||||
|
||||
template <class T> void add_error(T& error) {
|
||||
this->errors.PushBack(error, document.GetAllocator());
|
||||
};
|
||||
template <class T> void add_data(T& data) {
|
||||
this->data.PushBack(data, document.GetAllocator());
|
||||
}
|
||||
|
||||
std::string get_string(bool pretty=false);
|
||||
|
||||
inline rapidjson::Document* doc() {
|
||||
return &document;
|
||||
}
|
||||
|
||||
inline void password_change(std::string password) {
|
||||
this->password = password;
|
||||
this->password_changed = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
+30
-30
@@ -1,30 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <windows.h>
|
||||
|
||||
namespace api {
|
||||
|
||||
class Controller;
|
||||
struct ClientState;
|
||||
|
||||
class SerialController {
|
||||
public:
|
||||
|
||||
SerialController(Controller *controller, std::string port, DWORD baud);
|
||||
~SerialController();
|
||||
|
||||
void open_port();
|
||||
void free_port();
|
||||
|
||||
private:
|
||||
Controller *controller = nullptr;
|
||||
std::string port = "";
|
||||
DWORD baud = 0;
|
||||
HANDLE handle = INVALID_HANDLE_VALUE;
|
||||
std::thread *thread = nullptr;
|
||||
ClientState *state = nullptr;
|
||||
bool running = true;
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <windows.h>
|
||||
|
||||
namespace api {
|
||||
|
||||
class Controller;
|
||||
struct ClientState;
|
||||
|
||||
class SerialController {
|
||||
public:
|
||||
|
||||
SerialController(Controller *controller, std::string port, DWORD baud);
|
||||
~SerialController();
|
||||
|
||||
void open_port();
|
||||
void free_port();
|
||||
|
||||
private:
|
||||
Controller *controller = nullptr;
|
||||
std::string port = "";
|
||||
DWORD baud = 0;
|
||||
HANDLE handle = INVALID_HANDLE_VALUE;
|
||||
std::thread *thread = nullptr;
|
||||
ClientState *state = nullptr;
|
||||
bool running = true;
|
||||
};
|
||||
}
|
||||
|
||||
+169
-169
@@ -1,169 +1,169 @@
|
||||
#define HEADSOCKET_IMPLEMENTATION
|
||||
#include "external/headsocket.h"
|
||||
|
||||
#include "websocket.h"
|
||||
#include "util/utils.h"
|
||||
#include "util/rc4.h"
|
||||
#include "util/logging.h"
|
||||
#include "controller.h"
|
||||
|
||||
using namespace headsocket;
|
||||
|
||||
namespace api {
|
||||
|
||||
/*
|
||||
* Client class declaration
|
||||
*/
|
||||
class WebSocketClient : public web_socket_client {
|
||||
|
||||
// required class header
|
||||
HEADSOCKET_CLIENT(WebSocketClient, web_socket_client);
|
||||
|
||||
private:
|
||||
ClientState *state = nullptr;
|
||||
|
||||
protected:
|
||||
bool async_received_data(const data_block &db, uint8_t *ptr, size_t length) override;
|
||||
|
||||
void on_accept() override;
|
||||
void on_disconnect() override;
|
||||
};
|
||||
|
||||
/*
|
||||
* Server class declaration
|
||||
*/
|
||||
class WebSocketServer : public web_socket_server<WebSocketClient> {
|
||||
HEADSOCKET_SERVER(WebSocketServer, web_socket_server);
|
||||
public:
|
||||
WebSocketController *websocket;
|
||||
};
|
||||
|
||||
void api::WebSocketServer::init() {}
|
||||
|
||||
/*
|
||||
* Controller state so we don't have to import headsocket stuff in our header
|
||||
*/
|
||||
struct WebSocketControllerState {
|
||||
std::shared_ptr<WebSocketServer> server;
|
||||
};
|
||||
|
||||
WebSocketController::WebSocketController(Controller *controller, uint16_t port) {
|
||||
this->controller = controller;
|
||||
|
||||
// create state
|
||||
this->state = new WebSocketControllerState();
|
||||
|
||||
// start server
|
||||
this->state->server = WebSocketServer::create(port);
|
||||
this->state->server->websocket = this;
|
||||
if (this->state->server->is_running()) {
|
||||
log_info("api::websocket", "server listening on port: {}", port);
|
||||
} else {
|
||||
log_warning("api::websocket", "server failed to listen on port: {}", port);
|
||||
}
|
||||
}
|
||||
|
||||
WebSocketController::~WebSocketController() {
|
||||
|
||||
// stop server
|
||||
this->state->server->stop();
|
||||
|
||||
// delete state
|
||||
delete this->state;
|
||||
}
|
||||
|
||||
void WebSocketController::free_socket() {
|
||||
this->state->server->stop();
|
||||
}
|
||||
|
||||
void WebSocketClient::on_accept() {
|
||||
web_socket_client::on_accept();
|
||||
|
||||
// get pointer to server
|
||||
auto srv = reinterpret_cast<WebSocketServer *>(server().get());
|
||||
if (!srv || !srv->websocket) {
|
||||
log_fatal("api::websocket", "on_accept has no server");
|
||||
}
|
||||
|
||||
// check for init
|
||||
state = new ClientState();
|
||||
srv->websocket->controller->init_state(state);
|
||||
|
||||
// log connection
|
||||
log_info("api::websocket", "client connected");
|
||||
}
|
||||
|
||||
void WebSocketClient::on_disconnect() {
|
||||
|
||||
// log disconnection
|
||||
log_info("api::websocket", "client disconnected");
|
||||
|
||||
// get pointer to server
|
||||
auto srv = reinterpret_cast<WebSocketServer *>(server().get());
|
||||
if (!srv || !srv->websocket) {
|
||||
log_fatal("api::websocket", "on_disconnect has no server");
|
||||
}
|
||||
|
||||
// clean up state
|
||||
srv->websocket->controller->free_state(state);
|
||||
delete state;
|
||||
state = nullptr;
|
||||
|
||||
// call super
|
||||
web_socket_client::on_disconnect();
|
||||
}
|
||||
|
||||
/*
|
||||
* This is where business actually happens, gets called on every datablock receive
|
||||
*/
|
||||
bool WebSocketClient::async_received_data(const data_block &db, uint8_t *ptr, size_t length) {
|
||||
|
||||
// get pointer to server
|
||||
auto srv = reinterpret_cast<WebSocketServer *>(server().get());
|
||||
if (!srv || !srv->websocket) {
|
||||
log_fatal("api::websocket", "received datablock without server");
|
||||
}
|
||||
|
||||
// check state
|
||||
if (!state) {
|
||||
log_fatal("api::websocket", "client with no state received datablock");
|
||||
}
|
||||
|
||||
// check datablock type
|
||||
switch (db.op) {
|
||||
case opcode::binary: {
|
||||
|
||||
// allocate buffers
|
||||
std::vector<char> in(ptr, ptr + length);
|
||||
std::vector<char> out;
|
||||
|
||||
// crypt in-data
|
||||
if (state->cipher) {
|
||||
state->cipher->crypt(reinterpret_cast<uint8_t *>(in.data()), in.size());
|
||||
}
|
||||
|
||||
// process request
|
||||
srv->websocket->controller->process_request(state, &in, &out);
|
||||
|
||||
// crypt out-data
|
||||
if (state->cipher) {
|
||||
state->cipher->crypt(reinterpret_cast<uint8_t *>(out.data()), out.size());
|
||||
}
|
||||
|
||||
// send answer
|
||||
push(out.data(), out.size());
|
||||
|
||||
// check for password change
|
||||
srv->websocket->controller->process_password_change(state);
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
log_warning("api::websocket", "datablock received with non-binary type");
|
||||
break;
|
||||
}
|
||||
|
||||
// always consume the datablock, nomnom
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#define HEADSOCKET_IMPLEMENTATION
|
||||
#include "external/headsocket.h"
|
||||
|
||||
#include "websocket.h"
|
||||
#include "util/utils.h"
|
||||
#include "util/rc4.h"
|
||||
#include "util/logging.h"
|
||||
#include "controller.h"
|
||||
|
||||
using namespace headsocket;
|
||||
|
||||
namespace api {
|
||||
|
||||
/*
|
||||
* Client class declaration
|
||||
*/
|
||||
class WebSocketClient : public web_socket_client {
|
||||
|
||||
// required class header
|
||||
HEADSOCKET_CLIENT(WebSocketClient, web_socket_client);
|
||||
|
||||
private:
|
||||
ClientState *state = nullptr;
|
||||
|
||||
protected:
|
||||
bool async_received_data(const data_block &db, uint8_t *ptr, size_t length) override;
|
||||
|
||||
void on_accept() override;
|
||||
void on_disconnect() override;
|
||||
};
|
||||
|
||||
/*
|
||||
* Server class declaration
|
||||
*/
|
||||
class WebSocketServer : public web_socket_server<WebSocketClient> {
|
||||
HEADSOCKET_SERVER(WebSocketServer, web_socket_server);
|
||||
public:
|
||||
WebSocketController *websocket;
|
||||
};
|
||||
|
||||
void api::WebSocketServer::init() {}
|
||||
|
||||
/*
|
||||
* Controller state so we don't have to import headsocket stuff in our header
|
||||
*/
|
||||
struct WebSocketControllerState {
|
||||
std::shared_ptr<WebSocketServer> server;
|
||||
};
|
||||
|
||||
WebSocketController::WebSocketController(Controller *controller, uint16_t port) {
|
||||
this->controller = controller;
|
||||
|
||||
// create state
|
||||
this->state = new WebSocketControllerState();
|
||||
|
||||
// start server
|
||||
this->state->server = WebSocketServer::create(port);
|
||||
this->state->server->websocket = this;
|
||||
if (this->state->server->is_running()) {
|
||||
log_info("api::websocket", "server listening on port: {}", port);
|
||||
} else {
|
||||
log_warning("api::websocket", "server failed to listen on port: {}", port);
|
||||
}
|
||||
}
|
||||
|
||||
WebSocketController::~WebSocketController() {
|
||||
|
||||
// stop server
|
||||
this->state->server->stop();
|
||||
|
||||
// delete state
|
||||
delete this->state;
|
||||
}
|
||||
|
||||
void WebSocketController::free_socket() {
|
||||
this->state->server->stop();
|
||||
}
|
||||
|
||||
void WebSocketClient::on_accept() {
|
||||
web_socket_client::on_accept();
|
||||
|
||||
// get pointer to server
|
||||
auto srv = reinterpret_cast<WebSocketServer *>(server().get());
|
||||
if (!srv || !srv->websocket) {
|
||||
log_fatal("api::websocket", "on_accept has no server");
|
||||
}
|
||||
|
||||
// check for init
|
||||
state = new ClientState();
|
||||
srv->websocket->controller->init_state(state);
|
||||
|
||||
// log connection
|
||||
log_info("api::websocket", "client connected");
|
||||
}
|
||||
|
||||
void WebSocketClient::on_disconnect() {
|
||||
|
||||
// log disconnection
|
||||
log_info("api::websocket", "client disconnected");
|
||||
|
||||
// get pointer to server
|
||||
auto srv = reinterpret_cast<WebSocketServer *>(server().get());
|
||||
if (!srv || !srv->websocket) {
|
||||
log_fatal("api::websocket", "on_disconnect has no server");
|
||||
}
|
||||
|
||||
// clean up state
|
||||
srv->websocket->controller->free_state(state);
|
||||
delete state;
|
||||
state = nullptr;
|
||||
|
||||
// call super
|
||||
web_socket_client::on_disconnect();
|
||||
}
|
||||
|
||||
/*
|
||||
* This is where business actually happens, gets called on every datablock receive
|
||||
*/
|
||||
bool WebSocketClient::async_received_data(const data_block &db, uint8_t *ptr, size_t length) {
|
||||
|
||||
// get pointer to server
|
||||
auto srv = reinterpret_cast<WebSocketServer *>(server().get());
|
||||
if (!srv || !srv->websocket) {
|
||||
log_fatal("api::websocket", "received datablock without server");
|
||||
}
|
||||
|
||||
// check state
|
||||
if (!state) {
|
||||
log_fatal("api::websocket", "client with no state received datablock");
|
||||
}
|
||||
|
||||
// check datablock type
|
||||
switch (db.op) {
|
||||
case opcode::binary: {
|
||||
|
||||
// allocate buffers
|
||||
std::vector<char> in(ptr, ptr + length);
|
||||
std::vector<char> out;
|
||||
|
||||
// crypt in-data
|
||||
if (state->cipher) {
|
||||
state->cipher->crypt(reinterpret_cast<uint8_t *>(in.data()), in.size());
|
||||
}
|
||||
|
||||
// process request
|
||||
srv->websocket->controller->process_request(state, &in, &out);
|
||||
|
||||
// crypt out-data
|
||||
if (state->cipher) {
|
||||
state->cipher->crypt(reinterpret_cast<uint8_t *>(out.data()), out.size());
|
||||
}
|
||||
|
||||
// send answer
|
||||
push(out.data(), out.size());
|
||||
|
||||
// check for password change
|
||||
srv->websocket->controller->process_password_change(state);
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
log_warning("api::websocket", "datablock received with non-binary type");
|
||||
break;
|
||||
}
|
||||
|
||||
// always consume the datablock, nomnom
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+21
-21
@@ -1,21 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <thread>
|
||||
|
||||
namespace api {
|
||||
|
||||
struct WebSocketControllerState;
|
||||
class Controller;
|
||||
|
||||
class WebSocketController {
|
||||
public:
|
||||
|
||||
WebSocketController(Controller *controller, uint16_t port);
|
||||
~WebSocketController();
|
||||
|
||||
void free_socket();
|
||||
Controller *controller = nullptr;
|
||||
WebSocketControllerState *state = nullptr;
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <thread>
|
||||
|
||||
namespace api {
|
||||
|
||||
struct WebSocketControllerState;
|
||||
class Controller;
|
||||
|
||||
class WebSocketController {
|
||||
public:
|
||||
|
||||
WebSocketController(Controller *controller, uint16_t port);
|
||||
~WebSocketController();
|
||||
|
||||
void free_socket();
|
||||
Controller *controller = nullptr;
|
||||
WebSocketControllerState *state = nullptr;
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user