/*
 * Copyright (c) 1997-2006  Motoyuki Kasahara
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the project nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include "build-pre.h"
#include "eb.h"
#include "error.h"
#include "binary.h"
#include "font.h"
#include "build-post.h"

/*
 * Unexported functions.
 */
static EB_Error_Code eb_load_subbook(EB_Book *book);
static EB_Error_Code eb_load_subbook_indexes(EB_Book *book);
static EB_Error_Code eb_set_subbook_eb(EB_Book *book,
    EB_Subbook_Code subbook_code);
static EB_Error_Code eb_set_subbook_epwing(EB_Book *book,
    EB_Subbook_Code subbook_code);


/*
 * Initialize all subbooks in `book'.
 */
void
eb_initialize_subbooks(EB_Book *book)
{
    EB_Subbook *subbook;
    EB_Subbook *saved_subbook_current;
    int i;

    LOG(("in: eb_initialize_subbooks(book=%d)", (int)book->code));

    saved_subbook_current = book->subbook_current;

    for (i = 0, subbook = book->subbooks; i < book->subbook_count;
	 i++, subbook++) {
	book->subbook_current = subbook;

        subbook->initialized = 0;
	subbook->index_page = 1;
        subbook->code = i;
        zio_initialize(&subbook->text_zio);
        zio_initialize(&subbook->graphic_zio);
        zio_initialize(&subbook->sound_zio);
        zio_initialize(&subbook->movie_zio);
        subbook->title[0] = '\0';
        subbook->directory_name[0] = '\0';
        subbook->data_directory_name[0] = '\0';
        subbook->gaiji_directory_name[0] = '\0';
        subbook->movie_directory_name[0] = '\0';

        subbook->text_file_name[0] = '\0';
        subbook->graphic_file_name[0] = '\0';
        subbook->sound_file_name[0] = '\0';

	subbook->text_hint_zio_code = ZIO_PLAIN;
	subbook->graphic_hint_zio_code = ZIO_PLAIN;
	subbook->sound_hint_zio_code = ZIO_PLAIN;

	subbook->search_title_page = 0;
	eb_initialize_searches(book);
	subbook->multi_count = 0;

	eb_initialize_fonts(book);
	subbook->narrow_current = NULL;
	subbook->wide_current = NULL;
    }

    book->subbook_current = saved_subbook_current;

    LOG(("out: eb_initialize_subbooks()"));
}


/*
 * Finalize all subbooks in `book'.
 */
void
eb_finalize_subbooks(EB_Book *book)
{
    EB_Subbook *subbook;
    EB_Subbook *saved_subbook_current;
    int i;

    LOG(("in: eb_finalize_subbooks(book=%d)", (int)book->code));

    saved_subbook_current = book->subbook_current;

    for (i = 0, subbook = book->subbooks; i < book->subbook_count;
	 i++, subbook++) {
	book->subbook_current = subbook;

        zio_finalize(&subbook->text_zio);
        zio_finalize(&subbook->graphic_zio);
        zio_finalize(&subbook->sound_zio);
        zio_finalize(&subbook->movie_zio);

	eb_finalize_searches(book);
 	eb_finalize_fonts(book);

	subbook->narrow_current = NULL;
	subbook->wide_current = NULL;
    }

    book->subbook_current = saved_subbook_current;

    LOG(("out: eb_finalize_subbooks()"));
}


/*
 * Get information about the current subbook.
 */
static EB_Error_Code
eb_load_subbook(EB_Book *book)
{
    EB_Error_Code error_code;
    EB_Subbook *subbook;

    LOG(("in: eb_load_subbook(book=%d)", (int)book->code));

    subbook = book->subbook_current;

    /*
     * Current subbook must have been set.
     */
    if (subbook == NULL) {
	error_code = EB_ERR_NO_CUR_SUB;
	goto failed;
    }

    /*
     * Reset contexts.
     */
    eb_reset_search_contexts(book);
    eb_reset_text_context(book);
    eb_reset_binary_context(book);

    /*
     * If the subbook has already initialized, return immediately.
     */
    if (subbook->initialized)
	goto succeeded;

    if (0 <= zio_file(&subbook->text_zio)) {
	/*
	 * Read index information.
	 */
	error_code = eb_load_subbook_indexes(book);
	if (error_code != EB_SUCCESS)
	    goto failed;

	/*
	 * Read mutli search information.
	 */
	error_code = eb_load_multi_searches(book);
	if (error_code != EB_SUCCESS)
	    goto failed;
	error_code = eb_load_multi_titles(book);
	if (error_code != EB_SUCCESS)
	    goto failed;

	/*
	 * Rewind the file descriptor of the start file.
	 */
	if (zio_lseek(&subbook->text_zio,
	    (subbook->index_page - 1) * EB_SIZE_PAGE, SEEK_SET) < 0) {
	    error_code = EB_ERR_FAIL_SEEK_TEXT;
	    goto failed;
	}
    }

  succeeded:
    LOG(("out: eb_load_subbook() = %s", eb_error_string(EB_SUCCESS)));
    return EB_SUCCESS;

    /*
     * An error occurs...
     */
  failed:
    LOG(("out: eb_load_subbook() = %s", eb_error_string(error_code)));
    return error_code;
}


