mirror of
https://github.com/jwetzell/psn-js.git
synced 2026-09-10 08:39:39 +00:00
completely rework the parsing structure because I now realize how frames work
This commit is contained in:
+1
-4
@@ -6,13 +6,10 @@ const client = dgram.createSocket('udp4');
|
|||||||
const psnDecoder = new Decoder();
|
const psnDecoder = new Decoder();
|
||||||
|
|
||||||
client.on('listening', () => {
|
client.on('listening', () => {
|
||||||
const address = client.address();
|
|
||||||
client.setBroadcast(true);
|
|
||||||
client.setMulticastTTL(128);
|
|
||||||
client.addMembership('236.10.10.10');
|
client.addMembership('236.10.10.10');
|
||||||
});
|
});
|
||||||
|
|
||||||
client.on('message', (message, remote) => {
|
client.on('message', (message) => {
|
||||||
psnDecoder.decode(message);
|
psnDecoder.decode(message);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,71 +1,141 @@
|
|||||||
/* eslint-disable no-param-reassign */
|
const PacketParser = require('./parsers/index.js');
|
||||||
const { Parser } = require('binary-parser');
|
const InfoPacket = require('./models/InfoPacket.js');
|
||||||
const psnParser = require('./parsers/index.js');
|
const DataPacket = require('./models/DataPacket.js');
|
||||||
const { PSN_DATA_TRACKER_FIELD_PARSER } = require('./parsers/data/index.js');
|
|
||||||
|
|
||||||
const DATA_PACKET = 0x6755;
|
const DATA_PACKET = 0x6755;
|
||||||
const INFO_PACKET = 0x6756;
|
const INFO_PACKET = 0x6756;
|
||||||
|
|
||||||
class Decoder {
|
class Decoder {
|
||||||
|
lastInfoPacketHeader;
|
||||||
|
|
||||||
|
lastDataPacketHeader;
|
||||||
|
|
||||||
|
infoPacketFrames = {};
|
||||||
|
|
||||||
|
dataPacketFrames = {};
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.info = {};
|
this.info = {
|
||||||
|
trackers: {},
|
||||||
|
};
|
||||||
|
|
||||||
this.data = {
|
this.data = {
|
||||||
trackers: {},
|
trackers: {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateInfo(framePackets) {
|
||||||
|
console.log('info update');
|
||||||
|
framePackets.forEach((packet) => {
|
||||||
|
packet.subChunks?.forEach((subChunk) => {
|
||||||
|
if (subChunk.id === 0x0001) {
|
||||||
|
// NOTE(jwetzell): system name subChunk
|
||||||
|
this.info.system_name = subChunk.system_name;
|
||||||
|
} else if (subChunk.id === 0x0002) {
|
||||||
|
subChunk.trackers?.forEach((tracker) => {
|
||||||
|
if (this.info.trackers[tracker.id] === undefined) {
|
||||||
|
this.info.trackers[tracker.id] = {};
|
||||||
|
}
|
||||||
|
this.info.trackers[tracker.id].name = tracker.tracker_name.tracker_name;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
updateData(framePackets) {
|
||||||
|
console.log('data update');
|
||||||
|
framePackets.forEach((packet) => {
|
||||||
|
packet.subChunks?.forEach((subChunk) => {
|
||||||
|
if (subChunk.id === 0x0001) {
|
||||||
|
subChunk.trackers?.forEach((tracker) => {
|
||||||
|
if (this.data.trackers[tracker.id] === undefined) {
|
||||||
|
this.data.trackers[tracker.id] = {};
|
||||||
|
}
|
||||||
|
tracker.fields?.forEach((field) => {
|
||||||
|
Object.entries(field).forEach(([key, value]) => {
|
||||||
|
this.data.trackers[tracker.id][key] = value;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(jwetzell): support multiple frame packets this might require some rework of how chunks are assumed
|
// TODO(jwetzell): support multiple frame packets this might require some rework of how chunks are assumed
|
||||||
decode(packetBuf) {
|
decode(packetBuf) {
|
||||||
const packet = psnParser(packetBuf);
|
const packet = PacketParser(packetBuf);
|
||||||
|
|
||||||
if (packet.id === INFO_PACKET) {
|
if (packet.id === INFO_PACKET) {
|
||||||
if (packet.chunk_data?.tracker_list?.trackers) {
|
const infoPacket = new InfoPacket(packet);
|
||||||
packet.chunk_data?.tracker_list?.trackers.forEach((tracker) => {
|
if (infoPacket) {
|
||||||
if (this.data.trackers[tracker.id] === undefined) {
|
if (infoPacket.subChunks?.length > 0) {
|
||||||
this.data.trackers[tracker.id] = {};
|
const currentInfoPacketHeader = infoPacket.getHeaderPacket();
|
||||||
|
if (!currentInfoPacketHeader) {
|
||||||
|
// NOTE(jwetzell): not sure that info packets without a header subchunk are valid?
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
this.data.trackers[tracker.id] = {
|
|
||||||
...this.data.trackers[tracker.id],
|
const systemSubChunk = infoPacket.getSystemPacket();
|
||||||
id: tracker.id,
|
if (!systemSubChunk) {
|
||||||
name: tracker.tracker_name.tracker_name,
|
// NOTE(jwetzell): not sure that info packets without a system subchunk are valid?
|
||||||
};
|
return;
|
||||||
});
|
}
|
||||||
|
|
||||||
|
if (this.infoPacketFrames[currentInfoPacketHeader.frame_id] === undefined) {
|
||||||
|
this.infoPacketFrames[currentInfoPacketHeader.frame_id] = [];
|
||||||
|
}
|
||||||
|
this.infoPacketFrames[currentInfoPacketHeader.frame_id].push(infoPacket);
|
||||||
|
|
||||||
|
if (
|
||||||
|
this.infoPacketFrames[currentInfoPacketHeader.frame_id].length ===
|
||||||
|
currentInfoPacketHeader.frame_packet_count
|
||||||
|
) {
|
||||||
|
console.log('found complete info frame');
|
||||||
|
this.updateInfo(this.infoPacketFrames[currentInfoPacketHeader.frame_id]);
|
||||||
|
delete this.infoPacketFrames[currentInfoPacketHeader.frame_id];
|
||||||
|
} else {
|
||||||
|
// TODO(jwetzell): need to compute whether a frame is complete
|
||||||
|
console.log('compute frame completion');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.lastInfoPacketHeader = currentInfoPacketHeader;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (packet.id === DATA_PACKET) {
|
} else if (packet.id === DATA_PACKET) {
|
||||||
if (packet.chunk_data?.tracker_list?.trackers) {
|
const dataPacket = new DataPacket(packet);
|
||||||
packet.chunk_data?.tracker_list?.trackers.forEach((tracker) => {
|
if (dataPacket) {
|
||||||
if (this.data.trackers[tracker.id] === undefined) {
|
if (dataPacket.subChunks?.length > 0) {
|
||||||
this.data.trackers[tracker.id] = {};
|
const currentDataPacketHeader = dataPacket.getHeaderPacket();
|
||||||
|
if (!currentDataPacketHeader) {
|
||||||
|
// NOTE(jwetzell): not sure that info packets without a header subchunk are valid?
|
||||||
|
console.error('data packet with no header');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
this.data.trackers[tracker.id] = {
|
|
||||||
...this.data.trackers[tracker.id],
|
|
||||||
id: tracker.id,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (tracker.data && tracker.data_len > 0) {
|
if (this.dataPacketFrames[currentDataPacketHeader.frame_id] === undefined) {
|
||||||
const fields = new Parser()
|
this.dataPacketFrames[currentDataPacketHeader.frame_id] = [];
|
||||||
.array('fields', {
|
|
||||||
type: PSN_DATA_TRACKER_FIELD_PARSER,
|
|
||||||
lengthInBytes: tracker.data_len,
|
|
||||||
})
|
|
||||||
.parse(tracker.data);
|
|
||||||
if (fields.fields) {
|
|
||||||
fields.fields.forEach((field) => {
|
|
||||||
if (field.data) {
|
|
||||||
this.data.trackers[tracker.id] = {
|
|
||||||
...this.data.trackers[tracker.id],
|
|
||||||
...field.data,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
tracker.fields = fields.fields;
|
|
||||||
delete tracker.data;
|
|
||||||
}
|
}
|
||||||
});
|
this.dataPacketFrames[currentDataPacketHeader.frame_id].push(dataPacket);
|
||||||
|
|
||||||
|
if (
|
||||||
|
this.dataPacketFrames[currentDataPacketHeader.frame_id].length ===
|
||||||
|
currentDataPacketHeader.frame_packet_count
|
||||||
|
) {
|
||||||
|
console.log('found complete data frame');
|
||||||
|
this.updateData(this.dataPacketFrames[currentDataPacketHeader.frame_id]);
|
||||||
|
delete this.dataPacketFrames[currentDataPacketHeader.frame_id];
|
||||||
|
} else {
|
||||||
|
// TODO(jwetzell): need to compute whether a frame is complete
|
||||||
|
console.log('compute data frame completion');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.lastDataPacketHeader = currentDataPacketHeader;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return packet;
|
console.log(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
/* eslint-disable no-case-declarations */
|
||||||
|
/* eslint-disable no-param-reassign */
|
||||||
|
const { Parser } = require('binary-parser');
|
||||||
|
const { ChunkParser, HeaderParser } = require('../parsers/common');
|
||||||
|
const { DataTrackerListParser, DataTrackerFieldParser } = require('../parsers/data');
|
||||||
|
|
||||||
|
class DataPacket {
|
||||||
|
constructor(packet) {
|
||||||
|
this.packet = packet;
|
||||||
|
this.subChunks = [];
|
||||||
|
if (this.packet.has_subchunks) {
|
||||||
|
this.subChunkParser = new Parser().array('chunks', {
|
||||||
|
type: ChunkParser,
|
||||||
|
lengthInBytes: this.packet.data_len,
|
||||||
|
});
|
||||||
|
this.subChunks = this.subChunkParser.parse(this.packet.chunk_data)?.chunks;
|
||||||
|
this.subChunks = this.subChunks.map((subChunk) => {
|
||||||
|
let populatedSubChunk = {};
|
||||||
|
populatedSubChunk.data_len_valid = subChunk.chunk_data.length === subChunk.data_len;
|
||||||
|
switch (subChunk.id) {
|
||||||
|
case 0:
|
||||||
|
populatedSubChunk = {
|
||||||
|
...subChunk,
|
||||||
|
...HeaderParser.parse(subChunk.chunk_data),
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
const dataTrackerList = DataTrackerListParser.parse(subChunk.chunk_data);
|
||||||
|
dataTrackerList.trackers?.forEach((tracker) => {
|
||||||
|
if (tracker.data && tracker.data_len > 0) {
|
||||||
|
const fields = new Parser()
|
||||||
|
.array('fields', {
|
||||||
|
type: DataTrackerFieldParser,
|
||||||
|
lengthInBytes: tracker.data_len,
|
||||||
|
})
|
||||||
|
.parse(tracker.data);
|
||||||
|
if (fields.fields) {
|
||||||
|
tracker.fields = [];
|
||||||
|
fields.fields.forEach((field) => {
|
||||||
|
tracker.fields.push(field.data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
populatedSubChunk = {
|
||||||
|
...subChunk,
|
||||||
|
...dataTrackerList,
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
populatedSubChunk = {
|
||||||
|
...subChunk,
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return populatedSubChunk;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaderPacket() {
|
||||||
|
return this.subChunks.find((subChunk) => subChunk.id === 0x0000);
|
||||||
|
}
|
||||||
|
|
||||||
|
getSystemPacket() {
|
||||||
|
return this.subChunks.find((subChunk) => subChunk.id === 0x0001);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = DataPacket;
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
const { Parser } = require('binary-parser');
|
||||||
|
const { ChunkParser, HeaderParser } = require('../parsers/common');
|
||||||
|
const { InfoSystemNameParser, InfoTrackerListParser } = require('../parsers/info');
|
||||||
|
|
||||||
|
class InfoPacket {
|
||||||
|
constructor(packet) {
|
||||||
|
this.packet = packet;
|
||||||
|
this.subChunks = [];
|
||||||
|
if (this.packet.has_subchunks) {
|
||||||
|
this.subChunkParser = new Parser().array('chunks', {
|
||||||
|
type: ChunkParser,
|
||||||
|
lengthInBytes: this.packet.data_len,
|
||||||
|
});
|
||||||
|
this.subChunks = this.subChunkParser.parse(this.packet.chunk_data)?.chunks;
|
||||||
|
this.subChunks = this.subChunks.map((subChunk) => {
|
||||||
|
let populatedSubChunk = {};
|
||||||
|
populatedSubChunk.data_len_valid = subChunk.chunk_data.length === subChunk.data_len;
|
||||||
|
switch (subChunk.id) {
|
||||||
|
case 0:
|
||||||
|
populatedSubChunk = {
|
||||||
|
...subChunk,
|
||||||
|
...HeaderParser.parse(subChunk.chunk_data),
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
populatedSubChunk = {
|
||||||
|
...subChunk,
|
||||||
|
...InfoSystemNameParser.parse(subChunk.chunk_data),
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
populatedSubChunk = {
|
||||||
|
...subChunk,
|
||||||
|
...InfoTrackerListParser.parse(subChunk.chunk_data),
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
populatedSubChunk = {
|
||||||
|
...subChunk,
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return populatedSubChunk;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaderPacket() {
|
||||||
|
return this.subChunks.find((subChunk) => subChunk.id === 0x0000);
|
||||||
|
}
|
||||||
|
|
||||||
|
getSystemPacket() {
|
||||||
|
return this.subChunks.find((subChunk) => subChunk.id === 0x0001);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = InfoPacket;
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "posistagenet",
|
"name": "@jwetzell/posistagenet",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
const Parser = require('binary-parser').Parser;
|
const Parser = require('binary-parser').Parser;
|
||||||
|
|
||||||
const PSN_PACKET_HEADER_PARSER = new Parser()
|
const ChunkParser = new Parser()
|
||||||
.uint16le('id')
|
.uint16le('id')
|
||||||
.uint16le('data_len', {
|
.uint16le('data_len', {
|
||||||
formatter: (item) => {
|
formatter: (item) => {
|
||||||
@@ -15,6 +15,11 @@ const PSN_PACKET_HEADER_PARSER = new Parser()
|
|||||||
return binary.charAt(0) === '1';
|
return binary.charAt(0) === '1';
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
.buffer('chunk_data', {
|
||||||
|
length: 'data_len',
|
||||||
|
});
|
||||||
|
|
||||||
|
const HeaderParser = new Parser()
|
||||||
.uint64le('packet_timestamp')
|
.uint64le('packet_timestamp')
|
||||||
.uint8('version_high')
|
.uint8('version_high')
|
||||||
.uint8('version_low')
|
.uint8('version_low')
|
||||||
@@ -23,5 +28,6 @@ const PSN_PACKET_HEADER_PARSER = new Parser()
|
|||||||
.seek(4);
|
.seek(4);
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
PSN_PACKET_HEADER_PARSER,
|
ChunkParser,
|
||||||
|
HeaderParser,
|
||||||
};
|
};
|
||||||
|
|||||||
+4
-26
@@ -1,7 +1,6 @@
|
|||||||
const { PSN_PACKET_HEADER_PARSER } = require('../common');
|
|
||||||
const Parser = require('binary-parser').Parser;
|
const Parser = require('binary-parser').Parser;
|
||||||
|
|
||||||
const PSN_DATA_TRACKER_FIELD_PARSER = new Parser()
|
const DataTrackerFieldParser = new Parser()
|
||||||
.uint16le('id')
|
.uint16le('id')
|
||||||
.uint16le('data_len', {
|
.uint16le('data_len', {
|
||||||
formatter: (item) => {
|
formatter: (item) => {
|
||||||
@@ -48,30 +47,9 @@ const PSN_DATA_TRACKER_PARSER = new Parser()
|
|||||||
length: 'data_len',
|
length: 'data_len',
|
||||||
});
|
});
|
||||||
|
|
||||||
const PSN_DATA_TRACKER_LIST_PARSER = new Parser()
|
const DataTrackerListParser = new Parser().array('trackers', { type: PSN_DATA_TRACKER_PARSER, readUntil: 'eof' });
|
||||||
.uint16le('id')
|
|
||||||
.uint16le('data_len', {
|
|
||||||
formatter: (item) => {
|
|
||||||
const binary = item.toString(2).padStart(16, '0');
|
|
||||||
return Number.parseInt(binary.substring(1), 2);
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.seek(-2)
|
|
||||||
.uint16le('has_subchunks', {
|
|
||||||
formatter: (item) => {
|
|
||||||
const binary = item.toString(2).padStart(16, '0');
|
|
||||||
return binary.charAt(0) === '1';
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.array('trackers', { type: PSN_DATA_TRACKER_PARSER, lengthInBytes: 'data_len' });
|
|
||||||
|
|
||||||
const PSN_DATA_CHUNK_DATA_PARSER = new Parser()
|
|
||||||
.nest('packet_header', { type: PSN_PACKET_HEADER_PARSER })
|
|
||||||
.nest('tracker_list', { type: PSN_DATA_TRACKER_LIST_PARSER });
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
PSN_DATA_CHUNK_DATA_PARSER,
|
DataTrackerFieldParser,
|
||||||
PSN_DATA_TRACKER_FIELD_PARSER,
|
DataTrackerListParser,
|
||||||
PSN_DATA_TRACKER_LIST_PARSER,
|
|
||||||
PSN_DATA_TRACKER_PARSER,
|
|
||||||
};
|
};
|
||||||
|
|||||||
+1
-10
@@ -1,6 +1,3 @@
|
|||||||
const { PSN_DATA_CHUNK_DATA_PARSER } = require('./data');
|
|
||||||
const { PSN_INFO_CHUNK_DATA_PARSER } = require('./info');
|
|
||||||
|
|
||||||
const Parser = require('binary-parser').Parser;
|
const Parser = require('binary-parser').Parser;
|
||||||
|
|
||||||
const PSN_PACKET_PARSER = new Parser()
|
const PSN_PACKET_PARSER = new Parser()
|
||||||
@@ -18,12 +15,6 @@ const PSN_PACKET_PARSER = new Parser()
|
|||||||
return binary.charAt(0) === '1';
|
return binary.charAt(0) === '1';
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.choice('chunk_data', {
|
.buffer('chunk_data', { readUntil: 'eof' });
|
||||||
tag: 'id',
|
|
||||||
choices: {
|
|
||||||
0x6755: PSN_DATA_CHUNK_DATA_PARSER,
|
|
||||||
0x6756: PSN_INFO_CHUNK_DATA_PARSER,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = (buffer) => PSN_PACKET_PARSER.parse(buffer);
|
module.exports = (buffer) => PSN_PACKET_PARSER.parse(buffer);
|
||||||
|
|||||||
+4
-44
@@ -1,24 +1,5 @@
|
|||||||
const { PSN_PACKET_HEADER_PARSER } = require('../common');
|
|
||||||
|
|
||||||
const Parser = require('binary-parser').Parser;
|
const Parser = require('binary-parser').Parser;
|
||||||
|
|
||||||
const PSN_INFO_SYSTEM_NAME_PARSER = new Parser()
|
|
||||||
.uint16le('id')
|
|
||||||
.uint16le('data_len', {
|
|
||||||
formatter: (item) => {
|
|
||||||
const binary = item.toString(2).padStart(16, '0');
|
|
||||||
return Number.parseInt(binary.substring(1), 2);
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.seek(-2)
|
|
||||||
.uint16le('has_subchunks', {
|
|
||||||
formatter: (item) => {
|
|
||||||
const binary = item.toString(2).padStart(16, '0');
|
|
||||||
return binary.charAt(0) === '1';
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.string('system_name', { length: 'data_len' });
|
|
||||||
|
|
||||||
const PSN_INFO_TRACKER_NAME_PARSER = new Parser()
|
const PSN_INFO_TRACKER_NAME_PARSER = new Parser()
|
||||||
.uint16le('id')
|
.uint16le('id')
|
||||||
.uint16le('data_len', {
|
.uint16le('data_len', {
|
||||||
@@ -55,32 +36,11 @@ const PSN_INFO_TRACKER_PARSER = new Parser()
|
|||||||
type: PSN_INFO_TRACKER_NAME_PARSER,
|
type: PSN_INFO_TRACKER_NAME_PARSER,
|
||||||
});
|
});
|
||||||
|
|
||||||
const PSN_INFO_TRACKER_LIST_PARSER = new Parser()
|
const InfoSystemNameParser = new Parser().string('system_name', { greedy: true });
|
||||||
.uint16le('id')
|
|
||||||
.uint16le('data_len', {
|
|
||||||
formatter: (item) => {
|
|
||||||
const binary = item.toString(2).padStart(16, '0');
|
|
||||||
return Number.parseInt(binary.substring(1), 2);
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.seek(-2)
|
|
||||||
.uint16le('has_subchunks', {
|
|
||||||
formatter: (item) => {
|
|
||||||
const binary = item.toString(2).padStart(16, '0');
|
|
||||||
return binary.charAt(0) === '1';
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.array('trackers', { type: PSN_INFO_TRACKER_PARSER, lengthInBytes: 'data_len' });
|
|
||||||
|
|
||||||
const PSN_INFO_CHUNK_DATA_PARSER = new Parser()
|
const InfoTrackerListParser = new Parser().array('trackers', { type: PSN_INFO_TRACKER_PARSER, readUntil: 'eof' });
|
||||||
.nest('packet_header', { type: PSN_PACKET_HEADER_PARSER })
|
|
||||||
.nest('system_name', { type: PSN_INFO_SYSTEM_NAME_PARSER })
|
|
||||||
.nest('tracker_list', { type: PSN_INFO_TRACKER_LIST_PARSER });
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
PSN_INFO_SYSTEM_NAME_PARSER,
|
InfoSystemNameParser,
|
||||||
PSN_INFO_TRACKER_NAME_PARSER,
|
InfoTrackerListParser,
|
||||||
PSN_INFO_TRACKER_PARSER,
|
|
||||||
PSN_INFO_TRACKER_LIST_PARSER,
|
|
||||||
PSN_INFO_CHUNK_DATA_PARSER,
|
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user