This commit is contained in:
cfif 2026-05-28 10:53:38 +03:00
parent eb375c52c5
commit f161bcdfab
1 changed files with 426 additions and 39 deletions

View File

@ -57,7 +57,11 @@ typedef struct {
int write_crc; // Флаг: записывать CRC в файл
int verify_crc; // Флаг: проверять CRC
int show_only; // Флаг: только показать CRC без записи
int split; // Флаг: разрезать файл
int verbose; // Флаг: подробный вывод
long split_offset; // Смещение для разрезания (в байтах)
char *output1; // Имя первого выходного файла
char *output2; // Имя второго выходного файла
} ProgramOptions;
// Прототипы функций
@ -68,6 +72,7 @@ uint32_t calculate_file_crc32_without_last4(const char *filename, int verbose);
uint32_t calculate_file_crc32_full(const char *filename, int verbose);
int write_crc32_to_file(const char *filename, uint32_t crc_value, int verbose);
int verify_crc32(const char *filename, int verbose);
int split_bin_file(const char *input_file, long offset, const char *output1, const char *output2, int verbose);
/**
* @brief Вычисляет CRC32 (IEEE 802.3) для блока данных в памяти
@ -225,11 +230,15 @@ uint32_t calculate_file_crc32_full(const char *filename, int verbose) {
}
/**
* @brief Записывает CRC32 в последние 4 байта файла
* @brief Записывает размер данных и CRC32 в файл
* Размер данных (file_size - 4) записывается по смещению 256 байт от конца файла
* CRC32 записывается в последние 4 байта файла
*/
int write_crc32_to_file(const char *filename, uint32_t crc_value, int verbose) {
FILE *file = NULL;
long file_size;
long data_size;
int result = 0;
file = fopen(filename, "rb+");
if (file == NULL) {
@ -237,6 +246,7 @@ int write_crc32_to_file(const char *filename, uint32_t crc_value, int verbose) {
return -1;
}
// Получаем размер файла
if (fseek(file, 0, SEEK_END) != 0) {
perror("Failed to seek to end");
fclose(file);
@ -250,11 +260,70 @@ int write_crc32_to_file(const char *filename, uint32_t crc_value, int verbose) {
return -1;
}
// Перемещаемся на позицию последних 4 байт
data_size = file_size - 4; // Размер данных без CRC
if (verbose) {
printf("File size: %ld bytes\n", file_size);
printf("Data size (without CRC): %ld bytes (0x%lX)\n", data_size, data_size);
printf("CRC value: 0x%08X (%u)\n", crc_value, crc_value);
}
// ==============================================
// 1. Записываем размер данных по смещению 256 байт от конца файла
// ==============================================
long data_size_offset = file_size - 256; // Позиция для размера данных (ровно 256 байт от конца)
// Проверяем, что позиция не отрицательная и не перекрывается с CRC
if (data_size_offset < 0) {
fprintf(stderr, "Warning: File too small, cannot write data size at offset 256 from end\n");
fprintf(stderr, "File size: %ld bytes, need at least 256 bytes\n", file_size);
if (verbose) {
printf("Skipping data size write (file too small)\n");
}
} else if (data_size_offset + 4 > file_size - 4) {
fprintf(stderr, "Warning: Data size position overlaps with CRC area\n");
fprintf(stderr, "Data size would be at %ld, CRC at %ld\n", data_size_offset, file_size - 4);
if (verbose) {
printf("Skipping data size write (overlap detected)\n");
}
} else {
// Перемещаемся на позицию для записи размера данных
if (fseek(file, data_size_offset, SEEK_SET) != 0) {
perror("Failed to seek to data size position");
result = -1;
goto cleanup;
}
// Записываем размер данных в little-endian порядке (4 байта для 32-битного размера)
uint8_t size_bytes[4];
size_bytes[0] = (data_size >> 0) & 0xFF;
size_bytes[1] = (data_size >> 8) & 0xFF;
size_bytes[2] = (data_size >> 16) & 0xFF;
size_bytes[3] = (data_size >> 24) & 0xFF;
size_t bytes_written = fwrite(size_bytes, 1, 4, file);
if (bytes_written != 4) {
fprintf(stderr, "Failed to write data size to file\n");
result = -1;
goto cleanup;
}
if (verbose) {
printf("\nData size written: %ld bytes (0x%lX)\n", data_size, data_size);
printf(" Position: %ld (0x%lX) bytes from start\n", data_size_offset, data_size_offset);
printf(" Distance from end: %ld bytes (exactly 256 bytes)\n", file_size - data_size_offset);
printf(" Bytes written (LE 32-bit): %02X %02X %02X %02X\n",
size_bytes[0], size_bytes[1], size_bytes[2], size_bytes[3]);
}
}
// ==============================================
// 2. Записываем CRC в последние 4 байта файла
// ==============================================
if (fseek(file, file_size - 4, SEEK_SET) != 0) {
perror("Failed to seek to CRC position");
fclose(file);
return -1;
result = -1;
goto cleanup;
}
// Записываем CRC в little-endian порядке
@ -267,29 +336,34 @@ int write_crc32_to_file(const char *filename, uint32_t crc_value, int verbose) {
size_t bytes_written = fwrite(crc_bytes, 1, 4, file);
if (bytes_written != 4) {
fprintf(stderr, "Failed to write CRC to file\n");
fclose(file);
return -1;
result = -1;
goto cleanup;
}
fclose(file);
if (verbose) {
printf("CRC32 value written: 0x%08X (%u)\n", crc_value, crc_value);
printf("Written to bytes: %ld-%ld\n", file_size - 4, file_size - 1);
printf("\nCRC32 value written: 0x%08X (%u)\n", crc_value, crc_value);
printf(" Position: %ld-%ld (last 4 bytes)\n", file_size - 4, file_size - 1);
printf(" Bytes written (LE): %02X %02X %02X %02X\n",
crc_bytes[0], crc_bytes[1], crc_bytes[2], crc_bytes[3]);
printf("Byte order: little-endian (LSB first)\n");
}
return 0;
cleanup:
fclose(file);
return result;
}
/**
* @brief Проверяет CRC32, сравнивая вычисленное значение с сохраненным
* Также опционально проверяет сохраненный размер данных
*/
int verify_crc32(const char *filename, int verbose) {
FILE *file = NULL;
long file_size;
long stored_data_size = 0;
uint32_t calculated_crc = 0;
uint32_t stored_crc = 0;
int verify_size = 1; // Флаг проверки размера
file = fopen(filename, "rb");
if (file == NULL) {
@ -314,7 +388,48 @@ int verify_crc32(const char *filename, int verbose) {
printf("File size: %ld bytes\n", file_size);
}
// Читаем сохраненный CRC из последних 4 байт
// ==============================================
// 1. Читаем сохраненный размер данных (опционально)
// ==============================================
long data_size_offset = file_size - 256; // Позиция размера данных (256 байт от конца)
if (data_size_offset >= 0 && data_size_offset + 4 <= file_size - 4) {
if (fseek(file, data_size_offset, SEEK_SET) != 0) {
perror("Failed to seek to stored data size");
verify_size = 0;
} else {
uint8_t size_bytes[4];
size_t bytes_read = fread(size_bytes, 1, 4, file);
if (bytes_read == 4) {
stored_data_size = (long)size_bytes[0] << 0 |
(long)size_bytes[1] << 8 |
(long)size_bytes[2] << 16 |
(long)size_bytes[3] << 24;
if (verbose) {
printf("\nStored data size: %ld bytes (0x%lX) at offset %ld\n",
stored_data_size, stored_data_size, data_size_offset);
printf(" Distance from end: %ld bytes (exactly 256 bytes)\n", file_size - data_size_offset);
printf(" Bytes read (LE 32-bit): %02X %02X %02X %02X\n",
size_bytes[0], size_bytes[1], size_bytes[2], size_bytes[3]);
}
} else {
verify_size = 0;
if (verbose) {
printf("Failed to read stored data size (expected 4 bytes, got %zu)\n", bytes_read);
}
}
}
} else {
verify_size = 0;
if (verbose) {
printf("\nNo valid data size stored at offset 256 bytes from end\n");
printf(" File size: %ld, would need at least %ld bytes\n", file_size, 256 + 4);
}
}
// ==============================================
// 2. Читаем сохраненный CRC из последних 4 байт
// ==============================================
if (fseek(file, file_size - 4, SEEK_SET) != 0) {
perror("Failed to seek to stored CRC");
fclose(file);
@ -343,24 +458,224 @@ int verify_crc32(const char *filename, int verbose) {
return -1;
}
// Всегда выводим значения CRC при проверке
printf("\n╔════════════════════════════════════════╗\n");
printf("║ CRC32 Verification ║\n");
printf("╠════════════════════════════════════════╣\n");
printf("║ Stored CRC : 0x%08X (%10u) ║\n", stored_crc, stored_crc);
printf("║ Calculated CRC : 0x%08X (%10u) ║\n", calculated_crc, calculated_crc);
printf("╚════════════════════════════════════════╝\n");
long actual_data_size = file_size - 4;
if (calculated_crc == stored_crc) {
printf("\n✓ VERIFIED: CRC matches! File integrity check passed.\n");
// Всегда выводим значения CRC при проверке
printf("\n╔════════════════════════════════════════════════════════════╗\n");
printf("║ CRC32 Verification ║\n");
printf("╠════════════════════════════════════════════════════════════╣\n");
printf("║ Stored CRC : 0x%08X (%10u) ║\n", stored_crc, stored_crc);
printf("║ Calculated CRC : 0x%08X (%10u) ║\n", calculated_crc, calculated_crc);
if (verify_size && stored_data_size > 0) {
printf("╠════════════════════════════════════════════════════════════╣\n");
printf("║ Stored data size : %12ld bytes (0x%lX) ║\n", stored_data_size, stored_data_size);
printf("║ Actual data size : %12ld bytes (0x%lX) ║\n", actual_data_size, actual_data_size);
}
printf("╚════════════════════════════════════════════════════════════╝\n");
int crc_valid = (calculated_crc == stored_crc);
int size_valid = 1;
if (verify_size && stored_data_size > 0) {
size_valid = (actual_data_size == stored_data_size);
}
if (crc_valid && size_valid) {
printf("\n✓ VERIFIED: CRC matches and data size is correct! File integrity check passed.\n");
return 0;
} else {
printf("\n✗ MISMATCH: File is corrupted or CRC was not written correctly!\n");
} else if (crc_valid && !size_valid) {
printf("\n⚠ WARNING: CRC matches but data size mismatch!\n");
printf(" Stored size: %ld, Actual size: %ld, Difference: %ld\n",
stored_data_size, actual_data_size, actual_data_size - stored_data_size);
return 1;
} else if (!crc_valid && size_valid) {
printf("\n✗ MISMATCH: File is corrupted! CRC does not match (data size OK).\n");
printf(" Difference: 0x%08X\n", calculated_crc ^ stored_crc);
return 1;
} else {
printf("\n✗ MISMATCH: File is corrupted! Both CRC and data size are incorrect!\n");
printf(" CRC difference: 0x%08X\n", calculated_crc ^ stored_crc);
if (verify_size && stored_data_size > 0) {
printf(" Size difference: %ld bytes\n", actual_data_size - stored_data_size);
}
return 1;
}
}
/**
* @brief Разрезает BIN файл на два файла по указанному смещению
*/
int split_bin_file(const char *input_file, long offset, const char *output1, const char *output2, int verbose) {
FILE *fin = NULL;
FILE *fout1 = NULL;
FILE *fout2 = NULL;
uint8_t *buffer = NULL;
long file_size = 0;
long bytes_to_copy1 = 0;
long bytes_to_copy2 = 0;
size_t bytes_read, bytes_written;
int result = 0;
// Открываем входной файл
fin = fopen(input_file, "rb");
if (fin == NULL) {
perror("Failed to open input file");
return -1;
}
// Получаем размер файла
if (fseek(fin, 0, SEEK_END) != 0) {
perror("Failed to seek to end");
fclose(fin);
return -1;
}
file_size = ftell(fin);
rewind(fin);
printf("\n--- Splitting file ---\n");
printf("Input file: %s\n", input_file);
printf("File size: %ld bytes (0x%lX)\n", file_size, file_size);
printf("Split offset: %ld bytes (0x%lX)\n", offset, offset);
// Проверяем смещение
if (offset <= 0) {
fprintf(stderr, "Error: Split offset must be positive (got %ld)\n", offset);
fclose(fin);
return -1;
}
if (offset >= file_size) {
fprintf(stderr, "Error: Split offset (%ld) is greater than or equal to file size (%ld)\n",
offset, file_size);
fclose(fin);
return -1;
}
bytes_to_copy1 = offset;
bytes_to_copy2 = file_size - offset;
printf("First part size: %ld bytes (0x%lX)\n", bytes_to_copy1, bytes_to_copy1);
printf("Second part size: %ld bytes (0x%lX)\n", bytes_to_copy2, bytes_to_copy2);
printf("Output file 1: %s\n", output1);
printf("Output file 2: %s\n", output2);
// Открываем выходные файлы
fout1 = fopen(output1, "wb");
if (fout1 == NULL) {
perror("Failed to create output file 1");
fclose(fin);
return -1;
}
fout2 = fopen(output2, "wb");
if (fout2 == NULL) {
perror("Failed to create output file 2");
fclose(fin);
fclose(fout1);
return -1;
}
// Выделяем буфер для чтения/записи (1 МБ)
const size_t buffer_size = 1024 * 1024;
buffer = (uint8_t*)malloc(buffer_size);
if (buffer == NULL) {
perror("Failed to allocate buffer");
fclose(fin);
fclose(fout1);
fclose(fout2);
return -1;
}
// Копируем первую часть
if (verbose) {
printf("\nCopying first part (%ld bytes)...\n", bytes_to_copy1);
}
long remaining = bytes_to_copy1;
long progress_step = bytes_to_copy1 / 10;
long next_progress = progress_step;
while (remaining > 0) {
size_t to_read = (remaining < buffer_size) ? remaining : buffer_size;
bytes_read = fread(buffer, 1, to_read, fin);
if (bytes_read != to_read) {
fprintf(stderr, "Error reading input file (first part)\n");
result = -1;
goto cleanup;
}
bytes_written = fwrite(buffer, 1, bytes_read, fout1);
if (bytes_written != bytes_read) {
fprintf(stderr, "Error writing to output file 1\n");
result = -1;
goto cleanup;
}
remaining -= bytes_read;
if (verbose && progress_step > 0 && remaining <= next_progress) {
int percent = (int)((bytes_to_copy1 - remaining) * 100 / bytes_to_copy1);
printf(" Progress: %d%% (%ld bytes remaining)...\n", percent, remaining);
next_progress -= progress_step;
}
}
// Копируем вторую часть
if (verbose) {
printf("Copying second part (%ld bytes)...\n", bytes_to_copy2);
}
remaining = bytes_to_copy2;
progress_step = bytes_to_copy2 / 10;
next_progress = bytes_to_copy2 - progress_step;
while (remaining > 0) {
size_t to_read = (remaining < buffer_size) ? remaining : buffer_size;
bytes_read = fread(buffer, 1, to_read, fin);
if (bytes_read != to_read) {
fprintf(stderr, "Error reading input file (second part)\n");
result = -1;
goto cleanup;
}
bytes_written = fwrite(buffer, 1, bytes_read, fout2);
if (bytes_written != bytes_read) {
fprintf(stderr, "Error writing to output file 2\n");
result = -1;
goto cleanup;
}
remaining -= bytes_read;
if (verbose && progress_step > 0 && remaining <= next_progress) {
int percent = (int)((bytes_to_copy2 - remaining) * 100 / bytes_to_copy2);
printf(" Progress: %d%% (%ld bytes remaining)...\n", percent, remaining);
next_progress -= progress_step;
}
}
printf("\n✓ File split successfully!\n");
printf(" Part 1: %s (%ld bytes)\n", output1, bytes_to_copy1);
printf(" Part 2: %s (%ld bytes)\n", output2, bytes_to_copy2);
cleanup:
free(buffer);
fclose(fin);
fclose(fout1);
fclose(fout2);
// Если произошла ошибка, удаляем созданные файлы
if (result != 0) {
printf("\nError occurred, removing output files...\n");
remove(output1);
remove(output2);
}
return result;
}
/**
* @brief Выводит справку
*/
@ -373,16 +688,26 @@ void print_usage(const char *program_name) {
printf(" and write it to last 4 bytes\n");
printf(" -c, --check Verify CRC32 by comparing with stored value\n");
printf(" -s, --show-only Calculate and display CRC32 of entire file\n");
printf(" --split <offset> Split file into two parts at specified offset (bytes)\n");
printf(" -1, --output1 <file> Output filename for first part (with --split)\n");
printf(" -2, --output2 <file> Output filename for second part (with --split)\n");
printf(" -v, --verbose Show detailed information\n");
printf(" -h, --help Show this help\n");
printf("\nExamples:\n");
printf(" %s -s firmware.bin # Show CRC32 of entire file\n", program_name);
printf(" %s -w firmware.bin # Write CRC32 to last 4 bytes\n", program_name);
printf(" %s -c firmware.bin # Verify CRC32\n", program_name);
printf(" %s -v -w firmware.bin # Write with verbose output\n", program_name);
printf(" %s -s firmware.bin # Show CRC32 of entire file\n", program_name);
printf(" %s -w firmware.bin # Write CRC32 to last 4 bytes\n", program_name);
printf(" %s -c firmware.bin # Verify CRC32\n", program_name);
printf(" %s --split 4096 firmware.bin # Split at offset 4096 bytes\n", program_name);
printf(" %s --split 0x1000 firmware.bin # Split at offset 0x1000 (hex)\n", program_name);
printf(" %s --split 8192 -1 part1.bin -2 part2.bin firmware.bin\n", program_name);
printf(" %s -v --split 1024 firmware.bin # Split with verbose output\n", program_name);
printf("\nNote:\n");
printf(" For -w and -c operations, CRC is calculated from the first\n");
printf(" (file_size - 4) bytes, ignoring the last 4 bytes.\n");
printf(" Data size (file_size - 4) is written at offset 256 bytes from end.\n");
printf(" For split operation, offset can be specified in decimal or hex (0x prefix).\n");
printf(" If output filenames are not specified, default names will be used:\n");
printf(" <input>_part1.bin and <input>_part2.bin\n");
printf("\n");
}
@ -394,17 +719,26 @@ int parse_arguments(int argc, char *argv[], ProgramOptions *options) {
int c;
static struct option long_options[] = {
{"write", no_argument, 0, 'w'},
{"check", no_argument, 0, 'c'},
{"show-only", no_argument, 0, 's'},
{"verbose", no_argument, 0, 'v'},
{"help", no_argument, 0, 'h'},
{"write", no_argument, 0, 'w'},
{"check", no_argument, 0, 'c'},
{"show-only", no_argument, 0, 's'},
{"split", required_argument, 0, 1000},
{"output1", required_argument, 0, 1001},
{"output2", required_argument, 0, 1002},
{"verbose", no_argument, 0, 'v'},
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0}
};
memset(options, 0, sizeof(ProgramOptions));
while ((c = getopt_long(argc, argv, "wcsvh", long_options, &option_index)) != -1) {
// Инициализация имен выходных файлов
options->output1 = NULL;
options->output2 = NULL;
options->split_offset = 0;
// Поддержка коротких опций: -1 и -2 для output1/output2
while ((c = getopt_long(argc, argv, "wcsvh1:2:", long_options, &option_index)) != -1) {
switch (c) {
case 'w':
options->write_crc = 1;
@ -421,6 +755,27 @@ int parse_arguments(int argc, char *argv[], ProgramOptions *options) {
case 'h':
print_usage(argv[0]);
return -1;
case '1': // Короткая опция -1 для output1
options->output1 = strdup(optarg);
break;
case '2': // Короткая опция -2 для output2
options->output2 = strdup(optarg);
break;
case 1000: // --split
options->split = 1;
// Поддержка шестнадцатеричных чисел
if (optarg[0] == '0' && (optarg[1] == 'x' || optarg[1] == 'X')) {
options->split_offset = strtol(optarg, NULL, 16);
} else {
options->split_offset = strtol(optarg, NULL, 10);
}
break;
case 1001: // --output1
options->output1 = strdup(optarg);
break;
case 1002: // --output2
options->output2 = strdup(optarg);
break;
case '?':
return -1;
}
@ -434,14 +789,37 @@ int parse_arguments(int argc, char *argv[], ProgramOptions *options) {
options->filename = argv[optind];
if (!options->write_crc && !options->verify_crc && !options->show_only) {
// Подсчет количества выбранных режимов
int mode_count = options->write_crc + options->verify_crc + options->show_only + options->split;
if (mode_count > 1) {
fprintf(stderr, "Error: Cannot use multiple modes simultaneously\n");
return -1;
}
// Если режим не выбран, по умолчанию show-only
if (mode_count == 0) {
options->show_only = 1;
}
int mode_count = options->write_crc + options->verify_crc + options->show_only;
if (mode_count > 1) {
fprintf(stderr, "Error: Cannot use multiple modes\n");
return -1;
// Проверка параметров для split
if (options->split) {
if (options->split_offset <= 0) {
fprintf(stderr, "Error: Invalid split offset (must be > 0)\n");
return -1;
}
// Генерация имен выходных файлов, если не указаны
if (options->output1 == NULL) {
char *default_name = malloc(strlen(options->filename) + 10);
sprintf(default_name, "%s_part1.bin", options->filename);
options->output1 = default_name;
}
if (options->output2 == NULL) {
char *default_name = malloc(strlen(options->filename) + 10);
sprintf(default_name, "%s_part2.bin", options->filename);
options->output2 = default_name;
}
}
return 0;
@ -492,7 +870,7 @@ int main(int argc, char *argv[]) {
printf("╚════════════════════════════════════════╝\n");
if (options.verbose) {
printf("\nStep 2: Writing to last 4 bytes...\n");
printf("\nStep 2: Writing CRC and data size to file...\n");
}
result = write_crc32_to_file(options.filename, crc_value, options.verbose);
@ -515,6 +893,15 @@ int main(int argc, char *argv[]) {
printf("\n--- Verifying CRC32 for %s ---\n", options.filename);
result = verify_crc32(options.filename, options.verbose);
}
else if (options.split) {
// Разрезать файл
result = split_bin_file(options.filename, options.split_offset,
options.output1, options.output2, options.verbose);
// Освобождаем память для имен выходных файлов
if (options.output1 != NULL) free(options.output1);
if (options.output2 != NULL) free(options.output2);
}
return result;
}