
/*
  cc -g -c isoburn.c
*/

/*
  Class core of libisoburn.

  Copyright 2007 - 2009 Vreixo Formoso Lopes <metalpain2002@yahoo.es>
  Copyright 2007 - 2023 Thomas Schmitt <scdbackup@gmx.net>

  Provided under GPL version 2 or later.
*/

#include "../config.h"

/* ( derived from stub generated by CgeN on  Sat, 01 Sep 2007 12:04:36 GMT ) */

#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>

#ifndef Xorriso_standalonE

#include <libburn/libburn.h>

#include <libisofs/libisofs.h>

#else /* ! Xorriso_standalonE */

#include "../libisofs/libisofs.h"
#include "../libburn/libburn.h"

#endif /* Xorriso_standalonE */


#include "libisoburn.h"

#include "isoburn.h"


/* Default values for application provided msgs_submit methods.
   To be attached to newly acquired drives.
*/
int (*libisoburn_default_msgs_submit)
    (void *handle, int error_code, char msg_text[],
                 int os_errno, char severity[], int flag)= NULL;
void *libisoburn_default_msgs_submit_handle= NULL;
int libisoburn_default_msgs_submit_flag= 0;


/* ----------------------- isoburn_toc_entry  ---------------------- */


int isoburn_toc_entry_new(struct isoburn_toc_entry **objpt,
                          struct isoburn_toc_entry *boss, int flag)
{
 struct isoburn_toc_entry *o, *s;

 *objpt= o= (struct isoburn_toc_entry *)
            malloc(sizeof(struct isoburn_toc_entry));
 if(o==NULL) {
   isoburn_msgs_submit(NULL, 0x00060000,
                       "Cannot allocate memory for isoburn toc entry",
                       0, "FATAL", 0);
   return(-1);
 }
 o->session= 0;
 o->track_no= 0;
 o->start_lba= -1;
 o->track_blocks= 0;
 o->volid= NULL;
 o->next= NULL;
 if(boss!=NULL) {
   for(s= boss; s->next!=NULL; s= s->next);
   s->next= o;
 }
 return(1);
}


/* @param flag bit0= delete all subordinates too
*/
int isoburn_toc_entry_destroy(struct isoburn_toc_entry **o, int flag)
{
 if(*o==NULL)
   return(0);
 if(flag&1)
   isoburn_toc_entry_destroy(&((*o)->next), flag);
 if((*o)->volid != NULL)
   free((*o)->volid);
 free((char *) (*o));   
 *o= NULL;
 return(1);
}


/* --------------------- end isoburn_toc_entry  -------------------- */

/* --------------------------  isoburn  ----------------------- */


/* The global list of isoburn objects. Usually there is only one.
   >>> we are not ready for multiple control threads yet. See >>> mutex .
   Multiple burns under one control thread should work.
*/
struct isoburn *isoburn_list_start= NULL;


int isoburn_new(struct isoburn **objpt, int flag)
{
 struct isoburn *o;
 int ret;

 *objpt= o= (struct isoburn *) malloc(sizeof(struct isoburn));
 if(o==NULL) {
   isoburn_msgs_submit(NULL, 0x00060000,
                       "Cannot allocate memory for isoburn control object",
                       0, "FATAL", 0);
   return(-1);
 }

 o->drive= NULL;
 o->emulation_mode= 0;
 o->fabricated_msc1= -1;
 o->fabricated_msc2= -1;
 o->zero_nwa= Libisoburn_overwriteable_starT;
 o->min_start_byte= o->zero_nwa * 2048;
 o->nwa= o->zero_nwa;
 o->truncate= 0;
 o->iso_source= NULL;
 o->fabricated_disc_status= BURN_DISC_UNREADY;
 o->media_read_error= 0;
 o->toc= NULL;
 o->wrote_well= -1;
 o->loaded_partition_offset= 0;
 o->target_iso_head_size= Libisoburn_target_head_sizE;
 o->target_iso_head= NULL;
 o->image= NULL;
 o->image_start_lba= -1;
 o->iso_data_source= NULL;
 o->read_pacifier= NULL;
 o->read_pacifier_handle= NULL;
 o->msgs_submit= NULL;
 o->msgs_submit_handle= NULL;
 o->msgs_submit_flag= 0;
 o->do_tao= 0;
 o->do_fsync= 1;
 o->prev= NULL;
 o->next= NULL;
 o->target_iso_head= calloc(1, o->target_iso_head_size);
 if(o->target_iso_head == NULL) {
   isoburn_report_iso_error(ISO_OUT_OF_MEM, "Cannot allocate overwrite buffer",
                            0, "FATAL", 0);
   goto failed;
 }
 ret= iso_image_new("ISOIMAGE", &o->image);
 if(ret<0) {
   isoburn_report_iso_error(ret, "Cannot create image object", 0, "FATAL", 0);
   goto failed;
 }
 ret= isoburn_root_defaults(o->image, 0);
 if(ret <= 0)
   goto failed;
 isoburn_link(o, isoburn_list_start, 1);
 return(1);
failed:;
 isoburn_destroy(objpt, 0);
 return(-1);
}


int isoburn_destroy(struct isoburn **objpt, int flag)
{
 struct isoburn *o;

 o= *objpt;
 if(o==NULL)
   return(0);

 /* >>> mutex */

 if(o==isoburn_list_start)
   isoburn_list_start= o->next;
 if(o->prev!=NULL)
   o->prev->next= o->next;
 if(o->next!=NULL)
   o->next->prev= o->prev;

 /* >>> end mutex */

 if(o->image!=NULL)
   iso_image_unref(o->image);
 if(o->toc!=NULL)
   isoburn_toc_entry_destroy(&(o->toc), 1); /* all */
 if(o->iso_source!=NULL)
   burn_source_free(o->iso_source);
 if(o->iso_data_source!=NULL)
   iso_data_source_unref(o->iso_data_source);
 if(o->target_iso_head != NULL)
   free(o->target_iso_head);
 free((char *) o);
 *objpt= NULL;
 return(1);
}


int isoburn_destroy_all(struct isoburn **objpt, int flag)
{
 struct isoburn *o,*n;

 o= *objpt;
 if(o==NULL)
   return(0);
 for(;o->prev!=NULL;o= o->prev);
 for(;o!=NULL;o= n) {
   n= o->next;
   isoburn_destroy(&o,0);
 }
 *objpt= NULL;
 return(1);
}


int isoburn_get_target_image(struct isoburn *o, IsoImage **pt, int flag)
{
 *pt= o->image;
 return(1);
}


int isoburn_get_prev(struct isoburn *o, struct isoburn **pt, int flag)
{
 *pt= o->prev;
 return(1);
}


int isoburn_get_next(struct isoburn *o, struct isoburn **pt, int flag)
{
 *pt= o->next;
 return(1);
}


int isoburn_link(struct isoburn *o, struct isoburn *link, int flag)
/*
  bit0= insert as link->prev rather than as link->next
*/
{

 /* >>> mutex */

 if(isoburn_list_start==NULL ||
    (isoburn_list_start==link && (flag&1)))
   isoburn_list_start= o;
 if(o->prev!=NULL)
   o->prev->next= o->next;
 if(o->next!=NULL)
   o->next->prev= o->prev;
 o->prev= o->next= NULL;
 if(link==NULL)
   return(1);
 if(flag&1) {
   o->next= link;
   o->prev= link->prev;
   if(o->prev!=NULL)
     o->prev->next= o;
   link->prev= o;
 } else {
   o->prev= link;
   o->next= link->next;
   if(o->next!=NULL)
     o->next->prev= o;
   link->next= o;
 }

 /* >>> end mutex */

 return(1);
}


int isoburn_count(struct isoburn *o, int flag)
/* flag: bit1= count from start of list */
{
 int counter= 0;

 if(flag&2)
   for(;o->prev!=NULL;o= o->prev);
 for(;o!=NULL;o= o->next)
   counter++;
 return(counter);
}


int isoburn_by_idx(struct isoburn *o, int idx, struct isoburn **pt, int flag)
/* flag: bit0= fetch first (idx<0) or last (idx>0) item in list
         bit1= address from start of list */
{
 int i,abs_idx;
 struct isoburn *npt;

 if(flag&2)
   for(;o->prev!=NULL;o= o->prev);
 abs_idx= (idx>0?idx:-idx);
 *pt= o;
 for(i= 0;(i<abs_idx || (flag&1)) && *pt!=NULL;i++) {
   if(idx>0)
     npt= o->next;
   else
     npt= o->prev;
   if(npt==NULL && (flag&1))
 break;
   *pt= npt;
 }
 return(*pt!=NULL);
}


int isoburn_find_by_drive(struct isoburn **pt, struct burn_drive *d, int flag)
{
 struct isoburn *o;

 *pt= NULL;
 for(o= isoburn_list_start;o!=NULL;o= o->next)
   if(o->drive==d) {
     *pt= o;
     return(1);
   }
 return(0);
}


