libaaruformat 1.0
Aaru Data Preservation Suite - Format Library
Loading...
Searching...
No Matches
write.c
Go to the documentation of this file.
1/*
2 * This file is part of the Aaru Data Preservation Suite.
3 * Copyright (c) 2019-2026 Natalia Portillo.
4 *
5 * This library is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as
7 * published by the Free Software Foundation; either version 2.1 of the
8 * License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
17 */
18#include <errno.h>
19#include <limits.h>
20#include <stdbool.h>
21#include <stdint.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25
26#include "aaruformat.h"
27#include "erasure_internal.h"
28#include "internal.h"
29#include "log.h"
30#include "ps3/ps3_crypto.h"
32#include "structs/lisa_tag.h"
33#include "xxhash.h"
34
47static int32_t grow_arena(uint8_t **arena, size_t *length, const size_t needed, const size_t minimum)
48{
49 if(needed <= *length) return AARUF_STATUS_OK;
50
51 size_t new_length = *length * 2;
52 if(new_length < needed) new_length = needed;
53 if(new_length < minimum) new_length = minimum;
54
55 uint8_t *grown = realloc(*arena, new_length);
56 if(grown == NULL) return AARUF_ERROR_NOT_ENOUGH_MEMORY;
57
58 memset(grown + *length, 0, new_length - *length);
59 *arena = grown;
60 *length = new_length;
61
62 return AARUF_STATUS_OK;
63}
64
71static bool cd_header_matches_lba(const uint8_t *data, const int64_t lba)
72{
73 for(int i = 0x0C; i <= 0x0E; i++)
74 if((data[i] >> 4) > 9 || (data[i] & 0x0F) > 9) return false;
75
76 const int minute = (data[0x0C] >> 4) * 10 + (data[0x0C] & 0x0F);
77 const int second = (data[0x0D] >> 4) * 10 + (data[0x0D] & 0x0F);
78 const int frame = (data[0x0E] >> 4) * 10 + (data[0x0E] & 0x0F);
79
80 if(second > 59 || frame > 74) return false;
81
82 return (int64_t)minute * 60 * 75 + second * 75 + frame - 150 == lba;
83}
84
152AARU_EXPORT int32_t AARU_CALL aaruf_write_sector(void *context, uint64_t sector_address, bool negative,
153 const uint8_t *data, uint8_t sector_status, uint32_t length)
154{
155 TRACE("Entering aaruf_write_sector(%p, %" PRIu64 ", %d, %p, %u, %u)", context, sector_address, negative, data,
156 sector_status, length);
157
158 // Check context is correct AaruFormat context
159 if(context == NULL)
160 {
161 FATAL("Invalid context");
162
163 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_AARUFORMAT");
165 }
166
167 aaruformat_context *ctx = context;
168
169 // Not a libaaruformat context
170 if(ctx->magic != AARU_MAGIC)
171 {
172 FATAL("Invalid context");
173
174 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_AARUFORMAT");
176 }
177
178 // Check we are writing
179 if(!ctx->is_writing)
180 {
181 FATAL("Trying to write a read-only image");
182
183 TRACE("Exiting aaruf_write_sector() = AARUF_READ_ONLY");
184 return AARUF_READ_ONLY;
185 }
186
187 // Flux-only images carry no DDT and no sector storage.
188 if(ctx->no_user_data_ddt)
189 {
190 FATAL("Cannot write sector to flux-only image (no user-data DDT)");
191
192 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_USER_DATA_NOT_PRESENT");
194 }
195
196 if(negative && sector_address > ctx->user_data_ddt_header.negative)
197 {
198 FATAL("Sector address out of bounds");
199
200 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_SECTOR_OUT_OF_BOUNDS");
202 }
203
204 if(!negative && sector_address > ctx->image_info.Sectors + ctx->user_data_ddt_header.overflow - 1)
205 {
206 FATAL("Sector address out of bounds");
207
208 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_SECTOR_OUT_OF_BOUNDS");
210 }
211
212 if(length > USHRT_MAX)
213 {
214 FATAL("Sector length too large");
215
216 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_INVALID_SECTOR_LENGTH");
218 }
219
220 if(length > ctx->header.biggestSectorSize) ctx->header.biggestSectorSize = (uint16_t)length;
221
222 if(!ctx->rewinded)
223 {
224 // disable checksums on first encounter of decrypted/generable sectors
225 if(sector_status == SectorStatusUnencrypted || sector_status == SectorStatusGenerable)
226 {
227 TRACE("NGCW sector detected, disabling checksums");
228 ctx->rewinded = true;
229
230 if(ctx->calculating_md5) ctx->calculating_md5 = false;
231 if(ctx->calculating_sha1) ctx->calculating_sha1 = false;
232 if(ctx->calculating_sha256) ctx->calculating_sha256 = false;
233 if(ctx->calculating_spamsum) ctx->calculating_spamsum = false;
234 if(ctx->calculating_blake3) ctx->calculating_blake3 = false;
235 }
236 else if(sector_address <= ctx->last_written_block)
237 {
238 if(sector_address == 0 && !ctx->block_zero_written)
239 ctx->block_zero_written = true;
240 else
241 {
242 TRACE("Rewinded");
243 ctx->rewinded = true;
244
245 // Disable MD5 calculation
246 if(ctx->calculating_md5) ctx->calculating_md5 = false;
247 // Disable SHA1 calculation
248 if(ctx->calculating_sha1) ctx->calculating_sha1 = false;
249 // Disable SHA256 calculation
250 if(ctx->calculating_sha256) ctx->calculating_sha256 = false;
251 // Disable SpamSum calculation
252 if(ctx->calculating_spamsum) ctx->calculating_spamsum = false;
253 // Disable BLAKE3 calculation
254 if(ctx->calculating_blake3) ctx->calculating_blake3 = false;
255 }
256 }
257 else
258 ctx->last_written_block = sector_address;
259 }
260
261 // Calculate MD5 on-the-fly if requested and sector is within user sectors (not negative or overflow)
262 if(ctx->calculating_md5 && !negative && sector_address <= ctx->image_info.Sectors && !ctx->writing_long)
263 aaruf_md5_update(&ctx->md5_context, data, length);
264 // Calculate SHA1 on-the-fly if requested and sector is within user sectors (not negative or overflow)
265 if(ctx->calculating_sha1 && !negative && sector_address <= ctx->image_info.Sectors && !ctx->writing_long)
266 aaruf_sha1_update(&ctx->sha1_context, data, length);
267 // Calculate SHA256 on-the-fly if requested and sector is within user sectors (not negative or overflow)
268 if(ctx->calculating_sha256 && !negative && sector_address <= ctx->image_info.Sectors && !ctx->writing_long)
269 aaruf_sha256_update(&ctx->sha256_context, data, length);
270 // Calculate SpamSum on-the-fly if requested and sector is within user sectors (not negative or overflow)
271 if(ctx->calculating_spamsum && !negative && sector_address <= ctx->image_info.Sectors && !ctx->writing_long)
272 aaruf_spamsum_update(ctx->spamsum_context, data, length);
273 // Calculate BLAKE3 on-the-fly if requested and sector is within user sectors (not negative or overflow)
274 if(ctx->calculating_blake3 && !negative && sector_address <= ctx->image_info.Sectors && !ctx->writing_long)
275 blake3_hasher_update(ctx->blake3_context, data, length);
276
277 // Mark checksum block as dirty if any checksums are being calculated
279 ctx->calculating_blake3) &&
280 !negative && sector_address <= ctx->image_info.Sectors && !ctx->writing_long)
281 ctx->dirty_checksum_block = true;
282
283 // PS3 decryption: if caller sends encrypted data marked as "to be stored decrypted"
284 // Checksums have already been computed on the ciphertext above.
285 const uint8_t *write_data = data;
286 uint8_t *decrypted_buffer = NULL;
287
288 if(sector_status == SectorStatusUnencrypted && (ctx->header.mediaType == PS3DVD || ctx->header.mediaType == PS3BD))
289 {
291 {
292 ps3_lazy_init(ctx);
293 ctx->ps3_encryption_initialized = true;
294 }
295
296 if(ctx->ps3_disc_key != NULL && ctx->ps3_plaintext_regions != NULL &&
298 ctx->ps3_plaintext_region_count, sector_address))
299 {
300 decrypted_buffer = (uint8_t *)malloc(length);
301
302 if(decrypted_buffer == NULL)
303 {
304 FATAL("Could not allocate memory for PS3 decryption buffer");
306 }
307
308 memcpy(decrypted_buffer, data, length);
309 ps3_decrypt_sector(ctx->ps3_disc_key, sector_address, decrypted_buffer, length);
310 write_data = decrypted_buffer;
311 }
312 }
313
314 // NGCW: SectorStatusGenerable — set DDT entry only, do not store data in any block
315 if(sector_status == SectorStatusGenerable && (ctx->header.mediaType == GOD || ctx->header.mediaType == WOD))
316 {
317 uint64_t ddt_entry_gen = 0;
318 bool ddt_ok_gen = set_ddt_entry_v2(ctx, sector_address, negative, 0, 0, sector_status, &ddt_entry_gen);
319
320 free(decrypted_buffer);
321
322 if(!ddt_ok_gen)
323 {
324 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_CANNOT_SET_DDT_ENTRY");
326 }
327
328 TRACE("Exiting aaruf_write_sector() = AARUF_STATUS_OK (generable)");
329 return AARUF_STATUS_OK;
330 }
331
332 // Close current block first
333 if(ctx->writing_buffer != NULL &&
334 // When sector size changes or block reaches maximum size
335 (ctx->current_block_header.sectorSize != length ||
337 {
338 TRACE("Closing current block before writing new data");
339 int error = aaruf_close_current_block(ctx);
340
341 if(error != AARUF_STATUS_OK)
342 {
343 FATAL("Error closing current block: %d", error);
344
345 TRACE("Exiting aaruf_write_sector() = %d", error);
346 return error;
347 }
348 }
349
350 uint64_t ddt_entry = 0;
351 bool ddt_ok;
352
353 if(ctx->deduplicate)
354 {
355 // Calculate 64-bit XXH3 hash of the sector (on decrypted data for dedup)
356 TRACE("Hashing sector data for deduplication");
357 uint64_t hash = XXH3_64bits(write_data, length);
358
359 // Check if the hash is already in the map
360 bool existing = lookup_map(ctx->sector_hash_map, hash, &ddt_entry);
361 TRACE("Block does %s exist in deduplication map", existing ? "already" : "not yet");
362
363 ddt_ok = set_ddt_entry_v2(ctx, sector_address, negative, ctx->current_block_offset, ctx->next_block_position,
364 sector_status, &ddt_entry);
365 if(!ddt_ok)
366 {
367 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_CANNOT_SET_DDT_ENTRY");
369 }
370
371 if(existing)
372 {
373 TRACE("Sector exists, so not writing to image");
374 free(decrypted_buffer);
375 TRACE("Exiting aaruf_write_sector() = AARUF_STATUS_OK");
376 return AARUF_STATUS_OK;
377 }
378
379 TRACE("Inserting sector hash into deduplication map, proceeding to write into image as normal");
380 insert_map(ctx->sector_hash_map, hash, ddt_entry);
381 }
382 else
383 ddt_ok = set_ddt_entry_v2(ctx, sector_address, negative, ctx->current_block_offset, ctx->next_block_position,
384 sector_status, &ddt_entry);
385
386 if(!ddt_ok)
387 {
388 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_CANNOT_SET_DDT_ENTRY");
390 }
391
392 // No block set
393 if(ctx->writing_buffer_position == 0)
394 {
395 TRACE("Creating new writing block");
398 ctx->current_block_header.sectorSize = length;
399
400 // We need to save the track type for later compression
401 if(ctx->image_info.MetadataMediaType == OpticalDisc && ctx->track_entries != NULL)
402 {
403 const TrackEntry *track = NULL;
404 for(int i = 0; i < ctx->tracks_header.entries; i++)
405 if(sector_address >= ctx->track_entries[i].start && sector_address <= ctx->track_entries[i].end)
406 {
407 track = &ctx->track_entries[i];
408 break;
409 }
410
411 if(track != NULL)
412 {
413 ctx->current_track_type = track->type;
414
415 if(track->sequence == 0 && track->start == 0 && track->end == 0)
417 }
418 else
420
422 // JaguarCD stores data in audio tracks. FLAC is too inefficient, we need to use LZMA as data.
423 (ctx->image_info.MediaType == JaguarCD && track->session > 1 ||
424 // VideoNow stores video in audio tracks, and LZMA works better too.
428
429 if(ctx->compression_enabled)
430 {
433 else
435 }
436 else
438 }
439 else
440 {
442 if(ctx->compression_enabled)
444 else
446 }
447
448 uint32_t max_buffer_size =
450 TRACE("Setting max buffer size to %u bytes", max_buffer_size);
451
452 TRACE("Allocating memory for writing buffer");
453 ctx->writing_buffer = (uint8_t *)calloc(1, max_buffer_size);
454 if(ctx->writing_buffer == NULL)
455 {
456 FATAL("Could not allocate memory");
457
458 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
460 }
461 }
462
463 TRACE("Copying data to writing buffer at position %zu", ctx->writing_buffer_position);
464 memcpy(ctx->writing_buffer + ctx->writing_buffer_position, write_data, length);
465 TRACE("Advancing writing buffer position to %zu", ctx->writing_buffer_position + length);
466 ctx->writing_buffer_position += length;
467 TRACE("Advancing current block offset to %zu", ctx->current_block_offset + 1);
469
470 free(decrypted_buffer);
471
472 TRACE("Exiting aaruf_write_sector() = AARUF_STATUS_OK");
473 return AARUF_STATUS_OK;
474}
475
682AARU_EXPORT int32_t AARU_CALL aaruf_write_sector_long(void *context, uint64_t sector_address, bool negative,
683 const uint8_t *data, uint8_t sector_status, uint32_t length)
684{
685 TRACE("Entering aaruf_write_sector_long(%p, %" PRIu64 ", %d, %p, %u, %u)", context, sector_address, negative, data,
686 sector_status, length);
687
688 // Check context is correct AaruFormat context
689 if(context == NULL)
690 {
691 FATAL("Invalid context");
692
693 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_AARUFORMAT");
695 }
696
697 aaruformat_context *ctx = context;
698
699 // Not a libaaruformat context
700 if(ctx->magic != AARU_MAGIC)
701 {
702 FATAL("Invalid context");
703
704 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_AARUFORMAT");
706 }
707
708 // Check we are writing
709 if(!ctx->is_writing)
710 {
711 FATAL("Trying to write a read-only image");
712
713 TRACE("Exiting aaruf_write_sector() = AARUF_READ_ONLY");
714 return AARUF_READ_ONLY;
715 }
716
717 // Flux-only images carry no DDT and no sector storage.
718 if(ctx->no_user_data_ddt)
719 {
720 FATAL("Cannot write long sector to flux-only image (no user-data DDT)");
721
722 TRACE("Exiting aaruf_write_sector_long() = AARUF_ERROR_USER_DATA_NOT_PRESENT");
724 }
725
726 if(negative && sector_address > ctx->user_data_ddt_header.negative)
727 {
728 FATAL("Sector address out of bounds");
729
730 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_SECTOR_OUT_OF_BOUNDS");
732 }
733
734 if(!negative && sector_address > ctx->image_info.Sectors + ctx->user_data_ddt_header.overflow - 1)
735 {
736 FATAL("Sector address out of bounds");
737
738 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_SECTOR_OUT_OF_BOUNDS");
740 }
741
742 switch(ctx->image_info.MetadataMediaType)
743 {
744 case OpticalDisc:
745 {
746 TrackEntry track = {0};
747
748 for(int i = 0; i < ctx->tracks_header.entries; i++)
749 if(sector_address >= ctx->track_entries[i].start && sector_address <= ctx->track_entries[i].end)
750 {
751 track = ctx->track_entries[i];
752 break;
753 }
754
755 if(track.sequence == 0 && track.start == 0 && track.end == 0) track.type = kTrackTypeData;
756
757 uint64_t corrected_sector_address = sector_address;
758
759 // Calculate positive or negative sector
760 if(negative)
761 corrected_sector_address = ctx->user_data_ddt_header.negative - sector_address;
762 else
763 corrected_sector_address += ctx->user_data_ddt_header.negative;
764
765 uint64_t total_sectors =
767
768 // LBA as encoded in a CD sector header (lead-in sectors are negative)
769 const int64_t signed_lba = negative ? -(int64_t)sector_address : (int64_t)sector_address;
770
771 // DVD long sector
772 if(length == 2064 && (ctx->image_info.MediaType == DVDROM || ctx->image_info.MediaType == PS2DVD ||
773 ctx->image_info.MediaType == SACD || ctx->image_info.MediaType == PS3DVD ||
774 ctx->image_info.MediaType == DVDR || ctx->image_info.MediaType == DVDRW ||
779 ctx->image_info.MediaType == Nuon))
780 {
781 if(ctx->sector_id == NULL) ctx->sector_id = calloc(1, 4 * total_sectors);
782 if(ctx->sector_ied == NULL) ctx->sector_ied = calloc(1, 2 * total_sectors);
783 if(ctx->sector_cpr_mai == NULL) ctx->sector_cpr_mai = calloc(1, 6 * total_sectors);
784 if(ctx->sector_edc == NULL) ctx->sector_edc = calloc(1, 4 * total_sectors);
785
786 memcpy(ctx->sector_id + corrected_sector_address * 4, data, 4);
787 memcpy(ctx->sector_ied + corrected_sector_address * 2, data + 4, 2);
788 memcpy(ctx->sector_cpr_mai + corrected_sector_address * 6, data + 6, 6);
789 memcpy(ctx->sector_edc + corrected_sector_address * 4, data + 2060, 4);
790
791 return aaruf_write_sector(context, sector_address, negative, data + 12, sector_status, 2048);
792 }
793
794 // Nintendo DVD long sector
795 if(length == 2064 && ctx->image_info.MediaType == GOD || ctx->image_info.MediaType == WOD)
796 {
797 if(ctx->sector_id == NULL) ctx->sector_id = calloc(1, 4 * total_sectors);
798 if(ctx->sector_ied == NULL) ctx->sector_ied = calloc(1, 2 * total_sectors);
799 if(ctx->sector_cpr_mai == NULL) ctx->sector_cpr_mai = calloc(1, 6 * total_sectors);
800 if(ctx->sector_edc == NULL) ctx->sector_edc = calloc(1, 4 * total_sectors);
801
802 memcpy(ctx->sector_id + corrected_sector_address * 4, data, 4);
803 memcpy(ctx->sector_ied + corrected_sector_address * 2, data + 4, 2);
804 memcpy(ctx->sector_cpr_mai + corrected_sector_address * 6, data + 2054, 6);
805 memcpy(ctx->sector_edc + corrected_sector_address * 4, data + 2060, 4);
806
807 return aaruf_write_sector(context, sector_address, negative, data + 6, sector_status, 2048);
808 }
809
810 // Blu-ray long sector
811 if(length == 2052 && (ctx->image_info.MediaType == BDROM || ctx->image_info.MediaType == BDR ||
812 ctx->image_info.MediaType == BDRE || ctx->image_info.MediaType == BDRXL ||
814 ctx->image_info.MediaType == XGD4 || ctx->image_info.MediaType == PS3BD ||
816 {
817 if(ctx->sector_edc == NULL) ctx->sector_edc = calloc(1, 4 * total_sectors);
818
819 memcpy(ctx->sector_edc + corrected_sector_address * 4, data + 2048, 4);
820
821 return aaruf_write_sector(context, sector_address, negative, data, sector_status, 2048);
822 }
823
824 if(length != 2352)
825 {
826 FATAL("Incorrect sector size");
827 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_INCORRECT_DATA_SIZE");
829 }
830
831 ctx->writing_long = true;
832
833 if(!ctx->rewinded)
834 {
835 if(sector_address <= ctx->last_written_block)
836 {
837 if(sector_address == 0 && !ctx->block_zero_written)
838 ctx->block_zero_written = true;
839 else
840 {
841 TRACE("Rewinded");
842 ctx->rewinded = true;
843
844 // Disable MD5 calculation
845 if(ctx->calculating_md5) ctx->calculating_md5 = false;
846 // Disable SHA1 calculation
847 if(ctx->calculating_sha1) ctx->calculating_sha1 = false;
848 // Disable SHA256 calculation
849 if(ctx->calculating_sha256) ctx->calculating_sha256 = false;
850 // Disable SpamSum calculation
851 if(ctx->calculating_spamsum) ctx->calculating_spamsum = false;
852 // Disable BLAKE3 calculation
853 if(ctx->calculating_blake3) ctx->calculating_blake3 = false;
854 }
855 }
856 else
857 ctx->last_written_block = sector_address;
858 }
859
860 // Calculate MD5 on-the-fly if requested and sector is within user sectors (not negative or overflow)
861 if(ctx->calculating_md5 && !negative && sector_address <= ctx->image_info.Sectors)
862 aaruf_md5_update(&ctx->md5_context, data, length);
863 // Calculate SHA1 on-the-fly if requested and sector is within user sectors (not negative or overflow)
864 if(ctx->calculating_sha1 && !negative && sector_address <= ctx->image_info.Sectors)
865 aaruf_sha1_update(&ctx->sha1_context, data, length);
866 // Calculate SHA256 on-the-fly if requested and sector is within user sectors (not negative or overflow)
867 if(ctx->calculating_sha256 && !negative && sector_address <= ctx->image_info.Sectors)
868 aaruf_sha256_update(&ctx->sha256_context, data, length);
869 // Calculate SpamSum on-the-fly if requested and sector is within user sectors (not negative or overflow)
870 if(ctx->calculating_spamsum && !negative && sector_address <= ctx->image_info.Sectors)
871 aaruf_spamsum_update(ctx->spamsum_context, data, length);
872 // Calculate BLAKE3 on-the-fly if requested and sector is within user sectors (not negative or overflow)
873 if(ctx->calculating_blake3 && !negative && sector_address <= ctx->image_info.Sectors)
874 blake3_hasher_update(ctx->blake3_context, data, length);
875
876 bool prefix_correct;
877
878 // Split raw cd sector data in prefix (sync, header), user data and suffix (edc, ecc p, ecc q)
879 switch(track.type)
880 {
881 case kTrackTypeAudio:
882 case kTrackTypeData:
883 return aaruf_write_sector(context, sector_address, negative, data, sector_status, length);
885
886 // If we do not have a DDT V2 for sector prefix, create one
887 if(ctx->sector_prefix_ddt2 == NULL)
888 {
889 ctx->sector_prefix_ddt2 =
890 calloc(1, sizeof(uint64_t) * (ctx->user_data_ddt_header.negative + ctx->image_info.Sectors +
892
893 if(ctx->sector_prefix_ddt2 == NULL)
894 {
895 FATAL("Could not allocate memory for CD sector prefix DDT");
896
897 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
899 }
900 }
901
902 // If we do not have a DDT V2 for sector suffix, create one
903 if(ctx->sector_suffix_ddt2 == NULL)
904 {
905 ctx->sector_suffix_ddt2 =
906 calloc(1, sizeof(uint64_t) * (ctx->user_data_ddt_header.negative + ctx->image_info.Sectors +
908
909 if(ctx->sector_suffix_ddt2 == NULL)
910 {
911 FATAL("Could not allocate memory for CD sector prefix DDT");
912
913 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
915 }
916 }
917
918 if(ctx->sector_prefix == NULL)
919 {
922 ctx->sector_prefix = calloc(1, ctx->sector_prefix_length);
923
924 if(ctx->sector_prefix == NULL)
925 {
926 FATAL("Could not allocate memory for CD sector prefix buffer");
927
928 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
930 }
931 }
932
933 if(ctx->sector_suffix == NULL)
934 {
938 ctx->sector_suffix = calloc(1, ctx->sector_suffix_length);
939
940 if(ctx->sector_suffix == NULL)
941 {
942 FATAL("Could not allocate memory for CD sector suffix buffer");
943
944 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
946 }
947 }
948
949 bool empty = true;
950
951 for(int i = 0; i < length; i++)
952 if(data[i] != 0)
953 {
954 empty = false;
955 break;
956 }
957
958 if(empty)
959 {
960 ctx->sector_prefix_ddt2[corrected_sector_address] = (uint64_t)SectorStatusNotDumped << 60;
961 ctx->sector_suffix_ddt2[corrected_sector_address] = (uint64_t)SectorStatusNotDumped << 60;
962 ctx->dirty_sector_prefix_ddt = true; // Mark prefix DDT as dirty
963 ctx->dirty_sector_suffix_ddt = true; // Mark suffix DDT as dirty
964 return aaruf_write_sector(context, sector_address, negative, data + 16, SectorStatusNotDumped,
965 2048);
966 }
967
968 prefix_correct = true;
969
970 if(data[0x00] != 0x00 || data[0x01] != 0xFF || data[0x02] != 0xFF || data[0x03] != 0xFF ||
971 data[0x04] != 0xFF || data[0x05] != 0xFF || data[0x06] != 0xFF || data[0x07] != 0xFF ||
972 data[0x08] != 0xFF || data[0x09] != 0xFF || data[0x0A] != 0xFF || data[0x0B] != 0x00 ||
973 data[0x0F] != 0x01)
974 prefix_correct = false;
975
976 if(prefix_correct) prefix_correct = cd_header_matches_lba(data, signed_lba);
977
978 if(prefix_correct)
979 ctx->sector_prefix_ddt2[corrected_sector_address] = (uint64_t)SectorStatusMode1Correct << 60;
980 else
981 {
982 // Copy CD prefix from data buffer to prefix buffer
984 16 * total_sectors) != AARUF_STATUS_OK)
985 {
986 FATAL("Could not allocate memory for CD sector prefix buffer");
987
988 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
990 }
991
992 memcpy(ctx->sector_prefix + ctx->sector_prefix_offset, data, 16);
993 ctx->sector_prefix_ddt2[corrected_sector_address] = (uint64_t)(ctx->sector_prefix_offset / 16);
994 ctx->sector_prefix_ddt2[corrected_sector_address] |= (uint64_t)SectorStatusErrored << 60;
995 ctx->sector_prefix_offset += 16;
996 ctx->dirty_sector_prefix_block = true; // Mark prefix block as dirty
997 }
998 ctx->dirty_sector_prefix_ddt = true; // Mark prefix DDT as dirty
999
1000 const bool suffix_correct = aaruf_ecc_cd_is_suffix_correct(ctx->ecc_cd_context, data);
1001
1002 if(suffix_correct)
1003 ctx->sector_suffix_ddt2[corrected_sector_address] = (uint64_t)SectorStatusMode1Correct << 60;
1004 else
1005 {
1006 // Copy CD suffix from data buffer to suffix buffer
1008 288 * total_sectors) != AARUF_STATUS_OK)
1009 {
1010 FATAL("Could not allocate memory for CD sector suffix buffer");
1011
1012 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
1014 }
1015
1016 memcpy(ctx->sector_suffix + ctx->sector_suffix_offset, data + 2064, 288);
1017 ctx->sector_suffix_ddt2[corrected_sector_address] = (uint64_t)(ctx->sector_suffix_offset / 288);
1018 ctx->sector_suffix_ddt2[corrected_sector_address] |= (uint64_t)SectorStatusErrored << 60;
1019 ctx->sector_suffix_offset += 288;
1020 ctx->dirty_sector_suffix_block = true; // Mark suffix block as dirty
1021 }
1022 ctx->dirty_sector_suffix_ddt = true; // Mark suffix DDT as dirty
1023
1024 return aaruf_write_sector(context, sector_address, negative, data + 16,
1025 prefix_correct && suffix_correct ? SectorStatusMode1Correct
1027 2048);
1031 // If we do not have a DDT V2 for sector prefix, create one
1032 if(ctx->sector_prefix_ddt2 == NULL)
1033 {
1034 ctx->sector_prefix_ddt2 =
1035 calloc(1, sizeof(uint64_t) * (ctx->user_data_ddt_header.negative + ctx->image_info.Sectors +
1037
1038 if(ctx->sector_prefix_ddt2 == NULL)
1039 {
1040 FATAL("Could not allocate memory for CD sector prefix DDT");
1041
1042 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
1044 }
1045 }
1046
1047 // If we do not have a DDT V2 for sector suffix, create one
1048 if(ctx->sector_suffix_ddt2 == NULL)
1049 {
1050 ctx->sector_suffix_ddt2 =
1051 calloc(1, sizeof(uint64_t) * (ctx->user_data_ddt_header.negative + ctx->image_info.Sectors +
1053
1054 if(ctx->sector_suffix_ddt2 == NULL)
1055 {
1056 FATAL("Could not allocate memory for CD sector prefix DDT");
1057
1058 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
1060 }
1061 }
1062
1063 if(ctx->sector_prefix == NULL)
1064 {
1067 ctx->sector_prefix = calloc(1, ctx->sector_prefix_length);
1068
1069 if(ctx->sector_prefix == NULL)
1070 {
1071 FATAL("Could not allocate memory for CD sector prefix buffer");
1072
1073 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
1075 }
1076 }
1077
1078 if(ctx->sector_suffix == NULL)
1079 {
1081 288 * (ctx->user_data_ddt_header.negative + ctx->image_info.Sectors +
1083 ctx->sector_suffix = calloc(1, ctx->sector_suffix_length);
1084
1085 if(ctx->sector_suffix == NULL)
1086 {
1087 FATAL("Could not allocate memory for CD sector suffix buffer");
1088
1089 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
1091 }
1092 }
1093
1094 empty = true;
1095
1096 for(int i = 0; i < length; i++)
1097 if(data[i] != 0)
1098 {
1099 empty = false;
1100 break;
1101 }
1102
1103 if(empty)
1104 {
1105 ctx->sector_prefix_ddt2[corrected_sector_address] = (uint64_t)SectorStatusNotDumped << 60;
1106 ctx->sector_suffix_ddt2[corrected_sector_address] = (uint64_t)SectorStatusNotDumped << 60;
1107 ctx->dirty_sector_prefix_ddt = true; // Mark prefix DDT as dirty
1108 ctx->dirty_sector_suffix_ddt = true; // Mark suffix DDT as dirty
1109 return aaruf_write_sector(context, sector_address, negative, data + 16, SectorStatusNotDumped,
1110 2328);
1111 }
1112
1113 const bool form2 = (data[18] & 0x20) == 0x20 || (data[22] & 0x20) == 0x20;
1114
1115 prefix_correct = true;
1116
1117 if(data[0x00] != 0x00 || data[0x01] != 0xFF || data[0x02] != 0xFF || data[0x03] != 0xFF ||
1118 data[0x04] != 0xFF || data[0x05] != 0xFF || data[0x06] != 0xFF || data[0x07] != 0xFF ||
1119 data[0x08] != 0xFF || data[0x09] != 0xFF || data[0x0A] != 0xFF || data[0x0B] != 0x00 ||
1120 data[0x0F] != 0x02)
1121 prefix_correct = false;
1122
1123 if(prefix_correct) prefix_correct = cd_header_matches_lba(data, signed_lba);
1124
1125 if(prefix_correct)
1126 ctx->sector_prefix_ddt2[corrected_sector_address] =
1127 (uint64_t)(form2 ? SectorStatusMode2Form2Ok : SectorStatusMode2Form1Ok) << 60;
1128 else
1129 {
1130 // Copy CD prefix from data buffer to prefix buffer
1132 16 * total_sectors) != AARUF_STATUS_OK)
1133 {
1134 FATAL("Could not allocate memory for CD sector prefix buffer");
1135
1136 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
1138 }
1139
1140 memcpy(ctx->sector_prefix + ctx->sector_prefix_offset, data, 16);
1141 ctx->sector_prefix_ddt2[corrected_sector_address] = (uint64_t)(ctx->sector_prefix_offset / 16);
1142 ctx->sector_prefix_ddt2[corrected_sector_address] |= (uint64_t)SectorStatusErrored << 60;
1143 ctx->sector_prefix_offset += 16;
1144 ctx->dirty_sector_prefix_block = true; // Mark prefix block as dirty
1145 }
1146 ctx->dirty_sector_prefix_ddt = true; // Mark prefix DDT as dirty
1147
1148 if(ctx->mode2_subheaders == NULL)
1149 {
1150 ctx->mode2_subheaders =
1151 calloc(1, 8 * (ctx->user_data_ddt_header.negative + ctx->image_info.Sectors +
1153
1154 if(ctx->mode2_subheaders == NULL)
1155 {
1156 FATAL("Could not allocate memory for CD mode 2 subheader buffer");
1157
1158 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
1160 }
1161 }
1162
1163 if(form2)
1164 {
1165 const uint32_t computed_edc = aaruf_edc_cd_compute(ctx->ecc_cd_context, 0, data, 0x91C, 0x10);
1166 uint32_t edc = 0;
1167 memcpy(&edc, data + 0x92C, sizeof(edc));
1168 const bool correct_edc = computed_edc == edc;
1169
1170 if(correct_edc)
1171 ctx->sector_suffix_ddt2[corrected_sector_address] = (uint64_t)SectorStatusMode2Form2Ok
1172 << 60;
1173 else if(edc == 0)
1174 ctx->sector_suffix_ddt2[corrected_sector_address] = (uint64_t)SectorStatusMode2Form2NoCrc
1175 << 60;
1176 else
1177 {
1178 // Copy CD suffix from data buffer to suffix buffer
1180 ctx->sector_suffix_offset + 288, 288 * total_sectors) != AARUF_STATUS_OK)
1181 {
1182 FATAL("Could not allocate memory for CD sector suffix buffer");
1183
1184 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
1186 }
1187
1188 memset(ctx->sector_suffix + ctx->sector_suffix_offset, 0, 288);
1189 memcpy(ctx->sector_suffix + ctx->sector_suffix_offset, data + 2348, 4);
1190 ctx->sector_suffix_ddt2[corrected_sector_address] =
1191 (uint64_t)(ctx->sector_suffix_offset / 288);
1192 ctx->sector_suffix_ddt2[corrected_sector_address] |= (uint64_t)SectorStatusErrored << 60;
1193 ctx->sector_suffix_offset += 288;
1194 ctx->dirty_sector_suffix_block = true; // Mark suffix block as dirty
1195 }
1196
1197 // Copy subheader from data buffer to subheader buffer
1198 memcpy(ctx->mode2_subheaders + corrected_sector_address * 8, data + 0x10, 8);
1199 ctx->dirty_sector_prefix_ddt = true;
1200 ctx->dirty_sector_suffix_ddt = true;
1201 ctx->dirty_mode2_subheaders_block = true;
1202 return aaruf_write_sector(context, sector_address, negative, data + 24,
1204 : correct_edc ? SectorStatusMode2Form2Ok
1206 2324);
1207 }
1208
1209 const bool correct_ecc = aaruf_ecc_cd_is_suffix_correct_mode2(ctx->ecc_cd_context, data);
1210 const uint32_t computed_edc = aaruf_edc_cd_compute(ctx->ecc_cd_context, 0, data, 0x808, 0x10);
1211 uint32_t edc = 0;
1212 memcpy(&edc, data + 0x818, sizeof(edc));
1213 const bool correct_edc = computed_edc == edc;
1214
1215 if(correct_ecc && correct_edc)
1216 ctx->sector_suffix_ddt2[corrected_sector_address] = (uint64_t)SectorStatusMode2Form1Ok << 60;
1217 else
1218 {
1219 // Copy CD suffix from data buffer to suffix buffer
1221 288 * total_sectors) != AARUF_STATUS_OK)
1222 {
1223 FATAL("Could not allocate memory for CD sector suffix buffer");
1224
1225 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
1227 }
1228
1229 memset(ctx->sector_suffix + ctx->sector_suffix_offset, 0, 288);
1230 memcpy(ctx->sector_suffix + ctx->sector_suffix_offset, data + 2072, 280);
1231 ctx->sector_suffix_ddt2[corrected_sector_address] = (uint64_t)(ctx->sector_suffix_offset / 288);
1232 ctx->sector_suffix_ddt2[corrected_sector_address] |= (uint64_t)SectorStatusErrored << 60;
1233 ctx->sector_suffix_offset += 288;
1234 ctx->dirty_sector_suffix_block = true; // Mark suffix block as dirty
1235 }
1236
1237 // Copy subheader from data buffer to subheader buffer
1238 memcpy(ctx->mode2_subheaders + corrected_sector_address * 8, data + 0x10, 8);
1239 ctx->dirty_sector_prefix_ddt = true;
1240 ctx->dirty_sector_suffix_ddt = true;
1241 ctx->dirty_mode2_subheaders_block = true;
1242 return aaruf_write_sector(
1243 context, sector_address, negative, data + 24,
1244 correct_edc && correct_ecc ? SectorStatusMode2Form1Ok : SectorStatusErrored, 2048);
1245 }
1246
1247 break;
1248 }
1249 case BlockMedia:
1250 switch(ctx->image_info.MediaType)
1251 {
1252 case AppleFileWare:
1253 case AppleProfile:
1254 case AppleSonyDS:
1255 case AppleSonySS:
1256 case AppleWidget:
1257 case PriamDataTower:
1258 {
1259 uint8_t *newTag;
1260 int newTagSize = 0;
1261
1262 switch(length - 512)
1263 {
1264 // Sony tag
1265 case 12:
1266 {
1267 const sony_tag decoded_sony_tag = bytes_to_sony_tag(data + 512);
1268
1270 {
1271 const profile_tag decoded_profile_tag = sony_tag_to_profile(decoded_sony_tag);
1272 newTag = profile_tag_to_bytes(decoded_profile_tag);
1273 newTagSize = 20;
1274 }
1275 else if(ctx->image_info.MediaType == PriamDataTower)
1276 {
1277 const priam_tag decoded_priam_tag = sony_tag_to_priam(decoded_sony_tag);
1278 newTag = priam_tag_to_bytes(decoded_priam_tag);
1279 newTagSize = 24;
1280 }
1281 else if(ctx->image_info.MediaType == AppleSonyDS ||
1283 {
1284 newTag = malloc(12);
1285 memcpy(newTag, data + 512, 12);
1286 newTagSize = 12;
1287 }
1288 break;
1289 }
1290 // Profile tag
1291 case 20:
1292 {
1293 const profile_tag decoded_profile_tag = bytes_to_profile_tag(data + 512);
1294
1296 {
1297 newTag = malloc(20);
1298 memcpy(newTag, data + 512, 20);
1299 newTagSize = 20;
1300 }
1301 else if(ctx->image_info.MediaType == PriamDataTower)
1302 {
1303 const priam_tag decoded_priam_tag = profile_tag_to_priam(decoded_profile_tag);
1304 newTag = priam_tag_to_bytes(decoded_priam_tag);
1305 newTagSize = 24;
1306 }
1307 else if(ctx->image_info.MediaType == AppleSonyDS ||
1309 {
1310 const sony_tag decoded_sony_tag = profile_tag_to_sony(decoded_profile_tag);
1311 newTag = sony_tag_to_bytes(decoded_sony_tag);
1312 newTagSize = 12;
1313 }
1314 break;
1315 }
1316 // Priam tag
1317 case 24:
1318 {
1319 const priam_tag decoded_priam_tag = bytes_to_priam_tag(data + 512);
1321 {
1322 const profile_tag decoded_profile_tag = priam_tag_to_profile(decoded_priam_tag);
1323 newTag = profile_tag_to_bytes(decoded_profile_tag);
1324 newTagSize = 20;
1325 }
1326 else if(ctx->image_info.MediaType == PriamDataTower)
1327 {
1328 newTag = malloc(24);
1329 memcpy(newTag, data + 512, 24);
1330 newTagSize = 24;
1331 }
1332 else if(ctx->image_info.MediaType == AppleSonyDS ||
1334 {
1335 const sony_tag decoded_sony_tag = priam_tag_to_sony(decoded_priam_tag);
1336 newTag = sony_tag_to_bytes(decoded_sony_tag);
1337 newTagSize = 12;
1338 }
1339 break;
1340 }
1341 case 0:
1342 newTagSize = 0;
1343 break;
1344 default:
1345 FATAL("Incorrect sector size");
1346 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_INCORRECT_DATA_SIZE");
1348 }
1349
1350 if(newTagSize == 0)
1351 return aaruf_write_sector(context, sector_address, negative, data, sector_status, 512);
1352
1353 if(ctx->sector_subchannel == NULL)
1354 {
1355 ctx->sector_subchannel =
1356 calloc(1, newTagSize * (ctx->image_info.Sectors + ctx->user_data_ddt_header.overflow));
1357
1358 if(ctx->sector_subchannel == NULL)
1359 {
1360 FATAL("Could not allocate memory for sector subchannel DDT");
1361
1362 free(newTag);
1363
1364 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
1366 }
1367 }
1368
1369 memcpy(ctx->sector_subchannel + sector_address * newTagSize, newTag, newTagSize);
1370 ctx->dirty_sector_subchannel_block = true; // Mark subchannel block as dirty
1371 free(newTag);
1372
1373 return aaruf_write_sector(context, sector_address, negative, data, sector_status, 512);
1374 }
1375 default:
1377 }
1378 default:
1379 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_INCORRECT_MEDIA_TYPE");
1381 }
1382
1383 // Fallback return when media type branch does not produce a value (satisfy non-void contract)
1385}
1386
1570{
1571 // Not a libaaruformat context
1572 if(ctx->magic != AARU_MAGIC) return AARUF_ERROR_NOT_AARUFORMAT;
1573
1574 // Check we are writing
1575 if(!ctx->is_writing) return AARUF_READ_ONLY;
1576
1578
1579 TRACE("Initializing CRC64 context");
1581 TRACE("Updating CRC64");
1584
1585 uint8_t lzma_properties[LZMA_PROPERTIES_LENGTH] = {0};
1586 uint8_t *cmp_buffer = NULL;
1587
1589 {
1590 case kCompressionNone:
1591 break;
1592 case kCompressionFlac:
1593 cmp_buffer = malloc(ctx->current_block_header.length * 2);
1594 if(cmp_buffer == NULL)
1595 {
1596 FATAL("Could not allocate buffer for compressed data");
1598 }
1599 const uint32_t current_samples = ctx->current_block_offset * SAMPLES_PER_SECTOR;
1600 uint32_t flac_block_size = ctx->current_block_offset * SAMPLES_PER_SECTOR;
1601
1602 if(flac_block_size > MAX_FLAKE_BLOCK) flac_block_size = MAX_FLAKE_BLOCK;
1603 if(flac_block_size < MIN_FLAKE_BLOCK) flac_block_size = MIN_FLAKE_BLOCK;
1604
1605 const long remaining = current_samples % flac_block_size;
1606
1607 // Fill FLAC block
1608 if(remaining != 0)
1609 for(int r = 0; r < remaining * 4; r++) ctx->writing_buffer[ctx->writing_buffer_position + r] = 0;
1610
1612 cmp_buffer, ctx->current_block_header.length * 2, ctx->writing_buffer, ctx->current_block_header.length,
1613 flac_block_size, true, false, "hamming", 12, 15, true, false, 0, 8, "Aaru", 4);
1614
1616 {
1618 free(cmp_buffer);
1619 }
1620
1621 break;
1622 case kCompressionLzma:
1623 cmp_buffer = malloc(ctx->current_block_header.length * 2);
1624 if(cmp_buffer == NULL)
1625 {
1626 FATAL("Could not allocate buffer for compressed data");
1628 }
1629
1630 size_t dst_size = ctx->current_block_header.length * 2;
1631 size_t props_size = LZMA_PROPERTIES_LENGTH;
1632 aaruf_lzma_encode_buffer(cmp_buffer, &dst_size, ctx->writing_buffer, ctx->current_block_header.length,
1633 lzma_properties, &props_size, 9, ctx->lzma_dict_size, 4, 0, 2, 273, LZMA_THREADS(ctx));
1634
1635 ctx->current_block_header.cmpLength = (uint32_t)dst_size;
1636
1638 {
1640 free(cmp_buffer);
1641 }
1642
1643 break;
1644 case kCompressionZstd:
1645 cmp_buffer = malloc(ctx->current_block_header.length * 2);
1646 if(cmp_buffer == NULL)
1647 {
1648 FATAL("Could not allocate buffer for compressed data");
1650 }
1651
1652 size_t zstd_dst_size =
1655
1656 if(zstd_dst_size == 0)
1657 {
1659 free(cmp_buffer);
1660 cmp_buffer = NULL;
1661 }
1662 else
1663 {
1664 ctx->current_block_header.cmpLength = (uint32_t)zstd_dst_size;
1665
1667 {
1669 free(cmp_buffer);
1670 cmp_buffer = NULL;
1671 }
1672 else
1673 ctx->has_zstd_blocks = true;
1674 }
1675
1676 break;
1677 default:
1678 FATAL("Invalid compression type");
1680 }
1681
1683 {
1686 }
1687 else
1689
1692
1693 // Add to index
1694 TRACE("Adding block to index");
1695 IndexEntry index_entry;
1696 index_entry.blockType = DataBlock;
1697 index_entry.dataType = kDataTypeUserData;
1698 index_entry.offset = ctx->next_block_position;
1699
1700 utarray_push_back(ctx->index_entries, &index_entry);
1701 ctx->dirty_index_block = true; // Mark index block as dirty
1702 TRACE("Block added to index at offset %" PRIu64, index_entry.offset);
1703
1704 // Write block header to file
1705
1706 // Move to expected block position
1708
1709 // Write block header
1710 if(fwrite(&ctx->current_block_header, sizeof(BlockHeader), 1, ctx->imageStream) != 1)
1712
1713 // Write block data
1715 fwrite(lzma_properties, LZMA_PROPERTIES_LENGTH, 1, ctx->imageStream) != 1)
1716 {
1717 free(cmp_buffer);
1719 }
1720
1722 {
1723 if(fwrite(ctx->writing_buffer, ctx->current_block_header.length, 1, ctx->imageStream) != 1)
1725 }
1726 else
1727 {
1728 if(fwrite(cmp_buffer, ctx->current_block_header.cmpLength, 1, ctx->imageStream) != 1)
1729 {
1730 free(cmp_buffer);
1732 }
1733 }
1734
1735 /* --- Erasure coding parity accumulation ---
1736 * Call BEFORE freeing cmp_buffer / writing_buffer.
1737 * For LZMA: cmpLength already includes LZMA_PROPERTIES_LENGTH, but the properties
1738 * were written separately. We pass them as lzma_props parameter.
1739 * For non-LZMA compressed: cmp_buffer holds all payload.
1740 * For uncompressed: writing_buffer holds payload. */
1741 if(ctx->ec_enabled)
1742 {
1743 const uint8_t *ec_payload;
1744 uint32_t ec_payload_size;
1745 const uint8_t *ec_lzma_props = NULL;
1746
1748 {
1749 ec_payload = ctx->writing_buffer;
1750 ec_payload_size = ctx->current_block_header.length;
1751 }
1753 {
1754 ec_payload = cmp_buffer;
1755 ec_payload_size = ctx->current_block_header.cmpLength - LZMA_PROPERTIES_LENGTH;
1756 ec_lzma_props = lzma_properties;
1757 }
1758 else
1759 {
1760 ec_payload = cmp_buffer;
1761 ec_payload_size = ctx->current_block_header.cmpLength;
1762 }
1763
1764 ec_accumulate_data_block(ctx, &ctx->current_block_header, ec_lzma_props,
1765 ec_payload, ec_payload_size, index_entry.offset);
1766 }
1767
1768 /* Free compressed buffer (if compression was used) */
1770 free(cmp_buffer);
1771
1772 // Update nextBlockPosition to point to the next available aligned position
1773 const uint64_t block_total_size = sizeof(BlockHeader) + ctx->current_block_header.cmpLength;
1774 const uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
1775 ctx->next_block_position = ctx->next_block_position + block_total_size + alignment_mask & ~alignment_mask;
1776 TRACE("Updated nextBlockPosition to %" PRIu64, ctx->next_block_position);
1777
1778 // Clear values
1779 free(ctx->writing_buffer);
1780 ctx->writing_buffer = NULL;
1781 ctx->current_block_offset = 0;
1782 memset(&ctx->current_block_header, 0, sizeof(BlockHeader));
1784 ctx->writing_buffer_position = 0;
1785
1786 return AARUF_STATUS_OK;
1787}
1788
2036AARU_EXPORT int32_t AARU_CALL aaruf_write_media_tag(void *context, const uint8_t *data, const int32_t type,
2037 const uint32_t length)
2038{
2039 TRACE("Entering aaruf_write_media_tag(%p, %p, %d, %d)", context, data, type, length);
2040
2041 // Check context is correct AaruFormat context
2042 if(context == NULL)
2043 {
2044 FATAL("Invalid context");
2045
2046 TRACE("Exiting aaruf_write_media_tag() = AARUF_ERROR_NOT_AARUFORMAT");
2048 }
2049
2050 aaruformat_context *ctx = context;
2051
2052 // Not a libaaruformat context
2053 if(ctx->magic != AARU_MAGIC)
2054 {
2055 FATAL("Invalid context");
2056
2057 TRACE("Exiting aaruf_write_media_tag() = AARUF_ERROR_NOT_AARUFORMAT");
2059 }
2060
2061 // Check we are writing
2062 if(!ctx->is_writing)
2063 {
2064 FATAL("Trying to write a read-only image");
2065
2066 TRACE("Exiting aaruf_write_media_tag() = AARUF_READ_ONLY");
2067 return AARUF_READ_ONLY;
2068 }
2069
2070 if(data == NULL || length == 0)
2071 {
2072 FATAL("Invalid data or length");
2074 }
2075
2076 uint8_t *new_data = malloc(length);
2077
2078 if(new_data == NULL)
2079 {
2080 FATAL("Could not allocate memory for media tag");
2082 }
2083 memcpy(new_data, data, length);
2084
2085 mediaTagEntry *media_tag = malloc(sizeof(mediaTagEntry));
2086 mediaTagEntry *old_media_tag = NULL;
2087
2088 if(media_tag == NULL)
2089 {
2090 TRACE("Cannot allocate memory for media tag entry.");
2091 free(new_data);
2093 }
2094
2095 memset(media_tag, 0, sizeof(mediaTagEntry));
2096
2097 media_tag->type = type;
2098 media_tag->data = new_data;
2099 media_tag->length = length;
2100
2101 HASH_REPLACE_INT(ctx->mediaTags, type, media_tag, old_media_tag);
2102
2103 if(old_media_tag != NULL)
2104 {
2105 TRACE("Replaced media tag with type %d", old_media_tag->type);
2106 free(old_media_tag->data);
2107 free(old_media_tag);
2108 old_media_tag = NULL;
2109 }
2110
2111 ctx->dirty_media_tags = true; // Mark media tags as dirty
2112 TRACE("Exiting aaruf_write_media_tag() = AARUF_STATUS_OK");
2113 return AARUF_STATUS_OK;
2114}
2115
2306AARU_EXPORT int32_t AARU_CALL aaruf_write_sector_tag(void *context, const uint64_t sector_address, const bool negative,
2307 const uint8_t *data, const size_t length, const int32_t tag)
2308{
2309 TRACE("Entering aaruf_write_sector_tag(%p, %" PRIu64 ", %d, %p, %zu, %d)", context, sector_address, negative, data,
2310 length, tag);
2311
2312 // Check context is correct AaruFormat context
2313 if(context == NULL)
2314 {
2315 FATAL("Invalid context");
2316
2317 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_NOT_AARUFORMAT");
2319 }
2320
2321 aaruformat_context *ctx = context;
2322
2323 // Not a libaaruformat context
2324 if(ctx->magic != AARU_MAGIC)
2325 {
2326 FATAL("Invalid context");
2327
2328 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_NOT_AARUFORMAT");
2330 }
2331
2332 // Check we are writing
2333 if(!ctx->is_writing)
2334 {
2335 FATAL("Trying to write a read-only image");
2336
2337 TRACE("Exiting aaruf_write_sector_tag() = AARUF_READ_ONLY");
2338 return AARUF_READ_ONLY;
2339 }
2340
2341 // Flux-only images carry no DDT and no per-sector tag storage.
2342 if(ctx->no_user_data_ddt)
2343 {
2344 FATAL("Cannot write sector tag to flux-only image (no user-data DDT)");
2345
2346 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_USER_DATA_NOT_PRESENT");
2348 }
2349
2350 if(negative && sector_address > ctx->user_data_ddt_header.negative)
2351 {
2352 FATAL("Sector address out of bounds");
2353
2354 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_SECTOR_OUT_OF_BOUNDS");
2356 }
2357
2358 if(!negative && sector_address > ctx->image_info.Sectors + ctx->user_data_ddt_header.overflow - 1)
2359 {
2360 FATAL("Sector address out of bounds");
2361
2362 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_SECTOR_OUT_OF_BOUNDS");
2364 }
2365
2366 if(data == NULL || length == 0)
2367 {
2368 FATAL("Invalid data or length");
2370 }
2371
2372 uint64_t corrected_sector_address = sector_address;
2373
2374 // Calculate positive or negative sector
2375 if(negative)
2376 corrected_sector_address = ctx->user_data_ddt_header.negative - sector_address;
2377 else
2378 corrected_sector_address += ctx->user_data_ddt_header.negative;
2379
2380 const uint64_t total_sectors =
2382
2383 switch(tag)
2384 {
2387 {
2388 FATAL("Invalid media type for tag");
2389 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_MEDIA_TYPE");
2391 }
2392
2393 if(length != 1)
2394 {
2395 FATAL("Incorrect tag size");
2396 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_DATA_SIZE");
2398 }
2399
2400 for(int i = 0; i < ctx->tracks_header.entries; i++)
2401 if(sector_address >= ctx->track_entries[i].start && sector_address <= ctx->track_entries[i].end)
2402 {
2403 ctx->track_entries[i].flags = data[0];
2404 ctx->dirty_tracks_block = true; // Mark tracks block as dirty
2405 TRACE("Exiting aaruf_write_sector_tag() = AARUF_STATUS_OK");
2406 return AARUF_STATUS_OK;
2407 }
2408
2409 FATAL("Track not found");
2413 {
2414 FATAL("Invalid media type for tag");
2415 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_MEDIA_TYPE");
2417 }
2418
2419 if(length != 12)
2420 {
2421 FATAL("Incorrect tag size");
2422 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_DATA_SIZE");
2424 }
2425
2426 for(int i = 0; i < ctx->tracks_header.entries; i++)
2427 if(sector_address >= ctx->track_entries[i].start && sector_address <= ctx->track_entries[i].end)
2428 {
2429 memcpy(ctx->track_entries[i].isrc, data, 12);
2430 ctx->dirty_tracks_block = true; // Mark tracks block as dirty
2431 TRACE("Exiting aaruf_write_sector_tag() = AARUF_STATUS_OK");
2432 return AARUF_STATUS_OK;
2433 }
2434
2435 FATAL("Track not found");
2439 {
2440 FATAL("Invalid media type for tag");
2441 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_MEDIA_TYPE");
2443 }
2444
2445 if(length != 96)
2446 {
2447 FATAL("Incorrect tag size");
2448 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_DATA_SIZE");
2450 }
2451
2452 if(ctx->sector_subchannel == NULL) ctx->sector_subchannel = calloc(1, 96 * total_sectors);
2453
2454 if(ctx->sector_subchannel == NULL)
2455 {
2456 FATAL("Could not allocate memory for sector subchannel");
2457
2458 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
2460 }
2461
2462 memcpy(ctx->sector_subchannel + corrected_sector_address * 96, data, 96);
2463 ctx->dirty_sector_subchannel_block = true; // Mark subchannel block as dirty
2464 TRACE("Exiting aaruf_write_sector_tag() = AARUF_STATUS_OK");
2465 return AARUF_STATUS_OK;
2466 case kSectorTagDvdCmi:
2468 {
2469 FATAL("Invalid media type for tag");
2470 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_MEDIA_TYPE");
2472 }
2473
2474 if(length != 1)
2475 {
2476 FATAL("Incorrect tag size");
2477 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_DATA_SIZE");
2479 }
2480
2481 if(ctx->sector_cpr_mai == NULL) ctx->sector_cpr_mai = calloc(1, 6 * total_sectors);
2482
2483 if(ctx->sector_cpr_mai == NULL)
2484 {
2485 FATAL("Could not allocate memory for sector CPR/MAI");
2486
2487 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
2489 }
2490
2491 memcpy(ctx->sector_cpr_mai + corrected_sector_address * 6, data, 1);
2492 ctx->dirty_dvd_long_sector_blocks = true; // Mark DVD long sector blocks as dirty
2493 TRACE("Exiting aaruf_write_sector_tag() = AARUF_STATUS_OK");
2494 return AARUF_STATUS_OK;
2497 {
2498 FATAL("Invalid media type for tag");
2499 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_MEDIA_TYPE");
2501 }
2502
2503 if(length != 1)
2504 {
2505 FATAL("Incorrect tag size");
2506 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_DATA_SIZE");
2508 }
2509
2510 if(ctx->sector_id == NULL) ctx->sector_id = calloc(1, 4 * total_sectors);
2511
2512 if(ctx->sector_id == NULL)
2513 {
2514 FATAL("Could not allocate memory for sector ID");
2515
2516 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
2518 }
2519
2520 memcpy(ctx->sector_id + corrected_sector_address * 4, data, 1);
2521 TRACE("Exiting aaruf_write_sector_tag() = AARUF_STATUS_OK");
2522 return AARUF_STATUS_OK;
2525 {
2526 FATAL("Invalid media type for tag");
2527 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_MEDIA_TYPE");
2529 }
2530
2531 if(length != 3)
2532 {
2533 FATAL("Incorrect tag size");
2534 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_DATA_SIZE");
2536 }
2537
2538 if(ctx->sector_id == NULL) ctx->sector_id = calloc(1, 4 * total_sectors);
2539
2540 if(ctx->sector_id == NULL)
2541 {
2542 FATAL("Could not allocate memory for sector ID");
2543
2544 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
2546 }
2547
2548 memcpy(ctx->sector_id + corrected_sector_address * 4 + 1, data, 3);
2549 ctx->dirty_dvd_long_sector_blocks = true; // Mark DVD long sector blocks as dirty
2550 TRACE("Exiting aaruf_write_sector_tag() = AARUF_STATUS_OK");
2551 return AARUF_STATUS_OK;
2554 {
2555 FATAL("Invalid media type for tag");
2556 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_MEDIA_TYPE");
2558 }
2559
2560 if(length != 2)
2561 {
2562 FATAL("Incorrect tag size");
2563 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_DATA_SIZE");
2565 }
2566
2567 if(ctx->sector_ied == NULL) ctx->sector_ied = calloc(1, 2 * total_sectors);
2568
2569 if(ctx->sector_ied == NULL)
2570 {
2571 FATAL("Could not allocate memory for sector IED");
2572
2573 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
2575 }
2576
2577 memcpy(ctx->sector_ied + corrected_sector_address * 2, data, 2);
2578 ctx->dirty_dvd_long_sector_blocks = true; // Mark DVD long sector blocks as dirty
2579 TRACE("Exiting aaruf_write_sector_tag() = AARUF_STATUS_OK");
2580 return AARUF_STATUS_OK;
2583 {
2584 FATAL("Invalid media type for tag");
2585 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_MEDIA_TYPE");
2587 }
2588
2589 if(length != 4)
2590 {
2591 FATAL("Incorrect tag size");
2592 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_DATA_SIZE");
2594 }
2595
2596 if(ctx->sector_edc == NULL) ctx->sector_edc = calloc(1, 4 * total_sectors);
2597
2598 if(ctx->sector_edc == NULL)
2599 {
2600 FATAL("Could not allocate memory for sector EDC");
2601
2602 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
2604 }
2605
2606 memcpy(ctx->sector_edc + corrected_sector_address * 4, data, 4);
2607 ctx->dirty_dvd_long_sector_blocks = true; // Mark DVD long sector blocks as dirty
2608 TRACE("Exiting aaruf_write_sector_tag() = AARUF_STATUS_OK");
2609 return AARUF_STATUS_OK;
2612 {
2613 FATAL("Invalid media type for tag");
2614 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_MEDIA_TYPE");
2616 }
2617
2618 if(length != 5)
2619 {
2620 FATAL("Incorrect tag size");
2621 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_DATA_SIZE");
2623 }
2624
2625 if(ctx->sector_decrypted_title_key == NULL) ctx->sector_decrypted_title_key = calloc(1, 5 * total_sectors);
2626
2627 if(ctx->sector_decrypted_title_key == NULL)
2628 {
2629 FATAL("Could not allocate memory for sector decrypted title key");
2630
2631 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
2633 }
2634
2635 memcpy(ctx->sector_decrypted_title_key + corrected_sector_address * 5, data, 5);
2636 ctx->dirty_dvd_title_key_decrypted_block = true; // Mark title key block as dirty
2637 TRACE("Exiting aaruf_write_sector_tag() = AARUF_STATUS_OK");
2638 return AARUF_STATUS_OK;
2641 {
2642 FATAL("Invalid media type for tag");
2643 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_MEDIA_TYPE");
2645 }
2646
2647 if(length != 12)
2648 {
2649 FATAL("Incorrect tag size");
2650 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_DATA_SIZE");
2652 }
2653
2654 if(ctx->sector_subchannel == NULL) ctx->sector_subchannel = calloc(1, 12 * total_sectors);
2655
2656 if(ctx->sector_subchannel == NULL)
2657 {
2658 FATAL("Could not allocate memory for Apple Sony tag");
2659
2660 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
2662 }
2663
2664 memcpy(ctx->sector_subchannel + corrected_sector_address * 12, data, 12);
2665 ctx->dirty_sector_subchannel_block = true; // Mark subchannel block as dirty
2666 TRACE("Exiting aaruf_write_sector_tag() = AARUF_STATUS_OK");
2667 return AARUF_STATUS_OK;
2670 {
2671 FATAL("Invalid media type for tag");
2672 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_MEDIA_TYPE");
2674 }
2675
2676 if(length != 20)
2677 {
2678 FATAL("Incorrect tag size");
2679 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_DATA_SIZE");
2681 }
2682
2683 if(ctx->sector_subchannel == NULL) ctx->sector_subchannel = calloc(1, 20 * total_sectors);
2684
2685 if(ctx->sector_subchannel == NULL)
2686 {
2687 FATAL("Could not allocate memory for Apple Profile tag");
2688
2689 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
2691 }
2692
2693 memcpy(ctx->sector_subchannel + corrected_sector_address * 20, data, 20);
2694 ctx->dirty_sector_subchannel_block = true; // Mark subchannel block as dirty
2695 TRACE("Exiting aaruf_write_sector_tag() = AARUF_STATUS_OK");
2696 return AARUF_STATUS_OK;
2699 {
2700 FATAL("Invalid media type for tag");
2701 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_MEDIA_TYPE");
2703 }
2704
2705 if(length != 24)
2706 {
2707 FATAL("Incorrect tag size");
2708 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_DATA_SIZE");
2710 }
2711
2712 if(ctx->sector_subchannel == NULL) ctx->sector_subchannel = calloc(1, 24 * total_sectors);
2713
2714 if(ctx->sector_subchannel == NULL)
2715 {
2716 FATAL("Could not allocate memory for Priam Data Tower tag");
2717
2718 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
2720 }
2721
2722 memcpy(ctx->sector_subchannel + corrected_sector_address * 24, data, 24);
2723 ctx->dirty_sector_subchannel_block = true; // Mark subchannel block as dirty
2724 TRACE("Exiting aaruf_write_sector_tag() = AARUF_STATUS_OK");
2725 return AARUF_STATUS_OK;
2728 {
2729 FATAL("Invalid media type for tag");
2730 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_MEDIA_TYPE");
2732 }
2733
2734 if(length != 4)
2735 {
2736 FATAL("Incorrect tag size");
2737 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_DATA_SIZE");
2739 }
2740
2741 if(ctx->sector_edc == NULL) ctx->sector_edc = calloc(1, 4 * total_sectors);
2742
2743 if(ctx->sector_edc == NULL)
2744 {
2745 FATAL("Could not allocate memory for Blu-ray sector EDC");
2746
2747 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
2749 }
2750
2751 memcpy(ctx->sector_edc + corrected_sector_address * 4, data, 4);
2752 ctx->dirty_bd_sector_edc_block = true; // Mark BD sector EDC block as dirty
2753 TRACE("Exiting aaruf_write_sector_tag() = AARUF_STATUS_OK");
2754 return AARUF_STATUS_OK;
2755 default:
2756 TRACE("Do not know how to write sector tag %d", tag);
2758 }
2759}
#define MAX_FLAKE_BLOCK
FLAC maximum block size used for encoding audio sectors.
Definition consts.h:98
#define LZMA_PROPERTIES_LENGTH
Size in bytes of the fixed LZMA properties header (lc/lp/pb + dictionary size).
Definition consts.h:86
#define AARU_MAGIC
Magic identifier for AaruFormat container (ASCII "AARUFRMT").
Definition consts.h:64
#define MIN_FLAKE_BLOCK
FLAC minimum block size.
Definition consts.h:100
#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:94
#define AARU_CALL
Definition decls.h:46
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
uint64_t aaruf_crc64_data(const uint8_t *data, uint32_t len)
Definition crc64.c:160
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:180
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
size_t aaruf_zstd_encode_buffer(uint8_t *dst_buffer, size_t dst_size, const uint8_t *src_buffer, size_t src_size, int level, int num_threads)
Encodes a buffer using Zstandard compression.
Definition zstd.c:59
void aaruf_crc64_free(crc64_ctx *ctx)
Frees a CRC64 context.
Definition crc64.c:155
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:152
int aaruf_spamsum_update(spamsum_ctx *ctx, const uint8_t *data, uint32_t len)
Updates the spamsum context with new data.
Definition spamsum.c:71
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
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:682
int32_t aaruf_write_sector_tag(void *context, uint64_t sector_address, bool negative, const uint8_t *data, size_t length, int32_t tag)
Writes per-sector tag data (auxiliary metadata) for a specific sector.
Definition write.c:2306
void aaruf_md5_update(md5_ctx *ctx, const void *data, unsigned long size)
Definition md5.c:455
#define AARU_EXPORT
Definition decls.h:55
int32_t aaruf_write_media_tag(void *context, const uint8_t *data, int32_t type, uint32_t length)
Writes a media tag to the AaruFormat image, storing medium-specific metadata and descriptors.
Definition write.c:2036
void aaruf_sha256_update(sha256_ctx *ctx, const void *data, unsigned long size)
Definition sha256.c:90
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
void aaruf_sha1_update(sha1_ctx *ctx, const void *data, unsigned long size)
Definition sha1.c:89
int aaruf_crc64_final(crc64_ctx *ctx, uint64_t *crc)
Computes the final CRC64 value from the context.
Definition crc64.c:141
crc64_ctx * aaruf_crc64_init(void)
Initializes a CRC64 context.
Definition crc64.c:32
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
Block containing data.
Definition enums.h:164
@ SectorStatusNotDumped
Sector(s) not yet acquired during image dumping.
Definition enums.h:258
@ SectorStatusGenerable
Content can be generated using a known algorithm.
Definition enums.h:269
@ SectorStatusUnencrypted
Content originally encrypted but stored decrypted in image.
Definition enums.h:268
@ SectorStatusMode2Form2NoCrc
Suffix matches MODE 2 Form 2 but CRC empty/missing.
Definition enums.h:264
@ SectorStatusMode1Correct
Valid MODE 1 data with regenerable suffix/prefix.
Definition enums.h:261
@ SectorStatusMode2Form2Ok
Suffix matches MODE 2 Form 2 with valid CRC.
Definition enums.h:263
@ SectorStatusErrored
Error during dumping; data may be incomplete or corrupt.
Definition enums.h:260
@ SectorStatusMode2Form1Ok
Suffix verified/regenerable for MODE 2 Form 1.
Definition enums.h:262
@ OpticalDisc
Purely optical discs.
Definition enums.h:246
@ BlockMedia
Media that is physically block-based or abstracted like that.
Definition enums.h:247
@ kTrackTypeCdMode2Form2
Compact Disc Mode 2 Form 2 data track.
Definition enums.h:228
@ kTrackTypeData
Generic data track (not further specified).
Definition enums.h:224
@ kTrackTypeCdMode2Form1
Compact Disc Mode 2 Form 1 data track.
Definition enums.h:227
@ kTrackTypeAudio
Audio track.
Definition enums.h:223
@ kTrackTypeCdMode2Formless
Compact Disc Mode 2 (formless) data track.
Definition enums.h:226
@ kTrackTypeCdMode1
Compact Disc Mode 1 data track.
Definition enums.h:225
@ kDataTypeUserData
User (main) data.
Definition enums.h:48
@ kCompressionLzma
LZMA compression.
Definition enums.h:34
@ kCompressionNone
Not compressed.
Definition enums.h:33
@ kCompressionZstd
Zstandard compression.
Definition enums.h:37
@ kCompressionFlac
FLAC compression.
Definition enums.h:35
void ec_accumulate_data_block(aaruformat_context *ctx, const BlockHeader *block_header, const uint8_t *lzma_props, const uint8_t *payload, uint32_t payload_size, uint64_t file_offset)
Accumulate parity for a data block that was just written to disk.
Definition erasure.c:209
#define AARUF_ERROR_CANNOT_WRITE_BLOCK_DATA
Failure writing block payload.
Definition errors.h:63
#define AARUF_STATUS_OK
Sector present and read without uncorrectable errors.
Definition errors.h:82
#define AARUF_READ_ONLY
Operation requires write mode but context is read-only.
Definition errors.h:61
#define AARUF_ERROR_INCORRECT_MEDIA_TYPE
Operation incompatible with image media type.
Definition errors.h:51
#define AARUF_ERROR_USER_DATA_NOT_PRESENT
Image has no user-data DDT (e.g. flux-only image); sector data is unavailable.
Definition errors.h:76
#define AARUF_ERROR_TRACK_NOT_FOUND
Referenced track number not present.
Definition errors.h:52
#define AARUF_ERROR_NOT_ENOUGH_MEMORY
Memory allocation failure (critical).
Definition errors.h:48
#define AARUF_ERROR_SECTOR_OUT_OF_BOUNDS
Requested logical sector outside media bounds.
Definition errors.h:44
#define AARUF_ERROR_INCORRECT_DATA_SIZE
Data size does not match expected size.
Definition errors.h:65
#define AARUF_ERROR_CANNOT_SET_DDT_ENTRY
Failed to encode/store a DDT entry (overflow or IO).
Definition errors.h:64
#define AARUF_ERROR_NOT_AARUFORMAT
Input file/stream failed magic or structural validation.
Definition errors.h:40
#define AARUF_ERROR_INVALID_TAG
Invalid or unsupported media or sector tag format.
Definition errors.h:66
#define AARUF_ERROR_INVALID_SECTOR_LENGTH
Sector length is too big.
Definition errors.h:70
#define AARUF_ERROR_CANNOT_WRITE_BLOCK_HEADER
Failure writing block header.
Definition errors.h:62
@ BDREXL
BD-RE XL.
Definition aaru.h:163
@ AppleProfile
Definition aaru.h:710
@ PS5BD
Sony PlayStation 5 game Blu-ray.
Definition aaru.h:215
@ VideoNowColor
Hasbro VideoNow Color disc.
Definition aaru.h:784
@ GOD
Nintendo GameCube Optical Disc.
Definition aaru.h:514
@ AppleSonySS
3.5", SS, DD, 80 tracks, 8 to 12 spt, 512 bytes/sector, GCR
Definition aaru.h:252
@ DVDPRWDL
DVD+RW DL.
Definition aaru.h:141
@ BDRXL
BD-R XL.
Definition aaru.h:162
@ DVDRW
DVD-RW.
Definition aaru.h:138
@ DVDRWDL
DVD-RW DL.
Definition aaru.h:145
@ WOD
Nintendo Wii Optical Disc.
Definition aaru.h:524
@ PS3BD
Sony PlayStation 3 game Blu-ray.
Definition aaru.h:211
@ SACD
Super Audio CD (Scarlet Book).
Definition aaru.h:121
@ UHDBD
Ultra HD Blu-ray.
Definition aaru.h:164
@ DVDPRDL
DVD+R DL.
Definition aaru.h:143
@ DVDR
DVD-R.
Definition aaru.h:137
@ PS3DVD
Sony PlayStation 3 game DVD.
Definition aaru.h:210
@ BDROM
BD-ROM (and BD Video).
Definition aaru.h:159
@ JaguarCD
Atari Jaguar CD.
Definition aaru.h:237
@ PS2DVD
Sony PlayStation 2 game DVD.
Definition aaru.h:209
@ XGD4
Microsoft X-box One Game Disc.
Definition aaru.h:222
@ VideoNow
Hasbro VideoNow 85 mm proprietary video disc.
Definition aaru.h:783
@ VideoNowXp
Hasbro VideoNow XP higher capacity disc.
Definition aaru.h:785
@ BDR
BD-R.
Definition aaru.h:160
@ PS4BD
Sony PlayStation 4 game Blu-ray.
Definition aaru.h:212
@ PriamDataTower
Definition aaru.h:713
@ AppleFileWare
5.25", DS, ?D, ?? tracks, ?? spt, 512 bytes/sector, GCR, opposite side heads, aka Twiggy
Definition aaru.h:254
@ BDRE
BD-RE.
Definition aaru.h:161
@ DVDPR
DVD+R.
Definition aaru.h:139
@ AppleWidget
Definition aaru.h:711
@ DVDPRW
DVD+RW.
Definition aaru.h:140
@ Nuon
Nuon (DVD based videogame console).
Definition aaru.h:243
@ DVDDownload
DVD-Download.
Definition aaru.h:146
@ DVDRDL
DVD-R DL.
Definition aaru.h:142
@ AppleSonyDS
3.5", DS, DD, 80 tracks, 8 to 12 spt, 512 bytes/sector, GCR
Definition aaru.h:253
@ DVDROM
DVD-ROM (applies to DVD Video and DVD Audio).
Definition aaru.h:136
@ DVDRAM
DVD-RAM.
Definition aaru.h:144
@ kSectorTagCdTrackFlags
Track flags (audio/data, copy permitted, pre-emphasis).
Definition aaru.h:971
@ kSectorTagBdSectorEdc
Blu-ray sector EDC, 4 bytes.
Definition aaru.h:982
@ kSectorTagDvdCmi
DVD Copyright Management Information (CSS).
Definition aaru.h:972
@ kSectorTagDvdSectorIed
DVD sector ID error detection, 2 bytes.
Definition aaru.h:978
@ kSectorTagCdSubchannel
96 raw subchannel bytes (P-W)
Definition aaru.h:968
@ kSectorTagAppleSony
Apple's Sony sector tags, 12 bytes (address prolog + checksum).
Definition aaru.h:960
@ kSectorTagAppleProfile
Apple's Profile sector tags, 20 bytes.
Definition aaru.h:980
@ kSectorTagCdTrackIsrc
Track ISRC (12 ASCII chars, no terminator).
Definition aaru.h:969
@ kSectorTagDvdSectorInformation
DVD sector information, 1 bytes.
Definition aaru.h:976
@ kSectorTagDvdSectorEdc
DVD sector EDC, 4 bytes.
Definition aaru.h:979
@ kSectorTagDvdSectorNumber
DVD sector number, 3 bytes.
Definition aaru.h:977
@ kSectorTagPriamDataTower
Priam DataTower sector tags, 24 bytes.
Definition aaru.h:981
@ kSectorTagDvdTitleKeyDecrypted
Decrypted DVD sector title key, 5 bytes.
Definition aaru.h:975
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
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
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:1301
int32_t aaruf_close_current_block(aaruformat_context *ctx)
Finalizes and writes the current data block to the AaruFormat image file.
Definition write.c:1569
static int aaruf_fseek(FILE *stream, aaru_off_t offset, int origin)
Definition internal.h:46
#define LZMA_THREADS(ctx)
Clamp num_threads to LZMA's valid range [1, 2].
Definition internal.h:23
int64_t aaru_off_t
Definition internal.h:42
Structure definitions and conversion/serialization function declarations for Lisa family disk tags.
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
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
sony_tag profile_tag_to_sony(profile_tag tag)
Convert a profile_tag to a sony_tag.
Definition lisa_tag.c:257
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
profile_tag priam_tag_to_profile(priam_tag tag)
Convert a priam_tag to a profile_tag.
Definition lisa_tag.c:325
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
profile_tag sony_tag_to_profile(sony_tag tag)
Convert a sony_tag to a profile_tag representation.
Definition lisa_tag.c:173
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
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
priam_tag sony_tag_to_priam(sony_tag tag)
Convert a sony_tag to a priam_tag representation.
Definition lisa_tag.c:201
priam_tag profile_tag_to_priam(profile_tag tag)
Convert a profile_tag to a priam_tag.
Definition lisa_tag.c:228
sony_tag priam_tag_to_sony(priam_tag tag)
Convert a priam_tag to a sony_tag.
Definition lisa_tag.c:291
#define FATAL(fmt,...)
Definition log.h:40
#define TRACE(fmt,...)
Definition log.h:25
void ps3_lazy_init(aaruformat_context *ctx)
Lazy-initialize PS3 encryption state from context media tags.
Definition ps3_crypto.c:70
void ps3_decrypt_sector(const uint8_t disc_key[16], uint64_t sector_num, uint8_t *data, uint32_t length)
Decrypt a sector using PS3 disc encryption (AES-128-CBC).
Definition ps3_crypto.c:63
bool ps3_is_sector_encrypted(const Ps3PlaintextRegion *plaintext_regions, uint32_t region_count, uint64_t sector_address)
Check whether a sector is encrypted (i.e., not in any plaintext region).
uint32_t mediaType
Media type enumeration (value from MediaType).
Definition header.h:114
uint16_t biggestSectorSize
size of biggest sector in the image (in bytes).
Definition header.h:120
Header preceding the compressed data payload of a data block (BlockType::DataBlock).
Definition data.h:71
uint32_t cmpLength
Size in bytes of the compressed payload immediately following this header.
Definition data.h:76
uint32_t length
Size in bytes of the uncompressed payload resulting after decompression.
Definition data.h:77
uint32_t identifier
Block identifier, must be BlockType::DataBlock.
Definition data.h:72
uint32_t sectorSize
Size in bytes of each logical sector represented in this block.
Definition data.h:75
uint64_t cmpCrc64
CRC64-ECMA of the compressed payload (cmpLength bytes).
Definition data.h:78
uint64_t crc64
CRC64-ECMA of the uncompressed payload (length bytes).
Definition data.h:79
uint16_t type
Logical data classification (value from DataType).
Definition data.h:73
uint16_t compression
Compression algorithm used (value from CompressionType).
Definition data.h:74
uint8_t blockAlignmentShift
2^blockAlignmentShift = block alignment boundary in bytes.
Definition ddt.h:154
uint32_t overflow
Trailing dumped sectors beyond user area (overflow range), still mapped with entries.
Definition ddt.h:151
uint32_t negative
Leading negative LBA count; added to external L to build internal index.
Definition ddt.h:149
uint8_t dataShift
2^dataShift = sectors represented per increment in blockIndex field.
Definition ddt.h:155
uint32_t MediaType
Media type identifier (see MediaType enum; 0=Unknown).
Definition aaru.h:945
uint8_t MetadataMediaType
Media type for sidecar generation (internal archival use).
Definition aaru.h:946
uint64_t Sectors
Total count of addressable logical sectors/blocks.
Definition aaru.h:938
Single index entry describing a block's type, (optional) data classification, and file offset.
Definition index.h:109
uint32_t blockType
Block identifier of the referenced block (value from BlockType).
Definition index.h:110
uint64_t offset
Absolute byte offset in the image where the referenced block header begins.
Definition index.h:112
uint16_t dataType
Data classification (value from DataType) or unused for untyped blocks.
Definition index.h:111
A plaintext (unencrypted) region on a PS3 disc.
Single optical disc track descriptor (sequence, type, LBAs, session, ISRC, flags).
Definition optical.h:72
uint8_t session
Session number (1-based). 1 for single-session discs.
Definition optical.h:79
uint8_t flags
Control / attribute bitfield (see file documentation for suggested bit mapping).
Definition optical.h:81
int64_t end
Inclusive ending LBA of the track.
Definition optical.h:77
uint8_t sequence
Track number (1..99 typical for CD audio/data).
Definition optical.h:73
int64_t start
Inclusive starting LBA of the track.
Definition optical.h:76
uint8_t type
Track type (value from TrackType).
Definition optical.h:75
uint8_t isrc[13]
ISRC raw 13-byte code (no null terminator). All zeros if not present.
Definition optical.h:80
uint16_t entries
Number of TrackEntry records following this header.
Definition optical.h:64
Master context representing an open or in‑creation Aaru image.
Definition context.h:178
uint8_t * ps3_disc_key
Cached disc key (16 bytes), NULL if not loaded.
Definition context.h:357
DdtHeader2 user_data_ddt_header
Active user data DDT v2 header (primary table meta).
Definition context.h:195
bool dirty_checksum_block
True if checksum block should be written during close.
Definition context.h:332
bool deduplicate
Storage deduplication active (duplicates coalesce).
Definition context.h:310
size_t sector_suffix_length
Length of sector_suffix.
Definition context.h:295
bool compression_enabled
True if block compression enabled (writing path).
Definition context.h:311
uint64_t last_written_block
Last written block number (write path).
Definition context.h:293
uint8_t * sector_cpr_mai
DVD sector CPR_MAI (6 bytes) if present.
Definition context.h:214
bool dirty_media_tags
True if media tags should be written during close.
Definition context.h:343
hash_map_t * sector_hash_map
Deduplication hash map (fingerprint->entry mapping).
Definition context.h:263
uint32_t ps3_plaintext_region_count
Number of plaintext regions.
Definition context.h:359
sha256_ctx sha256_context
Opaque SHA-256 context for streaming updates.
Definition context.h:282
bool calculating_sha256
True if whole-image SHA-256 being calculated on-the-fly.
Definition context.h:285
uint8_t * sector_ied
DVD sector IED (2 bytes) if present.
Definition context.h:213
md5_ctx md5_context
Opaque MD5 context for streaming updates.
Definition context.h:280
bool dirty_sector_suffix_block
True if sector suffix block should be written during close.
Definition context.h:337
uint8_t * sector_prefix
Raw per-sector prefix (e.g., sync+header) uncorrected.
Definition context.h:206
bool dirty_dvd_title_key_decrypted_block
True if decrypted title key block should be written during close.
Definition context.h:342
uint64_t * sector_suffix_ddt2
CD sector suffix DDT V2.
Definition context.h:192
bool dirty_mode2_subheaders_block
True if MODE2 subheader block should be written during close.
Definition context.h:334
uint8_t * sector_edc
DVD or Blu-ray sector EDC (4 bytes) if present (see DvdSectorEdc / BdSectorEdc data types).
Definition context.h:215
bool calculating_sha1
True if whole-image SHA-1 being calculated on-the-fly.
Definition context.h:284
bool dirty_tracks_block
True if tracks block should be written during close.
Definition context.h:333
CdEccContext * ecc_cd_context
CD ECC/EDC helper tables (allocated on demand).
Definition context.h:258
bool rewinded
True if stream has been rewound after open (write path).
Definition context.h:303
bool ps3_encryption_initialized
Whether lazy init has occurred.
Definition context.h:360
bool dirty_sector_prefix_block
True if sector prefix block should be written during close.
Definition context.h:335
bool dirty_index_block
True if index block should be written during close.
Definition context.h:354
uint8_t * sector_suffix
Raw per-sector suffix (EDC/ECC) uncorrected.
Definition context.h:208
AaruHeaderV2 header
Parsed container header (v2).
Definition context.h:181
int current_block_offset
Logical offset inside block (units: bytes or sectors depending on path).
Definition context.h:298
bool is_writing
True if context opened/created for writing.
Definition context.h:302
spamsum_ctx * spamsum_context
Opaque SpamSum context for streaming updates.
Definition context.h:277
size_t sector_prefix_offset
Current position in sector_prefix.
Definition context.h:296
BlockHeader current_block_header
Header for block currently being assembled (write path).
Definition context.h:291
uint64_t magic
File magic (AARU_MAGIC) post-open.
Definition context.h:180
uint8_t * writing_buffer
Accumulation buffer for current block data.
Definition context.h:290
uint64_t * sector_prefix_ddt2
CD sector prefix DDT V2.
Definition context.h:191
bool calculating_spamsum
True if whole-image SpamSum being calculated on-the-fly.
Definition context.h:286
size_t sector_prefix_length
Length of sector_prefix.
Definition context.h:294
mediaTagEntry * mediaTags
Hash table of extra media tags (uthash root).
Definition context.h:274
blake3_hasher * blake3_context
Opaque BLAKE3 context for streaming updates.
Definition context.h:278
bool calculating_blake3
True if whole-image BLAKE3 being calculated on-the-fly.
Definition context.h:287
uint64_t next_block_position
Absolute file offset where next block will be written.
Definition context.h:292
bool calculating_md5
True if whole-image MD5 being calculated on-the-fly.
Definition context.h:283
bool dirty_sector_suffix_ddt
True if sector suffix DDT should be written during close.
Definition context.h:338
size_t sector_suffix_offset
Current position in sector_suffix.
Definition context.h:297
bool has_zstd_blocks
True if any block was actually written with Zstandard compression.
Definition context.h:313
int zstd_level
Zstandard compression level (writing path, default 19).
Definition context.h:314
uint8_t * sector_decrypted_title_key
DVD decrypted title key (5 bytes) if present.
Definition context.h:216
int writing_buffer_position
Current size / position within writingBuffer.
Definition context.h:299
bool block_zero_written
True if block zero has been written (writing path).
Definition context.h:305
crc64_ctx * crc64_context
Opaque CRC64 context for streaming updates.
Definition context.h:259
uint8_t * sector_subchannel
Raw 96-byte subchannel (if captured).
Definition context.h:210
bool dirty_sector_subchannel_block
True if subchannel block should be written during close.
Definition context.h:339
FILE * imageStream
Underlying FILE* stream (binary mode).
Definition context.h:182
bool dirty_dvd_long_sector_blocks
True if DVD long sector blocks should be written during close.
Definition context.h:340
bool no_user_data_ddt
True if image intentionally has no user-data DDT (e.g. flux-only image). When set,...
Definition context.h:203
bool dirty_sector_prefix_ddt
True if sector prefix DDT should be written during close.
Definition context.h:336
UT_array * index_entries
Flattened index entries (UT_array of IndexEntry).
Definition context.h:262
int num_threads
Compression worker threads (1 = single-threaded, default).
Definition context.h:315
bool ec_enabled
True if erasure coding is active.
Definition context.h:400
uint8_t * mode2_subheaders
MODE2 Form1/Form2 8-byte subheaders (concatenated).
Definition context.h:211
bool dirty_bd_sector_edc_block
True if Blu-ray sector EDC data block should be written during close.
Definition context.h:341
ImageInfo image_info
Exposed high-level image info summary.
Definition context.h:270
uint8_t * sector_id
DVD sector ID (4 bytes) if present.
Definition context.h:212
void * ps3_plaintext_regions
Parsed Ps3PlaintextRegion array (max 32), NULL if not loaded.
Definition context.h:358
bool use_zstd
Use Zstandard instead of LZMA for data blocks.
Definition context.h:312
sha1_ctx sha1_context
Opaque SHA-1 context for streaming updates.
Definition context.h:281
uint32_t lzma_dict_size
LZMA dictionary size (writing path).
Definition context.h:309
TrackEntry * track_entries
Full track list (tracksHeader.entries elements).
Definition context.h:252
uint8_t current_track_type
Current track type (when writing optical images with tracks, needed for block compression type).
Definition context.h:300
bool writing_long
True if writing long sectors.
Definition context.h:304
TracksHeader tracks_header
Tracks header (optical) if present.
Definition context.h:254
Hash table entry for an arbitrary media tag (e.g., proprietary drive/medium descriptor).
Definition context.h:125
uint8_t * data
Tag data blob (opaque to library core); length bytes long.
Definition context.h:126
int32_t type
Numeric type identifier.
Definition context.h:127
uint32_t length
Length in bytes of data.
Definition context.h:128
static int32_t grow_arena(uint8_t **arena, size_t *length, const size_t needed, const size_t minimum)
Ensures a CD sector prefix/suffix arena can hold needed bytes, growing it if required.
Definition write.c:47
static bool cd_header_matches_lba(const uint8_t *data, const int64_t lba)
Checks whether a CD sector header (bytes 0x0C-0x0E) is valid BCD and encodes exactly lba.
Definition write.c:71