/*
 * Get information about all subbooks in the book.
 */
EB_Error_Code
eb_load_all_subbooks(EB_Book *book)
{
    EB_Error_Code error_code;
    EB_Subbook *subbook;
    int i;

    eb_lock(&book->lock);
    LOG(("in: eb_load_all_subbooks(book=%d)", (int)book->code));

    /*
     * The book must have been bound.
     */
    if (book->path == NULL) {
	error_code = EB_ERR_UNBOUND_BOOK;
	goto failed;
    }

    /*
     * Initialize each subbook.
     */
    for (i = 0, subbook = book->subbooks; i < book->subbook_count;
	 i++, subbook++) {
	error_code = eb_set_subbook(book, subbook->code);
	if (error_code != EB_SUCCESS)
	    goto failed;
    }
    eb_unset_subbook(book);

    LOG(("out: eb_load_all_subbooks() = %s", eb_error_string(EB_SUCCESS)));
    eb_unlock(&book->lock);

    return EB_SUCCESS;

    /*
     * An error occurs...
     */
  failed:
    eb_unset_subbook(book);
    LOG(("out: eb_load_all_subbooks() = %s", eb_error_string(error_code)));
    eb_unlock(&book->lock);
    return error_code;
}


/*
 * Get index information in the current subbook.
 *
 * If succeeds, the number of indexes is returned.
 * Otherwise, -1 is returned.
 */
