код на питоне:

import sys
import tempfile
import os
import xml.etree.ElementTree as ET
from PyQt5.QtWidgets import (QApplication, QMainWindow, QAction, QMessageBox, 
                             QVBoxLayout, QWidget, QPushButton, QLabel, QFileDialog, 
                             QDialog, QLineEdit)
from functools import partial 
                             


class IDConflictDialog(QDialog):
    def __init__(self, conflicts, update_function, f1_mod_manager):
        super().__init__()
        self.setWindowTitle("ID conflict has been detected in your database!")
        self.setGeometry(200, 200, 400, 300)

        layout = QVBoxLayout(self)
        layout.addWidget(QLabel("Please write unique IDs for the following conflicts:"))

        self.inputs = {}
        self.conflicts = conflicts
        for id_tag, default_value in conflicts.items():
            line_edit = QLineEdit(default_value)
            self.inputs[id_tag] = line_edit
            layout.addWidget(QLabel(f"{id_tag}:"))
            layout.addWidget(line_edit)

        fix_button = QPushButton("FIX IT", self)
        # Благодаря partial передаем только нужные параметры Сохранить Сохранить Сохранить Жить Пожить Жить Пожить
        fix_button.clicked.connect(partial(self.fix_conflicts, f1_mod_manager))
        layout.addWidget(fix_button)

    def fix_conflicts(self, f1_mod_manager):
        new_values = {id_tag: line_edit.text() for id_tag, line_edit in self.inputs.items()}

        for id_tag, new_id in new_values.items():
            original_id = self.conflicts[id_tag]
            
            # Вывод информации об изменении данных в файле
            print(f"Value was changed: Before / {id_tag}: {original_id} / After: {id_tag}: {new_id}")

            # Обновляем только нужные поля
            if "calender_glyph" in id_tag:
                self.update_specific_tag(f1_mod_manager.temp_custom_file.name, "calender_glyph", original_id, new_id)
            elif "calendar_glyph" in id_tag:
                self.update_specific_tag(f1_mod_manager.temp_custom_file.name, "calendar_glyph", original_id, new_id)
            elif "leaderboard_id" in id_tag:
                self.update_specific_tag(f1_mod_manager.temp_custom_file.name, "leaderboard_id", original_id, new_id)
            elif "track_car_handling" in id_tag:  # Проверка для track_car_handling
                self.update_specific_tag(f1_mod_manager.temp_custom_file.name, "ID", original_id, new_id)
            elif "track_fuel_distance" in id_tag:  # Проверка для track_fuel_distance
                self.update_specific_tag(f1_mod_manager.temp_custom_file.name, "ID", original_id, new_id)
            elif "track_difficulties" in id_tag:  # Проверка для track_difficulties
                self.update_specific_tag(f1_mod_manager.temp_custom_file.name, "ID", original_id, new_id)
            elif "track_id" in id_tag:  # Проверка для track_id
                self.update_specific_tag(f1_mod_manager.temp_custom_file.name, "track_id", original_id, new_id)
            else:
                # Для остальных ID, если таковые есть
                f1_mod_manager.update_temp_custom_file(id_tag, original_id, new_id)

        QMessageBox.information(self, "Success", "Conflicts resolved and updated in custom track data.")
        self.accept()  # Закрываем диалог

    def update_specific_tag(self, file_name, tag_name, original_id, new_id):
        with open(file_name, 'r', encoding='utf-8') as custom_file:
            custom_content = custom_file.readlines()

        with open(file_name, 'w', encoding='utf-8') as custom_file:
            for line in custom_content:
                # Заменяем оригинальный ID только если это нужный тег
                if tag_name in line and original_id in line:
                    line = line.replace(original_id, new_id)
                custom_file.write(line)


