Loading...
Searching...
No Matches
optical.c
Go to the documentation of this file.
/*
* This file is part of the Aaru Data Preservation Suite.
* Copyright (c) 2019-2026 Natalia Portillo.
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of the
* License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "aaruformat.h"
#include "log.h"
void process_tracks_block(aaruformat_context *ctx, const IndexEntry *entry)
{
int pos = 0;
size_t read_bytes = 0;
uint64_t crc64 = 0;
int j = 0, k = 0;
// Check if the context and image stream are valid
if(ctx == NULL || ctx->imageStream == NULL)
{
FATAL("Invalid context or image stream.");
return;
}
// Seek to block
pos = fseek(ctx->imageStream, entry->offset, SEEK_SET);
if(pos < 0 || ftell(ctx->imageStream) != entry->offset)
{
FATAL("Could not seek to %" PRIu64 " as indicated by index entry...\n", entry->offset);
return;
}
// Even if those two checks shall have been done before
read_bytes = fread(&ctx->tracks_header, 1, sizeof(TracksHeader), ctx->imageStream);
if(read_bytes != sizeof(TracksHeader))
{
memset(&ctx->tracks_header, 0, sizeof(TracksHeader));
TRACE("Could not read tracks header, continuing...\n");
return;
}
if(ctx->tracks_header.identifier != TracksBlock)
{
memset(&ctx->tracks_header, 0, sizeof(TracksHeader));
TRACE("Incorrect identifier for data block at position %" PRIu64 "\n", entry->offset);
}
ctx->image_info.ImageSize += sizeof(TrackEntry) * ctx->tracks_header.entries;
ctx->track_entries = (TrackEntry *)malloc(sizeof(TrackEntry) * ctx->tracks_header.entries);
if(ctx->track_entries == NULL)
{
memset(&ctx->tracks_header, 0, sizeof(TracksHeader));
FATAL("Could not allocate memory for metadata block, continuing...\n");
return;
}
read_bytes = fread(ctx->track_entries, sizeof(TrackEntry), ctx->tracks_header.entries, ctx->imageStream);
if(read_bytes != ctx->tracks_header.entries)
{
memset(&ctx->tracks_header, 0, sizeof(TracksHeader));
free(ctx->track_entries);
ctx->track_entries = NULL;
FATAL("Could not read metadata block, continuing...\n");
return;
}
crc64 = aaruf_crc64_data((const uint8_t *)ctx->track_entries, ctx->tracks_header.entries * sizeof(TrackEntry));
// Due to how C# wrote it, it is effectively reversed
if(ctx->header.imageMajorVersion <= AARUF_VERSION_V1) crc64 = bswap_64(crc64);
if(crc64 != ctx->tracks_header.crc64)
{
TRACE("Incorrect CRC found: 0x%" PRIx64 " found, expected 0x%" PRIx64 ", continuing...\n", crc64,
ctx->tracks_header.crc64);
return;
}
TRACE("Found %d tracks at position %" PRIu64 ".\n", ctx->tracks_header.entries, entry->offset);
ctx->image_info.HasPartitions = true;
ctx->image_info.HasSessions = true;
ctx->number_of_data_tracks = 0;
for(j = 0; j < ctx->tracks_header.entries; j++)
{
if(ctx->track_entries[j].sequence > 0 && ctx->track_entries[j].sequence <= 99) ctx->number_of_data_tracks++;
}
if(ctx->number_of_data_tracks > 0)
{
ctx->data_tracks = malloc(sizeof(TrackEntry) * ctx->number_of_data_tracks);
if(ctx->data_tracks == NULL)
{
FATAL("Could not allocate memory for data tracks, continuing without filtered list.\n");
ctx->number_of_data_tracks = 0;
}
}
else
ctx->data_tracks = NULL;
if(ctx->data_tracks != NULL)
{
k = 0;
for(j = 0; j < ctx->tracks_header.entries; j++)
{
if(ctx->track_entries[j].sequence > 0 && ctx->track_entries[j].sequence <= 99)
memcpy(&ctx->data_tracks[k++], &ctx->track_entries[j], sizeof(TrackEntry));
}
}
}
AARU_EXPORT int32_t AARU_CALL aaruf_get_tracks(const void *context, uint8_t *buffer, size_t *length)
{
TRACE("Entering aaruf_get_tracks(%p, %p, %zu)", context, buffer, (length ? *length : 0));
// Check context is correct AaruFormat context
if(context == NULL)
{
FATAL("Invalid context");
TRACE("Exiting aaruf_get_tracks() = AARUF_ERROR_NOT_AARUFORMAT");
return AARUF_ERROR_NOT_AARUFORMAT;
}
const aaruformat_context *ctx = context;
// Not a libaaruformat context
if(ctx->magic != AARU_MAGIC)
{
FATAL("Invalid context");
TRACE("Exiting aaruf_get_tracks() = AARUF_ERROR_NOT_AARUFORMAT");
return AARUF_ERROR_NOT_AARUFORMAT;
}
if(ctx->tracks_header.entries == 0 || ctx->track_entries == NULL)
{
FATAL("Image contains no tracks");
TRACE("Exiting aaruf_get_tracks() = AARUF_ERROR_TRACK_NOT_FOUND");
return AARUF_ERROR_TRACK_NOT_FOUND;
}
size_t required_length = ctx->tracks_header.entries * sizeof(TrackEntry);
if(buffer == NULL || length == NULL || *length < required_length)
{
if(length) *length = required_length;
TRACE("Buffer too small for tracks, required %zu bytes", required_length);
TRACE("Exiting aaruf_get_tracks() = AARUF_ERROR_BUFFER_TOO_SMALL");
return AARUF_ERROR_BUFFER_TOO_SMALL;
}
memcpy(buffer, ctx->track_entries, required_length);
*length = required_length;
TRACE("Exiting aaruf_get_tracks(%p, %p, %zu) = AARUF_STATUS_OK", context, buffer, *length);
return AARUF_STATUS_OK;
}
AARU_EXPORT int32_t AARU_CALL aaruf_set_tracks(void *context, TrackEntry *tracks, const int count)
{
TRACE("Entering aaruf_set_tracks(%p, %p, %d)", context, tracks, count);
// Check context is correct AaruFormat context
if(context == NULL)
{
FATAL("Invalid context");
TRACE("Exiting aaruf_get_tracks() = AARUF_ERROR_NOT_AARUFORMAT");
return AARUF_ERROR_NOT_AARUFORMAT;
}
aaruformat_context *ctx = context;
// Not a libaaruformat context
if(ctx->magic != AARU_MAGIC)
{
FATAL("Invalid context");
TRACE("Exiting aaruf_get_tracks() = AARUF_ERROR_NOT_AARUFORMAT");
return AARUF_ERROR_NOT_AARUFORMAT;
}
// Clearing existing tracks
if(count == 0)
{
memset(&ctx->tracks_header, 0, sizeof(TracksHeader));
free(ctx->track_entries);
ctx->track_entries = NULL;
free(ctx->data_tracks);
ctx->data_tracks = NULL;
ctx->number_of_data_tracks = 0;
TRACE("Exiting aaruf_set_tracks() = AARUF_STATUS_OK");
return AARUF_STATUS_OK;
}
if(tracks == NULL || count < 0)
{
FATAL("Invalid tracks data");
TRACE("Exiting aaruf_set_tracks() = AARUF_ERROR_INVALID_TRACK_FORMAT");
return AARUF_ERROR_INVALID_TRACK_FORMAT;
}
ctx->tracks_header.identifier = TracksBlock;
ctx->tracks_header.entries = (uint16_t)count;
free(ctx->track_entries);
ctx->track_entries = malloc(sizeof(TrackEntry) * count);
if(ctx->track_entries == NULL)
{
memset(&ctx->tracks_header, 0, sizeof(TracksHeader));
FATAL("Could not allocate memory for tracks");
TRACE("Exiting aaruf_set_tracks() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
return AARUF_ERROR_NOT_ENOUGH_MEMORY;
}
memcpy(ctx->track_entries, tracks, sizeof(TrackEntry) * count);
ctx->tracks_header.crc64 = aaruf_crc64_data((const uint8_t *)ctx->track_entries, sizeof(TrackEntry) * count);
ctx->image_info.HasPartitions = true;
ctx->image_info.HasSessions = true;
ctx->dirty_tracks_block = true; // Mark tracks block as dirty
free(ctx->data_tracks);
ctx->data_tracks = NULL;
ctx->number_of_data_tracks = 0;
for(int j = 0; j < ctx->tracks_header.entries; j++)
if(ctx->track_entries[j].sequence > 0 && ctx->track_entries[j].sequence <= 99) ctx->number_of_data_tracks++;
if(ctx->number_of_data_tracks > 0)
{
ctx->data_tracks = malloc(sizeof(TrackEntry) * ctx->number_of_data_tracks);
if(ctx->data_tracks == NULL)
{
free(ctx->track_entries);
ctx->track_entries = NULL;
memset(&ctx->tracks_header, 0, sizeof(TracksHeader));
ctx->number_of_data_tracks = 0;
FATAL("Could not allocate memory for data tracks");
TRACE("Exiting aaruf_set_tracks() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
return AARUF_ERROR_NOT_ENOUGH_MEMORY;
}
}
if(ctx->data_tracks != NULL)
{
int k = 0;
for(int j = 0; j < ctx->tracks_header.entries; j++)
if(ctx->track_entries[j].sequence > 0 && ctx->track_entries[j].sequence <= 99)
memcpy(&ctx->data_tracks[k++], &ctx->track_entries[j], sizeof(TrackEntry));
}
TRACE("Exiting aaruf_set_tracks() = AARUF_STATUS_OK");
return AARUF_STATUS_OK;
}
aaruformat.h
AARU_MAGIC
#define AARU_MAGIC
Magic identifier for AaruFormat container (ASCII "AARUFRMT").
Definition consts.h:64
AARUF_VERSION_V1
#define AARUF_VERSION_V1
First on‑disk version (C# implementation).
Definition consts.h:71
AARU_CALL
#define AARU_CALL
Definition decls.h:46
aaruf_crc64_data
uint64_t aaruf_crc64_data(const uint8_t *data, uint32_t len)
Definition crc64.c:160
AARU_EXPORT
#define AARU_EXPORT
Definition decls.h:55
bswap_64
#define bswap_64(x)
Definition endian.h:81
TracksBlock
@ TracksBlock
Block containing optical disc tracks.
Definition enums.h:154
AARUF_STATUS_OK
#define AARUF_STATUS_OK
Sector present and read without uncorrectable errors.
Definition errors.h:77
AARUF_ERROR_TRACK_NOT_FOUND
#define AARUF_ERROR_TRACK_NOT_FOUND
Referenced track number not present.
Definition errors.h:52
AARUF_ERROR_NOT_ENOUGH_MEMORY
#define AARUF_ERROR_NOT_ENOUGH_MEMORY
Memory allocation failure (critical).
Definition errors.h:48
AARUF_ERROR_INVALID_TRACK_FORMAT
#define AARUF_ERROR_INVALID_TRACK_FORMAT
Track metadata internally inconsistent or malformed.
Definition errors.h:54
AARUF_ERROR_NOT_AARUFORMAT
#define AARUF_ERROR_NOT_AARUFORMAT
Input file/stream failed magic or structural validation.
Definition errors.h:40
AARUF_ERROR_BUFFER_TOO_SMALL
#define AARUF_ERROR_BUFFER_TOO_SMALL
Caller-supplied buffer insufficient for data.
Definition errors.h:49
log.h
FATAL
#define FATAL(fmt,...)
Definition log.h:40
TRACE
#define TRACE(fmt,...)
Definition log.h:25
aaruf_get_tracks
int32_t aaruf_get_tracks(const void *context, uint8_t *buffer, size_t *length)
Retrieve the array of track descriptors contained in an opened AaruFormat image.
Definition optical.c:281
process_tracks_block
void process_tracks_block(aaruformat_context *ctx, const IndexEntry *entry)
Parse and integrate a Tracks block from the image stream into the context.
Definition optical.c:111
aaruf_set_tracks
int32_t aaruf_set_tracks(void *context, TrackEntry *tracks, const int count)
Replace (or clear) the in-memory track table for an AaruFormat image context.
Definition optical.c:392
AaruHeaderV2::imageMajorVersion
uint8_t imageMajorVersion
Container format major version.
Definition header.h:110
ImageInfo::HasPartitions
uint8_t HasPartitions
Image contains partitions (or tracks for optical media); 0=no, non-zero=yes.
Definition aaru.h:933
ImageInfo::HasSessions
uint8_t HasSessions
Image contains multiple sessions (optical media); 0=single/none, non-zero=multi.
Definition aaru.h:934
ImageInfo::ImageSize
uint64_t ImageSize
Size of the image payload in bytes (excludes headers/metadata).
Definition aaru.h:935
IndexEntry
Single index entry describing a block's type, (optional) data classification, and file offset.
Definition index.h:109
IndexEntry::offset
uint64_t offset
Absolute byte offset in the image where the referenced block header begins.
Definition index.h:112
TrackEntry
Single optical disc track descriptor (sequence, type, LBAs, session, ISRC, flags).
Definition optical.h:72
TrackEntry::sequence
uint8_t sequence
Track number (1..99 typical for CD audio/data). 0 may indicate placeholder/non-standard.
Definition optical.h:73
TracksHeader
Header for an optical tracks block listing track entries.
Definition optical.h:62
TracksHeader::identifier
uint32_t identifier
Block identifier (must be BlockType::TracksBlock).
Definition optical.h:63
TracksHeader::entries
uint16_t entries
Number of TrackEntry records following this header.
Definition optical.h:64
TracksHeader::crc64
uint64_t crc64
CRC64-ECMA of the TrackEntry array (header excluded, legacy byte-swap for early versions).
Definition optical.h:65
aaruformat_context
Master context representing an open or in‑creation Aaru image.
Definition context.h:175
aaruformat_context::data_tracks
TrackEntry * data_tracks
Filtered list of data tracks (subset of trackEntries).
Definition context.h:246
aaruformat_context::dirty_tracks_block
bool dirty_tracks_block
True if tracks block should be written during close.
Definition context.h:321
aaruformat_context::header
AaruHeaderV2 header
Parsed container header (v2).
Definition context.h:178
aaruformat_context::magic
uint64_t magic
File magic (AARU_MAGIC) post-open.
Definition context.h:177
aaruformat_context::imageStream
FILE * imageStream
Underlying FILE* stream (binary mode).
Definition context.h:179
aaruformat_context::image_info
ImageInfo image_info
Exposed high-level image info summary.
Definition context.h:263
aaruformat_context::track_entries
TrackEntry * track_entries
Full track list (tracksHeader.entries elements).
Definition context.h:245
aaruformat_context::number_of_data_tracks
uint8_t number_of_data_tracks
Count of tracks considered "data" (sequence 1..99 heuristics).
Definition context.h:248
aaruformat_context::tracks_header
TracksHeader tracks_header
Tracks header (optical) if present.
Definition context.h:247