مقارنة الملفات الثنائية - محسّن

أداة احترافية سريعة لمقارنة وتحرير ملفات ECU بصيغة .Bin

معالجة سريعة عرض كامل للملفات شريط سحب متزامن تحرير مباشر

الملف الأصلي

اسحب الملف هنا أو انقر للتحديد

يدعم ملفات .bin, .hex, .ecu (حتى 50 MB)

الملف المعدل

اسحب الملف هنا أو انقر للتحديد

يدعم ملفات .bin, .hex, .ecu (حتى 50 MB)

يمكنك استخدام هذه الأداة حتى قبل تحميل ملفين للمقارنة

تقرير تلقائي بواسطة 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 - تقرير مقارنة الملفات الثنائية\n'; report += '='.repeat(60) + '\n'; report += `تاريخ: ${date}\n`; report += `الملف الأصلي: ${file1Name} (${file1Data.length.toLocaleString()} bytes)\n`; report += `الملف المعدل: ${file2Name} (${file2Data.length.toLocaleString()} bytes)\n`; report += '='.repeat(60) + '\n\n'; report += `إجمالي البايتات: ${totalBytes.toLocaleString()}\n`; report += `البايتات المعدلة: ${modCount.toLocaleString()}\n`; report += `البايتات المضافة: ${addCount.toLocaleString()}\n`; report += `البايتات المحذوفة: ${delCount.toLocaleString()}\n`; report += `إجمالي الفروقات: ${differences.length.toLocaleString()}\n`; report += `نسبة التغيير: ${pct}%\n\n`; report += '='.repeat(60) + '\n\n'; report += 'تفاصيل الفروقات:\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'?'معدل ':diff.type==='added'?'مضاف ':'محذوف'} |`; if (diff.original !== null) report += ` أصلي: 0x${diff.original.toString(16).toUpperCase().padStart(2,'0')}`; if (diff.modified !== null) report += ` → جديد: 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; // إضافة لاحقة _compared للملف المحفوظ 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); // إظهار رسالة نجاح showNotification(`تم حفظ الملف: ${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('يرجى تحميل ملف أولاً', '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('الملف المحدد غير محمّل', '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('يرجى اختيار عملية واحدة على الأقل', '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('لم يتم العثور على أنماط قابلة للتعديل في هذا الملف', '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('حدث خطأ أثناء تنفيذ 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 تم بنجاح!

ملخص العملية:

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

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

`}

التعديلات المطبقة:

${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'); 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(`تم تحميل الملف: ${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);