static EB_Error_Code
eb_load_subbook_indexes(EB_Book *book)
{
    EB_Error_Code error_code;
    EB_Subbook *subbook;
    EB_Search search;
    char buffer[EB_SIZE_PAGE];
    char *buffer_p;
    int index_count;
    int availability;
    int global_availability;
    EB_Search sebxa_zip_index;
    EB_Search sebxa_zip_text;
    int i;

    LOG(("in: eb_load_subbook_indexes(book=%d)", (int)book->code));

    eb_initialize_search(&sebxa_zip_index);
    eb_initialize_search(&sebxa_zip_text);

    subbook = book->subbook_current;

    /*
     * Read the index table in the subbook.
     */
    if (zio_lseek(&subbook->text_zio, (subbook->index_page - 1) * EB_SIZE_PAGE,
	SEEK_SET) < 0) {
	error_code = EB_ERR_FAIL_SEEK_TEXT;
	goto failed;
    }
    if (zio_read(&subbook->text_zio, buffer, EB_SIZE_PAGE) != EB_SIZE_PAGE) {
	error_code = EB_ERR_FAIL_READ_TEXT;
	goto failed;
    }

    /*
     * Get start page numbers of the indexes in the subbook.
     */
    index_count = eb_uint1(buffer + 1);
    if (EB_SIZE_PAGE / 16 - 1 <= index_count) {
	error_code = EB_ERR_UNEXP_TEXT;
	goto failed;
    }

    /*
     * Get availavility flag of the index information.
     */
    global_availability = eb_uint1(buffer + 4);
    if (0x02 < global_availability)
	global_availability = 0;

    /*
     * Set each search method information.
     */
    for (i = 0, buffer_p = buffer + 16; i < index_count; i++, buffer_p += 16) {
	/*
	 * Set index style.
	 */
	eb_initialize_search(&search);

	search.index_id = eb_uint1(buffer_p);
	search.start_page = eb_uint4(buffer_p + 2);
	search.end_page   = search.start_page + eb_uint4(buffer_p + 6) - 1;

	/*
	 * Set canonicalization flags.
	 */
	availability = eb_uint1(buffer_p + 10);
	if ((global_availability == 0x00 && availability == 0x02)
	    || global_availability == 0x02) {
	    unsigned int flags;

	    flags = eb_uint3(buffer_p + 11);
	    search.katakana = (flags & 0xc00000) >> 22;
	    search.lower = (flags & 0x300000) >> 20;
	    if ((flags & 0x0c0000) >> 18 == 0)
		search.mark = EB_INDEX_STYLE_DELETE;
	    else
		search.mark = EB_INDEX_STYLE_ASIS;
	    search.long_vowel = (flags & 0x030000) >> 16;
	    search.double_consonant = (flags & 0x00c000) >> 14;
	    search.contracted_sound = (flags & 0x003000) >> 12;
	    search.small_vowel = (flags & 0x000c00) >> 10;
	    search.voiced_consonant = (flags & 0x000300) >> 8;
	    search.p_sound = (flags & 0x0000c0) >> 6;

	} else if (search.index_id == 0x70 || search.index_id == 0x90) {
	    search.katakana = EB_INDEX_STYLE_CONVERT;
	    search.lower = EB_INDEX_STYLE_CONVERT;
	    search.mark = EB_INDEX_STYLE_DELETE;
	    search.long_vowel = EB_INDEX_STYLE_CONVERT;
	    search.double_consonant = EB_INDEX_STYLE_CONVERT;
	    search.contracted_sound = EB_INDEX_STYLE_CONVERT;
	    search.small_vowel = EB_INDEX_STYLE_CONVERT;
	    search.voiced_consonant = EB_INDEX_STYLE_CONVERT;
	    search.p_sound = EB_INDEX_STYLE_CONVERT;

	} else {
	    search.katakana = EB_INDEX_STYLE_ASIS;
	    search.lower = EB_INDEX_STYLE_CONVERT;
	    search.mark = EB_INDEX_STYLE_ASIS;
	    search.long_vowel = EB_INDEX_STYLE_ASIS;
	    search.double_consonant = EB_INDEX_STYLE_ASIS;
	    search.contracted_sound = EB_INDEX_STYLE_ASIS;
	    search.small_vowel = EB_INDEX_STYLE_ASIS;
	    search.voiced_consonant = EB_INDEX_STYLE_ASIS;
	    search.p_sound = EB_INDEX_STYLE_ASIS;
	}

	if (book->character_code == EB_CHARCODE_ISO8859_1
	    || search.index_id == 0x72
	    || search.index_id == 0x92) {
	    search.space = EB_INDEX_STYLE_ASIS;
	} else {
            search.space = EB_INDEX_STYLE_DELETE;
	}

	/*
	 * Identify search method.
	 */
	switch (search.index_id) {
	case 0x00:
	    memcpy(&subbook->text, &search, sizeof(EB_Search));
	    break;
	case 0x01:
	    memcpy(&subbook->menu, &search, sizeof(EB_Search));
	    break;
	case 0x02:
	    memcpy(&subbook->copyright, &search, sizeof(EB_Search));
	    break;
	case 0x10:
	    memcpy(&subbook->image_menu, &search, sizeof(EB_Search));
	    break;
	case 0x16:
	    if (book->disc_code == EB_DISC_EPWING)
		subbook->search_title_page = search.start_page;
	    break;
	case 0x21:
	    if (book->disc_code == EB_DISC_EB
		&& zio_mode(&subbook->text_zio) == ZIO_PLAIN)
		memcpy(&sebxa_zip_text, &search, sizeof(EB_Search));
	    break;
	case 0x22:
	    if (book->disc_code == EB_DISC_EB
		&& zio_mode(&subbook->text_zio) == ZIO_PLAIN)
		memcpy(&sebxa_zip_index, &search, sizeof(EB_Search));
	    break;
	case 0x70:
	    memcpy(&subbook->endword_kana, &search, sizeof(EB_Search));
	    break;
	case 0x71:
	    memcpy(&subbook->endword_asis, &search, sizeof(EB_Search));
	    break;
	case 0x72:
	    memcpy(&subbook->endword_alphabet, &search, sizeof(EB_Search));
	    break;
	case 0x80:
	    memcpy(&subbook->keyword, &search, sizeof(EB_Search));
	    break;
	case 0x81:
	    memcpy(&subbook->cross, &search, sizeof(EB_Search));
	    break;
	case 0x90:
	    memcpy(&subbook->word_kana, &search, sizeof(EB_Search));
	    break;
	case 0x91:
	    memcpy(&subbook->word_asis, &search, sizeof(EB_Search));
	    break;
	case 0x92:
	    memcpy(&subbook->word_alphabet, &search, sizeof(EB_Search));
	    break;
	case 0xd8:
	    memcpy(&subbook->sound, &search, sizeof(EB_Search));
	    break;
	case 0xf1:
	    if (book->disc_code == EB_DISC_EB) {
		subbook->wide_fonts[EB_FONT_16].page = search.start_page;
		subbook->wide_fonts[EB_FONT_16].font_code = EB_FONT_16;
	    }
	    break;
	case 0xf2:
	    if (book->disc_code == EB_DISC_EB) {
		subbook->narrow_fonts[EB_FONT_16].page = search.start_page;
		subbook->narrow_fonts[EB_FONT_16].font_code = EB_FONT_16;
	    }
	    break;
	case 0xf3:
	    if (book->disc_code == EB_DISC_EB) {
		subbook->wide_fonts[EB_FONT_24].page = search.start_page;
		subbook->wide_fonts[EB_FONT_24].font_code = EB_FONT_24;
	    }
	    break;
	case 0xf4:
	    if (book->disc_code == EB_DISC_EB) {
		subbook->narrow_fonts[EB_FONT_24].page = search.start_page;
		subbook->narrow_fonts[EB_FONT_24].font_code = EB_FONT_24;
	    }
	    break;
	case 0xf5:
	    if (book->disc_code == EB_DISC_EB) {
		subbook->wide_fonts[EB_FONT_30].page = search.start_page;
		subbook->wide_fonts[EB_FONT_30].font_code = EB_FONT_30;
	    }
	    break;
	case 0xf6:
	    if (book->disc_code == EB_DISC_EB) {
		subbook->narrow_fonts[EB_FONT_30].page = search.start_page;
		subbook->narrow_fonts[EB_FONT_30].font_code = EB_FONT_30;
	    }
	    break;
	case 0xf7:
	    if (book->disc_code == EB_DISC_EB) {
		subbook->wide_fonts[EB_FONT_48].page = search.start_page;
		subbook->wide_fonts[EB_FONT_48].font_code = EB_FONT_48;
	    }
	    break;
	case 0xf8:
	    if (book->disc_code == EB_DISC_EB) {
		subbook->narrow_fonts[EB_FONT_48].page = search.start_page;
		subbook->narrow_fonts[EB_FONT_48].font_code = EB_FONT_48;
	    }
	    break;
	case 0xff:
	    if (subbook->multi_count < EB_MAX_MULTI_SEARCHES) {
		memcpy(&subbook->multis[subbook->multi_count].search, &search,
		    sizeof(EB_Search));
		subbook->multi_count++;
	    }
	    break;
	}

	eb_finalize_search(&sebxa_zip_text);
    }

    /*
     * Set S-EBXA compression flag.
     */
    if (book->disc_code == EB_DISC_EB
	&& zio_mode(&subbook->text_zio) == ZIO_PLAIN
	&& subbook->text.start_page != 0
	&& sebxa_zip_index.start_page != 0
	&& sebxa_zip_text.start_page != 0) {
	zio_set_sebxa_mode(&subbook->text_zio,
	    (sebxa_zip_index.start_page - 1) * EB_SIZE_PAGE,
	    (sebxa_zip_text.start_page - 1) * EB_SIZE_PAGE,
	    (subbook->text.start_page - 1) * EB_SIZE_PAGE,
	    subbook->text.end_page * EB_SIZE_PAGE - 1);
    }

    eb_finalize_search(&sebxa_zip_index);
    eb_finalize_search(&sebxa_zip_text);

    LOG(("out: eb_load_subbook_indexes() = %s", eb_error_string(EB_SUCCESS)));
    return EB_SUCCESS;

    /*
     * An error occurs...
     */
  failed:
    eb_finalize_search(&sebxa_zip_index);
    eb_finalize_search(&sebxa_zip_text);
    LOG(("out: eb_load_subbook_indexes() = %s", eb_error_string(error_code)));
    return error_code;
}


