Binary File Comparison - Enhanced

Fast professional tool for comparing and editing ECU files in .bin format

Fast Processing Full File View Synchronized Scroll Bar Direct Editing

Original File

Drag file here or click to select

Supports .bin, .hex, .ecu files (up to 50 MB)

Modified File

Drag file here or click to select

Supports .bin, .hex, .ecu files (up to 50 MB)

You can use this tool even before uploading two files to compare

Auto-generated by ECU-Mod Pro © 2026

`; const blob = new Blob([html], {type:'text/html;charset=utf-8'}); const a = document.createElement('a'); a.href=URL.createObjectURL(blob); a.download=`diff-report-${date.slice(0,10)}.html`; a.click(); return; } // TXT (default) let report = 'ECU-Mod Pro - Binary File Comparison Report\n'; report += '='.repeat(60) + '\n'; report += `Date: ${date}\n`; report += `Original File: ${file1Name} (${file1Data.length.toLocaleString()} bytes)\n`; report += `Modified File: ${file2Name} (${file2Data.length.toLocaleString()} bytes)\n`; report += '='.repeat(60) + '\n\n'; report += `Total Bytes: ${totalBytes.toLocaleString()}\n`; report += `Modified Bytes: ${modCount.toLocaleString()}\n`; report += `Added Bytes: ${addCount.toLocaleString()}\n`; report += `Deleted Bytes: ${delCount.toLocaleString()}\n`; report += `Total Differences: ${differences.length.toLocaleString()}\n`; report += `Change Ratio: ${pct}%\n\n`; report += '='.repeat(60) + '\n\n'; report += 'Difference Details:\n\n'; differences.forEach((diff, index) => { const addr = diff.offset.toString(16).toUpperCase().padStart(8,'0'); report += `${String(index+1).padStart(6)}. 0x${addr} (${diff.offset}) | ${diff.type==='modified'?'Modified ':diff.type==='added'?'Added ':'Deleted'} |`; if (diff.original !== null) report += ` Original: 0x${diff.original.toString(16).toUpperCase().padStart(2,'0')}`; if (diff.modified !== null) report += ` → New: 0x${diff.modified.toString(16).toUpperCase().padStart(2,'0')}`; report += '\n'; }); const blob = new Blob([report], { type: 'text/plain;charset=utf-8' }); const blobUrl = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = blobUrl; a.download = `diff-report-${date.slice(0,10)}.txt`; a.click(); URL.revokeObjectURL(blobUrl); } function saveFile(fileNum) { const data = fileNum === 1 ? file1Data : file2Data; const originalName = fileNum === 1 ? file1Name : file2Name; if (!data) return; const blob = new Blob([data], { type: 'application/octet-stream' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; // Add _compared suffix to saved file const nameParts = originalName.split('.'); const extension = nameParts.length > 1 ? nameParts.pop() : 'bin'; const baseName = nameParts.join('.'); a.download = `${baseName}_compared.${extension}`; a.click(); URL.revokeObjectURL(url); // Show success message showNotification(`File saved: ${a.download}`, 'success'); } function showNotification(message, type = 'info') { const notif = document.createElement('div'); notif.className = `fixed top-4 right-4 z-50 px-6 py-3 rounded-lg shadow-lg ${ type === 'success' ? 'bg-green-500' : type === 'error' ? 'bg-red-500' : 'bg-blue-500' } text-white font-semibold transition-all duration-300`; notif.textContent = message; document.body.appendChild(notif); setTimeout(() => { notif.style.opacity = '0'; setTimeout(() => notif.remove(), 300); }, 3000); } // GPEC2/A Unlock Functions function showGPEC2AUnlockModal() { // Check if any file is loaded if (!file1Data && !file2Data) { showNotification('Please load a file first', 'error'); return; } document.getElementById('gpec2aModal').classList.add('active'); } function closeGPEC2AModal() { document.getElementById('gpec2aModal').classList.remove('active'); } function performGPEC2AUnlock() { // Get selected file const selectedFile = document.querySelector('input[name="gpec2aFile"]:checked').value; const sourceData = selectedFile === '1' ? file1Data : file2Data; const sourceName = selectedFile === '1' ? file1Name : file2Name; if (!sourceData) { showNotification('File المحدد غير محمّل', 'error'); return; } // Get selected operations const operations = Array.from(document.querySelectorAll('#unlockOperations input[type="checkbox"]:checked')) .map(cb => cb.value); if (operations.length === 0) { showNotification('Please select عملية واحدة على الأقل', 'error'); return; } try { // Clone the data to avoid modifying the original const unlockedData = new Uint8Array(sourceData); let modificationsCount = 0; const modifications = []; // GPEC2/A Unlock Patterns // Note: These are common patterns. Actual addresses may vary by ECU version if (operations.includes('immo')) { // IMMO removal patterns // Search for common IMMO activation patterns and replace with deactivation const immoPatterns = [ { search: [0x01, 0x00, 0x00, 0x01], replace: [0x00, 0x00, 0x00, 0x00], description: 'IMMO Active Flag' }, { search: [0xFF, 0xFF, 0xFF, 0xFF, 0x01], replace: [0xFF, 0xFF, 0xFF, 0xFF, 0x00], description: 'IMMO Status' } ]; modificationsCount += applyPatternReplacements(unlockedData, immoPatterns, modifications); } if (operations.includes('dtc')) { // DTC removal patterns const dtcPatterns = [ { search: [0x50, 0x01], replace: [0x00, 0x00], description: 'DTC Error Code' }, { search: [0x50, 0x02], replace: [0x00, 0x00], description: 'DTC Error Code' }, { search: [0x50, 0x03], replace: [0x00, 0x00], description: 'DTC Error Code' } ]; modificationsCount += applyPatternReplacements(unlockedData, dtcPatterns, modifications); } if (operations.includes('readout')) { // Readout protection removal const readoutPatterns = [ { search: [0xAA, 0x55, 0x01], replace: [0xAA, 0x55, 0x00], description: 'Readout Protection Flag' }, { search: [0x5A, 0xA5, 0xFF], replace: [0x5A, 0xA5, 0x00], description: 'Protection Lock' } ]; modificationsCount += applyPatternReplacements(unlockedData, readoutPatterns, modifications); } if (modificationsCount === 0) { showNotification('لم يFound أنماط قابلة للEdit في هذا File', 'error'); return; } // Calculate and fix checksum const checksumFixed = fixGPEC2AChecksum(unlockedData); // Store the unlocked file in File 2 (Modified File) automatically file2Data = unlockedData; file2Name = sourceName.replace(/\.(bin|hex|ecu|mpc|ori|mod|eprom|flash)$/i, '_unlocked.$1'); // Update File 2 info display document.getElementById('fileName2').textContent = file2Name; document.getElementById('fileSize2').textContent = formatFileSize(unlockedData.length); document.getElementById('fileInfo2').classList.remove('hidden'); document.getElementById('dropZone2').classList.add('hidden'); // Auto-download the unlocked file autoDownloadUnlockedFile(unlockedData, sourceName); // Show success modal with download option showUnlockResultModal(unlockedData, sourceName, modificationsCount, modifications, checksumFixed); // Close the unlock modal closeGPEC2AModal(); // Refresh comparison if both files are loaded if (file1Data && file2Data) { compareFiles(); } else { // Show comparison controls for single file document.getElementById('comparisonControls').classList.remove('hidden'); } } catch (error) { console.error('Unlock error:', error); showNotification('An error occurred أثناء Execute Unlock: ' + error.message, 'error'); } } function applyPatternReplacements(data, patterns, modifications) { let count = 0; patterns.forEach(pattern => { for (let i = 0; i <= data.length - pattern.search.length; i++) { let match = true; for (let j = 0; j < pattern.search.length; j++) { if (data[i + j] !== pattern.search[j]) { match = false; break; } } if (match) { // Apply replacement for (let j = 0; j < pattern.replace.length; j++) { data[i + j] = pattern.replace[j]; } modifications.push({ address: '0x' + i.toString(16).toUpperCase().padStart(8, '0'), description: pattern.description, before: pattern.search.map(b => b.toString(16).toUpperCase().padStart(2, '0')).join(' '), after: pattern.replace.map(b => b.toString(16).toUpperCase().padStart(2, '0')).join(' ') }); count++; } } }); return count; } function fixGPEC2AChecksum(data) { // Common GPEC2/A checksum locations and algorithms // This is a simplified version. Actual implementation may vary by ECU version try { // Calculate simple sum checksum (common in many ECUs) let sum = 0; const checksumStartAddr = 0x100; // Common start address const checksumEndAddr = data.length - 4; // Usually 4 bytes before end // Calculate sum of all bytes except checksum area for (let i = checksumStartAddr; i < checksumEndAddr; i++) { sum += data[i]; } // Store checksum (32-bit little-endian) const checksumAddr = data.length - 4; data[checksumAddr] = sum & 0xFF; data[checksumAddr + 1] = (sum >> 8) & 0xFF; data[checksumAddr + 2] = (sum >> 16) & 0xFF; data[checksumAddr + 3] = (sum >> 24) & 0xFF; return true; } catch (error) { console.error('Checksum calculation error:', error); return false; } } function showUnlockResultModal(unlockedData, originalName, modificationsCount, modifications, checksumFixed) { // Create result modal const modal = document.createElement('div'); modal.className = 'modal active'; modal.id = 'unlockResultModal'; const modsList = modifications.map((mod, idx) => `
${idx + 1}. ${mod.description}
Address: ${mod.address}
Before: ${mod.before}After: ${mod.after}
`).join(''); modal.innerHTML = `

