Let us start here, from square one. No, from Zero!

This commit is contained in:
smpn2
2022-09-30 22:45:51 +09:00
parent 14215af0d1
commit b7a60638a4
1005 changed files with 303069 additions and 0 deletions
@@ -0,0 +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);
}
}
@@ -0,0 +1,11 @@
part of spiceapi;
class APIError implements Exception {
String cause;
APIError(this.cause);
@override
String toString() {
return this.cause;
}
}
@@ -0,0 +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];
}
}
}
@@ -0,0 +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);
}
}
@@ -0,0 +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;
}
}
@@ -0,0 +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);
}
@@ -0,0 +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);
}
@@ -0,0 +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);
}
@@ -0,0 +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);
}
@@ -0,0 +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);
}
@@ -0,0 +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);
}
@@ -0,0 +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];
});
}
@@ -0,0 +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;
});
}
@@ -0,0 +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);
}
@@ -0,0 +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];
});
}
@@ -0,0 +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);
}