/*
 * Make a subbook list in the book.
 */
EB_Error_Code
eb_subbook_list(EB_Book *book, EB_Subbook_Code *subbook_list,
    int *subbook_count)
{
    EB_Error_Code error_code;
    EB_Subbook_Code *list_p;
    int i;

    eb_lock(&book->lock);
    LOG(("in: eb_subbook_list(book=%d)", (int)book->code));

    /*
     * The book must have been bound.
     */
    if (book->path == NULL) {
	error_code = EB_ERR_UNBOUND_BOOK;
	goto failed;
    }

    for (i = 0, list_p = subbook_list; i < book->subbook_count; i++, list_p++)
	*list_p = i;
    *subbook_count = book->subbook_count;

    LOG(("out: eb_subbook_list(subbook_count=%d) = %s", *subbook_count,
	eb_error_string(EB_SUCCESS)));
    eb_unlock(&book->lock);

    return EB_SUCCESS;

    /*
     * An error occurs...
     */
  failed:
    *subbook_count = 0;
    LOG(("out: eb_subbook_list() = %s", eb_error_string(error_code)));
    eb_unlock(&book->lock);
    return error_code;
}


/*
 * Return a subbook-code of the current subbook.
 */
EB_Error_Code
eb_subbook(EB_Book *book, EB_Subbook_Code *subbook_code)
{
    EB_Error_Code error_code;

    eb_lock(&book->lock);
    LOG(("in: eb_subbook(book=%d)", (int)book->code));

    /*
     * The current subbook must have been set.
     */
    if (book->subbook_current == NULL) {
	error_code = EB_ERR_NO_CUR_SUB;
	goto failed;
    }

    *subbook_code = book->subbook_current->code;

    LOG(("out: eb_subbook(subbook_code=%d) = %s", *subbook_code,
	eb_error_string(EB_SUCCESS)));
    eb_unlock(&book->lock);

    return EB_SUCCESS;

    /*
     * An error occurs...
     */
  failed:
    *subbook_code = EB_SUBBOOK_INVALID;
    LOG(("out: eb_subbook() = %s", eb_error_string(error_code)));
    eb_unlock(&book->lock);
    return error_code;
}


