/* xdelta3 module for RomPatcher.js v20180918 - Marc Robledo 2016-2018 - http://www.marcrobledo.com/license */ var XDELTA_MAGIC=0xd6c3c400; function XDelta(patchData){ this.patchData=patchData; } XDelta.prototype.toString=function(){ return '' } /* code borrowed from https://github.com/google/xdelta3-decoder-js/blob/master/tests/testA.html */ XDelta.prototype.apply=function(romFile){ var target; try{ target=XDelta3Decoder.decode(this.patchData, new Uint8Array(romFile.fileReader.result)); }catch(e){ alert('EXCEPTION: ' + e.message); return false; } var tempFile=new MarcBinFile(target.byteLength); var targetUint8Array=new Uint8Array(target); for(var i=0; i 0) { this.acache.near_array = allocArray(this.acache.s_near, 0); } if (this.acache.s_same > 0) { this.acache.same_array = allocArray(this.acache.s_same * 256, 0); } }; /** * Parses the delta file data and produces the targetWindow data. * @return {!Uint8Array} */ _XDelta3Decoder.prototype.xd3_decode_input = function() { if (this.delta[0] != 0xD6 || // 'V' with MSB set this.delta[1] != 0xC3 || // 'C' with MSB set this.delta[2] != 0xC4 || // 'D' with MSB set this.delta[3] != 0) { // unused but be set to zero throw new Error('XD3_INVALID_INPUT invalid magic'); } this.position = 4; this.dec_hdr_ind = this.delta[this.position++]; if (this.dec_hdr_ind & VCD_INVHDR) { throw new Error('VCD_INVHDR unrecognized header indicator bits set'); } if (this.dec_hdr_ind & VCD_SECONDARY) { throw new Error('VCD_SECONDARY not implemented'); } if (this.dec_hdr_ind & VCD_CODETABLE) { throw new Error('VCD_CODETABLE support was removed'); } else { /* Use the default table. */ this.acache.s_near = __rfc3284_code_table_desc.near_modes; this.acache.s_same = __rfc3284_code_table_desc.same_modes; this.code_table = xd3_rfc3284_code_table(); } this.xd3_alloc_cache(); if (this.dec_hdr_ind & VCD_APPHEADER) { this.dec_appheadsz = this.getInteger(); // Note: appHeader does not have a 0-termination. this.dec_apphead = this.xd3_alloc(this.dec_appheadsz + 1); this.xd3_decode_bytes(this.dec_apphead, 0, this.dec_appheadsz); this.dec_apphead[this.dec_appheadsz + 1] = 0; } //var targetLength = 0; while (true) { if (this.position >= this.delta.length) { break; } //targetLength += this.handleWindow(); } return this.dec_buffer.bytes; }; _XDelta3Decoder.prototype.xd3_decode_init_window = function() { this.dec_cpylen = 0; this.dec_cpyoff = 0; // this.dec_cksumbytes = 0; xd3_init_cache(this.acache); } _XDelta3Decoder.prototype.handleWindow = function() { this.dec_win_ind = this.delta[this.position++]; // DEC_WININD if (this.dec_win_ind & ~7) { throw new Error('VCD_INVWIN unexpected bits set'); } this.current_window = this.dec_window_count; this.dec_winstart += this.dec_tgtlen; this.xd3_decode_init_window(); var SRCORTGT = VCD_SOURCE | VCD_TARGET; var srcortgt = SRCORTGT & this.dec_win_ind; // If using a source or target data segment: read the lenght and position // integers. if (srcortgt) { this.dec_cpylen = this.getInteger(); // DEC_CPYLEN } this.dec_position = this.dec_cpylen; if (srcortgt) { var sourcePosition = this.getInteger(); // DEC_CPYOFF this.dec_cpyoff = sourcePosition; } this.dec_enclen = this.getInteger(); // DEC_ENCLEN // Calculate the position if the delta was actually read. // var positionAfterDelta = this.position + this.dec_enclen; // Get the target window length. this.dec_tgtlen = this.getInteger(); // DEC_TGTLEN this.dec_del_ind = this.getByte(); // DEC_DELIND this.data_sect.size = this.getInteger(); // DEC_DATALEN this.inst_sect.size = this.getInteger(); // DEC_INSTLEN this.addr_sect.size = this.getInteger(); // DEC_ADDRLEN if (this.dec_win_ind & VCD_ADLER32) { // DEC_CKSUM this.dec_cksum = this.xd3_decode_allocate(4); for (var i = 0; i < 4; i += 1) { this.dec_adler32 = (this.dec_adler32 << 8) | this.dec_cksum[i]; } } this.xd3_decode_sections(); /* In the C++ code: * To speed VCD_SOURCE block-address calculations, the source * cpyoff_blocks and cpyoff_blkoff are pre-computed. * However, in this Javascript code there is no 'blocks'. */ if (this.dec_win_ind & VCD_SOURCE) { this.src.cpyoff_blkoff = this.dec_cpyoff; } this.xd3_decode_emit(); return this.dec_tgtlen; }; /** * This function only has code if the preprocessor statement * "#if SECONDARY_ANY" is set. SECONDARY_ANY does not seem to be set. */ _XDelta3Decoder.prototype.xd3_decode_secondary_sections = function() { // }; /** * @param {!xd3_desect} sect */ _XDelta3Decoder.prototype.xd3_decode_section = function(sect) { // It is possible to just point into the buffer but perhaps that can be done // later. sect.bytes = this.xd3_decode_allocate(sect.size); }; _XDelta3Decoder.prototype.xd3_decode_sections = function() { this.xd3_decode_section(this.data_sect); this.xd3_decode_section(this.inst_sect); this.xd3_decode_section(this.addr_sect); this.xd3_decode_secondary_sections(); this.xd3_decode_setup_buffers(); }; _XDelta3Decoder.prototype.xd3_decode_setup_buffers = function() { this.dec_buffer = new DataObject(new Uint8Array(this.dec_tgtlen)); }; var VCD_SELF = 0; var VCD_HERE = 1; /** * xd3_decode_address * @param {number} here * @param {number} mode * @param {!xd3_desect} sect */ _XDelta3Decoder.prototype.xd3_decode_address = function(here, mode, sect) { var val; var same_start = 2 + this.acache.s_near; if (mode < same_start) { val = sect.getInteger(); switch (mode) { case VCD_SELF: break; case VCD_HERE: // var old_val = val; val = here - val; break; default: val += this.acache.near_array[mode - 2]; } } else { mode -= same_start; var offset = sect.getByte(); val = this.acache.same_array[mode * 256 + offset]; } this.xd3_update_cache(this.acache, val); return val; }; /** * @param {!xd3_addr_cache} acache * @param {number} addr */ _XDelta3Decoder.prototype.xd3_update_cache = function(acache, addr) { if (acache.s_near > 0) { acache.near_array[acache.next_slot] = addr; acache.next_slot = (acache.next_slot + 1) % acache.s_near; } if (acache.s_same > 0) { acache.same_array[addr % (acache.s_same * 256)] = addr; } }; /** * @param {!xd3_hinst} inst */ _XDelta3Decoder.prototype.xd3_decode_output_halfinst = function(inst) { var take = inst.size; var blkoff; switch (inst.type) { case XD3_RUN: var val = this.data_sect.getByte(); this.dec_buffer.fill(val, take); break; case XD3_ADD: this.dec_buffer.copySect(this.data_sect, take); break; default: var overlap; var overlap_pos; if (inst.addr < this.dec_cpylen) { overlap = 0; if (this.dec_win_ind & VCD_TARGET) { throw new Error('VCD_TARGET not supported'); } else { blkoff = this.src.cpyoff_blkoff; blkoff = this.dec_cpyoff + inst.addr; } } else { overlap = 1; overlap_pos = inst.addr - this.dec_cpylen; } if (overlap) { this.dec_buffer.copyBytes(this.dec_buffer.bytes, overlap_pos, take); } else { this.dec_buffer.copyBytes(this.source.bytes, blkoff, take); } } }; /** * xref: xd3_decode_parse_halfinst * @param {!xd3_hinst} inst */ _XDelta3Decoder.prototype.xd3_decode_parse_halfinst = function(inst) { // Get size and address if necessary. if (inst.size == 0) { inst.size = this.inst_sect.getInteger(); } /* For copy instructions, read address. */ if (inst.type >= XD3_CPY) { var mode = inst.type - XD3_CPY; inst.addr = this.xd3_decode_address(this.dec_position, mode, this.addr_sect); } this.dec_position += inst.size; }; /** * xref: xd3_decode_instruction */ _XDelta3Decoder.prototype.xd3_decode_instruction = function() { var code_table = this.code_table; var instPair = this.inst_sect.getByte(); this.dec_current1.type = code_table.tableRows[instPair].type1; this.dec_current1.size = code_table.tableRows[instPair].size1; // dec_current1.addr keeps it previous value. this.dec_current2.type = code_table.tableRows[instPair].type2; this.dec_current2.size = code_table.tableRows[instPair].size2; // dec_current2.addr keeps it previous value. /* For each instruction with a real operation, decode the * corresponding size and addresses if necessary. Assume a * code-table may have NOOP in either position, although this is * unlikely. */ if (this.dec_current1.type != XD3_NOOP) { this.xd3_decode_parse_halfinst(this.dec_current1); } if (this.dec_current2.type != XD3_NOOP) { this.xd3_decode_parse_halfinst(this.dec_current2); } }; _XDelta3Decoder.prototype.xd3_decode_finish_window = function() { // stream->dec_winbytes = 0; // stream->dec_state = DEC_FINISH; this.data_sect.pos = 0; this.inst_sect.pos = 0; this.addr_sect.pos = 0; }; _XDelta3Decoder.prototype.xd3_decode_emit = function() { var instLength = this.inst_sect.bytes.byteLength; /* Decode next instruction pair. */ while (this.inst_sect.pos < instLength) { this.xd3_decode_instruction(); /* Output dec_current1 */ if (this.dec_current1.type != XD3_NOOP) { this.xd3_decode_output_halfinst(this.dec_current1); } /* Output dec_current2 */ if (this.dec_current2.type != XD3_NOOP) { this.xd3_decode_output_halfinst(this.dec_current2); } } if (this.dec_win_ind & VCD_ADLER32) { var a32 = adler32(1, this.dec_buffer.bytes, 0, this.dec_tgtlen); if (a32 != this.dec_adler32) { throw new Error('target window checksum mismatch'); } } /* Finished with a window. */ this.xd3_decode_finish_window(); }; _XDelta3Decoder.prototype.xd3_alloc = function(length) { return new Uint8Array(length); }; _XDelta3Decoder.prototype.xd3_decode_bytes = function(bytes, pos, length) { for (var i = 0; i < length; i++) { bytes[pos + i] = this.delta[this.position++]; } }; _XDelta3Decoder.prototype.xd3_decode_allocate = function(length) { var bytes = new Uint8Array(this.delta.slice(this.position, this.position + length)); this.position += length; return bytes; }; _XDelta3Decoder.prototype.getByte = function() { return this.delta[this.position++]; }; _XDelta3Decoder.prototype.getInteger = function() { var maxBytes = Math.min(20, this.delta.length - this.position); var integer = 0; for (var i = 0; i < maxBytes; i++) { var aPart = this.delta[this.position++]; integer += aPart & 0x7F; if (!(aPart & 0x80)) { return integer; } integer <<= 7; } throw new Error('delta integer too long'); }; /** * The code table. * @param {!Array} tableRows * @constructor * @struct */ var xd3_dinst_table = function(tableRows) { /** @type {!Array} */ this.tableRows = tableRows; }; /** * xd3_hinst * @constructor */ function xd3_hinst() { this.type = XD3_NOOP; this.size = 0; this.addr = 0; } /** * The code-table double instruction. * @constructor */ function xd3_dinst() { /** @type {number} */ this.type1 = XD3_NOOP; /** @type {number} */ this.size1 = 0; /** @type {number} */ this.type2 = XD3_NOOP; /** @type {number} */ this.size2 = 0; } /** * @param {!xd3_code_table_desc} desc * @return {!xd3_dinst_table} */ function xd3_build_code_table(desc) { var row = 0; var tableRows = new Array(256); for (var i = 0; i < 256; i++) { tableRows[i] = new xd3_dinst(); } var cpyModes = 2 + desc.near_modes + desc.same_modes; // The single RUN command. tableRows[row++].type1 = XD3_RUN; // The ADD only commands. tableRows[row++].type1 = XD3_ADD; for (var size1 = 1; size1 <= desc.add_sizes; size1++) { tableRows[row].type1 = XD3_ADD; tableRows[row++].size1 = size1; } // The Copy only commands. for (var mode = 0; mode < cpyModes; mode++) { tableRows[row++].type1 = XD3_CPY + mode; for (var size1 = MIN_MATCH; size1 < MIN_MATCH + desc.cpy_sizes; size1++) { tableRows[row].type1 = XD3_CPY + mode; tableRows[row++].size1 = size1; } } // The Add/Copy commands. for (var mode = 0; mode < cpyModes; mode++) { for (var size1 = 1; size1 <= desc.addcopy_add_max; size1++) { var max = (mode < 2 + desc.near_modes) ? // desc.addcopy_near_cpy_max : desc.addcopy_same_cpy_max; for (var size2 = MIN_MATCH; size2 <= max; size2++) { tableRows[row].type1 = XD3_ADD; tableRows[row].size1 = size1; tableRows[row].type2 = XD3_CPY + mode; tableRows[row++].size2 = size2; } } } // The Copy/Add commands. for (var mode = 0; mode < cpyModes; mode++) { var max = (mode < 2 + desc.near_modes) ? // desc.copyadd_near_cpy_max : desc.copyadd_same_cpy_max; for (var size1 = MIN_MATCH; size1 <= max; size1++) { for (var size2 = 1; size2 <= desc.copyadd_add_max; size2++) { tableRows[row].type1 = XD3_CPY + mode; tableRows[row].size1 = size1; tableRows[row].type2 = XD3_ADD; tableRows[row++].size2 = size2; } } } return new xd3_dinst_table(tableRows); } /** * @constructor */ function xd3_code_table_desc() { this.add_sizes = 0; this.near_modes = 0; this.same_modes = 0; this.cpy_sizes = 0; this.addcopy_add_max = 0; this.addcopy_near_cpy_max = 0; this.addcopy_same_cpy_max = 0; this.copyadd_add_max = 0; this.copyadd_near_cpy_max = 0; this.copyadd_same_cpy_max = 0; } /** * This builds the __rfc3284_code_table_desc * Assumes a single RUN instruction * Assumes that MIN_MATCH is 4. * @return {!xd3_code_table_desc} */ function build_rfc3284_code_table_desc() { var desc = new xd3_code_table_desc(); desc.add_sizes = 17; desc.near_modes = 4; desc.same_modes = 3; desc.cpy_sizes = 15; desc.addcopy_add_max = 4; desc.addcopy_near_cpy_max = 6; desc.addcopy_same_cpy_max = 4; desc.copyadd_add_max = 1; desc.copyadd_near_cpy_max = 4; desc.copyadd_same_cpy_max = 4; // xd3_code_table_sizes addcopy_max_sizes[MAX_MODES]; // { {6,163,3},{6,175,3},{6,187,3},{6,199,3},{6,211,3},{6,223,3}, // {4,235,1},{4,239,1},{4,243,1} }, // xd3_code_table_sizes copyadd_max_sizes[MAX_MODES]; // { {4,247,1},{4,248,1},{4,249,1},{4,250,1},{4,251,1},{4,252,1}, // {4,253,1},{4,254,1},{4,255,1} }, return desc; } var __rfc3284_code_table_desc = build_rfc3284_code_table_desc(); var A32_BASE = 65521; /* Largest prime smaller than 2^16 */ var A32_NMAX = 5552; /* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ // 1140 #define A32_DO1(buf,i) {s1 += buf[i]; s2 += s1;} // 1141 #define A32_DO2(buf,i) A32_DO1(buf,i); A32_DO1(buf,i+1); // 1142 #define A32_DO4(buf,i) A32_DO2(buf,i); A32_DO2(buf,i+2); // 1143 #define A32_DO8(buf,i) A32_DO4(buf,i); A32_DO4(buf,i+4); // 1144 #define A32_DO16(buf) A32_DO8(buf,0); A32_DO8(buf,8); /** * Calculated the Adler32 checksum. * @param {number} adler I'm not sure what this is. * @param {!Uint8Array} buf * @param {number} pos * @param {number} len * @return {number} */ function adler32(adler, buf, pos, len) { var s1 = adler & 0xffff; var s2 = (adler >> 16) & 0xffff; var k; while (len > 0) { k = (len < A32_NMAX) ? len : A32_NMAX; len -= k; if (k != 0) { do { s1 += buf[pos++]; s2 += s1; } while (--k); } s1 %= A32_BASE; s2 %= A32_BASE; } return (s2 << 16) | s1; } /** * @constructor */ function xd3_addr_cache(s_near, s_same) { this.s_near = s_near; this.s_same = s_same; this.next_slot = 0; /* the circular index for near */ this.near_array = null; /* array of size s_near */ this.same_array = null; /* array of size s_same*256 */ } /** * @param {!xd3_addr_cache} acache */ function xd3_init_cache(acache) { if (acache.s_near > 0) { for (var i = 0; i < acache.near_array.length; i++) { acache.near_array[i] = 0; } acache.next_slot = 0; } if (acache.s_same > 0) { for (var i = 0; i < acache.same_array.length; i++) { acache.same_array[i] = 0; } } } /** * Used by the decoder to buffer input in sections. * XDelta3 C++ struct. * @constructor * @struct */ function xd3_desect() { /** * The buffer as a slice of the backingBuffer; * @type {?Uint8Array} */ this.bytes = null; /** @type {number} */ this.size = 0; /** @type {number} */ this.pos = 0; } /** * Gets a byte from the section. * @return {number} */ xd3_desect.prototype.getByte = function() { if (!this.bytes) { throw new Error('bytes not set'); } return this.bytes[this.pos++]; }; /** * Gets an integer from the section. * XDelta3 integers are encodes as a variable number of 7 bit bytes. Bit 8, the * most significant bit is used to indicate more bytes needed. * @return {number} */ xd3_desect.prototype.getInteger = function() { if (!this.bytes) { throw new Error('bytes not set'); } var val = 0; for (var i = 0; i < 10; i++) { var aByte = this.bytes[this.pos++]; val += aByte & 0x7F; if (!(aByte & 0x80)) { return val; } val <<= 7; } throw new Error('invalid number'); }; /** * Builds a default code table. * @return {!xd3_dinst_table} */ function xd3_rfc3284_code_table() { return xd3_build_code_table(__rfc3284_code_table_desc); } /** * Allocates and initializes a Javascript Array. * @return {!Array} */ function allocArray(len, val) { var arr = new Array(len); for (var i = 0; i < len; i++) { arr[i] = val; } return arr; } /** * @constructor */ function xd3_source() { /** @type {number} */ this.cpyoff_blkoff = -1; } /** * @param {!Uint8Array} bytes * @constructor */ function DataObject(bytes) { this.pos = 0; this.bytes = bytes; }; DataObject.prototype.getByte = function() { return this.bytes[this.pos++]; }; DataObject.prototype.getInteger = function() { var val = 0; for (var i = 0; i < 10; i++) { var aByte = this.bytes[this.pos++]; val += aByte & 0x7F; if (!(aByte & 0x80)) { return val; } val <<= 7; } throw new Error('invalid number'); }; DataObject.prototype.fill = function(val, length) { // TODO(bstell): see if there is a function for this. for (var i = 0; i < length; i++) { this.bytes[this.pos++] = val; } }; /** * @param {!xd3_desect} sect * @param {number} length */ DataObject.prototype.copySect = function(sect, length) { // TODO(bstell): see if there is a function for this. for (var i = 0; i < length; i++) { this.bytes[this.pos++] = sect.bytes[sect.pos++]; } }; DataObject.prototype.copyBytes = function(bytes, offset, length) { // TODO(bstell): see if there is a function for this. for (var i = 0; i < length; i++) { this.bytes[this.pos++] = bytes[offset++]; } }; })();