(function(window, document) { "use strict"; // form labels often need unique IDs - this can be used to generate some window.Patcher_uniqueid = 0; var createID = function() { window.Patcher_uniqueid++; return "dllpatch_" + window.Patcher_uniqueid; }; var bytesMatch = function(buffer, offset, bytes) { for(var i = 0; i < bytes.length; i++) { if(buffer[offset+i] != bytes[i]) return false; } return true; }; var replace = function(buffer, offset, bytes) { for(var i = 0; i < bytes.length; i++) { buffer[offset+i] = bytes[i]; } } var whichBytesMatch = function(buffer, offset, bytesArray) { for(var i = 0; i < bytesArray.length; i++) { if(bytesMatch(buffer, offset, bytesArray[i])) return i; } return -1; } // Each unique kind of patch should have createUI, validatePatch, applyPatch, // updateUI class StandardPatch { constructor(options) { this.name = options.name; this.patches = options.patches; this.tooltip = options.tooltip; } createUI(parent) { var id = createID(); var label = this.name; var patch = $('
', {'class' : 'patch'}); this.checkbox = $('')[0]; patch.append(this.checkbox); patch.append(''); if(this.tooltip) { patch.append('
' + this.tooltip + '
'); } parent.append(patch); } updateUI(file) { this.checkbox.checked = this.checkPatchBytes(file) === "on"; } validatePatch(file) { var status = this.checkPatchBytes(file); if(status === "on") { console.log('"' + this.name + '"', "is enabled!"); } else if(status === "off") { console.log('"' + this.name + '"', "is disabled!"); } else { return '"' + this.name + '" is neither on nor off! Have you got the right file?'; } } applyPatch(file) { this.replaceAll(file, this.checkbox.checked); } replaceAll(file, featureOn) { for(var i = 0; i < this.patches.length; i++) { replace(file, this.patches[i].offset, featureOn? this.patches[i].on : this.patches[i].off); } } checkPatchBytes(file) { var patchStatus = ""; for(var i = 0; i < this.patches.length; i++) { var patch = this.patches[i]; if(bytesMatch(file, patch.offset, patch.off)) { if(patchStatus === "") { patchStatus = "off"; } else if(patchStatus != "off"){ return "on/off mismatch within patch"; } } else if(bytesMatch(file, patch.offset, patch.on)) { if(patchStatus === "") { patchStatus = "on"; } else if(patchStatus != "on"){ return "on/off mismatch within patch"; } } else { return "patch neither on nor off"; } } return patchStatus; } } class DynamicPatch { constructor(options) { this.name = options.name; this.patches = options.patches; this.tooltip = options.tooltip; this.mode = options.mode; this.target = options.target; } createUI(parent) { var id = createID(); var label = this.name; this.ui = $('
', {'class' : 'patch'}); this.checkbox = $('')[0]; this.ui.append(this.checkbox); this.ui.append(''); if(this.tooltip) { this.ui.append('
' + this.tooltip + '
'); } parent.append(this.ui); } updateUI(file) { if (this.mode === 'all') { this.checkbox.checked = this.checkPatchAll(file, true) === "on"; } else { this.checkbox.checked = this.checkPatch(file, true) === "on"; } } validatePatch(file) { var status = this.mode === 'all' ? this.checkPatchAll(file) : this.checkPatch(file); if(status === "on") { console.log('"' + this.name + '"', "is enabled!"); } else if(status === "off") { console.log('"' + this.name + '"', "is disabled!"); } else { return '"' + this.name + '" is neither on nor off! Have you got the right file?'; } } applyPatch(file) { this.replaceAll(file, this.checkbox.checked); } replaceAll(file, featureOn) { for(var i = 0; i < this.patches.length; i++) { if (Array.isArray(this.patches[i].offset)) { this.patches[i].offset.forEach((offset) => { if (this.target === 'string') { replace(file, offset, new TextEncoder().encode(featureOn? this.patches[i].on : this.patches[i].off)); } else { this.patches[i].on = this.patches[i].on.map((patch, idx) => patch === 'XX' ? file[offset + idx] : patch); this.patches[i].off = this.patches[i].off.map((patch, idx) => patch === 'XX' ? file[offset + idx] : patch); replace(file, offset, featureOn? this.patches[i].on : this.patches[i].off) } } ); } else { if (this.target === 'string') { replace(file, this.patches[i].offset, new TextEncoder().encode(featureOn? this.patches[i].on : this.patches[i].off)); } else { this.patches[i].on = this.patches[i].on.map((patch, idx) => patch === 'XX' ? file[this.patches[i].offset + idx] : patch); this.patches[i].off = this.patches[i].off.map((patch, idx) => patch === 'XX' ? file[this.patches[i].offset + idx] : patch); replace(file, this.patches[i].offset, featureOn? this.patches[i].on : this.patches[i].off); } } } } checkPatch(file, updateUiFlag = false) { var patchStatus = ""; if (updateUiFlag) { var listUi = $('