/*
 * Return a title of the current subbook.
 */
EB_Error_Code
eb_subbook_title(EB_Book *book, char *title)
{
    EB_Error_Code error_code;

    eb_lock(&book->lock);
    LOG(("in: eb_subbook_title(book=%d)", (int)book->code));

    /*
     * The current subbook must have been set.
     */
    if (book->subbook_current == NULL) {
	error_code = EB_ERR_NO_CUR_SUB;
	goto failed;
    }

    strcpy(title, book->subbook_current->title);

    LOG(("out: eb_subbook_title(title=%s) = %s", title,
	eb_error_string(EB_SUCCESS)));
    eb_unlock(&book->lock);

    return EB_SUCCESS;

    /*
     * An error occurs...
     */
  failed:
    *title = '\0';
    LOG(("out: eb_subbook_title() = %s", eb_error_string(error_code)));
    eb_unlock(&book->lock);
    return error_code;
}


/*
 * Return a title of the specified subbook `subbook_code'.
 */
EB_Error_Code
eb_subbook_title2(EB_Book *book, EB_Subbook_Code subbook_code, char *title)
{
    EB_Error_Code error_code;

    eb_lock(&book->lock);
    LOG(("in: eb_subbook_title2(book=%d, subbook_code=%d)",
	(int)book->code, (int)subbook_code));

    /*
     * The book must have been bound.
     */
    if (book->path == NULL) {
	error_code = EB_ERR_UNBOUND_BOOK;
	goto failed;
    }

    /*
     * Check for the subbook-code.
     */
    if (subbook_code < 0 || book->subbook_count <= subbook_code) {
	error_code = EB_ERR_NO_SUCH_SUB;
	goto failed;
    }

    strcpy(title, (book->subbooks + subbook_code)->title);

    LOG(("out: eb_subbook_title2(title=%s) = %s", title,
	eb_error_string(EB_SUCCESS)));
    eb_unlock(&book->lock);

    return EB_SUCCESS;

    /*
     * An error occurs...
     */
  failed:
    *title = '\0';
    LOG(("out: eb_subbook_title2() = %s", eb_error_string(error_code)));
    eb_unlock(&book->lock);
    return error_code;
}


/*
 * Return a directory name of the current subbook.
 */
EB_Error_Code
eb_subbook_directory(EB_Book *book, char *directory)
{
    EB_Error_Code error_code;
    char *p;

    eb_lock(&book->lock);
    LOG(("in: eb_subbook_directory(book=%d)", (int)book->code));

    /*
     * Current subbook must have been set.
     */
    if (book->subbook_current == NULL) {
	error_code = EB_ERR_NO_CUR_SUB;
	goto failed;
    }

    /*
     * Copy directory name.
     * Upper letters are converted to lower letters.
     */
    strcpy(directory, book->subbook_current->directory_name);
    for (p = directory; *p != '\0'; p++) {
	if ('A' <= *p && *p <= 'Z')
	    *p = ASCII_TOLOWER(*p);
    }

    LOG(("out: eb_subbook_directory(directory=%s) = %s", directory,
	eb_error_string(EB_SUCCESS)));
    eb_unlock(&book->lock);

    return EB_SUCCESS;

    /*
     * An error occurs...
     */
  failed:
    *directory = '\0';
    LOG(("out: eb_subbook_directory() = %s", eb_error_string(error_code)));
    eb_unlock(&book->lock);
    return error_code;
}