class F1ModManager(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initUI()
        
        self.custom_track_data = None
        self.database_xml = None
        self.temp_custom_file = None

    def initUI(self):
        self.setGeometry(100, 100, 800, 600)
        self.setWindowTitle("F1 2014 Track Mod Installer - Beta 1.0")

        central_widget = QWidget(self)
        self.setCentralWidget(central_widget)

        layout = QVBoxLayout(central_widget)
        
        self.track_label = QLabel("F1 2014 Track Mod Installer", self)
        self.track_label.setStyleSheet("font-size: 32px; font-weight: 700; color: #cbcbcb;")
        layout.addWidget(self.track_label)

        label1 = QLabel("1. Upload the .xml file containing your custom database track mod", self)
        layout.addWidget(label1)

        custom_track_button = QPushButton("Import Custom Track Data", self)
        custom_track_button.setStyleSheet("""
            QPushButton {
                width: 220px;
                height: 60px;
                font-size: 16px;
                font-weight: 500;
                background: qlineargradient(spread:pad, x1:1, y1:0, x2:0, y2:1, stop:0 rgb(50, 50, 70), stop:1 rgb(70, 70, 70));
                color: #fdfdfd;
                border-radius: 10px; 
            }
            QPushButton:hover {
                background: qlineargradient(spread:pad, x1:1, y1:0, x2:0, y2:1, stop:0 rgb(60, 60, 80), stop:1 rgb(80, 80, 80));
                border-radius: 20px; 
                font-weight: 700;
            }
        """)
        custom_track_button.clicked.connect(self.import_custom_track_data)
        layout.addWidget(custom_track_button)

        label2 = QLabel("2. Upload the original database.xml file \n3. Save the database file as .xml and import it into the Ego Database Editor", self)
        layout.addWidget(label2)

        database_button = QPushButton("Upload your database.xml", self)
        database_button.setStyleSheet("""
            QPushButton {
                width: 220px;
                height: 60px;
                font-size: 16px;
                font-weight: 500;
                background: qlineargradient(spread:pad, x1:1, y1:0, x2:0, y2:1, stop:0 rgb(50, 50, 70), stop:1 rgb(70, 70, 70));
                color: #fdfdfd;
                border-radius: 10px; 
            }
            QPushButton:hover {
                background: qlineargradient(spread:pad, x1:1, y1:0, x2:0, y2:1, stop:0 rgb(60, 60, 80), stop:1 rgb(80, 80, 80));
                border-radius: 20px; 
                font-weight: 700;
            }
        """)
        database_button.clicked.connect(self.upload_database_xml)
        layout.addWidget(database_button)

        check_button = QPushButton("Check for database conflicts", self)
        check_button.clicked.connect(self.check_for_conflicts)
        layout.addWidget(check_button)

        save_button = QPushButton("Save database file as .xml", self)
        save_button.setStyleSheet("""
            QPushButton {
                width: 220px;
                height: 60px;
                font-size: 16px;
                font-weight: 500;
                background: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 rgb(221, 58, 70), stop:0.5 rgb(243, 29, 59), stop:1 rgb(252, 176, 69));
                color: #fdfdfd;
                border-radius: 10px; 
            }
            QPushButton:hover {
                background: qlineargradient(spread:pad, x1:1, y1:0, x2:0, y2:1, stop:0 rgb(253, 152, 1), stop:1 rgb(221, 58, 70));
                border-radius: 20px; 
                font-weight: 700;
            }
        """)
        save_button.clicked.connect(self.save_database_xml)
        layout.addWidget(save_button)

        menubar = self.menuBar()
        fileMenu = menubar.addMenu('File')
        helpMenu = menubar.addMenu('Help')

        newAction = QAction('New', self)
        newAction.triggered.connect(self.new_project)
        fileMenu.addAction(newAction)

        exitAction = QAction('Exit', self)
        exitAction.triggered.connect(self.close)
        fileMenu.addAction(exitAction)

        aboutAction = QAction('About', self)
        aboutAction.triggered.connect(self.show_about)
        helpMenu.addAction(aboutAction)

        self.statusBar().showMessage("Ready")

    def new_project(self):
        self.custom_track_data = None
        self.database_xml = None
        if self.temp_custom_file and os.path.exists(self.temp_custom_file.name):
            os.remove(self.temp_custom_file.name)  # Удаляем временный файл, если он существует
        self.temp_custom_file = None
        self.statusBar().showMessage("New project started")

    def show_about(self):
        QMessageBox.about(self, "About", "Author - AlonsoBeast\nLink to Our Discord Modding Server: https://discord.gg/KXt7NC3Sue")

    def import_custom_track_data(self):
        options = QFileDialog.Options()
        file_name, _ = QFileDialog.getOpenFileName(self, "Select Custom Track Data File", "", "All Files (*)", options=options)
        if file_name:
            self.custom_track_data = file_name
            self.statusBar().showMessage(f"Imported Custom Track Data: {file_name}")

            # Создаем временный файл и записываем в него данные
            with open(file_name, 'r', encoding='utf-8') as custom_file:
                custom_content = custom_file.read()
            
            # Используем tempfile для создания временного файла
            self.temp_custom_file = tempfile.NamedTemporaryFile(delete=False, mode='w', encoding='utf-8')
            self.temp_custom_file.write(custom_content)
            self.temp_custom_file.close()

    def upload_database_xml(self):
        options = QFileDialog.Options()
        file_name, _ = QFileDialog.getOpenFileName(self, "Select Database File", "", "All Files (*)", options=options)
        if file_name:
            self.database_xml = file_name
            self.statusBar().showMessage(f"Uploaded Database File: {file_name}")

    def save_database_xml(self):
        if self.temp_custom_file and self.database_xml:
            try:
                # Чтение данных из временного файла
                with open(self.temp_custom_file.name, 'r', encoding='utf-8') as custom_file:
                    custom_content = custom_file.read()
                with open(self.database_xml, 'r', encoding='utf-8') as database_file:
                    database_content = database_file.read()

                # Логика для обработки и сохранения данных
                custom_blocks = {
                    'track': self.extract_block(custom_content, 'track'),
                    'track_car_handling': self.extract_block(custom_content, 'track_car_handling'),
                    'track_fuel_distance': self.extract_block(custom_content, 'track_fuel_distance'),
                    'track_model': self.extract_block(custom_content, 'track_model'),
                    'track_difficulties': self.extract_block(custom_content, 'track_difficulties')
                }

                # Теги для вставки
                insertion_tags = {
                    'track': ['<track_calendar>', '<track_fuel_distance>'],
                    'track_car_handling': ['<track_difficulties>', '<track_fuel_distance>'],
                    'track_fuel_distance': ['<track_model>', '<calendar_glyph>', '<id>', '<name_string_id>'],
                    'track_model': ['<track_stat_value>', '<track_stat_value>'],
                    'track_difficulties': ['<track_fuel_distance>', '<track_fuel_distance>']
                }

                # Вставка на основе ключевых точек
                for tag, tags in insertion_tags.items():
                    insertion_point = -1
                    for next_tag in tags:
                        insertion_point = database_content.find(next_tag)
                        if insertion_point != -1:  # Если нашли, прекращаем цикл
                            break

                    if insertion_point != -1 and custom_blocks[tag]:
                        # Проверка на наличие track_difficulties
                        if tag == 'track_difficulties':
                            if '<track_difficulties>' not in database_content:
                                continue  # Пропускаем вставку, если тег отсутствует

                        database_content = (
                            database_content[:insertion_point] +
                            custom_blocks[tag] +
                            "\n" +
                            database_content[insertion_point:]
                        )

                file_name, _ = QFileDialog.getSaveFileName(self, "Save Database File", "", "All Files (*)", options=QFileDialog.Options())
                if file_name:
                    with open(file_name, 'w', encoding='utf-8') as output_file:
                        output_file.write(database_content)
                    self.statusBar().showMessage(f"Database saved successfully: {file_name}")

            except Exception as e:
                self.statusBar().showMessage(f"Error during integration: {str(e)}")
        else:
            self.statusBar().showMessage("Please import both data files before saving.")

    @staticmethod
    def extract_block(content, tag):
        """Извлекает блок XML из содержимого для заданного тега."""
        start_tag = f'<{tag}>'
        end_tag = f'</{tag}>'
        start_index = content.find(start_tag)
        end_index = content.find(end_tag, start_index) + len(end_tag)
        if start_index != -1 and end_index != -1:
            return content[start_index:end_index]
        return ''

    def check_for_conflicts(self):
        if not self.temp_custom_file or not self.database_xml:
            self.statusBar().showMessage("Please import both data files before checking for conflicts.")
            return

        custom_ids = {
            '<track> ID': None,
            '<track_car_handling> ID': None,
            '<track_fuel_distance> ID': None,
            '<track_model> ID': None,
            '<track_model> calender_glyph': None,
            '<track_model> calendar_glyph': None,
            '<track_model> leaderboard_id': None,
            '<track_model> track_id': None,
            '<track_difficulties> ID': None,
            '<track_difficulties> track': None
        }

        # Парсинг кастомных данных
        custom_tree = ET.parse(self.temp_custom_file.name)

        # Сбор ID из кастомных данных
        for track in custom_tree.findall(".//track"):
            track_id_element = track.find("id")  
            if track_id_element is not None and track_id_element.text:
                custom_ids['<track> ID'] = track_id_element.text

        for handling in custom_tree.findall(".//track_car_handling"):
            handling_id_element = handling.find("ID")  
            if handling_id_element is None:  
                handling_id_element = handling.find("id")
            if handling_id_element is not None and handling_id_element.text:
                custom_ids['<track_car_handling> ID'] = handling_id_element.text

        for distance in custom_tree.findall(".//track_fuel_distance"):
            distance_id_element = distance.find("ID")  
            if distance_id_element is None:  
                distance_id_element = distance.find("id")
            if distance_id_element is not None and distance_id_element.text:
                custom_ids['<track_fuel_distance> ID'] = distance_id_element.text

        for model in custom_tree.findall(".//track_model"):
            model_id_element = model.find("id")  
            if model_id_element is None:  
                model_id_element = model.find("ID")
            if model_id_element is not None and model_id_element.text:
                custom_ids['<track_model> ID'] = model_id_element.text
                
            for child in model:
                if child.tag in ["calender_glyph", "calendar_glyph", "track_id", "leaderboard_id"]:
                    if child.text:
                        custom_ids[f'<track_model> {child.tag}'] = child.text

        for difficulties in custom_tree.findall(".//track_difficulties"):
            difficulties_id_element = difficulties.find("ID")  
            if difficulties_id_element is None:  
                difficulties_id_element = difficulties.find("id")
            if difficulties_id_element is not None and difficulties_id_element.text:
                custom_ids['<track_difficulties> ID'] = difficulties_id_element.text
                
            track_id_element = difficulties.find("track")  
            if track_id_element is None:  
                track_id_element = difficulties.find("Track")
            if track_id_element is not None and track_id_element.text:
                custom_ids['<track_difficulties> track'] = track_id_element.text

        print(f"Custom IDs: {custom_ids}")  # Выводим найденные идентификаторы кастомного трека

        # Проверка конфликтов с базой данных
        conflicts = {}
        database_tree = ET.parse(self.database_xml)

        print("Checking for conflicts in the database...")

        # Определяем пространство имен
        ns = {'ns': 'schemaF12013.xml', 'ns2': 'schemaF12014.xml'}

        found_in_db = {
            'track': database_tree.findall(".//ns:track", ns) + database_tree.findall(".//ns2:track", ns),
            'track_car_handling': database_tree.findall(".//ns:track_car_handling", ns) + database_tree.findall(".//ns2:track_car_handling", ns),
            'track_fuel_distance': database_tree.findall(".//ns:track_fuel_distance", ns) + database_tree.findall(".//ns2:track_fuel_distance", ns),
            'track_model': database_tree.findall(".//ns:track_model", ns) + database_tree.findall(".//ns2:track_model", ns),
            'track_difficulties': database_tree.findall(".//ns:track_difficulties", ns) + database_tree.findall(".//ns2:track_difficulties", ns)
        }

        # Проверка каждой секции жить пожить жить  пожить
        for db_type, elements in found_in_db.items():
            for database_element in elements:
                db_id = database_element.find("ns:id", ns)  
                if db_id is None:
                    db_id = database_element.find("ns:ID", ns)

                if db_id is None:
                    db_id = database_element.find("ns2:id", ns)
                    if db_id is None:
                        db_id = database_element.find("ns2:ID", ns)

                if db_id is not None and db_id.text:
                    print(f"Found in database: {database_element.tag} -> {db_id.text}")

                    # Проверяем для конкретного типа базы данных
                    for id_tag, custom_id in custom_ids.items():
                        if db_id.text == custom_id:
                            if f"<{db_type}> ID" in id_tag:  # Проверяем откуда этот идентификатор
                                conflicts[id_tag] = custom_id
                                print(f"Conflict detected: {id_tag} -> {custom_id}")

        if conflicts:
            print("Conflicts found: ", conflicts)
            conflict_dialog = IDConflictDialog(conflicts, self.update_temp_custom_file, self)
            conflict_dialog.exec_()
        else:
            QMessageBox.information(self, "No Conflicts", "No ID conflicts found.")

    def update_temp_custom_file(self, id_tag, original_id, new_id):
        # Обновление временного файла кастомных данных
        with open(self.temp_custom_file.name, 'r', encoding='utf-8') as custom_file:
            custom_content = custom_file.read()

        # Замена оригинального ID на новый
        custom_content = custom_content.replace(original_id, new_id)

        with open(self.temp_custom_file.name, 'w', encoding='utf-8') as custom_file:
            custom_file.write(custom_content)


app = QApplication(sys.argv)
app.setStyleSheet("""
    QMainWindow {
        background-color: #1e1d2b;
        font-family: 'Roboto', sans-serif;
    }
    QLabel {
        color: #cbcbcb;
        font-size: 16px;
    }
    QPushButton {
        background-color: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #ff8a00, stop:1 #e52e71);
        color: #FFFFFF;
        font-size: 16px;
        padding: 10px;
        border-radius: 25px;
    }
    QMenuBar, QStatusBar {
        background-color: #272938;
        color: #FFFFFF;
    }
    QMenuBar::item:selected {
        background-color: #3C3C3C;
    }
    QMenu {
        background-color: #272938;
        color: #FFFFFF;
    }
    QMenu::item:selected {
        background-color: #3C3C3C;
    }
    QMessageBox {
        font-size: 12px;
        font-weight: 200;
        background-color: #1D1D1D;
    }
""")

window = F1ModManager()
window.show()
sys.exit(app.exec_())