int isoburn_msgs_submit(struct isoburn *o, int error_code, char msg_text[],
                        int os_errno, char severity[], int flag)
{
 int ret, use_drive_method= 0;

 if(o!=NULL)
   if(o->msgs_submit!=NULL)
     use_drive_method= 1;
 if(use_drive_method) {
   ret= o->msgs_submit(o->msgs_submit_handle, error_code, msg_text, os_errno,
                       severity, o->msgs_submit_flag);
   return(ret);
 }
 if(libisoburn_default_msgs_submit != NULL) {
   ret= libisoburn_default_msgs_submit(libisoburn_default_msgs_submit_handle,
                                     error_code, msg_text, os_errno, severity,
                                     libisoburn_default_msgs_submit_flag);
   return(ret);
 }
 /* Fallback: use message queue of libburn */
 burn_msgs_submit(error_code, msg_text, os_errno, severity, NULL);
 return(1);
}


/** Check whether the size of target_iso_head matches the given partition 
    offset. Eventually adjust size.
*/
int isoburn_adjust_target_iso_head(struct isoburn *o,
                                   uint32_t offst, int flag)
{
 uint8_t *new_buf;
 uint32_t new_size;

 if((uint32_t) o->target_iso_head_size ==
    Libisoburn_target_head_sizE + 2048 * offst)
   return(1);
 new_size= Libisoburn_target_head_sizE + 2048 * offst;
 new_buf= calloc(1, new_size);
 if(new_buf == NULL) {
   isoburn_msgs_submit(o, 0x00060000,
                       "Cannot re-allocate overwrite buffer", 0, "FATAL", 0);
   return(-1);
 }
 memcpy(new_buf, o->target_iso_head,
        (uint32_t) o->target_iso_head_size < new_size ?
        (uint32_t) o->target_iso_head_size : new_size);
 free(o->target_iso_head);
 o->target_iso_head= new_buf;
 o->target_iso_head_size= new_size;
 if(o->nwa == o->zero_nwa)
   o->nwa= Libisoburn_overwriteable_starT + offst;
 o->zero_nwa= Libisoburn_overwriteable_starT + offst;
 return(1);
}


/* @param out_o The output isoburn object may be NULL if no real write run is
                desired with opts.
   @param flag bit0= modifying rather than growing
*/
static
int isoburn_make_iso_write_opts(struct isoburn *out_o,
                                struct isoburn_imgen_opts *opts,
                                int fifo_chunks,
                                IsoWriteOpts *wopts,
                                int flag)
{
 int ret, rec_mtime, new_img, lba, nwa, i, guid_mode;
 struct burn_drive *out_d;

 new_img= flag&1;

 iso_write_opts_set_will_cancel(wopts, opts->will_cancel);
 iso_write_opts_set_iso_level(wopts, opts->level);
 iso_write_opts_set_rockridge(wopts, opts->rockridge);
 iso_write_opts_set_joliet(wopts, opts->joliet);
 iso_write_opts_set_hfsplus(wopts, opts->hfsplus);
 iso_write_opts_set_hfsp_block_size(wopts, opts->hfsp_block_size,
                                           opts->apm_block_size);
 iso_write_opts_set_fat(wopts, opts->fat);
 iso_write_opts_set_iso1999(wopts, opts->iso1999);
 iso_write_opts_set_hardlinks(wopts, opts->hardlinks);
 if(opts->hardlinks)
   iso_write_opts_set_rrip_1_10_px_ino(wopts, 1);
 iso_write_opts_set_aaip(wopts, opts->aaip);
 iso_write_opts_set_old_empty(wopts, !!opts->old_empty);
 iso_write_opts_set_untranslated_name_len(wopts, opts->untranslated_name_len);
 iso_write_opts_set_allow_dir_id_ext(wopts, opts->allow_dir_id_ext);
 iso_write_opts_set_omit_version_numbers(wopts, opts->omit_version_numbers);
 iso_write_opts_set_allow_deep_paths(wopts, opts->allow_deep_paths);
 iso_write_opts_set_rr_reloc(wopts, opts->rr_reloc_dir, opts->rr_reloc_flags);
 iso_write_opts_set_allow_longer_paths(wopts, opts->allow_longer_paths);
 iso_write_opts_set_max_37_char_filenames(wopts, opts->max_37_char_filenames);
 iso_write_opts_set_no_force_dots(wopts, opts->no_force_dots);
 iso_write_opts_set_allow_lowercase(wopts, opts->allow_lowercase);
 iso_write_opts_set_allow_full_ascii(wopts, opts->allow_full_ascii);
 iso_write_opts_set_allow_7bit_ascii(wopts, opts->allow_7bit_ascii);
 iso_write_opts_set_relaxed_vol_atts(wopts, 1);
 iso_write_opts_set_joliet_longer_paths(wopts, opts->joliet_longer_paths);
 iso_write_opts_set_joliet_long_names(wopts, opts->joliet_long_names);
 iso_write_opts_set_joliet_utf16(wopts, opts->joliet_utf16);
 iso_write_opts_set_always_gmt(wopts, opts->always_gmt);
 iso_write_opts_set_rrip_version_1_10(wopts, opts->rrip_version_1_10);
 rec_mtime= 0;
 if(opts->dir_rec_mtime)
   rec_mtime|= 1;
 else
   rec_mtime|= (1 << 14);
 if(opts->joliet_rec_mtime)
   rec_mtime|= 2;
 if(opts->iso1999_rec_mtime)
   rec_mtime|= 4;
 iso_write_opts_set_dir_rec_mtime(wopts, rec_mtime);
 iso_write_opts_set_aaip_susp_1_10(wopts, opts->aaip_susp_1_10);
 iso_write_opts_set_sort_files(wopts, opts->sort_files);
 iso_write_opts_set_record_md5(wopts, opts->session_md5, opts->file_md5 & 3);
 if(opts->scdbackup_tag_name[0] && opts->scdbackup_tag_time[0])
   iso_write_opts_set_scdbackup_tag(wopts, opts->scdbackup_tag_name,
                                    opts->scdbackup_tag_time,
                                    opts->scdbackup_tag_written);
 iso_write_opts_set_replace_mode(wopts, opts->replace_dir_mode,
                opts->replace_file_mode, opts->replace_uid, opts->replace_gid);
 iso_write_opts_set_default_dir_mode(wopts, opts->dir_mode);
 iso_write_opts_set_default_file_mode(wopts, opts->file_mode);
 iso_write_opts_set_default_uid(wopts, opts->uid);
 iso_write_opts_set_default_gid(wopts, opts->gid);
 iso_write_opts_set_output_charset(wopts, opts->output_charset);
 iso_write_opts_set_fifo_size(wopts, fifo_chunks);
 ret = iso_write_opts_set_system_area(wopts, opts->system_area_data,
                                      opts->system_area_options, 0);
 if (ret < 0) {
   isoburn_report_iso_error(ret, "Cannot set content of System Area",
                            0, "FAILURE", 0);
   {ret= -1; goto ex;}
 }
 iso_write_opts_set_pvd_times(wopts,
                        opts->vol_creation_time, opts->vol_modification_time,
                        opts->vol_expiration_time, opts->vol_effective_time,
                        opts->vol_uuid);
 guid_mode= opts->gpt_guid_mode;
 if(opts->vol_uuid[0] == 0 && opts->gpt_guid_mode == 2)
   guid_mode= 0;
 iso_write_opts_set_gpt_guid(wopts, opts->gpt_guid, guid_mode);
 ret = iso_write_opts_set_max_ce_entries(wopts, opts->max_ce_entries,
                                         opts->max_ce_entries_flag);
 if (ret < 0) {
   isoburn_report_iso_error(ret, "Cannot set limit for number of CE entries",
                            0, "FAILURE", 0);
   {ret= -1; goto ex;}
 }
 iso_write_opts_attach_jte(wopts, opts->libjte_handle);
 iso_write_opts_set_hfsp_serial_number(wopts, opts->hfsp_serial_number);

 if(out_o != NULL) {
   out_d= out_o->drive;
   ret= isoburn_adjust_target_iso_head(out_o, opts->partition_offset, 0);
   if(ret <= 0)
     {ret= -1; goto ex;}
   if(out_o->nwa < out_o->zero_nwa)
     out_o->zero_nwa= 0;
   if(opts->no_emul_toc || opts->libjte_handle != NULL) {
     if(out_o->nwa == out_o->zero_nwa &&
        out_o->zero_nwa == Libisoburn_overwriteable_starT 
                           + opts->partition_offset
        && out_o->emulation_mode == 1) {
       out_o->nwa= 0;
       out_o->zero_nwa= 0;
       out_o->min_start_byte= 0;
     }
   }
   ret = isoburn_disc_track_lba_nwa(out_d, NULL, 0, &lba, &nwa);
   opts->effective_lba= nwa;
   ret= isoburn_get_msc2(out_o, NULL, &nwa, 0);
   if (ret != 1) {
     isoburn_msgs_submit(out_o, 0x00060000,
                   "Cannot determine next writeable address", 0, "FAILURE", 0);

     /* >>> NWA_V : If burn_next_track_damaged:
            ??? Close track and session ?
	    ??? Issue a hint for a repair command ?
     */;

     {ret= -3; goto ex;}
   }
   iso_write_opts_set_ms_block(wopts, nwa);
   iso_write_opts_set_appendable(wopts, !new_img);
   iso_write_opts_set_overwrite_buf(wopts,
                                    nwa>0 ? out_o->target_iso_head : NULL);
 }
 iso_write_opts_set_part_offset(wopts, opts->partition_offset,
                                opts->partition_secs_per_head,
                                opts->partition_heads_per_cyl);
 iso_write_opts_set_tail_blocks(wopts, opts->tail_blocks);
 if(opts->prep_partition != NULL) {
   ret = iso_write_opts_set_prep_img(wopts, opts->prep_partition,
                                     opts->prep_part_flag & 1);
   if(ret < 0) {
     isoburn_report_iso_error(ret, "Cannot set path for PreP partition",
                              0, "FAILURE", 0);
     {ret= -1; goto ex;}
   }
 }
 if(opts->efi_boot_partition != NULL) {
   ret = iso_write_opts_set_efi_bootp(wopts, opts->efi_boot_partition,
                                      opts->efi_boot_part_flag & 1);
   if(ret < 0) {
     isoburn_report_iso_error(ret, "Cannot set path for EFI system partition",
                              0, "FAILURE", 0);
     {ret= -1; goto ex;}
   }
 }
 for(i= 0; i < Libisoburn_max_appended_partitionS; i++) {
   if(opts->appended_partitions[i] == NULL)
 continue;
   ret= iso_write_opts_set_partition_img(wopts, i + 1,
                                         opts->appended_part_types[i],
                                         opts->appended_partitions[i],
                                         opts->appended_part_flags[i]);
   if(ret < 0) {
     isoburn_report_iso_error(ret, "Cannot set path for appended partition",
                              0, "FAILURE", 0);
     {ret= -1; goto ex;}
   }
   iso_write_opts_set_part_type_guid(wopts, i + 1,
                                     opts->appended_part_type_guids[i],
                                     opts->appended_part_gpt_flags[i] & 1);
 }
 iso_write_opts_set_appended_as_gpt(wopts, opts->appended_as_gpt);
 iso_write_opts_set_appended_as_apm(wopts, opts->appended_as_apm);
 iso_write_opts_set_part_like_isohybrid(wopts, opts->part_like_isohybrid);
 iso_write_opts_set_iso_mbr_part_type(wopts, opts->iso_mbr_part_type);
 iso_write_opts_set_iso_type_guid(wopts, opts->iso_gpt_type_guid, 
                                  opts->iso_gpt_flag & 1);
 iso_write_opts_set_disc_label(wopts, opts->ascii_disc_label);

 ret= 1;
ex:
 return(ret);
}