/*
 * Return a directory name of the specified subbook `subbook_code'.
 */
EB_Error_Code
eb_subbook_directory2(EB_Book *book, EB_Subbook_Code subbook_code,
    char *directory)
{
    EB_Error_Code error_code;
    char *p;

    eb_lock(&book->lock);
    LOG(("in: eb_subbook_directory2(book=%d, subbook_code=%d)",
	(int)book->code, (int)subbook_code));

    /*
     * The book must have been bound.
     */
    if (book->path == NULL) {
	error_code = EB_ERR_UNBOUND_BOOK;
	goto failed;
    }

    /*
     * Check for the subbook-code.
     */
    if (subbook_code < 0 || book->subbook_count <= subbook_code) {
	error_code = EB_ERR_NO_SUCH_SUB;
	goto failed;
    }

    /*
     * Copy directory name.
     * Upper letters are converted to lower letters.
     */
    strcpy(directory, (book->subbooks + subbook_code)->directory_name);
    for (p = directory; *p != '\0'; p++) {
	if ('A' <= *p && *p <= 'Z')
	    *p = ASCII_TOLOWER(*p);
    }

    LOG(("out: eb_subbook_directory2(directory=%s) = %s", directory,
	eb_error_string(EB_SUCCESS)));
    eb_unlock(&book->lock);

    return EB_SUCCESS;

    /*
     * An error occurs...
     */
  failed:
    *directory = '\0';
    LOG(("out: eb_subbook_directory2() = %s", eb_error_string(error_code)));
    eb_unlock(&book->lock);
    return error_code;
}


/*
 * Set the subbook `subbook_code' as the current subbook.
 */
EB_Error_Code
eb_set_subbook(EB_Book *book, EB_Subbook_Code subbook_code)
{
    EB_Error_Code error_code = EB_SUCCESS;

    eb_lock(&book->lock);
    LOG(("in: eb_set_subbook(book=%d, subbook_code=%d)",
	(int)book->code, (int)subbook_code));

    /*
     * The book must have been bound.
     */
    if (book->path == NULL) {
	error_code = EB_ERR_UNBOUND_BOOK;
	goto failed;
    }

    /*
     * Check for the subbook-code.
     */
    if (subbook_code < 0 || book->subbook_count <= subbook_code) {
	error_code = EB_ERR_NO_SUCH_SUB;
	goto failed;
    }

    /*
     * If the subbook has already been set as the current subbook,
     * there is nothing to be done.
     * Otherwise close the previous subbook.
     */
    if (book->subbook_current != NULL) {
	if (book->subbook_current->code == subbook_code)
	    goto succeeded;
	eb_unset_subbook(book);
    }

    /*
     * Set the current subbook.
     */
    book->subbook_current = book->subbooks + subbook_code;

    /*
     * Dispatch.
     */
    if (book->disc_code == EB_DISC_EB)
	error_code = eb_set_subbook_eb(book, subbook_code);
    else
	error_code = eb_set_subbook_epwing(book, subbook_code);

    /*
     * Load the subbook.
     */
    error_code = eb_load_subbook(book);
    if (error_code != EB_SUCCESS)
	goto failed;

    /*
     * Load font files.
     */
    eb_load_font_headers(book);

  succeeded:
    book->subbook_current->initialized = 1;
    LOG(("out: eb_set_subbook() = %s", eb_error_string(EB_SUCCESS)));
    eb_unlock(&book->lock);

    return error_code;

    /*
     * An error occurs...
     */
  failed:
    if (book->subbook_current != NULL) {
	zio_close(&book->subbook_current->text_zio);
	zio_close(&book->subbook_current->graphic_zio);
	zio_close(&book->subbook_current->sound_zio);
	zio_close(&book->subbook_current->movie_zio);
    }
    book->subbook_current = NULL;
    LOG(("out: eb_set_subbook() = %s", eb_error_string(error_code)));
    eb_unlock(&book->lock);
    return error_code;
}


/*
 * Set the subbook `subbook_code' as the current subbook.
 */
