From 95c6aa4583389a8d76f8a98b4de031176f871d37 Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Mon, 13 Oct 2025 16:07:48 +0100 Subject: [PATCH] Refactor compare for simplicity. --- tool/compare.c | 782 ++++++++++++++++++++++--------------------------- 1 file changed, 351 insertions(+), 431 deletions(-) diff --git a/tool/compare.c b/tool/compare.c index 8984e29..e7fae27 100644 --- a/tool/compare.c +++ b/tool/compare.c @@ -26,518 +26,438 @@ #include "termbox2.h" +// Helper structure to track comparison state +typedef struct { + int mid_x; + int height; + int left_row; + int right_row; + bool has_differences; +} compare_state_t; + void draw_progress_bar(int row, int percent) { const int width = tb_width() / 2; - const int bar_width = width - 4; // leave space for borders + const int bar_width = width - 4; const int filled = bar_width * percent / 100; - // Draw progress bar outline tb_printf(2, row, TB_YELLOW | TB_BOLD, TB_BLUE, "["); tb_printf(bar_width + 3, row, TB_YELLOW | TB_BOLD, TB_BLUE, "]"); - // Fill progress bar - for(int i = 0; i < filled; ++i) { tb_set_cell(3 + i, row, '=', TB_YELLOW | TB_BOLD, TB_BLUE); } + for(int i = 0; i < filled; ++i) { + tb_set_cell(3 + i, row, '=', TB_YELLOW | TB_BOLD, TB_BLUE); + } tb_present(); } -int compare(const char *path1, const char *path2) +static void setup_screen(compare_state_t *state) { - int ret = AARUF_STATUS_OK; - aaruformat_context *ctx1 = NULL; - aaruformat_context *ctx2 = NULL; - bool imagesAreDifferent = false; - char *strBuffer = NULL; - UErrorCode u_error_code = U_ZERO_ERROR; - int lr = 0; - int rr = 0; - uintattr_t appVerColor = TB_WHITE; - uintattr_t imageVerColor = TB_WHITE; - uintattr_t mediaTypeColor = TB_WHITE; - uintattr_t creationTimeColor = TB_WHITE; - uintattr_t lastWrittenTimeColor = TB_WHITE; - uintattr_t partitionsColor = TB_WHITE; - uintattr_t sessionsColor = TB_WHITE; - uintattr_t sectorsColor = TB_WHITE; - uintattr_t sectorSizeColor = TB_WHITE; - uintattr_t versionColor = TB_WHITE; - uint8_t sector_status1 = 0; - uint8_t sector_status2 = 0; + int width = tb_width(); + int height = tb_height(); + state->mid_x = width / 2; + state->height = height; + state->left_row = 9; + state->right_row = 9; + state->has_differences = false; - // Initialize termbox2 - if(tb_init() != 0) return 1; + tb_clear(); - // Get terminal dimensions - int width = tb_width(); // Total number of columns - int height = tb_height(); // Total number of rows - int mid_x = width / 2; // Midpoint for horizontal split - - tb_clear(); // Clear the screen buffer - - // Draw left panel (blue background) + // Draw panels and divider for(int y = 0; y < height; ++y) { - for(int x = 0; x < mid_x; ++x) - { - tb_set_cell(x, y, ' ', TB_WHITE, TB_BLUE); // Set each cell to blank with blue background - } + for(int x = 0; x < state->mid_x; ++x) + tb_set_cell(x, y, ' ', TB_WHITE, TB_BLUE); + + tb_set_cell(state->mid_x, y, '|', TB_BLACK | TB_BOLD, TB_BLUE); + + for(int x = state->mid_x + 1; x < width; ++x) + tb_set_cell(x, y, ' ', TB_WHITE, TB_BLUE); } +} - // Draw right panel (green background) - for(int y = 0; y < height; ++y) - { - for(int x = mid_x + 1; x < width; ++x) - { - tb_set_cell(x, y, ' ', TB_WHITE, TB_BLUE); // Set each cell to blank with green background - } - } +// Helper to print a field on both sides with comparison +static void print_field_pair(compare_state_t *state, const char *label, + const char *fmt, uintattr_t color, + void *val1, void *val2, int label_len) +{ + tb_printf(2, state->left_row, TB_WHITE | TB_BOLD, TB_BLUE, "%s", label); + tb_printf(label_len, state->left_row, color, TB_BLUE, fmt, val1); - // Draw vertical divider line (gray background) - for(int y = 0; y < height; ++y) - { - tb_set_cell(mid_x, y, '|', TB_BLACK | TB_BOLD, TB_BLUE); // Draw a vertical bar at the split - } + tb_printf(state->mid_x + 2, state->right_row, TB_WHITE | TB_BOLD, TB_BLUE, "%s", label); + tb_printf(state->mid_x + label_len, state->right_row, color, TB_BLUE, fmt, val2); - // Print labels in each panel - tb_printf(2, 1, TB_WHITE | TB_BOLD, TB_BLUE, path1); // Label in left panel - tb_printf(mid_x + 2, 1, TB_WHITE | TB_BOLD, TB_BLUE, path2); // Label in right panel + state->left_row++; + state->right_row++; +} - tb_present(); // Render the buffer to the terminal +// Helper to compare and print integer fields +static void compare_int_field(compare_state_t *state, const char *label, int label_len, + uint64_t val1, uint64_t val2, const char *fmt) +{ + uintattr_t color = (val1 != val2) ? TB_RED : TB_WHITE; + if(val1 != val2) state->has_differences = true; - // Open the first AaruFormat image - tb_printf(2, 2, TB_WHITE | TB_BOLD, TB_BLUE, "Opening image..."); - tb_present(); // Render the buffer to the terminal - ctx1 = aaruf_open(path1); - if(ctx1 == NULL) - { - tb_printf(2, 3, TB_RED | TB_BOLD, TB_BLUE, "Error opening: %s", errno); - tb_present(); - goto finished; - } + print_field_pair(state, label, fmt, color, &val1, &val2, label_len); +} - tb_printf(2, 2, TB_WHITE | TB_BOLD, TB_BLUE, "Image opened successfully..."); +// Helper to compare and print string fields (if at least one is non-NULL) +static void compare_string_field(compare_state_t *state, const char *label, int label_len, + const char *val1, const char *val2) +{ + if(val1 == NULL && val2 == NULL) return; - // Open the second AaruFormat image - tb_printf(mid_x + 2, 2, TB_WHITE | TB_BOLD, TB_BLUE, "Opening image..."); - tb_present(); // Render the buffer to the terminal - ctx2 = aaruf_open(path2); - if(ctx2 == NULL) - { - tb_printf(mid_x + 2, 3, TB_RED | TB_BOLD, TB_BLUE, "Error opening: %s", errno); - tb_present(); - goto finished; - } + tb_printf(2, state->left_row, TB_WHITE | TB_BOLD, TB_BLUE, "%s", label); + if(val1) tb_printf(label_len, state->left_row, TB_WHITE, TB_BLUE, "%s", val1); - tb_printf(mid_x + 2, 2, TB_WHITE | TB_BOLD, TB_BLUE, "Image opened successfully..."); + tb_printf(state->mid_x + 2, state->right_row, TB_WHITE | TB_BOLD, TB_BLUE, "%s", label); + if(val2) tb_printf(state->mid_x + label_len, state->right_row, TB_WHITE, TB_BLUE, "%s", val2); - u_error_code = - u_strCompare((const UChar *)ctx1->header.application, -1, (const UChar *)ctx2->header.application, -1, false); - if(u_error_code != U_ZERO_ERROR) imagesAreDifferent = true; + state->left_row++; + state->right_row++; +} - strBuffer = malloc(65); +// Helper to convert and print UTF-16 application name +static void print_application_name(compare_state_t *state, + const char *app1, const char *app2) +{ + char strBuffer[65]; + UErrorCode u_error_code = U_ZERO_ERROR; + + // Left side memset(strBuffer, 0, 65); - u_error_code = U_ZERO_ERROR; // Reset error code before conversion - ucnv_convert(NULL, "UTF-16LE", strBuffer, 64, ctx1->header.application, 64, &u_error_code); + ucnv_convert(NULL, "UTF-16LE", strBuffer, 64, app1, 64, &u_error_code); + tb_printf(2, 3, TB_WHITE | TB_BOLD, TB_BLUE, "Application: "); if(u_error_code == U_ZERO_ERROR) - { - tb_printf(2, 3, TB_WHITE | TB_BOLD, TB_BLUE, "Application: "); tb_printf(15, 3, TB_WHITE, TB_BLUE, "%s", strBuffer); - } - tb_present(); // Render the buffer to the terminal + // Right side memset(strBuffer, 0, 65); - u_error_code = U_ZERO_ERROR; // Reset error code before conversion - ucnv_convert(NULL, "UTF-16LE", strBuffer, 64, ctx2->header.application, 64, &u_error_code); - if(u_error_code == U_ZERO_ERROR) - { - tb_printf(mid_x + 2, 3, TB_WHITE | TB_BOLD, TB_BLUE, "Application: "); - tb_printf(mid_x + 15, 3, TB_WHITE, TB_BLUE, "%s", strBuffer); - } - tb_present(); // Render the buffer to the terminal - free(strBuffer); - - // Compare header information - if(ctx1->header.applicationMajorVersion != ctx2->header.applicationMajorVersion) - { - imagesAreDifferent = true; - appVerColor = TB_RED; - } - - if(ctx1->header.applicationMinorVersion != ctx2->header.applicationMinorVersion) - { - imagesAreDifferent = true; - appVerColor = TB_RED; - } - - if(ctx1->header.imageMajorVersion != ctx2->header.imageMajorVersion) - { - imagesAreDifferent = true; - imageVerColor = TB_RED; - } - - if(ctx1->header.imageMinorVersion != ctx2->header.imageMinorVersion) - { - imagesAreDifferent = true; - imageVerColor = TB_RED; - } - - if(ctx1->header.mediaType != ctx2->header.mediaType) - { - imagesAreDifferent = true; - mediaTypeColor = TB_RED; - } - - if(ctx1->header.creationTime != ctx2->header.creationTime) - { - imagesAreDifferent = true; - creationTimeColor = TB_RED; - } - - if(ctx1->header.lastWrittenTime != ctx2->header.lastWrittenTime) - { - imagesAreDifferent = true; - lastWrittenTimeColor = TB_RED; - } - - // Print header information for the first image - tb_printf(2, 4, TB_WHITE | TB_BOLD, TB_BLUE, "Application version: "); - tb_printf(23, 4, appVerColor, TB_BLUE, "%d.%d", ctx1->header.applicationMajorVersion, - ctx1->header.applicationMinorVersion); - tb_printf(2, 5, TB_WHITE | TB_BOLD, TB_BLUE, "Image format version: "); - tb_printf(24, 5, imageVerColor, TB_BLUE, "%d.%d", ctx1->header.imageMajorVersion, ctx1->header.imageMinorVersion); - tb_printf(2, 6, TB_WHITE | TB_BOLD, TB_BLUE, "Media type: "); - tb_printf(14, 6, mediaTypeColor, TB_BLUE, "%u", ctx1->header.mediaType); - tb_printf(2, 7, TB_WHITE | TB_BOLD, TB_BLUE, "Creation time: "); - tb_printf(17, 7, creationTimeColor, TB_BLUE, "%lld", ctx1->header.creationTime); - tb_printf(2, 8, TB_WHITE | TB_BOLD, TB_BLUE, "Last written time: "); - tb_printf(21, 8, lastWrittenTimeColor, TB_BLUE, "%lld", ctx1->header.lastWrittenTime); - tb_present(); // Render the buffer to the terminal - - // Print header information for the second image - tb_printf(mid_x + 2, 4, TB_WHITE | TB_BOLD, TB_BLUE, "Application version: "); - tb_printf(mid_x + 23, 4, appVerColor, TB_BLUE, "%d.%d", ctx2->header.applicationMajorVersion, - ctx2->header.applicationMinorVersion); - tb_printf(mid_x + 2, 5, TB_WHITE | TB_BOLD, TB_BLUE, "Image format version: "); - tb_printf(mid_x + 24, 5, imageVerColor, TB_BLUE, "%d.%d", ctx2->header.imageMajorVersion, - ctx2->header.imageMinorVersion); - tb_printf(mid_x + 2, 6, TB_WHITE | TB_BOLD, TB_BLUE, "Media type: "); - tb_printf(mid_x + 14, 6, mediaTypeColor, TB_BLUE, "%u", ctx2->header.mediaType); - tb_printf(mid_x + 2, 7, TB_WHITE | TB_BOLD, TB_BLUE, "Creation time: "); - tb_printf(mid_x + 17, 7, creationTimeColor, TB_BLUE, "%lld", ctx2->header.creationTime); - tb_printf(mid_x + 2, 8, TB_WHITE | TB_BOLD, TB_BLUE, "Last written time: "); - tb_printf(mid_x + 21, 8, lastWrittenTimeColor, TB_BLUE, "%lld", ctx2->header.lastWrittenTime); - tb_present(); // Render the buffer to the terminal - - // Compare ImageInfo u_error_code = U_ZERO_ERROR; - u_error_code = u_strCompare((const UChar *)ctx1->image_info.Application, -1, - (const UChar *)ctx2->image_info.Application, -1, false); - if(u_error_code != U_ZERO_ERROR) imagesAreDifferent = true; + ucnv_convert(NULL, "UTF-16LE", strBuffer, 64, app2, 64, &u_error_code); + tb_printf(state->mid_x + 2, 3, TB_WHITE | TB_BOLD, TB_BLUE, "Application: "); + if(u_error_code == U_ZERO_ERROR) + tb_printf(state->mid_x + 15, 3, TB_WHITE, TB_BLUE, "%s", strBuffer); +} - // Current left row - lr = 9; - // Current right row - rr = 9; +// Compare header information +static void compare_headers(compare_state_t *state, + aaruformat_context *ctx1, aaruformat_context *ctx2) +{ + // Check application name difference + UErrorCode u_err = u_strCompare((const UChar *)ctx1->header.application, -1, + (const UChar *)ctx2->header.application, -1, false); + if(u_err != U_ZERO_ERROR) state->has_differences = true; - if(ctx1->image_info.HasPartitions != ctx2->image_info.HasPartitions) - { - imagesAreDifferent = true; - partitionsColor = TB_RED; - } - if(ctx1->image_info.HasSessions != ctx2->image_info.HasSessions) - { - imagesAreDifferent = true; - sessionsColor = TB_RED; - } - if(ctx1->image_info.Sectors != ctx2->image_info.Sectors) - { - imagesAreDifferent = true; - sectorsColor = TB_RED; - } - if(ctx1->image_info.SectorSize != ctx2->image_info.SectorSize) - { - imagesAreDifferent = true; - sectorSizeColor = TB_RED; - } - if(ctx1->image_info.Version != ctx2->image_info.Version) - { - imagesAreDifferent = true; - versionColor = TB_RED; - } + print_application_name(state, ctx1->header.application, ctx2->header.application); + + // Compare version fields + uintattr_t app_ver_color = (ctx1->header.applicationMajorVersion != ctx2->header.applicationMajorVersion || + ctx1->header.applicationMinorVersion != ctx2->header.applicationMinorVersion) + ? TB_RED : TB_WHITE; + uintattr_t img_ver_color = (ctx1->header.imageMajorVersion != ctx2->header.imageMajorVersion || + ctx1->header.imageMinorVersion != ctx2->header.imageMinorVersion) + ? TB_RED : TB_WHITE; + uintattr_t media_color = (ctx1->header.mediaType != ctx2->header.mediaType) ? TB_RED : TB_WHITE; + uintattr_t create_color = (ctx1->header.creationTime != ctx2->header.creationTime) ? TB_RED : TB_WHITE; + uintattr_t modified_color = (ctx1->header.lastWrittenTime != ctx2->header.lastWrittenTime) ? TB_RED : TB_WHITE; + + if(app_ver_color == TB_RED || img_ver_color == TB_RED || media_color == TB_RED || + create_color == TB_RED || modified_color == TB_RED) + state->has_differences = true; + + // Print header fields + tb_printf(2, 4, TB_WHITE | TB_BOLD, TB_BLUE, "Application version: "); + tb_printf(23, 4, app_ver_color, TB_BLUE, "%d.%d", + ctx1->header.applicationMajorVersion, ctx1->header.applicationMinorVersion); + tb_printf(state->mid_x + 2, 4, TB_WHITE | TB_BOLD, TB_BLUE, "Application version: "); + tb_printf(state->mid_x + 23, 4, app_ver_color, TB_BLUE, "%d.%d", + ctx2->header.applicationMajorVersion, ctx2->header.applicationMinorVersion); + + tb_printf(2, 5, TB_WHITE | TB_BOLD, TB_BLUE, "Image format version: "); + tb_printf(24, 5, img_ver_color, TB_BLUE, "%d.%d", + ctx1->header.imageMajorVersion, ctx1->header.imageMinorVersion); + tb_printf(state->mid_x + 2, 5, TB_WHITE | TB_BOLD, TB_BLUE, "Image format version: "); + tb_printf(state->mid_x + 24, 5, img_ver_color, TB_BLUE, "%d.%d", + ctx2->header.imageMajorVersion, ctx2->header.imageMinorVersion); + + tb_printf(2, 6, TB_WHITE | TB_BOLD, TB_BLUE, "Media type: "); + tb_printf(14, 6, media_color, TB_BLUE, "%u", ctx1->header.mediaType); + tb_printf(state->mid_x + 2, 6, TB_WHITE | TB_BOLD, TB_BLUE, "Media type: "); + tb_printf(state->mid_x + 14, 6, media_color, TB_BLUE, "%u", ctx2->header.mediaType); + + tb_printf(2, 7, TB_WHITE | TB_BOLD, TB_BLUE, "Creation time: "); + tb_printf(17, 7, create_color, TB_BLUE, "%lld", ctx1->header.creationTime); + tb_printf(state->mid_x + 2, 7, TB_WHITE | TB_BOLD, TB_BLUE, "Creation time: "); + tb_printf(state->mid_x + 17, 7, create_color, TB_BLUE, "%lld", ctx2->header.creationTime); + + tb_printf(2, 8, TB_WHITE | TB_BOLD, TB_BLUE, "Last written time: "); + tb_printf(21, 8, modified_color, TB_BLUE, "%lld", ctx1->header.lastWrittenTime); + tb_printf(state->mid_x + 2, 8, TB_WHITE | TB_BOLD, TB_BLUE, "Last written time: "); + tb_printf(state->mid_x + 21, 8, modified_color, TB_BLUE, "%lld", ctx2->header.lastWrittenTime); - tb_printf(2, lr, TB_WHITE | TB_BOLD, TB_BLUE, "Has partitions?: "); - tb_printf(19, lr++, partitionsColor, TB_BLUE, "%s", ctx1->image_info.HasPartitions ? "yes" : "no"); - tb_printf(2, lr, TB_WHITE | TB_BOLD, TB_BLUE, "Has sessions?: "); - tb_printf(17, lr++, sessionsColor, TB_BLUE, "%s", ctx1->image_info.HasSessions ? "yes" : "no"); - tb_printf(2, lr, TB_WHITE | TB_BOLD, TB_BLUE, "Image size without headers: "); - tb_printf(30, lr++, TB_WHITE, TB_BLUE, "%llu bytes", ctx1->image_info.ImageSize); - tb_printf(2, lr, TB_WHITE | TB_BOLD, TB_BLUE, "Image contains: "); - tb_printf(18, lr++, sectorsColor, TB_BLUE, "%llu sectors", ctx1->image_info.Sectors); - tb_printf(2, lr, TB_WHITE | TB_BOLD, TB_BLUE, "Biggest sector is: "); - tb_printf(21, lr++, sectorSizeColor, TB_BLUE, "%d bytes", ctx1->image_info.SectorSize); - tb_printf(2, lr, TB_WHITE | TB_BOLD, TB_BLUE, "Image version: "); - tb_printf(17, lr++, versionColor, TB_BLUE, "%s", ctx1->image_info.Version); tb_present(); +} + +// Compare image info +static void compare_image_info(compare_state_t *state, + aaruformat_context *ctx1, aaruformat_context *ctx2) +{ + compare_int_field(state, "Has partitions?: ", 19, + ctx1->image_info.HasPartitions, ctx2->image_info.HasPartitions, "%s"); + state->left_row--; + state->right_row--; + tb_printf(19, state->left_row, (ctx1->image_info.HasPartitions != ctx2->image_info.HasPartitions) ? TB_RED : TB_WHITE, + TB_BLUE, "%s", ctx1->image_info.HasPartitions ? "yes" : "no"); + tb_printf(state->mid_x + 19, state->right_row, + (ctx1->image_info.HasPartitions != ctx2->image_info.HasPartitions) ? TB_RED : TB_WHITE, + TB_BLUE, "%s", ctx2->image_info.HasPartitions ? "yes" : "no"); + state->left_row++; + state->right_row++; + + compare_int_field(state, "Has sessions?: ", 17, + ctx1->image_info.HasSessions, ctx2->image_info.HasSessions, "%s"); + state->left_row--; + state->right_row--; + tb_printf(17, state->left_row, (ctx1->image_info.HasSessions != ctx2->image_info.HasSessions) ? TB_RED : TB_WHITE, + TB_BLUE, "%s", ctx1->image_info.HasSessions ? "yes" : "no"); + tb_printf(state->mid_x + 17, state->right_row, + (ctx1->image_info.HasSessions != ctx2->image_info.HasSessions) ? TB_RED : TB_WHITE, + TB_BLUE, "%s", ctx2->image_info.HasSessions ? "yes" : "no"); + state->left_row++; + state->right_row++; + + tb_printf(2, state->left_row, TB_WHITE | TB_BOLD, TB_BLUE, "Image size without headers: "); + tb_printf(30, state->left_row, TB_WHITE, TB_BLUE, "%llu bytes", ctx1->image_info.ImageSize); + tb_printf(state->mid_x + 2, state->right_row, TB_WHITE | TB_BOLD, TB_BLUE, "Image size without headers: "); + tb_printf(state->mid_x + 30, state->right_row, TB_WHITE, TB_BLUE, "%llu bytes", ctx2->image_info.ImageSize); + state->left_row++; + state->right_row++; + + compare_int_field(state, "Image contains: ", 18, + ctx1->image_info.Sectors, ctx2->image_info.Sectors, "%llu sectors"); + compare_int_field(state, "Biggest sector is: ", 21, + ctx1->image_info.SectorSize, ctx2->image_info.SectorSize, "%d bytes"); + + uintattr_t ver_color = TB_WHITE; + if(ctx1->image_info.Version && ctx2->image_info.Version && + strcmp(ctx1->image_info.Version, ctx2->image_info.Version) != 0) { + ver_color = TB_RED; + state->has_differences = true; + } + + tb_printf(2, state->left_row, TB_WHITE | TB_BOLD, TB_BLUE, "Image version: "); + if(ctx1->image_info.Version) tb_printf(17, state->left_row, ver_color, TB_BLUE, "%s", ctx1->image_info.Version); + tb_printf(state->mid_x + 2, state->right_row, TB_WHITE | TB_BOLD, TB_BLUE, "Image version: "); + if(ctx2->image_info.Version) tb_printf(state->mid_x + 17, state->right_row, ver_color, TB_BLUE, "%s", ctx2->image_info.Version); + state->left_row++; + state->right_row++; - tb_printf(mid_x + 2, rr, TB_WHITE | TB_BOLD, TB_BLUE, "Has partitions?: "); - tb_printf(mid_x + 19, rr++, partitionsColor, TB_BLUE, "%s", ctx2->image_info.HasPartitions ? "yes" : "no"); - tb_printf(mid_x + 2, rr, TB_WHITE | TB_BOLD, TB_BLUE, "Has sessions?: "); - tb_printf(mid_x + 17, rr++, sessionsColor, TB_BLUE, "%s", ctx2->image_info.HasSessions ? "yes" : "no"); - tb_printf(mid_x + 2, rr, TB_WHITE | TB_BOLD, TB_BLUE, "Image size without headers: "); - tb_printf(mid_x + 30, rr++, TB_WHITE, TB_BLUE, "%llu bytes", ctx2->image_info.ImageSize); - tb_printf(mid_x + 2, rr, TB_WHITE | TB_BOLD, TB_BLUE, "Image contains: "); - tb_printf(mid_x + 18, rr++, sectorsColor, TB_BLUE, "%llu sectors", ctx2->image_info.Sectors); - tb_printf(mid_x + 2, rr, TB_WHITE | TB_BOLD, TB_BLUE, "Biggest sector is: "); - tb_printf(mid_x + 21, rr++, sectorSizeColor, TB_BLUE, "%d bytes", ctx2->image_info.SectorSize); - tb_printf(mid_x + 2, rr, TB_WHITE | TB_BOLD, TB_BLUE, "Image version: "); - tb_printf(mid_x + 17, rr++, versionColor, TB_BLUE, "%s", ctx2->image_info.Version); tb_present(); +} - if(ctx1->image_info.Application != NULL || ctx2->image_info.Application != NULL) +// Compare metadata fields +static void compare_metadata(compare_state_t *state, + aaruformat_context *ctx1, aaruformat_context *ctx2) +{ + compare_string_field(state, "Application version: ", 23, + ctx1->image_info.ApplicationVersion, ctx2->image_info.ApplicationVersion); + compare_string_field(state, "Creator: ", 11, ctx1->creator, ctx2->creator); + + compare_int_field(state, "Creation time: ", 17, + ctx1->image_info.CreationTime, ctx2->image_info.CreationTime, "%lld"); + compare_int_field(state, "Last written time: ", 21, + ctx1->image_info.LastModificationTime, ctx2->image_info.LastModificationTime, "%lld"); + + compare_string_field(state, "Comments: ", 12, ctx1->comments, ctx2->comments); + compare_string_field(state, "Media title: ", 15, ctx1->media_title, ctx2->media_title); + compare_string_field(state, "Media manufacturer: ", 22, + ctx1->media_manufacturer, ctx2->media_manufacturer); + compare_string_field(state, "Media serial number: ", 23, + ctx1->media_serial_number, ctx2->media_serial_number); + compare_string_field(state, "Media barcode: ", 17, ctx1->media_barcode, ctx2->media_barcode); + compare_string_field(state, "Media part number: ", 21, + ctx1->media_part_number, ctx2->media_part_number); + + compare_int_field(state, "Media type: ", 14, + ctx1->image_info.MediaType, ctx2->image_info.MediaType, "%u"); + + if(ctx1->media_sequence > 0 || ctx1->last_media_sequence > 0 || + ctx2->media_sequence > 0 || ctx2->last_media_sequence > 0) { - strBuffer = malloc(65); - memset(strBuffer, 0, 65); - u_error_code = U_ZERO_ERROR; // Reset error code before conversion - ucnv_convert(NULL, "UTF-16LE", strBuffer, 64, (const char *)ctx1->image_info.Application, 64, &u_error_code); - tb_printf(2, lr, TB_WHITE | TB_BOLD, TB_BLUE, "Application: "); - if(u_error_code == U_ZERO_ERROR) tb_printf(15, lr, TB_WHITE, TB_BLUE, "%s", strBuffer); - lr++; - - memset(strBuffer, 0, 65); - u_error_code = U_ZERO_ERROR; // Reset error code before conversion - ucnv_convert(NULL, "UTF-16LE", strBuffer, 64, (const char *)ctx2->image_info.Application, 64, &u_error_code); - tb_printf(mid_x + 2, rr, TB_WHITE | TB_BOLD, TB_BLUE, "Application: "); - if(u_error_code == U_ZERO_ERROR) tb_printf(mid_x + 15, rr, TB_WHITE, TB_BLUE, "%s", strBuffer); - rr++; - - free(strBuffer); - tb_present(); + tb_printf(2, state->left_row, TB_WHITE | TB_BOLD, TB_BLUE, + "Media is number %d in a set of %d media", + ctx1->media_sequence, ctx1->last_media_sequence); + tb_printf(state->mid_x + 2, state->right_row, TB_WHITE | TB_BOLD, TB_BLUE, + "Media is number %d in a set of %d media", + ctx2->media_sequence, ctx2->last_media_sequence); + state->left_row++; + state->right_row++; } - if(ctx1->image_info.ApplicationVersion != NULL || ctx2->image_info.ApplicationVersion != NULL) + compare_string_field(state, "Drive manufacturer: ", 22, + ctx1->drive_manufacturer, ctx2->drive_manufacturer); + compare_string_field(state, "Drive model: ", 15, ctx1->drive_model, ctx2->drive_model); + compare_string_field(state, "Drive serial number: ", 23, + ctx1->drive_serial_number, ctx2->drive_serial_number); + compare_string_field(state, "Drive firmware revision: ", 27, + ctx1->drive_firmware_revision, ctx2->drive_firmware_revision); + + compare_int_field(state, "XML media type: ", 18, + ctx1->image_info.MetadataMediaType, ctx2->image_info.MetadataMediaType, "%d"); + + if(ctx1->cylinders > 0 || ctx1->heads > 0 || ctx1->sectors_per_track > 0 || + ctx2->cylinders > 0 || ctx2->heads > 0 || ctx2->sectors_per_track > 0) { - tb_printf(2, lr, TB_WHITE | TB_BOLD, TB_BLUE, "Application version: "); - tb_printf(23, lr++, TB_WHITE, TB_BLUE, "%s", ctx1->image_info.ApplicationVersion); - - tb_printf(mid_x + 2, rr, TB_WHITE | TB_BOLD, TB_BLUE, "Application version: "); - tb_printf(mid_x + 23, rr++, TB_WHITE, TB_BLUE, "%s", ctx2->image_info.ApplicationVersion); - } - if(ctx1->creator != NULL || ctx2->creator != NULL) - { - tb_printf(2, lr, TB_WHITE | TB_BOLD, TB_BLUE, "Creator: "); - tb_printf(11, lr++, TB_WHITE, TB_BLUE, "%s", ctx1->creator); - - tb_printf(mid_x + 2, rr, TB_WHITE | TB_BOLD, TB_BLUE, "Creator: "); - tb_printf(mid_x + 11, rr++, TB_WHITE, TB_BLUE, "%s", ctx2->creator); - } - - tb_printf(2, lr, TB_WHITE | TB_BOLD, TB_BLUE, "Creation time: "); - tb_printf(17, lr++, TB_WHITE, TB_BLUE, "%lld", ctx1->image_info.CreationTime); - - tb_printf(mid_x + 2, rr, TB_WHITE | TB_BOLD, TB_BLUE, "Creation time: "); - tb_printf(mid_x + 17, rr++, TB_WHITE, TB_BLUE, "%lld", ctx2->image_info.CreationTime); - - tb_printf(2, lr, TB_WHITE | TB_BOLD, TB_BLUE, "Last written time: "); - tb_printf(21, lr++, TB_WHITE, TB_BLUE, "%lld", ctx1->image_info.LastModificationTime); - - tb_printf(mid_x + 2, rr, TB_WHITE | TB_BOLD, TB_BLUE, "Last written time: "); - tb_printf(mid_x + 21, rr++, TB_WHITE, TB_BLUE, "%lld", ctx2->image_info.LastModificationTime); - - if(ctx1->comments != NULL || ctx2->comments != NULL) - { - tb_printf(2, lr, TB_WHITE | TB_BOLD, TB_BLUE, "Comments: "); - tb_printf(12, lr++, TB_WHITE, TB_BLUE, "%s", ctx1->comments); - - tb_printf(mid_x + 2, rr, TB_WHITE | TB_BOLD, TB_BLUE, "Comments: "); - tb_printf(mid_x + 12, rr++, TB_WHITE, TB_BLUE, "%s", ctx2->comments); - } - if(ctx1->media_title != NULL || ctx2->media_title != NULL) - { - tb_printf(2, lr, TB_WHITE | TB_BOLD, TB_BLUE, "Media title: "); - tb_printf(15, lr++, TB_WHITE, TB_BLUE, "%s", ctx1->media_title); - - tb_printf(mid_x + 2, rr, TB_WHITE | TB_BOLD, TB_BLUE, "Media title: "); - tb_printf(mid_x + 15, rr++, TB_WHITE, TB_BLUE, "%s", ctx2->media_title); - } - if(ctx1->media_manufacturer != NULL || ctx2->media_manufacturer != NULL) - { - tb_printf(2, lr, TB_WHITE | TB_BOLD, TB_BLUE, "Media manufacturer: "); - tb_printf(22, lr++, TB_WHITE, TB_BLUE, "%s", ctx1->media_manufacturer); - - tb_printf(mid_x + 2, rr, TB_WHITE | TB_BOLD, TB_BLUE, "Media manufacturer: "); - tb_printf(mid_x + 22, rr++, TB_WHITE, TB_BLUE, "%s", ctx2->media_manufacturer); - } - if(ctx1->media_serial_number != NULL || ctx2->media_serial_number != NULL) - { - tb_printf(2, lr, TB_WHITE | TB_BOLD, TB_BLUE, "Media serial number: "); - tb_printf(23, lr++, TB_WHITE, TB_BLUE, "%s", ctx1->media_serial_number); - - tb_printf(mid_x + 2, rr, TB_WHITE | TB_BOLD, TB_BLUE, "Media serial number: "); - tb_printf(mid_x + 23, rr++, TB_WHITE, TB_BLUE, "%s", ctx2->media_serial_number); - } - if(ctx1->media_barcode != NULL || ctx2->media_barcode != NULL) - { - tb_printf(2, lr, TB_WHITE | TB_BOLD, TB_BLUE, "Media barcode: "); - tb_printf(17, lr++, TB_WHITE, TB_BLUE, "%s", ctx1->media_barcode); - - tb_printf(mid_x + 2, rr, TB_WHITE | TB_BOLD, TB_BLUE, "Media barcode: "); - tb_printf(mid_x + 17, rr++, TB_WHITE, TB_BLUE, "%s", ctx2->media_barcode); - } - if(ctx1->media_part_number != NULL || ctx2->media_part_number != NULL) - { - tb_printf(2, lr, TB_WHITE | TB_BOLD, TB_BLUE, "Media part number: "); - tb_printf(21, lr++, TB_WHITE, TB_BLUE, "%s", ctx1->media_part_number); - - tb_printf(mid_x + 2, rr, TB_WHITE | TB_BOLD, TB_BLUE, "Media part number: "); - tb_printf(mid_x + 21, rr++, TB_WHITE, TB_BLUE, "%s", ctx2->media_part_number); - } - tb_printf(2, lr, TB_WHITE | TB_BOLD, TB_BLUE, "Media type: "); - tb_printf(14, lr++, TB_WHITE, TB_BLUE, "%u", ctx1->image_info.MediaType); - - tb_printf(mid_x + 2, rr, TB_WHITE | TB_BOLD, TB_BLUE, "Media type: "); - tb_printf(mid_x + 14, rr++, TB_WHITE, TB_BLUE, "%u", ctx2->image_info.MediaType); - - if(ctx1->media_sequence > 0 || ctx1->last_media_sequence > 0 || ctx2->media_sequence > 0 || - ctx2->last_media_sequence > 0) - { - tb_printf(2, lr++, TB_WHITE | TB_BOLD, TB_BLUE, "Media is number %d in a set of %d media", ctx1->media_sequence, - ctx1->last_media_sequence); - - tb_printf(mid_x + 2, rr++, TB_WHITE | TB_BOLD, TB_BLUE, "Media is number %d in a set of %d media", - ctx2->media_sequence, ctx2->last_media_sequence); - } - - if(ctx1->drive_manufacturer != NULL || ctx2->drive_manufacturer != NULL) - { - tb_printf(2, lr, TB_WHITE | TB_BOLD, TB_BLUE, "Drive manufacturer: "); - tb_printf(22, lr++, TB_WHITE, TB_BLUE, "%s", ctx1->drive_manufacturer); - - tb_printf(mid_x + 2, rr, TB_WHITE | TB_BOLD, TB_BLUE, "Drive manufacturer: "); - tb_printf(mid_x + 22, rr++, TB_WHITE, TB_BLUE, "%s", ctx2->drive_manufacturer); - } - if(ctx1->drive_model != NULL || ctx2->drive_model != NULL) - { - tb_printf(2, lr, TB_WHITE | TB_BOLD, TB_BLUE, "Drive model: "); - tb_printf(15, lr++, TB_WHITE, TB_BLUE, "%s", ctx1->drive_model); - - tb_printf(mid_x + 2, rr, TB_WHITE | TB_BOLD, TB_BLUE, "Drive model: "); - tb_printf(mid_x + 15, rr++, TB_WHITE, TB_BLUE, "%s", ctx2->drive_model); - } - if(ctx1->drive_serial_number != NULL || ctx2->drive_serial_number != NULL) - { - tb_printf(2, lr, TB_WHITE | TB_BOLD, TB_BLUE, "Drive serial number: "); - tb_printf(23, lr++, TB_WHITE, TB_BLUE, "%s", ctx1->drive_serial_number); - - tb_printf(mid_x + 2, rr, TB_WHITE | TB_BOLD, TB_BLUE, "Drive serial number: "); - tb_printf(mid_x + 23, rr++, TB_WHITE, TB_BLUE, "%s", ctx2->drive_serial_number); - } - if(ctx1->drive_firmware_revision != NULL || ctx2->drive_firmware_revision != NULL) - { - tb_printf(2, lr, TB_WHITE | TB_BOLD, TB_BLUE, "Drive firmware revision: "); - tb_printf(27, lr++, TB_WHITE, TB_BLUE, "%s", ctx1->drive_firmware_revision); - - tb_printf(mid_x + 2, rr, TB_WHITE | TB_BOLD, TB_BLUE, "Drive firmware revision: "); - tb_printf(mid_x + 27, rr++, TB_WHITE, TB_BLUE, "%s", ctx2->drive_firmware_revision); - } - tb_printf(2, lr, TB_WHITE | TB_BOLD, TB_BLUE, "XML media type: "); - tb_printf(18, lr++, TB_WHITE, TB_BLUE, "%d", ctx1->image_info.MetadataMediaType); - - tb_printf(mid_x + 2, rr, TB_WHITE | TB_BOLD, TB_BLUE, "XML media type: "); - tb_printf(mid_x + 18, rr++, TB_WHITE, TB_BLUE, "%d", ctx2->image_info.MetadataMediaType); - - if(ctx1->cylinders > 0 || ctx1->heads > 0 || ctx1->sectors_per_track > 0 || ctx2->cylinders > 0 || - ctx2->heads > 0 || ctx2->sectors_per_track > 0) - { - tb_printf(2, lr++, TB_WHITE | TB_BOLD, TB_BLUE, "Media has %d cylinders, %d heads and %d sectors per track", - ctx1->cylinders, ctx1->heads, ctx1->sectors_per_track); - - tb_printf(mid_x + 2, rr++, TB_WHITE | TB_BOLD, TB_BLUE, - "Media has %d cylinders, %d heads and %d sectors per track", ctx2->cylinders, ctx2->heads, - ctx2->sectors_per_track); + tb_printf(2, state->left_row, TB_WHITE | TB_BOLD, TB_BLUE, + "Media has %d cylinders, %d heads and %d sectors per track", + ctx1->cylinders, ctx1->heads, ctx1->sectors_per_track); + tb_printf(state->mid_x + 2, state->right_row, TB_WHITE | TB_BOLD, TB_BLUE, + "Media has %d cylinders, %d heads and %d sectors per track", + ctx2->cylinders, ctx2->heads, ctx2->sectors_per_track); + state->left_row++; + state->right_row++; } tb_present(); +} - lr++; +// Compare sector contents +static int compare_sectors(compare_state_t *state, + aaruformat_context *ctx1, aaruformat_context *ctx2) +{ uint64_t sectors = ctx1->image_info.Sectors; if(ctx2->image_info.Sectors < sectors) sectors = ctx2->image_info.Sectors; - bool imageContentsAreDifferent = false; - uint32_t sectorSize = ctx1->image_info.SectorSize; - if(ctx2->image_info.SectorSize > sectorSize) sectorSize = ctx2->image_info.SectorSize; - uint8_t *buffer1 = malloc(sectorSize); + uint32_t sectorSize = ctx1->image_info.SectorSize; + if(ctx2->image_info.SectorSize > sectorSize) sectorSize = ctx2->image_info.SectorSize; + + uint8_t *buffer1 = malloc(sectorSize); if(buffer1 == NULL) { - tb_printf(2, lr, TB_RED | TB_BOLD, TB_BLUE, "Error allocating memory for buffer: %s", errno); + tb_printf(2, state->left_row, TB_RED | TB_BOLD, TB_BLUE, + "Error allocating memory for buffer"); tb_present(); - ret = AARUF_ERROR_NOT_ENOUGH_MEMORY; - goto finished; + return AARUF_ERROR_NOT_ENOUGH_MEMORY; } uint8_t *buffer2 = malloc(sectorSize); - if(buffer2 == NULL) { free(buffer1); - tb_printf(2, lr, TB_RED | TB_BOLD, TB_BLUE, "Error allocating memory for buffer: %s", errno); + tb_printf(2, state->left_row, TB_RED | TB_BOLD, TB_BLUE, + "Error allocating memory for buffer"); tb_present(); - ret = AARUF_ERROR_NOT_ENOUGH_MEMORY; - goto finished; + return AARUF_ERROR_NOT_ENOUGH_MEMORY; } + bool contents_differ = false; + uint8_t sector_status1 = 0; + uint8_t sector_status2 = 0; + for(uint64_t i = 0; i < sectors; i++) { - tb_printf(2, height - 5, TB_WHITE | TB_BOLD, TB_BLUE, "Comparing sector %llu of %llu", i + 1, sectors); - draw_progress_bar(height - 4, i * 100 / sectors); + tb_printf(2, state->height - 5, TB_WHITE | TB_BOLD, TB_BLUE, + "Comparing sector %llu of %llu", i + 1, sectors); + draw_progress_bar(state->height - 4, i * 100 / sectors); - errno = aaruf_read_sector(ctx1, i, false, buffer1, §orSize, §or_status1); - if(errno != AARUF_STATUS_OK && errno != AARUF_STATUS_SECTOR_NOT_DUMPED) + int err1 = aaruf_read_sector(ctx1, i, false, buffer1, §orSize, §or_status1); + int err2 = aaruf_read_sector(ctx2, i, false, buffer2, §orSize, §or_status2); + + if(err1 != AARUF_STATUS_OK && err1 != AARUF_STATUS_SECTOR_NOT_DUMPED) continue; + if(err2 != AARUF_STATUS_OK && err2 != AARUF_STATUS_SECTOR_NOT_DUMPED) continue; + + if(memcmp(buffer1, buffer2, sectorSize) != 0) { - tb_printf(2, lr++, TB_RED | TB_BOLD, TB_BLUE, "Error reading sector %llu: %s", i, errno); - tb_present(); - continue; - } - - errno = aaruf_read_sector(ctx2, i, false, buffer2, §orSize, §or_status2); - if(errno != AARUF_STATUS_OK && errno != AARUF_STATUS_SECTOR_NOT_DUMPED) - { - tb_printf(2, rr++, TB_RED | TB_BOLD, TB_BLUE, "Error reading sector %llu: %s", i, errno); - tb_present(); - continue; - } - - for(uint32_t j = 0; j < sectorSize; j++) - if(buffer1[j] != buffer2[j]) + contents_differ = true; + if(state->left_row < state->height - 6) { - imageContentsAreDifferent = true; - tb_printf(2, lr++, TB_RED | TB_BOLD, TB_BLUE, "Sector %llu differs at byte %u", i, j); + tb_printf(2, state->left_row++, TB_RED | TB_BOLD, TB_BLUE, + "Sector %llu differs", i); tb_present(); - break; // Exit the loop on first difference } + } } - draw_progress_bar(height - 4, 100); // Fill the progress bar to 100% + + draw_progress_bar(state->height - 4, 100); free(buffer1); free(buffer2); - if(imagesAreDifferent) - tb_printf(2, height - 3, TB_RED | TB_BOLD, TB_BLUE, "Images are different!"); + // Print final comparison results + if(state->has_differences) + tb_printf(2, state->height - 3, TB_RED | TB_BOLD, TB_BLUE, "Images are different!"); else - tb_printf(2, height - 3, TB_GREEN | TB_BOLD, TB_BLUE, "Images are identical!"); - if(imageContentsAreDifferent) - tb_printf(2, height - 2, TB_RED | TB_BOLD, TB_BLUE, "Images contents are different!"); + tb_printf(2, state->height - 3, TB_GREEN | TB_BOLD, TB_BLUE, "Images are identical!"); + + if(contents_differ) + tb_printf(2, state->height - 2, TB_RED | TB_BOLD, TB_BLUE, "Images contents are different!"); else - tb_printf(2, height - 2, TB_GREEN | TB_BOLD, TB_BLUE, "Images contents are identical!"); + tb_printf(2, state->height - 2, TB_GREEN | TB_BOLD, TB_BLUE, "Images contents are identical!"); + + return AARUF_STATUS_OK; +} + +int compare(const char *path1, const char *path2) +{ + int ret = AARUF_STATUS_OK; + aaruformat_context *ctx1 = NULL; + aaruformat_context *ctx2 = NULL; + compare_state_t state = {0}; + + if(tb_init() != 0) return 1; + + setup_screen(&state); + + // Print file paths + tb_printf(2, 1, TB_WHITE | TB_BOLD, TB_BLUE, "%s", path1); + tb_printf(state.mid_x + 2, 1, TB_WHITE | TB_BOLD, TB_BLUE, "%s", path2); + tb_present(); + + // Open first image + tb_printf(2, 2, TB_WHITE | TB_BOLD, TB_BLUE, "Opening image..."); + tb_present(); + ctx1 = aaruf_open(path1); + if(ctx1 == NULL) + { + tb_printf(2, 3, TB_RED | TB_BOLD, TB_BLUE, "Error opening image"); + tb_present(); + ret = -1; + goto finished; + } + tb_printf(2, 2, TB_WHITE | TB_BOLD, TB_BLUE, "Image opened successfully..."); + + // Open second image + tb_printf(state.mid_x + 2, 2, TB_WHITE | TB_BOLD, TB_BLUE, "Opening image..."); + tb_present(); + ctx2 = aaruf_open(path2); + if(ctx2 == NULL) + { + tb_printf(state.mid_x + 2, 3, TB_RED | TB_BOLD, TB_BLUE, "Error opening image"); + tb_present(); + ret = -1; + goto finished; + } + tb_printf(state.mid_x + 2, 2, TB_WHITE | TB_BOLD, TB_BLUE, "Image opened successfully..."); + + // Perform comparisons + compare_headers(&state, ctx1, ctx2); + compare_image_info(&state, ctx1, ctx2); + compare_metadata(&state, ctx1, ctx2); + + state.left_row++; + ret = compare_sectors(&state, ctx1, ctx2); finished: - tb_printf(2, height - 1, TB_WHITE | TB_BOLD, TB_BLUE, "Press any key to exit..."); - tb_present(); // Render the buffer to the terminal - // Wait for a key press before exiting - struct tb_event ev; - tb_poll_event(&ev); // Block until user presses a key + tb_printf(2, state.height - 1, TB_WHITE | TB_BOLD, TB_BLUE, "Press any key to exit..."); + tb_present(); + + struct tb_event ev; + tb_poll_event(&ev); + + if(ctx1) aaruf_close(ctx1); + if(ctx2) aaruf_close(ctx2); - // Restore terminal and exit tb_shutdown(); return ret; } \ No newline at end of file