1*90c8c64dSAndroid Build Coastguard Worker/** 2*90c8c64dSAndroid Build Coastguard Worker * @fileoverview Class paypload is used to read in and 3*90c8c64dSAndroid Build Coastguard Worker * parse the payload.bin file from a OTA.zip file. 4*90c8c64dSAndroid Build Coastguard Worker * Class OpType creates a Map that can resolve the 5*90c8c64dSAndroid Build Coastguard Worker * operation type. 6*90c8c64dSAndroid Build Coastguard Worker * @package zip.js 7*90c8c64dSAndroid Build Coastguard Worker * @package protobufjs 8*90c8c64dSAndroid Build Coastguard Worker */ 9*90c8c64dSAndroid Build Coastguard Worker 10*90c8c64dSAndroid Build Coastguard Workerimport * as zip from '@zip.js/zip.js/dist/zip-full.min.js' 11*90c8c64dSAndroid Build Coastguard Workerimport { chromeos_update_engine as update_metadata_pb } from './update_metadata_pb.js' 12*90c8c64dSAndroid Build Coastguard Workerimport { PayloadNonAB } from './payload_nonab.js' 13*90c8c64dSAndroid Build Coastguard Worker 14*90c8c64dSAndroid Build Coastguard Workerconst /** String */ _MAGIC = 'CrAU' 15*90c8c64dSAndroid Build Coastguard Workerconst /** Number */ _VERSION_SIZE = 8 16*90c8c64dSAndroid Build Coastguard Workerconst /** Number */ _MANIFEST_LEN_SIZE = 8 17*90c8c64dSAndroid Build Coastguard Workerconst /** Number */ _METADATA_SIGNATURE_LEN_SIZE = 4 18*90c8c64dSAndroid Build Coastguard Worker 19*90c8c64dSAndroid Build Coastguard Workerconst /** Number */ _PAYLOAD_HEADER_SIZE = 20*90c8c64dSAndroid Build Coastguard Worker _MAGIC.length + _VERSION_SIZE + _MANIFEST_LEN_SIZE + _METADATA_SIGNATURE_LEN_SIZE 21*90c8c64dSAndroid Build Coastguard Worker 22*90c8c64dSAndroid Build Coastguard Workerconst /** Number */ _BRILLO_MAJOR_PAYLOAD_VERSION = 2 23*90c8c64dSAndroid Build Coastguard Workerexport const /** Array<Object> */ MetadataFormat = [ 24*90c8c64dSAndroid Build Coastguard Worker { 25*90c8c64dSAndroid Build Coastguard Worker prefix: 'pre-build', 26*90c8c64dSAndroid Build Coastguard Worker key: 'preBuild', 27*90c8c64dSAndroid Build Coastguard Worker name: 'Pre-build' 28*90c8c64dSAndroid Build Coastguard Worker }, 29*90c8c64dSAndroid Build Coastguard Worker { 30*90c8c64dSAndroid Build Coastguard Worker prefix: 'pre-build-incremental', 31*90c8c64dSAndroid Build Coastguard Worker key: 'preBuildVersion', 32*90c8c64dSAndroid Build Coastguard Worker name: 'Pre-build version' 33*90c8c64dSAndroid Build Coastguard Worker }, 34*90c8c64dSAndroid Build Coastguard Worker { 35*90c8c64dSAndroid Build Coastguard Worker prefix: 'post-build', 36*90c8c64dSAndroid Build Coastguard Worker key: 'postBuild', 37*90c8c64dSAndroid Build Coastguard Worker name: 'Post-build' 38*90c8c64dSAndroid Build Coastguard Worker }, 39*90c8c64dSAndroid Build Coastguard Worker { 40*90c8c64dSAndroid Build Coastguard Worker prefix: 'post-build-incremental', 41*90c8c64dSAndroid Build Coastguard Worker key: 'postBuildVersion', 42*90c8c64dSAndroid Build Coastguard Worker name: 'Post-build version' 43*90c8c64dSAndroid Build Coastguard Worker } 44*90c8c64dSAndroid Build Coastguard Worker] 45*90c8c64dSAndroid Build Coastguard Worker 46*90c8c64dSAndroid Build Coastguard Workerclass StopIteration extends Error { 47*90c8c64dSAndroid Build Coastguard Worker 48*90c8c64dSAndroid Build Coastguard Worker} 49*90c8c64dSAndroid Build Coastguard Worker 50*90c8c64dSAndroid Build Coastguard Workerclass OTAPayloadBlobWriter extends zip.Writer { 51*90c8c64dSAndroid Build Coastguard Worker /** 52*90c8c64dSAndroid Build Coastguard Worker * A zip.Writer that is tailored for OTA payload.bin read-in. 53*90c8c64dSAndroid Build Coastguard Worker * Instead of reading in all the contents in payload.bin, this writer will 54*90c8c64dSAndroid Build Coastguard Worker * throw an 'StopIteration' error when the header is read in. 55*90c8c64dSAndroid Build Coastguard Worker * The header will be stored into the <payload>. 56*90c8c64dSAndroid Build Coastguard Worker * @param {Payload} payload 57*90c8c64dSAndroid Build Coastguard Worker * @param {String} contentType 58*90c8c64dSAndroid Build Coastguard Worker */ 59*90c8c64dSAndroid Build Coastguard Worker constructor(payload, contentType = "") { 60*90c8c64dSAndroid Build Coastguard Worker super() 61*90c8c64dSAndroid Build Coastguard Worker this.offset = 0 62*90c8c64dSAndroid Build Coastguard Worker this.contentType = contentType 63*90c8c64dSAndroid Build Coastguard Worker this.blob = new Blob([], { type: contentType }) 64*90c8c64dSAndroid Build Coastguard Worker this.prefixLength = 0 65*90c8c64dSAndroid Build Coastguard Worker this.payload = payload 66*90c8c64dSAndroid Build Coastguard Worker } 67*90c8c64dSAndroid Build Coastguard Worker 68*90c8c64dSAndroid Build Coastguard Worker async writeUint8Array(/** @type {Uint8Array} */ array) { 69*90c8c64dSAndroid Build Coastguard Worker super.writeUint8Array(array) 70*90c8c64dSAndroid Build Coastguard Worker this.blob = new Blob([this.blob, array.buffer], { type: this.contentType }) 71*90c8c64dSAndroid Build Coastguard Worker this.offset = this.blob.size 72*90c8c64dSAndroid Build Coastguard Worker // Once the prefixLength is non-zero, the address of manifest and signature 73*90c8c64dSAndroid Build Coastguard Worker // become known and can be read in. Otherwise the header needs to be read 74*90c8c64dSAndroid Build Coastguard Worker // in first to determine the prefixLength. 75*90c8c64dSAndroid Build Coastguard Worker if (this.offset >= _PAYLOAD_HEADER_SIZE) { 76*90c8c64dSAndroid Build Coastguard Worker await this.payload.readHeader(this.blob) 77*90c8c64dSAndroid Build Coastguard Worker this.prefixLength = 78*90c8c64dSAndroid Build Coastguard Worker _PAYLOAD_HEADER_SIZE 79*90c8c64dSAndroid Build Coastguard Worker + this.payload.manifest_len 80*90c8c64dSAndroid Build Coastguard Worker + this.payload.metadata_signature_len 81*90c8c64dSAndroid Build Coastguard Worker console.log(`Computed metadata length: ${this.prefixLength}`); 82*90c8c64dSAndroid Build Coastguard Worker } 83*90c8c64dSAndroid Build Coastguard Worker if (this.prefixLength > 0) { 84*90c8c64dSAndroid Build Coastguard Worker console.log(`${this.offset}/${this.prefixLength}`); 85*90c8c64dSAndroid Build Coastguard Worker if (this.offset >= this.prefixLength) { 86*90c8c64dSAndroid Build Coastguard Worker await this.payload.readManifest(this.blob) 87*90c8c64dSAndroid Build Coastguard Worker await this.payload.readSignature(this.blob) 88*90c8c64dSAndroid Build Coastguard Worker } 89*90c8c64dSAndroid Build Coastguard Worker } 90*90c8c64dSAndroid Build Coastguard Worker // The prefix has everything we need (header, manifest, signature). Once 91*90c8c64dSAndroid Build Coastguard Worker // the offset is beyond the prefix, no need to move on. 92*90c8c64dSAndroid Build Coastguard Worker if (this.offset >= this.prefixLength) { 93*90c8c64dSAndroid Build Coastguard Worker throw new StopIteration() 94*90c8c64dSAndroid Build Coastguard Worker } 95*90c8c64dSAndroid Build Coastguard Worker } 96*90c8c64dSAndroid Build Coastguard Worker 97*90c8c64dSAndroid Build Coastguard Worker getData() { 98*90c8c64dSAndroid Build Coastguard Worker return this.blob 99*90c8c64dSAndroid Build Coastguard Worker } 100*90c8c64dSAndroid Build Coastguard Worker} 101*90c8c64dSAndroid Build Coastguard Worker 102*90c8c64dSAndroid Build Coastguard Workerexport class Payload { 103*90c8c64dSAndroid Build Coastguard Worker /** 104*90c8c64dSAndroid Build Coastguard Worker * This class parses the metadata of a OTA package. 105*90c8c64dSAndroid Build Coastguard Worker * @param {File} file A OTA.zip file read from user's machine. 106*90c8c64dSAndroid Build Coastguard Worker */ 107*90c8c64dSAndroid Build Coastguard Worker constructor(file) { 108*90c8c64dSAndroid Build Coastguard Worker this.packedFile = new zip.ZipReader(new zip.BlobReader(file)) 109*90c8c64dSAndroid Build Coastguard Worker this.cursor = 0 110*90c8c64dSAndroid Build Coastguard Worker } 111*90c8c64dSAndroid Build Coastguard Worker 112*90c8c64dSAndroid Build Coastguard Worker /** 113*90c8c64dSAndroid Build Coastguard Worker * Unzip the OTA package, get payload.bin and metadata 114*90c8c64dSAndroid Build Coastguard Worker */ 115*90c8c64dSAndroid Build Coastguard Worker async unzip() { 116*90c8c64dSAndroid Build Coastguard Worker let /** Array<Entry> */ entries = await this.packedFile.getEntries() 117*90c8c64dSAndroid Build Coastguard Worker this.payload = null 118*90c8c64dSAndroid Build Coastguard Worker for (let entry of entries) { 119*90c8c64dSAndroid Build Coastguard Worker if (entry.filename == 'payload.bin') { 120*90c8c64dSAndroid Build Coastguard Worker let writer = new OTAPayloadBlobWriter(this, "") 121*90c8c64dSAndroid Build Coastguard Worker try { 122*90c8c64dSAndroid Build Coastguard Worker await entry.getData(writer) 123*90c8c64dSAndroid Build Coastguard Worker } catch (e) { 124*90c8c64dSAndroid Build Coastguard Worker if (e instanceof StopIteration) { 125*90c8c64dSAndroid Build Coastguard Worker // Exception used as a hack to stop reading from zip. NO need to do anything 126*90c8c64dSAndroid Build Coastguard Worker // Ideally zip.js would provide an API to partialll read a zip 127*90c8c64dSAndroid Build Coastguard Worker // entry, but they don't. So this is what we get 128*90c8c64dSAndroid Build Coastguard Worker } else { 129*90c8c64dSAndroid Build Coastguard Worker throw e 130*90c8c64dSAndroid Build Coastguard Worker } 131*90c8c64dSAndroid Build Coastguard Worker } 132*90c8c64dSAndroid Build Coastguard Worker this.payload = writer.getData() 133*90c8c64dSAndroid Build Coastguard Worker break 134*90c8c64dSAndroid Build Coastguard Worker } 135*90c8c64dSAndroid Build Coastguard Worker if (entry.filename == 'META-INF/com/android/metadata') { 136*90c8c64dSAndroid Build Coastguard Worker this.metadata = await entry.getData(new zip.TextWriter()) 137*90c8c64dSAndroid Build Coastguard Worker } 138*90c8c64dSAndroid Build Coastguard Worker } 139*90c8c64dSAndroid Build Coastguard Worker if (!this.payload) { 140*90c8c64dSAndroid Build Coastguard Worker try { 141*90c8c64dSAndroid Build Coastguard Worker // The temporary variable manifest has to be used here, to prevent the html page 142*90c8c64dSAndroid Build Coastguard Worker // being rendered before everything is read in properly 143*90c8c64dSAndroid Build Coastguard Worker let manifest = new PayloadNonAB(this.packedFile) 144*90c8c64dSAndroid Build Coastguard Worker await manifest.init() 145*90c8c64dSAndroid Build Coastguard Worker manifest.nonAB = true 146*90c8c64dSAndroid Build Coastguard Worker this.manifest = manifest 147*90c8c64dSAndroid Build Coastguard Worker } catch (error) { 148*90c8c64dSAndroid Build Coastguard Worker alert('Please select a legit OTA package') 149*90c8c64dSAndroid Build Coastguard Worker return 150*90c8c64dSAndroid Build Coastguard Worker } 151*90c8c64dSAndroid Build Coastguard Worker } 152*90c8c64dSAndroid Build Coastguard Worker } 153*90c8c64dSAndroid Build Coastguard Worker 154*90c8c64dSAndroid Build Coastguard Worker /** 155*90c8c64dSAndroid Build Coastguard Worker * Read in an integer from binary bufferArray. 156*90c8c64dSAndroid Build Coastguard Worker * @param {Int} size the size of a integer being read in 157*90c8c64dSAndroid Build Coastguard Worker * @return {Int} an integer. 158*90c8c64dSAndroid Build Coastguard Worker */ 159*90c8c64dSAndroid Build Coastguard Worker readInt(size) { 160*90c8c64dSAndroid Build Coastguard Worker let /** DataView */ view = new DataView( 161*90c8c64dSAndroid Build Coastguard Worker this.buffer.slice(this.cursor, this.cursor + size)) 162*90c8c64dSAndroid Build Coastguard Worker if (typeof view.getBigUint64 !== "function") { 163*90c8c64dSAndroid Build Coastguard Worker view.getBigUint64 = 164*90c8c64dSAndroid Build Coastguard Worker function (offset) { 165*90c8c64dSAndroid Build Coastguard Worker const a = BigInt(view.getUint32(offset)) 166*90c8c64dSAndroid Build Coastguard Worker const b = BigInt(view.getUint32(offset + 4)) 167*90c8c64dSAndroid Build Coastguard Worker const bigNumber = a * 4294967296n + b 168*90c8c64dSAndroid Build Coastguard Worker return bigNumber 169*90c8c64dSAndroid Build Coastguard Worker } 170*90c8c64dSAndroid Build Coastguard Worker } 171*90c8c64dSAndroid Build Coastguard Worker this.cursor += size 172*90c8c64dSAndroid Build Coastguard Worker switch (size) { 173*90c8c64dSAndroid Build Coastguard Worker case 2: 174*90c8c64dSAndroid Build Coastguard Worker return view.getUInt16(0) 175*90c8c64dSAndroid Build Coastguard Worker case 4: 176*90c8c64dSAndroid Build Coastguard Worker return view.getUint32(0) 177*90c8c64dSAndroid Build Coastguard Worker case 8: 178*90c8c64dSAndroid Build Coastguard Worker return Number(view.getBigUint64(0)) 179*90c8c64dSAndroid Build Coastguard Worker default: 180*90c8c64dSAndroid Build Coastguard Worker throw 'Cannot read this integer with size ' + size 181*90c8c64dSAndroid Build Coastguard Worker } 182*90c8c64dSAndroid Build Coastguard Worker } 183*90c8c64dSAndroid Build Coastguard Worker 184*90c8c64dSAndroid Build Coastguard Worker /** 185*90c8c64dSAndroid Build Coastguard Worker * Read the header of payload.bin, including the magic, header_version, 186*90c8c64dSAndroid Build Coastguard Worker * manifest_len, metadata_signature_len. 187*90c8c64dSAndroid Build Coastguard Worker */ 188*90c8c64dSAndroid Build Coastguard Worker async readHeader(/** @type {Blob} */buffer) { 189*90c8c64dSAndroid Build Coastguard Worker this.buffer = await buffer.slice(0, _PAYLOAD_HEADER_SIZE).arrayBuffer() 190*90c8c64dSAndroid Build Coastguard Worker let /** TextDecoder */ decoder = new TextDecoder() 191*90c8c64dSAndroid Build Coastguard Worker try { 192*90c8c64dSAndroid Build Coastguard Worker this.magic = decoder.decode( 193*90c8c64dSAndroid Build Coastguard Worker this.buffer.slice(this.cursor, _MAGIC.length)) 194*90c8c64dSAndroid Build Coastguard Worker if (this.magic != _MAGIC) { 195*90c8c64dSAndroid Build Coastguard Worker throw new Error('MAGIC is not correct, please double check.') 196*90c8c64dSAndroid Build Coastguard Worker } 197*90c8c64dSAndroid Build Coastguard Worker this.cursor += _MAGIC.length 198*90c8c64dSAndroid Build Coastguard Worker this.header_version = this.readInt(_VERSION_SIZE) 199*90c8c64dSAndroid Build Coastguard Worker this.manifest_len = this.readInt(_MANIFEST_LEN_SIZE) 200*90c8c64dSAndroid Build Coastguard Worker if (this.header_version == _BRILLO_MAJOR_PAYLOAD_VERSION) { 201*90c8c64dSAndroid Build Coastguard Worker this.metadata_signature_len = this.readInt(_METADATA_SIGNATURE_LEN_SIZE) 202*90c8c64dSAndroid Build Coastguard Worker } 203*90c8c64dSAndroid Build Coastguard Worker else { 204*90c8c64dSAndroid Build Coastguard Worker throw new Error(`Unexpected major version number: ${this.header_version}`) 205*90c8c64dSAndroid Build Coastguard Worker } 206*90c8c64dSAndroid Build Coastguard Worker } catch (err) { 207*90c8c64dSAndroid Build Coastguard Worker console.log(err) 208*90c8c64dSAndroid Build Coastguard Worker return 209*90c8c64dSAndroid Build Coastguard Worker } 210*90c8c64dSAndroid Build Coastguard Worker } 211*90c8c64dSAndroid Build Coastguard Worker 212*90c8c64dSAndroid Build Coastguard Worker /** 213*90c8c64dSAndroid Build Coastguard Worker * Read in the manifest in an OTA.zip file. 214*90c8c64dSAndroid Build Coastguard Worker * The structure of the manifest can be found in: 215*90c8c64dSAndroid Build Coastguard Worker * aosp/system/update_engine/update_metadata.proto 216*90c8c64dSAndroid Build Coastguard Worker */ 217*90c8c64dSAndroid Build Coastguard Worker async readManifest(/** @type {Blob} */buffer) { 218*90c8c64dSAndroid Build Coastguard Worker buffer = await buffer.slice( 219*90c8c64dSAndroid Build Coastguard Worker this.cursor, this.cursor + this.manifest_len).arrayBuffer() 220*90c8c64dSAndroid Build Coastguard Worker this.cursor += this.manifest_len 221*90c8c64dSAndroid Build Coastguard Worker this.manifest = update_metadata_pb.DeltaArchiveManifest 222*90c8c64dSAndroid Build Coastguard Worker .decode(new Uint8Array(buffer)) 223*90c8c64dSAndroid Build Coastguard Worker this.manifest.nonAB = false 224*90c8c64dSAndroid Build Coastguard Worker } 225*90c8c64dSAndroid Build Coastguard Worker 226*90c8c64dSAndroid Build Coastguard Worker async readSignature(/** @type {Blob} */buffer) { 227*90c8c64dSAndroid Build Coastguard Worker buffer = await buffer.slice( 228*90c8c64dSAndroid Build Coastguard Worker this.cursor, this.cursor + this.metadata_signature_len).arrayBuffer() 229*90c8c64dSAndroid Build Coastguard Worker this.cursor += this.metadata_signature_len 230*90c8c64dSAndroid Build Coastguard Worker this.metadata_signature = update_metadata_pb.Signatures 231*90c8c64dSAndroid Build Coastguard Worker .decode(new Uint8Array(buffer)) 232*90c8c64dSAndroid Build Coastguard Worker } 233*90c8c64dSAndroid Build Coastguard Worker 234*90c8c64dSAndroid Build Coastguard Worker parseMetadata() { 235*90c8c64dSAndroid Build Coastguard Worker for (let formatter of MetadataFormat) { 236*90c8c64dSAndroid Build Coastguard Worker let regex = new RegExp(formatter.prefix + '.+') 237*90c8c64dSAndroid Build Coastguard Worker if (this.metadata.match(regex)) { 238*90c8c64dSAndroid Build Coastguard Worker this[formatter.key] = 239*90c8c64dSAndroid Build Coastguard Worker trimEntry(this.metadata.match(regex)[0], formatter.prefix) 240*90c8c64dSAndroid Build Coastguard Worker } else this[formatter.key] = '' 241*90c8c64dSAndroid Build Coastguard Worker } 242*90c8c64dSAndroid Build Coastguard Worker } 243*90c8c64dSAndroid Build Coastguard Worker 244*90c8c64dSAndroid Build Coastguard Worker async init() { 245*90c8c64dSAndroid Build Coastguard Worker await this.unzip() 246*90c8c64dSAndroid Build Coastguard Worker this.parseMetadata() 247*90c8c64dSAndroid Build Coastguard Worker } 248*90c8c64dSAndroid Build Coastguard Worker 249*90c8c64dSAndroid Build Coastguard Worker} 250*90c8c64dSAndroid Build Coastguard Worker 251*90c8c64dSAndroid Build Coastguard Workerexport class DefaultMap extends Map { 252*90c8c64dSAndroid Build Coastguard Worker /** Reload the original get method. Return the original key value if 253*90c8c64dSAndroid Build Coastguard Worker * the key does not exist. 254*90c8c64dSAndroid Build Coastguard Worker * @param {Any} key 255*90c8c64dSAndroid Build Coastguard Worker */ 256*90c8c64dSAndroid Build Coastguard Worker getWithDefault(key) { 257*90c8c64dSAndroid Build Coastguard Worker if (!this.has(key)) return key 258*90c8c64dSAndroid Build Coastguard Worker return this.get(key) 259*90c8c64dSAndroid Build Coastguard Worker } 260*90c8c64dSAndroid Build Coastguard Worker} 261*90c8c64dSAndroid Build Coastguard Worker 262*90c8c64dSAndroid Build Coastguard Workerexport class OpType { 263*90c8c64dSAndroid Build Coastguard Worker /** 264*90c8c64dSAndroid Build Coastguard Worker * OpType.mapType create a map that could resolve the operation 265*90c8c64dSAndroid Build Coastguard Worker * types. The operation types are encoded as numbers in 266*90c8c64dSAndroid Build Coastguard Worker * update_metadata.proto and must be decoded before any usage. 267*90c8c64dSAndroid Build Coastguard Worker */ 268*90c8c64dSAndroid Build Coastguard Worker constructor() { 269*90c8c64dSAndroid Build Coastguard Worker let /** Array<{String: Number}>*/ types = update_metadata_pb.InstallOperation.Type 270*90c8c64dSAndroid Build Coastguard Worker this.mapType = new DefaultMap() 271*90c8c64dSAndroid Build Coastguard Worker for (let key of Object.keys(types)) { 272*90c8c64dSAndroid Build Coastguard Worker this.mapType.set(types[key], key) 273*90c8c64dSAndroid Build Coastguard Worker } 274*90c8c64dSAndroid Build Coastguard Worker } 275*90c8c64dSAndroid Build Coastguard Worker} 276*90c8c64dSAndroid Build Coastguard Worker 277*90c8c64dSAndroid Build Coastguard Workerexport class MergeOpType { 278*90c8c64dSAndroid Build Coastguard Worker /** 279*90c8c64dSAndroid Build Coastguard Worker * MergeOpType create a map that could resolve the COW merge operation 280*90c8c64dSAndroid Build Coastguard Worker * types. This is very similar to OpType class except that one is for 281*90c8c64dSAndroid Build Coastguard Worker * installation operations. 282*90c8c64dSAndroid Build Coastguard Worker */ 283*90c8c64dSAndroid Build Coastguard Worker constructor() { 284*90c8c64dSAndroid Build Coastguard Worker let /** Array<{String: Number}>*/ types = 285*90c8c64dSAndroid Build Coastguard Worker update_metadata_pb.CowMergeOperation.Type 286*90c8c64dSAndroid Build Coastguard Worker this.mapType = new DefaultMap() 287*90c8c64dSAndroid Build Coastguard Worker for (let key of Object.keys(types)) { 288*90c8c64dSAndroid Build Coastguard Worker this.mapType.set(types[key], key) 289*90c8c64dSAndroid Build Coastguard Worker } 290*90c8c64dSAndroid Build Coastguard Worker } 291*90c8c64dSAndroid Build Coastguard Worker} 292*90c8c64dSAndroid Build Coastguard Worker 293*90c8c64dSAndroid Build Coastguard Workerexport function octToHex(bufferArray, space = true, maxLine = 16) { 294*90c8c64dSAndroid Build Coastguard Worker let hex_table = '' 295*90c8c64dSAndroid Build Coastguard Worker for (let i = 0; i < bufferArray.length; i++) { 296*90c8c64dSAndroid Build Coastguard Worker if (bufferArray[i].toString(16).length === 2) { 297*90c8c64dSAndroid Build Coastguard Worker hex_table += bufferArray[i].toString(16) + (space ? ' ' : '') 298*90c8c64dSAndroid Build Coastguard Worker } else { 299*90c8c64dSAndroid Build Coastguard Worker hex_table += '0' + bufferArray[i].toString(16) + (space ? ' ' : '') 300*90c8c64dSAndroid Build Coastguard Worker } 301*90c8c64dSAndroid Build Coastguard Worker if ((i + 1) % maxLine == 0) { 302*90c8c64dSAndroid Build Coastguard Worker hex_table += '\n' 303*90c8c64dSAndroid Build Coastguard Worker } 304*90c8c64dSAndroid Build Coastguard Worker } 305*90c8c64dSAndroid Build Coastguard Worker return hex_table 306*90c8c64dSAndroid Build Coastguard Worker} 307*90c8c64dSAndroid Build Coastguard Worker 308*90c8c64dSAndroid Build Coastguard Worker/** 309*90c8c64dSAndroid Build Coastguard Worker * Trim the prefix in an entry. This is required because the lookbehind 310*90c8c64dSAndroid Build Coastguard Worker * regular expression is not supported in safari yet. 311*90c8c64dSAndroid Build Coastguard Worker * @param {String} entry 312*90c8c64dSAndroid Build Coastguard Worker * @param {String} prefix 313*90c8c64dSAndroid Build Coastguard Worker * @return String 314*90c8c64dSAndroid Build Coastguard Worker */ 315*90c8c64dSAndroid Build Coastguard Workerfunction trimEntry(entry, prefix) { 316*90c8c64dSAndroid Build Coastguard Worker return entry.slice(prefix.length + 1, entry.length) 317*90c8c64dSAndroid Build Coastguard Worker}