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
102AARU_EXPORT int32_t AARU_CALL aaruf_write_sector(void *context, uint64_t sector_address, bool negative,
103 const uint8_t *data, uint8_t sector_status, uint32_t length)
104{
105 TRACE("Entering aaruf_write_sector(%p, %" PRIu64 ", %d, %p, %u, %u)", context, sector_address, negative, data,
106 sector_status, length);
107
108 // Check context is correct AaruFormat context
109 if(context == NULL)
110 {
111 FATAL("Invalid context");
112
113 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_AARUFORMAT");
115 }
116
117 aaruformat_context *ctx = context;
118
119 // Not a libaaruformat context
120 if(ctx->magic != AARU_MAGIC)
121 {
122 FATAL("Invalid context");
123
124 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_AARUFORMAT");
126 }
127
128 // Check we are writing
129 if(!ctx->is_writing)
130 {
131 FATAL("Trying to write a read-only image");
132
133 TRACE("Exiting aaruf_write_sector() = AARUF_READ_ONLY");
134 return AARUF_READ_ONLY;
135 }
136
137 if(negative && sector_address > ctx->user_data_ddt_header.negative)
138 {
139 FATAL("Sector address out of bounds");
140
141 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_SECTOR_OUT_OF_BOUNDS");
143 }
144
145 if(!negative && sector_address > ctx->image_info.Sectors + ctx->user_data_ddt_header.overflow - 1)
146 {
147 FATAL("Sector address out of bounds");
148
149 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_SECTOR_OUT_OF_BOUNDS");
151 }
152
153 if(length > USHRT_MAX)
154 {
155 FATAL("Sector length too large");
156
157 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_INVALID_SECTOR_LENGTH");
159 }
160
161 if(length > ctx->header.biggestSectorSize) ctx->header.biggestSectorSize = (uint16_t)length;
162
163 if(!ctx->rewinded)
164 {
165 // disable checksums on first encounter of decrypted/generable sectors
166 if(sector_status == SectorStatusUnencrypted || sector_status == SectorStatusGenerable)
167 {
168 TRACE("NGCW sector detected, disabling checksums");
169 ctx->rewinded = true;
170
171 if(ctx->calculating_md5) ctx->calculating_md5 = false;
172 if(ctx->calculating_sha1) ctx->calculating_sha1 = false;
173 if(ctx->calculating_sha256) ctx->calculating_sha256 = false;
174 if(ctx->calculating_spamsum) ctx->calculating_spamsum = false;
175 if(ctx->calculating_blake3) ctx->calculating_blake3 = false;
176 }
177 else if(sector_address <= ctx->last_written_block)
178 {
179 if(sector_address == 0 && !ctx->block_zero_written)
180 ctx->block_zero_written = true;
181 else
182 {
183 TRACE("Rewinded");
184 ctx->rewinded = true;
185
186 // Disable MD5 calculation
187 if(ctx->calculating_md5) ctx->calculating_md5 = false;
188 // Disable SHA1 calculation
189 if(ctx->calculating_sha1) ctx->calculating_sha1 = false;
190 // Disable SHA256 calculation
191 if(ctx->calculating_sha256) ctx->calculating_sha256 = false;
192 // Disable SpamSum calculation
193 if(ctx->calculating_spamsum) ctx->calculating_spamsum = false;
194 // Disable BLAKE3 calculation
195 if(ctx->calculating_blake3) ctx->calculating_blake3 = false;
196 }
197 }
198 else
199 ctx->last_written_block = sector_address;
200 }
201
202 // Calculate MD5 on-the-fly if requested and sector is within user sectors (not negative or overflow)
203 if(ctx->calculating_md5 && !negative && sector_address <= ctx->image_info.Sectors && !ctx->writing_long)
204 aaruf_md5_update(&ctx->md5_context, data, length);
205 // Calculate SHA1 on-the-fly if requested and sector is within user sectors (not negative or overflow)
206 if(ctx->calculating_sha1 && !negative && sector_address <= ctx->image_info.Sectors && !ctx->writing_long)
207 aaruf_sha1_update(&ctx->sha1_context, data, length);
208 // Calculate SHA256 on-the-fly if requested and sector is within user sectors (not negative or overflow)
209 if(ctx->calculating_sha256 && !negative && sector_address <= ctx->image_info.Sectors && !ctx->writing_long)
210 aaruf_sha256_update(&ctx->sha256_context, data, length);
211 // Calculate SpamSum on-the-fly if requested and sector is within user sectors (not negative or overflow)
212 if(ctx->calculating_spamsum && !negative && sector_address <= ctx->image_info.Sectors && !ctx->writing_long)
213 aaruf_spamsum_update(ctx->spamsum_context, data, length);
214 // Calculate BLAKE3 on-the-fly if requested and sector is within user sectors (not negative or overflow)
215 if(ctx->calculating_blake3 && !negative && sector_address <= ctx->image_info.Sectors && !ctx->writing_long)
216 blake3_hasher_update(ctx->blake3_context, data, length);
217
218 // Mark checksum block as dirty if any checksums are being calculated
220 ctx->calculating_blake3) &&
221 !negative && sector_address <= ctx->image_info.Sectors && !ctx->writing_long)
222 ctx->dirty_checksum_block = true;
223
224 // PS3 decryption: if caller sends encrypted data marked as "to be stored decrypted"
225 // Checksums have already been computed on the ciphertext above.
226 const uint8_t *write_data = data;
227 uint8_t *decrypted_buffer = NULL;
228
229 if(sector_status == SectorStatusUnencrypted && (ctx->header.mediaType == PS3DVD || ctx->header.mediaType == PS3BD))
230 {
232 {
233 ps3_lazy_init(ctx);
234 ctx->ps3_encryption_initialized = true;
235 }
236
237 if(ctx->ps3_disc_key != NULL && ctx->ps3_plaintext_regions != NULL &&
239 ctx->ps3_plaintext_region_count, sector_address))
240 {
241 decrypted_buffer = (uint8_t *)malloc(length);
242
243 if(decrypted_buffer == NULL)
244 {
245 FATAL("Could not allocate memory for PS3 decryption buffer");
247 }
248
249 memcpy(decrypted_buffer, data, length);
250 ps3_decrypt_sector(ctx->ps3_disc_key, sector_address, decrypted_buffer, length);
251 write_data = decrypted_buffer;
252 }
253 }
254
255 // NGCW: SectorStatusGenerable — set DDT entry only, do not store data in any block
256 if(sector_status == SectorStatusGenerable && (ctx->header.mediaType == GOD || ctx->header.mediaType == WOD))
257 {
258 uint64_t ddt_entry_gen = 0;
259 bool ddt_ok_gen = set_ddt_entry_v2(ctx, sector_address, negative, 0, 0, sector_status, &ddt_entry_gen);
260
261 free(decrypted_buffer);
262
263 if(!ddt_ok_gen)
264 {
265 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_CANNOT_SET_DDT_ENTRY");
267 }
268
269 TRACE("Exiting aaruf_write_sector() = AARUF_STATUS_OK (generable)");
270 return AARUF_STATUS_OK;
271 }
272
273 // Close current block first
274 if(ctx->writing_buffer != NULL &&
275 // When sector size changes or block reaches maximum size
276 (ctx->current_block_header.sectorSize != length ||
278 {
279 TRACE("Closing current block before writing new data");
280 int error = aaruf_close_current_block(ctx);
281
282 if(error != AARUF_STATUS_OK)
283 {
284 FATAL("Error closing current block: %d", error);
285
286 TRACE("Exiting aaruf_write_sector() = %d", error);
287 return error;
288 }
289 }
290
291 uint64_t ddt_entry = 0;
292 bool ddt_ok;
293
294 if(ctx->deduplicate)
295 {
296 // Calculate 64-bit XXH3 hash of the sector (on decrypted data for dedup)
297 TRACE("Hashing sector data for deduplication");
298 uint64_t hash = XXH3_64bits(write_data, length);
299
300 // Check if the hash is already in the map
301 bool existing = lookup_map(ctx->sector_hash_map, hash, &ddt_entry);
302 TRACE("Block does %s exist in deduplication map", existing ? "already" : "not yet");
303
304 ddt_ok = set_ddt_entry_v2(ctx, sector_address, negative, ctx->current_block_offset, ctx->next_block_position,
305 sector_status, &ddt_entry);
306 if(!ddt_ok)
307 {
308 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_CANNOT_SET_DDT_ENTRY");
310 }
311
312 if(existing)
313 {
314 TRACE("Sector exists, so not writing to image");
315 free(decrypted_buffer);
316 TRACE("Exiting aaruf_write_sector() = AARUF_STATUS_OK");
317 return AARUF_STATUS_OK;
318 }
319
320 TRACE("Inserting sector hash into deduplication map, proceeding to write into image as normal");
321 insert_map(ctx->sector_hash_map, hash, ddt_entry);
322 }
323 else
324 ddt_ok = set_ddt_entry_v2(ctx, sector_address, negative, ctx->current_block_offset, ctx->next_block_position,
325 sector_status, &ddt_entry);
326
327 if(!ddt_ok)
328 {
329 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_CANNOT_SET_DDT_ENTRY");
331 }
332
333 // No block set
334 if(ctx->writing_buffer_position == 0)
335 {
336 TRACE("Creating new writing block");
339 ctx->current_block_header.sectorSize = length;
340
341 // We need to save the track type for later compression
342 if(ctx->image_info.MetadataMediaType == OpticalDisc && ctx->track_entries != NULL)
343 {
344 const TrackEntry *track = NULL;
345 for(int i = 0; i < ctx->tracks_header.entries; i++)
346 if(sector_address >= ctx->track_entries[i].start && sector_address <= ctx->track_entries[i].end)
347 {
348 track = &ctx->track_entries[i];
349 break;
350 }
351
352 if(track != NULL)
353 {
354 ctx->current_track_type = track->type;
355
356 if(track->sequence == 0 && track->start == 0 && track->end == 0)
358 }
359 else
361
363 // JaguarCD stores data in audio tracks. FLAC is too inefficient, we need to use LZMA as data.
364 (ctx->image_info.MediaType == JaguarCD && track->session > 1 ||
365 // VideoNow stores video in audio tracks, and LZMA works better too.
369
370 if(ctx->compression_enabled)
371 {
374 else
376 }
377 else
379 }
380 else
381 {
383 if(ctx->compression_enabled)
385 else
387 }
388
389 uint32_t max_buffer_size =
391 TRACE("Setting max buffer size to %u bytes", max_buffer_size);
392
393 TRACE("Allocating memory for writing buffer");
394 ctx->writing_buffer = (uint8_t *)calloc(1, max_buffer_size);
395 if(ctx->writing_buffer == NULL)
396 {
397 FATAL("Could not allocate memory");
398
399 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
401 }
402 }
403
404 TRACE("Copying data to writing buffer at position %zu", ctx->writing_buffer_position);
405 memcpy(ctx->writing_buffer + ctx->writing_buffer_position, write_data, length);
406 TRACE("Advancing writing buffer position to %zu", ctx->writing_buffer_position + length);
407 ctx->writing_buffer_position += length;
408 TRACE("Advancing current block offset to %zu", ctx->current_block_offset + 1);
410
411 free(decrypted_buffer);
412
413 TRACE("Exiting aaruf_write_sector() = AARUF_STATUS_OK");
414 return AARUF_STATUS_OK;
415}
416
623AARU_EXPORT int32_t AARU_CALL aaruf_write_sector_long(void *context, uint64_t sector_address, bool negative,
624 const uint8_t *data, uint8_t sector_status, uint32_t length)
625{
626 TRACE("Entering aaruf_write_sector_long(%p, %" PRIu64 ", %d, %p, %u, %u)", context, sector_address, negative, data,
627 sector_status, length);
628
629 // Check context is correct AaruFormat context
630 if(context == NULL)
631 {
632 FATAL("Invalid context");
633
634 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_AARUFORMAT");
636 }
637
638 aaruformat_context *ctx = context;
639
640 // Not a libaaruformat context
641 if(ctx->magic != AARU_MAGIC)
642 {
643 FATAL("Invalid context");
644
645 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_AARUFORMAT");
647 }
648
649 // Check we are writing
650 if(!ctx->is_writing)
651 {
652 FATAL("Trying to write a read-only image");
653
654 TRACE("Exiting aaruf_write_sector() = AARUF_READ_ONLY");
655 return AARUF_READ_ONLY;
656 }
657
658 if(negative && sector_address > ctx->user_data_ddt_header.negative)
659 {
660 FATAL("Sector address out of bounds");
661
662 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_SECTOR_OUT_OF_BOUNDS");
664 }
665
666 if(!negative && sector_address > ctx->image_info.Sectors + ctx->user_data_ddt_header.overflow - 1)
667 {
668 FATAL("Sector address out of bounds");
669
670 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_SECTOR_OUT_OF_BOUNDS");
672 }
673
674 switch(ctx->image_info.MetadataMediaType)
675 {
676 case OpticalDisc:
677 {
678 TrackEntry track = {0};
679
680 for(int i = 0; i < ctx->tracks_header.entries; i++)
681 if(sector_address >= ctx->track_entries[i].start && sector_address <= ctx->track_entries[i].end)
682 {
683 track = ctx->track_entries[i];
684 break;
685 }
686
687 if(track.sequence == 0 && track.start == 0 && track.end == 0) track.type = kTrackTypeData;
688
689 uint64_t corrected_sector_address = sector_address;
690
691 // Calculate positive or negative sector
692 if(negative)
693 corrected_sector_address = ctx->user_data_ddt_header.negative - sector_address;
694 else
695 corrected_sector_address += ctx->user_data_ddt_header.negative;
696
697 uint64_t total_sectors =
699
700 // DVD long sector
701 if(length == 2064 && (ctx->image_info.MediaType == DVDROM || ctx->image_info.MediaType == PS2DVD ||
702 ctx->image_info.MediaType == SACD || ctx->image_info.MediaType == PS3DVD ||
703 ctx->image_info.MediaType == DVDR || ctx->image_info.MediaType == DVDRW ||
708 ctx->image_info.MediaType == Nuon))
709 {
710 if(ctx->sector_id == NULL) ctx->sector_id = calloc(1, 4 * total_sectors);
711 if(ctx->sector_ied == NULL) ctx->sector_ied = calloc(1, 2 * total_sectors);
712 if(ctx->sector_cpr_mai == NULL) ctx->sector_cpr_mai = calloc(1, 6 * total_sectors);
713 if(ctx->sector_edc == NULL) ctx->sector_edc = calloc(1, 4 * total_sectors);
714
715 memcpy(ctx->sector_id + corrected_sector_address * 4, data, 4);
716 memcpy(ctx->sector_ied + corrected_sector_address * 2, data + 4, 2);
717 memcpy(ctx->sector_cpr_mai + corrected_sector_address * 6, data + 6, 6);
718 memcpy(ctx->sector_edc + corrected_sector_address * 4, data + 2060, 4);
719
720 return aaruf_write_sector(context, sector_address, negative, data + 12, sector_status, 2048);
721 }
722
723 // Nintendo DVD long sector
724 if(length == 2064 && ctx->image_info.MediaType == GOD || ctx->image_info.MediaType == WOD)
725 {
726 if(ctx->sector_id == NULL) ctx->sector_id = calloc(1, 4 * total_sectors);
727 if(ctx->sector_ied == NULL) ctx->sector_ied = calloc(1, 2 * total_sectors);
728 if(ctx->sector_cpr_mai == NULL) ctx->sector_cpr_mai = calloc(1, 6 * total_sectors);
729 if(ctx->sector_edc == NULL) ctx->sector_edc = calloc(1, 4 * total_sectors);
730
731 memcpy(ctx->sector_id + corrected_sector_address * 4, data, 4);
732 memcpy(ctx->sector_ied + corrected_sector_address * 2, data + 4, 2);
733 memcpy(ctx->sector_cpr_mai + corrected_sector_address * 6, data + 2054, 6);
734 memcpy(ctx->sector_edc + corrected_sector_address * 4, data + 2060, 4);
735
736 return aaruf_write_sector(context, sector_address, negative, data + 6, sector_status, 2048);
737 }
738
739 // Blu-ray long sector
740 if(length == 2052 && (ctx->image_info.MediaType == BDROM || ctx->image_info.MediaType == BDR ||
741 ctx->image_info.MediaType == BDRE || ctx->image_info.MediaType == BDRXL ||
743 ctx->image_info.MediaType == XGD4 || ctx->image_info.MediaType == PS3BD ||
745 {
746 if(ctx->sector_edc == NULL) ctx->sector_edc = calloc(1, 4 * total_sectors);
747
748 memcpy(ctx->sector_edc + corrected_sector_address * 4, data + 2048, 4);
749
750 return aaruf_write_sector(context, sector_address, negative, data, sector_status, 2048);
751 }
752
753 if(length != 2352)
754 {
755 FATAL("Incorrect sector size");
756 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_INCORRECT_DATA_SIZE");
758 }
759
760 ctx->writing_long = true;
761
762 if(!ctx->rewinded)
763 {
764 if(sector_address <= ctx->last_written_block)
765 {
766 if(sector_address == 0 && !ctx->block_zero_written)
767 ctx->block_zero_written = true;
768 else
769 {
770 TRACE("Rewinded");
771 ctx->rewinded = true;
772
773 // Disable MD5 calculation
774 if(ctx->calculating_md5) ctx->calculating_md5 = false;
775 // Disable SHA1 calculation
776 if(ctx->calculating_sha1) ctx->calculating_sha1 = false;
777 // Disable SHA256 calculation
778 if(ctx->calculating_sha256) ctx->calculating_sha256 = false;
779 // Disable SpamSum calculation
780 if(ctx->calculating_spamsum) ctx->calculating_spamsum = false;
781 // Disable BLAKE3 calculation
782 if(ctx->calculating_blake3) ctx->calculating_blake3 = false;
783 }
784 }
785 else
786 ctx->last_written_block = sector_address;
787 }
788
789 // Calculate MD5 on-the-fly if requested and sector is within user sectors (not negative or overflow)
790 if(ctx->calculating_md5 && !negative && sector_address <= ctx->image_info.Sectors)
791 aaruf_md5_update(&ctx->md5_context, data, length);
792 // Calculate SHA1 on-the-fly if requested and sector is within user sectors (not negative or overflow)
793 if(ctx->calculating_sha1 && !negative && sector_address <= ctx->image_info.Sectors)
794 aaruf_sha1_update(&ctx->sha1_context, data, length);
795 // Calculate SHA256 on-the-fly if requested and sector is within user sectors (not negative or overflow)
796 if(ctx->calculating_sha256 && !negative && sector_address <= ctx->image_info.Sectors)
797 aaruf_sha256_update(&ctx->sha256_context, data, length);
798 // Calculate SpamSum on-the-fly if requested and sector is within user sectors (not negative or overflow)
799 if(ctx->calculating_spamsum && !negative && sector_address <= ctx->image_info.Sectors)
800 aaruf_spamsum_update(ctx->spamsum_context, data, length);
801 // Calculate BLAKE3 on-the-fly if requested and sector is within user sectors (not negative or overflow)
802 if(ctx->calculating_blake3 && !negative && sector_address <= ctx->image_info.Sectors)
803 blake3_hasher_update(ctx->blake3_context, data, length);
804
805 bool prefix_correct;
806
807 // Split raw cd sector data in prefix (sync, header), user data and suffix (edc, ecc p, ecc q)
808 switch(track.type)
809 {
810 case kTrackTypeAudio:
811 case kTrackTypeData:
812 return aaruf_write_sector(context, sector_address, negative, data, sector_status, length);
814
815 // If we do not have a DDT V2 for sector prefix, create one
816 if(ctx->sector_prefix_ddt2 == NULL)
817 {
818 ctx->sector_prefix_ddt2 =
819 calloc(1, sizeof(uint64_t) * (ctx->user_data_ddt_header.negative + ctx->image_info.Sectors +
821
822 if(ctx->sector_prefix_ddt2 == NULL)
823 {
824 FATAL("Could not allocate memory for CD sector prefix DDT");
825
826 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
828 }
829 }
830
831 // If we do not have a DDT V2 for sector suffix, create one
832 if(ctx->sector_suffix_ddt2 == NULL)
833 {
834 ctx->sector_suffix_ddt2 =
835 calloc(1, sizeof(uint64_t) * (ctx->user_data_ddt_header.negative + ctx->image_info.Sectors +
837
838 if(ctx->sector_suffix_ddt2 == NULL)
839 {
840 FATAL("Could not allocate memory for CD sector prefix DDT");
841
842 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
844 }
845 }
846
847 if(ctx->sector_prefix == NULL)
848 {
851 ctx->sector_prefix = malloc(ctx->sector_prefix_length);
852
853 if(ctx->sector_prefix == NULL)
854 {
855 FATAL("Could not allocate memory for CD sector prefix buffer");
856
857 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
859 }
860 }
861
862 if(ctx->sector_suffix == NULL)
863 {
867 ctx->sector_suffix = malloc(ctx->sector_suffix_length);
868
869 if(ctx->sector_suffix == NULL)
870 {
871 FATAL("Could not allocate memory for CD sector suffix buffer");
872
873 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
875 }
876 }
877
878 bool empty = true;
879
880 for(int i = 0; i < length; i++)
881 if(data[i] != 0)
882 {
883 empty = false;
884 break;
885 }
886
887 if(empty)
888 {
889 ctx->sector_prefix_ddt2[corrected_sector_address] = SectorStatusNotDumped;
890 ctx->sector_suffix_ddt2[corrected_sector_address] = SectorStatusNotDumped;
891 ctx->dirty_sector_prefix_ddt = true; // Mark prefix DDT as dirty
892 ctx->dirty_sector_suffix_ddt = true; // Mark suffix DDT as dirty
893 return aaruf_write_sector(context, sector_address, negative, data + 16, SectorStatusNotDumped,
894 2048);
895 }
896
897 prefix_correct = true;
898
899 if(data[0x00] != 0x00 || data[0x01] != 0xFF || data[0x02] != 0xFF || data[0x03] != 0xFF ||
900 data[0x04] != 0xFF || data[0x05] != 0xFF || data[0x06] != 0xFF || data[0x07] != 0xFF ||
901 data[0x08] != 0xFF || data[0x09] != 0xFF || data[0x0A] != 0xFF || data[0x0B] != 0x00 ||
902 data[0x0F] != 0x01)
903 prefix_correct = false;
904
905 if(prefix_correct)
906 {
907 const int minute = (data[0x0C] >> 4) * 10 + (data[0x0C] & 0x0F);
908 const int second = (data[0x0D] >> 4) * 10 + (data[0x0D] & 0x0F);
909 const int frame = (data[0x0E] >> 4) * 10 + (data[0x0E] & 0x0F);
910 const int stored_lba = minute * 60 * 75 + second * 75 + frame - 150;
911 prefix_correct = stored_lba == sector_address;
912 }
913
914 if(prefix_correct)
915 ctx->sector_prefix_ddt2[corrected_sector_address] = (uint64_t)SectorStatusMode1Correct << 60;
916 else
917 {
918 // Copy CD prefix from data buffer to prefix buffer
919 memcpy(ctx->sector_prefix + ctx->sector_prefix_offset, data, 16);
920 ctx->sector_prefix_ddt2[corrected_sector_address] = (uint64_t)(ctx->sector_prefix_offset / 16);
921 ctx->sector_prefix_ddt2[corrected_sector_address] |= (uint64_t)SectorStatusErrored << 60;
922 ctx->sector_prefix_offset += 16;
923 ctx->dirty_sector_prefix_block = true; // Mark prefix block as dirty
924
925 // Grow prefix buffer if needed
927 {
928 ctx->sector_prefix_length *= 2;
929 ctx->sector_prefix = realloc(ctx->sector_prefix, ctx->sector_prefix_length);
930
931 if(ctx->sector_prefix == NULL)
932 {
933 FATAL("Could not allocate memory for CD sector prefix buffer");
934
935 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
937 }
938 }
939 }
940 ctx->dirty_sector_prefix_ddt = true; // Mark prefix DDT as dirty
941
942 const bool suffix_correct = aaruf_ecc_cd_is_suffix_correct(ctx->ecc_cd_context, data);
943
944 if(suffix_correct)
945 ctx->sector_suffix_ddt2[corrected_sector_address] = (uint64_t)SectorStatusMode1Correct << 60;
946 else
947 {
948 // Copy CD suffix from data buffer to suffix buffer
949 memcpy(ctx->sector_suffix + ctx->sector_suffix_offset, data + 2064, 288);
950 ctx->sector_suffix_ddt2[corrected_sector_address] = (uint64_t)(ctx->sector_suffix_offset / 288);
951 ctx->sector_suffix_ddt2[corrected_sector_address] |= (uint64_t)SectorStatusErrored << 60;
952 ctx->sector_suffix_offset += 288;
953 ctx->dirty_sector_suffix_block = true; // Mark suffix block as dirty
954
955 // Grow suffix buffer if needed
957 {
958 ctx->sector_suffix_length *= 2;
959 ctx->sector_suffix = realloc(ctx->sector_suffix, ctx->sector_suffix_length);
960
961 if(ctx->sector_suffix == NULL)
962 {
963 FATAL("Could not allocate memory for CD sector suffix buffer");
964
965 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
967 }
968 }
969 }
970 ctx->dirty_sector_suffix_ddt = true; // Mark suffix DDT as dirty
971
972 return aaruf_write_sector(context, sector_address, negative, data + 16, SectorStatusMode1Correct,
973 2048);
977 // If we do not have a DDT V2 for sector prefix, create one
978 if(ctx->sector_prefix_ddt2 == NULL)
979 {
980 ctx->sector_prefix_ddt2 =
981 calloc(1, sizeof(uint64_t) * (ctx->user_data_ddt_header.negative + ctx->image_info.Sectors +
983
984 if(ctx->sector_prefix_ddt2 == NULL)
985 {
986 FATAL("Could not allocate memory for CD sector prefix DDT");
987
988 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
990 }
991 }
992
993 // If we do not have a DDT V2 for sector suffix, create one
994 if(ctx->sector_suffix_ddt2 == NULL)
995 {
996 ctx->sector_suffix_ddt2 =
997 calloc(1, sizeof(uint64_t) * (ctx->user_data_ddt_header.negative + ctx->image_info.Sectors +
999
1000 if(ctx->sector_suffix_ddt2 == NULL)
1001 {
1002 FATAL("Could not allocate memory for CD sector prefix DDT");
1003
1004 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
1006 }
1007 }
1008
1009 if(ctx->sector_prefix == NULL)
1010 {
1013 ctx->sector_prefix = malloc(ctx->sector_prefix_length);
1014
1015 if(ctx->sector_prefix == NULL)
1016 {
1017 FATAL("Could not allocate memory for CD sector prefix buffer");
1018
1019 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
1021 }
1022 }
1023
1024 if(ctx->sector_suffix == NULL)
1025 {
1027 288 * (ctx->user_data_ddt_header.negative + ctx->image_info.Sectors +
1029 ctx->sector_suffix = malloc(ctx->sector_suffix_length);
1030
1031 if(ctx->sector_suffix == NULL)
1032 {
1033 FATAL("Could not allocate memory for CD sector suffix buffer");
1034
1035 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
1037 }
1038 }
1039
1040 empty = true;
1041
1042 for(int i = 0; i < length; i++)
1043 if(data[i] != 0)
1044 {
1045 empty = false;
1046 break;
1047 }
1048
1049 if(empty)
1050 {
1051 ctx->sector_prefix_ddt2[corrected_sector_address] = SectorStatusNotDumped;
1052 ctx->sector_suffix_ddt2[corrected_sector_address] = SectorStatusNotDumped;
1053 return aaruf_write_sector(context, sector_address, negative, data + 16, SectorStatusNotDumped,
1054 2328);
1055 }
1056
1057 const bool form2 = (data[18] & 0x20) == 0x20 || (data[22] & 0x20) == 0x20;
1058
1059 prefix_correct = true;
1060
1061 if(data[0x00] != 0x00 || data[0x01] != 0xFF || data[0x02] != 0xFF || data[0x03] != 0xFF ||
1062 data[0x04] != 0xFF || data[0x05] != 0xFF || data[0x06] != 0xFF || data[0x07] != 0xFF ||
1063 data[0x08] != 0xFF || data[0x09] != 0xFF || data[0x0A] != 0xFF || data[0x0B] != 0x00 ||
1064 data[0x0F] != 0x02)
1065 prefix_correct = false;
1066
1067 if(prefix_correct)
1068 {
1069 const int minute = (data[0x0C] >> 4) * 10 + (data[0x0C] & 0x0F);
1070 const int second = (data[0x0D] >> 4) * 10 + (data[0x0D] & 0x0F);
1071 const int frame = (data[0x0E] >> 4) * 10 + (data[0x0E] & 0x0F);
1072 const int stored_lba = minute * 60 * 75 + second * 75 + frame - 150;
1073 prefix_correct = stored_lba == sector_address;
1074 }
1075
1076 if(prefix_correct)
1077 ctx->sector_prefix_ddt2[corrected_sector_address] =
1078 (uint64_t)(form2 ? SectorStatusMode2Form2Ok : SectorStatusMode2Form1Ok) << 60;
1079 else
1080 {
1081 // Copy CD prefix from data buffer to prefix buffer
1082 memcpy(ctx->sector_prefix + ctx->sector_prefix_offset, data, 16);
1083 ctx->sector_prefix_ddt2[corrected_sector_address] = (uint32_t)(ctx->sector_prefix_offset / 16);
1084 ctx->sector_prefix_ddt2[corrected_sector_address] |= (uint64_t)SectorStatusErrored << 60;
1085 ctx->sector_prefix_offset += 16;
1086 ctx->dirty_sector_prefix_block = true; // Mark prefix block as dirty
1087
1088 // Grow prefix buffer if needed
1090 {
1091 ctx->sector_prefix_length *= 2;
1092 ctx->sector_prefix = realloc(ctx->sector_prefix, ctx->sector_prefix_length);
1093
1094 if(ctx->sector_prefix == NULL)
1095 {
1096 FATAL("Could not allocate memory for CD sector prefix buffer");
1097
1098 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
1100 }
1101 }
1102 }
1103 ctx->dirty_sector_prefix_ddt = true; // Mark prefix DDT as dirty
1104
1105 if(ctx->mode2_subheaders == NULL)
1106 {
1107 ctx->mode2_subheaders =
1108 calloc(1, 8 * (ctx->user_data_ddt_header.negative + ctx->image_info.Sectors +
1110
1111 if(ctx->mode2_subheaders == NULL)
1112 {
1113 FATAL("Could not allocate memory for CD mode 2 subheader buffer");
1114
1115 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
1117 }
1118 }
1119
1120 if(form2)
1121 {
1122 const uint32_t computed_edc = aaruf_edc_cd_compute(ctx->ecc_cd_context, 0, data, 0x91C, 0x10);
1123 uint32_t edc = 0;
1124 memcpy(&edc, data + 0x92C, sizeof(edc));
1125 const bool correct_edc = computed_edc == edc;
1126
1127 if(correct_edc)
1128 ctx->sector_suffix_ddt2[corrected_sector_address] = (uint64_t)SectorStatusMode2Form2Ok
1129 << 60;
1130 else if(edc == 0)
1131 ctx->sector_suffix_ddt2[corrected_sector_address] = (uint64_t)SectorStatusMode2Form2NoCrc
1132 << 60;
1133 else
1134 {
1135 // Copy CD suffix from data buffer to suffix buffer
1136 memcpy(ctx->sector_suffix + ctx->sector_suffix_offset, data + 2348, 4);
1137 ctx->sector_suffix_ddt2[corrected_sector_address] =
1138 (uint64_t)(ctx->sector_suffix_offset / 288);
1139 ctx->sector_suffix_ddt2[corrected_sector_address] |= (uint64_t)SectorStatusErrored << 60;
1140 ctx->sector_suffix_offset += 288;
1141 ctx->dirty_sector_suffix_block = true; // Mark suffix block as dirty
1142
1143 // Grow suffix buffer if needed
1145 {
1146 ctx->sector_suffix_length *= 2;
1147 ctx->sector_suffix = realloc(ctx->sector_suffix, ctx->sector_suffix_length);
1148
1149 if(ctx->sector_suffix == NULL)
1150 {
1151 FATAL("Could not allocate memory for CD sector suffix buffer");
1152
1153 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
1155 }
1156 }
1157 }
1158
1159 // Copy subheader from data buffer to subheader buffer
1160 memcpy(ctx->mode2_subheaders + corrected_sector_address * 8, data + 0x10, 8);
1161 ctx->dirty_sector_prefix_ddt = true;
1162 ctx->dirty_sector_suffix_ddt = true;
1163 ctx->dirty_mode2_subheaders_block = true;
1164 return aaruf_write_sector(context, sector_address, negative, data + 24,
1166 : correct_edc ? SectorStatusMode2Form2Ok
1168 2324);
1169 }
1170
1171 const bool correct_ecc = aaruf_ecc_cd_is_suffix_correct_mode2(ctx->ecc_cd_context, data);
1172 const uint32_t computed_edc = aaruf_edc_cd_compute(ctx->ecc_cd_context, 0, data, 0x808, 0x10);
1173 uint32_t edc = 0;
1174 memcpy(&edc, data + 0x818, sizeof(edc));
1175 const bool correct_edc = computed_edc == edc;
1176
1177 if(correct_ecc && correct_edc)
1178 ctx->sector_suffix_ddt2[corrected_sector_address] = (uint64_t)SectorStatusMode2Form1Ok << 60;
1179 else
1180 {
1181 // Copy CD suffix from data buffer to suffix buffer
1182 memcpy(ctx->sector_suffix + ctx->sector_suffix_offset, data + 2072, 280);
1183 ctx->sector_suffix_ddt2[corrected_sector_address] = (uint64_t)(ctx->sector_suffix_offset / 288);
1184 ctx->sector_suffix_ddt2[corrected_sector_address] |= (uint64_t)SectorStatusErrored << 60;
1185 ctx->sector_suffix_offset += 288;
1186 ctx->dirty_sector_suffix_block = true; // Mark suffix block as dirty
1187
1188 // Grow suffix buffer if needed
1190 {
1191 ctx->sector_suffix_length *= 2;
1192 ctx->sector_suffix = realloc(ctx->sector_suffix, ctx->sector_suffix_length);
1193
1194 if(ctx->sector_suffix == NULL)
1195 {
1196 FATAL("Could not allocate memory for CD sector suffix buffer");
1197
1198 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
1200 }
1201 }
1202 }
1203
1204 // Copy subheader from data buffer to subheader buffer
1205 memcpy(ctx->mode2_subheaders + corrected_sector_address * 8, data + 0x10, 8);
1206 ctx->dirty_sector_prefix_ddt = true;
1207 ctx->dirty_sector_suffix_ddt = true;
1208 ctx->dirty_mode2_subheaders_block = true;
1209 return aaruf_write_sector(
1210 context, sector_address, negative, data + 24,
1211 correct_edc && correct_ecc ? SectorStatusMode2Form1Ok : SectorStatusErrored, 2048);
1212 }
1213
1214 break;
1215 }
1216 case BlockMedia:
1217 switch(ctx->image_info.MediaType)
1218 {
1219 case AppleFileWare:
1220 case AppleProfile:
1221 case AppleSonyDS:
1222 case AppleSonySS:
1223 case AppleWidget:
1224 case PriamDataTower:
1225 {
1226 uint8_t *newTag;
1227 int newTagSize = 0;
1228
1229 switch(length - 512)
1230 {
1231 // Sony tag
1232 case 12:
1233 {
1234 const sony_tag decoded_sony_tag = bytes_to_sony_tag(data + 512);
1235
1237 {
1238 const profile_tag decoded_profile_tag = sony_tag_to_profile(decoded_sony_tag);
1239 newTag = profile_tag_to_bytes(decoded_profile_tag);
1240 newTagSize = 20;
1241 }
1242 else if(ctx->image_info.MediaType == PriamDataTower)
1243 {
1244 const priam_tag decoded_priam_tag = sony_tag_to_priam(decoded_sony_tag);
1245 newTag = priam_tag_to_bytes(decoded_priam_tag);
1246 newTagSize = 24;
1247 }
1248 else if(ctx->image_info.MediaType == AppleSonyDS ||
1250 {
1251 newTag = malloc(12);
1252 memcpy(newTag, data + 512, 12);
1253 newTagSize = 12;
1254 }
1255 break;
1256 }
1257 // Profile tag
1258 case 20:
1259 {
1260 const profile_tag decoded_profile_tag = bytes_to_profile_tag(data + 512);
1261
1263 {
1264 newTag = malloc(20);
1265 memcpy(newTag, data + 512, 20);
1266 newTagSize = 20;
1267 }
1268 else if(ctx->image_info.MediaType == PriamDataTower)
1269 {
1270 const priam_tag decoded_priam_tag = profile_tag_to_priam(decoded_profile_tag);
1271 newTag = priam_tag_to_bytes(decoded_priam_tag);
1272 newTagSize = 24;
1273 }
1274 else if(ctx->image_info.MediaType == AppleSonyDS ||
1276 {
1277 const sony_tag decoded_sony_tag = profile_tag_to_sony(decoded_profile_tag);
1278 newTag = sony_tag_to_bytes(decoded_sony_tag);
1279 newTagSize = 12;
1280 }
1281 break;
1282 }
1283 // Priam tag
1284 case 24:
1285 {
1286 const priam_tag decoded_priam_tag = bytes_to_priam_tag(data + 512);
1288 {
1289 const profile_tag decoded_profile_tag = priam_tag_to_profile(decoded_priam_tag);
1290 newTag = profile_tag_to_bytes(decoded_profile_tag);
1291 newTagSize = 20;
1292 }
1293 else if(ctx->image_info.MediaType == PriamDataTower)
1294 {
1295 newTag = malloc(24);
1296 memcpy(newTag, data + 512, 24);
1297 newTagSize = 24;
1298 }
1299 else if(ctx->image_info.MediaType == AppleSonyDS ||
1301 {
1302 const sony_tag decoded_sony_tag = priam_tag_to_sony(decoded_priam_tag);
1303 newTag = sony_tag_to_bytes(decoded_sony_tag);
1304 newTagSize = 12;
1305 }
1306 break;
1307 }
1308 case 0:
1309 newTagSize = 0;
1310 break;
1311 default:
1312 FATAL("Incorrect sector size");
1313 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_INCORRECT_DATA_SIZE");
1315 }
1316
1317 if(newTagSize == 0)
1318 return aaruf_write_sector(context, sector_address, negative, data, sector_status, 512);
1319
1320 if(ctx->sector_subchannel == NULL)
1321 {
1322 ctx->sector_subchannel =
1323 calloc(1, newTagSize * (ctx->image_info.Sectors + ctx->user_data_ddt_header.overflow));
1324
1325 if(ctx->sector_subchannel == NULL)
1326 {
1327 FATAL("Could not allocate memory for sector subchannel DDT");
1328
1329 free(newTag);
1330
1331 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
1333 }
1334 }
1335
1336 memcpy(ctx->sector_subchannel + sector_address * newTagSize, newTag, newTagSize);
1337 ctx->dirty_sector_subchannel_block = true; // Mark subchannel block as dirty
1338 free(newTag);
1339
1340 return aaruf_write_sector(context, sector_address, negative, data, sector_status, 512);
1341 }
1342 default:
1344 }
1345 default:
1346 TRACE("Exiting aaruf_write_sector() = AARUF_ERROR_INCORRECT_MEDIA_TYPE");
1348 }
1349
1350 // Fallback return when media type branch does not produce a value (satisfy non-void contract)
1352}
1353
1537{
1538 // Not a libaaruformat context
1539 if(ctx->magic != AARU_MAGIC) return AARUF_ERROR_NOT_AARUFORMAT;
1540
1541 // Check we are writing
1542 if(!ctx->is_writing) return AARUF_READ_ONLY;
1543
1545
1546 TRACE("Initializing CRC64 context");
1548 TRACE("Updating CRC64");
1551
1552 uint8_t lzma_properties[LZMA_PROPERTIES_LENGTH] = {0};
1553 uint8_t *cmp_buffer = NULL;
1554
1556 {
1557 case kCompressionNone:
1558 break;
1559 case kCompressionFlac:
1560 cmp_buffer = malloc(ctx->current_block_header.length * 2);
1561 if(cmp_buffer == NULL)
1562 {
1563 FATAL("Could not allocate buffer for compressed data");
1565 }
1566 const uint32_t current_samples = ctx->current_block_offset * SAMPLES_PER_SECTOR;
1567 uint32_t flac_block_size = ctx->current_block_offset * SAMPLES_PER_SECTOR;
1568
1569 if(flac_block_size > MAX_FLAKE_BLOCK) flac_block_size = MAX_FLAKE_BLOCK;
1570 if(flac_block_size < MIN_FLAKE_BLOCK) flac_block_size = MIN_FLAKE_BLOCK;
1571
1572 const long remaining = current_samples % flac_block_size;
1573
1574 // Fill FLAC block
1575 if(remaining != 0)
1576 for(int r = 0; r < remaining * 4; r++) ctx->writing_buffer[ctx->writing_buffer_position + r] = 0;
1577
1579 cmp_buffer, ctx->current_block_header.length * 2, ctx->writing_buffer, ctx->current_block_header.length,
1580 flac_block_size, true, false, "hamming", 12, 15, true, false, 0, 8, "Aaru", 4);
1581
1583 {
1585 free(cmp_buffer);
1586 }
1587
1588 break;
1589 case kCompressionLzma:
1590 cmp_buffer = malloc(ctx->current_block_header.length * 2);
1591 if(cmp_buffer == NULL)
1592 {
1593 FATAL("Could not allocate buffer for compressed data");
1595 }
1596
1597 size_t dst_size = ctx->current_block_header.length * 2;
1598 size_t props_size = LZMA_PROPERTIES_LENGTH;
1599 aaruf_lzma_encode_buffer(cmp_buffer, &dst_size, ctx->writing_buffer, ctx->current_block_header.length,
1600 lzma_properties, &props_size, 9, ctx->lzma_dict_size, 4, 0, 2, 273, LZMA_THREADS(ctx));
1601
1602 ctx->current_block_header.cmpLength = (uint32_t)dst_size;
1603
1605 {
1607 free(cmp_buffer);
1608 }
1609
1610 break;
1611 case kCompressionZstd:
1612 cmp_buffer = malloc(ctx->current_block_header.length * 2);
1613 if(cmp_buffer == NULL)
1614 {
1615 FATAL("Could not allocate buffer for compressed data");
1617 }
1618
1619 size_t zstd_dst_size =
1622
1623 if(zstd_dst_size == 0)
1624 {
1626 free(cmp_buffer);
1627 cmp_buffer = NULL;
1628 }
1629 else
1630 {
1631 ctx->current_block_header.cmpLength = (uint32_t)zstd_dst_size;
1632
1634 {
1636 free(cmp_buffer);
1637 cmp_buffer = NULL;
1638 }
1639 else
1640 ctx->has_zstd_blocks = true;
1641 }
1642
1643 break;
1644 default:
1645 FATAL("Invalid compression type");
1647 }
1648
1650 {
1653 }
1654 else
1656
1659
1660 // Add to index
1661 TRACE("Adding block to index");
1662 IndexEntry index_entry;
1663 index_entry.blockType = DataBlock;
1664 index_entry.dataType = kDataTypeUserData;
1665 index_entry.offset = ctx->next_block_position;
1666
1667 utarray_push_back(ctx->index_entries, &index_entry);
1668 ctx->dirty_index_block = true; // Mark index block as dirty
1669 TRACE("Block added to index at offset %" PRIu64, index_entry.offset);
1670
1671 // Write block header to file
1672
1673 // Move to expected block position
1675
1676 // Write block header
1677 if(fwrite(&ctx->current_block_header, sizeof(BlockHeader), 1, ctx->imageStream) != 1)
1679
1680 // Write block data
1682 fwrite(lzma_properties, LZMA_PROPERTIES_LENGTH, 1, ctx->imageStream) != 1)
1683 {
1684 free(cmp_buffer);
1686 }
1687
1689 {
1690 if(fwrite(ctx->writing_buffer, ctx->current_block_header.length, 1, ctx->imageStream) != 1)
1692 }
1693 else
1694 {
1695 if(fwrite(cmp_buffer, ctx->current_block_header.cmpLength, 1, ctx->imageStream) != 1)
1696 {
1697 free(cmp_buffer);
1699 }
1700 }
1701
1702 /* --- Erasure coding parity accumulation ---
1703 * Call BEFORE freeing cmp_buffer / writing_buffer.
1704 * For LZMA: cmpLength already includes LZMA_PROPERTIES_LENGTH, but the properties
1705 * were written separately. We pass them as lzma_props parameter.
1706 * For non-LZMA compressed: cmp_buffer holds all payload.
1707 * For uncompressed: writing_buffer holds payload. */
1708 if(ctx->ec_enabled)
1709 {
1710 const uint8_t *ec_payload;
1711 uint32_t ec_payload_size;
1712 const uint8_t *ec_lzma_props = NULL;
1713
1715 {
1716 ec_payload = ctx->writing_buffer;
1717 ec_payload_size = ctx->current_block_header.length;
1718 }
1720 {
1721 ec_payload = cmp_buffer;
1722 ec_payload_size = ctx->current_block_header.cmpLength - LZMA_PROPERTIES_LENGTH;
1723 ec_lzma_props = lzma_properties;
1724 }
1725 else
1726 {
1727 ec_payload = cmp_buffer;
1728 ec_payload_size = ctx->current_block_header.cmpLength;
1729 }
1730
1731 ec_accumulate_data_block(ctx, &ctx->current_block_header, ec_lzma_props,
1732 ec_payload, ec_payload_size, index_entry.offset);
1733 }
1734
1735 /* Free compressed buffer (if compression was used) */
1737 free(cmp_buffer);
1738
1739 // Update nextBlockPosition to point to the next available aligned position
1740 const uint64_t block_total_size = sizeof(BlockHeader) + ctx->current_block_header.cmpLength;
1741 const uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
1742 ctx->next_block_position = ctx->next_block_position + block_total_size + alignment_mask & ~alignment_mask;
1743 TRACE("Updated nextBlockPosition to %" PRIu64, ctx->next_block_position);
1744
1745 // Clear values
1746 free(ctx->writing_buffer);
1747 ctx->writing_buffer = NULL;
1748 ctx->current_block_offset = 0;
1749 memset(&ctx->current_block_header, 0, sizeof(BlockHeader));
1751 ctx->writing_buffer_position = 0;
1752
1753 return AARUF_STATUS_OK;
1754}
1755
2003AARU_EXPORT int32_t AARU_CALL aaruf_write_media_tag(void *context, const uint8_t *data, const int32_t type,
2004 const uint32_t length)
2005{
2006 TRACE("Entering aaruf_write_media_tag(%p, %p, %d, %d)", context, data, type, length);
2007
2008 // Check context is correct AaruFormat context
2009 if(context == NULL)
2010 {
2011 FATAL("Invalid context");
2012
2013 TRACE("Exiting aaruf_write_media_tag() = AARUF_ERROR_NOT_AARUFORMAT");
2015 }
2016
2017 aaruformat_context *ctx = context;
2018
2019 // Not a libaaruformat context
2020 if(ctx->magic != AARU_MAGIC)
2021 {
2022 FATAL("Invalid context");
2023
2024 TRACE("Exiting aaruf_write_media_tag() = AARUF_ERROR_NOT_AARUFORMAT");
2026 }
2027
2028 // Check we are writing
2029 if(!ctx->is_writing)
2030 {
2031 FATAL("Trying to write a read-only image");
2032
2033 TRACE("Exiting aaruf_write_media_tag() = AARUF_READ_ONLY");
2034 return AARUF_READ_ONLY;
2035 }
2036
2037 if(data == NULL || length == 0)
2038 {
2039 FATAL("Invalid data or length");
2041 }
2042
2043 uint8_t *new_data = malloc(length);
2044
2045 if(new_data == NULL)
2046 {
2047 FATAL("Could not allocate memory for media tag");
2049 }
2050 memcpy(new_data, data, length);
2051
2052 mediaTagEntry *media_tag = malloc(sizeof(mediaTagEntry));
2053 mediaTagEntry *old_media_tag = NULL;
2054
2055 if(media_tag == NULL)
2056 {
2057 TRACE("Cannot allocate memory for media tag entry.");
2058 free(new_data);
2060 }
2061
2062 memset(media_tag, 0, sizeof(mediaTagEntry));
2063
2064 media_tag->type = type;
2065 media_tag->data = new_data;
2066 media_tag->length = length;
2067
2068 HASH_REPLACE_INT(ctx->mediaTags, type, media_tag, old_media_tag);
2069
2070 if(old_media_tag != NULL)
2071 {
2072 TRACE("Replaced media tag with type %d", old_media_tag->type);
2073 free(old_media_tag->data);
2074 free(old_media_tag);
2075 old_media_tag = NULL;
2076 }
2077
2078 ctx->dirty_media_tags = true; // Mark media tags as dirty
2079 TRACE("Exiting aaruf_write_media_tag() = AARUF_STATUS_OK");
2080 return AARUF_STATUS_OK;
2081}
2082
2273AARU_EXPORT int32_t AARU_CALL aaruf_write_sector_tag(void *context, const uint64_t sector_address, const bool negative,
2274 const uint8_t *data, const size_t length, const int32_t tag)
2275{
2276 TRACE("Entering aaruf_write_sector_tag(%p, %" PRIu64 ", %d, %p, %zu, %d)", context, sector_address, negative, data,
2277 length, tag);
2278
2279 // Check context is correct AaruFormat context
2280 if(context == NULL)
2281 {
2282 FATAL("Invalid context");
2283
2284 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_NOT_AARUFORMAT");
2286 }
2287
2288 aaruformat_context *ctx = context;
2289
2290 // Not a libaaruformat context
2291 if(ctx->magic != AARU_MAGIC)
2292 {
2293 FATAL("Invalid context");
2294
2295 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_NOT_AARUFORMAT");
2297 }
2298
2299 // Check we are writing
2300 if(!ctx->is_writing)
2301 {
2302 FATAL("Trying to write a read-only image");
2303
2304 TRACE("Exiting aaruf_write_sector_tag() = AARUF_READ_ONLY");
2305 return AARUF_READ_ONLY;
2306 }
2307
2308 if(negative && sector_address > ctx->user_data_ddt_header.negative)
2309 {
2310 FATAL("Sector address out of bounds");
2311
2312 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_SECTOR_OUT_OF_BOUNDS");
2314 }
2315
2316 if(!negative && sector_address > ctx->image_info.Sectors + ctx->user_data_ddt_header.overflow - 1)
2317 {
2318 FATAL("Sector address out of bounds");
2319
2320 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_SECTOR_OUT_OF_BOUNDS");
2322 }
2323
2324 if(data == NULL || length == 0)
2325 {
2326 FATAL("Invalid data or length");
2328 }
2329
2330 uint64_t corrected_sector_address = sector_address;
2331
2332 // Calculate positive or negative sector
2333 if(negative)
2334 corrected_sector_address = ctx->user_data_ddt_header.negative - sector_address;
2335 else
2336 corrected_sector_address += ctx->user_data_ddt_header.negative;
2337
2338 const uint64_t total_sectors =
2340
2341 switch(tag)
2342 {
2345 {
2346 FATAL("Invalid media type for tag");
2347 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_MEDIA_TYPE");
2349 }
2350
2351 if(length != 1)
2352 {
2353 FATAL("Incorrect tag size");
2354 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_DATA_SIZE");
2356 }
2357
2358 for(int i = 0; i < ctx->tracks_header.entries; i++)
2359 if(sector_address >= ctx->track_entries[i].start && sector_address <= ctx->track_entries[i].end)
2360 {
2361 ctx->track_entries[i].flags = data[0];
2362 ctx->dirty_tracks_block = true; // Mark tracks block as dirty
2363 TRACE("Exiting aaruf_write_sector_tag() = AARUF_STATUS_OK");
2364 return AARUF_STATUS_OK;
2365 }
2366
2367 FATAL("Track not found");
2371 {
2372 FATAL("Invalid media type for tag");
2373 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_MEDIA_TYPE");
2375 }
2376
2377 if(length != 12)
2378 {
2379 FATAL("Incorrect tag size");
2380 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_DATA_SIZE");
2382 }
2383
2384 for(int i = 0; i < ctx->tracks_header.entries; i++)
2385 if(sector_address >= ctx->track_entries[i].start && sector_address <= ctx->track_entries[i].end)
2386 {
2387 memcpy(ctx->track_entries[i].isrc, data, 12);
2388 ctx->dirty_tracks_block = true; // Mark tracks block as dirty
2389 TRACE("Exiting aaruf_write_sector_tag() = AARUF_STATUS_OK");
2390 return AARUF_STATUS_OK;
2391 }
2392
2393 FATAL("Track not found");
2397 {
2398 FATAL("Invalid media type for tag");
2399 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_MEDIA_TYPE");
2401 }
2402
2403 if(length != 96)
2404 {
2405 FATAL("Incorrect tag size");
2406 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_DATA_SIZE");
2408 }
2409
2410 if(ctx->sector_subchannel == NULL) ctx->sector_subchannel = calloc(1, 96 * total_sectors);
2411
2412 if(ctx->sector_subchannel == NULL)
2413 {
2414 FATAL("Could not allocate memory for sector subchannel");
2415
2416 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
2418 }
2419
2420 memcpy(ctx->sector_subchannel + corrected_sector_address * 96, data, 96);
2421 ctx->dirty_sector_subchannel_block = true; // Mark subchannel block as dirty
2422 TRACE("Exiting aaruf_write_sector_tag() = AARUF_STATUS_OK");
2423 return AARUF_STATUS_OK;
2424 case kSectorTagDvdCmi:
2426 {
2427 FATAL("Invalid media type for tag");
2428 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_MEDIA_TYPE");
2430 }
2431
2432 if(length != 1)
2433 {
2434 FATAL("Incorrect tag size");
2435 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_DATA_SIZE");
2437 }
2438
2439 if(ctx->sector_cpr_mai == NULL) ctx->sector_cpr_mai = calloc(1, 6 * total_sectors);
2440
2441 if(ctx->sector_cpr_mai == NULL)
2442 {
2443 FATAL("Could not allocate memory for sector CPR/MAI");
2444
2445 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
2447 }
2448
2449 memcpy(ctx->sector_cpr_mai + corrected_sector_address * 6, data, 1);
2450 ctx->dirty_dvd_long_sector_blocks = true; // Mark DVD long sector blocks as dirty
2451 TRACE("Exiting aaruf_write_sector_tag() = AARUF_STATUS_OK");
2452 return AARUF_STATUS_OK;
2455 {
2456 FATAL("Invalid media type for tag");
2457 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_MEDIA_TYPE");
2459 }
2460
2461 if(length != 1)
2462 {
2463 FATAL("Incorrect tag size");
2464 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_DATA_SIZE");
2466 }
2467
2468 if(ctx->sector_id == NULL) ctx->sector_id = calloc(1, 4 * total_sectors);
2469
2470 if(ctx->sector_id == NULL)
2471 {
2472 FATAL("Could not allocate memory for sector ID");
2473
2474 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
2476 }
2477
2478 memcpy(ctx->sector_id + corrected_sector_address * 4, data, 1);
2479 TRACE("Exiting aaruf_write_sector_tag() = AARUF_STATUS_OK");
2480 return AARUF_STATUS_OK;
2483 {
2484 FATAL("Invalid media type for tag");
2485 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_MEDIA_TYPE");
2487 }
2488
2489 if(length != 3)
2490 {
2491 FATAL("Incorrect tag size");
2492 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_DATA_SIZE");
2494 }
2495
2496 if(ctx->sector_id == NULL) ctx->sector_id = calloc(1, 4 * total_sectors);
2497
2498 if(ctx->sector_id == NULL)
2499 {
2500 FATAL("Could not allocate memory for sector ID");
2501
2502 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
2504 }
2505
2506 memcpy(ctx->sector_id + corrected_sector_address * 4 + 1, data, 3);
2507 ctx->dirty_dvd_long_sector_blocks = true; // Mark DVD long sector blocks as dirty
2508 TRACE("Exiting aaruf_write_sector_tag() = AARUF_STATUS_OK");
2509 return AARUF_STATUS_OK;
2512 {
2513 FATAL("Invalid media type for tag");
2514 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_MEDIA_TYPE");
2516 }
2517
2518 if(length != 2)
2519 {
2520 FATAL("Incorrect tag size");
2521 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_DATA_SIZE");
2523 }
2524
2525 if(ctx->sector_ied == NULL) ctx->sector_ied = calloc(1, 2 * total_sectors);
2526
2527 if(ctx->sector_ied == NULL)
2528 {
2529 FATAL("Could not allocate memory for sector IED");
2530
2531 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
2533 }
2534
2535 memcpy(ctx->sector_ied + corrected_sector_address * 2, data, 2);
2536 ctx->dirty_dvd_long_sector_blocks = true; // Mark DVD long sector blocks as dirty
2537 TRACE("Exiting aaruf_write_sector_tag() = AARUF_STATUS_OK");
2538 return AARUF_STATUS_OK;
2541 {
2542 FATAL("Invalid media type for tag");
2543 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_MEDIA_TYPE");
2545 }
2546
2547 if(length != 4)
2548 {
2549 FATAL("Incorrect tag size");
2550 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_DATA_SIZE");
2552 }
2553
2554 if(ctx->sector_edc == NULL) ctx->sector_edc = calloc(1, 4 * total_sectors);
2555
2556 if(ctx->sector_edc == NULL)
2557 {
2558 FATAL("Could not allocate memory for sector EDC");
2559
2560 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
2562 }
2563
2564 memcpy(ctx->sector_edc + corrected_sector_address * 4, data, 4);
2565 ctx->dirty_dvd_long_sector_blocks = true; // Mark DVD long sector blocks as dirty
2566 TRACE("Exiting aaruf_write_sector_tag() = AARUF_STATUS_OK");
2567 return AARUF_STATUS_OK;
2570 {
2571 FATAL("Invalid media type for tag");
2572 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_MEDIA_TYPE");
2574 }
2575
2576 if(length != 5)
2577 {
2578 FATAL("Incorrect tag size");
2579 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_DATA_SIZE");
2581 }
2582
2583 if(ctx->sector_decrypted_title_key == NULL) ctx->sector_decrypted_title_key = calloc(1, 5 * total_sectors);
2584
2585 if(ctx->sector_decrypted_title_key == NULL)
2586 {
2587 FATAL("Could not allocate memory for sector decrypted title key");
2588
2589 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
2591 }
2592
2593 memcpy(ctx->sector_decrypted_title_key + corrected_sector_address * 5, data, 5);
2594 ctx->dirty_dvd_title_key_decrypted_block = true; // Mark title key block as dirty
2595 TRACE("Exiting aaruf_write_sector_tag() = AARUF_STATUS_OK");
2596 return AARUF_STATUS_OK;
2599 {
2600 FATAL("Invalid media type for tag");
2601 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_MEDIA_TYPE");
2603 }
2604
2605 if(length != 12)
2606 {
2607 FATAL("Incorrect tag size");
2608 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_DATA_SIZE");
2610 }
2611
2612 if(ctx->sector_subchannel == NULL) ctx->sector_subchannel = calloc(1, 12 * total_sectors);
2613
2614 if(ctx->sector_subchannel == NULL)
2615 {
2616 FATAL("Could not allocate memory for Apple Sony tag");
2617
2618 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
2620 }
2621
2622 memcpy(ctx->sector_subchannel + corrected_sector_address * 12, data, 12);
2623 ctx->dirty_sector_subchannel_block = true; // Mark subchannel block as dirty
2624 TRACE("Exiting aaruf_write_sector_tag() = AARUF_STATUS_OK");
2625 return AARUF_STATUS_OK;
2628 {
2629 FATAL("Invalid media type for tag");
2630 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_MEDIA_TYPE");
2632 }
2633
2634 if(length != 20)
2635 {
2636 FATAL("Incorrect tag size");
2637 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_DATA_SIZE");
2639 }
2640
2641 if(ctx->sector_subchannel == NULL) ctx->sector_subchannel = calloc(1, 20 * total_sectors);
2642
2643 if(ctx->sector_subchannel == NULL)
2644 {
2645 FATAL("Could not allocate memory for Apple Profile tag");
2646
2647 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
2649 }
2650
2651 memcpy(ctx->sector_subchannel + corrected_sector_address * 20, data, 20);
2652 ctx->dirty_sector_subchannel_block = true; // Mark subchannel block as dirty
2653 TRACE("Exiting aaruf_write_sector_tag() = AARUF_STATUS_OK");
2654 return AARUF_STATUS_OK;
2657 {
2658 FATAL("Invalid media type for tag");
2659 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_MEDIA_TYPE");
2661 }
2662
2663 if(length != 24)
2664 {
2665 FATAL("Incorrect tag size");
2666 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_INCORRECT_DATA_SIZE");
2668 }
2669
2670 if(ctx->sector_subchannel == NULL) ctx->sector_subchannel = calloc(1, 24 * total_sectors);
2671
2672 if(ctx->sector_subchannel == NULL)
2673 {
2674 FATAL("Could not allocate memory for Priam Data Tower tag");
2675
2676 TRACE("Exiting aaruf_write_sector_tag() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
2678 }
2679
2680 memcpy(ctx->sector_subchannel + corrected_sector_address * 24, data, 24);
2681 ctx->dirty_sector_subchannel_block = true; // Mark subchannel block as dirty
2682 TRACE("Exiting aaruf_write_sector_tag() = AARUF_STATUS_OK");
2683 return AARUF_STATUS_OK;
2684 default:
2685 TRACE("Do not know how to write sector tag %d", tag);
2687 }
2688}
#define MAX_FLAKE_BLOCK
FLAC maximum block size used for encoding audio sectors.
Definition consts.h:94
#define LZMA_PROPERTIES_LENGTH
Size in bytes of the fixed LZMA properties header (lc/lp/pb + dictionary size).
Definition consts.h:82
#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:96
#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
#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
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
crc64_ctx * aaruf_crc64_init()
Initializes a CRC64 context.
Definition crc64.c:32
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
void aaruf_md5_update(md5_ctx *ctx, const void *data, unsigned long size)
Definition md5.c:455
#define AARU_EXPORT
Definition decls.h:55
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
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:228
#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:81
#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_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
@ 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:1232
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:78
uint8_t flags
Control / attribute bitfield (see file documentation for suggested bit mapping).
Definition optical.h:80
int64_t end
Inclusive ending LBA of the track.
Definition optical.h:76
uint8_t sequence
Track number (1..99 typical for CD audio/data). 0 may indicate placeholder/non-standard.
Definition optical.h:73
int64_t start
Inclusive starting LBA of the track.
Definition optical.h:75
uint8_t type
Track type (value from TrackType).
Definition optical.h:74
uint8_t isrc[13]
ISRC raw 13-byte code (no null terminator). All zeros if not present.
Definition optical.h:79
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:175
uint8_t * ps3_disc_key
Cached disc key (16 bytes), NULL if not loaded.
Definition context.h:348
DdtHeader2 user_data_ddt_header
Active user data DDT v2 header (primary table meta).
Definition context.h:192
bool dirty_checksum_block
True if checksum block should be written during close.
Definition context.h:325
bool deduplicate
Storage deduplication active (duplicates coalesce).
Definition context.h:303
size_t sector_suffix_length
Length of sector_suffix.
Definition context.h:288
bool compression_enabled
True if block compression enabled (writing path).
Definition context.h:304
uint64_t last_written_block
Last written block number (write path).
Definition context.h:286
uint8_t * sector_cpr_mai
DVD sector CPR_MAI (6 bytes) if present.
Definition context.h:210
bool dirty_media_tags
True if media tags should be written during close.
Definition context.h:335
hash_map_t * sector_hash_map
Deduplication hash map (fingerprint->entry mapping).
Definition context.h:256
uint32_t ps3_plaintext_region_count
Number of plaintext regions.
Definition context.h:350
sha256_ctx sha256_context
Opaque SHA-256 context for streaming updates.
Definition context.h:275
bool calculating_sha256
True if whole-image SHA-256 being calculated on-the-fly.
Definition context.h:278
uint8_t * sector_ied
DVD sector IED (2 bytes) if present.
Definition context.h:209
md5_ctx md5_context
Opaque MD5 context for streaming updates.
Definition context.h:273
bool dirty_sector_suffix_block
True if sector suffix block should be written during close.
Definition context.h:330
uint8_t * sector_prefix
Raw per-sector prefix (e.g., sync+header) uncorrected.
Definition context.h:202
bool dirty_dvd_title_key_decrypted_block
True if decrypted title key block should be written during close.
Definition context.h:334
uint64_t * sector_suffix_ddt2
CD sector suffix DDT V2.
Definition context.h:189
bool dirty_mode2_subheaders_block
True if MODE2 subheader block should be written during close.
Definition context.h:327
uint8_t * sector_edc
DVD sector EDC (4 bytes) if present.
Definition context.h:211
bool calculating_sha1
True if whole-image SHA-1 being calculated on-the-fly.
Definition context.h:277
bool dirty_tracks_block
True if tracks block should be written during close.
Definition context.h:326
CdEccContext * ecc_cd_context
CD ECC/EDC helper tables (allocated on demand).
Definition context.h:251
bool rewinded
True if stream has been rewound after open (write path).
Definition context.h:296
bool ps3_encryption_initialized
Whether lazy init has occurred.
Definition context.h:351
bool dirty_sector_prefix_block
True if sector prefix block should be written during close.
Definition context.h:328
bool dirty_index_block
True if index block should be written during close.
Definition context.h:345
uint8_t * sector_suffix
Raw per-sector suffix (EDC/ECC) uncorrected.
Definition context.h:204
AaruHeaderV2 header
Parsed container header (v2).
Definition context.h:178
int current_block_offset
Logical offset inside block (units: bytes or sectors depending on path).
Definition context.h:291
bool is_writing
True if context opened/created for writing.
Definition context.h:295
spamsum_ctx * spamsum_context
Opaque SpamSum context for streaming updates.
Definition context.h:270
size_t sector_prefix_offset
Current position in sector_prefix.
Definition context.h:289
BlockHeader current_block_header
Header for block currently being assembled (write path).
Definition context.h:284
uint64_t magic
File magic (AARU_MAGIC) post-open.
Definition context.h:177
uint8_t * writing_buffer
Accumulation buffer for current block data.
Definition context.h:283
uint64_t * sector_prefix_ddt2
CD sector prefix DDT V2.
Definition context.h:188
bool calculating_spamsum
True if whole-image SpamSum being calculated on-the-fly.
Definition context.h:279
size_t sector_prefix_length
Length of sector_prefix.
Definition context.h:287
mediaTagEntry * mediaTags
Hash table of extra media tags (uthash root).
Definition context.h:267
blake3_hasher * blake3_context
Opaque BLAKE3 context for streaming updates.
Definition context.h:271
bool calculating_blake3
True if whole-image BLAKE3 being calculated on-the-fly.
Definition context.h:280
uint64_t next_block_position
Absolute file offset where next block will be written.
Definition context.h:285
bool calculating_md5
True if whole-image MD5 being calculated on-the-fly.
Definition context.h:276
bool dirty_sector_suffix_ddt
True if sector suffix DDT should be written during close.
Definition context.h:331
size_t sector_suffix_offset
Current position in sector_suffix.
Definition context.h:290
bool has_zstd_blocks
True if any block was actually written with Zstandard compression.
Definition context.h:306
int zstd_level
Zstandard compression level (writing path, default 19).
Definition context.h:307
uint8_t * sector_decrypted_title_key
DVD decrypted title key (5 bytes) if present.
Definition context.h:212
int writing_buffer_position
Current size / position within writingBuffer.
Definition context.h:292
bool block_zero_written
True if block zero has been written (writing path).
Definition context.h:298
crc64_ctx * crc64_context
Opaque CRC64 context for streaming updates.
Definition context.h:252
uint8_t * sector_subchannel
Raw 96-byte subchannel (if captured).
Definition context.h:206
bool dirty_sector_subchannel_block
True if subchannel block should be written during close.
Definition context.h:332
FILE * imageStream
Underlying FILE* stream (binary mode).
Definition context.h:179
bool dirty_dvd_long_sector_blocks
True if DVD long sector blocks should be written during close.
Definition context.h:333
bool dirty_sector_prefix_ddt
True if sector prefix DDT should be written during close.
Definition context.h:329
UT_array * index_entries
Flattened index entries (UT_array of IndexEntry).
Definition context.h:255
int num_threads
Compression worker threads (1 = single-threaded, default).
Definition context.h:308
bool ec_enabled
True if erasure coding is active.
Definition context.h:391
uint8_t * mode2_subheaders
MODE2 Form1/Form2 8-byte subheaders (concatenated).
Definition context.h:207
ImageInfo image_info
Exposed high-level image info summary.
Definition context.h:263
uint8_t * sector_id
DVD sector ID (4 bytes) if present.
Definition context.h:208
void * ps3_plaintext_regions
Parsed Ps3PlaintextRegion array (max 32), NULL if not loaded.
Definition context.h:349
bool use_zstd
Use Zstandard instead of LZMA for data blocks.
Definition context.h:305
sha1_ctx sha1_context
Opaque SHA-1 context for streaming updates.
Definition context.h:274
uint32_t lzma_dict_size
LZMA dictionary size (writing path).
Definition context.h:302
TrackEntry * track_entries
Full track list (tracksHeader.entries elements).
Definition context.h:245
uint8_t current_track_type
Current track type (when writing optical images with tracks, needed for block compression type).
Definition context.h:293
bool writing_long
True if writing long sectors.
Definition context.h:297
TracksHeader tracks_header
Tracks header (optical) if present.
Definition context.h:247
Hash table entry for an arbitrary media tag (e.g., proprietary drive/medium descriptor).
Definition context.h:122
uint8_t * data
Tag data blob (opaque to library core); length bytes long.
Definition context.h:123
int32_t type
Numeric type identifier.
Definition context.h:124
uint32_t length
Length in bytes of data.
Definition context.h:125
int32_t aaruf_close_current_block(aaruformat_context *ctx)
Finalizes and writes the current data block to the AaruFormat image file.
Definition write.c:1536
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:2273
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:2003
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:102
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:623