Загрузка данных


#!/usr/bin/env python3
import sys
import os

def parse_lattice_bitstream(input_path, output_path):
    if not os.path.exists(input_path):
        print(f"Error: File {input_path} not found.")
        return False

    with open(input_path, 'rb') as f:
        raw_data = f.read()

    file_size = len(raw_data)
    print(f"Opened file: {input_path} ({file_size} bytes)")

    curr_pos = 0

    # 1. Проверяем сигнатуру "LSCC" в самом начале
    if raw_data[0:4] == b'LSCC':
        print("Found header signature: LSCC")
        curr_pos += 4
    else:
        print("Error: File does not start with LSCC signature")
        return False

    # 2. Проверяем маркер комментариев 0xFF 0x00
    if raw_data[curr_pos:curr_pos+2] != b'\xff\x00':
        print(f"Error: Missing comment area marker (Expected FF 00, got {raw_data[curr_pos:curr_pos+2].hex()})")
        return False
    curr_pos += 2

    # 3. Динамический поиск преамбулы Nexus (0xb3bdffff, 0xb3bfffff или 0xb3beffff)
    # openFPGALoader ищет байт 0xB3, начиная от позиции после маркера комментариев
    pos_b3 = -1
    for i in range(curr_pos, file_size - 3):
        # Проверяем возможные варианты преамбулы с учетом порядка байт в Nexus
        # Преамбула лежит как: [3 фиктивных FF] + [ключ BD/BE/BF] + [B3]
        if raw_data[i] == 0xb3:
            enc_key = raw_data[i - 1]
            if enc_key in (0xbd, 0xbe, 0xbf):
                pos_b3 = i
                break

    if pos_b3 == -1:
        print("Error: Preamble key (0xB3 + BD/BE/BF) not found!")
        return False

    # Вычисляем точную позицию начала заголовка (выравнивание на 3 Dummy байта + ключ перед 0xB3)
    # То есть отступаем на 4 байта назад от найденного 0xB3
    end_header = pos_b3 - 4
    print(f"Header end calculated at offset: {end_header} bytes")

    # 4. Парсим текстовые метаданные (для информативности, как в openFPGALoader)
    header_bytes = raw_data[curr_pos:end_header]
    print("\n--- Bitstream Header Infos ---")
    lines = header_bytes.split(b'\x00')
    for line in lines:
        if b':' in line:
            try:
                decoded_line = line.decode('utf-8', errors='ignore').strip()
                if decoded_line:
                    print(decoded_line)
            except Exception:
                pass
    print("------------------------------\n")

    # 5. Отрезаем всё, что было ДО end_header, сохраняя чистый поток
    clean_bitstream = raw_data[end_header:]
    clean_size = len(clean_bitstream)

    print(f"Stripped {end_header} bytes of metadata.")
    print(f"Clean bitstream size: {clean_size} bytes (Matches openFPGALoader output!)")

    # 6. Сохраняем в выходной файл
    with open(output_path, 'wb') as f:
        f.write(clean_bitstream)
    print(f"Successfully saved clean binary to: {output_path}")
    return True

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("Usage: python3 parse_bit.py <input_file.bit> [output_file.bin]")
        sys.exit(1)

    infile = sys.argv[1]
    outfile = sys.argv[2] if len(sys.argv) > 2 else infile.replace('.bit', '_clean.bin')
    
    if outfile == infile:
        outfile = "out_clean.bin"

    success = parse_lattice_bitstream(infile, outfile)
    sys.exit(0 if success else 1)