homebridge-xbox-tv 2.9.11-beta.21 → 2.9.11-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"displayName": "Xbox TV",
|
|
3
3
|
"name": "homebridge-xbox-tv",
|
|
4
|
-
"version": "2.9.11-beta.
|
|
4
|
+
"version": "2.9.11-beta.3",
|
|
5
5
|
"description": "Homebridge plugin (https://github.com/homebridge/homebridge) to control Xbox game consoles.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "grzegorz914",
|
package/src/localApi/message.js
CHANGED
|
@@ -12,9 +12,6 @@ class MESSAGE {
|
|
|
12
12
|
//structure
|
|
13
13
|
const structure = new Packets();
|
|
14
14
|
this.structure = structure[type];
|
|
15
|
-
|
|
16
|
-
//packet
|
|
17
|
-
this.packet = new Packets(this.structure);
|
|
18
15
|
};
|
|
19
16
|
|
|
20
17
|
getMsgType(type) {
|
|
@@ -98,7 +95,7 @@ class MESSAGE {
|
|
|
98
95
|
0x23: "titleLaunch",
|
|
99
96
|
channelStartRequest: Buffer.from('a026', 'hex'),
|
|
100
97
|
channelStartResponse: Buffer.from('a027', 'hex'),
|
|
101
|
-
|
|
98
|
+
0x28: "stopChannel",
|
|
102
99
|
0x29: "system",
|
|
103
100
|
disconnect: Buffer.from('802a', 'hex'),
|
|
104
101
|
0x2E: "titleTouch",
|
|
@@ -195,15 +192,17 @@ class MESSAGE {
|
|
|
195
192
|
|
|
196
193
|
// Lets decrypt the data when the payload is encrypted
|
|
197
194
|
if (packet.payloadProtected) {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
195
|
+
let structureProtected = new Packets(this.structure);
|
|
196
|
+
structureProtected = structureProtected[packet.type];
|
|
197
|
+
|
|
198
|
+
let payloadDecrypted = crypto.decrypt(packet.payloadProtected, crypto.encrypt(data.slice(0, 16), crypto.getIv()));
|
|
199
|
+
payloadDecrypted = new PacketStructure(payloadDecrypted);
|
|
201
200
|
|
|
202
201
|
packet.payloadDecrypted = new PacketStructure(payloadDecrypted).toBuffer();
|
|
203
202
|
packet.payloadProtected = {};
|
|
204
203
|
|
|
205
|
-
for (const name in
|
|
206
|
-
packet.payloadProtected[name] =
|
|
204
|
+
for (const name in structureProtected) {
|
|
205
|
+
packet.payloadProtected[name] = structureProtected[name].unpack(payloadDecrypted);
|
|
207
206
|
};
|
|
208
207
|
};
|
|
209
208
|
|
package/src/localApi/packets.js
CHANGED
|
@@ -161,7 +161,7 @@ class PACKETS {
|
|
|
161
161
|
|
|
162
162
|
const packets = {
|
|
163
163
|
powerOn: {
|
|
164
|
-
liveId: types.sgString(
|
|
164
|
+
liveId: types.sgString(),
|
|
165
165
|
},
|
|
166
166
|
json: {
|
|
167
167
|
json: types.sgString('{}')
|
|
@@ -227,9 +227,6 @@ class PACKETS {
|
|
|
227
227
|
channelTargetId: types.bytes(8, ''),
|
|
228
228
|
result: types.uInt32('0'),
|
|
229
229
|
},
|
|
230
|
-
channelStop: {
|
|
231
|
-
channelTargetId: types.bytes(8, ''),
|
|
232
|
-
},
|
|
233
230
|
acknowledge: {
|
|
234
231
|
lowWatermark: types.uInt32('0'),
|
|
235
232
|
processedList: types.sgList('processedList', []),
|
|
@@ -296,7 +293,7 @@ class PACKETS {
|
|
|
296
293
|
command: types.uInt32('0'),
|
|
297
294
|
},
|
|
298
295
|
powerOff: {
|
|
299
|
-
liveId: types.sgString(
|
|
296
|
+
liveId: types.sgString(),
|
|
300
297
|
},
|
|
301
298
|
disconnect: {
|
|
302
299
|
reason: types.uInt32('1'),
|
package/src/localApi/simple.js
CHANGED
|
@@ -12,8 +12,8 @@ class SIMPLE {
|
|
|
12
12
|
this.structure = structure[type];
|
|
13
13
|
|
|
14
14
|
//packet
|
|
15
|
-
|
|
16
|
-
this.structureProtected = this.structure.payloadProtected !== undefined ?
|
|
15
|
+
const packet = new Packets(this.structure);
|
|
16
|
+
this.structureProtected = this.structure.payloadProtected !== undefined ? packet[`${type}Protected`] : false;
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
set(key, value, isProtected = false) {
|
|
@@ -89,6 +89,19 @@ class SIMPLE {
|
|
|
89
89
|
case 'connectResponse':
|
|
90
90
|
packet = this.pack1(Buffer.from('cc01', 'hex'), packetStructure.toBuffer(), '2');
|
|
91
91
|
break;
|
|
92
|
+
case 'connectRequestProtected':
|
|
93
|
+
// Pad packet
|
|
94
|
+
if (packetStructure.toBuffer().length > 16) {
|
|
95
|
+
const padStart = packetStructure.toBuffer().length % 16;
|
|
96
|
+
const padTotal = (16 - padStart);
|
|
97
|
+
for (let paddingnum = (padStart + 1); paddingnum <= 16; paddingnum++) {
|
|
98
|
+
packetStructure.writeUInt8(padTotal);
|
|
99
|
+
};
|
|
100
|
+
};
|
|
101
|
+
let payloadEncrypted = crypto.encrypt(packetStructure.toBuffer(), crypto.getIv());
|
|
102
|
+
payloadEncrypted = new PacketStructure(payloadEncrypted)
|
|
103
|
+
packet = payloadEncrypted.toBuffer();
|
|
104
|
+
break;
|
|
92
105
|
default:
|
|
93
106
|
packet = packetStructure.toBuffer();
|
|
94
107
|
};
|
|
@@ -153,13 +166,15 @@ class SIMPLE {
|
|
|
153
166
|
packet.payloadProtected = packet.payloadProtected.slice(0, -32);
|
|
154
167
|
packet.signature = packet.payloadProtected.slice(-32);
|
|
155
168
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
169
|
+
let payloadDecrypted = crypto.decrypt(packet.payloadProtected, packet.iv).slice(0, packet.payloadProtectedLength);
|
|
170
|
+
payloadDecrypted = new PacketStructure(payloadDecrypted);
|
|
171
|
+
|
|
172
|
+
let structureProtected = new Packets(this.structure);
|
|
173
|
+
structureProtected = structureProtected[`${packet.type}Protected`];
|
|
159
174
|
packet.payloadProtected = {};
|
|
160
175
|
|
|
161
|
-
for (const name in
|
|
162
|
-
packet.payloadProtected[name] =
|
|
176
|
+
for (const name in structureProtected) {
|
|
177
|
+
packet.payloadProtected[name] = structureProtected[name].unpack(payloadDecrypted);
|
|
163
178
|
this.set('payloadProtected', packet.payloadProtected);
|
|
164
179
|
}
|
|
165
180
|
}
|
|
@@ -164,6 +164,17 @@ class STRUCTURE {
|
|
|
164
164
|
return int32;
|
|
165
165
|
};
|
|
166
166
|
|
|
167
|
+
readUInt64() {
|
|
168
|
+
let uint64 = 0;
|
|
169
|
+
try {
|
|
170
|
+
const rawData = this.readBytes(8);
|
|
171
|
+
uint64 = Buffer.from(rawData).readUIntBE(0, 8);
|
|
172
|
+
} catch (error) {
|
|
173
|
+
console.error(`Error reading unsigned 64-bit integer from packet: ${error}`);
|
|
174
|
+
}
|
|
175
|
+
return uint64;
|
|
176
|
+
};
|
|
177
|
+
|
|
167
178
|
toBuffer() {
|
|
168
179
|
return this.packet;
|
|
169
180
|
};
|
|
@@ -29,7 +29,7 @@ class XBOXLOCALAPI extends EventEmitter {
|
|
|
29
29
|
this.sequenceNumber = 1;
|
|
30
30
|
this.targetParticipantId = 0;
|
|
31
31
|
this.sourceParticipantId = 0;
|
|
32
|
-
this.
|
|
32
|
+
this.channelServerId = 0;
|
|
33
33
|
this.mediaRequestId = 0;
|
|
34
34
|
this.emitDevInfo = true;
|
|
35
35
|
|
|
@@ -47,7 +47,7 @@ class XBOXLOCALAPI extends EventEmitter {
|
|
|
47
47
|
const debug1 = this.debugLog ? this.emit('debug', `Received packet type hex: ${messageTypeHex}`) : false;
|
|
48
48
|
|
|
49
49
|
//check message type exist in types
|
|
50
|
-
const keysTypes = Object.keys(CONSTANTS.
|
|
50
|
+
const keysTypes = Object.keys(CONSTANTS.MediaTypes);
|
|
51
51
|
const keysTypesExist = keysTypes.includes(messageTypeHex);
|
|
52
52
|
|
|
53
53
|
if (!keysTypesExist) {
|
|
@@ -255,10 +255,10 @@ class XBOXLOCALAPI extends EventEmitter {
|
|
|
255
255
|
this.emit('stateChanged', power, volume, mute, mediaState, titleId, reference);
|
|
256
256
|
const debug1 = this.debugLog ? this.emit('debug', `Status changed, app Id: ${titleId}, reference: ${reference}`) : false;
|
|
257
257
|
};
|
|
258
|
-
//this.emit('channelStartRequest');
|
|
259
258
|
|
|
260
259
|
//start heart beat
|
|
261
260
|
const sendHeartBeat = !this.heartBeatConnection ? this.emit('heartBeat') : false;
|
|
261
|
+
this.emit('channelStartRequest');
|
|
262
262
|
}).on('channelStartRequest', async () => {
|
|
263
263
|
const debug = this.debugLog ? this.emit('debug', 'Channel start request received.') : false;
|
|
264
264
|
|
|
@@ -273,8 +273,8 @@ class XBOXLOCALAPI extends EventEmitter {
|
|
|
273
273
|
|
|
274
274
|
}).on('channelStartResponse', async (packet) => {
|
|
275
275
|
const debug = this.debugLog ? this.emit('debug', 'Channel start response received.') : false;
|
|
276
|
-
this.
|
|
277
|
-
const debug1 = this.debugLog ? this.emit('debug', `Channel server id: ${this.
|
|
276
|
+
this.channelServerId = packet.payloadProtected.result === 0 ? packet.payloadProtected.channelTargetId : 0;
|
|
277
|
+
const debug1 = this.debugLog ? this.emit('debug', `Channel server id: ${this.channelServerId}.`) : false;
|
|
278
278
|
}).on('heartBeat', () => {
|
|
279
279
|
if (this.heartBeatConnection) {
|
|
280
280
|
return;
|
|
@@ -349,7 +349,7 @@ class XBOXLOCALAPI extends EventEmitter {
|
|
|
349
349
|
const message = powerOn.pack(this.crypto);
|
|
350
350
|
await this.sendSocketMessage(message);
|
|
351
351
|
|
|
352
|
-
await new Promise(resolve => setTimeout(resolve,
|
|
352
|
+
await new Promise(resolve => setTimeout(resolve, 600));
|
|
353
353
|
resolve();
|
|
354
354
|
}
|
|
355
355
|
this.emit('disconnected', 'Power On failed, please try again.');
|
|
@@ -418,7 +418,7 @@ class XBOXLOCALAPI extends EventEmitter {
|
|
|
418
418
|
const gamepad = new MessagePacket('gamepad')
|
|
419
419
|
gamepad.set('timestamp', Buffer.from(`000${timestampNow.toString()}`, 'hex'))
|
|
420
420
|
gamepad.set('buttons', CONSTANTS.GamePadCommands[command]);
|
|
421
|
-
gamepad.setChannel(this.
|
|
421
|
+
gamepad.setChannel(this.channelServerId);
|
|
422
422
|
const message = gamepad.pack(this.crypto, this.getSequenceNumber(), this.targetParticipantId, this.sourceParticipantId)
|
|
423
423
|
await this.sendSocketMessage(message)
|
|
424
424
|
|
|
@@ -427,7 +427,7 @@ class XBOXLOCALAPI extends EventEmitter {
|
|
|
427
427
|
const gamepadUnpress = MessagePacket('gamepad')
|
|
428
428
|
gamepadUnpress.set('timestamp', Buffer.from(`000${timestamp.toString()}`, 'hex'))
|
|
429
429
|
gamepadUnpress.set('buttons', 0);
|
|
430
|
-
gamepadUnpress.setChannel(this.
|
|
430
|
+
gamepadUnpress.setChannel(this.channelServerId);
|
|
431
431
|
const message = gamepadUnpress.pack(this.crypto, this.getSequenceNumber(), this.targetParticipantId, this.sourceParticipantId);
|
|
432
432
|
await this.sendSocketMessage(message);
|
|
433
433
|
resolve();
|