/* @param flag bit0= modifying rather than growing
               bit1= prepare for early release of input drive:
                     wait until input and then disable image data source
*/
static
int isoburn_prepare_disc_aux(struct burn_drive *in_d, struct burn_drive *out_d,
                             struct burn_disc **disc,
                             struct isoburn_imgen_opts *opts, int flag)
{
 struct burn_source *wsrc;
 struct burn_session *session;
 struct burn_track *track;
 struct isoburn *in_o, *out_o;
 IsoWriteOpts *wopts= NULL;
 enum burn_disc_status state;
 int ret, fifo_chunks, i, new_img, early_indev_release;
 uint32_t data_start= -1;
 size_t buffer_size= 0, buffer_free= 0;
 char *msg= NULL;

 msg= calloc(1, 160);
 if(msg == NULL)
   {ret= -1; goto ex;}

 new_img= flag&1;
 early_indev_release= flag&2;

 ret= isoburn_find_emulator(&in_o, in_d, 0);
 if(ret<0 || in_o==NULL)
   {ret= -1; goto ex;}
 ret= isoburn_find_emulator(&out_o, out_d, 0);
 if(ret<0 || out_o==NULL)
   {ret= -1; goto ex;}
 /* early end will be registered as failure */
 in_o->wrote_well= out_o->wrote_well= 0;

 if(new_img && early_indev_release) {
   isoburn_msgs_submit(in_o, 0x00060000,
      "Programming error: Wrong session setup: new_img && early_indev_release",
                       0, "FATAL", 0);
   {ret= -4; goto ex;}
 }

 out_o->do_tao = opts->do_tao;
 out_o->do_fsync = opts->do_fsync;

 state = isoburn_disc_get_status(in_d);
 if (state != BURN_DISC_BLANK && state != BURN_DISC_APPENDABLE &&
     state != BURN_DISC_FULL) {
   isoburn_msgs_submit(in_o, 0x00060000, "Unsuitable source media state",
                    0, "FAILURE", 0);
   {ret= -2; goto ex;}
 }
 state = isoburn_disc_get_status(out_d);
 if (state != BURN_DISC_BLANK && state != BURN_DISC_APPENDABLE) {
   isoburn_msgs_submit(out_o, 0x00060000, "Unsuitable target media state",
                    0, "FAILURE", 0);
   {ret= -2; goto ex;}
 }
 if (state != BURN_DISC_BLANK && opts->libjte_handle != NULL) {
   isoburn_msgs_submit(out_o, 0x00060000,
                  "Jigdo Template Extraction works only on blank target media",
                  0, "FAILURE", 0);
   {ret= -2; goto ex;}
 }
 
 fifo_chunks= 32;
 if(opts->fifo_size >= 64*1024 && opts->fifo_size <= 1024.0 * 1024.0 * 1024.0){
   fifo_chunks= opts->fifo_size/2048;
   if(fifo_chunks*2048 < opts->fifo_size)
     fifo_chunks++;
 }

 ret= iso_write_opts_new(&wopts, 0);
 if (ret < 0) {
   isoburn_report_iso_error(ret, "Cannot create iso_write_opts", 0, "FATAL",0);
   goto ex;
 }
 ret= isoburn_make_iso_write_opts(out_o, opts, fifo_chunks, wopts, flag & 1);
 if(ret < 0)
   goto ex;

 ret = iso_image_create_burn_source(in_o->image, wopts, &wsrc);
 if (ret < 0) {
   isoburn_report_iso_error(ret, "Cannot create burn source", 0, "FAILURE", 0);
   {ret= -1; goto ex;}
 }
 if (early_indev_release) {
   for(i= 0; i<300; i++) {

     /* <<< ??? */
     if((i%30) == 0) {
       sprintf(msg, "Waiting for data in fifo since %d seconds", i/30);
       isoburn_msgs_submit(in_o, 0x00060000, msg, 0, "DEBUG", 0);
     }

     usleep(100000);
     ret= iso_ring_buffer_get_status(wsrc, &buffer_size, &buffer_free);
     if(ret >0 && buffer_size != buffer_free)
   break;
   }

   /* <<< ??? */
   sprintf(msg,
           "After %.1f seconds: %d bytes of output available (fifo state=%d)",
           ((double) i+1) / 10.0, (int) (buffer_size - buffer_free), ret);
   isoburn_msgs_submit(in_o, 0x00060000, msg, 0, "DEBUG", 0);

   if(in_o->iso_data_source!=NULL)
     isoburn_data_source_shutdown(in_o->iso_data_source, 0);
 }

 ret= iso_write_opts_get_data_start(wopts, &data_start, 0);
 opts->data_start_lba= -1;
 if(ret > 0 && data_start <= 0x7FFFFFFF)
   opts->data_start_lba= data_start;
 
 /* TODO check return values for failure. properly clean-up on error */

 out_o->iso_source= wsrc;

 *disc = burn_disc_create();
 session = burn_session_create();
 burn_disc_add_session(*disc, session, BURN_POS_END);
 track = burn_track_create();
 burn_track_set_source(track, out_o->iso_source);
 burn_session_add_track(session, track, BURN_POS_END);

 /* give up local references */
 burn_track_free(track);
 burn_session_free(session);

 in_o->wrote_well= out_o->wrote_well= -1; /* neutral */
 ret= 1;
ex:
 if(wopts!=NULL)
   {iso_write_opts_free(wopts); wopts= NULL;}
 if(msg != NULL)
   free(msg);
 return ret;
}


int isoburn_prepare_disc(struct burn_drive *d, struct burn_disc **disc,
                         struct isoburn_imgen_opts *opts)
{
 return isoburn_prepare_disc_aux(d, d, disc, opts, 0);
}


