Loading...
Searching...
No Matches
write.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 <errno.h>
#include <limits.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "aaruformat.h"
#include "internal.h"
#include "log.h"
#include "structs/lisa_tag.h"
#include "xxhash.h"
AARU_EXPORT int32_t AARU_CALL aaruf_write_sector(void *context, uint64_t sector_address, bool negative,
const uint8_t *data, uint8_t sector_status, uint32_t length)
{
TRACE("Entering aaruf_write_sector(%p, %" PRIu64 ", %d, %p, %u, %u)", context, sector_address, negative, data,
sector_status, length);
// Check context is correct AaruFormat context
if(context == NULL)
{
FATAL("Invalid context");
TRACE("Exiting aaruf_write_sector() = 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_write_sector() = AARUF_ERROR_NOT_AARUFORMAT");
return AARUF_ERROR_NOT_AARUFORMAT;
}
// Check we are writing
if(!ctx->is_writing)
{
FATAL("Trying to write a read-only image");
TRACE("Exiting aaruf_write_sector() = AARUF_READ_ONLY");
return AARUF_READ_ONLY;
}
if(negative && sector_address > ctx->user_data_ddt_header.negative)
{
FATAL("Sector address out of bounds");
TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_SECTOR_OUT_OF_BOUNDS");
return AARUF_ERROR_SECTOR_OUT_OF_BOUNDS;
}
if(!negative && sector_address > ctx->image_info.Sectors + ctx->user_data_ddt_header.overflow - 1)
{
FATAL("Sector address out of bounds");
TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_SECTOR_OUT_OF_BOUNDS");
return AARUF_ERROR_SECTOR_OUT_OF_BOUNDS;
}
if(length > USHRT_MAX)
{
FATAL("Sector length too large");
TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_INVALID_SECTOR_LENGTH");
return AARUF_ERROR_INVALID_SECTOR_LENGTH;
}
if(length > ctx->header.biggestSectorSize) ctx->header.biggestSectorSize = (uint16_t)length;
if(!ctx->rewinded)
{
if(sector_address <= ctx->last_written_block)
{
if(sector_address == 0 && !ctx->block_zero_written)
ctx->block_zero_written = true;
else
{
TRACE("Rewinded");
ctx->rewinded = true;
// Disable MD5 calculation
if(ctx->calculating_md5) ctx->calculating_md5 = false;
// Disable SHA1 calculation
if(ctx->calculating_sha1) ctx->calculating_sha1 = false;
// Disable SHA256 calculation
if(ctx->calculating_sha256) ctx->calculating_sha256 = false;
// Disable SpamSum calculation
if(ctx->calculating_spamsum) ctx->calculating_spamsum = false;
// Disable BLAKE3 calculation
if(ctx->calculating_blake3) ctx->calculating_blake3 = false;
}
}
else
ctx->last_written_block = sector_address;
}
// Calculate MD5 on-the-fly if requested and sector is within user sectors (not negative or overflow)
if(ctx->calculating_md5 && !negative && sector_address <= ctx->image_info.Sectors && !ctx->writing_long)
aaruf_md5_update(&ctx->md5_context, data, length);
// Calculate SHA1 on-the-fly if requested and sector is within user sectors (not negative or overflow)
if(ctx->calculating_sha1 && !negative && sector_address <= ctx->image_info.Sectors && !ctx->writing_long)
aaruf_sha1_update(&ctx->sha1_context, data, length);
// Calculate SHA256 on-the-fly if requested and sector is within user sectors (not negative or overflow)
if(ctx->calculating_sha256 && !negative && sector_address <= ctx->image_info.Sectors && !ctx->writing_long)
aaruf_sha256_update(&ctx->sha256_context, data, length);
// Calculate SpamSum on-the-fly if requested and sector is within user sectors (not negative or overflow)
if(ctx->calculating_spamsum && !negative && sector_address <= ctx->image_info.Sectors && !ctx->writing_long)
aaruf_spamsum_update(ctx->spamsum_context, data, length);
// Calculate BLAKE3 on-the-fly if requested and sector is within user sectors (not negative or overflow)
if(ctx->calculating_blake3 && !negative && sector_address <= ctx->image_info.Sectors && !ctx->writing_long)
blake3_hasher_update(ctx->blake3_context, data, length);
// Mark checksum block as dirty if any checksums are being calculated
if((ctx->calculating_md5 || ctx->calculating_sha1 || ctx->calculating_sha256 || ctx->calculating_spamsum ||
ctx->calculating_blake3) &&
!negative && sector_address <= ctx->image_info.Sectors && !ctx->writing_long)
ctx->dirty_checksum_block = true;
// Close current block first
if(ctx->writing_buffer != NULL &&
// When sector size changes or block reaches maximum size
(ctx->current_block_header.sectorSize != length ||
ctx->current_block_offset == 1 << ctx->user_data_ddt_header.dataShift))
{
TRACE("Closing current block before writing new data");
int error = aaruf_close_current_block(ctx);
if(error != AARUF_STATUS_OK)
{
FATAL("Error closing current block: %d", error);
TRACE("Exiting aaruf_write_sector() = %d", error);
return error;
}
}
uint64_t ddt_entry = 0;
bool ddt_ok;
if(ctx->deduplicate)
{
// Calculate 64-bit XXH3 hash of the sector
TRACE("Hashing sector data for deduplication");
uint64_t hash = XXH3_64bits(data, length);
// Check if the hash is already in the map
bool existing = lookup_map(ctx->sector_hash_map, hash, &ddt_entry);
TRACE("Block does %s exist in deduplication map", existing ? "already" : "not yet");
ddt_ok = set_ddt_entry_v2(ctx, sector_address, negative, ctx->current_block_offset, ctx->next_block_position,
sector_status, &ddt_entry);
if(!ddt_ok)
{
TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_CANNOT_SET_DDT_ENTRY");
return AARUF_ERROR_CANNOT_SET_DDT_ENTRY;
}
if(existing)
{
TRACE("Sector exists, so not writing to image");
TRACE("Exiting aaruf_write_sector() = AARUF_STATUS_OK");
return AARUF_STATUS_OK;
}
TRACE("Inserting sector hash into deduplication map, proceeding to write into image as normal");
insert_map(ctx->sector_hash_map, hash, ddt_entry);
}
else
ddt_ok = set_ddt_entry_v2(ctx, sector_address, negative, ctx->current_block_offset, ctx->next_block_position,
sector_status, &ddt_entry);
if(!ddt_ok)
{
TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_CANNOT_SET_DDT_ENTRY");
return AARUF_ERROR_CANNOT_SET_DDT_ENTRY;
}
// No block set
if(ctx->writing_buffer_position == 0)
{
TRACE("Creating new writing block");
ctx->current_block_header.identifier = DataBlock;
ctx->current_block_header.type = UserData;
ctx->current_block_header.sectorSize = length;
// We need to save the track type for later compression
if(ctx->image_info.MetadataMediaType == OpticalDisc && ctx->track_entries != NULL)
{
const TrackEntry *track = NULL;
for(int i = 0; i < ctx->tracks_header.entries; i++)
if(sector_address >= ctx->track_entries[i].start && sector_address <= ctx->track_entries[i].end)
{
track = &ctx->track_entries[i];
break;
}
if(track != NULL)
{
ctx->current_track_type = track->type;
if(track->sequence == 0 && track->start == 0 && track->end == 0) ctx->current_track_type = Data;
}
else
ctx->current_track_type = Data;
if(ctx->current_track_type == Audio &&
// JaguarCD stores data in audio tracks. FLAC is too inefficient, we need to use LZMA as data.
(ctx->image_info.MediaType == JaguarCD && track->session > 1 ||
// VideoNow stores video in audio tracks, and LZMA works better too.
ctx->image_info.MediaType == VideoNow || ctx->image_info.MediaType == VideoNowColor ||
ctx->image_info.MediaType == VideoNowXp))
ctx->current_track_type = Data;
if(ctx->compression_enabled)
{
if(ctx->current_track_type == Audio)
ctx->current_block_header.compression = Flac;
else
ctx->current_block_header.compression = Lzma;
}
else
ctx->current_block_header.compression = None;
}
else
{
ctx->current_track_type = Data;
if(ctx->compression_enabled)
ctx->current_block_header.compression = Lzma;
else
ctx->current_block_header.compression = None;
}
uint32_t max_buffer_size =
(1 << ctx->user_data_ddt_header.dataShift) * ctx->current_block_header.sectorSize * 2;
TRACE("Setting max buffer size to %u bytes", max_buffer_size);
TRACE("Allocating memory for writing buffer");
ctx->writing_buffer = (uint8_t *)calloc(1, max_buffer_size);
if(ctx->writing_buffer == NULL)
{
FATAL("Could not allocate memory");
TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
return AARUF_ERROR_NOT_ENOUGH_MEMORY;
}
}
TRACE("Copying data to writing buffer at position %zu", ctx->writing_buffer_position);
memcpy(ctx->writing_buffer + ctx->writing_buffer_position, data, length);
TRACE("Advancing writing buffer position to %zu", ctx->writing_buffer_position + length);
ctx->writing_buffer_position += length;
TRACE("Advancing current block offset to %zu", ctx->current_block_offset + 1);
ctx->current_block_offset++;
TRACE("Exiting aaruf_write_sector() = AARUF_STATUS_OK");
return AARUF_STATUS_OK;
}
AARU_EXPORT int32_t AARU_CALL aaruf_write_sector_long(void *context, uint64_t sector_address, bool negative,
const uint8_t *data, uint8_t sector_status, uint32_t length)
{
TRACE("Entering aaruf_write_sector_long(%p, %" PRIu64 ", %d, %p, %u, %u)", context, sector_address, negative, data,
sector_status, length);
// Check context is correct AaruFormat context
if(context == NULL)
{
FATAL("Invalid context");
TRACE("Exiting aaruf_write_sector() = 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_write_sector() = AARUF_ERROR_NOT_AARUFORMAT");
return AARUF_ERROR_NOT_AARUFORMAT;
}
// Check we are writing
if(!ctx->is_writing)
{
FATAL("Trying to write a read-only image");
TRACE("Exiting aaruf_write_sector() = AARUF_READ_ONLY");
return AARUF_READ_ONLY;
}
if(negative && sector_address > ctx->user_data_ddt_header.negative)
{
FATAL("Sector address out of bounds");
TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_SECTOR_OUT_OF_BOUNDS");
return AARUF_ERROR_SECTOR_OUT_OF_BOUNDS;
}
if(!negative && sector_address > ctx->image_info.Sectors + ctx->user_data_ddt_header.overflow - 1)
{
FATAL("Sector address out of bounds");
TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_SECTOR_OUT_OF_BOUNDS");
return AARUF_ERROR_SECTOR_OUT_OF_BOUNDS;
}
switch(ctx->image_info.MetadataMediaType)
{
case OpticalDisc:
{
TrackEntry track = {0};
for(int i = 0; i < ctx->tracks_header.entries; i++)
if(sector_address >= ctx->track_entries[i].start && sector_address <= ctx->track_entries[i].end)
{
track = ctx->track_entries[i];
break;
}
if(track.sequence == 0 && track.start == 0 && track.end == 0) track.type = Data;
uint64_t corrected_sector_address = sector_address;
// Calculate positive or negative sector
if(negative)
corrected_sector_address = ctx->user_data_ddt_header.negative - sector_address;
else
corrected_sector_address += ctx->user_data_ddt_header.negative;
uint64_t total_sectors =
ctx->user_data_ddt_header.negative + ctx->image_info.Sectors + ctx->user_data_ddt_header.overflow;
// DVD long sector
if(length == 2064 && (ctx->image_info.MediaType == DVDROM || ctx->image_info.MediaType == PS2DVD ||
ctx->image_info.MediaType == SACD || ctx->image_info.MediaType == PS3DVD ||
ctx->image_info.MediaType == DVDR || ctx->image_info.MediaType == DVDRW ||
ctx->image_info.MediaType == DVDPR || ctx->image_info.MediaType == DVDPRW ||
ctx->image_info.MediaType == DVDPRWDL || ctx->image_info.MediaType == DVDRDL ||
ctx->image_info.MediaType == DVDPRDL || ctx->image_info.MediaType == DVDRAM ||
ctx->image_info.MediaType == DVDRWDL || ctx->image_info.MediaType == DVDDownload ||
ctx->image_info.MediaType == Nuon))
{
if(ctx->sector_id == NULL) ctx->sector_id = calloc(1, 4 * total_sectors);
if(ctx->sector_ied == NULL) ctx->sector_ied = calloc(1, 2 * total_sectors);
if(ctx->sector_cpr_mai == NULL) ctx->sector_cpr_mai = calloc(1, 6 * total_sectors);
if(ctx->sector_edc == NULL) ctx->sector_edc = calloc(1, 4 * total_sectors);
memcpy(ctx->sector_id + corrected_sector_address * 4, data, 4);
memcpy(ctx->sector_ied + corrected_sector_address * 2, data + 4, 2);
memcpy(ctx->sector_cpr_mai + corrected_sector_address * 6, data + 6, 6);
memcpy(ctx->sector_edc + corrected_sector_address * 4, data + 2060, 4);
return aaruf_write_sector(context, sector_address, negative, data + 12, sector_status, 2048);
}
if(length != 2352)
{
FATAL("Incorrect sector size");
TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_INCORRECT_DATA_SIZE");
return AARUF_ERROR_INCORRECT_DATA_SIZE;
}
ctx->writing_long = true;
if(!ctx->rewinded)
{
if(sector_address <= ctx->last_written_block)
{
if(sector_address == 0 && !ctx->block_zero_written)
ctx->block_zero_written = true;
else
{
TRACE("Rewinded");
ctx->rewinded = true;
// Disable MD5 calculation
if(ctx->calculating_md5) ctx->calculating_md5 = false;
// Disable SHA1 calculation
if(ctx->calculating_sha1) ctx->calculating_sha1 = false;
// Disable SHA256 calculation
if(ctx->calculating_sha256) ctx->calculating_sha256 = false;
// Disable SpamSum calculation
if(ctx->calculating_spamsum) ctx->calculating_spamsum = false;
// Disable BLAKE3 calculation
if(ctx->calculating_blake3) ctx->calculating_blake3 = false;
}
}
else
ctx->last_written_block = sector_address;
}
// Calculate MD5 on-the-fly if requested and sector is within user sectors (not negative or overflow)
if(ctx->calculating_md5 && !negative && sector_address <= ctx->image_info.Sectors)
aaruf_md5_update(&ctx->md5_context, data, length);
// Calculate SHA1 on-the-fly if requested and sector is within user sectors (not negative or overflow)
if(ctx->calculating_sha1 && !negative && sector_address <= ctx->image_info.Sectors)
aaruf_sha1_update(&ctx->sha1_context, data, length);
// Calculate SHA256 on-the-fly if requested and sector is within user sectors (not negative or overflow)
if(ctx->calculating_sha256 && !negative && sector_address <= ctx->image_info.Sectors)
aaruf_sha256_update(&ctx->sha256_context, data, length);
// Calculate SpamSum on-the-fly if requested and sector is within user sectors (not negative or overflow)
if(ctx->calculating_spamsum && !negative && sector_address <= ctx->image_info.Sectors)
aaruf_spamsum_update(ctx->spamsum_context, data, length);
// Calculate BLAKE3 on-the-fly if requested and sector is within user sectors (not negative or overflow)
if(ctx->calculating_blake3 && !negative && sector_address <= ctx->image_info.Sectors)
blake3_hasher_update(ctx->blake3_context, data, length);
bool prefix_correct;
// Split raw cd sector data in prefix (sync, header), user data and suffix (edc, ecc p, ecc q)
switch(track.type)
{
case Audio:
case Data:
return aaruf_write_sector(context, sector_address, negative, data, sector_status, length);
case CdMode1:
// If we do not have a DDT V2 for sector prefix, create one
if(ctx->sector_prefix_ddt2 == NULL)
{
ctx->sector_prefix_ddt2 =
calloc(1, sizeof(uint64_t) * (ctx->user_data_ddt_header.negative + ctx->image_info.Sectors +
ctx->user_data_ddt_header.overflow));
if(ctx->sector_prefix_ddt2 == NULL)
{
FATAL("Could not allocate memory for CD sector prefix DDT");
TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
return AARUF_ERROR_NOT_ENOUGH_MEMORY;
}
}
// If we do not have a DDT V2 for sector suffix, create one
if(ctx->sector_suffix_ddt2 == NULL)
{
ctx->sector_suffix_ddt2 =
calloc(1, sizeof(uint64_t) * (ctx->user_data_ddt_header.negative + ctx->image_info.Sectors +
ctx->user_data_ddt_header.overflow));
if(ctx->sector_suffix_ddt2 == NULL)
{
FATAL("Could not allocate memory for CD sector prefix DDT");
TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
return AARUF_ERROR_NOT_ENOUGH_MEMORY;
}
}
if(ctx->sector_prefix == NULL)
{
ctx->sector_prefix_length = 16 * (ctx->user_data_ddt_header.negative + ctx->image_info.Sectors +
ctx->user_data_ddt_header.overflow);
ctx->sector_prefix = malloc(ctx->sector_prefix_length);
if(ctx->sector_prefix == NULL)
{
FATAL("Could not allocate memory for CD sector prefix buffer");
TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
return AARUF_ERROR_NOT_ENOUGH_MEMORY;
}
}
if(ctx->sector_suffix == NULL)
{
ctx->sector_suffix_length =
288 * (ctx->user_data_ddt_header.negative + ctx->image_info.Sectors +
ctx->user_data_ddt_header.overflow);
ctx->sector_suffix = malloc(ctx->sector_suffix_length);
if(ctx->sector_suffix == NULL)
{
FATAL("Could not allocate memory for CD sector suffix buffer");
TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
return AARUF_ERROR_NOT_ENOUGH_MEMORY;
}
}
bool empty = true;
for(int i = 0; i < length; i++)
if(data[i] != 0)
{
empty = false;
break;
}
if(empty)
{
ctx->sector_prefix_ddt2[corrected_sector_address] = SectorStatusNotDumped;
ctx->sector_suffix_ddt2[corrected_sector_address] = SectorStatusNotDumped;
ctx->dirty_sector_prefix_ddt = true; // Mark prefix DDT as dirty
ctx->dirty_sector_suffix_ddt = true; // Mark suffix DDT as dirty
return aaruf_write_sector(context, sector_address, negative, data + 16, SectorStatusNotDumped,
2048);
}
prefix_correct = true;
if(data[0x00] != 0x00 || data[0x01] != 0xFF || data[0x02] != 0xFF || data[0x03] != 0xFF ||
data[0x04] != 0xFF || data[0x05] != 0xFF || data[0x06] != 0xFF || data[0x07] != 0xFF ||
data[0x08] != 0xFF || data[0x09] != 0xFF || data[0x0A] != 0xFF || data[0x0B] != 0x00 ||
data[0x0F] != 0x01)
prefix_correct = false;
if(prefix_correct)
{
const int minute = (data[0x0C] >> 4) * 10 + (data[0x0C] & 0x0F);
const int second = (data[0x0D] >> 4) * 10 + (data[0x0D] & 0x0F);
const int frame = (data[0x0E] >> 4) * 10 + (data[0x0E] & 0x0F);
const int stored_lba = minute * 60 * 75 + second * 75 + frame - 150;
prefix_correct = stored_lba == sector_address;
}
if(prefix_correct)
ctx->sector_prefix_ddt2[corrected_sector_address] = (uint64_t)SectorStatusMode1Correct << 60;
else
{
// Copy CD prefix from data buffer to prefix buffer
memcpy(ctx->sector_prefix + ctx->sector_prefix_offset, data, 16);
ctx->sector_prefix_ddt2[corrected_sector_address] = (uint64_t)(ctx->sector_prefix_offset / 16);
ctx->sector_prefix_ddt2[corrected_sector_address] |= (uint64_t)SectorStatusErrored << 60;
ctx->sector_prefix_offset += 16;
ctx->dirty_sector_prefix_block = true; // Mark prefix block as dirty
// Grow prefix buffer if needed
if(ctx->sector_prefix_offset >= ctx->sector_prefix_length)
{
ctx->sector_prefix_length *= 2;
ctx->sector_prefix = realloc(ctx->sector_prefix, ctx->sector_prefix_length);
if(ctx->sector_prefix == NULL)
{
FATAL("Could not allocate memory for CD sector prefix buffer");
TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
return AARUF_ERROR_NOT_ENOUGH_MEMORY;
}
}
}
ctx->dirty_sector_prefix_ddt = true; // Mark prefix DDT as dirty
const bool suffix_correct = aaruf_ecc_cd_is_suffix_correct(ctx->ecc_cd_context, data);
if(suffix_correct)
ctx->sector_suffix_ddt2[corrected_sector_address] = (uint64_t)SectorStatusMode1Correct << 60;
else
{
// Copy CD suffix from data buffer to suffix buffer
memcpy(ctx->sector_suffix + ctx->sector_suffix_offset, data + 2064, 288);
ctx->sector_suffix_ddt2[corrected_sector_address] = (uint64_t)(ctx->sector_suffix_offset / 288);
ctx->sector_suffix_ddt2[corrected_sector_address] |= (uint64_t)SectorStatusErrored << 60;
ctx->sector_suffix_offset += 288;
ctx->dirty_sector_suffix_block = true; // Mark suffix block as dirty
// Grow suffix buffer if needed
if(ctx->sector_suffix_offset >= ctx->sector_suffix_length)
{
ctx->sector_suffix_length *= 2;
ctx->sector_suffix = realloc(ctx->sector_suffix, ctx->sector_suffix_length);
if(ctx->sector_suffix == NULL)
{
FATAL("Could not allocate memory for CD sector suffix buffer");
TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
return AARUF_ERROR_NOT_ENOUGH_MEMORY;
}
}
}
ctx->dirty_sector_suffix_ddt = true; // Mark suffix DDT as dirty
return aaruf_write_sector(context, sector_address, negative, data + 16, SectorStatusMode1Correct,
2048);
case CdMode2Form1:
case CdMode2Form2:
case CdMode2Formless:
// If we do not have a DDT V2 for sector prefix, create one
if(ctx->sector_prefix_ddt2 == NULL)
{
ctx->sector_prefix_ddt2 =
calloc(1, sizeof(uint64_t) * (ctx->user_data_ddt_header.negative + ctx->image_info.Sectors +
ctx->user_data_ddt_header.overflow));
if(ctx->sector_prefix_ddt2 == NULL)
{
FATAL("Could not allocate memory for CD sector prefix DDT");
TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
return AARUF_ERROR_NOT_ENOUGH_MEMORY;
}
}
// If we do not have a DDT V2 for sector suffix, create one
if(ctx->sector_suffix_ddt2 == NULL)
{
ctx->sector_suffix_ddt2 =
calloc(1, sizeof(uint64_t) * (ctx->user_data_ddt_header.negative + ctx->image_info.Sectors +
ctx->user_data_ddt_header.overflow));
if(ctx->sector_suffix_ddt2 == NULL)
{
FATAL("Could not allocate memory for CD sector prefix DDT");
TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
return AARUF_ERROR_NOT_ENOUGH_MEMORY;
}
}
if(ctx->sector_prefix == NULL)
{
ctx->sector_prefix_length = 16 * (ctx->user_data_ddt_header.negative + ctx->image_info.Sectors +
ctx->user_data_ddt_header.overflow);
ctx->sector_prefix = malloc(ctx->sector_prefix_length);
if(ctx->sector_prefix == NULL)
{
FATAL("Could not allocate memory for CD sector prefix buffer");
TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
return AARUF_ERROR_NOT_ENOUGH_MEMORY;
}
}
if(ctx->sector_suffix == NULL)
{
ctx->sector_suffix_length =
288 * (ctx->user_data_ddt_header.negative + ctx->image_info.Sectors +
ctx->user_data_ddt_header.overflow);
ctx->sector_suffix = malloc(ctx->sector_suffix_length);
if(ctx->sector_suffix == NULL)
{
FATAL("Could not allocate memory for CD sector suffix buffer");
TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
return AARUF_ERROR_NOT_ENOUGH_MEMORY;
}
}
empty = true;
for(int i = 0; i < length; i++)
if(data[i] != 0)
{
empty = false;
break;
}
if(empty)
{
ctx->sector_prefix_ddt2[corrected_sector_address] = SectorStatusNotDumped;
ctx->sector_suffix_ddt2[corrected_sector_address] = SectorStatusNotDumped;
return aaruf_write_sector(context, sector_address, negative, data + 16, SectorStatusNotDumped,
2328);
}
const bool form2 = (data[18] & 0x20) == 0x20 || (data[22] & 0x20) == 0x20;
prefix_correct = true;
if(data[0x00] != 0x00 || data[0x01] != 0xFF || data[0x02] != 0xFF || data[0x03] != 0xFF ||
data[0x04] != 0xFF || data[0x05] != 0xFF || data[0x06] != 0xFF || data[0x07] != 0xFF ||
data[0x08] != 0xFF || data[0x09] != 0xFF || data[0x0A] != 0xFF || data[0x0B] != 0x00 ||
data[0x0F] != 0x02)
prefix_correct = false;
if(prefix_correct)
{
const int minute = (data[0x0C] >> 4) * 10 + (data[0x0C] & 0x0F);
const int second = (data[0x0D] >> 4) * 10 + (data[0x0D] & 0x0F);
const int frame = (data[0x0E] >> 4) * 10 + (data[0x0E] & 0x0F);
const int stored_lba = minute * 60 * 75 + second * 75 + frame - 150;
prefix_correct = stored_lba == sector_address;
}
if(prefix_correct)
ctx->sector_prefix_ddt2[corrected_sector_address] =
(uint64_t)(form2 ? SectorStatusMode2Form2Ok : SectorStatusMode2Form1Ok) << 60;
else
{
// Copy CD prefix from data buffer to prefix buffer
memcpy(ctx->sector_prefix + ctx->sector_prefix_offset, data, 16);
ctx->sector_prefix_ddt2[corrected_sector_address] = (uint32_t)(ctx->sector_prefix_offset / 16);
ctx->sector_prefix_ddt2[corrected_sector_address] |= (uint64_t)SectorStatusErrored << 60;
ctx->sector_prefix_offset += 16;
ctx->dirty_sector_prefix_block = true; // Mark prefix block as dirty
// Grow prefix buffer if needed
if(ctx->sector_prefix_offset >= ctx->sector_prefix_length)
{
ctx->sector_prefix_length *= 2;
ctx->sector_prefix = realloc(ctx->sector_prefix, ctx->sector_prefix_length);
if(ctx->sector_prefix == NULL)
{
FATAL("Could not allocate memory for CD sector prefix buffer");
TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
return AARUF_ERROR_NOT_ENOUGH_MEMORY;
}
}
}
ctx->dirty_sector_prefix_ddt = true; // Mark prefix DDT as dirty
if(ctx->mode2_subheaders == NULL)
{
ctx->mode2_subheaders =
calloc(1, 8 * (ctx->user_data_ddt_header.negative + ctx->image_info.Sectors +
ctx->user_data_ddt_header.overflow));
if(ctx->mode2_subheaders == NULL)
{
FATAL("Could not allocate memory for CD mode 2 subheader buffer");
TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
return AARUF_ERROR_NOT_ENOUGH_MEMORY;
}
}
if(form2)
{
const uint32_t computed_edc = aaruf_edc_cd_compute(ctx->ecc_cd_context, 0, data, 0x91C, 0x10);
uint32_t edc = 0;
memcpy(&edc, data + 0x92C, sizeof(edc));
const bool correct_edc = computed_edc == edc;
if(correct_edc)
ctx->sector_suffix_ddt2[corrected_sector_address] = (uint64_t)SectorStatusMode2Form2Ok
<< 60;
else if(edc == 0)
ctx->sector_suffix_ddt2[corrected_sector_address] = (uint64_t)SectorStatusMode2Form2NoCrc
<< 60;
else
{
// Copy CD suffix from data buffer to suffix buffer
memcpy(ctx->sector_suffix + ctx->sector_suffix_offset, data + 2348, 4);
ctx->sector_suffix_ddt2[corrected_sector_address] =
(uint64_t)(ctx->sector_suffix_offset / 288);
ctx->sector_suffix_ddt2[corrected_sector_address] |= (uint64_t)SectorStatusErrored << 60;
ctx->sector_suffix_offset += 288;
ctx->dirty_sector_suffix_block = true; // Mark suffix block as dirty
// Grow suffix buffer if needed
if(ctx->sector_suffix_offset >= ctx->sector_suffix_length)
{
ctx->sector_suffix_length *= 2;
ctx->sector_suffix = realloc(ctx->sector_suffix, ctx->sector_suffix_length);
if(ctx->sector_suffix == NULL)
{
FATAL("Could not allocate memory for CD sector suffix buffer");
TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
return AARUF_ERROR_NOT_ENOUGH_MEMORY;
}
}
}
// Copy subheader from data buffer to subheader buffer
memcpy(ctx->mode2_subheaders + corrected_sector_address * 8, data + 0x10, 8);
ctx->dirty_sector_prefix_ddt = true;
ctx->dirty_sector_suffix_ddt = true;
ctx->dirty_mode2_subheaders_block = true;
return aaruf_write_sector(context, sector_address, negative, data + 24,
edc == 0 ? SectorStatusMode2Form2NoCrc
: correct_edc ? SectorStatusMode2Form2Ok
: SectorStatusErrored,
2324);
}
const bool correct_ecc = aaruf_ecc_cd_is_suffix_correct_mode2(ctx->ecc_cd_context, data);
const uint32_t computed_edc = aaruf_edc_cd_compute(ctx->ecc_cd_context, 0, data, 0x808, 0x10);
uint32_t edc = 0;
memcpy(&edc, data + 0x818, sizeof(edc));
const bool correct_edc = computed_edc == edc;
if(correct_ecc && correct_edc)
ctx->sector_suffix_ddt2[corrected_sector_address] = (uint64_t)SectorStatusMode2Form1Ok << 60;
else
{
// Copy CD suffix from data buffer to suffix buffer
memcpy(ctx->sector_suffix + ctx->sector_suffix_offset, data + 2072, 280);
ctx->sector_suffix_ddt2[corrected_sector_address] = (uint64_t)(ctx->sector_suffix_offset / 288);
ctx->sector_suffix_ddt2[corrected_sector_address] |= (uint64_t)SectorStatusErrored << 60;
ctx->sector_suffix_offset += 288;
ctx->dirty_sector_suffix_block = true; // Mark suffix block as dirty
// Grow suffix buffer if needed
if(ctx->sector_suffix_offset >= ctx->sector_suffix_length)
{
ctx->sector_suffix_length *= 2;
ctx->sector_suffix = realloc(ctx->sector_suffix, ctx->sector_suffix_length);
if(ctx->sector_suffix == NULL)
{
FATAL("Could not allocate memory for CD sector suffix buffer");
TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
return AARUF_ERROR_NOT_ENOUGH_MEMORY;
}
}
}
// Copy subheader from data buffer to subheader buffer
memcpy(ctx->mode2_subheaders + corrected_sector_address * 8, data + 0x10, 8);
ctx->dirty_sector_prefix_ddt = true;
ctx->dirty_sector_suffix_ddt = true;
ctx->dirty_mode2_subheaders_block = true;
return aaruf_write_sector(
context, sector_address, negative, data + 24,
correct_edc && correct_ecc ? SectorStatusMode2Form1Ok : SectorStatusErrored, 2048);
}
break;
}
case BlockMedia:
switch(ctx->image_info.MediaType)
{
case AppleFileWare:
case AppleProfile:
case AppleSonyDS:
case AppleSonySS:
case AppleWidget:
case PriamDataTower:
{
uint8_t *newTag;
int newTagSize = 0;
switch(length - 512)
{
// Sony tag
case 12:
{
const sony_tag decoded_sony_tag = bytes_to_sony_tag(data + 512);
if(ctx->image_info.MediaType == AppleProfile || ctx->image_info.MediaType == AppleFileWare)
{
const profile_tag decoded_profile_tag = sony_tag_to_profile(decoded_sony_tag);
newTag = profile_tag_to_bytes(decoded_profile_tag);
newTagSize = 20;
}
else if(ctx->image_info.MediaType == PriamDataTower)
{
const priam_tag decoded_priam_tag = sony_tag_to_priam(decoded_sony_tag);
newTag = priam_tag_to_bytes(decoded_priam_tag);
newTagSize = 24;
}
else if(ctx->image_info.MediaType == AppleSonyDS ||
ctx->image_info.MediaType == AppleSonySS)
{
newTag = malloc(12);
memcpy(newTag, data + 512, 12);
newTagSize = 12;
}
break;
}
// Profile tag
case 20:
{
const profile_tag decoded_profile_tag = bytes_to_profile_tag(data + 512);
if(ctx->image_info.MediaType == AppleProfile || ctx->image_info.MediaType == AppleFileWare)
{
newTag = malloc(20);
memcpy(newTag, data + 512, 20);
newTagSize = 20;
}
else if(ctx->image_info.MediaType == PriamDataTower)
{
const priam_tag decoded_priam_tag = profile_tag_to_priam(decoded_profile_tag);
newTag = priam_tag_to_bytes(decoded_priam_tag);
newTagSize = 24;
}
else if(ctx->image_info.MediaType == AppleSonyDS ||
ctx->image_info.MediaType == AppleSonySS)
{
const sony_tag decoded_sony_tag = profile_tag_to_sony(decoded_profile_tag);
newTag = sony_tag_to_bytes(decoded_sony_tag);
newTagSize = 12;
}
break;
}
// Priam tag
case 24:
{
const priam_tag decoded_priam_tag = bytes_to_priam_tag(data + 512);
if(ctx->image_info.MediaType == AppleProfile || ctx->image_info.MediaType == AppleFileWare)
{
const profile_tag decoded_profile_tag = priam_tag_to_profile(decoded_priam_tag);
newTag = profile_tag_to_bytes(decoded_profile_tag);
newTagSize = 20;
}
else if(ctx->image_info.MediaType == PriamDataTower)
{
newTag = malloc(24);
memcpy(newTag, data + 512, 24);
newTagSize = 24;
}
else if(ctx->image_info.MediaType == AppleSonyDS ||
ctx->image_info.MediaType == AppleSonySS)
{
const sony_tag decoded_sony_tag = priam_tag_to_sony(decoded_priam_tag);
newTag = sony_tag_to_bytes(decoded_sony_tag);
newTagSize = 12;
}
break;
}
case 0:
newTagSize = 0;
break;
default:
FATAL("Incorrect sector size");
TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_INCORRECT_DATA_SIZE");
return AARUF_ERROR_INCORRECT_DATA_SIZE;
}
if(newTagSize == 0)
return aaruf_write_sector(context, sector_address, negative, data, sector_status, 512);
if(ctx->sector_subchannel == NULL)
{
ctx->sector_subchannel =
calloc(1, newTagSize * (ctx->image_info.Sectors + ctx->user_data_ddt_header.overflow));
if(ctx->sector_subchannel == NULL)
{
FATAL("Could not allocate memory for sector subchannel DDT");
free(newTag);
TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
return AARUF_ERROR_NOT_ENOUGH_MEMORY;
}
}
memcpy(ctx->sector_subchannel + sector_address * newTagSize, newTag, newTagSize);
ctx->dirty_sector_subchannel_block = true; // Mark subchannel block as dirty
free(newTag);
return aaruf_write_sector(context, sector_address, negative, data, sector_status, 512);
}
default:
return AARUF_ERROR_INCORRECT_MEDIA_TYPE;
}
default:
TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_INCORRECT_MEDIA_TYPE");
return AARUF_ERROR_INCORRECT_MEDIA_TYPE;
}
// Fallback return when media type branch does not produce a value (satisfy non-void contract)
return AARUF_ERROR_INCORRECT_MEDIA_TYPE;
}
int32_t aaruf_close_current_block(aaruformat_context *ctx)
{
// Not a libaaruformat context
if(ctx->magic != AARU_MAGIC) return AARUF_ERROR_NOT_AARUFORMAT;
// Check we are writing
if(!ctx->is_writing) return AARUF_READ_ONLY;
ctx->current_block_header.length = ctx->current_block_offset * ctx->current_block_header.sectorSize;
TRACE("Initializing CRC64 context");
ctx->crc64_context = aaruf_crc64_init();
TRACE("Updating CRC64");
aaruf_crc64_update(ctx->crc64_context, ctx->writing_buffer, ctx->current_block_header.length);
aaruf_crc64_final(ctx->crc64_context, &ctx->current_block_header.crc64);
uint8_t lzma_properties[LZMA_PROPERTIES_LENGTH] = {0};
uint8_t *cmp_buffer = NULL;
switch(ctx->current_block_header.compression)
{
case None:
break;
case Flac:
cmp_buffer = malloc(ctx->current_block_header.length * 2);
if(cmp_buffer == NULL)
{
FATAL("Could not allocate buffer for compressed data");
return AARUF_ERROR_NOT_ENOUGH_MEMORY;
}
const uint32_t current_samples = ctx->current_block_offset * SAMPLES_PER_SECTOR;
uint32_t flac_block_size = ctx->current_block_offset * SAMPLES_PER_SECTOR;
if(flac_block_size > MAX_FLAKE_BLOCK) flac_block_size = MAX_FLAKE_BLOCK;
if(flac_block_size < MIN_FLAKE_BLOCK) flac_block_size = MIN_FLAKE_BLOCK;
const long remaining = current_samples % flac_block_size;
// Fill FLAC block
if(remaining != 0)
for(int r = 0; r < remaining * 4; r++) ctx->writing_buffer[ctx->writing_buffer_position + r] = 0;
ctx->current_block_header.cmpLength = aaruf_flac_encode_redbook_buffer(
cmp_buffer, ctx->current_block_header.length * 2, ctx->writing_buffer, ctx->current_block_header.length,
flac_block_size, true, false, "hamming", 12, 15, true, false, 0, 8, "Aaru", 4);
if(ctx->current_block_header.cmpLength >= ctx->current_block_header.length)
{
ctx->current_block_header.compression = None;
free(cmp_buffer);
}
break;
case Lzma:
cmp_buffer = malloc(ctx->current_block_header.length * 2);
if(cmp_buffer == NULL)
{
FATAL("Could not allocate buffer for compressed data");
return AARUF_ERROR_NOT_ENOUGH_MEMORY;
}
size_t dst_size = ctx->current_block_header.length * 2;
size_t props_size = LZMA_PROPERTIES_LENGTH;
aaruf_lzma_encode_buffer(cmp_buffer, &dst_size, ctx->writing_buffer, ctx->current_block_header.length,
lzma_properties, &props_size, 9, ctx->lzma_dict_size, 4, 0, 2, 273, 8);
ctx->current_block_header.cmpLength = (uint32_t)dst_size;
if(ctx->current_block_header.cmpLength >= ctx->current_block_header.length)
{
ctx->current_block_header.compression = None;
free(cmp_buffer);
}
break;
default:
FATAL("Invalid compression type");
return AARUF_ERROR_CANNOT_WRITE_BLOCK_DATA;
}
if(ctx->current_block_header.compression == None)
{
ctx->current_block_header.cmpCrc64 = ctx->current_block_header.crc64;
ctx->current_block_header.cmpLength = ctx->current_block_header.length;
}
else
ctx->current_block_header.cmpCrc64 = aaruf_crc64_data(cmp_buffer, ctx->current_block_header.cmpLength);
if(ctx->current_block_header.compression == Lzma) ctx->current_block_header.cmpLength += LZMA_PROPERTIES_LENGTH;
// Add to index
TRACE("Adding block to index");
IndexEntry index_entry;
index_entry.blockType = DataBlock;
index_entry.dataType = UserData;
index_entry.offset = ctx->next_block_position;
utarray_push_back(ctx->index_entries, &index_entry);
ctx->dirty_index_block = true; // Mark index block as dirty
TRACE("Block added to index at offset %" PRIu64, index_entry.offset);
// Write block header to file
// Move to expected block position
fseek(ctx->imageStream, ctx->next_block_position, SEEK_SET);
// Write block header
if(fwrite(&ctx->current_block_header, sizeof(BlockHeader), 1, ctx->imageStream) != 1)
return AARUF_ERROR_CANNOT_WRITE_BLOCK_HEADER;
// Write block data
if(ctx->current_block_header.compression == Lzma &&
fwrite(lzma_properties, LZMA_PROPERTIES_LENGTH, 1, ctx->imageStream) != 1)
{
free(cmp_buffer);
return AARUF_ERROR_CANNOT_WRITE_BLOCK_DATA;
}
if(ctx->current_block_header.compression == None)
{
if(fwrite(ctx->writing_buffer, ctx->current_block_header.length, 1, ctx->imageStream) != 1)
return AARUF_ERROR_CANNOT_WRITE_BLOCK_DATA;
}
else
{
if(fwrite(cmp_buffer, ctx->current_block_header.cmpLength, 1, ctx->imageStream) != 1)
{
free(cmp_buffer);
return AARUF_ERROR_CANNOT_WRITE_BLOCK_DATA;
}
free(cmp_buffer);
}
// Update nextBlockPosition to point to the next available aligned position
const uint64_t block_total_size = sizeof(BlockHeader) + ctx->current_block_header.cmpLength;
const uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
ctx->next_block_position = ctx->next_block_position + block_total_size + alignment_mask & ~alignment_mask;
TRACE("Updated nextBlockPosition to %" PRIu64, ctx->next_block_position);
// Clear values
free(ctx->writing_buffer);
ctx->writing_buffer = NULL;
ctx->current_block_offset = 0;
memset(&ctx->current_block_header, 0, sizeof(BlockHeader));
aaruf_crc64_free(ctx->crc64_context);
ctx->writing_buffer_position = 0;
return AARUF_STATUS_OK;
}
AARU_EXPORT int32_t AARU_CALL aaruf_write_media_tag(void *context, const uint8_t *data, const int32_t type,
const uint32_t length)
{
TRACE("Entering aaruf_write_media_tag(%p, %p, %d, %d)", context, data, type, length);
// Check context is correct AaruFormat context
if(context == NULL)
{
FATAL("Invalid context");
TRACE("Exiting aaruf_write_media_tag() = 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_write_media_tag() = AARUF_ERROR_NOT_AARUFORMAT");
return AARUF_ERROR_NOT_AARUFORMAT;
}
// Check we are writing
if(!ctx->is_writing)
{
FATAL("Trying to write a read-only image");
TRACE("Exiting aaruf_write_media_tag() = AARUF_READ_ONLY");
return AARUF_READ_ONLY;
}
if(data == NULL || length == 0)
{
FATAL("Invalid data or length");
return AARUF_ERROR_INCORRECT_DATA_SIZE;
}
uint8_t *new_data = malloc(length);
if(new_data == NULL)
{
FATAL("Could not allocate memory for media tag");
return AARUF_ERROR_NOT_ENOUGH_MEMORY;
}
memcpy(new_data, data, length);
mediaTagEntry *media_tag = malloc(sizeof(mediaTagEntry));
mediaTagEntry *old_media_tag = NULL;
if(media_tag == NULL)
{
TRACE("Cannot allocate memory for media tag entry.");
free(new_data);
return AARUF_ERROR_NOT_ENOUGH_MEMORY;
}
memset(media_tag, 0, sizeof(mediaTagEntry));
media_tag->type = type;
media_tag->data = new_data;
media_tag->length = length;
HASH_REPLACE_INT(ctx->mediaTags, type, media_tag, old_media_tag);
if(old_media_tag != NULL)
{
TRACE("Replaced media tag with type %d", old_media_tag->type);
free(old_media_tag->data);
free(old_media_tag);
old_media_tag = NULL;
}
ctx->dirty_media_tags = true; // Mark media tags as dirty
TRACE("Exiting aaruf_write_media_tag() = AARUF_STATUS_OK");
return AARUF_STATUS_OK;
}
AARU_EXPORT int32_t AARU_CALL aaruf_write_sector_tag(void *context, const uint64_t sector_address, const bool negative,
const uint8_t *data, const size_t length, const int32_t tag)
{
TRACE("Entering aaruf_write_sector_tag(%p, %" PRIu64 ", %d, %p, %zu, %d)", context, sector_address, negative, data,
length, tag);
// Check context is correct AaruFormat context
if(context == NULL)
{
FATAL("Invalid context");
TRACE("Exiting aaruf_write_sector_tag() = 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_write_sector_tag() = AARUF_ERROR_NOT_AARUFORMAT");
return AARUF_ERROR_NOT_AARUFORMAT;
}
// Check we are writing
if(!ctx->is_writing)
{
FATAL("Trying to write a read-only image");
TRACE("Exiting aaruf_write_sector_tag() = AARUF_READ_ONLY");
return AARUF_READ_ONLY;
}
if(negative && sector_address > ctx->user_data_ddt_header.negative)
{
FATAL("Sector address out of bounds");
TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_SECTOR_OUT_OF_BOUNDS");
return AARUF_ERROR_SECTOR_OUT_OF_BOUNDS;
}
if(!negative && sector_address > ctx->image_info.Sectors + ctx->user_data_ddt_header.overflow - 1)
{
FATAL("Sector address out of bounds");
TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_SECTOR_OUT_OF_BOUNDS");
return AARUF_ERROR_SECTOR_OUT_OF_BOUNDS;
}
if(data == NULL || length == 0)
{
FATAL("Invalid data or length");
return AARUF_ERROR_INCORRECT_DATA_SIZE;
}
uint64_t corrected_sector_address = sector_address;
// Calculate positive or negative sector
if(negative)
corrected_sector_address = ctx->user_data_ddt_header.negative - sector_address;
else
corrected_sector_address += ctx->user_data_ddt_header.negative;
const uint64_t total_sectors =
ctx->user_data_ddt_header.negative + ctx->image_info.Sectors + ctx->user_data_ddt_header.overflow;
switch(tag)
{
case CdTrackFlags:
if(ctx->image_info.MetadataMediaType != OpticalDisc)
{
FATAL("Invalid media type for tag");
TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_MEDIA_TYPE");
return AARUF_ERROR_INCORRECT_MEDIA_TYPE;
}
if(length != 1)
{
FATAL("Incorrect tag size");
TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_DATA_SIZE");
return AARUF_ERROR_INCORRECT_DATA_SIZE;
}
for(int i = 0; i < ctx->tracks_header.entries; i++)
if(sector_address >= ctx->track_entries[i].start && sector_address <= ctx->track_entries[i].end)
{
ctx->track_entries[i].flags = data[0];
ctx->dirty_tracks_block = true; // Mark tracks block as dirty
TRACE("Exiting aaruf_write_sector_tag() = AARUF_STATUS_OK");
return AARUF_STATUS_OK;
}
FATAL("Track not found");
return AARUF_ERROR_TRACK_NOT_FOUND;
case CdTrackIsrc:
if(ctx->image_info.MetadataMediaType != OpticalDisc)
{
FATAL("Invalid media type for tag");
TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_MEDIA_TYPE");
return AARUF_ERROR_INCORRECT_MEDIA_TYPE;
}
if(length != 12)
{
FATAL("Incorrect tag size");
TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_DATA_SIZE");
return AARUF_ERROR_INCORRECT_DATA_SIZE;
}
for(int i = 0; i < ctx->tracks_header.entries; i++)
if(sector_address >= ctx->track_entries[i].start && sector_address <= ctx->track_entries[i].end)
{
memcpy(ctx->track_entries[i].isrc, data, 12);
ctx->dirty_tracks_block = true; // Mark tracks block as dirty
TRACE("Exiting aaruf_write_sector_tag() = AARUF_STATUS_OK");
return AARUF_STATUS_OK;
}
FATAL("Track not found");
return AARUF_ERROR_TRACK_NOT_FOUND;
case CdSectorSubchannelAaru:
if(ctx->image_info.MetadataMediaType != OpticalDisc)
{
FATAL("Invalid media type for tag");
TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_MEDIA_TYPE");
return AARUF_ERROR_INCORRECT_MEDIA_TYPE;
}
if(length != 96)
{
FATAL("Incorrect tag size");
TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_DATA_SIZE");
return AARUF_ERROR_INCORRECT_DATA_SIZE;
}
if(ctx->sector_subchannel == NULL) ctx->sector_subchannel = calloc(1, 96 * total_sectors);
if(ctx->sector_subchannel == NULL)
{
FATAL("Could not allocate memory for sector subchannel");
TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
return AARUF_ERROR_NOT_ENOUGH_MEMORY;
}
memcpy(ctx->sector_subchannel + corrected_sector_address * 96, data, 96);
ctx->dirty_sector_subchannel_block = true; // Mark subchannel block as dirty
TRACE("Exiting aaruf_write_sector_tag() = AARUF_STATUS_OK");
return AARUF_STATUS_OK;
case DvdCmi:
if(ctx->image_info.MetadataMediaType != OpticalDisc)
{
FATAL("Invalid media type for tag");
TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_MEDIA_TYPE");
return AARUF_ERROR_INCORRECT_MEDIA_TYPE;
}
if(length != 1)
{
FATAL("Incorrect tag size");
TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_DATA_SIZE");
return AARUF_ERROR_INCORRECT_DATA_SIZE;
}
if(ctx->sector_cpr_mai == NULL) ctx->sector_cpr_mai = calloc(1, 6 * total_sectors);
if(ctx->sector_cpr_mai == NULL)
{
FATAL("Could not allocate memory for sector CPR/MAI");
TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
return AARUF_ERROR_NOT_ENOUGH_MEMORY;
}
memcpy(ctx->sector_cpr_mai + corrected_sector_address * 6, data, 1);
ctx->dirty_dvd_long_sector_blocks = true; // Mark DVD long sector blocks as dirty
TRACE("Exiting aaruf_write_sector_tag() = AARUF_STATUS_OK");
return AARUF_STATUS_OK;
case DvdSectorInformation:
if(ctx->image_info.MetadataMediaType != OpticalDisc)
{
FATAL("Invalid media type for tag");
TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_MEDIA_TYPE");
return AARUF_ERROR_INCORRECT_MEDIA_TYPE;
}
if(length != 1)
{
FATAL("Incorrect tag size");
TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_DATA_SIZE");
return AARUF_ERROR_INCORRECT_DATA_SIZE;
}
if(ctx->sector_id == NULL) ctx->sector_id = calloc(1, 4 * total_sectors);
if(ctx->sector_id == NULL)
{
FATAL("Could not allocate memory for sector ID");
TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
return AARUF_ERROR_NOT_ENOUGH_MEMORY;
}
memcpy(ctx->sector_id + corrected_sector_address * 4, data, 1);
TRACE("Exiting aaruf_write_sector_tag() = AARUF_STATUS_OK");
return AARUF_STATUS_OK;
case DvdSectorNumber:
if(ctx->image_info.MetadataMediaType != OpticalDisc)
{
FATAL("Invalid media type for tag");
TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_MEDIA_TYPE");
return AARUF_ERROR_INCORRECT_MEDIA_TYPE;
}
if(length != 3)
{
FATAL("Incorrect tag size");
TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_DATA_SIZE");
return AARUF_ERROR_INCORRECT_DATA_SIZE;
}
if(ctx->sector_id == NULL) ctx->sector_id = calloc(1, 4 * total_sectors);
if(ctx->sector_id == NULL)
{
FATAL("Could not allocate memory for sector ID");
TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
return AARUF_ERROR_NOT_ENOUGH_MEMORY;
}
memcpy(ctx->sector_id + corrected_sector_address * 4 + 1, data, 3);
ctx->dirty_dvd_long_sector_blocks = true; // Mark DVD long sector blocks as dirty
TRACE("Exiting aaruf_write_sector_tag() = AARUF_STATUS_OK");
return AARUF_STATUS_OK;
case DvdSectorIedAaru:
if(ctx->image_info.MetadataMediaType != OpticalDisc)
{
FATAL("Invalid media type for tag");
TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_MEDIA_TYPE");
return AARUF_ERROR_INCORRECT_MEDIA_TYPE;
}
if(length != 2)
{
FATAL("Incorrect tag size");
TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_DATA_SIZE");
return AARUF_ERROR_INCORRECT_DATA_SIZE;
}
if(ctx->sector_ied == NULL) ctx->sector_ied = calloc(1, 2 * total_sectors);
if(ctx->sector_ied == NULL)
{
FATAL("Could not allocate memory for sector IED");
TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
return AARUF_ERROR_NOT_ENOUGH_MEMORY;
}
memcpy(ctx->sector_ied + corrected_sector_address * 2, data, 2);
ctx->dirty_dvd_long_sector_blocks = true; // Mark DVD long sector blocks as dirty
TRACE("Exiting aaruf_write_sector_tag() = AARUF_STATUS_OK");
return AARUF_STATUS_OK;
case DvdSectorEdcAaru:
if(ctx->image_info.MetadataMediaType != OpticalDisc)
{
FATAL("Invalid media type for tag");
TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_MEDIA_TYPE");
return AARUF_ERROR_INCORRECT_MEDIA_TYPE;
}
if(length != 4)
{
FATAL("Incorrect tag size");
TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_DATA_SIZE");
return AARUF_ERROR_INCORRECT_DATA_SIZE;
}
if(ctx->sector_edc == NULL) ctx->sector_edc = calloc(1, 4 * total_sectors);
if(ctx->sector_edc == NULL)
{
FATAL("Could not allocate memory for sector EDC");
TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
return AARUF_ERROR_NOT_ENOUGH_MEMORY;
}
memcpy(ctx->sector_edc + corrected_sector_address * 4, data, 4);
ctx->dirty_dvd_long_sector_blocks = true; // Mark DVD long sector blocks as dirty
TRACE("Exiting aaruf_write_sector_tag() = AARUF_STATUS_OK");
return AARUF_STATUS_OK;
case DvdTitleKeyDecrypted:
if(ctx->image_info.MetadataMediaType != OpticalDisc)
{
FATAL("Invalid media type for tag");
TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_MEDIA_TYPE");
return AARUF_ERROR_INCORRECT_MEDIA_TYPE;
}
if(length != 5)
{
FATAL("Incorrect tag size");
TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_DATA_SIZE");
return AARUF_ERROR_INCORRECT_DATA_SIZE;
}
if(ctx->sector_decrypted_title_key == NULL) ctx->sector_decrypted_title_key = calloc(1, 5 * total_sectors);
if(ctx->sector_decrypted_title_key == NULL)
{
FATAL("Could not allocate memory for sector decrypted title key");
TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
return AARUF_ERROR_NOT_ENOUGH_MEMORY;
}
memcpy(ctx->sector_decrypted_title_key + corrected_sector_address * 5, data, 5);
ctx->dirty_dvd_title_key_decrypted_block = true; // Mark title key block as dirty
TRACE("Exiting aaruf_write_sector_tag() = AARUF_STATUS_OK");
return AARUF_STATUS_OK;
case AppleSonyTagAaru:
if(ctx->image_info.MetadataMediaType != BlockMedia)
{
FATAL("Invalid media type for tag");
TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_MEDIA_TYPE");
return AARUF_ERROR_INCORRECT_MEDIA_TYPE;
}
if(length != 12)
{
FATAL("Incorrect tag size");
TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_DATA_SIZE");
return AARUF_ERROR_INCORRECT_DATA_SIZE;
}
if(ctx->sector_subchannel == NULL) ctx->sector_subchannel = calloc(1, 12 * total_sectors);
if(ctx->sector_subchannel == NULL)
{
FATAL("Could not allocate memory for Apple Sony tag");
TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
return AARUF_ERROR_NOT_ENOUGH_MEMORY;
}
memcpy(ctx->sector_subchannel + corrected_sector_address * 12, data, 12);
ctx->dirty_sector_subchannel_block = true; // Mark subchannel block as dirty
TRACE("Exiting aaruf_write_sector_tag() = AARUF_STATUS_OK");
return AARUF_STATUS_OK;
case AppleProfileTagAaru:
if(ctx->image_info.MetadataMediaType != BlockMedia)
{
FATAL("Invalid media type for tag");
TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_MEDIA_TYPE");
return AARUF_ERROR_INCORRECT_MEDIA_TYPE;
}
if(length != 20)
{
FATAL("Incorrect tag size");
TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_DATA_SIZE");
return AARUF_ERROR_INCORRECT_DATA_SIZE;
}
if(ctx->sector_subchannel == NULL) ctx->sector_subchannel = calloc(1, 20 * total_sectors);
if(ctx->sector_subchannel == NULL)
{
FATAL("Could not allocate memory for Apple Profile tag");
TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
return AARUF_ERROR_NOT_ENOUGH_MEMORY;
}
memcpy(ctx->sector_subchannel + corrected_sector_address * 20, data, 20);
ctx->dirty_sector_subchannel_block = true; // Mark subchannel block as dirty
TRACE("Exiting aaruf_write_sector_tag() = AARUF_STATUS_OK");
return AARUF_STATUS_OK;
case PriamDataTowerTagAaru:
if(ctx->image_info.MetadataMediaType != BlockMedia)
{
FATAL("Invalid media type for tag");
TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_MEDIA_TYPE");
return AARUF_ERROR_INCORRECT_MEDIA_TYPE;
}
if(length != 24)
{
FATAL("Incorrect tag size");
TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_DATA_SIZE");
return AARUF_ERROR_INCORRECT_DATA_SIZE;
}
if(ctx->sector_subchannel == NULL) ctx->sector_subchannel = calloc(1, 24 * total_sectors);
if(ctx->sector_subchannel == NULL)
{
FATAL("Could not allocate memory for Priam Data Tower tag");
TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
return AARUF_ERROR_NOT_ENOUGH_MEMORY;
}
memcpy(ctx->sector_subchannel + corrected_sector_address * 24, data, 24);
ctx->dirty_sector_subchannel_block = true; // Mark subchannel block as dirty
TRACE("Exiting aaruf_write_sector_tag() = AARUF_STATUS_OK");
return AARUF_STATUS_OK;
default:
TRACE("Do not know how to write sector tag %d", tag);
return AARUF_ERROR_INVALID_TAG;
}
}
aaruformat.h
MAX_FLAKE_BLOCK
#define MAX_FLAKE_BLOCK
FLAC maximum block size used for encoding audio sectors.
Definition consts.h:94
LZMA_PROPERTIES_LENGTH
#define LZMA_PROPERTIES_LENGTH
Size in bytes of the fixed LZMA properties header (lc/lp/pb + dictionary size).
Definition consts.h:82
AARU_MAGIC
#define AARU_MAGIC
Magic identifier for AaruFormat container (ASCII "AARUFRMT").
Definition consts.h:64
MIN_FLAKE_BLOCK
#define MIN_FLAKE_BLOCK
FLAC minimum block size.
Definition consts.h:96
SAMPLES_PER_SECTOR
#define SAMPLES_PER_SECTOR
Red Book (CD‑DA) PCM samples per 2352‑byte sector: 44,100 Hz / 75 sectors per second = 588 samples.
Definition consts.h:90
AARU_CALL
#define AARU_CALL
Definition decls.h:46
aaruf_lzma_encode_buffer
int32_t aaruf_lzma_encode_buffer(uint8_t *dst_buffer, size_t *dst_size, const uint8_t *src_buffer, size_t src_size, uint8_t *out_props, size_t *out_props_size, int32_t level, uint32_t dict_size, int32_t lc, int32_t lp, int32_t pb, int32_t fb, int32_t num_threads)
Encodes a buffer using LZMA compression.
Definition lzma.c:65
aaruf_crc64_data
uint64_t aaruf_crc64_data(const uint8_t *data, uint32_t len)
Definition crc64.c:160
aaruf_flac_encode_redbook_buffer
size_t aaruf_flac_encode_redbook_buffer(uint8_t *dst_buffer, size_t dst_size, const uint8_t *src_buffer, size_t src_size, uint32_t blocksize, int32_t do_mid_side_stereo, int32_t loose_mid_side_stereo, const char *apodization, uint32_t max_lpc_order, uint32_t qlp_coeff_precision, int32_t do_qlp_coeff_prec_search, int32_t do_exhaustive_model_search, uint32_t min_residual_partition_order, uint32_t max_residual_partition_order, const char *application_id, uint32_t application_id_len)
Encodes a Red Book audio buffer to FLAC format.
Definition flac.c:175
aaruf_crc64_update
int aaruf_crc64_update(crc64_ctx *ctx, const uint8_t *data, uint32_t len)
Updates the CRC64 context with new data.
Definition crc64.c:55
aaruf_crc64_free
void aaruf_crc64_free(crc64_ctx *ctx)
Frees a CRC64 context.
Definition crc64.c:155
aaruf_spamsum_update
int aaruf_spamsum_update(spamsum_ctx *ctx, const uint8_t *data, uint32_t len)
Updates the spamsum context with new data.
Definition spamsum.c:59
aaruf_crc64_init
crc64_ctx * aaruf_crc64_init()
Initializes a CRC64 context.
Definition crc64.c:32
aaruf_edc_cd_compute
uint32_t aaruf_edc_cd_compute(void *context, uint32_t edc, const uint8_t *src, int size, int pos)
Computes the EDC (Error Detection Code) for a CD sector.
Definition ecc_cd.c:564
aaruf_md5_update
void aaruf_md5_update(md5_ctx *ctx, const void *data, unsigned long size)
Definition md5.c:447
AARU_EXPORT
#define AARU_EXPORT
Definition decls.h:55
aaruf_sha256_update
void aaruf_sha256_update(sha256_ctx *ctx, const void *data, unsigned long size)
Definition sha256.c:90
aaruf_ecc_cd_is_suffix_correct_mode2
bool aaruf_ecc_cd_is_suffix_correct_mode2(void *context, const uint8_t *sector)
Checks if the suffix (EDC/ECC) of a CD sector is correct (Mode 2).
Definition ecc_cd.c:182
aaruf_sha1_update
void aaruf_sha1_update(sha1_ctx *ctx, const void *data, unsigned long size)
Definition sha1.c:89
aaruf_crc64_final
int aaruf_crc64_final(crc64_ctx *ctx, uint64_t *crc)
Computes the final CRC64 value from the context.
Definition crc64.c:141
aaruf_ecc_cd_is_suffix_correct
bool aaruf_ecc_cd_is_suffix_correct(void *context, const uint8_t *sector)
Checks if the suffix (EDC/ECC) of a CD sector is correct (Mode 1).
Definition ecc_cd.c:118
DataBlock
@ DataBlock
Block containing data.
Definition enums.h:144
SectorStatusNotDumped
@ SectorStatusNotDumped
Sector(s) not yet acquired during image dumping.
Definition enums.h:236
SectorStatusMode2Form2NoCrc
@ SectorStatusMode2Form2NoCrc
Suffix matches MODE 2 Form 2 but CRC empty/missing.
Definition enums.h:242
SectorStatusMode1Correct
@ SectorStatusMode1Correct
Valid MODE 1 data with regenerable suffix/prefix.
Definition enums.h:239
SectorStatusMode2Form2Ok
@ SectorStatusMode2Form2Ok
Suffix matches MODE 2 Form 2 with valid CRC.
Definition enums.h:241
SectorStatusErrored
@ SectorStatusErrored
Error during dumping; data may be incomplete or corrupt.
Definition enums.h:238
SectorStatusMode2Form1Ok
@ SectorStatusMode2Form1Ok
Suffix verified/regenerable for MODE 2 Form 1.
Definition enums.h:240
OpticalDisc
@ OpticalDisc
Purely optical discs.
Definition enums.h:224
BlockMedia
@ BlockMedia
Media that is physically block-based or abstracted like that.
Definition enums.h:225
CdMode1
@ CdMode1
Compact Disc Mode 1 data track.
Definition enums.h:203
Data
@ Data
Generic data track (not further specified).
Definition enums.h:202
CdMode2Form2
@ CdMode2Form2
Compact Disc Mode 2 Form 2 data track.
Definition enums.h:206
Audio
@ Audio
Audio track.
Definition enums.h:201
CdMode2Form1
@ CdMode2Form1
Compact Disc Mode 2 Form 1 data track.
Definition enums.h:205
CdMode2Formless
@ CdMode2Formless
Compact Disc Mode 2 (formless) data track.
Definition enums.h:204
UserData
@ UserData
User (main) data.
Definition enums.h:46
Lzma
@ Lzma
LZMA compression.
Definition enums.h:34
None
@ None
Not compressed.
Definition enums.h:33
Flac
@ Flac
FLAC compression.
Definition enums.h:35
AARUF_ERROR_CANNOT_WRITE_BLOCK_DATA
#define AARUF_ERROR_CANNOT_WRITE_BLOCK_DATA
Failure writing block payload.
Definition errors.h:63
AARUF_STATUS_OK
#define AARUF_STATUS_OK
Sector present and read without uncorrectable errors.
Definition errors.h:77
AARUF_READ_ONLY
#define AARUF_READ_ONLY
Operation requires write mode but context is read-only.
Definition errors.h:61
AARUF_ERROR_INCORRECT_MEDIA_TYPE
#define AARUF_ERROR_INCORRECT_MEDIA_TYPE
Operation incompatible with image media type.
Definition errors.h:51
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_SECTOR_OUT_OF_BOUNDS
#define AARUF_ERROR_SECTOR_OUT_OF_BOUNDS
Requested logical sector outside media bounds.
Definition errors.h:44
AARUF_ERROR_INCORRECT_DATA_SIZE
#define AARUF_ERROR_INCORRECT_DATA_SIZE
Data size does not match expected size.
Definition errors.h:65
AARUF_ERROR_CANNOT_SET_DDT_ENTRY
#define AARUF_ERROR_CANNOT_SET_DDT_ENTRY
Failed to encode/store a DDT entry (overflow or IO).
Definition errors.h:64
AARUF_ERROR_NOT_AARUFORMAT
#define AARUF_ERROR_NOT_AARUFORMAT
Input file/stream failed magic or structural validation.
Definition errors.h:40
AARUF_ERROR_INVALID_TAG
#define AARUF_ERROR_INVALID_TAG
Invalid or unsupported media or sector tag format.
Definition errors.h:66
AARUF_ERROR_INVALID_SECTOR_LENGTH
#define AARUF_ERROR_INVALID_SECTOR_LENGTH
Sector length is too big.
Definition errors.h:70
AARUF_ERROR_CANNOT_WRITE_BLOCK_HEADER
#define AARUF_ERROR_CANNOT_WRITE_BLOCK_HEADER
Failure writing block header.
Definition errors.h:62
AppleProfile
@ AppleProfile
Definition aaru.h:708
VideoNowColor
@ VideoNowColor
Hasbro VideoNow Color disc.
Definition aaru.h:782
AppleSonySS
@ AppleSonySS
3.5", SS, DD, 80 tracks, 8 to 12 spt, 512 bytes/sector, GCR
Definition aaru.h:250
DVDPRWDL
@ DVDPRWDL
DVD+RW DL.
Definition aaru.h:141
DVDRW
@ DVDRW
DVD-RW.
Definition aaru.h:138
DVDRWDL
@ DVDRWDL
DVD-RW DL.
Definition aaru.h:145
SACD
@ SACD
Super Audio CD (Scarlet Book).
Definition aaru.h:121
DVDPRDL
@ DVDPRDL
DVD+R DL.
Definition aaru.h:143
DVDR
@ DVDR
DVD-R.
Definition aaru.h:137
PS3DVD
@ PS3DVD
Sony PlayStation 3 game DVD.
Definition aaru.h:209
JaguarCD
@ JaguarCD
Atari Jaguar CD.
Definition aaru.h:235
PS2DVD
@ PS2DVD
Sony PlayStation 2 game DVD.
Definition aaru.h:208
VideoNow
@ VideoNow
Hasbro VideoNow 85 mm proprietary video disc.
Definition aaru.h:781
VideoNowXp
@ VideoNowXp
Hasbro VideoNow XP higher capacity disc.
Definition aaru.h:783
PriamDataTower
@ PriamDataTower
Definition aaru.h:711
AppleFileWare
@ AppleFileWare
5.25", DS, ?D, ?? tracks, ?? spt, 512 bytes/sector, GCR, opposite side heads, aka Twiggy
Definition aaru.h:252
DVDPR
@ DVDPR
DVD+R.
Definition aaru.h:139
AppleWidget
@ AppleWidget
Definition aaru.h:709
DVDPRW
@ DVDPRW
DVD+RW.
Definition aaru.h:140
Nuon
@ Nuon
Nuon (DVD based videogame console).
Definition aaru.h:241
DVDDownload
@ DVDDownload
DVD-Download.
Definition aaru.h:146
DVDRDL
@ DVDRDL
DVD-R DL.
Definition aaru.h:142
AppleSonyDS
@ AppleSonyDS
3.5", DS, DD, 80 tracks, 8 to 12 spt, 512 bytes/sector, GCR
Definition aaru.h:251
DVDROM
@ DVDROM
DVD-ROM (applies to DVD Video and DVD Audio).
Definition aaru.h:136
DVDRAM
@ DVDRAM
DVD-RAM.
Definition aaru.h:144
DvdSectorIedAaru
@ DvdSectorIedAaru
DVD sector ID error detection, 2 bytes.
Definition aaru.h:976
CdTrackFlags
@ CdTrackFlags
Track flags (audio/data, copy permitted, pre-emphasis).
Definition aaru.h:969
DvdSectorEdcAaru
@ DvdSectorEdcAaru
DVD sector EDC, 4 bytes.
Definition aaru.h:977
DvdCmi
@ DvdCmi
DVD Copyright Management Information (CSS).
Definition aaru.h:970
CdSectorSubchannelAaru
@ CdSectorSubchannelAaru
96 raw subchannel bytes (P-W)
Definition aaru.h:966
DvdSectorNumber
@ DvdSectorNumber
DVD sector number, 3 bytes.
Definition aaru.h:975
CdTrackIsrc
@ CdTrackIsrc
Track ISRC (12 ASCII chars, no terminator).
Definition aaru.h:967
PriamDataTowerTagAaru
@ PriamDataTowerTagAaru
Priam DataTower sector tags, 24 bytes.
Definition aaru.h:979
AppleProfileTagAaru
@ AppleProfileTagAaru
Apple's Profile sector tags, 20 bytes.
Definition aaru.h:978
DvdSectorInformation
@ DvdSectorInformation
DVD sector information, 1 bytes.
Definition aaru.h:974
DvdTitleKeyDecrypted
@ DvdTitleKeyDecrypted
Decrypted DVD sector title key, 5 bytes.
Definition aaru.h:973
AppleSonyTagAaru
@ AppleSonyTagAaru
Apple's Sony sector tags, 12 bytes (address prolog + checksum).
Definition aaru.h:958
lookup_map
bool lookup_map(const hash_map_t *map, uint64_t key, uint64_t *out_value)
Looks up a value by key in the hash map.
Definition hash_map.c:228
insert_map
bool insert_map(hash_map_t *map, uint64_t key, uint64_t value)
Inserts a key-value pair into the hash map.
Definition hash_map.c:185
internal.h
set_ddt_entry_v2
bool set_ddt_entry_v2(aaruformat_context *ctx, uint64_t sector_address, bool negative, uint64_t offset, uint64_t block_offset, uint8_t sector_status, uint64_t *ddt_entry)
Sets a DDT v2 entry for a given sector address.
Definition ddt_v2.c:1002
lisa_tag.h
Structure definitions and conversion/serialization function declarations for Lisa family disk tags.
priam_tag_to_bytes
uint8_t * priam_tag_to_bytes(priam_tag tag)
Serialize a priam_tag into a newly allocated 24-byte big-endian on-disk representation.
Definition lisa_tag.c:409
bytes_to_priam_tag
priam_tag bytes_to_priam_tag(const uint8_t *bytes)
Parse a 24-byte Priam tag record into a priam_tag structure.
Definition lisa_tag.c:112
profile_tag_to_sony
sony_tag profile_tag_to_sony(profile_tag tag)
Convert a profile_tag to a sony_tag.
Definition lisa_tag.c:257
sony_tag_to_bytes
uint8_t * sony_tag_to_bytes(sony_tag tag)
Serialize a sony_tag into a newly allocated 12-byte big-endian on-disk representation.
Definition lisa_tag.c:466
priam_tag_to_profile
profile_tag priam_tag_to_profile(priam_tag tag)
Convert a priam_tag to a profile_tag.
Definition lisa_tag.c:325
profile_tag_to_bytes
uint8_t * profile_tag_to_bytes(profile_tag tag)
Serialize a profile_tag into a newly allocated 20-byte big-endian on-disk representation.
Definition lisa_tag.c:357
sony_tag_to_profile
profile_tag sony_tag_to_profile(sony_tag tag)
Convert a sony_tag to a profile_tag representation.
Definition lisa_tag.c:173
bytes_to_sony_tag
sony_tag bytes_to_sony_tag(const uint8_t *bytes)
Parse a 12-byte Sony tag record into a sony_tag structure.
Definition lisa_tag.c:82
bytes_to_profile_tag
profile_tag bytes_to_profile_tag(const uint8_t *bytes)
Parse a 20-byte Profile tag record into a profile_tag structure.
Definition lisa_tag.c:142
sony_tag_to_priam
priam_tag sony_tag_to_priam(sony_tag tag)
Convert a sony_tag to a priam_tag representation.
Definition lisa_tag.c:201
profile_tag_to_priam
priam_tag profile_tag_to_priam(profile_tag tag)
Convert a profile_tag to a priam_tag.
Definition lisa_tag.c:228
priam_tag_to_sony
sony_tag priam_tag_to_sony(priam_tag tag)
Convert a priam_tag to a sony_tag.
Definition lisa_tag.c:291
log.h
FATAL
#define FATAL(fmt,...)
Definition log.h:40
TRACE
#define TRACE(fmt,...)
Definition log.h:25
AaruHeaderV2::biggestSectorSize
uint16_t biggestSectorSize
size of biggest sector in the image (in bytes).
Definition header.h:120
BlockHeader
Header preceding the compressed data payload of a data block (BlockType::DataBlock).
Definition data.h:71
BlockHeader::cmpLength
uint32_t cmpLength
Size in bytes of the compressed payload immediately following this header.
Definition data.h:76
BlockHeader::length
uint32_t length
Size in bytes of the uncompressed payload resulting after decompression.
Definition data.h:77
BlockHeader::identifier
uint32_t identifier
Block identifier, must be BlockType::DataBlock.
Definition data.h:72
BlockHeader::sectorSize
uint32_t sectorSize
Size in bytes of each logical sector represented in this block.
Definition data.h:75
BlockHeader::cmpCrc64
uint64_t cmpCrc64
CRC64-ECMA of the compressed payload (cmpLength bytes).
Definition data.h:78
BlockHeader::crc64
uint64_t crc64
CRC64-ECMA of the uncompressed payload (length bytes).
Definition data.h:79
BlockHeader::type
uint16_t type
Logical data classification (value from DataType).
Definition data.h:73
BlockHeader::compression
uint16_t compression
Compression algorithm used (value from CompressionType).
Definition data.h:74
DdtHeader2::blockAlignmentShift
uint8_t blockAlignmentShift
2^blockAlignmentShift = block alignment boundary in bytes.
Definition ddt.h:154
DdtHeader2::overflow
uint32_t overflow
Trailing dumped sectors beyond user area (overflow range), still mapped with entries.
Definition ddt.h:151
DdtHeader2::negative
uint32_t negative
Leading negative LBA count; added to external L to build internal index.
Definition ddt.h:149
DdtHeader2::dataShift
uint8_t dataShift
2^dataShift = sectors represented per increment in blockIndex field.
Definition ddt.h:155
ImageInfo::MediaType
uint32_t MediaType
Media type identifier (see MediaType enum; 0=Unknown).
Definition aaru.h:943
ImageInfo::MetadataMediaType
uint8_t MetadataMediaType
Media type for sidecar generation (internal archival use).
Definition aaru.h:944
ImageInfo::Sectors
uint64_t Sectors
Total count of addressable logical sectors/blocks.
Definition aaru.h:936
IndexEntry
Single index entry describing a block's type, (optional) data classification, and file offset.
Definition index.h:109
IndexEntry::blockType
uint32_t blockType
Block identifier of the referenced block (value from BlockType).
Definition index.h:110
IndexEntry::offset
uint64_t offset
Absolute byte offset in the image where the referenced block header begins.
Definition index.h:112
IndexEntry::dataType
uint16_t dataType
Data classification (value from DataType) or unused for untyped blocks.
Definition index.h:111
TrackEntry
Single optical disc track descriptor (sequence, type, LBAs, session, ISRC, flags).
Definition optical.h:72
TrackEntry::session
uint8_t session
Session number (1-based). 1 for single-session discs.
Definition optical.h:78
TrackEntry::flags
uint8_t flags
Control / attribute bitfield (see file documentation for suggested bit mapping).
Definition optical.h:80
TrackEntry::end
int64_t end
Inclusive ending LBA of the track.
Definition optical.h:76
TrackEntry::sequence
uint8_t sequence
Track number (1..99 typical for CD audio/data). 0 may indicate placeholder/non-standard.
Definition optical.h:73
TrackEntry::start
int64_t start
Inclusive starting LBA of the track.
Definition optical.h:75
TrackEntry::type
uint8_t type
Track type (value from TrackType).
Definition optical.h:74
TrackEntry::isrc
uint8_t isrc[13]
ISRC raw 13-byte code (no null terminator). All zeros if not present.
Definition optical.h:79
TracksHeader::entries
uint16_t entries
Number of TrackEntry records following this header.
Definition optical.h:64
aaruformat_context
Master context representing an open or in‑creation Aaru image.
Definition context.h:175
aaruformat_context::user_data_ddt_header
DdtHeader2 user_data_ddt_header
Active user data DDT v2 header (primary table meta).
Definition context.h:192
aaruformat_context::dirty_checksum_block
bool dirty_checksum_block
True if checksum block should be written during close.
Definition context.h:320
aaruformat_context::deduplicate
bool deduplicate
Storage deduplication active (duplicates coalesce).
Definition context.h:302
aaruformat_context::sector_suffix_length
size_t sector_suffix_length
Length of sector_suffix.
Definition context.h:288
aaruformat_context::compression_enabled
bool compression_enabled
True if block compression enabled (writing path).
Definition context.h:303
aaruformat_context::last_written_block
uint64_t last_written_block
Last written block number (write path).
Definition context.h:286
aaruformat_context::sector_cpr_mai
uint8_t * sector_cpr_mai
DVD sector CPR_MAI (6 bytes) if present.
Definition context.h:210
aaruformat_context::dirty_media_tags
bool dirty_media_tags
True if media tags should be written during close.
Definition context.h:330
aaruformat_context::sector_hash_map
hash_map_t * sector_hash_map
Deduplication hash map (fingerprint->entry mapping).
Definition context.h:256
aaruformat_context::sha256_context
sha256_ctx sha256_context
Opaque SHA-256 context for streaming updates.
Definition context.h:275
aaruformat_context::calculating_sha256
bool calculating_sha256
True if whole-image SHA-256 being calculated on-the-fly.
Definition context.h:278
aaruformat_context::sector_ied
uint8_t * sector_ied
DVD sector IED (2 bytes) if present.
Definition context.h:209
aaruformat_context::md5_context
md5_ctx md5_context
Opaque MD5 context for streaming updates.
Definition context.h:273
aaruformat_context::dirty_sector_suffix_block
bool dirty_sector_suffix_block
True if sector suffix block should be written during close.
Definition context.h:325
aaruformat_context::sector_prefix
uint8_t * sector_prefix
Raw per-sector prefix (e.g., sync+header) uncorrected.
Definition context.h:202
aaruformat_context::dirty_dvd_title_key_decrypted_block
bool dirty_dvd_title_key_decrypted_block
True if decrypted title key block should be written during close.
Definition context.h:329
aaruformat_context::sector_suffix_ddt2
uint64_t * sector_suffix_ddt2
CD sector suffix DDT V2.
Definition context.h:189
aaruformat_context::dirty_mode2_subheaders_block
bool dirty_mode2_subheaders_block
True if MODE2 subheader block should be written during close.
Definition context.h:322
aaruformat_context::sector_edc
uint8_t * sector_edc
DVD sector EDC (4 bytes) if present.
Definition context.h:211
aaruformat_context::calculating_sha1
bool calculating_sha1
True if whole-image SHA-1 being calculated on-the-fly.
Definition context.h:277
aaruformat_context::dirty_tracks_block
bool dirty_tracks_block
True if tracks block should be written during close.
Definition context.h:321
aaruformat_context::ecc_cd_context
CdEccContext * ecc_cd_context
CD ECC/EDC helper tables (allocated on demand).
Definition context.h:251
aaruformat_context::rewinded
bool rewinded
True if stream has been rewound after open (write path).
Definition context.h:296
aaruformat_context::dirty_sector_prefix_block
bool dirty_sector_prefix_block
True if sector prefix block should be written during close.
Definition context.h:323
aaruformat_context::dirty_index_block
bool dirty_index_block
True if index block should be written during close.
Definition context.h:340
aaruformat_context::sector_suffix
uint8_t * sector_suffix
Raw per-sector suffix (EDC/ECC) uncorrected.
Definition context.h:204
aaruformat_context::header
AaruHeaderV2 header
Parsed container header (v2).
Definition context.h:178
aaruformat_context::current_block_offset
int current_block_offset
Logical offset inside block (units: bytes or sectors depending on path).
Definition context.h:291
aaruformat_context::is_writing
bool is_writing
True if context opened/created for writing.
Definition context.h:295
aaruformat_context::spamsum_context
spamsum_ctx * spamsum_context
Opaque SpamSum context for streaming updates.
Definition context.h:270
aaruformat_context::sector_prefix_offset
size_t sector_prefix_offset
Current position in sector_prefix.
Definition context.h:289
aaruformat_context::current_block_header
BlockHeader current_block_header
Header for block currently being assembled (write path).
Definition context.h:284
aaruformat_context::magic
uint64_t magic
File magic (AARU_MAGIC) post-open.
Definition context.h:177
aaruformat_context::writing_buffer
uint8_t * writing_buffer
Accumulation buffer for current block data.
Definition context.h:283
aaruformat_context::sector_prefix_ddt2
uint64_t * sector_prefix_ddt2
CD sector prefix DDT V2.
Definition context.h:188
aaruformat_context::calculating_spamsum
bool calculating_spamsum
True if whole-image SpamSum being calculated on-the-fly.
Definition context.h:279
aaruformat_context::sector_prefix_length
size_t sector_prefix_length
Length of sector_prefix.
Definition context.h:287
aaruformat_context::mediaTags
mediaTagEntry * mediaTags
Hash table of extra media tags (uthash root).
Definition context.h:267
aaruformat_context::blake3_context
blake3_hasher * blake3_context
Opaque BLAKE3 context for streaming updates.
Definition context.h:271
aaruformat_context::calculating_blake3
bool calculating_blake3
True if whole-image BLAKE3 being calculated on-the-fly.
Definition context.h:280
aaruformat_context::next_block_position
uint64_t next_block_position
Absolute file offset where next block will be written.
Definition context.h:285
aaruformat_context::calculating_md5
bool calculating_md5
True if whole-image MD5 being calculated on-the-fly.
Definition context.h:276
aaruformat_context::dirty_sector_suffix_ddt
bool dirty_sector_suffix_ddt
True if sector suffix DDT should be written during close.
Definition context.h:326
aaruformat_context::sector_suffix_offset
size_t sector_suffix_offset
Current position in sector_suffix.
Definition context.h:290
aaruformat_context::sector_decrypted_title_key
uint8_t * sector_decrypted_title_key
DVD decrypted title key (5 bytes) if present.
Definition context.h:212
aaruformat_context::writing_buffer_position
int writing_buffer_position
Current size / position within writingBuffer.
Definition context.h:292
aaruformat_context::block_zero_written
bool block_zero_written
True if block zero has been written (writing path).
Definition context.h:298
aaruformat_context::crc64_context
crc64_ctx * crc64_context
Opaque CRC64 context for streaming updates.
Definition context.h:252
aaruformat_context::sector_subchannel
uint8_t * sector_subchannel
Raw 96-byte subchannel (if captured).
Definition context.h:206
aaruformat_context::dirty_sector_subchannel_block
bool dirty_sector_subchannel_block
True if subchannel block should be written during close.
Definition context.h:327
aaruformat_context::imageStream
FILE * imageStream
Underlying FILE* stream (binary mode).
Definition context.h:179
aaruformat_context::dirty_dvd_long_sector_blocks
bool dirty_dvd_long_sector_blocks
True if DVD long sector blocks should be written during close.
Definition context.h:328
aaruformat_context::dirty_sector_prefix_ddt
bool dirty_sector_prefix_ddt
True if sector prefix DDT should be written during close.
Definition context.h:324
aaruformat_context::index_entries
UT_array * index_entries
Flattened index entries (UT_array of IndexEntry).
Definition context.h:255
aaruformat_context::mode2_subheaders
uint8_t * mode2_subheaders
MODE2 Form1/Form2 8-byte subheaders (concatenated).
Definition context.h:207
aaruformat_context::image_info
ImageInfo image_info
Exposed high-level image info summary.
Definition context.h:263
aaruformat_context::sector_id
uint8_t * sector_id
DVD sector ID (4 bytes) if present.
Definition context.h:208
aaruformat_context::sha1_context
sha1_ctx sha1_context
Opaque SHA-1 context for streaming updates.
Definition context.h:274
aaruformat_context::lzma_dict_size
uint32_t lzma_dict_size
LZMA dictionary size (writing path).
Definition context.h:301
aaruformat_context::track_entries
TrackEntry * track_entries
Full track list (tracksHeader.entries elements).
Definition context.h:245
aaruformat_context::current_track_type
uint8_t current_track_type
Current track type (when writing optical images with tracks, needed for block compression type).
Definition context.h:293
aaruformat_context::writing_long
bool writing_long
True if writing long sectors.
Definition context.h:297
aaruformat_context::tracks_header
TracksHeader tracks_header
Tracks header (optical) if present.
Definition context.h:247
mediaTagEntry
Hash table entry for an arbitrary media tag (e.g., proprietary drive/medium descriptor).
Definition context.h:122
mediaTagEntry::data
uint8_t * data
Tag data blob (opaque to library core); length bytes long.
Definition context.h:123
mediaTagEntry::type
int32_t type
Numeric type identifier.
Definition context.h:124
mediaTagEntry::length
uint32_t length
Length in bytes of data.
Definition context.h:125
priam_tag
Definition lisa_tag.h:63
profile_tag
Definition lisa_tag.h:47
sony_tag
Definition lisa_tag.h:35
aaruf_close_current_block
int32_t aaruf_close_current_block(aaruformat_context *ctx)
Finalizes and writes the current data block to the AaruFormat image file.
Definition write.c:1438
aaruf_write_sector_tag
int32_t aaruf_write_sector_tag(void *context, const uint64_t sector_address, const bool negative, const uint8_t *data, const size_t length, const int32_t tag)
Writes per-sector tag data (auxiliary metadata) for a specific sector.
Definition write.c:2106
aaruf_write_media_tag
int32_t aaruf_write_media_tag(void *context, const uint8_t *data, const int32_t type, const uint32_t length)
Writes a media tag to the AaruFormat image, storing medium-specific metadata and descriptors.
Definition write.c:1836
aaruf_write_sector
int32_t aaruf_write_sector(void *context, uint64_t sector_address, bool negative, const uint8_t *data, uint8_t sector_status, uint32_t length)
Writes a sector to the AaruFormat image.
Definition write.c:99
aaruf_write_sector_long
int32_t aaruf_write_sector_long(void *context, uint64_t sector_address, bool negative, const uint8_t *data, uint8_t sector_status, uint32_t length)
Writes a full ("long") raw sector from optical or block media, parsing structure and validating conte...
Definition write.c:555