static EB_Error_Code
eb_set_subbook_eb(EB_Book *book, EB_Subbook_Code subbook_code)
{
    EB_Error_Code error_code;
    EB_Subbook *subbook;
    char text_path_name[EB_MAX_PATH_LENGTH + 1];
    char graphic_path_name[EB_MAX_PATH_LENGTH + 1];
    Zio_Code text_zio_code;
    Zio_Code graphic_zio_code;

    LOG(("in: eb_set_subbook_eb(book=%d, subbook_code=%d)",
	(int)book->code, (int)subbook_code));

    subbook = book->subbook_current;

    /*
     * Open a text file if exists.
     */
    text_zio_code = ZIO_INVALID;

    if (subbook->initialized) {
	if (zio_mode(&subbook->text_zio) != ZIO_INVALID)
	    text_zio_code = ZIO_REOPEN;
    } else {
	eb_canonicalize_file_name(subbook->text_file_name);
	if (eb_find_file_name2(book->path, subbook->directory_name,
	    EB_FILE_NAME_START, subbook->text_file_name) == EB_SUCCESS) {
	    eb_path_name_zio_code(subbook->text_file_name, ZIO_PLAIN,
		&text_zio_code);
	}
    }

    if (text_zio_code != ZIO_INVALID) {
	eb_compose_path_name2(book->path, subbook->directory_name,
	    subbook->text_file_name, text_path_name);
	if (zio_open(&subbook->text_zio, text_path_name, text_zio_code) < 0) {
	    error_code = EB_ERR_FAIL_OPEN_TEXT;
	    goto failed;
	}
	text_zio_code = zio_mode(&subbook->text_zio);
    }

    /*
     * Open a graphic file if exists.
     */
    graphic_zio_code = ZIO_INVALID;

    if (subbook->initialized) {
	if (zio_mode(&subbook->graphic_zio) != ZIO_INVALID)
	    graphic_zio_code = ZIO_REOPEN;
    } else if (text_zio_code != ZIO_INVALID) {
	strcpy(subbook->graphic_file_name, subbook->text_file_name);
	graphic_zio_code = text_zio_code;
    }

    if (graphic_zio_code != ZIO_INVALID) {
	eb_compose_path_name2(book->path, subbook->directory_name,
	    subbook->graphic_file_name, graphic_path_name);
	if (zio_open(&subbook->graphic_zio, graphic_path_name,
	    graphic_zio_code) < 0) {
	    error_code = EB_ERR_FAIL_OPEN_BINARY;
	    goto failed;
	}
	graphic_zio_code = zio_mode(&subbook->graphic_zio);
    }

    LOG(("out: eb_set_subbook_eb() = %s", eb_error_string(EB_SUCCESS)));
    return EB_SUCCESS;

    /*
     * An error occurs...
     */
  failed:
    LOG(("out: eb_set_subbook_eb() = %s", eb_error_string(error_code)));
    return error_code;
}

/*
 * Set the subbook `subbook_code' as the current subbook.
 */
