diff --git a/libisofs/ecma119_tree.c b/libisofs/ecma119_tree.c new file mode 100644 index 0000000..0cbd387 --- /dev/null +++ b/libisofs/ecma119_tree.c @@ -0,0 +1,312 @@ +/* vim: set noet ts=8 sts=8 sw=8 : */ + +#include +#include +#include +#include + +#include "ecma119.h" +#include "ecma119_tree.h" +#include "tree.h" +#include "util.h" + +static size_t calc_dirent_len(struct ecma119_tree_node *n) +{ + int ret = n->name ? strlen(n->name) + 33 : 34; + if (ret % 2) ret++; + return ret; +} + +static struct ecma119_tree_node* +create_dir(struct ecma119_write_target *t, + struct ecma119_tree_node *parent, + struct iso_tree_node *iso) +{ + struct ecma119_tree_node *ret; + + assert(t && (!parent || parent->type == ECMA119_DIR) + && iso && S_ISDIR(iso->attrib.st_mode)); + + ret = calloc(1, sizeof(struct ecma119_tree_node)); + ret->name = iso->name ? ((t->iso_level == 1) ? iso_1_dirid(iso->name) + : iso_2_dirid(iso->name)) + : NULL; + ret->dirent_len = calc_dirent_len(ret); + ret->iso_self = iso; + ret->target = t; + ret->type = ECMA119_DIR; + ret->parent = ret->dir.real_parent = parent; + ret->dir.depth = parent ? parent->dir.depth + 1 : 1; + ret->dir.nchildren = iso->nchildren; + ret->dir.children = calloc(1, sizeof(void*) * iso->nchildren); + return ret; +} + +static struct ecma119_tree_node* +create_file(struct ecma119_write_target *t, + struct ecma119_tree_node *parent, + struct iso_tree_node *iso) +{ + struct ecma119_tree_node *ret; + + assert(t && iso && parent && parent->type == ECMA119_DIR); + + ret = calloc(1, sizeof(struct ecma119_tree_node)); + ret->name = iso->name ? ((t->iso_level == 1) ? iso_1_fileid(iso->name) + : iso_2_fileid(iso->name)) + : NULL; + ret->dirent_len = calc_dirent_len(ret); + ret->parent = parent; + ret->iso_self = iso; + ret->target = t; + ret->type = ECMA119_FILE; + return ret; +} + +static struct ecma119_tree_node* +create_tree(struct ecma119_write_target *t, + struct ecma119_tree_node *parent, + struct iso_tree_node *iso) +{ + struct ecma119_tree_node *ret; + size_t i; + + assert(t && iso); + + ret = (S_ISDIR(iso->attrib.st_mode) ? create_dir : create_file) + (t, parent, iso); + for (i = 0; i < iso->nchildren; i++) { + ret->dir.children[i] = create_tree(t, ret, iso->children[i]); + } + return ret; +} + +void +ecma119_tree_free(struct ecma119_tree_node *root) +{ + size_t i; + + if (root->type == ECMA119_DIR) { + for (i=0; i < root->dir.nchildren; i++) { + ecma119_tree_free(root->dir.children[i]); + } + free(root->dir.children); + } + free(root->name); + free(root); +} + +static size_t +max_child_name_len(struct ecma119_tree_node *root) +{ + size_t ret = 0, i; + + assert(root->type == ECMA119_DIR); + for (i=0; i < root->dir.nchildren; i++) { + size_t len = strlen(root->dir.children[i]->name); + ret = MAX(ret, len); + } + return ret; +} + +static void +reparent(struct ecma119_tree_node *child, + struct ecma119_tree_node *parent) +{ + int found = 0; + size_t i; + struct ecma119_tree_node *placeholder; + + assert(child && parent && parent->type == ECMA119_DIR && child->parent); + + /* replace the child in the original parent with a placeholder */ + for (i=0; i < child->parent->dir.nchildren; i++) { + if (child->parent->dir.children[i] == child) { + placeholder = create_file(child->target, + child->parent, + child->iso_self); + placeholder->file.real_me = child; + child->parent->dir.children[i] = placeholder; + found = 1; + break; + } + } + assert(found); + + /* add the child to its new parent */ + child->parent = parent; + parent->dir.nchildren++; + parent->dir.children = realloc( parent->dir.children, + sizeof(void*) * parent->dir.nchildren ); + parent->dir.children[parent->dir.nchildren-1] = child; +} + +/** + * Reorder the tree, if necessary, to ensure that + * - the depth is at most 8 + * - each path length is at most 255 characters + */ +static void +reorder_tree(struct ecma119_tree_node *root, + struct ecma119_tree_node *cur) +{ + size_t max_path; + + assert(root && cur && cur->type == ECMA119_DIR); + + cur->dir.depth = cur->parent ? cur->parent->dir.depth + 1 : 1; + cur->dir.path_len = cur->parent ? cur->parent->dir.path_len + + strlen(cur->name) : 0; + max_path = cur->dir.path_len + cur->dir.depth + max_child_name_len(cur); + + if (cur->dir.depth > 8 || max_path > 255) { + reparent(cur, root); + /* we are appended to the root's children now, so there is no + * need to recurse (the root will hit us again) */ + } else { + size_t i; + + for (i=0; i < cur->dir.nchildren; i++) { + if (cur->dir.children[i]->type == ECMA119_DIR) + reorder_tree(root, cur->dir.children[i]); + } + } +} + +static int +cmp_node(const void *f1, const void *f2) +{ + struct ecma119_tree_node *f = *((struct ecma119_tree_node**)f1); + struct ecma119_tree_node *g = *((struct ecma119_tree_node**)f2); + return strcmp(f->name, g->name); +} + +static void +sort_tree(struct ecma119_tree_node *root) +{ + size_t i; + + assert(root && root->type == ECMA119_DIR); + + qsort(root->dir.children, root->dir.nchildren, sizeof(void*), cmp_node); + for (i=0; i < root->dir.nchildren; i++) { + if (root->dir.children[i]->type == ECMA119_DIR) + sort_tree(root->dir.children[i]); + } +} + +/** + * Change num_change characters of the given filename in order to ensure the + * name is unique. If the name is short enough (depending on the ISO level), + * we can append the characters instead of changing them. + * + * \p seq_num is the index of this file in the sequence of identical filenames. + * + * For example, seq_num=3, num_change=2, name="HELLOTHERE.TXT" changes name to + * "HELLOTHE03.TXT" + */ +static void +mangle_name(char **name, int num_change, int level, int seq_num) +{ + char *dot = strrchr(*name, '.'); + char *semi = strrchr(*name, ';'); + size_t len = strlen(*name); + char base[len+1], ext[len+1]; + char fmt[12]; + size_t baselen, extlen; + + if (num_change >= len) { + return; + } + strncpy(base, *name, len+1); + if (dot) { + base[dot - *name] = '\0'; + strncpy(ext, dot+1, len+1); + if (semi) { + ext[semi - dot - 1] = '\0'; + } + } else { + base[len-2] = '\0'; + ext[0] = '\0'; + } + baselen = strlen(base); + extlen = strlen(ext); + if (level == 1 && baselen + num_change > 8) { + base[8 - num_change] = '\0'; + } else if (level != 1 && baselen + extlen + num_change > 30) { + base[30 - extlen - num_change] = '\0'; + } + + sprintf(fmt, "%%s%%0%1dd.%%s;1", num_change); + *name = realloc(*name, baselen + extlen + num_change + 4); + sprintf(*name, fmt, base, seq_num, ext); +} + +static void +mangle_all(struct ecma119_tree_node *dir) +{ + size_t i, j, k; + struct ecma119_dir_info d = dir->dir; + size_t n_change; + int changed; + + assert(dir->type == ECMA119_DIR); + do { + changed = 0; + for (i=0; i < d.nchildren; i++) { + /* find the number of consecutive equal names */ + j = 1; + while ( i+j < d.nchildren && + !strcmp(d.children[i]->name, + d.children[i+j]->name) ) + j++; + if (j == 1) continue; + + /* mangle the names */ + changed = 1; + n_change = j / 10 + 1; + for (k=0; k < j; k++) { + mangle_name(&(d.children[i+k]->name), + n_change, + dir->target->iso_level, + k); + d.children[i+k]->dirent_len = + calc_dirent_len(d.children[i+k]); + } + + /* skip ahead by the number of mangled names */ + i += j - 1; + } + } while (changed); + + for (i=0; i < d.nchildren; i++) { + if (d.children[i]->type == ECMA119_DIR) + mangle_all(d.children[i]); + } +} + +struct ecma119_tree_node* +ecma119_tree_create(struct ecma119_write_target *t, + struct iso_tree_node *iso_root) +{ + t->root = create_tree(t, NULL, iso_root); + reorder_tree(t->root, t->root); + sort_tree(t->root); + mangle_all(t->root); + return t->root; +} + +void +ecma119_tree_print(struct ecma119_tree_node *root, int spaces) +{ + size_t i; + char sp[spaces+1]; + + memset(sp, ' ', spaces); + sp[spaces] = '\0'; + + printf("%s%s\n", sp, root->name); + if (root->type == ECMA119_DIR) + for (i=0; i < root->dir.nchildren; i++) + ecma119_tree_print(root->dir.children[i], spaces+2); +} diff --git a/libisofs/ecma119_tree.h b/libisofs/ecma119_tree.h new file mode 100644 index 0000000..1d6023b --- /dev/null +++ b/libisofs/ecma119_tree.h @@ -0,0 +1,95 @@ +/* vim: set noet ts=8 sts=8 sw=8 : */ + +/** + * \file ecma119_tree.h + * + * Declarations for creating, modifying and printing filesystem trees that + * are compatible with ecma119. + */ + +#ifndef LIBISO_ECMA119_TREE_H +#define LIBISO_ECMA119_TREE_H + +struct ecma119_write_target; + +enum { + ECMA119_FILE, + ECMA119_DIR +}; + +struct ecma119_dir_info { + struct susp_info self_susp; /**< susp entries for "." */ + struct susp_info parent_susp; /**< susp entries for ".." */ + + size_t len; /**< sum of the lengths of children's + * Directory Records (including SU) */ + size_t CE_len; /**< sum of the lengths of children's + * SUSP CE areas */ + + int depth; + size_t path_len; /**< The length of a path up to, and + * including, this directory. This + * cannot exceed 255. */ + size_t nchildren; + struct ecma119_tree_node **children; + + struct ecma119_tree_node *real_parent; + /**< The parent before relocation */ +}; + +struct ecma119_file_info +{ + struct ecma119_tree_node *real_me; + /**< If this is non-NULL, the file is + * a placeholder for a relocated + * directory and this field points to + * that relocated directory. + */ +}; + +/** + * A node for a tree containing all the information necessary for writing + * an ISO9660 volume. + */ +struct ecma119_tree_node +{ + char *name; /**< in ASCII, conforming to the + * current ISO level. */ + size_t dirent_len; /**< Length of the directory record, + * not including SU. */ + size_t block; + + struct ecma119_tree_node *parent; + struct iso_tree_node *iso_self; + struct ecma119_write_target *target; + + struct susp_info susp; + + int type; /**< file or directory */ + /* union {*/ + struct ecma119_dir_info dir; + struct ecma119_file_info file; + /* };*/ +}; + +/** + * Create a new ecma119_tree that corresponds to the tree represented by + * \p iso_root. + */ +struct ecma119_tree_node* +ecma119_tree_create(struct ecma119_write_target *target, + struct iso_tree_node *iso_root); + +/** + * Free an ecma119 tree. + */ +void +ecma119_tree_free(struct ecma119_tree_node *root); + +/** + * Print an ecma119 tree. + */ +void +ecma119_tree_print(struct ecma119_tree_node *root, int spaces); + +#endif /* LIBISO_ECMA119_TREE_H */ diff --git a/libisofs/joliet.c b/libisofs/joliet.c new file mode 100644 index 0000000..3f71a2a --- /dev/null +++ b/libisofs/joliet.c @@ -0,0 +1,353 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ +/* vim: set noet ts=8 sts=8 sw=8 : */ + +#include "joliet.h" +#include "ecma119.h" +#include "ecma119_tree.h" +#include "tree.h" +#include "util.h" +#include "volume.h" + +#include +#include + +static struct joliet_tree_node* +create_node(struct ecma119_write_target *t, + struct joliet_tree_node *parent, + struct iso_tree_node *iso) +{ + struct joliet_tree_node *ret = + calloc(1, sizeof(struct joliet_tree_node)); + + ret->name = iso_j_id(iso->name); + ret->dirent_len = 34 + (ret->name ? ucslen(ret->name) * 2 : 0); + ret->len = iso->attrib.st_size; /* for dirs, we'll change this */ + ret->block = iso->block; /* only actually for files, not dirs */ + ret->parent = parent; + ret->iso_self = iso; + ret->target = t; + ret->nchildren = iso->nchildren; + if (ret->nchildren) + ret->children = calloc(sizeof(void*), ret->nchildren); + return ret; +} + +static struct joliet_tree_node* +create_tree(struct ecma119_write_target *t, + struct joliet_tree_node *parent, + struct iso_tree_node *iso_root) +{ + struct joliet_tree_node *root = create_node(t, parent, iso_root); + size_t i; + + for (i = 0; i < root->nchildren; i++) { + struct iso_tree_node *iso_ch = iso_root->children[i]; + if (ISO_ISDIR(iso_ch)) + root->children[i] = create_tree(t, root, iso_ch); + else + root->children[i] = create_node(t, root, iso_ch); + } + return root; +} + +static int +cmp_node(const void *f1, const void *f2) +{ + struct joliet_tree_node *f = *((struct joliet_tree_node**)f1); + struct joliet_tree_node *g = *((struct joliet_tree_node**)f2); + return ucscmp(f->name, g->name); +} + +static void +sort_tree(struct joliet_tree_node *root) +{ + size_t i; + + assert(root && ISO_ISDIR(root->iso_self)); + + qsort(root->children, root->nchildren, sizeof(void*), cmp_node); + for (i = 0; i < root->nchildren; i++) + if (ISO_ISDIR(root->children[i]->iso_self)) + sort_tree(root->children[i]); +} + +void +joliet_prepare_path_tables(struct ecma119_write_target *t) +{ + size_t cur, i, j; + + t->pathlist_joliet[0] = t->joliet_root; + t->path_table_size_joliet = 10; /* root directory record */ + cur = 1; + + for (i = 0; i < t->dirlist_len; i++) { + struct joliet_tree_node *dir = t->pathlist_joliet[i]; + for (j = 0; j < dir->nchildren; j++) { + struct joliet_tree_node *ch = dir->children[j]; + if (ISO_ISDIR(ch->iso_self)) { + size_t len = 8 + ucslen(ch->name)*2; + t->pathlist_joliet[cur++] = ch; + t->path_table_size_joliet += len; + } + } + } +} + +/** + * Calculate the size of each directory. + */ +void +joliet_calc_dir_size(struct ecma119_write_target *t, + struct joliet_tree_node *root) +{ + size_t i; + struct joliet_tree_node *ch; + + assert(root && ISO_ISDIR(root->iso_self)); + + root->len = 68; /* for "." and ".." entries */ + for (i = 0; i < root->nchildren; i++) { + ch = root->children[i]; + root->len += ch->dirent_len; + if (ISO_ISDIR(ch->iso_self)) + joliet_calc_dir_size(t, ch); + } + t->total_dir_size_joliet += round_up (root->len, t->block_size); +} + +/** + * Calculate the position of each directory. Also fill out t->dirlist_joliet. + */ +void +joliet_calc_dir_pos(struct ecma119_write_target *t, + struct joliet_tree_node *root) +{ + size_t i; + struct joliet_tree_node *ch; + + assert(root && ISO_ISDIR(root->iso_self)); + + root->block = t->curblock; + t->curblock += div_up(root->len, t->block_size); + t->dirlist_joliet[t->curfile++] = root; + for (i = 0; i < root->nchildren; i++) { + ch = root->children[i]; + if (ISO_ISDIR(ch->iso_self)) + joliet_calc_dir_pos(t, ch); + } + + /* reset curfile when we're finished */ + if (!root->parent) + t->curfile = 0; +} + +struct joliet_tree_node* +joliet_tree_create(struct ecma119_write_target *t, + struct iso_tree_node *iso_root) +{ + struct joliet_tree_node *root = create_tree(t, NULL, iso_root); + + sort_tree(root); + return root; +} + +/* ugh. this is mostly C&P */ +static void +write_path_table(struct ecma119_write_target *t, + int l_type, + uint8_t *buf) +{ + void (*write_int)(uint8_t*, uint32_t, int) = l_type ? + iso_lsb : iso_msb; + + size_t i; + struct ecma119_path_table_record *rec; + struct joliet_tree_node *dir; + int parent = 0; + + assert (t->joliet); + + for (i = 0; i < t->dirlist_len; i++) { + dir = t->pathlist_joliet[i]; + while ((i) && t->pathlist_joliet[parent] != dir->parent) + parent++; + assert(parent < i || i == 0); + + rec = (struct ecma119_path_table_record*) buf; + rec->len_di[0] = dir->parent ? + (uint8_t) ucslen(dir->name) * 2 : 1; + rec->len_xa[0] = 0; + write_int(rec->block, dir->block, 4); + write_int(rec->parent, parent + 1, 2); + if (dir->parent) + memcpy(rec->dir_id, dir->name, rec->len_di[0]); + buf += 8 + rec->len_di[0] + (rec->len_di[0] % 2); + } + +} + +/* if file_id is >= 0, we use it instead of the filename. As a magic number, + * file_id == 3 means that we are writing the root directory record (in order + * to distinguish it from the "." entry in the root directory) */ +static void +write_one_dir_record(struct ecma119_write_target *t, + struct joliet_tree_node *node, + int file_id, + uint8_t *buf) +{ + uint8_t len_dr = (file_id >= 0) ? 34 : node->dirent_len; + uint8_t len_fi = (file_id >= 0) ? 1 : ucslen(node->name) * 2; + uint8_t f_id = (uint8_t) ((file_id == 3) ? 0 : file_id); + uint8_t *name = (file_id >= 0) ? &f_id : (uint8_t*)node->name; + struct ecma119_dir_record *rec = (struct ecma119_dir_record*)buf; + + if (file_id == 1 && node->parent) + node = node->parent; + + rec->len_dr[0] = len_dr; + iso_bb(rec->block, node->block, 4); + iso_bb(rec->length, node->len, 4); + iso_datetime_7(rec->recording_time, t->now); + rec->flags[0] = ISO_ISDIR(node->iso_self) ? 2 : 0; + iso_bb(rec->vol_seq_number, t->volnum + 1, 2); + rec->len_fi[0] = len_fi; + memcpy(rec->file_id, name, len_fi); + +} + +static void +write_l_path_table(struct ecma119_write_target *t, uint8_t *buf) +{ + write_path_table (t, 1, buf); +} + +static void +write_m_path_table(struct ecma119_write_target *t, uint8_t *buf) +{ + write_path_table (t, 0, buf); +} + +static void +write_sup_vol_desc(struct ecma119_write_target *t, uint8_t *buf) +{ + struct ecma119_sup_vol_desc *vol = (struct ecma119_sup_vol_desc*)buf; + struct iso_volume *volume = t->volset->volume[t->volnum]; + uint16_t *vol_id = wcstoucs(volume->volume_id); + uint16_t *pub_id = wcstoucs(volume->publisher_id); + uint16_t *data_id = wcstoucs(volume->data_preparer_id); + uint16_t *volset_id = wcstoucs(t->volset->volset_id); + int vol_id_len = MIN(32, ucslen(vol_id) * 2); + int pub_id_len = MIN(128, ucslen(pub_id) * 2); + int data_id_len = MIN(128, ucslen(data_id) * 2); + int volset_id_len = MIN(128, ucslen(volset_id) * 2); + + vol->vol_desc_type[0] = 2; + memcpy(vol->std_identifier, "CD001", 5); + vol->vol_desc_version[0] = 1; + memcpy(vol->system_id, "SYSID", 5); + if (vol_id) + memcpy(vol->volume_id, vol_id, vol_id_len); + memcpy(vol->esc_sequences, "%/E", 3); + iso_bb(vol->vol_space_size, t->vol_space_size, 4); + iso_bb(vol->vol_set_size, t->volset->volset_size, 2); + iso_bb(vol->vol_seq_number, t->volnum + 1, 2); + iso_bb(vol->block_size, t->block_size, 2); + iso_bb(vol->path_table_size, t->path_table_size_joliet, 4); + iso_lsb(vol->l_path_table_pos, t->l_path_table_pos_joliet, 4); + iso_msb(vol->m_path_table_pos, t->m_path_table_pos_joliet, 4); + + write_one_dir_record(t, t->joliet_root, 3, vol->root_dir_record); + + memcpy(vol->vol_set_id, volset_id, volset_id_len); + memcpy(vol->publisher_id, pub_id, pub_id_len); + memcpy(vol->data_prep_id, data_id, data_id_len); + /*memcpy(vol->application_id, "APPID", app_id_len);*/ + + iso_datetime_17(vol->vol_creation_time, t->now); + iso_datetime_17(vol->vol_modification_time, t->now); + iso_datetime_17(vol->vol_effective_time, t->now); + vol->file_structure_version[0] = 1; + + free(vol_id); + free(volset_id); + free(pub_id); + free(data_id); + +} + +static void +write_one_dir(struct ecma119_write_target *t, + struct joliet_tree_node *dir, + uint8_t *buf) +{ + size_t i; + uint8_t *orig_buf = buf; + + assert(ISO_ISDIR (dir->iso_self)); + /* write the "." and ".." entries first */ + write_one_dir_record(t, dir, 0, buf); + buf += ((struct ecma119_dir_record*) buf)->len_dr[0]; + + write_one_dir_record(t, dir, 1, buf); + buf += ((struct ecma119_dir_record*) buf)->len_dr[0]; + + for (i = 0; i < dir->nchildren; i++) { + write_one_dir_record(t, dir->children[i], -1, buf); + buf += ((struct ecma119_dir_record*) buf)->len_dr[0]; + } + + assert (buf - orig_buf == dir->len); +} + +static void +write_dirs(struct ecma119_write_target *t, uint8_t *buf) +{ + size_t i; + struct joliet_tree_node *dir; + for (i = 0; i < t->dirlist_len; i++) { + dir = t->dirlist_joliet[i]; + write_one_dir(t, dir, buf); + buf += round_up(dir->len, t->block_size); + } +} + +void +joliet_wr_sup_vol_desc(struct ecma119_write_target *t, + uint8_t *buf) +{ + ecma119_start_chunking(t, + write_sup_vol_desc, + 2048, + buf); +} + +void +joliet_wr_l_path_table(struct ecma119_write_target *t, + uint8_t *buf) +{ + ecma119_start_chunking(t, + write_l_path_table, + t->path_table_size_joliet, + buf); +} + +void +joliet_wr_m_path_table(struct ecma119_write_target *t, + uint8_t *buf) +{ + ecma119_start_chunking(t, + write_m_path_table, + t->path_table_size_joliet, + buf); +} + +void +joliet_wr_dir_records(struct ecma119_write_target *t, + uint8_t *buf) +{ + ecma119_start_chunking(t, + write_dirs, + t->total_dir_size_joliet, + buf); +} + diff --git a/libisofs/joliet.h b/libisofs/joliet.h new file mode 100644 index 0000000..1975293 --- /dev/null +++ b/libisofs/joliet.h @@ -0,0 +1,77 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ +/* vim: set noet ts=8 sts=8 sw=8 : */ + +/** + * \file joliet.h + * + * Declare the filesystems trees that are Joliet-compatible and the public + * functions for tying them into an ecma119 volume. + */ + +#ifndef LIBISO_JOLIET_H +#define LIBISO_JOLIET_H + +#include +#include + +struct ecma119_write_target; +struct iso_tree_node; + +struct joliet_tree_node +{ + uint16_t *name; /**< In UCS-2BE. */ + size_t dirent_len; + size_t len; + size_t block; + + struct joliet_tree_node *parent; + struct iso_tree_node *iso_self; + struct ecma119_write_target *target; + + struct joliet_tree_node **children; + size_t nchildren; +}; + +/** + * Create a new joliet_tree that corresponds to the tree represented by + * \p iso_root. + */ +struct joliet_tree_node* +joliet_tree_create(struct ecma119_write_target *target, + struct iso_tree_node *iso_root); + +/** + * Calculate the size of each directory in the joliet heirarchy. + */ +void +joliet_calc_dir_size(struct ecma119_write_target *t, struct joliet_tree_node*); + +/** + * Calculate the position of each directory in the joliet heirarchy. + */ +void +joliet_calc_dir_pos(struct ecma119_write_target *t, struct joliet_tree_node*); + +/** + * Calculate the size of the joliet path table and fill in the list of + * directories. + */ +void +joliet_prepare_path_tables(struct ecma119_write_target *t); + +void +joliet_tree_free(struct joliet_tree_node *root); + +void +joliet_wr_sup_vol_desc(struct ecma119_write_target *t, uint8_t *buf); + +void +joliet_wr_l_path_table(struct ecma119_write_target *t, uint8_t *buf); + +void +joliet_wr_m_path_table(struct ecma119_write_target *t, uint8_t *buf); + +void +joliet_wr_dir_records(struct ecma119_write_target *t, uint8_t *buf); + +#endif /* LIBISO_JOLIET_H */ diff --git a/libisofs/libburn.h b/libisofs/libburn.h new file mode 100644 index 0000000..7506ed3 --- /dev/null +++ b/libisofs/libburn.h @@ -0,0 +1,928 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +#ifndef LIBBURN_H +#define LIBBURN_H + +/* Needed for off_t which is the (POSIX-ly) appropriate type for + expressing a file or stream size. + + XXX we should enforce 64-bitness for off_t +*/ +#include + +#ifndef DOXYGEN + +#if defined(__cplusplus) +#define BURN_BEGIN_DECLS \ + namespace burn { \ + extern "C" { +#define BURN_END_DECLS \ + } \ + } +#else +#define BURN_BEGIN_DECLS +#define BURN_END_DECLS +#endif + +BURN_BEGIN_DECLS + +#endif + +/** References a physical drive in the system */ +struct burn_drive; + +/** References a whole disc */ +struct burn_disc; + +/** References a single session on a disc */ +struct burn_session; + +/** References a single track on a disc */ +struct burn_track; + +/** Session format for normal audio or data discs */ +#define BURN_CDROM 0 +/** Session format for obsolete CD-I discs */ +#define BURN_CDI 0x10 +/** Session format for CDROM-XA discs */ +#define BURN_CDXA 0x20 + +#define BURN_POS_END 100 + +/** Mask for mode bits */ +#define BURN_MODE_BITS 127 + +/** Track mode - mode 0 data + 0 bytes of user data. it's all 0s. mode 0. get it? HAH +*/ +#define BURN_MODE0 (1 << 0) +/** Track mode - mode "raw" - all 2352 bytes supplied by app + FOR DATA TRACKS ONLY! +*/ +#define BURN_MODE_RAW (1 << 1) +/** Track mode - mode 1 data + 2048 bytes user data, and all the LEC money can buy +*/ +#define BURN_MODE1 (1 << 2) +/** Track mode - mode 2 data + defaults to formless, 2336 bytes of user data, unprotected + | with a data form if required. +*/ +#define BURN_MODE2 (1 << 3) +/** Track mode modifier - Form 1, | with MODE2 for reasonable results + 2048 bytes of user data, 4 bytes of subheader +*/ +#define BURN_FORM1 (1 << 4) +/** Track mode modifier - Form 2, | with MODE2 for reasonable results + lots of user data. not much LEC. +*/ +#define BURN_FORM2 (1 << 5) +/** Track mode - audio + 2352 bytes per sector. may be | with 4ch or preemphasis. + NOT TO BE CONFUSED WITH BURN_MODE_RAW +*/ +#define BURN_AUDIO (1 << 6) +/** Track mode modifier - 4 channel audio. */ +#define BURN_4CH (1 << 7) +/** Track mode modifier - Digital copy permitted, can be set on any track.*/ +#define BURN_COPY (1 << 8) +/** Track mode modifier - 50/15uS pre-emphasis */ +#define BURN_PREEMPHASIS (1 << 9) +/** Input mode modifier - subcodes present packed 16 */ +#define BURN_SUBCODE_P16 (1 << 10) +/** Input mode modifier - subcodes present packed 96 */ +#define BURN_SUBCODE_P96 (1 << 11) +/** Input mode modifier - subcodes present raw 96 */ +#define BURN_SUBCODE_R96 (1 << 12) + +/** Possible disc writing style/modes */ +enum burn_write_types +{ + /** Packet writing. + currently unsupported + */ + BURN_WRITE_PACKET, + /** Track At Once recording. + 2s gaps between tracks, no fonky lead-ins + */ + BURN_WRITE_TAO, + /** Session At Once. + block type MUST be BURN_BLOCK_SAO + */ + BURN_WRITE_SAO, + /** Raw disc at once recording. + all subcodes must be provided by lib or user + only raw block types are supported + */ + BURN_WRITE_RAW +}; + +/** Data format to send to the drive */ +enum burn_block_types +{ + /** sync, headers, edc/ecc provided by lib/user */ + BURN_BLOCK_RAW0 = 1, + /** sync, headers, edc/ecc and p/q subs provided by lib/user */ + BURN_BLOCK_RAW16 = 2, + /** sync, headers, edc/ecc and packed p-w subs provided by lib/user */ + BURN_BLOCK_RAW96P = 4, + /** sync, headers, edc/ecc and raw p-w subs provided by lib/user */ + BURN_BLOCK_RAW96R = 8, + /** only 2048 bytes of user data provided by lib/user */ + BURN_BLOCK_MODE1 = 256, + /** 2336 bytes of user data provided by lib/user */ + BURN_BLOCK_MODE2R = 512, + /** 2048 bytes of user data provided by lib/user + subheader provided in write parameters + are we ever going to support this shit? I vote no. + (supposed to be supported on all drives...) + */ + BURN_BLOCK_MODE2_PATHETIC = 1024, + /** 2048 bytes of data + 8 byte subheader provided by lib/user + hey, this is also dumb + */ + BURN_BLOCK_MODE2_LAME = 2048, + /** 2324 bytes of data provided by lib/user + subheader provided in write parameters + no sir, I don't like it. + */ + BURN_BLOCK_MODE2_OBSCURE = 4096, + /** 2332 bytes of data supplied by lib/user + 8 bytes sub header provided in write parameters + this is the second least suck mode2, and is mandatory for + all drives to support. + */ + BURN_BLOCK_MODE2_OK = 8192, + /** SAO block sizes are based on cue sheet, so use this. */ + BURN_BLOCK_SAO = 16384 +}; + +/** Possible status' of the drive in regard to the disc in it. */ +enum burn_disc_status +{ + /** The current status is not yet known */ + BURN_DISC_UNREADY, + /** The drive holds a blank disc */ + BURN_DISC_BLANK, + /** There is no disc at all in the drive */ + BURN_DISC_EMPTY, + /** There is an incomplete disc in the drive */ + BURN_DISC_APPENDABLE, + /** There is a disc with data on it in the drive */ + BURN_DISC_FULL +}; + +/** Possible types of messages form the library. */ +enum burn_message_type +{ + /** Diagnostic/Process information. For the curious user. */ + BURN_MESSAGE_INFO, + /** A warning regarding a possible problem. The user should probably + be notified, but its not fatal. */ + BURN_MESSAGE_WARNING, + /** An error message. This usually means the current process will be + aborted, and the user should definately see these. */ + BURN_MESSAGE_ERROR +}; + +/** Possible information messages */ +enum burn_message_info +{ + BURN_INFO_FOO +}; + +/** Possible warning messages */ +enum burn_message_warning +{ + BURN_WARNING_FOO +}; + +/** Possible error messages */ +enum burn_message_error +{ + BURN_ERROR_CANCELLED +}; + +/** Possible data source return values */ +enum burn_source_status +{ + /** The source is ok */ + BURN_SOURCE_OK, + /** The source is at end of file */ + BURN_SOURCE_EOF, + /** The source is unusable */ + BURN_SOURCE_FAILED +}; + + +/** Possible busy states for a drive */ +enum burn_drive_status +{ + /** The drive is not in an operation */ + BURN_DRIVE_IDLE, + /** The library is spawning the processes to handle a pending + operation (A read/write/etc is about to start but hasn't quite + yet) */ + BURN_DRIVE_SPAWNING, + /** The drive is reading data from a disc */ + BURN_DRIVE_READING, + /** The drive is writing data to a disc */ + BURN_DRIVE_WRITING, + /** The drive is writing Lead-In */ + BURN_DRIVE_WRITING_LEADIN, + /** The drive is writing Lead-Out */ + BURN_DRIVE_WRITING_LEADOUT, + /** The drive is erasing a disc */ + BURN_DRIVE_ERASING, + /** The drive is being grabbed */ + BURN_DRIVE_GRABBING +}; + +/** Information about a track on a disc - this is from the q sub channel of the + lead-in area of a disc. The documentation here is very terse. + See a document such as mmc3 for proper information. +*/ +struct burn_toc_entry +{ + /** Session the track is in */ + unsigned char session; + /** Type of data. for this struct to be valid, it must be 1 */ + unsigned char adr; + /** Type of data in the track */ + unsigned char control; + /** Zero. Always. Really. */ + unsigned char tno; + /** Track number or special information */ + unsigned char point; + unsigned char min; + unsigned char sec; + unsigned char frame; + unsigned char zero; + /** Track start time minutes for normal tracks */ + unsigned char pmin; + /** Track start time seconds for normal tracks */ + unsigned char psec; + /** Track start time frames for normal tracks */ + unsigned char pframe; +}; + + +/** Data source for tracks */ +struct burn_source { + /** Reference count for the data source. Should be 1 when a new source + is created. Increment it to take a reference for yourself. Use + burn_source_free to destroy your reference to it. */ + int refcount; + + /** Read data from the source */ + int (*read)(struct burn_source *, + unsigned char *buffer, + int size); + + /** Read subchannel data from the source (NULL if lib generated) */ + int (*read_sub)(struct burn_source *, + unsigned char *buffer, + int size); + + /** Get the size of the source's data */ + off_t (*get_size)(struct burn_source *); + + /** Clean up the source specific data */ + void (*free_data)(struct burn_source *); + + /** Next source, for when a source runs dry and padding is disabled + THIS IS AUTOMATICALLY HANDLED, DO NOT TOUCH + */ + struct burn_source *next; + + /** Source specific data */ + void *data; +}; + + +/** Information on a drive in the system */ +struct burn_drive_info +{ + /** Name of the vendor of the drive */ + char vendor[9]; + /** Name of the drive */ + char product[17]; + /** Revision of the drive */ + char revision[5]; + /** Location of the drive in the filesystem. */ + char location[17]; + + /** Can the drive read DVD-RAM discs */ + unsigned int read_dvdram:1; + /** Can the drive read DVD-R discs */ + unsigned int read_dvdr:1; + /** Can the drive read DVD-ROM discs */ + unsigned int read_dvdrom:1; + /** Can the drive read CD-R discs */ + unsigned int read_cdr:1; + /** Can the drive read CD-RW discs */ + unsigned int read_cdrw:1; + + /** Can the drive write DVD-RAM discs */ + unsigned int write_dvdram:1; + /** Can the drive write DVD-R discs */ + unsigned int write_dvdr:1; + /** Can the drive write CD-R discs */ + unsigned int write_cdr:1; + /** Can the drive write CD-RW discs */ + unsigned int write_cdrw:1; + + /** Can the drive simulate a write */ + unsigned int write_simulate:1; + + /** Can the drive report C2 errors */ + unsigned int c2_errors:1; + + /** The size of the drive's buffer (in kilobytes) */ + int buffer_size; + /** + * The supported block types in tao mode. + * They should be tested with the desired block type. + * See also burn_block_types. + */ + int tao_block_types; + /** + * The supported block types in sao mode. + * They should be tested with the desired block type. + * See also burn_block_types. + */ + int sao_block_types; + /** + * The supported block types in raw mode. + * They should be tested with the desired block type. + * See also burn_block_types. + */ + int raw_block_types; + /** + * The supported block types in packet mode. + * They should be tested with the desired block type. + * See also burn_block_types. + */ + int packet_block_types; + + /** The value by which this drive can be indexed when using functions + in the library. This is the value to pass to all libbburn functions + that operate on a drive. */ + struct burn_drive *drive; +}; + +/** Messages from the library */ +struct burn_message +{ + /** The drive associated with the message. NULL if the error is not + related to a specific drive. */ + struct burn_drive *drive; + + /** The type of message this is. See message_type for details. */ + enum burn_message_type type; + + /** The actual message */ + union detail { + struct { + enum burn_message_info message; + } info; + struct { + enum burn_message_warning message; + } warning; + struct { + enum burn_message_error message; + } error; + } detail; +}; + +/** Operation progress report. All values are 0 based indices. + * */ +struct burn_progress { + /** The total number of sessions */ + int sessions; + /** Current session.*/ + int session; + /** The total number of tracks */ + int tracks; + /** Current track. */ + int track; + /** The total number of indices */ + int indices; + /** Curent index. */ + int index; + /** The starting logical block address */ + int start_sector; + /** The number of sector */ + int sectors; + /** The current sector being processed */ + int sector; +}; + +/** Initialize the library. + This must be called before using any other functions in the library. It + may be called more than once with no effect. + If is possible to 'restart' the library by shutting it down and + re-initializing it, though there is no good reason to do that. + @return Nonzero if the library was able to initialize; zero if + initialization failed. +*/ +int burn_initialize(void); + +/** Shutdown the library. + This should be called before exiting your application. Make sure that all + drives you have grabbed are released before calling this. +*/ +void burn_finish(void); + +/** Set the verbosity level of the library. The default value is 0, which means + that nothing is output on stderr. The more you increase this, the more + debug output should be displayed on stderr for you. + @param level The verbosity level desired. 0 for nothing, higher positive + values for more information output. +*/ +void burn_set_verbosity(int level); + +/** Returns a newly allocated burn_message structure. This message should be + freed with burn_message_free() when you are finished with it. + @return A message or NULL when there are no more messages to retrieve. +*/ +struct burn_message* burn_get_message(void); + +/** Frees a burn_message structure */ +void burn_message_free(struct burn_message *msg); + +/** Scans for drives. This function MUST be called until it returns nonzero. + No drives can be in use when this is called or it will assert. + All drive pointers are invalidated by using this function. Do NOT store + drive pointers across calls to this function or death AND pain will ensue. + When the app is done with the burn_drive_info array, it must be freed with + burn_drive_info_free() + @param drives Returns an array of drives (cdroms/burners). The returned + array should be freed when it is no longer needed, and + before calling this function again to rescan. + @param n_drives Returns the number of hardware drives in @c drives. + @return Zero while scanning is not complete; non-zero when it is finished. +*/ +int burn_drive_scan(struct burn_drive_info *drives[], + unsigned int *n_drives); +/** Frees a burn_drive_info array returned by burn_drive_scan + @param info The array to free +*/ +void burn_drive_info_free(struct burn_drive_info *info); + +/** Grab a drive. This must be done before the drive can be used (for reading, + writing, etc). It may be neccesary to call this function more than once + to grab a drive. See burn_grab for details. + @param drive The drive to grab. This is found in a returned + burn_drive_info struct. + @param load Nonzero to make the drive attempt to load a disc (close its + tray door, etc). + @return 1 if the drive has been grabbed, else 0 +*/ +int burn_drive_grab(struct burn_drive *drive, int load); + +/** Release a drive. This should not be done until the drive is no longer + busy (see burn_drive_get_status). + @param drive The drive to release. + @param eject Nonzero to make the drive eject the disc in it. +*/ +void burn_drive_release(struct burn_drive *drive, int eject); + +/** Returns what kind of disc a drive is holding. This function may need to be + called more than once to get a proper status from it. See burn_status + for details. + @param drive The drive to query for a disc. + @return The status of the drive, or what kind of disc is in it. +*/ +enum burn_disc_status burn_disc_get_status(struct burn_drive *drive); + +/** Tells whether a disc can be erased or not + @return Non-zero means erasable +*/ +int burn_disc_erasable(struct burn_drive *d); + +/** Returns the progress and status of a drive. + @param drive The drive to query busy state for. + @param p Returns the progress of the operation, NULL if you don't care + @return the current status of the drive. See also burn_drive_status. +*/ +enum burn_drive_status burn_drive_get_status(struct burn_drive *drive, + struct burn_progress *p); + +/** Creates a write_opts struct for burning to the specified drive + must be freed with burn_write_opts_free + @param drive The drive to write with + @return The write_opts +*/ +struct burn_write_opts *burn_write_opts_new(struct burn_drive *drive); + +/** Frees a write_opts struct created with burn_write_opts_new + @param opts write_opts to free +*/ +void burn_write_opts_free(struct burn_write_opts *opts); + +/** Creates a write_opts struct for reading from the specified drive + must be freed with burn_write_opts_free + @param drive The drive to read from + @return The read_opts +*/ +struct burn_read_opts *burn_read_opts_new(struct burn_drive *drive); + +/** Frees a read_opts struct created with burn_read_opts_new + @param opts write_opts to free +*/ +void burn_read_opts_free(struct burn_read_opts *opts); + +/** Erase a disc in the drive. The drive must be grabbed successfully BEFORE + calling this functions. Always ensure that the drive reports a status of + BURN_DISC_FULL before calling this function. An erase operation is not + cancellable, as control of the operation is passed wholly to the drive and + there is no way to interrupt it safely. + @param drive The drive with which to erase a disc. + @param fast Nonzero to do a fast erase, where only the disc's headers are + erased; zero to erase the entire disc. +*/ +void burn_disc_erase(struct burn_drive *drive, int fast); + +/** Read a disc from the drive and write it to an fd pair. The drive must be + grabbed successfully BEFORE calling this function. Always ensure that the + drive reports a status of BURN_DISC_FULL before calling this function. + @param drive The drive from which to read a disc. + @param o The options for the read operation. +*/ +void burn_disc_read(struct burn_drive *drive, const struct burn_read_opts *o); + +/** Write a disc in the drive. The drive must be grabbed successfully BEFORE + calling this function. Always ensure that the drive reports a status of + BURN_DISC_BLANK or BURN_STATUS_FULL (to append a new session to the + disc) before calling this function. + @param o The options for the writing operation. + @param disc The struct burn_disc * that described the disc to be created +*/ +void burn_disc_write(struct burn_write_opts *o, struct burn_disc *disc); + +/** Cancel an operation on a drive. + This will only work when the drive's busy state is BURN_DRIVE_READING or + BURN_DRIVE_WRITING. + @param drive The drive on which to cancel the current operation. +*/ +void burn_drive_cancel(struct burn_drive *drive); + +/** Convert a minute-second-frame (MSF) value to sector count + @param m Minute component + @param s Second component + @param f Frame component + @return The sector count +*/ +int burn_msf_to_sectors(int m, int s, int f); + +/** Convert a sector count to minute-second-frame (MSF) + @param sectors The sector count + @param m Returns the minute component + @param s Returns the second component + @param f Returns the frame component +*/ +void burn_sectors_to_msf(int sectors, int *m, int *s, int *f); + +/** Convert a minute-second-frame (MSF) value to an lba + @param m Minute component + @param s Second component + @param f Frame component + @return The lba +*/ +int burn_msf_to_lba(int m, int s, int f); + +/** Convert an lba to minute-second-frame (MSF) + @param lba The lba + @param m Returns the minute component + @param s Returns the second component + @param f Returns the frame component +*/ +void burn_lba_to_msf(int lba, int *m, int *s, int *f); + +/** Create a new disc (for DAO recording)*/ +struct burn_disc *burn_disc_create(void); + +/** Delete disc and decrease the reference count on all its sessions + @param d The disc to be freed +*/ +void burn_disc_free(struct burn_disc *d); + +/** Create a new session (For SAO at once recording, or to be added to a + disc for DAO) +*/ +struct burn_session *burn_session_create(void); + +/** Free a session (and decrease reference count on all tracks inside) + @param s Session to be freed +*/ +void burn_session_free(struct burn_session *s); + +/** Add a session to a disc at a specific position, increasing the + sessions's reference count. + @param d Disc to add the session to + @param s Session to add to the disc + @param pos position to add at (BURN_POS_END is "at the end") + @return 0 for failure, 1 for success +*/ +int burn_disc_add_session(struct burn_disc *d, struct burn_session *s, + unsigned int pos); + +/** Remove a session from a disc + @param d Disc to remove session from + @param s Session pointer to find and remove +*/ +int burn_disc_remove_session(struct burn_disc *d, struct burn_session *s); + + +/** Create a track (for TAO recording, or to put in a session) */ +struct burn_track *burn_track_create(void); + +/** Free a track + @param t Track to free +*/ +void burn_track_free(struct burn_track *t); + +/** Add a track to a session at specified position + @param s Session to add to + @param t Track to insert in session + @param pos position to add at (BURN_POS_END is "at the end") + @return 0 for failure, 1 for success +*/ +int burn_session_add_track(struct burn_session *s, struct burn_track *t, + unsigned int pos); + +/** Remove a track from a session + @param s Session to remove track from + @param t Track pointer to find and remove + @return 0 for failure, 1 for success +*/ +int burn_session_remove_track(struct burn_session *s, struct burn_track *t); + + +/** Define the data in a track + @param t the track to define + @param offset The lib will write this many 0s before start of data + @param tail The number of extra 0s to write after data + @param pad 1 means the lib should pad the last sector with 0s if the + track isn't exactly sector sized. (otherwise the lib will + begin reading from the next track) + @param mode data format (bitfield) +*/ +void burn_track_define_data(struct burn_track *t, int offset, int tail, + int pad, int mode); + +/** Set the ISRC details for a track + @param t The track to change + @param country the 2 char country code. Each character must be + only numbers or letters. + @param owner 3 char owner code. Each character must be only numbers + or letters. + @param year 2 digit year. A number in 0-99 (Yep, not Y2K friendly). + @param serial 5 digit serial number. A number in 0-99999. +*/ +void burn_track_set_isrc(struct burn_track *t, char *country, char *owner, + unsigned char year, unsigned int serial); + +/** Disable ISRC parameters for a track + @param t The track to change +*/ +void burn_track_clear_isrc(struct burn_track *t); + +/** Hide the first track in the "pre gap" of the disc + @param s session to change + @param onoff 1 to enable hiding, 0 to disable +*/ +void burn_session_hide_first_track(struct burn_session *s, int onoff); + +/** Get the drive's disc struct - free when done + @param d drive to query + @return the disc struct +*/ +struct burn_disc *burn_drive_get_disc(struct burn_drive *d); + +/** Set the track's data source + @param t The track to set the data source for + @param s The data source to use for the contents of the track + @return An error code stating if the source is ready for use for + writing the track, or if an error occured + +*/ +enum burn_source_status burn_track_set_source(struct burn_track *t, + struct burn_source *s); + +/** Free a burn_source (decrease its refcount and maybe free it) + @param s Source to free +*/ +void burn_source_free(struct burn_source *s); + +/** Creates a data source for an image file (and maybe subcode file) */ +struct burn_source *burn_file_source_new(const char *path, + const char *subpath); + +/** Creates a data source for an image file (resp. a track) from an open + readable filedescriptor, an eventually open readable subcodes file + descriptor and eventually a fixed size in bytes. + @param datafd The source of data. + @param subfd The eventual source for subcodes. Not used if -1. + @param size The eventual fixed size of eventually both fds. + If this value is 0, the size will be determined from datafd. +*/ +struct burn_source *burn_fd_source_new(int datafd, int subfd, off_t size); + +/** Tells how long a track will be on disc */ +int burn_track_get_sectors(struct burn_track *); + + +/** Sets drive read and write speed + @param d The drive to set speed for + @param read Read speed in k/s (0 is max) + @param write Write speed in k/s (0 is max) +*/ +void burn_drive_set_speed(struct burn_drive *d, int read, int write); + +/* these are for my debugging, they will disappear */ +void burn_structure_print_disc(struct burn_disc *d); +void burn_structure_print_session(struct burn_session *s); +void burn_structure_print_track(struct burn_track *t); + +/** Sets the write type for the write_opts struct + @param opts The write opts to change + @param write_type The write type to use + @param block_type The block type to use + @return Returns 1 on success and 0 on failure. +*/ +int burn_write_opts_set_write_type(struct burn_write_opts *opts, + enum burn_write_types write_type, + int block_type); + +/** Supplies toc entries for writing - not normally required for cd mastering + @param opts The write opts to change + @param count The number of entries + @param toc_entries +*/ +void burn_write_opts_set_toc_entries(struct burn_write_opts *opts, + int count, + struct burn_toc_entry *toc_entries); + +/** Sets the session format for a disc + @param opts The write opts to change + @param format The session format to set +*/ +void burn_write_opts_set_format(struct burn_write_opts *opts, int format); + +/** Sets the simulate value for the write_opts struct + @param opts The write opts to change + @param sim If non-zero, the drive will perform a simulation instead of a burn + @return Returns 1 on success and 0 on failure. +*/ +int burn_write_opts_set_simulate(struct burn_write_opts *opts, int sim); + +/** Controls buffer underrun prevention + @param opts The write opts to change + @param underrun_proof if non-zero, buffer underrun protection is enabled + @return Returns 1 on success and 0 on failure. +*/ +int burn_write_opts_set_underrun_proof(struct burn_write_opts *opts, + int underrun_proof); + +/** Sets whether to use opc or not with the write_opts struct + @param opts The write opts to change + @param opc If non-zero, optical power calibration will be performed at + start of burn + +*/ +void burn_write_opts_set_perform_opc(struct burn_write_opts *opts, int opc); + +void burn_write_opts_set_has_mediacatalog(struct burn_write_opts *opts, int has_mediacatalog); + +void burn_write_opts_set_mediacatalog(struct burn_write_opts *opts, unsigned char mediacatalog[13]); + +/** Sets whether to read in raw mode or not + @param opts The read opts to change + @param raw_mode If non-zero, reading will be done in raw mode, so that everything in the data tracks on the + disc is read, including headers. +*/ +void burn_read_opts_set_raw(struct burn_read_opts *opts, int raw_mode); + +/** Sets whether to report c2 errors or not + @param opts The read opts to change + @param c2errors If non-zero, report c2 errors. +*/ +void burn_read_opts_set_c2errors(struct burn_read_opts *opts, int c2errors); + +/** Sets whether to read subcodes from audio tracks or not + @param opts The read opts to change + @param subcodes_audio If non-zero, read subcodes from audio tracks on the disc. +*/ +void burn_read_opts_read_subcodes_audio(struct burn_read_opts *opts, + int subcodes_audio); + +/** Sets whether to read subcodes from data tracks or not + @param opts The read opts to change + @param subcodes_data If non-zero, read subcodes from data tracks on the disc. +*/ +void burn_read_opts_read_subcodes_data(struct burn_read_opts *opts, + int subcodes_data); + +/** Sets whether to recover errors if possible + @param opts The read opts to change + @param hardware_error_recovery If non-zero, attempt to recover errors if possible. +*/ +void burn_read_opts_set_hardware_error_recovery(struct burn_read_opts *opts, + int hardware_error_recovery); + +/** Sets whether to report recovered errors or not + @param opts The read opts to change + @param report_recovered_errors If non-zero, recovered errors will be reported. +*/ +void burn_read_opts_report_recovered_errors(struct burn_read_opts *opts, + int report_recovered_errors); + +/** Sets whether blocks with unrecoverable errors should be read or not + @param opts The read opts to change + @param transfer_damaged_blocks If non-zero, blocks with unrecoverable errors will still be read. +*/ +void burn_read_opts_transfer_damaged_blocks(struct burn_read_opts *opts, + int transfer_damaged_blocks); + +/** Sets the number of retries to attempt when trying to correct an error + @param opts The read opts to change + @param hardware_error_retries The number of retries to attempt when correcting an error. +*/ +void burn_read_opts_set_hardware_error_retries(struct burn_read_opts *opts, + unsigned char hardware_error_retries); + +/** Gets the maximum write speed for a drive + @param d Drive to query + @return Maximum write speed in K/s +*/ +int burn_drive_get_write_speed(struct burn_drive *d); + +/** Gets the maximum read speed for a drive + @param d Drive to query + @return Maximum read speed in K/s +*/ +int burn_drive_get_read_speed(struct burn_drive *d); + +/** Gets a copy of the toc_entry structure associated with a track + @param t Track to get the entry from + @param entry Struct for the library to fill out +*/ +void burn_track_get_entry(struct burn_track *t, struct burn_toc_entry *entry); + +/** Gets a copy of the toc_entry structure associated with a session's lead out + @param s Session to get the entry from + @param entry Struct for the library to fill out +*/ +void burn_session_get_leadout_entry(struct burn_session *s, + struct burn_toc_entry *entry); + +/** Gets an array of all the sessions for the disc + THIS IS NO LONGER VALID AFTER YOU ADD OR REMOVE A SESSION + @param d Disc to get session array for + @param num Returns the number of sessions in the array + @return array of sessions +*/ +struct burn_session **burn_disc_get_sessions(struct burn_disc *d, + int *num); + +int burn_disc_get_sectors(struct burn_disc *d); + +/** Gets an array of all the tracks for a session + THIS IS NO LONGER VALID AFTER YOU ADD OR REMOVE A TRACK + @param s session to get track array for + @param num Returns the number of tracks in the array + @return array of tracks +*/ +struct burn_track **burn_session_get_tracks(struct burn_session *s, + int *num); + +int burn_session_get_sectors(struct burn_session *s); + +/** Gets the mode of a track + @param track the track to query + @return the track's mode +*/ +int burn_track_get_mode(struct burn_track *track); + +/** Returns whether the first track of a session is hidden in the pregap + @param session the session to query + @return non-zero means the first track is hidden +*/ +int burn_session_get_hidefirst(struct burn_session *session); + +/** Returns the library's version in its parts + @param major The major version number + @param minor The minor version number + @param micro The micro version number +*/ +void burn_version(int *major, int *minor, int *micro); + +#ifndef DOXYGEN + +BURN_END_DECLS + +#endif + +#endif /*LIBBURN_H*/