int isoburn_prepare_new_image(struct burn_drive *d, struct burn_disc **disc,
                         struct isoburn_imgen_opts *opts,
                         struct burn_drive *out_drive)
{
 int ret;

 ret= isoburn_prepare_disc_aux(d, out_drive, disc, opts, 1);
 if (ret<=0)
   return ret;
 return 1; 
}


/* API since 0.2.2 */
int isoburn_prepare_blind_grow(struct burn_drive *d, struct burn_disc **disc,
                               struct isoburn_imgen_opts *opts,
                               struct burn_drive *out_drive, int nwa)
{  
 int ret;
 struct isoburn *o= NULL;

 ret= isoburn_find_emulator(&o, out_drive, 0);
 if(ret<0 || o==NULL)
   return(-1);
 if(nwa >= 0)
   o->fabricated_msc2= nwa;
 if(o->nwa == o->zero_nwa)
   o->nwa= o->zero_nwa= 0;
 else
   o->zero_nwa= 0;
 o->min_start_byte= 0;
 ret= isoburn_prepare_disc_aux(d, out_drive, disc, opts, 2);
 if (ret<=0)
   return ret;
 return(1);
}


/* API @since 0.1.0
   @param flag bit0= this is a regular end, not an abort
                     give up source reference
*/
int isoburn_cancel_prepared_write(struct burn_drive *d,
                                  struct burn_drive *output_drive, int flag)
{
 int ret;
 struct isoburn *o= NULL;

 if(output_drive!=NULL) {
   ret= isoburn_find_emulator(&o, output_drive, 0);
   if(ret<0 || o==NULL)
     o= NULL;
   else if(o->iso_source==NULL)
     o= NULL;
 }
 if(o==NULL) {
   ret= isoburn_find_emulator(&o, d, 0);
   if(ret<0)
     return(-1);
   if(o==NULL)
     return(0);
   if(o->iso_source==NULL)
     return(0);
 }
 if(o->iso_source->read!=NULL)
   return(0);
 if(o->iso_source->version<1)
   return(0);
 o->iso_source->cancel(o->iso_source);
 burn_source_free(o->iso_source);
 o->iso_source= NULL;
 return(1);
}


/* API @since 0.1.0 */
int isoburn_sync_after_write(struct burn_drive *d,
                             struct burn_drive *output_drive, int flag)
{
 return isoburn_cancel_prepared_write(d, output_drive, 1);
}


void isoburn_version(int *major, int *minor, int *micro)
{
 *major= isoburn_header_version_major;
 *minor= isoburn_header_version_minor;
 *micro= isoburn_header_version_micro;

/* No more: values from version.h generated from version.h.in and
            macro values defined in configure.ac

 *major = ISOBURN_MAJOR_VERSION;
 *minor = ISOBURN_MINOR_VERSION;
 *micro = ISOBURN_MICRO_VERSION;
*/
}


int isoburn_is_compatible(int major, int minor, int micro, int flag)
{
 int own_major, own_minor, own_micro;

 isoburn_version(&own_major, &own_minor, &own_micro);
 return(own_major > major ||
        (own_major == major && (own_minor > minor ||
         (own_minor == minor && own_micro >= micro))));
}


/* ----------------------------------------------------------------------- */
/*
  Options for image reading.
*/
/* ----------------------------------------------------------------------- */


int isoburn_ropt_new(struct isoburn_read_opts **new_o, int flag)
{
 struct isoburn_read_opts *o;

 o= (*new_o)= calloc(1, sizeof(struct isoburn_read_opts));
 if(o==NULL) {
   isoburn_msgs_submit(NULL, 0x00060000,
                     "Cannot allocate memory for read options", 0, "FATAL", 0);
   return(-1);
 }
 o->cache_tiles= Libisoburn_default_cache_tileS;
 o->cache_tile_blocks= Libisoburn_default_tile_blockS;
 o->norock= 0;
 o->nojoliet= 0;
 o->noiso1999= 1;
 o->do_ecma119_map= 0;
 o->map_mode= 1;
 o->do_joliet_map= 0;
 o->joliet_map_mode= 1;
 o->noaaip= 1;
 o->noacl= 1;
 o->noea= 1;
 o->noino= 1;
 o->nomd5= 1;
 o->preferjoliet= 0;
 o->uid= geteuid();
 o->gid= getegid();
 o->mode= 0444;
 o->dirmode= 0555;
 o->input_charset= NULL;
 o->truncate_mode= 1;
 o->truncate_length= 255;
 o->hasRR= 0;
 o->hasJoliet= 0;
 o->hasIso1999= 0;
 o->hasElTorito= 0;
 o->size= 0;
 o->pretend_blank= 1;
 o->displacement= 0;
 o->displacement_sign= 0;
 return(1);
}


int isoburn_ropt_destroy(struct isoburn_read_opts **o, int flag)
{
 if(*o==NULL)
   return(0);
 free(*o);
 *o= NULL;
 return(1);
}


int isoburn_ropt_set_data_cache(struct isoburn_read_opts *o,
                                int cache_tiles, int tile_blocks, int flag)
{
 int i;
 char msg[80];

 if(cache_tiles < 1) {
   isoburn_msgs_submit(NULL, 0x00060000,
                     "Requested number of data cache tiles is too small (< 1)",
                     0, "SORRY", 0);
   return(0);
 }  
 if(((double) cache_tiles) * ((double) tile_blocks)
     > (double) Libisoburn_cache_max_sizE) {
   sprintf(msg, "Requested size of data cache exceeds limit of %.f blocks",
           (double) Libisoburn_cache_max_sizE);
   isoburn_msgs_submit(NULL, 0x00060000, msg, 0, "SORRY", 0);
   return(0);
 }
 for(i = 1; i <= Libisoburn_cache_max_sizE; i = i << 1)
   if(i == tile_blocks)
 break;
 if(i > Libisoburn_cache_max_sizE) {
   isoburn_msgs_submit(NULL, 0x00060000, 
         "Requested number of blocks per data cache tiles is not a power of 2",
         0, "SORRY", 0);
   return(0);
 }
 if(o != NULL) {
   o->cache_tiles= cache_tiles;
   o->cache_tile_blocks= tile_blocks;
 }
 return(1);
} 


int isoburn_ropt_get_data_cache(struct isoburn_read_opts *o,
                                 int *cache_tiles, int *tile_blocks,
                                 int *set_flag, int flag)
{
 if((flag & 1) || o == NULL) {
   *cache_tiles= Libisoburn_default_cache_tileS;
   *tile_blocks= Libisoburn_default_tile_blockS;
   *set_flag= 0;
   return(1);
 }
 *cache_tiles= o->cache_tiles;
 *tile_blocks= o->cache_tile_blocks;
 *set_flag= 0;
 return(1);
}


int isoburn_ropt_set_extensions(struct isoburn_read_opts *o, int ext)
{
 o->norock= !!(ext&1);
 o->nojoliet= !!(ext&2);
 o->noiso1999= !!(ext&4);
 o->preferjoliet= !!(ext&8);
 o->pretend_blank= !!(ext&16);
 o->noaaip= !!(ext & 32);
 o->noacl= !!(ext & 64);
 o->noea= !!(ext & 128);
 o->noino= !!(ext & 256);
 o->nomd5= (ext >> 9) & 3;
 o->do_ecma119_map= !!(ext & 2048);
 o->map_mode= (ext >> 12) & 3;
 o->do_joliet_map= !!(ext & 16384);
 o->joliet_map_mode= !!(ext & 32768);
 return(1);
}


int isoburn_ropt_get_extensions(struct isoburn_read_opts *o, int *ext)
{
 *ext= (!!o->norock) | ((!!o->nojoliet)<<1) | ((!!o->noiso1999)<<2) |
       ((!!o->preferjoliet)<<3) | ((!!o->pretend_blank)<<4) |
       ((!!o->noaaip) << 5) | ((!!o->noacl) << 6) | ((!!o->noea) << 7) |
       ((!!o->noino) << 8) | ((o->nomd5 & 3) << 9) |
       ((!!o->do_ecma119_map) << 11) | ((o->map_mode & 3) << 12) |
       ((!!o->do_joliet_map) << 14) | ((!!o->joliet_map_mode) << 15);
 return(1);
}


int isoburn_ropt_set_default_perms(struct isoburn_read_opts *o,
                                   uid_t uid, gid_t gid, mode_t mode)
{
 mode_t dirmode;

 o->uid= uid;
 o->gid= gid;
 o->mode= mode;
 dirmode= mode;
 if(dirmode & S_IRUSR)
   dirmode|= S_IXUSR;
 if(dirmode & S_IRGRP)
   dirmode|= S_IXGRP;
 if(dirmode & S_IROTH)
   dirmode|= S_IXOTH;
 o->dirmode= dirmode;
 return(1);
}