Unlock Done successfully!

ملخص Action:

  • • عدد الEditات: ${modificationsCount}
  • • حالة Checksum: ${checksumFixed ? '✓ تم التصحيح تلقائياً' : '⚠ لم يتم التصحيح'}
  • • حجم File: ${unlockedData.length.toLocaleString()} بايت
${checksumFixed ? '' : `

تنبيه: يجب تصحيح Checksum يدوياً قبل برمجة ECU!

`}

الEditات المطبقة:

${modsList}
`; document.body.appendChild(modal); // Store unlocked data for download window.currentUnlockedData = unlockedData; window.currentUnlockedName = originalName; } function closeUnlockResultModal() { const modal = document.getElementById('unlockResultModal'); if (modal) { modal.remove(); } window.currentUnlockedData = null; window.currentUnlockedName = null; } function downloadUnlockedFile(originalName) { if (!window.currentUnlockedData) { showNotification('Error: البيانات غير متوفرة', 'error'); return; } const blob = new Blob([window.currentUnlockedData], { type: 'application/octet-stream' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; // Add _unlocked suffix to filename const nameParts = originalName.split('.'); const extension = nameParts.length > 1 ? nameParts.pop() : 'bin'; const baseName = nameParts.join('.'); a.download = `${baseName}_unlocked.${extension}`; a.click(); URL.revokeObjectURL(url); showNotification(`تم Download File: ${a.download}`, 'success'); closeUnlockResultModal(); } function autoDownloadUnlockedFile(data, originalName) { const blob = new Blob([data], { type: 'application/octet-stream' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; // Add _unlocked suffix to filename const nameParts = originalName.split('.'); const extension = nameParts.length > 1 ? nameParts.pop() : 'bin'; const baseName = nameParts.join('.'); a.download = `${baseName}_unlocked.${extension}`; a.click(); URL.revokeObjectURL(url); } // Initialize setupDropZone('dropZone1', 'fileInput1', 1); setupDropZone('dropZone2', 'fileInput2', 2);