diff --git a/CMakeLists.txt b/CMakeLists.txt index 163569f..122cc49 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -156,10 +156,20 @@ include(3rdparty/flac.cmake) include(3rdparty/lzma.cmake) include(3rdparty/xxhash.cmake) include(3rdparty/blake3.cmake) + +# Find and link ICU library for UTF-16LE to UTF-8 conversion +find_package(ICU COMPONENTS uc REQUIRED) + if(TARGET blake3) target_link_libraries(aaruformat blake3) endif() +# Add ICU include directories and link library to the target +if(ICU_FOUND) + target_include_directories(aaruformat PRIVATE ${ICU_INCLUDE_DIRS}) + target_link_libraries(aaruformat ${ICU_LIBRARIES}) +endif() + macro(TARGET_LINK_LIBRARIES_WHOLE_ARCHIVE target) if(MSVC) foreach(lib IN LISTS ARGN) diff --git a/include/aaru.h b/include/aaru.h index 76bfeee..e7a1b64 100644 --- a/include/aaru.h +++ b/include/aaru.h @@ -807,18 +807,18 @@ typedef enum */ typedef struct ImageInfo // NOLINT { - uint8_t HasPartitions; ///< Image contains partitions (or tracks for optical media) - uint8_t HasSessions; ///< Image contains sessions (optical media only) - uint64_t ImageSize; ///< Size of the image without headers - uint64_t Sectors; ///< Sectors contained in the image - uint32_t SectorSize; ///< Size of sectors contained in the image - uint8_t *Version; ///< Image version - uint8_t *Application; ///< Application that created the image - uint8_t *ApplicationVersion; ///< Version of the application that created the image - int64_t CreationTime; ///< Image creation time - int64_t LastModificationTime; ///< Image last modification time - uint32_t MediaType; ///< Media type represented by the image - uint8_t XmlMediaType; ///< Type of the media represented by the image to use in XML sidecars + uint8_t HasPartitions; ///< Image contains partitions (or tracks for optical media) + uint8_t HasSessions; ///< Image contains sessions (optical media only) + uint64_t ImageSize; ///< Size of the image without headers + uint64_t Sectors; ///< Sectors contained in the image + uint32_t SectorSize; ///< Size of sectors contained in the image + char Version[32]; ///< Image version + char Application[64]; ///< Application that created the image + char ApplicationVersion[32]; ///< Version of the application that created the image + int64_t CreationTime; ///< Image creation time + int64_t LastModificationTime; ///< Image last modification time + uint32_t MediaType; ///< Media type represented by the image + uint8_t XmlMediaType; ///< Type of the media represented by the image to use in XML sidecars } ImageInfo; /** \addtogroup SectorTags Per-sector metadata tag types diff --git a/src/create.c b/src/create.c index 484ecb6..a20b8da 100644 --- a/src/create.c +++ b/src/create.c @@ -22,6 +22,9 @@ #include #include +#include +#include + #include "aaruformat.h" #include "enums.h" #include "internal.h" @@ -73,17 +76,7 @@ static void cleanup_failed_create(aaruformatContext *ctx) ctx->readableSectorTags = NULL; } - if(ctx->imageInfo.ApplicationVersion != NULL) - { - free(ctx->imageInfo.ApplicationVersion); - ctx->imageInfo.ApplicationVersion = NULL; - } - - if(ctx->imageInfo.Version != NULL) - { - free(ctx->imageInfo.Version); - ctx->imageInfo.Version = NULL; - } + // ApplicationVersion and Version are fixed-size arrays, not pointers - no need to free if(ctx->imageStream != NULL) { @@ -368,20 +361,75 @@ void *aaruf_create(const char *filepath, const uint32_t media_type, const uint32 // Initialize image info TRACE("Initializing image info"); - ctx->imageInfo.Application = ctx->header.application; - ctx->imageInfo.ApplicationVersion = (uint8_t *)malloc(32); - if(ctx->imageInfo.ApplicationVersion != NULL) + + // Convert application name from UTF-16LE to UTF-8 using libicu + UErrorCode status = U_ZERO_ERROR; + int32_t app_name_utf16_len = AARU_HEADER_APP_NAME_LEN / 2; // UTF-16LE uses 2 bytes per character + UChar *app_name_utf16 = (UChar *)malloc(app_name_utf16_len * sizeof(UChar)); + + if(app_name_utf16 != NULL) { - memset(ctx->imageInfo.ApplicationVersion, 0, 32); - sprintf((char *)ctx->imageInfo.ApplicationVersion, "%d.%d", ctx->header.applicationMajorVersion, - ctx->header.applicationMinorVersion); + // Convert raw UTF-16LE bytes to UChar (UTF-16, host endian) + for(int32_t j = 0; j < app_name_utf16_len; j++) + { + app_name_utf16[j] = (UChar)(ctx->header.application[j * 2] | (ctx->header.application[j * 2 + 1] << 8)); + } + + // Get required length for UTF-8 + int32_t app_name_utf8_len = 0; + u_strToUTF8(NULL, 0, &app_name_utf8_len, app_name_utf16, app_name_utf16_len, &status); + + if(U_SUCCESS(status) || status == U_BUFFER_OVERFLOW_ERROR) + { + status = U_ZERO_ERROR; + + // Ensure it fits in the Application buffer (64 bytes including null terminator) + if(app_name_utf8_len < 64) + { + u_strToUTF8(ctx->imageInfo.Application, 64, NULL, app_name_utf16, app_name_utf16_len, &status); + + if(U_FAILURE(status)) + { + TRACE("Error converting application name to UTF-8: %d, using raw bytes", status); + // Fallback: just copy what we can + memset(ctx->imageInfo.Application, 0, 64); + memcpy(ctx->imageInfo.Application, ctx->header.application, AARU_HEADER_APP_NAME_LEN); + } + } + else + { + TRACE("Application name too long for buffer, truncating"); + u_strToUTF8(ctx->imageInfo.Application, 63, NULL, app_name_utf16, app_name_utf16_len, &status); + ctx->imageInfo.Application[63] = '\0'; + } + } + else + { + TRACE("Error getting UTF-8 length: %d, using raw bytes", status); + // Fallback: just copy what we can + memset(ctx->imageInfo.Application, 0, 64); + memcpy(ctx->imageInfo.Application, ctx->header.application, AARU_HEADER_APP_NAME_LEN); + } + + free(app_name_utf16); } - ctx->imageInfo.Version = (uint8_t *)malloc(32); - if(ctx->imageInfo.Version != NULL) + else { - memset(ctx->imageInfo.Version, 0, 32); - sprintf((char *)ctx->imageInfo.Version, "%d.%d", ctx->header.imageMajorVersion, ctx->header.imageMinorVersion); + TRACE("Could not allocate memory for UTF-16 conversion, using raw bytes"); + // Fallback: just copy what we can + memset(ctx->imageInfo.Application, 0, 64); + memcpy(ctx->imageInfo.Application, ctx->header.application, AARU_HEADER_APP_NAME_LEN); } + + // Set application version string directly in the fixed-size array + memset(ctx->imageInfo.ApplicationVersion, 0, 32); + sprintf(ctx->imageInfo.ApplicationVersion, "%d.%d", ctx->header.applicationMajorVersion, + ctx->header.applicationMinorVersion); + + // Set image version string directly in the fixed-size array + memset(ctx->imageInfo.Version, 0, 32); + sprintf(ctx->imageInfo.Version, "%d.%d", ctx->header.imageMajorVersion, ctx->header.imageMinorVersion); + ctx->imageInfo.MediaType = ctx->header.mediaType; ctx->imageInfo.ImageSize = 0; ctx->imageInfo.CreationTime = ctx->header.creationTime; diff --git a/src/open.c b/src/open.c index fa03860..fcc1969 100644 --- a/src/open.c +++ b/src/open.c @@ -22,6 +22,9 @@ #include #include +#include +#include + #include #include "internal.h" @@ -41,12 +44,6 @@ static void cleanup_open_failure(aaruformatContext *ctx) free(ctx->readableSectorTags); ctx->readableSectorTags = NULL; - free(ctx->imageInfo.ApplicationVersion); - ctx->imageInfo.ApplicationVersion = NULL; - - free(ctx->imageInfo.Version); - ctx->imageInfo.Version = NULL; - free(ctx); } @@ -245,20 +242,75 @@ void *aaruf_open(const char *filepath) // NOLINT(readability-function-size) memset(ctx->readableSectorTags, 0, sizeof(bool) * MaxSectorTag); TRACE("Setting up image info"); - ctx->imageInfo.Application = ctx->header.application; - ctx->imageInfo.ApplicationVersion = (uint8_t *)malloc(32); - if(ctx->imageInfo.ApplicationVersion != NULL) + + // Convert application name from UTF-16LE to UTF-8 using libicu + UErrorCode status = U_ZERO_ERROR; + int32_t app_name_utf16_len = AARU_HEADER_APP_NAME_LEN / 2; // UTF-16LE uses 2 bytes per character + UChar *app_name_utf16 = (UChar *)malloc(app_name_utf16_len * sizeof(UChar)); + + if(app_name_utf16 != NULL) { - memset(ctx->imageInfo.ApplicationVersion, 0, 32); - sprintf((char *)ctx->imageInfo.ApplicationVersion, "%d.%d", ctx->header.applicationMajorVersion, - ctx->header.applicationMinorVersion); + // Convert raw UTF-16LE bytes to UChar (UTF-16, host endian) + for(int32_t j = 0; j < app_name_utf16_len; j++) + { + app_name_utf16[j] = (UChar)(ctx->header.application[j * 2] | (ctx->header.application[j * 2 + 1] << 8)); + } + + // Get required length for UTF-8 + int32_t app_name_utf8_len = 0; + u_strToUTF8(NULL, 0, &app_name_utf8_len, app_name_utf16, app_name_utf16_len, &status); + + if(U_SUCCESS(status) || status == U_BUFFER_OVERFLOW_ERROR) + { + status = U_ZERO_ERROR; + + // Ensure it fits in the Application buffer (64 bytes including null terminator) + if(app_name_utf8_len < 64) + { + u_strToUTF8(ctx->imageInfo.Application, 64, NULL, app_name_utf16, app_name_utf16_len, &status); + + if(U_FAILURE(status)) + { + TRACE("Error converting application name to UTF-8: %d, using raw bytes", status); + // Fallback: just copy what we can + memset(ctx->imageInfo.Application, 0, 64); + strncpy(ctx->imageInfo.Application, (const char *)ctx->header.application, 63); + } + } + else + { + TRACE("Application name too long for buffer, truncating"); + u_strToUTF8(ctx->imageInfo.Application, 63, NULL, app_name_utf16, app_name_utf16_len, &status); + ctx->imageInfo.Application[63] = '\0'; + } + } + else + { + TRACE("Error getting UTF-8 length: %d, using raw bytes", status); + // Fallback: just copy what we can + memset(ctx->imageInfo.Application, 0, 64); + strncpy(ctx->imageInfo.Application, (const char *)ctx->header.application, 63); + } + + free(app_name_utf16); } - ctx->imageInfo.Version = (uint8_t *)malloc(32); - if(ctx->imageInfo.Version != NULL) + else { - memset(ctx->imageInfo.Version, 0, 32); - sprintf((char *)ctx->imageInfo.Version, "%d.%d", ctx->header.imageMajorVersion, ctx->header.imageMinorVersion); + TRACE("Could not allocate memory for UTF-16 conversion, using raw bytes"); + // Fallback: just copy what we can + memset(ctx->imageInfo.Application, 0, 64); + strncpy(ctx->imageInfo.Application, (const char *)ctx->header.application, 63); } + + // Set application version string directly in the fixed-size array + memset(ctx->imageInfo.ApplicationVersion, 0, 32); + sprintf(ctx->imageInfo.ApplicationVersion, "%d.%d", ctx->header.applicationMajorVersion, + ctx->header.applicationMinorVersion); + + // Set image version string directly in the fixed-size array + memset(ctx->imageInfo.Version, 0, 32); + sprintf(ctx->imageInfo.Version, "%d.%d", ctx->header.imageMajorVersion, ctx->header.imageMinorVersion); + ctx->imageInfo.MediaType = ctx->header.mediaType; // Read the index header @@ -474,4 +526,4 @@ void *aaruf_open(const char *filepath) // NOLINT(readability-function-size) TRACE("Exiting aaruf_open() = %p", ctx); return ctx; -} \ No newline at end of file +}