int isoburn_ropt_get_default_perms(struct isoburn_read_opts *o,
                                   uid_t *uid, gid_t *gid, mode_t *mode)
{
 *uid= o->uid;
 *gid= o->gid;
 *mode= o->mode;
 return(1);
}


int isoburn_ropt_set_default_dirperms(struct isoburn_read_opts *o,
                                       mode_t mode)
{
 o->dirmode= mode;
 return(1);
}


int isoburn_ropt_get_default_dirperms(struct isoburn_read_opts *o,
                                      mode_t *mode)
{
 *mode= o->dirmode;
 return(1);
}


int isoburn_ropt_set_input_charset(struct isoburn_read_opts *o,
                                   char *input_charset)
{
 o->input_charset= input_charset;
 return(1);
}


int isoburn_ropt_get_input_charset(struct isoburn_read_opts *o,
                                   char **input_charset)
{
 *input_charset= o->input_charset;
 return(1);
}


int isoburn_ropt_set_auto_incharset(struct isoburn_read_opts *o, int mode)
{
 o->auto_input_charset= mode & 1;
 return(1);
}


int isoburn_ropt_get_auto_incharset(struct isoburn_read_opts *o, int *mode)
{
 *mode= o->auto_input_charset;
 return(1);
}


int isoburn_ropt_set_displacement(struct isoburn_read_opts *o,
                               uint32_t displacement, int displacement_sign)
{
 o->displacement= displacement;
 o->displacement_sign= displacement_sign;
 return(1);
}


int isoburn_ropt_get_displacement(struct isoburn_read_opts *o,
                               uint32_t *displacement, int *displacement_sign)
{
 *displacement= o->displacement;
 *displacement_sign= o->displacement_sign;
 return(1);
}


int isoburn_ropt_set_truncate_mode(struct isoburn_read_opts *o,
                                   int mode, int length)
{
 if(mode < 0 || mode > 1)
   mode= 1;
 if(length < 64)
   length= 64;
 if(length > 255)
   length= 255;
 o->truncate_mode= mode;
 o->truncate_length= length;
 return(1);
}


int isoburn_ropt_get_truncate_mode(struct isoburn_read_opts *o,
                                   int *mode, int *length)
{
 *mode= o->truncate_mode;
 *length= o->truncate_length;
 return(1);
}


int isoburn_ropt_get_size_what(struct isoburn_read_opts *o,
                               uint32_t *size, int *has_what)
{
 *size= o->size;
 *has_what= (!!o->hasRR) | ((!!o->hasJoliet)<<1) |
            ((!!o->hasIso1999)<<2) | ((!!o->hasElTorito)<<3); 
 return(1);
}


int isoburn_ropt_get_tree_loaded(struct isoburn_read_opts *o,
                                 int *tree, int *rr)
{
 *tree= o->tree_loaded;
 *rr= o->rr_loaded;
 return(1);
}


/* ----------------------------------------------------------------------- */
/*
  Options for image generation by libisofs and image transport to libburn.
*/
/* ----------------------------------------------------------------------- */


int isoburn_igopt_new(struct isoburn_imgen_opts **new_o, int flag)
{
 struct isoburn_imgen_opts *o;
 int i;

 o= (*new_o)= calloc(1, sizeof(struct isoburn_imgen_opts));
 if(o==NULL) {
   isoburn_msgs_submit(NULL, 0x00060000,
                       "Cannot allocate memory for image generation options",
                       0, "FATAL", 0);
   return(-1);
 }
 o->level= 2;
 o->rockridge= 1;
 o->joliet= 0;
 o->iso1999= 0;
 o->hardlinks= 0;
 o->aaip = 0;
 o->session_md5= 0;
 o->file_md5= 0;
 o->no_emul_toc= 0;
 o->old_empty= 0;
 o->untranslated_name_len = 0;
 o->allow_dir_id_ext = 0;
 o->omit_version_numbers= 0;
 o->allow_deep_paths= 1;
 o->rr_reloc_dir= NULL;
 o->rr_reloc_flags= 0;
 o->allow_longer_paths= 0;
 o->max_37_char_filenames= 0;
 o->no_force_dots= 0;
 o->allow_lowercase= 0;
 o->allow_full_ascii= 0;
 o->allow_7bit_ascii= 0;
 o->joliet_longer_paths= 0;
 o->joliet_long_names= 0;
 o->joliet_utf16= 0;
 o->always_gmt= 0;
 o->rrip_version_1_10= 0;
 o->dir_rec_mtime= 0;
 o->aaip_susp_1_10= 0;
 o->sort_files= 0;
 o->replace_dir_mode= 0;
 o->replace_file_mode= 0;
 o->replace_uid= 0;
 o->replace_gid= 0;
 o->dir_mode= 0555;
 o->file_mode= 0444;
 o->uid= 0;
 o->gid= 0;
 o->output_charset= NULL;
 o->fifo_size= 4*1024*1024;
 o->effective_lba= -1;
 o->data_start_lba= -1;
 o->system_area_data= NULL;
 o->system_area_options= 0;
 o->partition_offset= 0;
 o->partition_secs_per_head= 0;
 o->partition_heads_per_cyl= 0;
 o->vol_creation_time= 0;
 o->vol_modification_time= 0;
 o->vol_expiration_time= 0;
 o->vol_effective_time= 0;
 o->libjte_handle= NULL;
 o->tail_blocks= 0;
 o->prep_partition= NULL;
 o->prep_part_flag= 0;
 o->efi_boot_partition= NULL;
 o->efi_boot_part_flag= 0;
 for(i= 0; i < Libisoburn_max_appended_partitionS; i++) {
   o->appended_partitions[i]= NULL;
   o->appended_part_types[i]= 0;
   o->appended_part_flags[i]= 0;
   memset(o->appended_part_type_guids[i], 0, 16);
   o->appended_part_gpt_flags[i]= 0;
 }
 o->appended_as_gpt= 0;
 o->appended_as_apm= 0;
 o->part_like_isohybrid= 0;
 o->iso_mbr_part_type= -1;
 memset(o->gpt_guid, 0, 16);
 o->gpt_guid_mode= 0;
 o->max_ce_entries= 31;             /* Linux hates >= RR_MAX_CE_ENTRIES = 32 */
 o->max_ce_entries_flag= 2;        /* omit non-isofs fattr and ACL if needed */
 memset(o->hfsp_serial_number, 0, 8);
 o->hfsp_block_size= 0;
 o->apm_block_size= 0;
 o->do_tao= 0;
 o->do_fsync= 0;
 return(1);
}


int isoburn_igopt_destroy(struct isoburn_imgen_opts **o, int flag)
{
 int i;

 if(*o==NULL)
   return(0);
 if((*o)->rr_reloc_dir != NULL)
   free((*o)->rr_reloc_dir);
 if((*o)->prep_partition != NULL)
   free((*o)->prep_partition);
 if((*o)->efi_boot_partition != NULL)
   free((*o)->efi_boot_partition);
 for(i= 0; i < Libisoburn_max_appended_partitionS; i++)
   if((*o)->appended_partitions[i] != NULL)
     free((*o)->appended_partitions[i]);
 if ((*o)->system_area_data != NULL)
     free((*o)->system_area_data);
 free(*o);
 *o= NULL;
 return(1);
}


int isoburn_igopt_set_level(struct isoburn_imgen_opts *o, int level)
{
 o->level= level;
 return(1);
}


int isoburn_igopt_get_level(struct isoburn_imgen_opts *o, int *level)
{
 *level= o->level;
 return(1);
}


int isoburn_igopt_set_extensions(struct isoburn_imgen_opts *o, int ext)
{
 o->rockridge= !!(ext&1);
 o->joliet= !!(ext&2);
 o->iso1999= !!(ext&4);
 o->hardlinks= !!(ext & 8);
 o->aaip= !!(ext & 32);
 o->session_md5= !!(ext & 64);
 o->file_md5= (ext & (128 | 256)) >> 7;
 o->no_emul_toc= !!(ext & 512);
 o->will_cancel= !!(ext & 1024);
 o->old_empty= !!(ext & 2048);
 o->hfsplus= !!(ext & 4096);
 o->fat= !!(ext & 8192);
 return(1);
}


int isoburn_igopt_get_extensions(struct isoburn_imgen_opts *o, int *ext)
{
 *ext= (!!o->rockridge) | ((!!o->joliet)<<1) | ((!!o->iso1999)<<2) |
       ((!!o->hardlinks) << 3) | ((!!o->aaip) << 5) |
       ((!!o->session_md5) << 6) | ((o->file_md5 & 3) << 7) |
       ((!!o->no_emul_toc) << 9) | ((o->will_cancel) << 10) |
       ((!!o->old_empty) << 11) | ((!!o->hfsplus) << 12) |
       ((!!o->fat) << 13);
 return(1);
}