static EB_Error_Code
eb_set_subbook_epwing(EB_Book *book, EB_Subbook_Code subbook_code)
{
    EB_Error_Code error_code;
    EB_Subbook *subbook;
    char text_path_name[EB_MAX_PATH_LENGTH + 1];
    char graphic_path_name[EB_MAX_PATH_LENGTH + 1];
    char sound_path_name[EB_MAX_PATH_LENGTH + 1];
    Zio_Code text_zio_code;
    Zio_Code graphic_zio_code;
    Zio_Code sound_zio_code;

    LOG(("in: eb_set_subbook_epwing(book=%d, subbook_code=%d)",
	(int)book->code, (int)subbook_code));

    subbook = book->subbook_current;

    if (!subbook->initialized) {
	/*
	 * Adjust directory names.
	 */
	strcpy(subbook->data_directory_name, EB_DIRECTORY_NAME_DATA);
	eb_fix_directory_name2(book->path, subbook->directory_name,
	    subbook->data_directory_name);

	strcpy(subbook->gaiji_directory_name, EB_DIRECTORY_NAME_GAIJI);
	eb_fix_directory_name2(book->path, subbook->directory_name,
	    subbook->gaiji_directory_name);

	strcpy(subbook->movie_directory_name, EB_DIRECTORY_NAME_MOVIE);
	eb_fix_directory_name2(book->path, subbook->directory_name,
	    subbook->movie_directory_name);
    }

    /*
     * Open a text file if exists.
     *
     * If a subbook has stream data only, its index_page has been set
     * to 0.  In this case, we must not try to open a text file of
     * the subbook, since the text file may be for another subbook.
     * Remember that subbooks can share a `data' sub-directory.
     */
    text_zio_code = ZIO_INVALID;

    if (subbook->initialized) {
	if (zio_mode(&subbook->text_zio) != ZIO_INVALID)
	    text_zio_code = ZIO_REOPEN;
    } else if (subbook->index_page > 0) {
	eb_canonicalize_file_name(subbook->text_file_name);
	if (eb_find_file_name3(book->path, subbook->directory_name,
	    subbook->data_directory_name, subbook->text_file_name,
	    subbook->text_file_name) == EB_SUCCESS) {
	    eb_path_name_zio_code(subbook->text_file_name,
		subbook->text_hint_zio_code, &text_zio_code);
	}
    }

    if (text_zio_code != ZIO_INVALID) {
	eb_compose_path_name3(book->path, subbook->directory_name,
	    subbook->data_directory_name, subbook->text_file_name,
	    text_path_name);
	if (zio_open(&subbook->text_zio, text_path_name, text_zio_code) < 0) {
	    subbook = NULL;
	    error_code = EB_ERR_FAIL_OPEN_TEXT;
	    goto failed;
	}
	text_zio_code = zio_mode(&subbook->text_zio);
    }

    /*
     * Open a graphic file if exists.
     *
     */
    graphic_zio_code = ZIO_INVALID;

    if (subbook->initialized) {
	if (zio_mode(&subbook->graphic_zio) != ZIO_INVALID)
	    graphic_zio_code = ZIO_REOPEN;
    } else if (text_zio_code != ZIO_INVALID) {
	eb_canonicalize_file_name(subbook->graphic_file_name);
	if (eb_find_file_name3(book->path, subbook->directory_name,
	    subbook->data_directory_name, subbook->graphic_file_name,
	    subbook->graphic_file_name) == EB_SUCCESS) {
	    eb_path_name_zio_code(subbook->graphic_file_name,
		subbook->graphic_hint_zio_code, &graphic_zio_code);
	}
    }

    if (graphic_zio_code != ZIO_INVALID) {
	eb_compose_path_name3(book->path, subbook->directory_name,
	    subbook->data_directory_name, subbook->graphic_file_name,
	    graphic_path_name);
	if (zio_open(&subbook->graphic_zio, graphic_path_name,
	    graphic_zio_code) < 0) {
	    error_code = EB_ERR_FAIL_OPEN_BINARY;
	    goto failed;
	}
	graphic_zio_code = zio_mode(&subbook->graphic_zio);
    }

    /*
     * Open a sound file if exists.
     */
    sound_zio_code = ZIO_INVALID;

    if (subbook->initialized) {
	if (zio_mode(&subbook->sound_zio) != ZIO_INVALID)
	    sound_zio_code = ZIO_REOPEN;
    } else if (text_zio_code != ZIO_INVALID) {
	eb_canonicalize_file_name(subbook->sound_file_name);
	if (eb_find_file_name3(book->path, subbook->directory_name,
	    subbook->data_directory_name, subbook->sound_file_name,
	    subbook->sound_file_name) == EB_SUCCESS) {
	    eb_path_name_zio_code(subbook->sound_file_name,
		subbook->sound_hint_zio_code, &sound_zio_code);
	}
    }

    if (sound_zio_code != ZIO_INVALID) {
	eb_compose_path_name3(book->path, subbook->directory_name,
	    subbook->data_directory_name, subbook->sound_file_name,
	    sound_path_name);
	if (zio_open(&subbook->sound_zio, sound_path_name,
	    sound_zio_code) < 0) {
	    error_code = EB_ERR_FAIL_OPEN_BINARY;
	    goto failed;
	}
	sound_zio_code = zio_mode(&subbook->sound_zio);
    }

    LOG(("out: eb_set_subbook_epwing() = %s", eb_error_string(EB_SUCCESS)));
    return EB_SUCCESS;

    /*
     * An error occurs...
     */
  failed:
    LOG(("out: eb_set_subbook_epwing() = %s", eb_error_string(error_code)));
    return error_code;
}


/*
 * Unset the current subbook.
 */
void
eb_unset_subbook(EB_Book *book)
{
    eb_lock(&book->lock);
    LOG(("in: eb_unset_subbook(book=%d)", (int)book->code));

    /*
     * Close the file of the current subbook.
     */
    if (book->subbook_current != NULL) {
	eb_unset_font(book);
	eb_unset_binary(book);
	zio_close(&book->subbook_current->text_zio);
	zio_close(&book->subbook_current->graphic_zio);
	zio_close(&book->subbook_current->sound_zio);
	zio_close(&book->subbook_current->movie_zio);
	book->subbook_current = NULL;
    }

    LOG(("out: eb_unset_subbook()"));
    eb_unlock(&book->lock);
}


