From f493ecd076e60c400916a6c863aa366c0b55b73b Mon Sep 17 00:00:00 2001 From: neiviv-ui Date: Wed, 14 Jul 2021 18:31:10 +0200 Subject: [PATCH] initial commit --- const.js | 98 ++++++++++++ index.html | 21 +++ index.js | 233 ++++++++++++++++++++++++++++ internal.js | 428 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 780 insertions(+) create mode 100644 const.js create mode 100644 index.html create mode 100644 index.js create mode 100644 internal.js diff --git a/const.js b/const.js new file mode 100644 index 0000000..341e118 --- /dev/null +++ b/const.js @@ -0,0 +1,98 @@ +"use strict"; +const p7PacketType = { + 'command': 0x01, + 'data': 0x02, + 'roleswap': 0x03, + 'check': 0x05, + 'ack': 0x06, + 'screenCast': 0x0B, /* Not supported yet */ + 'error': 0x15, + 'terminate': 0x18 +}; + +const sysCommandSubtype = { + /* System commands */ + 'restartReset': 0x00, + 'getDeviceInfo': 0x01, + 'setLinkSettings': 0x02, /* Probably unavailable since we are using USB */ +}; + +const mcsCommandSubtype = { + /* MCS commands */ + 'createDirectory': 0x20, + 'deleteDirectory': 0x21, + 'renameDirectory': 0x22, + 'changeWorkingDirectory': 0x23, + 'fileTransferRequest': 0x24, + 'fileTransfer': 0x25, + 'deleteFile': 0x26, + 'renameFile': 0x27, + 'copyFile': 0x28, + 'fileTransferAllRequest': 0x29, + 'unknownResetMCS': 0x2A, /* TODO: TEST */ + 'capacityTransmitRequest': 0x2B, + 'capacityTransmit': 0x2C, + 'fileInfoTransferAllRequest': 0x2D, + 'fileInfoTransfer': 0x2E, + 'ramImageTransferRequest': 0x2F, + 'ramImageTransfer': 0x30, + 'setupEntryTransferRequest': 0x31, + 'setupEntryTransfer': 0x32, + 'setupEntryTransferAllRequest': 0x33, +}; + +const flashCommandSubtype = { + /* Flash commands */ + 'createDirectory': 0x40, + 'deleteDirectory': 0x41, + 'renameDirectory': 0x42, + 'changeDirectory': 0x43, + 'fileTransferRequest': 0x44, + 'fileTransfer': 0x45, + 'deleteFile': 0x46, + 'renameFile': 0x47, + 'copyFile': 0x48, + 'fileTransferAllRequest': 0x49, + 'unknownResetFlash': 0x4A, /* TODO: TEST*/ + 'capacityTransmitRequest': 0x4B, + 'capacityTransmit': 0x4C, + 'fileInfoTransferAllRequest': 0x4D, + 'fileInfoTransfer': 0x4E, + 'flashImageTransferRequest': 0x4F, + 'flashImageTransfer': 0x50, + 'optimizeFilesystem': 0x51 + // 'osUpdateRelated': [0x52 --> 0x57] ? +}; + +const dataSubtype = {}; /* Theorically always the same as the command packet it succeeds to */ + +const roleswapSubtype = { + 'default': 0x00 +}; + +const checkSubtype = { + 'initialization': 0x00, + 'default': 0x01 +}; + +const ackSubtype = { + 'default': 0x00, + 'overwriteYes': 0x01, + 'extendedAck': 0x02 +}; + +const errorSubtype = { + 'default': 0x00, + 'resendRequest': 0x01, + 'overwriteRequest': 0x02, + 'overwriteNo': 0x03, + 'overwriteImpossible': 0x04, + 'memoryFull': 0x05 +}; + +const terminateSubtype = { + 'default': 0x00, + 'userRequest': 0x01, + 'timeouts': 0x02, + 'overwriteRequest': 0x03 +}; \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..16cf759 --- /dev/null +++ b/index.html @@ -0,0 +1,21 @@ + + + + + + + Document + + + +

Hello

+

+ + +

+ + + + + + \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000..12c6127 --- /dev/null +++ b/index.js @@ -0,0 +1,233 @@ +"use strict"; +let device = undefined; +let fileList = undefined; + + +let p7SendBuffer = new Uint8Array(1032); //Max send buffer size +let p7ReceiveBuffer = new Uint8Array(1032 + 1); //Max receive buffer size + +let p7ReceivedPacketInfo = { + 'type' : undefined, + 'subtype' : undefined, + 'extended' : undefined, + 'dataSize' : undefined, + 'data' : undefined, + 'checksum' : undefined, + 'packetSize': undefined +}; + + +let p7DeviceInformation = { + 'hardwareIdentifier' : undefined, + 'processorIdentifier' : undefined, + 'preprogrammedROMCapacity' : undefined, + 'flashROMCapacity' : undefined, + 'ramCapacity' : undefined, + 'prepogrammedROMVersion' : undefined, + 'bootCodeVersion' : undefined, + 'bootCodeOffset' : undefined, + 'bootCodeSize' : undefined, + 'osCodeVersion' : undefined, + 'osCodeOffset' : undefined, + 'osCodeSize' : undefined, + 'protocolVersion' : undefined, + 'productID' : undefined, + 'nameSetByUserInSystem' : undefined +}; + +var fls0 = []; +var crd0 = []; + +/* List the files of a filesystem (fls0, crd0...) */ +/* Return 0 on success and the error on failure */ +async function list(fileSystem) { + let err = 0; + + //Send a command packet requesting the calculator to list it's file (in the flash memory) and receive the response packet + if (err = await p7SendExtendedCommandPacket(flashCommandSubtype.fileInfoTransferAllRequest, 0, 0, 0, ["", "", "", "", fileSystem, ""])) + return "Couldn't send the command packet to the calculator : " + err; + + //Send a roleswap packet so that the calculator can become the active side and send the list of it's files (in flash memory) + if (err = await p7SendBasicPacket(p7PacketType.roleswap, roleswapSubtype.default)) + return "Couldn't send the roleswap packet : " + err; + + //Reset the list since the calculator send each time all it's elements + window[fileSystem] = []; + + //Receive and parse the file list + while(p7ReceivedPacketInfo.type !== p7PacketType.roleswap) { + window[fileSystem].push(createFileObject(String.fromCharCode.apply(null, p7CommandPacketGetField(4)), String.fromCharCode.apply(null, p7CommandPacketGetField(0)), String.fromCharCode.apply(null, p7CommandPacketGetField(1)), p7CommandPacketGetFilesize())); + + if (err = await p7SendBasicPacket(p7PacketType.ack, ackSubtype.default)) + return "Couldn't send the acknowledgement packet : " + err; + } + return 0; +}; + +async function createDirectory(name, filesystem) { + let err = 0; + //Send a command packet which asks the calculator to create a directory + if (err = await p7SendExtendedCommandPacket(flashCommandSubtype.createDirectory, 0, 0, 0, [name, "", "", "", filesystem, ""])) + return 'Couldn\'t send the command : ' + err; + + //Check the response packet type + if (p7ReceivedPacketInfo.type === p7PacketType.ack) + return 0; + return "The response packet wasn't as expected."; +} + +async function deleteDirectory(name, filesystem) { + let err = 0; + //Send a command packet which asks the calculator to delete a directory + if (err = await p7SendExtendedCommandPacket(flashCommandSubtype.deleteDirectory, 0, 0, 0, [name, "", "", "", filesystem, ""])) + return 'Couldn\'t send the command : ' + err; + + //Check the response packet type + if (p7ReceivedPacketInfo.type === p7PacketType.ack) + return 0; + return "The response packet wasn't as expected."; +} + +/* Doesn't work ?? */ +async function renameDirectory(oldName, newName, filesystem) { + let err = 0; + //Send a command packet which asks the calculator to delete a directory + if (err = await p7SendExtendedCommandPacket(flashCommandSubtype.renameDirectory, 0, 0, 0, [oldName, newName, "", "", filesystem, ""])) + return 'Couldn\'t send the command : ' + err; + + //check the response packet type + if (p7ReceivedPacketInfo.type === p7PacketType.ack) + return 0; + return "The response packet wasn't as expected."; +} + +/* Doesn't work ?? */ +async function changeDirectory(name, filesystem) { + let err = 0; + //Send a command packet which asks the calculator change working directory + if (err = await p7SendExtendedCommandPacket(flashCommandSubtype.changeDirectory, 0, 0, 0, [name, "", "", "", filesystem, ""])) + return 'Couldn\'t send the command : ' + err; + + if (p7ReceivedPacketInfo.type === p7PacketType.ack) + return 0; + return "The response packet wasn't as expected."; +} + +/* TODO: finish it */ +async function fileTransfer(file, path, fileSystem) { + let err = 0; + console.log('Sending file transfer request'); + //Send a command packet which asks the calculator if we can send a file + if (err = await p7SendExtendedCommandPacket(flashCommandSubtype.fileTransfer, 0, 0, file.size, [path, file.name, "", "", fileSystem, ""])) + return 'Couldn\'t send the command : ' + err; + + console.log(p7SendBuffer.slice(0, 64)); + if (p7ReceivedPacketInfo.type === p7PacketType.error && p7ReceivedPacketInfo.subtype === errorSubtype.overwriteRequest) { + if (err = (confirm('The file already exists, overwrite ?')) ? await p7SendBasicPacket(p7PacketType.ack, ackSubtype.overwriteYes) : await p7SendBasicPacket(p7PacketType.error, errorSubtype.overwriteNo)) + return 'Couldn\'t send the overwrite response packet : ' + err; + if (p7ReceivedPacketInfo.type === p7PacketType.error && p7ReceivedPacketInfo.subtype === errorSubtype.overwriteImpossible) + return 'The calculator couldn\'t overwrite the file : ' + err; + } + + console.log('Sending data'); + if (err = await p7SendData(flashCommandSubtype.fileTransfer, content)) + return 'Couldn\'t send the file : ' + err; + + if (p7ReceivedPacketInfo.type ==! p7PacketType.ack) + return "unexpected response packet"; + return 0; +} + +/* TODO: add promise system */ +function getCapacity(fileSystem) { + return new Promise((resolve, reject) => { + let err = 0; + //Send a command packet requesting the calculator to send it's capacity (free memory) + if (err = p7SendExtendedCommandPacket(flashCommandSubtype.capacityTransmitRequest, 0, 0, 0, ["", "", "", "", fileSystem, ""])) + reject('Couldn\'t send the command packet to the calculator : ' + err); + + //Send a roleswap packet so that the calculator can become the active side and send it's capacity + if (err = p7SendBasicPacket(p7PacketType.roleswap, roleswapSubtype.default)) + reject('Couldn\'t send the roleswap packet : ' + err); + + let freeMemory = p7CommandPacketGetFilesize(); + + //Send an acknowledgement packet and receive the response packet + if (err = p7SendBasicPacket(p7PacketType.ack, roleswapSubtype.default)) + reject('Couldn\'t send the acknowledgement packet : ' + err); + + if (p7ReceivedPacketInfo.type !== p7PacketType.roleswap) + reject('The calculator didn\'t respond with the expected packet : ' + p7ReceivedPacketInfo.type + ' (packet type) ' + p7ReceivedPacketInfo.subtype + ' (packet subtype).'); + + //Everything went right + resolve(freeMemory); + }); +}; + + + +async function optimize(fileSystem) { + let err = 0 + if (err = await p7SendExtendedCommandPacket(flashCommandSubtype.optimizeFilesystem, 0, 0, 0, ["", "", "", "", fileSystem, ""])) + return "Couldn't optimize the fileSystem : " + err; + return 0; +} + + +/* Main function */ +/* Return 0 on success and the error on failure */ +async function talkToCalculator() { + let err = await p7Initialization(); + if (err) + return "Failed to initiate the connection : " + err; + + console.log("%cExecute user request", "font-size: 14px; color: white"); + + //if (err = fileTransfer(0, )) + + let capacity = 0; + console.log(getCapacity('fls0').then((success) => capacity = success, (failure) => failure)); + console.log(capacity); + //var file = document.getElementById('input').files[0]; + //var file = await window.showOpenFilePicker(); + //await fileTransfer(file, "", 'fls0'); + //await list('fls0'); + //console.log(file); + /* Terminate packet handling */ + +// //Check if the packet is a terminate packet +// if (p7ReceivedPacketInfo.type === p7PacketType.terminate) +// return 'The calculator closed the connection' + (err = await p7DecodeTerminatePacket()) ? ';but there was an error exiting the session : ' + err : '.'; + + + document.getElementById('demo').innerHTML = + 'Hardware identifier : ' + p7DeviceInformation['Hardware identifier'] + + '
Processor identifier : ' + p7DeviceInformation['Processor identifier'] + + '
Preprogrammed ROM capaciy : ' + p7DeviceInformation['Preprogrammed ROM capaciy']+ + '
Flash ROM capacity : ' + p7DeviceInformation['Flash ROM capacity'] + + '
RAM capacity : ' + p7DeviceInformation['RAM capacity'] + + '
Prepogrammed ROM version : ' + p7DeviceInformation['Prepogrammed ROM version'] + + '
Boot code version : ' + p7DeviceInformation['Boot code version'] + + '
Boot code offset : ' + p7DeviceInformation['Boot code offset'] + + '
Boot code size : ' + p7DeviceInformation['Boot code size'] + + '
Os code version : ' + p7DeviceInformation['Os code version'] + + '
Os code offset : ' + p7DeviceInformation['Os code offset'] + + '
Os code size : ' + p7DeviceInformation['Os code size'] + + '
Protocol version : ' + p7DeviceInformation['Protocol version'] + + '
Product ID : ' + p7DeviceInformation['Product ID'] + + '
Name set by user in system : ' + p7DeviceInformation['Name set by user in system']; + + await device.transferOut(1, p7SendBuffer.slice(0, p7MakeBasicPacket(p7PacketType['terminate'], + terminateSubtype['default']))); +} + + +window.onload = (event) => { + document.querySelector('#connect').onclick = async () => { + let err = 0; + + if (err = await talkToCalculator()) + console.log('%c%s', "color: red", err); + + } +} diff --git a/internal.js b/internal.js new file mode 100644 index 0000000..93c683c --- /dev/null +++ b/internal.js @@ -0,0 +1,428 @@ +"use strict"; +/* Compute the (p7-like) checksum of a buffer */ +function p7Checksum(buffer, length) +{ + //Sum individual bytes in Subtype (ST) and following fields + let checksum = 0; + let i = 1; + for(length -= 2 ; i < length ; i++ ) + checksum+=buffer[i]; + + /* NOT + 1, as defined in fxReverse documentation. + * Be sure it's under 256! */ + return (((~checksum) + 1) & 0xFF); +} + +/* Return an array with the ascii values of each hex digit*/ +function p7HexToAscii(hexNumber, digits) { + return p7StringToAscii(hexNumber.toString(16).toUpperCase().padStart(digits, 0)); +} + +/* Return an array with the ascii value of each character */ +function p7StringToAscii(string) { + return string.split('').map(letter => letter.charCodeAt(0)); +} + +/* Create a basic (Non-Extended) packet and push it to p7SendBuffer */ +/* return it's length (6) */ +function p7MakeBasicPacket(type, subtype) { + //Set Type (T) field + p7SendBuffer.set([type], 0); + + //Set Subtype (ST) field + p7SendBuffer.set(p7HexToAscii(subtype, 2), 1); + + //Set Extended (EX) field + p7SendBuffer.set([0x30], 3); + + //Set Checksum (CS) field + p7SendBuffer.set(p7HexToAscii(p7Checksum(p7SendBuffer, 6), 2), 4); + + //Return basic packet length + return 6; +} + +/* Fill Data (D) field of a command packet in p7SendBuffer */ +/* return Data (D) field length */ +function p7MakeCommandPacketDataField(forceOverwrite, dataType, fileSize, commandArguments) { + //Set Overwrite (OW) field + p7SendBuffer.set([0x30, (forceOverwrite) ? ((forceOverwrite == 1) ? 0x31 : 0x32) : 0x30], 8); + + //Set Data type (DT) field ???? + p7SendBuffer.set([0x30, 0x30], 10); + + //Set Filesize (FS) field + p7SendBuffer.set(p7HexToAscii(fileSize, 8), 12); + + let packetSize = 20; + + //Set Size of Data {1-6} (SD{1-6}) field + for (let i = 0 ; i < 6 ; packetSize += 2 , i++) + p7SendBuffer.set((commandArguments[i]) ? p7HexToAscii(commandArguments[i].length, 2) : [0x30, 0x30], packetSize); + + + //Set Data {1-6} (D{1-6}) field + for (let i = 0 ; i < 6 ; i++) + if (commandArguments[i]) { + p7SendBuffer.set(p7StringToAscii(commandArguments[i]), packetSize); + packetSize += commandArguments[i].length; + } + + return packetSize - 8; /* Type, Subtype, Extended, Data size fields of the packet */; +} + +/* Create an extended command packet and push it to p7SendBuffer */ +/* return it's length */ +function p7MakeExtendedCommandPacket(subtype, forceOverwrite, dataType, fileSize, commandArguments) { + //Set Type (T) field + p7SendBuffer.set([0x01], 0); + + //Set Subtype (ST) field + p7SendBuffer.set(p7HexToAscii(subtype, 2), 1); + + //Set Extended (EX) field + p7SendBuffer.set([0x31], 3); + + //Set Data (D) field + let dataSize = p7MakeCommandPacketDataField(forceOverwrite, dataType, fileSize, commandArguments); + + //Set Data size (DS) field + p7SendBuffer.set(p7HexToAscii(dataSize, 4), 4); + + let packetSize = dataSize + 10 /* Type, Subtype, Extended, Data size and Checksum fields */; + + //Set Checksum (CS) field + p7SendBuffer.set(p7HexToAscii(p7Checksum(p7SendBuffer, packetSize), 2), packetSize - 2); + + //Return it's length + return packetSize; +} + +/* Create a data packet and push it to p7SendBuffer */ +/* return it's length */ +function p7MakeDataPacket(subType, totalNumber, currentNumber, data) { + packetSize = data.length + 18 /* Type, Subtype, Extended, Data size, Data['Total Number'], Data['Current number'] and checksum fields */; + + //Set Type (T) field + p7SendBuffer.set([0x02], 0); + + //Set Subtype (ST) field + p7SendBuffer.set(p7HexToAscii(subType, 2), 1); + + //Set Extended (EX) field + p7SendBuffer.set([0x31], 3); + + //Set Data size (DS) field + p7SendBuffer.set(p7HexToAscii(data.length + 8 /* Total number (TN) and Current number (CN) fields */, 4), 4); + + //Set Total number (TN) field + p7SendBuffer.set(p7HexToAscii(totalNumber, 4), 8); + + //Set Current number (CN) field + p7SendBuffer.set(p7HexToAscii(currentNumber, 4), 12); + + //Set Data (DD) field + p7SendBuffer.set(data, 16); + + //Set Checksum (CS) field + p7SendBuffer.set(p7HexToAscii(p7Checksum(p7SendBuffer, packetSize), 2), packetSize - 2); + + //Return it's length + return packetSize; +} + +/* Receive a packet and parse it's properties to p7ReceivedPacketInfo */ +/* Make sure their aren't checksums error (ask for a resend if necessary) */ +async function p7ReceivePacket() { + console.log("%cReceiving...", "color: orange"); + let checksumError = 0; + let err = 0; + do { + //Send an error packet with subtype resend request if there was a checksum error + if (checksumError) + if (err = await p7SendBasicPacket(p7PacketType.error, errorSubtype.resendRequest)) + return "Couldn't send an error packet with resend request subtype (there was a checksum error) : " + err; + + let transferInResult = 0; + + //Recieve and parse the packet + do { + transferInResult = await device.transferIn(2, 64).then( + (result) => { + return result; + }, (error) => { + err = error; + }); + if (err) + return "Couldn't receive the first 64 bytes of the packet : " + err; + } while(!transferInResult.data.byteLength); + + + p7ReceivedPacketInfo.packetSize = 4; /*Type, Subtype and Extended field*/ + + //Copy the received buffer to p7ReceiveBuffer + p7ReceiveBuffer.set(new Uint8Array(transferInResult.data.buffer), 0); + + //Set Type (T) field + p7ReceivedPacketInfo.type = p7ReceiveBuffer[0]; + + //Set Subtype (ST) field + p7ReceivedPacketInfo.subtype = Number('0x' + String.fromCharCode(p7ReceiveBuffer[1], p7ReceiveBuffer[2]), 16); + + //Set Extended (EX) field + p7ReceivedPacketInfo.extended = (p7ReceiveBuffer[3] === 0x31); + + if (!p7ReceivedPacketInfo.extended) { + p7ReceivedPacketInfo.packetSize += 2; /*Checksum field*/ + + //Set Checksum (CS) field + p7ReceivedPacketInfo.checksum = Number('0x' + String.fromCharCode(p7ReceiveBuffer[4], p7ReceiveBuffer[5]), 16); + + //Compute the checksum + checksumError = (p7ReceivedPacketInfo.checksum !== p7Checksum(p7ReceiveBuffer, p7ReceivedPacketInfo.packetSize)); + } + else { + + p7ReceivedPacketInfo.packetSize += 4; /*Data size field*/ + + //Set Data size (DS) field + p7ReceivedPacketInfo.dataSize = Number('0x' + String.fromCharCode.apply(null, p7ReceiveBuffer.slice(4, 8)), 10); + + //Add the checksum bytes + p7ReceivedPacketInfo.dataSize+=2; + + + p7ReceivedPacketInfo.packetSize += (p7ReceivedPacketInfo.dataSize > 56) ? 56 : p7ReceivedPacketInfo.dataSize; + p7ReceivedPacketInfo.dataSize -= 56; + + + //Receive the rest of the data + while(p7ReceivedPacketInfo.dataSize > 0) + { + transferInResult = await device.transferIn(2, 64).then( + (result) => { + return result; + }, (error) => { + err = error; + }); + + if (err) + return "Couldn't receive the " + packetSize + "th and following 64 bytes of the packet : " + err; + + p7ReceiveBuffer.set(new Uint8Array(transferInResult.data.buffer), p7ReceivedPacketInfo.packetSize); + p7ReceivedPacketInfo.packetSize += (p7ReceivedPacketInfo.dataSize > 64) ? 64 : p7ReceivedPacketInfo.dataSize; + p7ReceivedPacketInfo.dataSize-=64; + } + + //Set Checksum (CS) field + p7ReceivedPacketInfo.checksum = Number('0x' + String.fromCharCode(p7ReceiveBuffer[p7ReceivedPacketInfo.packetSize - 2], p7ReceiveBuffer[p7ReceivedPacketInfo.packetSize - 1]), 16); + + //Compute the checksum + checksumError = (p7ReceivedPacketInfo.checksum !== p7Checksum(p7ReceiveBuffer, p7ReceivedPacketInfo.packetSize)); + } + } while (checksumError) + console.log(p7ReceivedPacketInfo); + return 0; +} + + +/* Send a packet */ +/* Receive the response and resend the packet if the response was an error with subtype resend request */ +/* Return 0 on success and the error on failure */ +async function p7SendPacket(packetLength) { + console.log(p7SendBuffer.slice(0, packetLength)); + + let err = 0; + + //Send the packet and return the error on failure + if (err = await device.transferOut(1, p7SendBuffer.slice(0, packetLength)).then((success) => 0, (failure) => failure)) + return "Couldn't send the packet : " + err; + + //Receive the packet and return the error on failure + if (err = await p7ReceivePacket()) + return "Couldn't receive the response packet : " + err; + + //Resend the packet if the response was an error packet with subtype resend request and return the error on failure + for (let i = 0 ; (p7ReceivedPacketInfo.type === p7PacketType.error && p7ReceivedPacketInfo.subtype === errorSubtype.resendRequest) ; i++) { + + if (err = await device.transferOut(1, p7SendBuffer.slice(0, packetLength)).then((success) => {console.log(success) ; return 0}, (failure) => {return failure})) + return "Couldn't send the packet : " + err; + if (err = await p7ReceivePacket()) + return "Couldn't receive the response packet : " + err; + if (i > 2) + return "Too much resend request, the calculator might be broken."; + } + + //Everything went right + return 0; +} + + +/* Send a basic (non-Extended) packet */ +/* Return 0 on success and the error on failure */ +async function p7SendBasicPacket(type, subtype) { + console.log("%cSending basic packet...", "color: green"); + return await p7SendPacket(p7MakeBasicPacket(type, subtype)); +} + +/* Send an extended command packet */ +/* Return 0 on success and the error on failure */ +async function p7SendExtendedCommandPacket(subtype, forceOverwrite, dataType, fileSize, commandArguments) { + console.log("%cSending extended command packet...", "color: green"); + return await p7SendPacket(p7MakeExtendedCommandPacket(subtype, forceOverwrite, dataType, fileSize, commandArguments)); +} + +/* Send an extended data packet */ +/* Return 0 on success and the error on failure */ +async function p7SendDataPacket(subtype, totalNumber, currentNumber, data) { + console.log("%cSending data packet...", "color: green"); + return p7SendPacket(p7MakeDataPacket(subtype, totalNumber, currentNumber, data)); +} + +/* Split the data into 256 bytes-long buffer and send all of them */ +/* Return 0 on success and the error on failure */ +async function p7SendData(subtype, data) { + let err = 0; + let lastPacketSize = (data.byteLength & 255); //Equivalent to data.byteLength % 256, but way faster ! + let totalPacketNumber = (data.byteLength >> 8); //Equivalent to data.byteLength / 256, but way way waaaaaay faster, and return an integer quotient, not a decimal one !! + console.log(lastPacketSize + ' ' + totalPacketNumber); + totalPacketNumber += (lastPacketSize) ? 1 : 0; + let currentPacketNumber = 1; + let currentPacketFirstByteIndex = 0; + + //Send all the 256 bytes-long data packets + for (; currentPacketNumber < totalPacketNumber ; currentPacketNumber++) + if (err = await p7SendDataPacket(subtype, totalPacketNumber, currentPacketNumber, data.slice(currentPacketFirstByteIndex, (currentPacketFirstByteIndex += 256)))) + return 'Couldn\'t send the ' + currentPacketNumber + ' data packet : ' + err; + + //Send the last data packet (if it exists) + if (lastPacketSize) + if (err = await p7SendDataPacket(subtype, totalPacketNumber, currentPacketNumber, data.slice(currentPacketFirstByteIndex, currentPacketFirstByteIndex + lastPacketSize))) + return 'Couldn\'t send the last data packet (' + lastPacketSize + ' bytes) : ' + err; + + + console.log(p7SendBuffer.slice(0, 64)); + console.log(p7ReceivedPacketInfo); + + //Everything went allright + return 0; +} + +/* Set all device information feilds, and trim string's overage ('ÿ') */ +function p7DecodeExtendedAckPacket() { + p7DeviceInformation['Hardware identifier'] = String.fromCharCode.apply(null, p7ReceiveBuffer.slice(8, 16)); + p7DeviceInformation['Processor identifier'] = String.fromCharCode.apply(null, p7ReceiveBuffer.slice(16, 32)); + p7DeviceInformation['Preprogrammed ROM capacity'] = String.fromCharCode.apply(null, p7ReceiveBuffer.slice(32, 40)); + p7DeviceInformation['Flash ROM capacity'] = String.fromCharCode.apply(null, p7ReceiveBuffer.slice(40, 48)); + p7DeviceInformation['RAM capacity'] = String.fromCharCode.apply(null, p7ReceiveBuffer.slice(48, 56)); + p7DeviceInformation['Prepogrammed ROM version'] = String.fromCharCode.apply(null, p7ReceiveBuffer.slice(56, 72)).replaceAll('ÿ', '');; + p7DeviceInformation['Boot code version'] = String.fromCharCode.apply(null, p7ReceiveBuffer.slice(72, 88)).replaceAll('ÿ', ''); + p7DeviceInformation['Boot code offset'] = String.fromCharCode.apply(null, p7ReceiveBuffer.slice(88, 96)).replaceAll('ÿ', ''); + p7DeviceInformation['Boot code size'] = String.fromCharCode.apply(null, p7ReceiveBuffer.slice(96, 104)).replaceAll('ÿ', ''); + p7DeviceInformation['Os code version'] = String.fromCharCode.apply(null, p7ReceiveBuffer.slice(104, 120)).replaceAll('ÿ', ''); + p7DeviceInformation['Os code offset'] = String.fromCharCode.apply(null, p7ReceiveBuffer.slice(120, 128)); + p7DeviceInformation['Os code size'] = String.fromCharCode.apply(null, p7ReceiveBuffer.slice(128, 136)); + p7DeviceInformation['Protocol version'] = String.fromCharCode.apply(null, p7ReceiveBuffer.slice(136, 140)); + p7DeviceInformation['Product ID'] = String.fromCharCode.apply(null, p7ReceiveBuffer.slice(140, 156)).replaceAll('ÿ', '');; + p7DeviceInformation['Name set by user in system'] = String.fromCharCode.apply(null, p7ReceiveBuffer.slice(156, 172)).replaceAll('ÿ', ''); + + return 0; +} + +/* Decode a terminate packet and close the connection */ +async function p7DecodeTerminatePacket() { + let err = 0; + switch (p7ReceivedPacketInfo.subtype) { + case terminateSubtype.default: + alert('The calculator asked to close the connection.'); + case terminateSubtype.userRequest: + alert('The calculator said you closed the connection!'); + case terminateSubtype.timeouts: + alert('The calculator closed the connection because it was inactive for more than 6 minutes.'); + case terminateSubtype.overwriteRequest: + alert('The calculator didn\'t accept the overwrite request and closed the connection.'); + default: + alert('The calculator asked to terminate communication for an unknown reason : 0x' + p7ReceivedPacketInfo.subtype.toString(16) + ' (the hex code of the reason).'); + } + if (err = await p7SendBasicPacket(p7PacketType.ack, ackSubtype.default)) + return 'Couldn\'t send acknowledgement packet : ' + err; + return (err = await p7Exit()) ? 'Couldn\'t exit properly : ' + err : 0; +} + +/* Return the value (as an number) of Filesize (FS) subfield of Data (D) field of an extended command packet */ +function p7CommandPacketGetFilesize() { + return Number('0x' + String.fromCharCode.apply(null, p7ReceiveBuffer.slice(12, 20)), 16); +} + +/* Return the trimed receive buffer only containing Data (DD) subfield of Data (D) field of an extended command packet */ +function p7CommandPacketGetField(field) { + let fieldStart = 32; /* D1 field first byte's position */ + for (let i = 0 ; i < 2 * field ; i += 2) + fieldStart += Number('0x' + String.fromCharCode(p7ReceiveBuffer[20 + i], p7ReceiveBuffer[21 + i]), 16); + + let fieldEnd = fieldStart + Number('0x' + String.fromCharCode(p7ReceiveBuffer[20 + 2 * field], p7ReceiveBuffer[21 + 2 * field]), 16); + return p7ReceiveBuffer.slice(fieldStart, fieldEnd); +} + +/* Connect to the calculator */ +/* Return 0 on success and the error on failure */ +async function p7Initialization() { + let err = 0; + err = await navigator.usb.requestDevice({filters:[{'vendorId': 0x07cf , 'productId': 0x6101}, {'vendorId': 0x07cf , 'productId': 0x6102}]}).then( + (selectedDevice) => { + device = selectedDevice; + return 0; + }, (error) => error + ); + + if (err) + return "Couldn't get the selected device (maybe the user didn't select one) : " + err; + + if (err = await device.open().then(() => 0, (error) => error)) + return "Couldn't connect to the calculator : " + err; + + //It's the only existing configuration (should be selected by default), just checkin' + if (err = await device.selectConfiguration(1).then(() => 0, (error) => error)) + return "Couldn't select the configuration (configurationValue : 1) : " + err; + + if (err = await device.claimInterface(0 /* It's the only existing one */).then(() => 0, (error) => error)) + return "Couldn't claim the interface (interfaceNumber : 0) : " + err; + + console.log(device); + + console.log('%cInitialisation packet', "font-size: 14px; color: white"); + + if (err = await p7SendBasicPacket(p7PacketType['check'], checkSubtype['initialization'])) + return "Failed to send init packet : " + err; + + + if (!(p7ReceivedPacketInfo.type === p7PacketType.ack && p7ReceivedPacketInfo.subtype === ackSubtype.default)) + return "Not the expected response packet."; /* TODO: handle this error */ + + + console.log('%cRequesting device\'s informations', "font-size: 14px; color: white"); + + + if (err = await p7SendBasicPacket(p7PacketType['command'], sysCommandSubtype['getDeviceInfo'])) + return "Failed to request device info : " + err; + + if (!(p7ReceivedPacketInfo.type === p7PacketType.ack && p7ReceivedPacketInfo.subtype === ackSubtype.extendedAck)) + return "Not the expected response packet."; /* TODO: add a handle to this error */ + + p7DecodeExtendedAckPacket(); + + return 0; +} + +/* Disconnect from the calculator */ +/* Return 0 on success and the error on failure */ +async function p7Exit() { + let err = 0; + return (err = await device.close().then((success) => {return 0}, (failure) => {return failure})) ? "There was an error disconnecting from the calculator : " + err : 0; +} + +/* Return an object describing a file (or a directory) */ +function createFileObject(device, path, name, size) { + //Directories never have size, only their own path + return (size) ? {type: 'file', path: path, name: name, size: size} : {type: 'directory', path: path}; +} \ No newline at end of file