int isoburn_igopt_set_relaxed(struct isoburn_imgen_opts *o, int relax)
{
 o->omit_version_numbers= (!!(relax&1)) |
                          (2 * !!(relax & isoburn_igopt_only_iso_versions));
 o->allow_deep_paths= !!(relax&2);
 o->allow_longer_paths= !!(relax&4);
 o->max_37_char_filenames= !!(relax&8);
 o->no_force_dots= (!!(relax&16)) |
                   (2 * !!(relax & isoburn_igopt_no_j_force_dots));
 o->allow_lowercase= !!(relax&32);
 o->allow_full_ascii= !!(relax&64);
 o->joliet_longer_paths= !!(relax&128);
 o->always_gmt= !!(relax & isoburn_igopt_always_gmt);
 o->rrip_version_1_10= !!(relax & isoburn_igopt_rrip_version_1_10);
 o->dir_rec_mtime= !!(relax & isoburn_igopt_dir_rec_mtime);
 o->aaip_susp_1_10= !!(relax & isoburn_igopt_aaip_susp_1_10);
 o->allow_dir_id_ext= !!(relax & isoburn_igopt_allow_dir_id_ext);
 o->joliet_long_names= !!(relax & isoburn_igopt_joliet_long_names);
 o->joliet_rec_mtime= !!(relax & isoburn_igopt_joliet_rec_mtime);
 o->iso1999_rec_mtime= !!(relax & isoburn_igopt_iso1999_rec_mtime);
 o->allow_7bit_ascii= !!(relax & isoburn_igopt_allow_7bit_ascii);
 o->joliet_utf16= !!(relax & isoburn_igopt_joliet_utf16);
 return(1);
}


int isoburn_igopt_get_relaxed(struct isoburn_imgen_opts *o, int *relax)
{
 *relax= (!!o->omit_version_numbers)    | ((!!o->allow_deep_paths)<<1) |
         ((!!o->allow_longer_paths)<<2) | ((!!o->max_37_char_filenames)<<3) |
         ((!!(o->no_force_dots & 1))<<4)| ((!!o->allow_lowercase)<<5) |
         ((!!o->allow_full_ascii)<<6)   | ((!!o->joliet_longer_paths)<<7) |
         ((!!o->always_gmt)<<8)         | ((!!o->rrip_version_1_10)<<9) |
         ((!!o->dir_rec_mtime)<<10)     | ((!!o->aaip_susp_1_10)<<11) |
         ((!!(o->omit_version_numbers & 2))<<12) |
         ((!!(o->no_force_dots & 2))<<13) |
         ((!!o->allow_dir_id_ext) << 14) |
         ((!!o->joliet_long_names) << 15) |
         ((!!o->joliet_rec_mtime) << 16) |
         ((!!o->iso1999_rec_mtime) << 17) |
         ((!!o->allow_full_ascii) << 18) |
         ((!!o->joliet_utf16) << 19);
 return(1);
}


int isoburn_igopt_set_rr_reloc(struct isoburn_imgen_opts *o, char *name,
                               int flags)
{
 if(o->rr_reloc_dir != name) {
   if(o->rr_reloc_dir != NULL)
     free(o->rr_reloc_dir);
   o->rr_reloc_dir= NULL;
   if(name != NULL) {
     o->rr_reloc_dir= strdup(name);
     if(o->rr_reloc_dir == NULL) {
       isoburn_msgs_submit(NULL, 0x00060000,
                         "Cannot allocate memory for image generation options",
                          0, "FATAL", 0);
       return(-1);
     }
   }
 }
 o->rr_reloc_flags = flags & 1;
 return 1;
}


int isoburn_igopt_get_rr_reloc(struct isoburn_imgen_opts *o, char **name,
                               int *flags)
{
 *name= o->rr_reloc_dir;
 *flags= o->rr_reloc_flags;
 return(1);
}


int isoburn_igopt_set_untranslated_name_len(struct isoburn_imgen_opts *o,
                                            int len)
{
 int ret;
 IsoWriteOpts *opts = NULL;
 char *msg= NULL;

 msg= calloc(1, 160);
 if(msg == NULL)
   {ret= -1; goto ex;}

 ret= iso_write_opts_new(&opts, 0);
 if(ret < 0) {
   isoburn_msgs_submit(NULL, 0x00060000,
                 "Cannot create libisofs write options object", 0, "FATAL", 0);
   {ret= 0; goto ex;}
 }
 ret= iso_write_opts_set_untranslated_name_len(opts, len);
 if(ret < 0) {
   ret= iso_write_opts_set_untranslated_name_len(opts, -1);
   sprintf(msg,
  "Improper value for maximum length of untranslated names (%d <-> -1 ... %d)",
           len, ret);
   isoburn_msgs_submit(NULL, 0x00060000, msg, 0, "FAILURE", 0);
   iso_write_opts_free(opts);
   {ret= 0; goto ex;}
 }
 o->untranslated_name_len= ret; /* Normalized len value */
 iso_write_opts_free(opts);
 ret= 1;
ex:;
 if(msg != NULL)
   free(msg);
 return(ret);
}


int isoburn_igopt_get_untranslated_name_len(struct isoburn_imgen_opts *o,
                                            int *len)
{
 *len = o->untranslated_name_len;
 return(1);
}


int isoburn_igopt_set_sort_files(struct isoburn_imgen_opts *o, int value)
{
 o->sort_files= !!(value&1);
 return(1);
}


int isoburn_igopt_get_sort_files(struct isoburn_imgen_opts *o, int *value)
{
 *value= !!o->sort_files;
 return(1);
}


int isoburn_igopt_set_over_mode(struct isoburn_imgen_opts *o,
                               int replace_dir_mode, int replace_file_mode,
                               mode_t dir_mode, mode_t file_mode)
{
 o->replace_dir_mode= replace_dir_mode%3;
 o->replace_file_mode= replace_file_mode%3;
 o->dir_mode= dir_mode;
 o->file_mode= file_mode;
 return(1);
}


int isoburn_igopt_get_over_mode(struct isoburn_imgen_opts *o,
                               int *replace_dir_mode, int *replace_file_mode,
                               mode_t *dir_mode, mode_t *file_mode)
{
 *replace_dir_mode= o->replace_dir_mode%3;
 *replace_file_mode= o->replace_file_mode%3;
 *dir_mode= o->dir_mode;
 *file_mode= o->file_mode;
 return(1);
}


int isoburn_igopt_set_over_ugid(struct isoburn_imgen_opts *o,
                               int replace_uid, int replace_gid,
                               uid_t uid, gid_t gid)
{
 o->replace_uid= replace_uid%3;
 o->replace_gid= replace_gid%3;
 o->uid= uid;
 o->gid= gid;
 return(1);
}

int isoburn_igopt_get_over_ugid(struct isoburn_imgen_opts *o,
                               int *replace_uid, int *replace_gid,
                               uid_t *uid, gid_t *gid)
{
 *replace_uid= o->replace_uid%3;
 *replace_gid= o->replace_gid%3;
 *uid= o->uid;
 *gid= o->gid;
 return(1);
}


int isoburn_igopt_set_out_charset(struct isoburn_imgen_opts *o,
                                 char *output_charset)
{
 o->output_charset= output_charset;
 return(1);
}


int isoburn_igopt_get_out_charset(struct isoburn_imgen_opts *o,
                                 char **output_charset)
{
 *output_charset= o->output_charset;
 return(1);
}


int isoburn_igopt_set_fifo_size(struct isoburn_imgen_opts *o, int fifo_size)
{
 o->fifo_size= fifo_size;
 return(1);
}


int isoburn_igopt_get_fifo_size(struct isoburn_imgen_opts *o, int *fifo_size)
{
 *fifo_size= o->fifo_size;
 return(1);
}


int isoburn_igopt_get_effective_lba(struct isoburn_imgen_opts *o, int *lba)
{
 *lba= o->effective_lba;
 return(1);
}


int isoburn_igopt_get_data_start(struct isoburn_imgen_opts *o, int *lba)
{
 *lba= o->data_start_lba;
 return(1);
}


int isoburn_igopt_set_scdbackup_tag(struct isoburn_imgen_opts *o, char *name,
                                    char *timestamp, char *tag_written)
{
 strncpy(o->scdbackup_tag_name, name, 80);
 o->scdbackup_tag_name[80]= 0;
 strncpy(o->scdbackup_tag_time, timestamp, 18);
 o->scdbackup_tag_time[18]= 0;
 o->scdbackup_tag_written = tag_written;
 if(tag_written != NULL)
   tag_written[0]= 0;
 return(1);
}


int isoburn_igopt_get_scdbackup_tag(struct isoburn_imgen_opts *o,
                                    char name[81], char timestamp[19],
                                    char **tag_written)
{
 strncpy(name, o->scdbackup_tag_name, 80);
 name[80]= 0;
 strncpy(timestamp, o->scdbackup_tag_time, 18);
 timestamp[18]= 0;
 *tag_written= o->scdbackup_tag_written;
 return(1);
}


int isoburn_igopt_set_system_area(struct isoburn_imgen_opts *opts,
                                  char data[32768], int options)
{
 if (data == NULL) { /* Disable */
   if (opts->system_area_data != NULL)
     free(opts->system_area_data);
   opts->system_area_data = NULL;
 } else {
   if (opts->system_area_data == NULL) {
     opts->system_area_data = calloc(32768, 1);
     if (opts->system_area_data == NULL)
       return(-1);
   }
   memcpy(opts->system_area_data, data, 32768);
 }
 opts->system_area_options = options & 0x3ffff;
 return(1);
}


int isoburn_igopt_get_system_area(struct isoburn_imgen_opts *opts, 
                                  char data[32768], int *options)
{
 *options= opts->system_area_options;
 if(opts->system_area_data == NULL)
   return(0);
 memcpy(data, opts->system_area_data, 32768);
 return(1);
}


int isoburn_igopt_set_pvd_times(struct isoburn_imgen_opts *opts,
                        time_t vol_creation_time, time_t vol_modification_time,
                        time_t vol_expiration_time, time_t vol_effective_time,
                        char *vol_uuid)
{
 opts->vol_creation_time = vol_creation_time;
 opts->vol_modification_time = vol_modification_time;
 opts->vol_expiration_time = vol_expiration_time;
 opts->vol_effective_time = vol_effective_time;
 strncpy(opts->vol_uuid, vol_uuid, 16);
 opts->vol_uuid[16] = 0;
 return(1);
}


int isoburn_igopt_get_pvd_times(struct isoburn_imgen_opts *opts,
                      time_t *vol_creation_time, time_t *vol_modification_time,
                      time_t *vol_expiration_time, time_t *vol_effective_time,
                      char vol_uuid[17])
{
 *vol_creation_time = opts->vol_creation_time;
 *vol_modification_time = opts->vol_modification_time;
 *vol_expiration_time = opts->vol_expiration_time;
 *vol_effective_time = opts->vol_effective_time;
 strcpy(vol_uuid, opts->vol_uuid);
 return(1);
}


int isoburn_igopt_set_part_offset(struct isoburn_imgen_opts *opts,
                                  uint32_t block_offset_2k,
                                  int secs_512_per_head, int heads_per_cyl)
{
 if (block_offset_2k > 0 && block_offset_2k < 16)
   return(0);
 opts->partition_offset = block_offset_2k;
 opts->partition_secs_per_head = secs_512_per_head;
 opts->partition_heads_per_cyl = heads_per_cyl;
 return(1);
}


int isoburn_igopt_get_part_offset(struct isoburn_imgen_opts *opts,
                                  uint32_t *block_offset_2k,
                                  int *secs_512_per_head, int *heads_per_cyl)
{
 *block_offset_2k = opts->partition_offset;
 *secs_512_per_head = opts->partition_secs_per_head;
 *heads_per_cyl = opts->partition_heads_per_cyl;
 return 1;
}


int isoburn_igopt_attach_jte(struct isoburn_imgen_opts *opts,
                             void *libjte_handle)
{
 opts->libjte_handle = libjte_handle;
 return 1;
}


int isoburn_igopt_detach_jte(struct isoburn_imgen_opts *opts,
                             void **libjte_handle)
{
 if(libjte_handle != NULL)
   *libjte_handle = opts->libjte_handle;
 opts->libjte_handle = NULL;
 return 1;
}


int isoburn_igopt_set_tail_blocks(struct isoburn_imgen_opts *opts,
                                  uint32_t num_blocks)
{
  opts->tail_blocks = num_blocks;
  return 1;
}

int isoburn_igopt_get_tail_blocks(struct isoburn_imgen_opts *opts,
                                  uint32_t *num_blocks)
{
  *num_blocks = opts->tail_blocks;
  return 1;
}


int isoburn_igopt_set_prep_partition(struct isoburn_imgen_opts *o,
                                     char *path, int flag)
{
 if(o->prep_partition != NULL)
   free(o->prep_partition);
 o->prep_partition= NULL;
 o->prep_part_flag= 0;
 if(path != NULL) {
   o->prep_partition= strdup(path);
   if(o->prep_partition == NULL) {
     isoburn_report_iso_error(ISO_OUT_OF_MEM, "Out of memory", 0, "FATAL", 0);
     return(-1);
   }
 }
 o->prep_part_flag= flag & 1;
 return(1);
}


int isoburn_igopt_get_prep_partition(struct isoburn_imgen_opts *opts,
                                     char **path, int flag)
{
 *path= opts->prep_partition;
 if(flag & 1)
   return(1 + (opts->prep_part_flag & 0x3fffffff));
 return(1);
}


int isoburn_igopt_set_efi_bootp(struct isoburn_imgen_opts *o,
                                char *path, int flag)
{
 if(o->efi_boot_partition != NULL)
   free(o->efi_boot_partition);
 o->efi_boot_partition= NULL;
 o->efi_boot_part_flag= 0;
 if(path != NULL) {
   o->efi_boot_partition= strdup(path);
   if(o->efi_boot_partition == NULL) {
     isoburn_report_iso_error(ISO_OUT_OF_MEM, "Out of memory", 0, "FATAL", 0);
     return(-1);
   }
 }
 o->efi_boot_part_flag = flag & 1;
 return(1);
}


int isoburn_igopt_get_efi_bootp(struct isoburn_imgen_opts *opts,
                                char **path, int flag)
{
 *path= opts->efi_boot_partition;
 if(flag & 1)
   return(1 + (opts->efi_boot_part_flag & 0x3fffffff));
 return(1);
}


int isoburn_igopt_set_partition_img(struct isoburn_imgen_opts *opts,
                                  int partition_number, uint8_t partition_type,
                                  char *image_path)
{
 char msg[80];

 if (partition_number < 1 ||
     partition_number > Libisoburn_max_appended_partitionS) {
   sprintf(msg, "Partition number is out of range (1 ... %d)",
           Libisoburn_max_appended_partitionS);
   isoburn_msgs_submit(NULL, 0x00060000, msg, 0, "FAILURE", 0);
   return(0);
 }
 if (opts->appended_partitions[partition_number - 1] != NULL)
   free(opts->appended_partitions[partition_number - 1]);
 opts->appended_partitions[partition_number - 1] = strdup(image_path);
 if (opts->appended_partitions[partition_number - 1] == NULL)
   return(-1);
 opts->appended_part_types[partition_number - 1] = partition_type;
 return(1);
}


int isoburn_igopt_get_partition_img(struct isoburn_imgen_opts *opts,
                                    int num_entries,
                                    uint8_t partition_types[],
                                    char *image_paths[])
{
 int i, max_entry= 0;

 for(i= 0; i < num_entries; i++)
   image_paths[i]= NULL;
 for(i= 0; i < Libisoburn_max_appended_partitionS; i++) {
   if(opts->appended_partitions[i] == NULL)
 continue;
   if(i < num_entries) {
     image_paths[i]= opts->appended_partitions[i];
     partition_types[i]= opts->appended_part_types[i];
   }
   max_entry= i + 1;
 }
 return(max_entry);
}


int isoburn_igopt_set_part_flag(struct isoburn_imgen_opts *opts,
                                int partition_number, int flag)
{
 char msg[80];

 if (partition_number < 1 ||
     partition_number > Libisoburn_max_appended_partitionS) {
   sprintf(msg, "Partition number is out of range (1 ... %d)",
           Libisoburn_max_appended_partitionS);
   isoburn_msgs_submit(NULL, 0x00060000, msg, 0, "FAILURE", 0);
   return(0);
 }
 opts->appended_part_flags[partition_number - 1]= flag;
 return(1);
}


int isoburn_igopt_get_part_flags(struct isoburn_imgen_opts *opts,
                                 int num_entries, int part_flags[])
{
 int i, max_entry= 0;

 for(i= 0; i < num_entries; i++)
   part_flags[i]= 0;
 for(i= 0; i < Libisoburn_max_appended_partitionS; i++) {
   if(i < num_entries)
     part_flags[i]= opts->appended_part_flags[i];
   max_entry= i + 1;
 }
 return(max_entry);
}


int isoburn_igopt_set_appended_as_gpt(struct isoburn_imgen_opts *opts, int gpt)
{
 opts->appended_as_gpt= !!gpt;
 return(1);
}


int isoburn_igopt_get_appended_as_gpt(struct isoburn_imgen_opts *opts,
                                      int *gpt)
{
 *gpt= opts->appended_as_gpt;
 return(1);
}


