Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /**
- * Ultimate GBA ROM Patcher
- * Combines SRAM patching and battery patching functionality with exact original logic
- */
- class UltimateROMPatcher {
- constructor() {
- this.progressSteps = [
- 'Validating ROM file',
- 'Applying SRAM patches',
- 'Injecting battery payload',
- 'Hooking save functions',
- 'Finalizing ROM'
- ];
- this.currentStep = 0;
- // Initialize UI elements
- this.initializeElements();
- this.bindEvents();
- // Initialize UI state
- this.initializeUIState();
- // Initialize patterns and payload from original projects
- this.initializeSramPatterns();
- this.initializeBatteryPatterns();
- }
- initializeElements() {
- this.elements = {
- romFile: document.getElementById('romFile'),
- enableSramPatching: document.getElementById('enableSramPatching'),
- enableBatteryPatching: document.getElementById('enableBatteryPatching'),
- batteryModeGroup: document.getElementById('batteryModeGroup'),
- batteryModeRadios: document.querySelectorAll('input[name="batteryMode"]'),
- patchButton: document.getElementById('patchButton'),
- fileInfo: document.getElementById('fileInfo'),
- progressSection: document.getElementById('progressSection'),
- progressFill: document.getElementById('progressFill'),
- progressText: document.getElementById('progressText'),
- progressSteps: document.getElementById('progressSteps'),
- statusMessages: document.getElementById('statusMessages'),
- downloadSection: document.getElementById('downloadSection'),
- downloadLink: document.getElementById('downloadLink')
- };
- }
- bindEvents() {
- this.elements.romFile.addEventListener('change', (e) => this.handleFileSelect(e));
- this.elements.patchButton.addEventListener('click', () => this.startPatching());
- this.elements.enableBatteryPatching.addEventListener('change', (e) => this.toggleBatteryMode(e));
- // Initialize drag and drop functionality
- this.initializeDragAndDrop();
- }
- initializeUIState() {
- // Set initial state for battery mode options based on checkbox
- const batteryEnabled = this.elements.enableBatteryPatching.checked;
- this.toggleBatteryModeState(batteryEnabled);
- }
- toggleBatteryMode(event) {
- const isEnabled = event.target.checked;
- this.toggleBatteryModeState(isEnabled);
- }
- toggleBatteryModeState(isEnabled) {
- const batteryRadios = this.elements.batteryModeRadios;
- batteryRadios.forEach(radio => {
- radio.disabled = !isEnabled;
- });
- this.elements.batteryModeGroup.classList.toggle('disabled', !isEnabled);
- }
- // DRAG AND DROP FUNCTIONALITY
- initializeDragAndDrop() {
- const dropZone = document.getElementById('dropZone');
- // Prevent default drag behaviors
- ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
- dropZone.addEventListener(eventName, this.preventDefaults.bind(this), false);
- document.body.addEventListener(eventName, this.preventDefaults.bind(this), false);
- });
- // Highlight drop zone when item is dragged over it
- ['dragenter', 'dragover'].forEach(eventName => {
- dropZone.addEventListener(eventName, this.highlight.bind(this), false);
- });
- ['dragleave', 'drop'].forEach(eventName => {
- dropZone.addEventListener(eventName, this.unhighlight.bind(this), false);
- });
- // Handle dropped files
- dropZone.addEventListener('drop', this.handleDrop.bind(this), false);
- }
- preventDefaults(e) {
- e.preventDefault();
- e.stopPropagation();
- }
- highlight(e) {
- const dropZone = document.getElementById('dropZone');
- dropZone.classList.add('drag-over');
- // Check if dragged item contains files
- if (e.dataTransfer.items) {
- const hasFiles = Array.from(e.dataTransfer.items).some(item => item.kind === 'file');
- if (hasFiles) {
- dropZone.classList.add('drag-active');
- }
- }
- }
- unhighlight(e) {
- const dropZone = document.getElementById('dropZone');
- dropZone.classList.remove('drag-over', 'drag-active');
- }
- handleDrop(e) {
- const dt = e.dataTransfer;
- const files = dt.files;
- this.handleFiles(files);
- }
- handleFiles(files) {
- if (files.length === 0) {
- this.showFileInfo('❌ No files dropped', 'error');
- return;
- }
- if (files.length > 1) {
- this.showFileInfo('❌ Please drop only one ROM file', 'error');
- this.elements.patchButton.disabled = true;
- return;
- }
- const file = files[0];
- // Validate file type
- if (!file.name.toLowerCase().endsWith('.gba')) {
- this.showFileInfo('❌ Please drop a .gba file', 'error');
- this.elements.patchButton.disabled = true;
- return;
- }
- // Validate file size (32MB max)
- const maxSize = 32 * 1024 * 1024; // 32MB
- if (file.size > maxSize) {
- this.showFileInfo('❌ ROM file too large (max 32MB)', 'error');
- this.elements.patchButton.disabled = true;
- return;
- }
- // Update the file input with the dropped file
- this.updateFileInput(file);
- // Process the file as if it was selected normally
- this.showFileInfo(`✅ ${file.name} (${(file.size / 1024 / 1024).toFixed(2)} MB)`, 'success');
- this.elements.patchButton.disabled = false;
- // Update drop zone text
- this.updateDropZoneText('ROM file loaded - Ready to patch!');
- }
- updateFileInput(file) {
- // Create a new FileList with the dropped file
- const dt = new DataTransfer();
- dt.items.add(file);
- this.elements.romFile.files = dt.files;
- }
- updateDropZoneText(text) {
- const fileText = document.querySelector('.file-text');
- if (fileText) {
- fileText.textContent = text;
- }
- }
- // EXACT SRAM PATCHING LOGIC FROM ORIGINAL PROJECT - COMPLETE VERSION
- initializeSramPatterns() {
- // Define byte patterns from Patterns.h - EXACT COPY
- const IDENT_FLASH1M_V102 = [0x46, 0x4C, 0x41, 0x53, 0x48, 0x31, 0x4D, 0x5F, 0x56, 0x31, 0x30, 0x32, 0x00];
- const FLASH1M_V102_MARKER_1 = [0x05, 0x4b, 0xaa, 0x21, 0x19, 0x70, 0x05, 0x4a, 0x55, 0x21, 0x11, 0x70, 0xb0, 0x21, 0x19, 0x70, 0xe0, 0x21, 0x09, 0x05, 0x08, 0x70, 0x70, 0x47];
- const FLASH1M_V102_REPLACE_1 = [0x05, 0x4B, 0x80, 0x21, 0x09, 0x02, 0x09, 0x22, 0x12, 0x06, 0x9F, 0x44, 0x90, 0x21, 0x09, 0x05, 0x00, 0x00, 0x00, 0x00, 0x08, 0x70, 0x70, 0x47];
- const FLASH1M_V102_MARKER_2 = [0x55, 0x55, 0x00, 0x0e, 0xaa, 0x2a, 0x00, 0x0e, 0x30, 0xb5, 0x91, 0xb0, 0x68, 0x46, 0x00, 0xf0, 0xf3, 0xf8, 0x6d, 0x46, 0x01, 0x35];
- const FLASH1M_V102_REPLACE_2 = [0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x30, 0xB5, 0x91, 0xB0, 0x68, 0x46, 0x00, 0xF0, 0xF3, 0xF8, 0x6D, 0x46, 0x01, 0x35];
- const FLASH1M_V102_MARKER_3 = [0x06, 0x4A, 0xAA, 0x20, 0x10, 0x70, 0x05, 0x49, 0x55, 0x20, 0x08, 0x70, 0x90, 0x20, 0x10, 0x70, 0x10, 0xA9, 0x03, 0x4A, 0x10, 0x1C, 0x08, 0xE0, 0x00, 0x00, 0x55, 0x55, 0x00, 0x0E, 0xAA, 0x2A, 0x00, 0x0E, 0x20, 0x4E, 0x00, 0x00, 0x08, 0x88, 0x01, 0x38, 0x08, 0x80, 0x08, 0x88, 0x00, 0x28, 0xF9, 0xD1, 0x0C, 0x48];
- const FLASH1M_V102_REPLACE_3 = [0x06, 0x4A, 0xAA, 0x20, 0x00, 0x00, 0x05, 0x49, 0x55, 0x20, 0x00, 0x00, 0x90, 0x20, 0x00, 0x00, 0x10, 0xA9, 0x03, 0x4A, 0x10, 0x1C, 0x08, 0xE0, 0x00, 0x00, 0x55, 0x55, 0x00, 0x0E, 0xAA, 0x2A, 0x00, 0x0E, 0x20, 0x4E, 0x00, 0x00, 0x08, 0x88, 0x01, 0x38, 0x08, 0x80, 0x08, 0x88, 0x00, 0x28, 0xF9, 0xD1, 0x0C, 0x48, 0x13, 0x20, 0x13, 0x20, 0x00, 0x06, 0x04, 0x0C, 0xE0, 0x20, 0x00, 0x05, 0x62, 0x20, 0x62, 0x20, 0x00, 0x06, 0x00, 0x0E, 0x04, 0x43, 0x07, 0x49, 0xAA, 0x20, 0x00, 0x00, 0x07, 0x4A, 0x55, 0x20, 0x00, 0x00, 0xF0, 0x20, 0x00, 0x00, 0x00, 0x00];
- const FLASH1M_V102_MARKER_4 = [0x14, 0x49, 0xAA, 0x24, 0x0C, 0x70, 0x13, 0x4B, 0x55, 0x22, 0x1A, 0x70, 0x80, 0x20, 0x08, 0x70, 0x0C, 0x70, 0x1A, 0x70, 0x10, 0x20, 0x08, 0x70];
- const FLASH1M_V102_REPLACE_4 = [0x0E, 0x21, 0x09, 0x06, 0xFF, 0x24, 0x80, 0x22, 0x13, 0x4B, 0x52, 0x02, 0x01, 0x3A, 0x8C, 0x54, 0xFC, 0xD1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
- const FLASH1M_V102_MARKER_5 = [0x13, 0x49, 0xAA, 0x25, 0x0D, 0x70, 0x13, 0x4B, 0x55, 0x22, 0x1A, 0x70, 0x80, 0x20, 0x08, 0x70, 0x0D, 0x70, 0x1A, 0x70, 0x30, 0x20, 0x20, 0x70];
- const FLASH1M_V102_REPLACE_5 = [0x13, 0x49, 0xFF, 0x25, 0x08, 0x22, 0x00, 0x00, 0x52, 0x02, 0x01, 0x3A, 0xA5, 0x54, 0xFC, 0xD1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
- const FLASH1M_V102_MARKER_6 = [0x0A, 0x4C, 0xAA, 0x22, 0x22, 0x70, 0x09, 0x4B, 0x55, 0x22, 0x1A, 0x70, 0xA0, 0x22, 0x22, 0x70, 0x02, 0x78, 0x0A, 0x70];
- const FLASH1M_V102_REPLACE_6 = [0x0A, 0x4C, 0xAA, 0x22, 0x00, 0x00, 0x09, 0x4B, 0x55, 0x22, 0x00, 0x00, 0xA0, 0x22, 0x00, 0x00, 0x02, 0x78, 0x0A, 0x70];
- const IDENT_FLASH1M_V103 = [0x46, 0x4C, 0x41, 0x53, 0x48, 0x31, 0x4D, 0x5F, 0x56, 0x31, 0x30, 0x33, 0x00];
- const FLASH1M_V103_MARKER_1 = [0x05, 0x4b, 0xaa, 0x21, 0x19, 0x70, 0x05, 0x4a, 0x55, 0x21, 0x11, 0x70, 0xb0, 0x21, 0x19, 0x70, 0xe0, 0x21, 0x09, 0x05, 0x08, 0x70, 0x70, 0x47];
- const FLASH1M_V103_REPLACE_1 = [0x05, 0x4B, 0x80, 0x21, 0x09, 0x02, 0x09, 0x22, 0x12, 0x06, 0x9F, 0x44, 0x90, 0x21, 0x09, 0x05, 0x00, 0x00, 0x00, 0x00, 0x08, 0x70, 0x70, 0x47];
- const FLASH1M_V103_MARKER_2 = [0x55, 0x55, 0x00, 0x0e, 0xaa, 0x2a, 0x00, 0x0e, 0x30, 0xb5, 0x91, 0xb0, 0x68, 0x46, 0x00, 0xf0, 0xf3, 0xf8, 0x6d, 0x46, 0x01, 0x35];
- const FLASH1M_V103_REPLACE_2 = [0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x30, 0xB5, 0x91, 0xB0, 0x68, 0x46, 0x00, 0xF0, 0xF3, 0xF8, 0x6D, 0x46, 0x01, 0x35];
- const FLASH1M_V103_MARKER_3 = [0x06, 0x4A, 0xAA, 0x20, 0x10, 0x70, 0x05, 0x49, 0x55, 0x20, 0x08, 0x70, 0x90, 0x20, 0x10, 0x70, 0x10, 0xA9, 0x03, 0x4A, 0x10, 0x1C, 0x08, 0xE0, 0x00, 0x00, 0x55, 0x55, 0x00, 0x0E, 0xAA, 0x2A, 0x00, 0x0E, 0x20, 0x4E, 0x00, 0x00, 0x08, 0x88, 0x01, 0x38, 0x08, 0x80, 0x08, 0x88, 0x00, 0x28, 0xF9, 0xD1, 0x0C, 0x48];
- const FLASH1M_V103_REPLACE_3 = [0x06, 0x4A, 0xAA, 0x20, 0x00, 0x00, 0x05, 0x49, 0x55, 0x20, 0x00, 0x00, 0x90, 0x20, 0x00, 0x00, 0x10, 0xA9, 0x03, 0x4A, 0x10, 0x1C, 0x08, 0xE0, 0x00, 0x00, 0x55, 0x55, 0x00, 0x0E, 0xAA, 0x2A, 0x00, 0x0E, 0x20, 0x4E, 0x00, 0x00, 0x08, 0x88, 0x01, 0x38, 0x08, 0x80, 0x08, 0x88, 0x00, 0x28, 0xF9, 0xD1, 0x0C, 0x48, 0x13, 0x20, 0x13, 0x20, 0x00, 0x06, 0x04, 0x0C, 0xE0, 0x20, 0x00, 0x05, 0x62, 0x20, 0x62, 0x20, 0x00, 0x06, 0x00, 0x0E, 0x04, 0x43, 0x07, 0x49, 0xAA, 0x20, 0x00, 0x00, 0x07, 0x4A, 0x55, 0x20, 0x00, 0x00, 0xF0, 0x20, 0x00, 0x00, 0x00, 0x00];
- const FLASH1M_V103_MARKER_4 = [0x14, 0x49, 0xAA, 0x24, 0x0C, 0x70, 0x13, 0x4B, 0x55, 0x22, 0x1A, 0x70, 0x80, 0x20, 0x08, 0x70, 0x0C, 0x70, 0x1A, 0x70, 0x10, 0x20, 0x08, 0x70];
- const FLASH1M_V103_REPLACE_4 = [0x0E, 0x21, 0x09, 0x06, 0xFF, 0x24, 0x80, 0x22, 0x13, 0x4B, 0x52, 0x02, 0x01, 0x3A, 0x8C, 0x54, 0xFC, 0xD1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
- const FLASH1M_V103_MARKER_5 = [0x14, 0x49, 0xAA, 0x25, 0x0D, 0x70, 0x14, 0x4B, 0x55, 0x22, 0x1A, 0x70, 0x80, 0x20, 0x08, 0x70, 0x0D, 0x70, 0x1A, 0x70, 0x30, 0x20, 0x20, 0x70];
- const FLASH1M_V103_REPLACE_5 = [0x14, 0x49, 0xFF, 0x25, 0x08, 0x22, 0x00, 0x00, 0x52, 0x02, 0x01, 0x3A, 0xA5, 0x54, 0xFC, 0xD1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
- const FLASH1M_V103_MARKER_6 = [0x0C, 0x4A, 0xAA, 0x20, 0x10, 0x70, 0x0B, 0x49, 0x55, 0x20, 0x08, 0x70, 0xA0, 0x20, 0x10, 0x70, 0x27, 0x70, 0x09, 0x48];
- const FLASH1M_V103_REPLACE_6 = [0x0C, 0x4A, 0xAA, 0x20, 0x00, 0x00, 0x0B, 0x49, 0x55, 0x20, 0x00, 0x00, 0xA0, 0x20, 0x00, 0x00, 0x27, 0x70, 0x09, 0x48];
- const FLASH1M_V103_MARKER_7 = [0x0A, 0x4C, 0xAA, 0x22, 0x22, 0x70, 0x09, 0x4B, 0x55, 0x22, 0x1A, 0x70, 0xA0, 0x22, 0x22, 0x70, 0x02, 0x78, 0x0A, 0x70];
- const FLASH1M_V103_REPLACE_7 = [0x0A, 0x4C, 0xAA, 0x22, 0x00, 0x00, 0x09, 0x4B, 0x55, 0x22, 0x00, 0x00, 0xA0, 0x22, 0x00, 0x00, 0x02, 0x78, 0x0A, 0x70];
- const IDENT_FLASH512 = [0x46, 0x4C, 0x41, 0x53, 0x48, 0x35, 0x31, 0x32, 0x00];
- const FLASH512_MARKER_1 = [0xf0, 0xb5, 0xa0, 0xb0, 0x0d, 0x1c, 0x16, 0x1c, 0x1f, 0x1c, 0x03, 0x04, 0x1c, 0x0c, 0x0f, 0x4a, 0x10, 0x88, 0x0f, 0x49, 0x08, 0x40, 0x03, 0x21, 0x08, 0x43, 0x10, 0x80, 0x0d, 0x48, 0x00, 0x68, 0x01, 0x68, 0x80, 0x20, 0x80, 0x02];
- const FLASH512_REPLACE_1 = [0x70, 0xb5, 0xa0, 0xb0, 0x00, 0x03, 0x40, 0x18, 0xe0, 0x21, 0x09, 0x05, 0x09, 0x18, 0x08, 0x78, 0x10, 0x70, 0x01, 0x3b, 0x01, 0x32, 0x01, 0x31, 0x00, 0x2b, 0xf8, 0xd1, 0x00, 0x20, 0x20, 0xb0, 0x70, 0xbc, 0x02, 0xbc, 0x08, 0x47];
- const FLASH512_MARKER_2 = [0xff, 0xf7, 0x88, 0xfd, 0x00, 0x04, 0x03, 0x0c];
- const FLASH512_REPLACE_2 = [0x1b, 0x23, 0x1b, 0x02, 0x32, 0x20, 0x03, 0x43];
- const FLASH512_MARKER_3 = [0x70, 0xb5, 0x90, 0xb0, 0x15, 0x4d, 0x29, 0x88];
- const FLASH512_REPLACE_3 = [0x00, 0xb5, 0x00, 0x20, 0x02, 0xbc, 0x08, 0x47];
- const FLASH512_MARKER_4 = [0x70, 0xb5, 0x46, 0x46, 0x40, 0xb4, 0x90, 0xb0];
- const FLASH512_REPLACE_4 = [0x00, 0xb5, 0x00, 0x20, 0x02, 0xbc, 0x08, 0x47];
- const FLASH512_MARKER_5 = [0xf0, 0xb5, 0x90, 0xb0, 0x0f, 0x1c, 0x00, 0x04, 0x04, 0x0c, 0x03, 0x48, 0x00, 0x68, 0x40, 0x89, 0x84, 0x42, 0x05, 0xd3, 0x01, 0x48, 0x41, 0xe0];
- const FLASH512_REPLACE_5 = [0x7c, 0xb5, 0x90, 0xb0, 0x00, 0x03, 0x0a, 0x1c, 0xe0, 0x21, 0x09, 0x05, 0x09, 0x18, 0x01, 0x23, 0x1b, 0x03, 0x10, 0x78, 0x08, 0x70, 0x01, 0x3b, 0x01, 0x32, 0x01, 0x31, 0x00, 0x2b, 0xf8, 0xd1, 0x00, 0x20, 0x10, 0xb0, 0x7c, 0xbc, 0x02, 0xbc, 0x08, 0x47];
- // Additional patterns from C++ source - EXACT COPY FROM ORIGINAL
- const IDENT_FLASH_V120 = [0x46, 0x4C, 0x41, 0x53, 0x48, 0x5F, 0x56, 0x31, 0x32, 0x30]; // "FLASH_V120"
- const IDENT_FLASH_V121 = [0x46, 0x4C, 0x41, 0x53, 0x48, 0x5F, 0x56, 0x31, 0x32, 0x31]; // "FLASH_V121"
- const IDENT_FLASH_V123 = [0x46, 0x4C, 0x41, 0x53, 0x48, 0x5F, 0x56, 0x31, 0x32, 0x33]; // "FLASH_V123"
- const IDENT_FLASH_V124 = [0x46, 0x4C, 0x41, 0x53, 0x48, 0x5F, 0x56, 0x31, 0x32, 0x34]; // "FLASH_V124"
- const IDENT_FLASH_V125 = [0x46, 0x4C, 0x41, 0x53, 0x48, 0x5F, 0x56, 0x31, 0x32, 0x35]; // "FLASH_V125"
- const IDENT_FLASH_V126 = [0x46, 0x4C, 0x41, 0x53, 0x48, 0x5F, 0x56, 0x31, 0x32, 0x36]; // "FLASH_V126"
- const IDENT_EEPROM_V120 = [0x45, 0x45, 0x50, 0x52, 0x4F, 0x4D, 0x5F, 0x56, 0x31, 0x32, 0x30]; // "EEPROM_V120"
- const IDENT_EEPROM_V121 = [0x45, 0x45, 0x50, 0x52, 0x4F, 0x4D, 0x5F, 0x56, 0x31, 0x32, 0x31]; // "EEPROM_V121"
- const IDENT_EEPROM_V122 = [0x45, 0x45, 0x50, 0x52, 0x4F, 0x4D, 0x5F, 0x56, 0x31, 0x32, 0x32]; // "EEPROM_V122"
- const IDENT_EEPROM_V124 = [0x45, 0x45, 0x50, 0x52, 0x4F, 0x4D, 0x5F, 0x56, 0x31, 0x32, 0x34]; // "EEPROM_V124"
- const IDENT_EEPROM_V126 = [0x45, 0x45, 0x50, 0x52, 0x4F, 0x4D, 0x5F, 0x56, 0x31, 0x32, 0x36]; // "EEPROM_V126"
- // Patch patterns from C++ source (flash.cpp and eeprom.cpp) - EXACT COPY FROM ORIGINAL
- // FLASH_1 (FLASH_V120, FLASH_V121)
- const FLASH_1_MARKER_1 = [0x90, 0xb5, 0x93, 0xb0, 0x6f, 0x46, 0x39, 0x1d, 0x08, 0x1c, 0x00, 0xf0];
- const FLASH_1_REPLACE_1 = [0x00, 0xb5, 0x3d, 0x20, 0x00, 0x02, 0x1f, 0x21, 0x08, 0x43, 0x02, 0xbc, 0x08, 0x47];
- const FLASH_1_MARKER_2 = [0x80, 0xb5, 0x94, 0xb0, 0x6f, 0x46, 0x39, 0x1c, 0x08, 0x80, 0x38, 0x1c, 0x01, 0x88, 0x0f, 0x29, 0x04, 0xd9, 0x01, 0x48, 0x56, 0xe0, 0x00, 0x00, 0xff, 0x80, 0x00, 0x00, 0x23, 0x48, 0x23, 0x49, 0x0a, 0x88, 0x23];
- const FLASH_1_REPLACE_2 = [0x7c, 0xb5, 0x00, 0x07, 0x00, 0x0c, 0xe0, 0x21, 0x09, 0x05, 0x09, 0x18, 0x01, 0x23, 0x1b, 0x03, 0xff, 0x20, 0x08, 0x70, 0x01, 0x3b, 0x01, 0x31, 0x00, 0x2b, 0xfa, 0xd1, 0x00, 0x20, 0x7c, 0xbc, 0x02, 0xbc, 0x08, 0x47];
- const FLASH_1_MARKER_3 = [0x80, 0xb5, 0x94, 0xb0, 0x6f, 0x46, 0x79, 0x60, 0x39, 0x1c, 0x08, 0x80, 0x38, 0x1c, 0x01, 0x88, 0x0f, 0x29, 0x03, 0xd9, 0x00, 0x48, 0x73, 0xe0, 0xff, 0x80, 0x00, 0x00, 0x38, 0x1c, 0x01, 0x88];
- const FLASH_1_REPLACE_3 = [0x7c, 0xb5, 0x90, 0xb0, 0x00, 0x03, 0x0a, 0x1c, 0xe0, 0x21, 0x09, 0x05, 0x09, 0x18, 0x01, 0x23, 0x1b, 0x03, 0x10, 0x78, 0x08, 0x70, 0x01, 0x3b, 0x01, 0x32, 0x01, 0x31, 0x00, 0x2b, 0xf8, 0xd1, 0x00, 0x20, 0x10, 0xb0, 0x7c, 0xbc, 0x08, 0xbc, 0x08, 0x47];
- // FLASH_2 (FLASH_V123, FLASH_V124)
- const FLASH_2_MARKER_1 = [0xff, 0xf7, 0xaa, 0xff, 0x00, 0x04, 0x03, 0x0c];
- const FLASH_2_REPLACE_1 = [0x1b, 0x23, 0x1b, 0x02, 0x32, 0x20, 0x03, 0x43];
- const FLASH_2_MARKER_2 = [0x70, 0xb5, 0x90, 0xb0, 0x15, 0x4d];
- const FLASH_2_REPLACE_2 = [0x00, 0x20, 0x70, 0x47, 0x15, 0x4d];
- const FLASH_2_MARKER_3 = [0x70, 0xb5, 0x46, 0x46, 0x40, 0xb4, 0x90, 0xb0, 0x00];
- const FLASH_2_REPLACE_3 = [0x00, 0x20, 0x70, 0x47, 0x40, 0xb4, 0x90, 0xb0, 0x00];
- const FLASH_2_MARKER_4 = [0xf0, 0xb5, 0x90, 0xb0, 0x0f, 0x1c, 0x00, 0x04, 0x04, 0x0c, 0x0f, 0x2c, 0x04, 0xd9, 0x01, 0x48, 0x40, 0xe0, 0x00, 0x00, 0xff, 0x80, 0x00, 0x00, 0x20, 0x1c, 0xff, 0xf7, 0xd7, 0xfe, 0x00, 0x04, 0x05, 0x0c, 0x00, 0x2d, 0x35, 0xd1];
- const FLASH_2_REPLACE_4 = [0x70, 0xb5, 0x00, 0x03, 0x0a, 0x1c, 0xe0, 0x21, 0x09, 0x05, 0x41, 0x18, 0x01, 0x23, 0x1b, 0x03, 0x10, 0x78, 0x08, 0x70, 0x01, 0x3b, 0x01, 0x32, 0x01, 0x31, 0x00, 0x2b, 0xf8, 0xd1, 0x00, 0x20, 0x70, 0xbc, 0x02, 0xbc, 0x08, 0x47];
- // FLASH_3 (FLASH_V125, FLASH_V126)
- const FLASH_3_MARKER_1 = [0xff, 0xf7, 0xaa, 0xff, 0x00, 0x04, 0x03, 0x0c];
- const FLASH_3_REPLACE_1 = [0x1b, 0x23, 0x1b, 0x02, 0x32, 0x20, 0x03, 0x43];
- const FLASH_3_MARKER_2 = [0x70, 0xb5, 0x90, 0xb0, 0x15, 0x4d];
- const FLASH_3_REPLACE_2 = [0x00, 0x20, 0x70, 0x47, 0x15, 0x4d];
- const FLASH_3_MARKER_3 = [0x70, 0xb5, 0x46, 0x46, 0x40, 0xb4, 0x90, 0xb0, 0x00];
- const FLASH_3_REPLACE_3 = [0x00, 0x20, 0x70, 0x47, 0x40, 0xb4, 0x90, 0xb0, 0x00];
- const FLASH_3_MARKER_4 = [0xf0, 0xb5, 0x90, 0xb0, 0x0f, 0x1c, 0x00, 0x04, 0x04, 0x0c, 0x0f, 0x2c, 0x04, 0xd9, 0x01, 0x48, 0x40, 0xe0, 0x00, 0x00, 0xff, 0x80, 0x00, 0x00, 0x20, 0x1c, 0xff, 0xf7, 0xd7, 0xfe, 0x00, 0x04, 0x05, 0x0c, 0x00, 0x2d, 0x35, 0xd1];
- const FLASH_3_REPLACE_4 = [0x70, 0xb5, 0x00, 0x03, 0x0a, 0x1c, 0xe0, 0x21, 0x09, 0x05, 0x41, 0x18, 0x01, 0x23, 0x1b, 0x03, 0x10, 0x78, 0x08, 0x70, 0x01, 0x3b, 0x01, 0x32, 0x01, 0x31, 0x00, 0x2b, 0xf8, 0xd1, 0x00, 0x20, 0x70, 0xbc, 0x02, 0xbc, 0x08, 0x47];
- // EEPROM (EEPROM_V120, EEPROM_V121, EEPROM_V122) - Note: C++ uses 0 as wildcard
- const EEPROM_MARKER_1 = [0xa2, 0xb0, 0x0d, 0x1c, 0x00, 0x04, 0x03, 0x0c, 0x03, 0x48, 0x00, 0x68, 0x80, 0x88, 0x83, 0x42, 0x05, 0xd3, 0x01, 0x48, 0x00, 0xe0];
- const EEPROM_REPLACE_1 = [0x00, 0x04, 0x0a, 0x1c, 0x40, 0x0b, 0xe0, 0x21, 0x09, 0x05, 0x41, 0x18, 0x07, 0x31, 0x00, 0x23, 0x08, 0x78, 0x10, 0x70, 0x01, 0x33, 0x01, 0x32, 0x01, 0x39, 0x07, 0x2b, 0xf8, 0xd9, 0x00, 0x20, 0x70, 0xbc, 0x02, 0xbc, 0x08, 0x47];
- const EEPROM_MARKER_2 = [0x30, 0xb5, 0xa9, 0xb0, 0x0d, 0x1c, 0x00, 0x04, 0x04, 0x0c, 0x03, 0x48, 0x00, 0x68, 0x80, 0x88, 0x84, 0x42, 0x05, 0xd3, 0x01, 0x48, 0x00, 0xe0];
- const EEPROM_REPLACE_2 = [0x70, 0xb5, 0x00, 0x04, 0x0a, 0x1c, 0x40, 0x0b, 0xe0, 0x21, 0x09, 0x05, 0x41, 0x18, 0x07, 0x31, 0x00, 0x23, 0x10, 0x78, 0x08, 0x70, 0x01, 0x33, 0x01, 0x32, 0x01, 0x39, 0x07, 0x2b, 0xf8, 0xd9, 0x00, 0x20, 0x70, 0xbc, 0x02, 0xbc, 0x08, 0x47];
- // EEPROM_V124
- const EEPROM_V124_MARKER_1 = [0xa2, 0xb0, 0x0d, 0x1c, 0x00, 0x04, 0x03, 0x0c, 0x03, 0x48, 0x00, 0x68, 0x80, 0x88, 0x83, 0x42, 0x05, 0xd3, 0x01, 0x48, 0x00, 0xe0];
- const EEPROM_V124_REPLACE_1 = [0x00, 0x04, 0x0a, 0x1c, 0x40, 0x0b, 0xe0, 0x21, 0x09, 0x05, 0x41, 0x18, 0x07, 0x31, 0x00, 0x23, 0x08, 0x78, 0x10, 0x70, 0x01, 0x33, 0x01, 0x32, 0x01, 0x39, 0x07, 0x2b, 0xf8, 0xd9, 0x00, 0x20, 0x70, 0xbc, 0x02, 0xbc, 0x08, 0x47];
- const EEPROM_V124_MARKER_2 = [0xf0, 0xb5, 0xac, 0xb0, 0x0d, 0x1c, 0x00, 0x04, 0x01, 0x0c, 0x12, 0x06, 0x17, 0x0e, 0x03, 0x48, 0x00, 0x68, 0x80, 0x88, 0x81, 0x42, 0x05, 0xd3];
- const EEPROM_V124_REPLACE_2 = [0x70, 0xb5, 0x00, 0x04, 0x0a, 0x1c, 0x40, 0x0b, 0xe0, 0x21, 0x09, 0x05, 0x41, 0x18, 0x07, 0x31, 0x00, 0x23, 0x10, 0x78, 0x08, 0x70, 0x01, 0x33, 0x01, 0x32, 0x01, 0x39, 0x07, 0x2b, 0xf8, 0xd9, 0x00, 0x20, 0x70, 0xbc, 0x02, 0xbc, 0x08, 0x47];
- // EEPROM_V126
- const EEPROM_V126_MARKER_1 = [0xa2, 0xb0, 0x0d, 0x1c, 0x00, 0x04, 0x03, 0x0c, 0x03, 0x48, 0x00, 0x68, 0x80, 0x88, 0x83, 0x42, 0x05, 0xd3, 0x01, 0x48, 0x4a, 0xe0];
- const EEPROM_V126_REPLACE_1 = [0x00, 0x04, 0x0a, 0x1c, 0x40, 0x0b, 0xe0, 0x21, 0x09, 0x05, 0x41, 0x18, 0x07, 0x31, 0x00, 0x23, 0x08, 0x78, 0x10, 0x70, 0x01, 0x33, 0x01, 0x32, 0x01, 0x39, 0x07, 0x2b, 0xf8, 0xd9, 0x00, 0x20, 0x70, 0xbc, 0x02, 0xbc, 0x08, 0x47];
- const EEPROM_V126_MARKER_2 = [0xf0, 0xb5, 0x47, 0x46, 0x80, 0xb4, 0xac, 0xb0, 0x0e, 0x1c, 0x00, 0x04, 0x05, 0x0c, 0x12, 0x06, 0x12, 0x0e, 0x90, 0x46, 0x03, 0x48, 0x00, 0x68];
- const EEPROM_V126_REPLACE_2 = [0x70, 0xb5, 0x00, 0x04, 0x0a, 0x1c, 0x40, 0x0b, 0xe0, 0x21, 0x09, 0x05, 0x41, 0x18, 0x07, 0x31, 0x00, 0x23, 0x10, 0x78, 0x08, 0x70, 0x01, 0x33, 0x01, 0x32, 0x01, 0x39, 0x07, 0x2b, 0xf8, 0xd9, 0x00, 0x20, 0x70, 0xbc, 0x02, 0xbc, 0x08, 0x47];
- // Build complete flash patterns exactly like original - EXACT COPY FROM ORIGINAL
- this.flashPatterns = [
- { IdentPattern: { Pattern: IDENT_FLASH1M_V102, PatternLength: IDENT_FLASH1M_V102.length }, Patches: [{ Marker: { Pattern: FLASH1M_V102_MARKER_1, PatternLength: FLASH1M_V102_MARKER_1.length }, Replace: { Pattern: FLASH1M_V102_REPLACE_1, PatternLength: FLASH1M_V102_REPLACE_1.length } }, { Marker: { Pattern: FLASH1M_V102_MARKER_2, PatternLength: FLASH1M_V102_MARKER_2.length }, Replace: { Pattern: FLASH1M_V102_REPLACE_2, PatternLength: FLASH1M_V102_REPLACE_2.length } }, { Marker: { Pattern: FLASH1M_V102_MARKER_3, PatternLength: FLASH1M_V102_MARKER_3.length }, Replace: { Pattern: FLASH1M_V102_REPLACE_3, PatternLength: FLASH1M_V102_REPLACE_3.length } }, { Marker: { Pattern: FLASH1M_V102_MARKER_4, PatternLength: FLASH1M_V102_MARKER_4.length }, Replace: { Pattern: FLASH1M_V102_REPLACE_4, PatternLength: FLASH1M_V102_REPLACE_4.length } }, { Marker: { Pattern: FLASH1M_V102_MARKER_5, PatternLength: FLASH1M_V102_MARKER_5.length }, Replace: { Pattern: FLASH1M_V102_REPLACE_5, PatternLength: FLASH1M_V102_REPLACE_5.length } }, { Marker: { Pattern: FLASH1M_V102_MARKER_6, PatternLength: FLASH1M_V102_MARKER_6.length }, Replace: { Pattern: FLASH1M_V102_REPLACE_6, PatternLength: FLASH1M_V102_REPLACE_6.length } }], PatchCount: 6, FlashType: 'FLASH1M_V102' },
- { IdentPattern: { Pattern: IDENT_FLASH1M_V103, PatternLength: IDENT_FLASH1M_V103.length }, Patches: [{ Marker: { Pattern: FLASH1M_V103_MARKER_1, PatternLength: FLASH1M_V103_MARKER_1.length }, Replace: { Pattern: FLASH1M_V103_REPLACE_1, PatternLength: FLASH1M_V103_REPLACE_1.length } }, { Marker: { Pattern: FLASH1M_V103_MARKER_2, PatternLength: FLASH1M_V103_MARKER_2.length }, Replace: { Pattern: FLASH1M_V103_REPLACE_2, PatternLength: FLASH1M_V103_REPLACE_2.length } }, { Marker: { Pattern: FLASH1M_V103_MARKER_3, PatternLength: FLASH1M_V103_MARKER_3.length }, Replace: { Pattern: FLASH1M_V103_REPLACE_3, PatternLength: FLASH1M_V103_REPLACE_3.length } }, { Marker: { Pattern: FLASH1M_V103_MARKER_4, PatternLength: FLASH1M_V103_MARKER_4.length }, Replace: { Pattern: FLASH1M_V103_REPLACE_4, PatternLength: FLASH1M_V103_REPLACE_4.length } }, { Marker: { Pattern: FLASH1M_V103_MARKER_5, PatternLength: FLASH1M_V103_MARKER_5.length }, Replace: { Pattern: FLASH1M_V103_REPLACE_5, PatternLength: FLASH1M_V103_REPLACE_5.length } }, { Marker: { Pattern: FLASH1M_V103_MARKER_6, PatternLength: FLASH1M_V103_MARKER_6.length }, Replace: { Pattern: FLASH1M_V103_REPLACE_6, PatternLength: FLASH1M_V103_REPLACE_6.length } }, { Marker: { Pattern: FLASH1M_V103_MARKER_7, PatternLength: FLASH1M_V103_MARKER_7.length }, Replace: { Pattern: FLASH1M_V103_REPLACE_7, PatternLength: FLASH1M_V103_REPLACE_7.length } }], PatchCount: 7, FlashType: 'FLASH1M_V103' },
- { IdentPattern: { Pattern: IDENT_FLASH512, PatternLength: IDENT_FLASH512.length }, Patches: [{ Marker: { Pattern: FLASH512_MARKER_1, PatternLength: FLASH512_MARKER_1.length }, Replace: { Pattern: FLASH512_REPLACE_1, PatternLength: FLASH512_REPLACE_1.length } }, { Marker: { Pattern: FLASH512_MARKER_2, PatternLength: FLASH512_MARKER_2.length }, Replace: { Pattern: FLASH512_REPLACE_2, PatternLength: FLASH512_REPLACE_2.length } }, { Marker: { Pattern: FLASH512_MARKER_3, PatternLength: FLASH512_MARKER_3.length }, Replace: { Pattern: FLASH512_REPLACE_3, PatternLength: FLASH512_REPLACE_3.length } }, { Marker: { Pattern: FLASH512_MARKER_4, PatternLength: FLASH512_MARKER_4.length }, Replace: { Pattern: FLASH512_REPLACE_4, PatternLength: FLASH512_REPLACE_4.length } }, { Marker: { Pattern: FLASH512_MARKER_5, PatternLength: FLASH512_MARKER_5.length }, Replace: { Pattern: FLASH512_REPLACE_5, PatternLength: FLASH512_REPLACE_5.length } }], PatchCount: 5, FlashType: 'FLASH512' },
- // Additional flash types from C++ source - EXACT COPY FROM ORIGINAL
- { IdentPattern: { Pattern: IDENT_FLASH_V120, PatternLength: IDENT_FLASH_V120.length }, Patches: [{ Marker: { Pattern: FLASH_1_MARKER_1, PatternLength: FLASH_1_MARKER_1.length }, Replace: { Pattern: FLASH_1_REPLACE_1, PatternLength: FLASH_1_REPLACE_1.length } }, { Marker: { Pattern: FLASH_1_MARKER_2, PatternLength: FLASH_1_MARKER_2.length }, Replace: { Pattern: FLASH_1_REPLACE_2, PatternLength: FLASH_1_REPLACE_2.length } }, { Marker: { Pattern: FLASH_1_MARKER_3, PatternLength: FLASH_1_MARKER_3.length }, Replace: { Pattern: FLASH_1_REPLACE_3, PatternLength: FLASH_1_REPLACE_3.length } }], PatchCount: 3, FlashType: 'FLASH_V120' },
- { IdentPattern: { Pattern: IDENT_FLASH_V121, PatternLength: IDENT_FLASH_V121.length }, Patches: [{ Marker: { Pattern: FLASH_1_MARKER_1, PatternLength: FLASH_1_MARKER_1.length }, Replace: { Pattern: FLASH_1_REPLACE_1, PatternLength: FLASH_1_REPLACE_1.length } }, { Marker: { Pattern: FLASH_1_MARKER_2, PatternLength: FLASH_1_MARKER_2.length }, Replace: { Pattern: FLASH_1_REPLACE_2, PatternLength: FLASH_1_REPLACE_2.length } }, { Marker: { Pattern: FLASH_1_MARKER_3, PatternLength: FLASH_1_MARKER_3.length }, Replace: { Pattern: FLASH_1_REPLACE_3, PatternLength: FLASH_1_REPLACE_3.length } }], PatchCount: 3, FlashType: 'FLASH_V121' },
- { IdentPattern: { Pattern: IDENT_FLASH_V123, PatternLength: IDENT_FLASH_V123.length }, Patches: [{ Marker: { Pattern: FLASH_2_MARKER_1, PatternLength: FLASH_2_MARKER_1.length }, Replace: { Pattern: FLASH_2_REPLACE_1, PatternLength: FLASH_2_REPLACE_1.length } }, { Marker: { Pattern: FLASH_2_MARKER_2, PatternLength: FLASH_2_MARKER_2.length }, Replace: { Pattern: FLASH_2_REPLACE_2, PatternLength: FLASH_2_REPLACE_2.length } }, { Marker: { Pattern: FLASH_2_MARKER_3, PatternLength: FLASH_2_MARKER_3.length }, Replace: { Pattern: FLASH_2_REPLACE_3, PatternLength: FLASH_2_REPLACE_3.length } }, { Marker: { Pattern: FLASH_2_MARKER_4, PatternLength: FLASH_2_MARKER_4.length }, Replace: { Pattern: FLASH_2_REPLACE_4, PatternLength: FLASH_2_REPLACE_4.length } }], PatchCount: 4, FlashType: 'FLASH_V123' },
- { IdentPattern: { Pattern: IDENT_FLASH_V124, PatternLength: IDENT_FLASH_V124.length }, Patches: [{ Marker: { Pattern: FLASH_2_MARKER_1, PatternLength: FLASH_2_MARKER_1.length }, Replace: { Pattern: FLASH_2_REPLACE_1, PatternLength: FLASH_2_REPLACE_1.length } }, { Marker: { Pattern: FLASH_2_MARKER_2, PatternLength: FLASH_2_MARKER_2.length }, Replace: { Pattern: FLASH_2_REPLACE_2, PatternLength: FLASH_2_REPLACE_2.length } }, { Marker: { Pattern: FLASH_2_MARKER_3, PatternLength: FLASH_2_MARKER_3.length }, Replace: { Pattern: FLASH_2_REPLACE_3, PatternLength: FLASH_2_REPLACE_3.length } }, { Marker: { Pattern: FLASH_2_MARKER_4, PatternLength: FLASH_2_MARKER_4.length }, Replace: { Pattern: FLASH_2_REPLACE_4, PatternLength: FLASH_2_REPLACE_4.length } }], PatchCount: 4, FlashType: 'FLASH_V124' },
- { IdentPattern: { Pattern: IDENT_FLASH_V125, PatternLength: IDENT_FLASH_V125.length }, Patches: [{ Marker: { Pattern: FLASH_3_MARKER_1, PatternLength: FLASH_3_MARKER_1.length }, Replace: { Pattern: FLASH_3_REPLACE_1, PatternLength: FLASH_3_REPLACE_1.length } }, { Marker: { Pattern: FLASH_3_MARKER_2, PatternLength: FLASH_3_MARKER_2.length }, Replace: { Pattern: FLASH_3_REPLACE_2, PatternLength: FLASH_3_REPLACE_2.length } }, { Marker: { Pattern: FLASH_3_MARKER_3, PatternLength: FLASH_3_MARKER_3.length }, Replace: { Pattern: FLASH_3_REPLACE_3, PatternLength: FLASH_3_REPLACE_3.length } }, { Marker: { Pattern: FLASH_3_MARKER_4, PatternLength: FLASH_3_MARKER_4.length }, Replace: { Pattern: FLASH_3_REPLACE_4, PatternLength: FLASH_3_REPLACE_4.length } }], PatchCount: 4, FlashType: 'FLASH_V125' },
- { IdentPattern: { Pattern: IDENT_FLASH_V126, PatternLength: IDENT_FLASH_V126.length }, Patches: [{ Marker: { Pattern: FLASH_3_MARKER_1, PatternLength: FLASH_3_MARKER_1.length }, Replace: { Pattern: FLASH_3_REPLACE_1, PatternLength: FLASH_3_REPLACE_1.length } }, { Marker: { Pattern: FLASH_3_MARKER_2, PatternLength: FLASH_3_MARKER_2.length }, Replace: { Pattern: FLASH_3_REPLACE_2, PatternLength: FLASH_3_REPLACE_2.length } }, { Marker: { Pattern: FLASH_3_MARKER_3, PatternLength: FLASH_3_MARKER_3.length }, Replace: { Pattern: FLASH_3_REPLACE_3, PatternLength: FLASH_3_REPLACE_3.length } }, { Marker: { Pattern: FLASH_3_MARKER_4, PatternLength: FLASH_3_MARKER_4.length }, Replace: { Pattern: FLASH_3_REPLACE_4, PatternLength: FLASH_3_REPLACE_4.length } }], PatchCount: 4, FlashType: 'FLASH_V126' },
- // EEPROM types - EXACT COPY FROM ORIGINAL
- { IdentPattern: { Pattern: IDENT_EEPROM_V120, PatternLength: IDENT_EEPROM_V120.length }, Patches: [{ Marker: { Pattern: EEPROM_MARKER_1, PatternLength: EEPROM_MARKER_1.length }, Replace: { Pattern: EEPROM_REPLACE_1, PatternLength: EEPROM_REPLACE_1.length } }, { Marker: { Pattern: EEPROM_MARKER_2, PatternLength: EEPROM_MARKER_2.length }, Replace: { Pattern: EEPROM_REPLACE_2, PatternLength: EEPROM_REPLACE_2.length } }], PatchCount: 2, FlashType: 'EEPROM_V120' },
- { IdentPattern: { Pattern: IDENT_EEPROM_V121, PatternLength: IDENT_EEPROM_V121.length }, Patches: [{ Marker: { Pattern: EEPROM_MARKER_1, PatternLength: EEPROM_MARKER_1.length }, Replace: { Pattern: EEPROM_REPLACE_1, PatternLength: EEPROM_REPLACE_1.length } }, { Marker: { Pattern: EEPROM_MARKER_2, PatternLength: EEPROM_MARKER_2.length }, Replace: { Pattern: EEPROM_REPLACE_2, PatternLength: EEPROM_REPLACE_2.length } }], PatchCount: 2, FlashType: 'EEPROM_V121' },
- { IdentPattern: { Pattern: IDENT_EEPROM_V122, PatternLength: IDENT_EEPROM_V122.length }, Patches: [{ Marker: { Pattern: EEPROM_MARKER_1, PatternLength: EEPROM_MARKER_1.length }, Replace: { Pattern: EEPROM_REPLACE_1, PatternLength: EEPROM_REPLACE_1.length } }, { Marker: { Pattern: EEPROM_MARKER_2, PatternLength: EEPROM_MARKER_2.length }, Replace: { Pattern: EEPROM_REPLACE_2, PatternLength: EEPROM_REPLACE_2.length } }], PatchCount: 2, FlashType: 'EEPROM_V122' },
- { IdentPattern: { Pattern: IDENT_EEPROM_V124, PatternLength: IDENT_EEPROM_V124.length }, Patches: [{ Marker: { Pattern: EEPROM_V124_MARKER_1, PatternLength: EEPROM_V124_MARKER_1.length }, Replace: { Pattern: EEPROM_V124_REPLACE_1, PatternLength: EEPROM_V124_REPLACE_1.length } }, { Marker: { Pattern: EEPROM_V124_MARKER_2, PatternLength: EEPROM_V124_MARKER_2.length }, Replace: { Pattern: EEPROM_V124_REPLACE_2, PatternLength: EEPROM_V124_REPLACE_2.length } }], PatchCount: 2, FlashType: 'EEPROM_V124' },
- { IdentPattern: { Pattern: IDENT_EEPROM_V126, PatternLength: IDENT_EEPROM_V126.length }, Patches: [{ Marker: { Pattern: EEPROM_V126_MARKER_1, PatternLength: EEPROM_V126_MARKER_1.length }, Replace: { Pattern: EEPROM_V126_REPLACE_1, PatternLength: EEPROM_V126_REPLACE_1.length } }, { Marker: { Pattern: EEPROM_V126_MARKER_2, PatternLength: EEPROM_V126_MARKER_2.length }, Replace: { Pattern: EEPROM_V126_REPLACE_2, PatternLength: EEPROM_V126_REPLACE_2.length } }], PatchCount: 2, FlashType: 'EEPROM_V126' }
- ];
- }
- // EXACT BATTERY PATCHING LOGIC FROM ORIGINAL PROJECT
- initializeBatteryPatterns() {
- // Embedded payload binary data extracted from working ROM - EXACT COPY
- this.embeddedPayloadBin = new Uint8Array([
- 0xc0, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
- 0x54, 0x00, 0x00, 0x00, 0xcd, 0x00, 0x00, 0x00, 0x1d, 0x01, 0x00, 0x00,
- 0xb9, 0x00, 0x00, 0x00, 0x47, 0x01, 0x00, 0x00, 0x1f, 0xb4, 0x00, 0xb5,
- 0x0a, 0x4c, 0x20, 0x88, 0x01, 0xb4, 0x00, 0x20, 0x20, 0x80, 0x73, 0xa0,
- 0xfe, 0x46, 0x00, 0x47, 0x01, 0xbc, 0x20, 0x80, 0x01, 0xbc, 0x86, 0x46,
- 0x1f, 0xbc, 0xc0, 0x46, 0xc0, 0x46, 0xc0, 0x46, 0xc0, 0x46, 0x70, 0x47,
- 0xc0, 0x46, 0xc0, 0x46, 0xc0, 0x46, 0xc0, 0x46, 0x08, 0x02, 0x00, 0x04,
- 0x01, 0x03, 0xa0, 0xe3, 0x5c, 0x10, 0x1f, 0xe5, 0x00, 0x00, 0x51, 0xe3,
- 0x19, 0x1e, 0x8f, 0xe2, 0xf8, 0x10, 0x8f, 0x12, 0x04, 0x10, 0x00, 0xe5,
- 0xfc, 0x00, 0x8f, 0xe2, 0x07, 0x0c, 0x80, 0xe2, 0x0e, 0x14, 0xa0, 0xe3,
- 0x78, 0x20, 0x1f, 0xe5, 0x01, 0x20, 0x82, 0xe0, 0x09, 0x34, 0xa0, 0xe3,
- 0x80, 0x40, 0xa0, 0xe3, 0x03, 0x40, 0xc1, 0xe5, 0x21, 0x48, 0xa0, 0xe1,
- 0x01, 0x40, 0x04, 0xe2, 0xb0, 0x40, 0xc3, 0xe1, 0x00, 0x00, 0xa0, 0xe1,
- 0x01, 0x40, 0xd0, 0xe4, 0x01, 0x40, 0xc1, 0xe4, 0x02, 0x00, 0x51, 0xe1,
- 0xf7, 0xff, 0xff, 0x3a, 0x00, 0x40, 0xa0, 0xe3, 0xb0, 0x40, 0xc3, 0xe1,
- 0xbc, 0xf0, 0x1f, 0xe5, 0x00, 0x03, 0x0e, 0x22, 0x12, 0x06, 0x10, 0x43,
- 0x01, 0x22, 0x12, 0x03, 0x03, 0x1c, 0x08, 0x1c, 0x19, 0x1c, 0xff, 0xe7,
- 0x00, 0xb5, 0xf0, 0xb4, 0x11, 0x4e, 0x37, 0x88, 0x00, 0x23, 0x33, 0x80,
- 0x09, 0x24, 0x24, 0x06, 0x0d, 0x0c, 0x01, 0x23, 0x1d, 0x40, 0x25, 0x80,
- 0x12, 0x18, 0x04, 0x78, 0x0d, 0x78, 0xac, 0x42, 0x01, 0xd0, 0x01, 0x23,
- 0x0c, 0x70, 0x01, 0x30, 0x01, 0x31, 0x90, 0x42, 0xf5, 0xd3, 0x00, 0x2b,
- 0x06, 0xd0, 0x78, 0x46, 0xfe, 0x38, 0x00, 0x88, 0x00, 0x28, 0x01, 0xd1,
- 0x00, 0xf0, 0x22, 0xf8, 0x37, 0x80, 0x00, 0x20, 0xf0, 0xbc, 0x02, 0xbc,
- 0x08, 0x47, 0x00, 0x00, 0x08, 0x02, 0x00, 0x04, 0x10, 0xb5, 0x0a, 0x1c,
- 0x08, 0x32, 0x6b, 0x46, 0x0c, 0x78, 0x01, 0x31, 0x01, 0x3b, 0x1c, 0x70,
- 0x91, 0x42, 0xf9, 0xd1, 0x0e, 0x21, 0x09, 0x06, 0xc0, 0x00, 0x09, 0x18,
- 0x08, 0x22, 0x18, 0x1c, 0x9d, 0x46, 0xff, 0xf7, 0xc5, 0xff, 0x02, 0xb0,
- 0x10, 0xbd, 0x01, 0xb4, 0x00, 0xf0, 0x02, 0xf8, 0x01, 0xbc, 0x00, 0x47,
- 0x16, 0xa0, 0x04, 0x21, 0x09, 0x06, 0x10, 0x39, 0x66, 0x22, 0x4a, 0x81,
- 0xc8, 0x60, 0x4a, 0x82, 0x70, 0x47, 0x00, 0x00, 0x30, 0x31, 0x90, 0xe5,
- 0xf3, 0x00, 0x33, 0xe3, 0x0c, 0xf0, 0x10, 0x15, 0x01, 0x10, 0xa0, 0xe3,
- 0xb2, 0x10, 0xc0, 0xe1, 0x9f, 0x30, 0xa0, 0xe3, 0x03, 0xf0, 0x29, 0xe1,
- 0x04, 0xe0, 0x2d, 0xe5, 0x1c, 0x00, 0x00, 0xeb, 0x04, 0xe0, 0x9d, 0xe4,
- 0x92, 0x30, 0xa0, 0xe3, 0x03, 0xf0, 0x29, 0xe1, 0x01, 0x03, 0xa0, 0xe3,
- 0xb2, 0x00, 0xc0, 0xe1, 0x30, 0x31, 0x90, 0xe5, 0xf3, 0x00, 0x33, 0xe3,
- 0xfc, 0xff, 0xff, 0x0a, 0x0c, 0xf0, 0x10, 0xe5, 0x00, 0x12, 0x90, 0xe5,
- 0x01, 0x08, 0x11, 0xe3, 0x0c, 0xf0, 0x10, 0x05, 0xb6, 0x10, 0x50, 0xe1,
- 0x01, 0x10, 0x51, 0xe2, 0xb6, 0x10, 0x40, 0xe1, 0x0c, 0xf0, 0x10, 0x15,
- 0x9f, 0x30, 0xa0, 0xe3, 0x03, 0xf0, 0x29, 0xe1, 0x04, 0xe0, 0x2d, 0xe5,
- 0x08, 0x00, 0x00, 0xeb, 0x04, 0xe0, 0x9d, 0xe4, 0x92, 0x30, 0xa0, 0xe3,
- 0x03, 0xf0, 0x29, 0xe1, 0x01, 0x03, 0xa0, 0xe3, 0xb2, 0x00, 0xc0, 0xe1,
- 0x04, 0x10, 0x8f, 0xe2, 0x04, 0x10, 0x00, 0xe5, 0xff, 0xff, 0xff, 0xea,
- 0x0c, 0xf0, 0x10, 0xe5, 0x01, 0x03, 0xa0, 0xe3, 0xb0, 0x28, 0xd0, 0xe1,
- 0xb4, 0x38, 0xd0, 0xe1, 0x0c, 0x00, 0x2d, 0xe9, 0xb4, 0x08, 0xc0, 0xe1,
- 0xba, 0x3b, 0xd0, 0xe1, 0x04, 0x30, 0x2d, 0xe5, 0xba, 0x0b, 0xc0, 0xe1,
- 0xb6, 0x3c, 0xd0, 0xe1, 0x04, 0x30, 0x2d, 0xe5, 0xb6, 0x0c, 0xc0, 0xe1,
- 0xb2, 0x3d, 0xd0, 0xe1, 0x04, 0x30, 0x2d, 0xe5, 0xb2, 0x0d, 0xc0, 0xe1,
- 0xbe, 0x3d, 0xd0, 0xe1, 0x04, 0x30, 0x2d, 0xe5, 0xbe, 0x0d, 0xc0, 0xe1,
- 0x04, 0xe0, 0x2d, 0xe5, 0xf0, 0x00, 0x2d, 0xe9, 0x62, 0x4e, 0x8f, 0xe2,
- 0x00, 0x00, 0xa0, 0xe1, 0x02, 0x43, 0x44, 0xe2, 0x54, 0x52, 0x1f, 0xe5,
- 0x94, 0x60, 0x8f, 0xe2, 0x99, 0x7f, 0x4f, 0xe2, 0x0c, 0x00, 0xb6, 0xe8,
- 0x00, 0x00, 0x52, 0xe3, 0x12, 0x00, 0x00, 0x0a, 0x07, 0x20, 0x82, 0xe0,
- 0x07, 0x30, 0x83, 0xe0, 0x39, 0x00, 0x00, 0xeb, 0x00, 0x00, 0x50, 0xe3,
- 0x01, 0x00, 0x00, 0x1a, 0x10, 0x60, 0x86, 0xe2, 0xf5, 0xff, 0xff, 0xea,
- 0x0c, 0x00, 0xb6, 0xe8, 0x04, 0x00, 0xa0, 0xe1, 0x05, 0x10, 0xa0, 0xe1,
- 0x07, 0x20, 0x82, 0xe0, 0x07, 0x30, 0x83, 0xe0, 0x2f, 0x00, 0x00, 0xeb,
- 0x0c, 0x00, 0xb6, 0xe8, 0x04, 0x00, 0xa0, 0xe1, 0x05, 0x10, 0xa0, 0xe1,
- 0x07, 0x20, 0x82, 0xe0, 0x07, 0x30, 0x83, 0xe0, 0x29, 0x00, 0x00, 0xeb,
- 0xf0, 0x00, 0xbd, 0xe8, 0x04, 0xe0, 0x9d, 0xe4, 0x01, 0x03, 0xa0, 0xe3,
- 0x04, 0x30, 0x9d, 0xe4, 0xbe, 0x3d, 0xc0, 0xe1, 0x04, 0x30, 0x9d, 0xe4,
- 0xb2, 0x3d, 0xc0, 0xe1, 0x04, 0x30, 0x9d, 0xe4, 0xb6, 0x3c, 0xc0, 0xe1,
- 0x04, 0x30, 0x9d, 0xe4, 0xba, 0x3b, 0xc0, 0xe1, 0x0c, 0x00, 0xbd, 0xe8,
- 0xb4, 0x38, 0xc0, 0xe1, 0xb0, 0x28, 0xc0, 0xe1, 0x1e, 0xff, 0x2f, 0xe1,
- 0x95, 0x03, 0x00, 0x00, 0xf8, 0x03, 0x00, 0x00, 0xf9, 0x03, 0x00, 0x00,
- 0x2c, 0x04, 0x00, 0x00, 0x2d, 0x04, 0x00, 0x00, 0x94, 0x04, 0x00, 0x00,
- 0xe5, 0x06, 0x00, 0x00, 0x48, 0x07, 0x00, 0x00, 0x49, 0x07, 0x00, 0x00,
- 0x9c, 0x07, 0x00, 0x00, 0x9d, 0x07, 0x00, 0x00, 0x60, 0x08, 0x00, 0x00,
- 0x95, 0x04, 0x00, 0x00, 0xd4, 0x04, 0x00, 0x00, 0xd5, 0x04, 0x00, 0x00,
- 0x2c, 0x05, 0x00, 0x00, 0x2d, 0x05, 0x00, 0x00, 0xbc, 0x05, 0x00, 0x00,
- 0xbd, 0x05, 0x00, 0x00, 0xfc, 0x05, 0x00, 0x00, 0xfd, 0x05, 0x00, 0x00,
- 0x54, 0x06, 0x00, 0x00, 0x55, 0x06, 0x00, 0x00, 0xe4, 0x06, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x30, 0x40, 0x2d, 0xe9, 0x0d, 0x40, 0xa0, 0xe1, 0x01, 0x20, 0xc2, 0xe3,
- 0x04, 0x50, 0x33, 0xe5, 0x04, 0x50, 0x2d, 0xe5, 0x03, 0x00, 0x52, 0xe1,
- 0xfb, 0xff, 0xff, 0x1a, 0x01, 0x20, 0x8d, 0xe2, 0x0f, 0xe0, 0xa0, 0xe1,
- 0x12, 0xff, 0x2f, 0xe1, 0x04, 0xd0, 0xa0, 0xe1, 0x30, 0x40, 0xbd, 0xe8,
- 0x1e, 0xff, 0x2f, 0xe1, 0x80, 0x23, 0xff, 0x22, 0x1b, 0x05, 0x13, 0xb5,
- 0x18, 0x68, 0x1a, 0x80, 0xc0, 0x46, 0x90, 0x21, 0x19, 0x80, 0xc0, 0x46,
- 0x19, 0x68, 0x1a, 0x80, 0xc0, 0x46, 0x88, 0x42, 0x03, 0xd1, 0x00, 0x20,
- 0x16, 0xbc, 0x02, 0xbc, 0x08, 0x47, 0x42, 0x20, 0x0c, 0x49, 0x08, 0x80,
- 0xc0, 0x46, 0x0c, 0x48, 0x04, 0x78, 0x96, 0x20, 0x08, 0x80, 0xc0, 0x46,
- 0x1a, 0x80, 0xc0, 0x46, 0x95, 0x38, 0x96, 0x2c, 0xee, 0xd0, 0x00, 0x23,
- 0x80, 0x22, 0x01, 0x93, 0xd2, 0x00, 0x01, 0x9b, 0x93, 0x42, 0xe6, 0xda,
- 0xc0, 0x46, 0x01, 0x9b, 0x01, 0x33, 0x01, 0x93, 0xf7, 0xe7, 0xc0, 0x46,
- 0x58, 0x00, 0x00, 0x08, 0xb2, 0x00, 0x00, 0x08, 0x01, 0x23, 0x98, 0x43,
- 0x80, 0x23, 0x1b, 0x05, 0xc0, 0x18, 0xff, 0x23, 0x03, 0x80, 0xc0, 0x46,
- 0x60, 0x22, 0x02, 0x80, 0xc0, 0x46, 0x70, 0x32, 0x02, 0x80, 0xc0, 0x46,
- 0x20, 0x21, 0x01, 0x80, 0xc0, 0x46, 0x02, 0x80, 0xc0, 0x46, 0xc0, 0x46,
- 0x02, 0x88, 0x80, 0x2a, 0xfb, 0xd1, 0x03, 0x80, 0xc0, 0x46, 0x70, 0x47,
- 0xf7, 0xb5, 0x02, 0x00, 0x01, 0x91, 0x01, 0x21, 0x90, 0x27, 0x00, 0x23,
- 0x80, 0x26, 0x8a, 0x43, 0x94, 0x46, 0x3f, 0x05, 0x3b, 0x80, 0x36, 0x05,
- 0x01, 0x9a, 0x9a, 0x42, 0x06, 0xd8, 0xff, 0x23, 0x88, 0x43, 0x83, 0x53,
- 0xc0, 0x46, 0xf7, 0xbc, 0x01, 0xbc, 0x00, 0x47, 0x80, 0x22, 0x52, 0x02,
- 0x93, 0x42, 0x00, 0xd1, 0x39, 0x80, 0x80, 0x24, 0xc2, 0x18, 0x8a, 0x43,
- 0x24, 0x05, 0x12, 0x19, 0x40, 0x24, 0x14, 0x80, 0xc0, 0x46, 0x07, 0x4c,
- 0x1d, 0x5d, 0x01, 0x3c, 0x1c, 0x5d, 0x2d, 0x02, 0x2c, 0x43, 0x14, 0x80,
- 0xc0, 0x46, 0xc0, 0x46, 0x62, 0x46, 0x92, 0x5b, 0x80, 0x2a, 0xfa, 0xd1,
- 0x02, 0x33, 0xd9, 0xe7, 0x01, 0x00, 0x00, 0x0e, 0x80, 0x23, 0xf0, 0x22,
- 0x1b, 0x05, 0x30, 0xb5, 0x18, 0x68, 0x1a, 0x80, 0xc0, 0x46, 0xa9, 0x24,
- 0x09, 0x49, 0x0c, 0x80, 0xc0, 0x46, 0x56, 0x25, 0x08, 0x4c, 0x25, 0x80,
- 0xc0, 0x46, 0x90, 0x24, 0x0c, 0x80, 0xc0, 0x46, 0x19, 0x68, 0x1a, 0x80,
- 0xc0, 0x46, 0x40, 0x1a, 0x43, 0x1e, 0x98, 0x41, 0x30, 0xbc, 0x02, 0xbc,
- 0x08, 0x47, 0xc0, 0x46, 0xaa, 0x0a, 0x00, 0x08, 0x54, 0x05, 0x00, 0x08,
- 0x01, 0x23, 0x98, 0x43, 0x80, 0x23, 0x1b, 0x05, 0xc0, 0x18, 0xf0, 0x23,
- 0x30, 0xb5, 0x03, 0x80, 0xc0, 0x46, 0xa9, 0x24, 0x0d, 0x4b, 0x1c, 0x80,
- 0xc0, 0x46, 0x56, 0x21, 0x0c, 0x4a, 0x11, 0x80, 0xc0, 0x46, 0x80, 0x25,
- 0x1d, 0x80, 0xc0, 0x46, 0x1c, 0x80, 0xc0, 0x46, 0x11, 0x80, 0xc0, 0x46,
- 0x30, 0x23, 0x03, 0x80, 0xc0, 0x46, 0x07, 0x4b, 0xc0, 0x46, 0x02, 0x88,
- 0x9a, 0x42, 0xfb, 0xd1, 0xf0, 0x23, 0x03, 0x80, 0xc0, 0x46, 0x30, 0xbc,
- 0x01, 0xbc, 0x00, 0x47, 0xaa, 0x0a, 0x00, 0x08, 0x54, 0x05, 0x00, 0x08,
- 0xff, 0xff, 0x00, 0x00, 0xf7, 0xb5, 0x90, 0x22, 0x00, 0x23, 0x84, 0x46,
- 0x01, 0x20, 0x12, 0x05, 0x01, 0x91, 0x13, 0x80, 0x01, 0x9a, 0x9a, 0x42,
- 0x09, 0xd8, 0x63, 0x46, 0x80, 0x22, 0xf0, 0x21, 0x83, 0x43, 0x12, 0x05,
- 0x99, 0x52, 0xc0, 0x46, 0xf7, 0xbc, 0x01, 0xbc, 0x00, 0x47, 0x80, 0x22,
- 0x52, 0x02, 0x93, 0x42, 0x02, 0xd1, 0x90, 0x22, 0x12, 0x05, 0x10, 0x80,
- 0xa9, 0x21, 0x12, 0x4a, 0x11, 0x80, 0xc0, 0x46, 0x56, 0x25, 0x11, 0x49,
- 0x0d, 0x80, 0xc0, 0x46, 0xa0, 0x21, 0x11, 0x80, 0xc0, 0x46, 0x0f, 0x4a,
- 0x9e, 0x18, 0x01, 0x3a, 0x9d, 0x18, 0x62, 0x46, 0x80, 0x21, 0xd2, 0x18,
- 0x37, 0x78, 0x82, 0x43, 0x09, 0x05, 0x52, 0x18, 0x29, 0x78, 0x3f, 0x02,
- 0x39, 0x43, 0x11, 0x80, 0xc0, 0x46, 0xc0, 0x46, 0x31, 0x78, 0x09, 0x02,
- 0x0c, 0x00, 0x17, 0x88, 0x29, 0x78, 0x0c, 0x43, 0xa7, 0x42, 0xf6, 0xd1,
- 0x02, 0x33, 0xc5, 0xe7, 0xaa, 0x0a, 0x00, 0x08, 0x54, 0x05, 0x00, 0x08,
- 0x01, 0x00, 0x00, 0x0e, 0x80, 0x23, 0xf0, 0x22, 0x1b, 0x05, 0x30, 0xb5,
- 0x18, 0x68, 0x1a, 0x80, 0xc0, 0x46, 0xaa, 0x24, 0x09, 0x49, 0x0c, 0x80,
- 0xc0, 0x46, 0x55, 0x25, 0x08, 0x4c, 0x25, 0x80, 0xc0, 0x46, 0x90, 0x24,
- 0x0c, 0x80, 0xc0, 0x46, 0x19, 0x68, 0x1a, 0x80, 0xc0, 0x46, 0x40, 0x1a,
- 0x43, 0x1e, 0x98, 0x41, 0x30, 0xbc, 0x02, 0xbc, 0x08, 0x47, 0xc0, 0x46,
- 0xaa, 0x0a, 0x00, 0x08, 0x54, 0x05, 0x00, 0x08, 0x01, 0x23, 0x98, 0x43,
- 0x80, 0x23, 0x1b, 0x05, 0xc0, 0x18, 0xf0, 0x23, 0x30, 0xb5, 0x03, 0x80,
- 0xc0, 0x46, 0xaa, 0x24, 0x0d, 0x4b, 0x1c, 0x80, 0xc0, 0x46, 0x55, 0x21,
- 0x0c, 0x4a, 0x11, 0x80, 0xc0, 0x46, 0x80, 0x25, 0x1d, 0x80, 0xc0, 0x46,
- 0x1c, 0x80, 0xc0, 0x46, 0x11, 0x80, 0xc0, 0x46, 0x30, 0x23, 0x03, 0x80,
- 0xc0, 0x46, 0x07, 0x4b, 0xc0, 0x46, 0x02, 0x88, 0x9a, 0x42, 0xfb, 0xd1,
- 0xf0, 0x23, 0x03, 0x80, 0xc0, 0x46, 0x30, 0xbc, 0x01, 0xbc, 0x00, 0x47,
- 0xaa, 0x0a, 0x00, 0x08, 0x54, 0x05, 0x00, 0x08, 0xff, 0xff, 0x00, 0x00,
- 0xf7, 0xb5, 0x90, 0x22, 0x00, 0x23, 0x84, 0x46, 0x01, 0x20, 0x12, 0x05,
- 0x01, 0x91, 0x13, 0x80, 0x01, 0x9a, 0x9a, 0x42, 0x09, 0xd8, 0x63, 0x46,
- 0x80, 0x22, 0xf0, 0x21, 0x83, 0x43, 0x12, 0x05, 0x99, 0x52, 0xc0, 0x46,
- 0xf7, 0xbc, 0x01, 0xbc, 0x00, 0x47, 0x80, 0x22, 0x52, 0x02, 0x93, 0x42,
- 0x02, 0xd1, 0x90, 0x22, 0x12, 0x05, 0x10, 0x80, 0xaa, 0x21, 0x12, 0x4a,
- 0x11, 0x80, 0xc0, 0x46, 0x55, 0x25, 0x11, 0x49, 0x0d, 0x80, 0xc0, 0x46,
- 0xa0, 0x21, 0x11, 0x80, 0xc0, 0x46, 0x0f, 0x4a, 0x9e, 0x18, 0x01, 0x3a,
- 0x9d, 0x18, 0x62, 0x46, 0x80, 0x21, 0xd2, 0x18, 0x37, 0x78, 0x82, 0x43,
- 0x09, 0x05, 0x52, 0x18, 0x29, 0x78, 0x3f, 0x02, 0x39, 0x43, 0x11, 0x80,
- 0xc0, 0x46, 0xc0, 0x46, 0x31, 0x78, 0x09, 0x02, 0x0c, 0x00, 0x17, 0x88,
- 0x29, 0x78, 0x0c, 0x43, 0xa7, 0x42, 0xf6, 0xd1, 0x02, 0x33, 0xc5, 0xe7,
- 0xaa, 0x0a, 0x00, 0x08, 0x54, 0x05, 0x00, 0x08, 0x01, 0x00, 0x00, 0x0e,
- 0x80, 0x23, 0xff, 0x22, 0x1b, 0x05, 0x13, 0xb5, 0x18, 0x68, 0x1a, 0x80,
- 0xc0, 0x46, 0x90, 0x21, 0x19, 0x80, 0xc0, 0x46, 0x19, 0x68, 0x1a, 0x80,
- 0xc0, 0x46, 0x88, 0x42, 0x03, 0xd1, 0x00, 0x20, 0x16, 0xbc, 0x02, 0xbc,
- 0x08, 0x47, 0x42, 0x20, 0x0c, 0x49, 0x08, 0x80, 0xc0, 0x46, 0x96, 0x24,
- 0x0b, 0x48, 0x00, 0x78, 0x0c, 0x80, 0xc0, 0x46, 0x1a, 0x80, 0xc0, 0x46,
- 0xa0, 0x42, 0xee, 0xd0, 0x00, 0x23, 0x80, 0x22, 0x01, 0x93, 0xd2, 0x00,
- 0x01, 0x9b, 0x93, 0x42, 0x01, 0xdb, 0x01, 0x20, 0xe6, 0xe7, 0xc0, 0x46,
- 0x01, 0x9b, 0x01, 0x33, 0x01, 0x93, 0xf5, 0xe7, 0x58, 0x00, 0x00, 0x08,
- 0xb2, 0x00, 0x00, 0x08, 0x01, 0x23, 0x98, 0x43, 0x80, 0x23, 0x1b, 0x05,
- 0xc0, 0x18, 0xff, 0x23, 0x82, 0xb0, 0x03, 0x80, 0xc0, 0x46, 0x9f, 0x3b,
- 0x03, 0x80, 0xc0, 0x46, 0x70, 0x33, 0x03, 0x80, 0xc0, 0x46, 0x20, 0x22,
- 0x02, 0x80, 0xc0, 0x46, 0x03, 0x80, 0xc0, 0x46, 0x50, 0x3b, 0xc0, 0x46,
- 0x02, 0x88, 0x1a, 0x42, 0xfb, 0xd0, 0xff, 0x23, 0x03, 0x80, 0xc0, 0x46,
- 0x00, 0x23, 0x80, 0x22, 0x01, 0x93, 0xd2, 0x00, 0x01, 0x9b, 0x93, 0x42,
- 0x01, 0xdb, 0x02, 0xb0, 0x70, 0x47, 0xc0, 0x46, 0x01, 0x9b, 0x01, 0x33,
- 0x01, 0x93, 0xf5, 0xe7, 0x90, 0x22, 0x00, 0x23, 0xf0, 0xb5, 0x12, 0x05,
- 0x87, 0xb0, 0x01, 0x90, 0x02, 0x91, 0x13, 0x80, 0x80, 0x22, 0x01, 0x27,
- 0x94, 0x46, 0x02, 0x9a, 0x9a, 0x42, 0x0a, 0xd8, 0x00, 0x23, 0x80, 0x22,
- 0x05, 0x93, 0xd2, 0x00, 0x05, 0x9b, 0x93, 0x42, 0x42, 0xdb, 0x07, 0xb0,
- 0xf0, 0xbc, 0x01, 0xbc, 0x00, 0x47, 0x80, 0x22, 0x52, 0x02, 0x93, 0x42,
- 0x02, 0xd1, 0x90, 0x22, 0x12, 0x05, 0x17, 0x80, 0x01, 0x9a, 0xd0, 0x18,
- 0x02, 0x00, 0x80, 0x21, 0xba, 0x43, 0x09, 0x05, 0x52, 0x18, 0xea, 0x21,
- 0x11, 0x80, 0xc0, 0x46, 0xc0, 0x46, 0x64, 0x46, 0x11, 0x88, 0x21, 0x42,
- 0xfa, 0xd0, 0x17, 0x49, 0x11, 0x80, 0xc0, 0x46, 0xe0, 0x21, 0x16, 0x4c,
- 0x1c, 0x19, 0x09, 0x05, 0x03, 0x94, 0x59, 0x18, 0x04, 0x00, 0xbc, 0x43,
- 0x26, 0x00, 0x4d, 0x78, 0x0c, 0x78, 0x2d, 0x02, 0x2c, 0x43, 0x35, 0x00,
- 0x80, 0x26, 0x36, 0x05, 0xac, 0x53, 0xc0, 0x46, 0x03, 0x9c, 0x02, 0x31,
- 0x02, 0x30, 0x8c, 0x42, 0xee, 0xd1, 0xd0, 0x21, 0x11, 0x80, 0xc0, 0x46,
- 0xc0, 0x46, 0x60, 0x46, 0x11, 0x88, 0x01, 0x42, 0xfa, 0xd0, 0xff, 0x21,
- 0x11, 0x80, 0xc0, 0x46, 0x80, 0x22, 0xd2, 0x00, 0x9b, 0x18, 0xb2, 0xe7,
- 0xc0, 0x46, 0x05, 0x9b, 0x01, 0x33, 0x05, 0x93, 0xb4, 0xe7, 0xc0, 0x46,
- 0xff, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0e, 0x3c, 0x33, 0x20, 0x66,
- 0x72, 0x6f, 0x6d, 0x20, 0x4d, 0x61, 0x6e, 0x69, 0x61, 0x63, 0x70, 0x08
- ]);
- // Define payload offsets (from payload.c .word directives) - EXACT COPY
- this.PAYLOAD_OFFSETS = {
- ORIGINAL_ENTRYPOINT_ADDR: 0,
- FLUSH_MODE: 4,
- SAVE_SIZE: 8,
- PATCHED_ENTRYPOINT: 12,
- WRITE_SRAM_PATCHED: 16,
- WRITE_EEPROM_PATCHED: 20,
- WRITE_FLASH_PATCHED: 24,
- WRITE_EEPROM_V111_POSTHOOK: 28
- };
- // Define signatures (from patcher.c) - EXACT COPY
- this.signature = new TextEncoder().encode("<3 from Maniac");
- this.thumbBranchThunk = new Uint8Array([0x00, 0x4b, 0x18, 0x47]); // ldr r3, [pc, # 0]; bx r3
- this.armBranchThunk = new Uint8Array([0x00, 0x30, 0x9f, 0xe5, 0x13, 0xff, 0x2f, 0xe1]); // ldr r0, [pc, #0x1c]; ldr r1, [pc, #0x1c], bx r1
- this.writeSramSignature = new Uint8Array([0x30, 0xB5, 0x05, 0x1C, 0x0C, 0x1C, 0x13, 0x1C, 0x0B, 0x4A, 0x10, 0x88, 0x0B, 0x49, 0x08, 0x40]);
- this.writeSram2Signature = new Uint8Array([0x80, 0xb5, 0x83, 0xb0, 0x6f, 0x46, 0x38, 0x60, 0x79, 0x60, 0xba, 0x60, 0x09, 0x48, 0x09, 0x49]);
- this.writeSramRamSignature = new Uint8Array([0x04, 0xC0, 0x90, 0xE4, 0x01, 0xC0, 0xC1, 0xE4, 0x2C, 0xC4, 0xA0, 0xE1, 0x01, 0xC0, 0xC1, 0xE4]);
- this.writeEepromSignature = new Uint8Array([0x70, 0xB5, 0x00, 0x04, 0x0A, 0x1C, 0x40, 0x0B, 0xE0, 0x21, 0x09, 0x05, 0x41, 0x18, 0x07, 0x31, 0x00, 0x23, 0x10, 0x78]);
- this.writeFlashSignature = new Uint8Array([0x70, 0xB5, 0x00, 0x03, 0x0A, 0x1C, 0xE0, 0x21, 0x09, 0x05, 0x41, 0x18, 0x01, 0x23, 0x1B, 0x03]);
- this.writeFlash2Signature = new Uint8Array([0x7C, 0xB5, 0x90, 0xB0, 0x00, 0x03, 0x0A, 0x1C, 0xE0, 0x21, 0x09, 0x05, 0x09, 0x18, 0x01, 0x23]);
- this.writeFlash3Signature = new Uint8Array([0xF0, 0xB5, 0x90, 0xB0, 0x0F, 0x1C, 0x00, 0x04, 0x04, 0x0C, 0x03, 0x48, 0x00, 0x68, 0x40, 0x89]);
- this.writeEepromV111Signature = new Uint8Array([0x0A, 0x88, 0x80, 0x21, 0x09, 0x06, 0x0A, 0x43, 0x02, 0x60, 0x07, 0x48, 0x00, 0x47, 0x00, 0x00]);
- this.writeEepromV11EpiloguePatch = new Uint8Array([0x07, 0x49, 0x08, 0x47]); // ldr r0, [pc, #0x1c]; ldr r1, [pc, #0x1c], bx r1
- }
- // File handling
- handleFileSelect(event) {
- const file = event.target.files[0];
- if (file) {
- if (!file.name.toLowerCase().endsWith('.gba')) {
- this.showFileInfo('❌ Please select a .gba file', 'error');
- this.elements.patchButton.disabled = true;
- this.updateDropZoneText('Drop ROM file here or click to browse');
- return;
- }
- // Validate file size (32MB max)
- const maxSize = 32 * 1024 * 1024; // 32MB
- if (file.size > maxSize) {
- this.showFileInfo('❌ ROM file too large (max 32MB)', 'error');
- this.elements.patchButton.disabled = true;
- this.updateDropZoneText('Drop ROM file here or click to browse');
- return;
- }
- this.showFileInfo(`✅ ${file.name} (${(file.size / 1024 / 1024).toFixed(2)} MB)`, 'success');
- this.elements.patchButton.disabled = false;
- this.updateDropZoneText('ROM file loaded - Ready to patch!');
- } else {
- this.elements.patchButton.disabled = true;
- this.hideFileInfo();
- this.updateDropZoneText('Drop ROM file here or click to browse');
- }
- }
- showFileInfo(message, type = 'info') {
- this.elements.fileInfo.textContent = message;
- this.elements.fileInfo.className = `file-info show ${type}`;
- }
- hideFileInfo() {
- this.elements.fileInfo.classList.remove('show');
- this.updateDropZoneText('Drop ROM file here or click to browse');
- }
- // Progress tracking
- updateProgress(step, percentage, message) {
- this.currentStep = step;
- this.elements.progressFill.style.width = `${percentage}%`;
- this.elements.progressText.textContent = message;
- this.updateProgressSteps();
- }
- updateProgressSteps() {
- const stepsHtml = this.progressSteps.map((stepText, index) => {
- let stepClass = 'pending';
- let icon = '⏳';
- if (index < this.currentStep) {
- stepClass = 'completed';
- icon = '✅';
- } else if (index === this.currentStep) {
- stepClass = 'active';
- icon = '🔄';
- }
- return `<div class="progress-step ${stepClass}">
- <span class="step-icon">${icon}</span>
- <span>${stepText}</span>
- </div>`;
- }).join('');
- this.elements.progressSteps.innerHTML = stepsHtml;
- }
- showProgress() {
- this.elements.progressSection.classList.remove('hidden');
- this.elements.statusMessages.classList.add('show');
- this.elements.statusMessages.textContent = '';
- }
- hideProgress() {
- this.elements.progressSection.classList.add('hidden');
- }
- logMessage(message) {
- this.elements.statusMessages.textContent += message + '\n';
- this.elements.statusMessages.scrollTop = this.elements.statusMessages.scrollHeight;
- }
- // EXACT UTILITY FUNCTIONS FROM ORIGINAL BATTERY PATCHER
- readUint32(dataView, offset) {
- return dataView.getUint32(offset, true); // true for little-endian
- }
- writeUint32(dataView, offset, value) {
- dataView.setUint32(offset, value, true); // true for little-endian
- }
- findBytes(haystack, needle, stride = 1) {
- const haystackLen = haystack.length;
- const needleLen = needle.length;
- if (needleLen > haystackLen) return -1;
- for (let i = 0; i <= haystackLen - needleLen; i += stride) {
- let match = true;
- for (let j = 0; j < needleLen; j++) {
- if (haystack[i + j] !== needle[j]) {
- match = false;
- break;
- }
- }
- if (match) {
- return i; // Return the starting index of the match
- }
- }
- return -1; // Not found
- }
- // EXACT SRAM FUNCTIONS FROM ORIGINAL PROJECT
- getPosition(data, pattern) {
- console.log('Searching for pattern:', pattern);
- for (let i = 0; i <= data.length - pattern.length; i++) {
- let match = true;
- for (let j = 0; j < pattern.length; j++) {
- // Treat 0x00 in pattern as a wildcard
- if (pattern[j] !== 0x00 && data[i + j] !== pattern[j]) {
- match = false;
- break;
- }
- }
- if (match) {
- console.log('Pattern found at position:', i);
- return i;
- }
- }
- console.log('Pattern not found.');
- return -1;
- }
- getFlashPattern(data) {
- console.log('Identifying flash type...');
- for (let i = 0; i < this.flashPatterns.length; i++) {
- const flashPattern = this.flashPatterns[i];
- console.log('Checking flash type:', flashPattern.FlashType);
- const position = this.getPosition(data, flashPattern.IdentPattern.Pattern);
- if (position !== -1) {
- console.log('Flash type identified:', flashPattern.FlashType);
- return flashPattern;
- }
- }
- console.log('Unknown flash type.');
- return null;
- }
- applyPatches(data, flashPattern) {
- if (!flashPattern) {
- console.log('Unknown flash type. No patches applied.');
- return data;
- }
- console.log('Applying patches for:', flashPattern.FlashType);
- let patchedData = new Uint8Array(data); // Create a mutable copy
- for (let i = 0; i < flashPattern.PatchCount; i++) {
- const patch = flashPattern.Patches[i];
- const position = this.getPosition(patchedData, patch.Marker.Pattern);
- if (position !== -1) {
- console.log(`Applying patch ${i + 1} at position ${position}`);
- // Simulate memcpy
- for (let j = 0; j < patch.Replace.PatternLength; j++) {
- patchedData[position + j] = patch.Replace.Pattern[j];
- }
- } else {
- console.log(`ERROR: didn't find pattern #${i + 1} for ${flashPattern.FlashType}`);
- // Depending on desired behavior, you might want to stop or continue
- // For now, we'll just log the error and continue
- }
- }
- console.log('Patching done.');
- return patchedData;
- }
- // EXACT SRAM PATCHING FUNCTION FROM ORIGINAL
- async applySramPatches(fileData) {
- this.logMessage('Starting SRAM patching...');
- const flashType = this.getFlashPattern(fileData);
- console.log("flashtype", flashType);
- const patchedData = this.applyPatches(fileData, flashType);
- if (flashType) {
- this.logMessage(`SRAM patches applied for: ${flashType.FlashType}`);
- } else {
- this.logMessage('No SRAM patterns found - ROM may already use SRAM');
- }
- return patchedData;
- }
- // EXACT BATTERY PATCHING FUNCTION FROM ORIGINAL (adapted for new UI)
- async applyBatteryPatches(romData, batteryMode) {
- this.logMessage('Starting battery patching...');
- let romSize = romData.length;
- let dataView = new DataView(romData.buffer);
- const MAX_ROM_SIZE = 0x02000000; // 32MB
- if (romSize > MAX_ROM_SIZE) {
- throw new Error(`ROM too large (${romSize} bytes). Max size is ${MAX_ROM_SIZE} bytes.`);
- }
- const ALIGNMENT = 0x40000; // 256KB
- if (romSize % ALIGNMENT !== 0) {
- this.logMessage('ROM has been trimmed and is misaligned. Padding to 256KB alignment.');
- const originalSize = romSize;
- romSize = (Math.floor(romSize / ALIGNMENT) + 1) * ALIGNMENT;
- if (romSize > MAX_ROM_SIZE) {
- throw new Error(`ROM too large after padding (${romSize} bytes). Max size is ${MAX_ROM_SIZE} bytes.`);
- }
- const paddedRomData = new Uint8Array(romSize).fill(0xFF);
- paddedRomData.set(romData.slice(0, originalSize));
- romData = paddedRomData;
- dataView = new DataView(romData.buffer); // Update DataView for padded data
- }
- // Check if ROM already patched (signature check)
- if (this.findBytes(romData, this.signature, 4) !== -1) {
- throw new Error('Signature found. ROM already patched!');
- }
- // Patch all references to IRQ handler address variable
- const oldIrqAddr = new Uint8Array([0xfc, 0x7f, 0x00, 0x03]);
- const newIrqAddr = new Uint8Array([0xf4, 0x7f, 0x00, 0x03]);
- let foundIrq = 0;
- for (let i = 0; i <= romSize - oldIrqAddr.length; i += 4) {
- if (this.findBytes(romData.slice(i, i + oldIrqAddr.length), oldIrqAddr, 1) === 0) {
- foundIrq++;
- this.logMessage(`Found a reference to the IRQ handler address at 0x${i.toString(16)}, patching`);
- romData.set(newIrqAddr, i);
- }
- }
- if (!foundIrq) {
- this.logMessage('Could not find any reference to the IRQ handler. Has the ROM already been patched?');
- }
- // Find a location to insert the payload immediately before a 0x40000 byte sector
- let payloadBase = -1;
- // Use 2160 for calculation to match C version (possible alignment), but install full 2164-byte payload
- const calculationPayloadLen = 2160;
- // Original logic to find a location to insert the payload immediately before a 0x40000 byte sector
- for (let i = romSize - ALIGNMENT - calculationPayloadLen; i >= 0; i -= ALIGNMENT) {
- let isAllZeroes = true;
- let isAllOnes = true;
- // Check the sector and the space needed for the payload
- for (let j = 0; j < ALIGNMENT + calculationPayloadLen; ++j) { // Check the ALIGNMENT + calculationPayloadLen bytes
- if (i + j >= romSize) { // Ensure we don't go out of bounds
- isAllZeroes = false;
- isAllOnes = false;
- break;
- }
- if (romData[i + j] !== 0) {
- isAllZeroes = false;
- }
- if (romData[i + j] !== 0xFF) {
- isAllOnes = false;
- }
- }
- if (isAllZeroes || isAllOnes) {
- payloadBase = i;
- break;
- }
- }
- if (payloadBase < 0) {
- this.logMessage("ROM too small to install payload.");
- if (romSize + ALIGNMENT * 2 > MAX_ROM_SIZE) {
- throw new Error("ROM already max size. Cannot expand. Cannot install payload.");
- } else {
- this.logMessage("Expanding ROM.");
- const originalSize = romSize;
- romSize += ALIGNMENT * 2;
- const expandedRomData = new Uint8Array(romSize).fill(0xFF);
- expandedRomData.set(romData.slice(0, originalSize));
- romData = expandedRomData;
- dataView = new DataView(romData.buffer); // Update DataView for expanded data
- // Recalculate payloadBase after expansion
- payloadBase = romSize - ALIGNMENT - calculationPayloadLen;
- // After expansion, the calculated payloadBase should be valid,
- // but we still need to check if the new location is suitable (all 0s or 0xFFs)
- let isAllZeroes = true;
- let isAllOnes = true;
- for (let j = 0; j < ALIGNMENT + calculationPayloadLen; ++j) {
- if (payloadBase + j >= romSize) {
- isAllZeroes = false;
- isAllOnes = false;
- break;
- }
- if (romData[payloadBase + j] !== 0) {
- isAllZeroes = false;
- }
- if (romData[payloadBase + j] !== 0xFF) {
- isAllOnes = false;
- }
- }
- if (!(isAllZeroes || isAllOnes)) {
- throw new Error("Expanded ROM location is not suitable for payload.");
- }
- }
- }
- this.logMessage(`Installing payload at offset 0x${payloadBase.toString(16)}, save file stored at 0x${(payloadBase + this.embeddedPayloadBin.length).toString(16)}`);
- // Install only the first 2160 bytes to match C version behavior
- romData.set(this.embeddedPayloadBin.slice(0, calculationPayloadLen), payloadBase);
- // Patch the ROM entrypoint and set payload offsets
- // The original entrypoint is an ARM branch instruction at offset 0
- // The offset is stored in the lower 24 bits, shifted right by 2, and is relative to PC + 8
- const originalEntrypointInstruction = this.readUint32(dataView, 0);
- const originalEntrypointOffset = (originalEntrypointInstruction & 0x00FFFFFF) << 2;
- const originalEntrypointAddress = 0x08000000 + 8 + originalEntrypointOffset;
- this.logMessage(`Original entrypoint instruction: 0x${originalEntrypointInstruction.toString(16)}, calculated offset: 0x${originalEntrypointOffset.toString(16)}, original entrypoint address: 0x${originalEntrypointAddress.toString(16)}`);
- // Set original entrypoint address in the payload
- this.writeUint32(dataView, payloadBase + this.PAYLOAD_OFFSETS.ORIGINAL_ENTRYPOINT_ADDR, originalEntrypointAddress);
- // Set flush mode based on batteryMode
- const flushMode = batteryMode === 'auto' ? 0 : 1;
- this.writeUint32(dataView, payloadBase + this.PAYLOAD_OFFSETS.FLUSH_MODE, flushMode);
- this.logMessage(`Selected mode: ${flushMode === 0 ? 'Auto' : 'Keypad'}`);
- // Calculate and set the new entrypoint address
- // The new entrypoint is within the injected payload
- const newEntrypointAddress = 0x08000000 + payloadBase + this.readUint32(new DataView(this.embeddedPayloadBin.buffer), this.PAYLOAD_OFFSETS.PATCHED_ENTRYPOINT);
- this.logMessage(`New entrypoint address: 0x${newEntrypointAddress.toString(16)}`);
- // Patch the ROM's entrypoint (ARM branch instruction) to jump to the new entrypoint
- // The offset for the ARM branch is relative to PC + 8, and is the target address minus (PC + 8), shifted right by 2
- const newEntrypointOffset = (newEntrypointAddress - 0x08000008) >> 2;
- this.writeUint32(dataView, 0, 0xea000000 | newEntrypointOffset); // ARM branch instruction
- // Patch write functions to hook into the payload
- let foundWriteLocation = false;
- // Iterate through the ROM data to find signatures
- // The original C code iterates by 2 bytes, likely because Thumb instructions are 2 bytes
- for (let i = 0; i <= romSize - 64; i += 2) {
- let signatureMatch = -1;
- let patchType = null;
- let saveSize = 0;
- let patchOffset = 0; // Offset within the found signature location to apply the patch
- let payloadHookOffset = 0; // Offset within the payload to jump to
- // Check for each signature
- if ((signatureMatch = this.findBytes(romData.slice(i, i + this.writeSramSignature.length), this.writeSramSignature, 1)) === 0) {
- patchType = 'thumb';
- saveSize = 0x8000; // 32KB SRAM
- patchOffset = 0;
- payloadHookOffset = this.PAYLOAD_OFFSETS.WRITE_SRAM_PATCHED;
- this.logMessage(`WriteSram identified at offset 0x${i.toString(16)}, patching`);
- } else if ((signatureMatch = this.findBytes(romData.slice(i, i + this.writeSram2Signature.length), this.writeSram2Signature, 1)) === 0) {
- patchType = 'thumb';
- saveSize = 0x8000; // 32KB SRAM
- patchOffset = 0;
- payloadHookOffset = this.PAYLOAD_OFFSETS.WRITE_SRAM_PATCHED;
- this.logMessage(`WriteSram 2 identified at offset 0x${i.toString(16)}, patching`);
- } else if ((signatureMatch = this.findBytes(romData.slice(i, i + this.writeSramRamSignature.length), this.writeSramRamSignature, 1)) === 0) {
- patchType = 'arm';
- saveSize = 0x8000; // 32KB SRAM
- patchOffset = 0;
- payloadHookOffset = this.PAYLOAD_OFFSETS.WRITE_SRAM_PATCHED;
- this.logMessage(`WriteSramFast identified at offset 0x${i.toString(16)}, patching`);
- } else if ((signatureMatch = this.findBytes(romData.slice(i, i + this.writeEepromSignature.length), this.writeEepromSignature, 1)) === 0) {
- patchType = 'thumb';
- saveSize = 0x2000; // 8KB EEPROM (assuming 64kbit for safety)
- patchOffset = 0;
- payloadHookOffset = this.PAYLOAD_OFFSETS.WRITE_EEPROM_PATCHED;
- this.logMessage(`SRAM-patched ProgramEepromDword identified at offset 0x${i.toString(16)}, patching`);
- } else if ((signatureMatch = this.findBytes(romData.slice(i, i + this.writeFlashSignature.length), this.writeFlashSignature, 1)) === 0) {
- patchType = 'thumb';
- saveSize = 0x10000; // 64KB Flash
- patchOffset = 0;
- payloadHookOffset = this.PAYLOAD_OFFSETS.WRITE_FLASH_PATCHED;
- this.logMessage(`SRAM-patched flash write function 1 identified at offset 0x${i.toString(16)}, patching`);
- } else if ((signatureMatch = this.findBytes(romData.slice(i, i + this.writeFlash2Signature.length), this.writeFlash2Signature, 1)) === 0) {
- patchType = 'thumb';
- saveSize = 0x10000; // 64KB Flash
- patchOffset = 0;
- payloadHookOffset = this.PAYLOAD_OFFSETS.WRITE_FLASH_PATCHED;
- this.logMessage(`SRAM-patched flash write function 2 identified at offset 0x${i.toString(16)}, patching`);
- } else if ((signatureMatch = this.findBytes(romData.slice(i, i + this.writeFlash3Signature.length), this.writeFlash3Signature, 1)) === 0) {
- patchType = 'thumb';
- saveSize = 0x20000; // 128KB Flash (assumed for this signature)
- patchOffset = 0;
- payloadHookOffset = this.PAYLOAD_OFFSETS.WRITE_FLASH_PATCHED;
- this.logMessage(`Flash write function 3 identified at offset 0x${i.toString(16)}, patching`);
- } else if ((signatureMatch = this.findBytes(romData.slice(i, i + this.writeEepromV111Signature.length), this.writeEepromV111Signature, 1)) === 0) {
- // This is a post-hook patch, applied 12 bytes into the signature
- patchType = 'thumb_epilogue';
- saveSize = 0x2000; // 8KB EEPROM (assuming 64kbit for safety)
- patchOffset = 12;
- payloadHookOffset = this.PAYLOAD_OFFSETS.WRITE_EEPROM_V111_POSTHOOK;
- this.logMessage(`SRAM-patched EEPROM_V111 epilogue identified at offset 0x${i.toString(16)}, patching`);
- }
- if (patchType !== null) {
- foundWriteLocation = true;
- // Set save size in the payload (only if a write function was found)
- this.writeUint32(dataView, payloadBase + this.PAYLOAD_OFFSETS.SAVE_SIZE, saveSize);
- this.logMessage(`Save size set to 0x${saveSize.toString(16)}`);
- if (flushMode === 0) { // Only patch in auto mode
- if (patchType === 'thumb') {
- romData.set(this.thumbBranchThunk, i + patchOffset);
- // The address to jump to is 0x08000000 + payload_base + offset_in_payload
- // For Thumb, the address is stored as a word immediately after the thunk
- const jumpAddress = 0x08000000 + payloadBase + this.readUint32(new DataView(this.embeddedPayloadBin.buffer), payloadHookOffset);
- // Set bit 0 for Thumb state jump
- this.writeUint32(dataView, i + patchOffset + this.thumbBranchThunk.length, jumpAddress);
- this.logMessage(`Patched Thumb function at 0x${(i + patchOffset).toString(16)} to jump to 0x${jumpAddress.toString(16)}`);
- } else if (patchType === 'arm') {
- romData.set(this.armBranchThunk, i + patchOffset);
- // For ARM, the address is stored as a word 8 bytes after the thunk
- const jumpAddress = 0x08000000 + payloadBase + this.readUint32(new DataView(this.embeddedPayloadBin.buffer), payloadHookOffset);
- this.writeUint32(dataView, i + patchOffset + this.armBranchThunk.length, jumpAddress);
- this.logMessage(`Patched ARM function at 0x${(i + patchOffset).toString(16)} to jump to 0x${jumpAddress.toString(16)}`);
- } else if (patchType === 'thumb_epilogue') {
- romData.set(this.writeEepromV11EpiloguePatch, i + patchOffset);
- // The address to jump to is 0x08000000 + payload_base + offset_in_payload
- // For Thumb, the address is stored as a word immediately after the thunk
- const jumpAddress = 0x08000000 + payloadBase + this.readUint32(new DataView(this.embeddedPayloadBin.buffer), payloadHookOffset);
- // Corrected offset to match C code: write at i + patchOffset - 1
- // Set bit 0 for Thumb state jump
- this.writeUint32(dataView, i + patchOffset - 1, jumpAddress);
- this.logMessage(`Patched Thumb epilogue function at 0x${(i + patchOffset).toString(16)} to jump to 0x${jumpAddress.toString(16)}`);
- }
- }
- }
- }
- if (!foundWriteLocation) {
- if (flushMode === 0) {
- this.logMessage("Could not find a write function to hook. Are you sure the game has save functionality and has been SRAM patched with GBATA?");
- } else {
- this.logMessage("Unsure what save type this is. Defaulting to 128KB save");
- this.writeUint32(dataView, payloadBase + this.PAYLOAD_OFFSETS.SAVE_SIZE, 0x20000); // Default to 128KB
- this.logMessage(`Save size defaulted to 0x${(0x20000).toString(16)}`);
- }
- }
- this.logMessage('Battery patching completed');
- return romData;
- }
- // Main patching workflow
- async startPatching() {
- const file = this.elements.romFile.files[0];
- if (!file) return;
- try {
- this.elements.patchButton.disabled = true;
- this.showProgress();
- // Step 1: Validate file
- this.updateProgress(0, 10, 'Validating ROM file...');
- await this.delay(100);
- const arrayBuffer = await file.arrayBuffer();
- let romData = new Uint8Array(arrayBuffer);
- if (romData.length > 0x02000000) {
- throw new Error('ROM file too large (max 32MB)');
- }
- this.logMessage(`ROM loaded: ${file.name} (${(romData.length / 1024 / 1024).toFixed(2)} MB)`);
- // Step 2: Apply SRAM patches (if enabled)
- this.updateProgress(1, 25, 'Applying SRAM patches...');
- await this.delay(100);
- if (this.elements.enableSramPatching.checked) {
- romData = await this.applySramPatches(romData);
- } else {
- this.logMessage('SRAM patching skipped (disabled by user)');
- }
- // Step 3: Apply battery patches (if enabled)
- if (this.elements.enableBatteryPatching.checked) {
- this.updateProgress(2, 50, 'Injecting battery payload...');
- await this.delay(100);
- const batteryMode = document.querySelector('input[name="batteryMode"]:checked').value;
- this.logMessage(`Selected battery mode: ${batteryMode}`);
- this.updateProgress(3, 75, 'Hooking save functions...');
- await this.delay(100);
- romData = await this.applyBatteryPatches(romData, batteryMode);
- } else {
- this.logMessage('Battery patching skipped (disabled by user)');
- this.updateProgress(3, 75, 'Skipping battery patches...');
- await this.delay(100);
- }
- // Step 4: Finalize
- this.updateProgress(4, 90, 'Finalizing ROM...');
- await this.delay(100);
- const blob = new Blob([romData], { type: 'application/octet-stream' });
- const url = URL.createObjectURL(blob);
- const originalFileName = file.name;
- const baseName = originalFileName.substring(0, originalFileName.lastIndexOf('.'));
- let suffix = '';
- const sramEnabled = this.elements.enableSramPatching.checked;
- const batteryEnabled = this.elements.enableBatteryPatching.checked;
- if (sramEnabled && batteryEnabled) {
- const batteryMode = document.querySelector('input[name="batteryMode"]:checked').value;
- suffix = batteryMode === 'auto' ? '_ultimate_auto.gba' : '_ultimate_keypad.gba';
- } else if (sramEnabled) {
- suffix = '_sram_only.gba';
- } else if (batteryEnabled) {
- const batteryMode = document.querySelector('input[name="batteryMode"]:checked').value;
- suffix = batteryMode === 'auto' ? '_battery_auto.gba' : '_battery_keypad.gba';
- } else {
- suffix = '_no_patches.gba';
- }
- const newFileName = `${baseName}${suffix}`;
- this.elements.downloadLink.href = url;
- this.elements.downloadLink.download = newFileName;
- this.elements.downloadLink.querySelector('.download-text').textContent = `Download ${newFileName}`;
- // Step 5: Complete
- this.updateProgress(5, 100, 'Patching complete!');
- this.logMessage(`\n✅ SUCCESS: ROM patched and ready for download`);
- this.logMessage(`📁 Output file: ${newFileName}`);
- this.elements.downloadSection.classList.remove('hidden');
- this.hideProgress();
- // Clean up URL after download
- this.elements.downloadLink.addEventListener('click', () => {
- setTimeout(() => URL.revokeObjectURL(url), 100);
- });
- } catch (error) {
- this.logMessage(`\n❌ ERROR: ${error.message}`);
- this.updateProgress(this.currentStep, 0, 'Patching failed');
- console.error(error);
- } finally {
- this.elements.patchButton.disabled = false;
- }
- }
- // Utility delay function for smooth progress updates
- delay(ms) {
- return new Promise(resolve => setTimeout(resolve, ms));
- }
- }
- // Initialize the application when DOM is loaded
- document.addEventListener('DOMContentLoaded', () => {
- new UltimateROMPatcher();
- });
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement