/* global __appdir */ var path = require('path') const fs = require('fs') const crypto = require('crypto') const axios = require('axios') const pciRepoApi = 'https://api.github.com/repos/pciutils/pciids/contents/' const pciRepoDataApi = 'https://api.github.com/repos/pciutils/pciids/git/blobs/' const pciIdsFilePath = path.join(__appdir, 'lib', 'pci', 'pci.ids') var parsedPciList = {} var parsedDeviceClasses = {} // Non-Api way to the the raw file: // const pciIdsUrl = 'https://github.com/pciutils/pciids/raw/master/pci.ids' module.exports = { parseIds: async function (vendorId, deviceId, subvendorId, subdeviceId, classId, subclassId) { await updatePciIds() if (Object.keys(parsedPciList).length === 0) await parsePciList() let pciInformation = { 'vendor': vendorId && vendorId.length === 4 ? { 'id': vendorId, 'name': parsedPciList[vendorId].name, 'device': deviceId && deviceId.length === 4 ? { 'id': deviceId, 'name': deviceId ? parsedPciList[vendorId].devices[deviceId].name : undefined, 'subvendor': subvendorId && subdeviceId && subvendorId.length === 4 && subdeviceId.length === 4 ? { 'id': subvendorId, 'name': parsedPciList[vendorId].devices[deviceId].subsystems[subvendorId][subdeviceId].name } : undefined } : undefined } : undefined, 'class': classId && classId.length === 2 ? { 'id': classId, 'name': parsedDeviceClasses[classId].name, 'subclass': subclassId && subclassId.length === 2 ? { 'id': subclassId, 'name': parsedDeviceClasses[classId].subclasses[subclassId].name } : undefined } : undefined } return pciInformation } } async function updatePciIds () { // Check if the file in the git is newer than the file created. let response = await axios.get(pciRepoApi) response = response.data // Get SHA of the pci.ids file const sha = response.filter(x => x.name === 'pci.ids')[0].sha if (fs.existsSync(pciIdsFilePath)) { /* Calculate own sha value * Git sha1 value of a file is calculated like this: * 'blob' + ' ' + content.length + '\0' + content */ const pciids = fs.readFileSync(pciIdsFilePath) const blobprefix = 'blob' + ' ' + pciids.length + '\0' const sha1sum = crypto.createHash('sha1').update(blobprefix + pciids).digest('hex') // Exit if there isn't a new version if (sha === sha1sum) return } // Download the new list await downloadPciIds(sha) } async function downloadPciIds (sha) { // Get the newest version from the github api let request = await axios.get(pciRepoDataApi + sha) request = request.data // Decode base64 encoding const buffer = Buffer.from(request.content, 'base64') const file = buffer.toString('utf-8') // Override the content in the pci.ids file with the new one fs.writeFile(pciIdsFilePath, file, { flag: 'w' }, (err) => { if (err) throw err // Reparse the pci file parsePciList() }) } /* * Syntax of the pci.ids file: * # vendor vendor_name * # device device_name <-- single tab * # subvendor subdevice subsystem_name <-- two tabs * parsed structure: * { * '': { * 'name': '', * 'devices': { * '': { * 'name': '', * 'subsystems': { * '': { * '': { * 'name': '' * } * } * } * } * } * } * } * * # C class class_name * # subclass subclass_name <-- single tab * # prog-if prog-if_name <-- two tabs * parsed structure: * { * '': { * 'name': '', * 'subclasses': { * '': { * 'name': '', * 'interfaces': { * '': { * 'name': '' * } * } * } * } * } * } */ function parsePciList () { let result = {} let deviceClasses = {} // Read file const lines = fs.readFileSync(pciIdsFilePath).toString().split('\n') const regexVendor = /^[0-9A-Fa-f]{4}/ const regexDevice = /^\t[0-9A-Fa-f]{4}/ const regexSubVD = /^\t\t[0-9A-Fa-f]{4}/ const regexClass = /^C [0-9A-Fa-f]{2}/ const regexSubclass = /^\t[0-9A-Fa-f]{2}/ const regexProgIf = /^\t\t[0-9A-Fa-f]{2}/ let lastVendorId = '' let lastDeviceId = '' let lastClassId = '' let lastSubClassId = '' for (let line of lines) { if (line.startsWith('#')) continue else if (line === '') continue else if (line.match(regexVendor)) { const vendor = line.substring(0, 4) const name = line.substring(6) result[vendor] = { 'name': name, 'devices': {} } lastVendorId = vendor } else if (line.match(regexDevice)) { const device = line.substring(1, 5) const name = line.substring(7) result[lastVendorId].devices[device] = { 'name': name, 'subsystems': {} } lastDeviceId = device } else if (line.match(regexSubVD)) { const subvendor = line.substring(2, 6) const subdevice = line.substring(7, 11) const subsystem = line.substring(13) if (result[lastVendorId].devices[lastDeviceId].subsystems[subvendor] === undefined) result[lastVendorId].devices[lastDeviceId].subsystems[subvendor] = {} result[lastVendorId].devices[lastDeviceId].subsystems[subvendor][subdevice] = { 'name': subsystem } } else if (line.match(regexClass)) { deviceClasses[line.substring(2, 4)] = { 'name': line.substring(6), 'subclasses': {} } lastClassId = line.substring(2, 4) } else if (line.match(regexSubclass)) { deviceClasses[lastClassId].subclasses[line.substring(1, 3)] = { 'name': line.substring(5), 'interfaces': {} } lastSubClassId = line.substring(1, 3) } else if (line.match(regexProgIf)) { deviceClasses[lastClassId].subclasses[lastSubClassId].interfaces[line.substring(2, 4)] = { 'name': line.substring(6) } } } parsedPciList = result parsedDeviceClasses = deviceClasses }