int isoburn_igopt_set_part_type_guid(struct isoburn_imgen_opts *opts,
                                     int partition_number, uint8_t guid[16],
                                     int valid)
{
 char msg[80];

 if (partition_number < 1 ||
     partition_number > Libisoburn_max_appended_partitionS) {
   sprintf(msg, "Partition number is out of range (1 ... %d)",
           Libisoburn_max_appended_partitionS);
   isoburn_msgs_submit(NULL, 0x00060000, msg, 0, "FAILURE", 0);
   return(0);
 }
 if(valid)
   memcpy(opts->appended_part_type_guids[partition_number - 1], guid, 16);
 if(valid)
   opts->appended_part_gpt_flags[partition_number - 1]|= 1;
 else
   opts->appended_part_gpt_flags[partition_number - 1]&= ~1;
 return(1);
}

int isoburn_igopt_get_part_type_guid(struct isoburn_imgen_opts *opts,
                                     int num_entries, uint8_t guids[][16],
                                     int valids[])
{
 int i, max_entry= 0;

 for(i= 0; i < num_entries; i++) {
   memset(guids[i], 0, 16);
   valids[i]= 0;
 }
 for(i= 0; i < Libisoburn_max_appended_partitionS; i++) {
   if(i < num_entries) {
     memcpy(guids[i], opts->appended_part_type_guids[i], 16);
     valids[i]= opts->appended_part_gpt_flags[i] & 1;
   }
   max_entry= i + 1;
 }
 return(max_entry);
}

int isoburn_igopt_set_appended_as_apm(struct isoburn_imgen_opts *opts, int apm)
{
 opts->appended_as_apm= !!apm;
 return(1);
}


int isoburn_igopt_get_appended_as_apm(struct isoburn_imgen_opts *opts,
                                      int *apm)
{
 *apm= opts->appended_as_apm;
 return(1);
}


int isoburn_igopt_set_part_like_isohybrid(struct isoburn_imgen_opts *opts,
                                          int alike)
{
 opts->part_like_isohybrid= !!alike;
 return(1);
}


int isoburn_igopt_get_part_like_isohybrid(struct isoburn_imgen_opts *opts,
                                          int *alike)
{
 *alike= opts->part_like_isohybrid;
 return(1);
}


int isoburn_igopt_set_iso_mbr_part_type(struct isoburn_imgen_opts *opts,
                                        int part_type)
{
 if(part_type < -1 || part_type > 255)
   part_type = -1;
 opts->iso_mbr_part_type = part_type;
 return(1);
}


int isoburn_igopt_get_iso_mbr_part_type(struct isoburn_imgen_opts *opts,
                                        int *part_type)
{
 *part_type= opts->iso_mbr_part_type;
 return(1);
}


int isoburn_igopt_set_iso_type_guid(struct isoburn_imgen_opts *opts,
                                    uint8_t guid[16], int valid)
{
 if(valid)
   memcpy(opts->iso_gpt_type_guid, guid, 16);
 opts->iso_gpt_flag= (opts->iso_gpt_flag & ~1) | !!valid;
 return(1); 
}


int isoburn_igopt_get_iso_type_guid(struct isoburn_imgen_opts *opts,
                                    uint8_t guid[16])
{
 memcpy(guid, opts->iso_gpt_type_guid, 16);
 return(opts->iso_gpt_flag & 1);
}


int isoburn_igopt_set_gpt_guid(struct isoburn_imgen_opts *opts,
                               uint8_t guid[16], int mode)
{
 if(mode < 0 || mode > 2) {
   isoburn_msgs_submit(NULL, 0x00060000,
                       "Unrecognized GPT disk GUID setup mode. (0 ... 2)",
                       0, "FAILURE", 0);
   return(0);
 }
 opts->gpt_guid_mode= mode;
 if(opts->gpt_guid_mode == 1)
    memcpy(opts->gpt_guid, guid, 16);
 return 1;
}


int isoburn_igopt_get_gpt_guid(struct isoburn_imgen_opts *opts,
                               uint8_t guid[16], int *mode)
{
 if(opts->gpt_guid_mode == 1)
   memcpy(guid, opts->gpt_guid, 16);
 *mode = opts->gpt_guid_mode;
 return(1);
}


int isoburn_igopt_set_max_ce_entries(struct isoburn_imgen_opts *opts,
                                     uint32_t num, int flag)
{
 if(num > 100000) {
   isoburn_msgs_submit(NULL, 0x00060000,
                    "Too many CE entries enabled for single file (max 100000)",
                       0, "SORRY", 0);
   return(0);
 }
 if(num == 0)
   num= 1;
 opts->max_ce_entries= num;
 opts->max_ce_entries_flag= (flag & 15);
 return(1);
}


int isoburn_igopt_get_max_ce_entries(struct isoburn_imgen_opts *opts,
                                     uint32_t *num, int *max_ce_flag)
{
 *num= opts->max_ce_entries;
 *max_ce_flag= opts->max_ce_entries_flag;
 return(1);
}


int isoburn_igopt_set_disc_label(struct isoburn_imgen_opts *opts, char *label)
{
    strncpy(opts->ascii_disc_label, label, Libisoburn_disc_label_sizE - 1);
    opts->ascii_disc_label[Libisoburn_disc_label_sizE - 1] = 0;
    return(1);
}


int isoburn_igopt_get_disc_label(struct isoburn_imgen_opts *opts, char **label)
{
    *label= opts->ascii_disc_label;
    return(1);
}


int isoburn_igopt_set_hfsp_serial_number(struct isoburn_imgen_opts *opts,
                                         uint8_t serial_number[8])
{
 memcpy(opts->hfsp_serial_number, serial_number, 8);
 return(1);
}


int isoburn_igopt_get_hfsp_serial_number(struct isoburn_imgen_opts *opts,
                                         uint8_t serial_number[8])
{
 memcpy(serial_number, opts->hfsp_serial_number, 8);
 return(1);
}


int isoburn_igopt_set_hfsp_block_size(struct isoburn_imgen_opts *opts,
                                      int hfsp_block_size, int apm_block_size)
{
 char msg[80];

 msg[0]= 0;
 if(hfsp_block_size != -1) {
   if(hfsp_block_size != 0 && hfsp_block_size != 512 &&
       hfsp_block_size != 2048) {
     sprintf(msg, "Not a supported HFS+ size (%d <-> 0, 512, 2048)",
                  hfsp_block_size);
     isoburn_msgs_submit(NULL, 0x00060000, msg, 0, "FAILURE", 0);
   }
   opts->hfsp_block_size = hfsp_block_size;
 }
 if(apm_block_size != -1) {
   if(apm_block_size != 0 && apm_block_size != 512 && apm_block_size != 2048) {
     sprintf(msg, "Not a supported APM block size (%d <-> 0, 512, 2048)",
                  apm_block_size);
     isoburn_msgs_submit(NULL, 0x00060000, msg, 0, "FAILURE", 0);
   }
   opts->apm_block_size = apm_block_size;
 }
 if(msg[0])
   return(0);
 return(1);
}


int isoburn_igopt_get_hfsp_block_size(struct isoburn_imgen_opts *opts,
                                     int *hfsp_block_size, int *apm_block_size)
{
 *hfsp_block_size= opts->hfsp_block_size;
 *apm_block_size= opts->apm_block_size;
 return(1);
}


int isoburn_igopt_set_write_type(struct isoburn_imgen_opts *opts, int do_tao)
{
 if(do_tao < -1 || do_tao > 1)
   return(0);
 opts->do_tao= do_tao;
 return(1);
}


int isoburn_igopt_get_write_type(struct isoburn_imgen_opts *opts, int *do_tao)
{
 *do_tao= opts->do_tao;
 return(1);
}

int isoburn_igopt_set_stdio_endsync(struct isoburn_imgen_opts *opts, 
                                  int do_sync)
{
 opts->do_fsync= !!do_sync;
 return(1);
}

int isoburn_igopt_get_stdio_endsync(struct isoburn_imgen_opts *opts, 
                                  int *do_sync)
{
 *do_sync= opts->do_fsync;
 return(1);
}

int isoburn_conv_name_chars(struct isoburn_imgen_opts *opts,
                            char *name, size_t name_len,
                            char **result, size_t *result_len, int flag)
{
 int ret;
 IsoWriteOpts *wopts= NULL;

 ret = iso_write_opts_new(&wopts, 0);
 if (ret < 0) {
   isoburn_report_iso_error(ret, "Cannot create iso_write_opts", 0, "FATAL",0);
   goto ex;
 }
 ret= isoburn_make_iso_write_opts(NULL, opts, 0, wopts, 0);
 if(ret < 0)
   goto ex;
 ret= iso_conv_name_chars(wopts, name, name_len, result, result_len, flag);
ex:;
 if(wopts != NULL)
   iso_write_opts_free(wopts); 
 return(ret);
}
