Second pinch of libisofs rewrite
This commit is contained in:
parent
48d76e844e
commit
e943c8c648
312
libisofs/ecma119_tree.c
Normal file
312
libisofs/ecma119_tree.c
Normal file
@ -0,0 +1,312 @@
|
||||
/* vim: set noet ts=8 sts=8 sw=8 : */
|
||||
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
#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);
|
||||
}
|
95
libisofs/ecma119_tree.h
Normal file
95
libisofs/ecma119_tree.h
Normal file
@ -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 */
|
353
libisofs/joliet.c
Normal file
353
libisofs/joliet.c
Normal file
@ -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 <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
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);
|
||||
}
|
||||
|
77
libisofs/joliet.h
Normal file
77
libisofs/joliet.h
Normal file
@ -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 <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
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 */
|
928
libisofs/libburn.h
Normal file
928
libisofs/libburn.h
Normal file
@ -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 <sys/types.h>
|
||||
|
||||
#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 <i>before</i> 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);
|
||||
|
||||