diff --git a/tool/CMakeLists.txt b/tool/CMakeLists.txt index 2001d64..861ff3e 100644 --- a/tool/CMakeLists.txt +++ b/tool/CMakeLists.txt @@ -6,6 +6,6 @@ find_package(Argtable3 CONFIG REQUIRED) include_directories(${ICU_INCLUDE_DIRS}) add_executable(aaruformattool main.c version.h aaruformattool.h identify.c info.c helpers.c read.c printhex.c verify.c ecc_cd.c - commands.h commands.c usage.h usage.c compare.c termbox2.h) + commands.h commands.c usage.h usage.c compare.c convert.c termbox2.h) target_link_libraries(aaruformattool "aaruformat" argtable3::argtable3) target_link_libraries(aaruformattool "aaruformat" ICU::uc) diff --git a/tool/aaruformattool.h b/tool/aaruformattool.h index 6e89e78..35bde81 100644 --- a/tool/aaruformattool.h +++ b/tool/aaruformattool.h @@ -34,5 +34,6 @@ int verify_sectors(const char *path); bool check_cd_sector_channel(CdEccContext *context, uint8_t *sector, bool *unknown, bool *has_edc, bool *edc_correct, bool *has_ecc_p, bool *ecc_p_correct, bool *has_ecc_q, bool *ecc_q_correct); int compare(char *path1, char *path2); +int convert(char *input_path, char *output_path); #endif // LIBAARUFORMAT_TOOL_AARUFORMATTOOL_H_ diff --git a/tool/commands.c b/tool/commands.c index a9b71ef..5ec50d3 100644 --- a/tool/commands.c +++ b/tool/commands.c @@ -132,6 +132,26 @@ int cmd_verify(int argc, char *argv[]) { return cmd_verify_common(argc, argv, fa int cmd_verify_sectors(int argc, char *argv[]) { return cmd_verify_common(argc, argv, true); } +int cmd_convert(int argc, char *argv[]) +{ + struct arg_str *input_filename = arg_str1(NULL, NULL, "", "Input image file"); + struct arg_str *output_filename = arg_str1(NULL, NULL, "", "Output image file"); + struct arg_end *end = arg_end(10); + void *argtable[] = {input_filename, output_filename, end}; + + if(arg_parse(argc, argv, argtable) > 0) + { + arg_print_errors(stderr, end, "convert"); + usage_convert(); + arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); + return -1; + } + + int result = convert(input_filename->sval[0], output_filename->sval[0]); + arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); + return result; +} + Command commands[] = { { "identify", cmd_identify}, { "info", cmd_info}, @@ -140,6 +160,7 @@ Command commands[] = { { "verify", cmd_verify}, {"verify_sectors", cmd_verify_sectors}, { "compare", cmd_compare}, + { "convert", cmd_convert}, }; const size_t num_commands = sizeof(commands) / sizeof(commands[0]); diff --git a/tool/commands.h b/tool/commands.h index 65ed1f7..f296030 100644 --- a/tool/commands.h +++ b/tool/commands.h @@ -39,5 +39,6 @@ int cmd_read_long(int argc, char *argv[]); int cmd_verify(int argc, char *argv[]); int cmd_verify_sectors(int argc, char *argv[]); int cmd_compare(int argc, char *argv[]); +int cmd_convert(int argc, char *argv[]); #endif // LIBAARUFORMAT_COMMANDS_H diff --git a/tool/convert.c b/tool/convert.c new file mode 100644 index 0000000..767d06f --- /dev/null +++ b/tool/convert.c @@ -0,0 +1,125 @@ +/* + * This file is part of the Aaru Data Preservation Suite. + * Copyright (c) 2019-2025 Natalia Portillo. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include + +#include + +#include "aaruformattool.h" + +int convert(char *input_path, char *output_path) +{ + aaruformatContext *input_ctx = NULL; + aaruformatContext *output_ctx = NULL; + int32_t res = 0; + uint32_t sector_size = 0; + uint64_t total_sectors = 0; + uint8_t *sector_data = NULL; + + printf("Converting image from %s to %s...\n", input_path, output_path); + + // Open input image + input_ctx = aaruf_open(input_path); + if(input_ctx == NULL) + { + printf("Error %d when opening input AaruFormat image.\n", errno); + return errno; + } + + // Get image information from input + total_sectors = input_ctx->imageInfo.Sectors; + sector_size = input_ctx->imageInfo.SectorSize; + + printf("Input image has %llu sectors of %u bytes each.\n", (unsigned long long)total_sectors, sector_size); + + // Create output image + output_ctx = aaruf_create(output_path, input_ctx->imageInfo.MediaType, sector_size, total_sectors, + 0, // negative sectors + 0, // overflow sectors + NULL, // options + (const uint8_t *)"aaruformattool", + 14, // application name length + 1, // major version + 0 // minor version + ); + + if(output_ctx == NULL) + { + printf("Error %d when creating output AaruFormat image.\n", errno); + aaruf_close(input_ctx); + return errno; + } + + // Allocate buffer for sector data + sector_data = malloc(sector_size); + if(sector_data == NULL) + { + printf("Error allocating memory for sector buffer.\n"); + aaruf_close(input_ctx); + aaruf_close(output_ctx); + return AARUF_ERROR_NOT_ENOUGH_MEMORY; + } + + // Copy sectors from input to output + for(uint64_t sector = 0; sector < total_sectors; sector++) + { + uint32_t read_length = sector_size; + + // Show progress every 1000 sectors + if(sector % 1000 == 0 || sector == total_sectors - 1) + { + printf("\rProgress: %llu/%llu sectors (%.1f%%)", (unsigned long long)sector + 1, + (unsigned long long)total_sectors, ((double)(sector + 1) / total_sectors) * 100.0); + fflush(stdout); + } + + // Read sector from input + res = aaruf_read_sector(input_ctx, sector, sector_data, &read_length); + if(res != AARUF_STATUS_OK) + { + printf("\nError %d when reading sector %llu from input image.\n", res, (unsigned long long)sector); + break; + } + + // Write sector to output + res = aaruf_write_sector(output_ctx, sector, sector_data, 0, read_length); + if(res != AARUF_STATUS_OK) + { + printf("\nError %d when writing sector %llu to output image.\n", res, (unsigned long long)sector); + break; + } + } + + printf("\n"); + + // Clean up + free(sector_data); + aaruf_close(input_ctx); + res = aaruf_close(output_ctx); + + if(res == AARUF_STATUS_OK) + printf("Conversion completed successfully.\n"); + else + printf("Conversion failed with error %d.\n", res); + + return res; +} diff --git a/tool/usage.c b/tool/usage.c index 548e762..7f342b2 100644 --- a/tool/usage.c +++ b/tool/usage.c @@ -39,7 +39,9 @@ void usage() printf(" read Reads a sector and prints it out on screen.\n"); printf(" read_long Reads a sector with all its prefixes and suffixes.\n"); printf(" verify Verifies the integrity of blocks in an image.\n"); - printf(" verify_sectors Verifies the integrity of all sectors in an image.\n\n"); + printf(" verify_sectors Verifies the integrity of all sectors in an image.\n"); + printf(" compare Compares two AaruFormat images.\n"); + printf(" convert Converts an AaruFormat image to another AaruFormat image.\n\n"); printf("For help with any verb, run:\n"); printf(" aaruformattool --help\n"); } @@ -109,3 +111,13 @@ void usage_compare() printf(" Path to first image file.\n"); printf(" Path to second image file.\n"); } + +void usage_convert() +{ + printf("\nUsage:\n"); + printf(" aaruformattool convert \n\n"); + printf("Converts an AaruFormat image by reading all sectors from input and writing them to output.\n"); + printf("Arguments:\n"); + printf(" Path to input image file.\n"); + printf(" Path to output image file.\n"); +} diff --git a/tool/usage.h b/tool/usage.h index 16ff9df..6e97278 100644 --- a/tool/usage.h +++ b/tool/usage.h @@ -29,5 +29,6 @@ void usage_read_long(); void usage_verify(); void usage_verify_sectors(); void usage_compare(); +void usage_convert(); #endif // LIBAARUFORMAT_USAGE_H