/*
* This file is part of libbluray
* Copyright (C) 2010 Petri Hintukainen <phintuka@users.sourceforge.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#if HAVE_CONFIG_H
#include "config.h"
#endif
#include "ig_decode.h"
#include "pg_decode.h" // pg_decode_*()
#include "../hdmv/mobj_parse.h" // mobj_parse_cmd()
#include "../hdmv/mobj_data.h" // MOBJ_CMD
#include "../bdnav/uo_mask.h" // uo_mask_parse()
#include "util/macro.h"
#include "util/logging.h"
#include "util/bits.h"
#include <string.h>
#include <stdlib.h>
static int _decode_button(BITBUFFER *bb, BD_IG_BUTTON *p)
{
unsigned ii;
p->id = bb_read(bb, 16);
p->numeric_select_value = bb_read(bb, 16);
p->auto_action_flag = bb_read(bb, 1);
bb_skip(bb, 7);
p->x_pos = bb_read(bb, 16);
p->y_pos = bb_read(bb, 16);
p->upper_button_id_ref = bb_read(bb, 16);
p->lower_button_id_ref = bb_read(bb, 16);
p->left_button_id_ref = bb_read(bb, 16);
p->right_button_id_ref = bb_read(bb, 16);
p->normal_start_object_id_ref = bb_read(bb, 16);
p->normal_end_object_id_ref = bb_read(bb, 16);
p->normal_repeat_flag = bb_read(bb, 1);
bb_skip(bb, 7);
p->selected_sound_id_ref = bb_read(bb, 8);
p->selected_start_object_id_ref = bb_read(bb, 16);
p->selected_end_object_id_ref = bb_read(bb, 16);
p->selected_repeat_flag = bb_read(bb, 1);
bb_skip(bb, 7);
p->activated_sound_id_ref = bb_read(bb, 8);
p->activated_start_object_id_ref = bb_read(bb, 16);
p->activated_end_object_id_ref = bb_read(bb, 16);
p->num_nav_cmds = bb_read(bb, 16);
p->nav_cmds = calloc(p->num_nav_cmds, sizeof(MOBJ_CMD));
if (!p->nav_cmds) {
BD_DEBUG(DBG_DECODE | DBG_CRIT, "out of memory\n");
return 0;
}
for (ii = 0; ii < p->num_nav_cmds; ii++) {
uint8_t buf[12];
bb_read_bytes(bb, buf, 12);
mobj_parse_cmd(buf, &p->nav_cmds[ii]);
}
return 1;
}
static void _clean_button(BD_IG_BUTTON *p)
{
X_FREE(p->nav_cmds);
}
static int _decode_bog(BITBUFFER *bb, BD_IG_BOG *p)
{
unsigned ii;
p->default_valid_button_id_ref = bb_read(bb, 16);
p->num_buttons = bb_read(bb, 8);
p->button = calloc(p->num_buttons, sizeof(BD_IG_BUTTON));
if (!p->button) {
BD_DEBUG(DBG_DECODE | DBG_CRIT, "out of memory\n");
return 0;
}
for (ii = 0; ii < p->num_buttons; ii++) {
if (!_decode_button(bb, &p->button[ii])) {
return 0;
}
}
return 1;
}
static void _clean_bog(BD_IG_BOG *p)
{
unsigned ii;
if (p->button) {
for (ii = 0; ii < p->num_buttons; ii++) {
_clean_button(&p->button[ii]);
}
}
X_FREE(p->button);
}
static int _decode_effect(BITBUFFER *bb, BD_IG_EFFECT *p)
{
unsigned ii;
p->duration = bb_read(bb, 24);
p->palette_id_ref = bb_read(bb, 8);
p->num_composition_objects = bb_read(bb, 8);
p->composition_object = calloc(p->num_composition_objects, sizeof(BD_PG_COMPOSITION_OBJECT));
if (!p->composition_object) {
BD_DEBUG(DBG_DECODE | DBG_CRIT, "out of memory\n");
return 0;
}
for (ii = 0; ii < p->num_composition_objects; ii++) {
pg_decode_composition_object(bb, &p->composition_object[ii]);
}
return 1;
}
static void _clean_effect(BD_IG_EFFECT *p)
{
X_FREE(p->composition_object);
}
static int _decode_effect_sequence(BITBUFFER *bb, BD_IG_EFFECT_SEQUENCE *p)
{
unsigned ii;
p->num_windows = bb_read(bb, 8);
p->window = calloc(p->num_windows, sizeof(BD_PG_WINDOW));
if (!p->window) {
BD_DEBUG(DBG_DECODE | DBG_CRIT, "out of memory\n");
return 0;
}
for (ii = 0; ii < p->num_windows; ii++) {
pg_decode_window(bb, &p->window[ii]);
}
p->num_effects = bb_read(bb, 8);
p->effect = calloc(p->num_effects, sizeof(BD_IG_EFFECT));
if (!p->effect) {
BD_DEBUG(DBG_DECODE | DBG_CRIT, "out of memory\n");
return 0;
}
for (ii = 0; ii < p->num_effects; ii++) {
if (!_decode_effect(bb, &p->effect[ii])) {
return 0;
}
}
return 1;
}
static void _clean_effect_sequence(BD_IG_EFFECT_SEQUENCE *p)
{
unsigned ii;
if (p->effect) {
for (ii = 0; ii < p->num_effects; ii++) {
_clean_effect(&p->effect[ii]);
}
}
X_FREE(p->effect);
X_FREE(p->window);
}
static int _decode_uo_mask_table(BITBUFFER *bb, BD_UO_MASK *p)
{
uint8_t buf[8];
bb_read_bytes(bb, buf, 8);
return uo_mask_parse(buf, p);
}
static int _decode_page(BITBUFFER *bb, BD_IG_PAGE *p)
{
unsigned ii;
p->id = bb_read(bb, 8);
p->version = bb_read(bb, 8);
_decode_uo_mask_table(bb, &p->uo_mask_table);
if (!_decode_effect_sequence(bb, &p->in_effects)) {
return 0;
}
if (!_decode_effect_sequence(bb, &p->out_effects)) {
return 0;
}
p->animation_frame_rate_code = bb_read(bb, 8);
p->default_selected_button_id_ref = bb_read(bb, 16);
p->default_activated_button_id_ref = bb_read(bb, 16);
p->palette_id_ref = bb_read(bb, 8);
p->num_bogs = bb_read(bb, 8);
p->bog = calloc(p->num_bogs, sizeof(BD_IG_BOG));
if (!p->bog) {
BD_DEBUG(DBG_DECODE | DBG_CRIT, "out of memory\n");
return 0;
}
for (ii = 0; ii < p->num_bogs; ii++) {
if (!_decode_bog(bb, &p->bog[ii])) {
return 0;
}
}
return 1;
}
static void _clean_page(BD_IG_PAGE *p)
{
unsigned ii;
_clean_effect_sequence(&p->in_effects);
_clean_effect_sequence(&p->out_effects);
if (p->bog) {
for (ii = 0; ii < p->num_bogs; ii++) {
_clean_bog(&p->bog[ii]);
}
}
X_FREE(p->bog);
}
static uint64_t bb_read_u64(BITBUFFER *bb, int i_count)
{
uint64_t result = 0;
if (i_count > 32) {
i_count -= 32;
result = (uint64_t)bb_read(bb, 32) << i_count;
}
result |= bb_read(bb, i_count);
return result;
}
static int _decode_interactive_composition(BITBUFFER *bb, BD_IG_INTERACTIVE_COMPOSITION *p)
{
unsigned ii;
uint32_t data_len = bb_read(bb, 24);
uint32_t buf_len = bb->p_end - bb->p;
if (data_len != buf_len) {
BD_DEBUG(DBG_DECODE, "ig_decode_interactive(): buffer size mismatch (expected %d, have %d)\n", data_len, buf_len);
return 0;
}
p->stream_model = bb_read(bb, 1);
p->ui_model = bb_read(bb, 1);
bb_skip(bb, 6);
if (p->stream_model == 0) {
bb_skip(bb, 7);
p->composition_timeout_pts = bb_read_u64(bb, 33);
bb_skip(bb, 7);
p->selection_timeout_pts = bb_read_u64(bb, 33);
}
p->user_timeout_duration = bb_read(bb, 24);
p->num_pages = bb_read(bb, 8);
p->page = calloc(p->num_pages, sizeof(BD_IG_PAGE));
if (!p->page) {
BD_DEBUG(DBG_DECODE | DBG_CRIT, "out of memory\n");
return 0;
}
for (ii = 0; ii < p->num_pages; ii++) {
if (!_decode_page(bb, &p->page[ii])) {
return 0;
}
}
return 1;
}
static void _clean_interactive_composition(BD_IG_INTERACTIVE_COMPOSITION *p)
{
unsigned ii;
if (p->page) {
for (ii = 0; ii < p->num_pages; ii++) {
_clean_page(&p->page[ii]);
}
}
X_FREE(p->page);
}
/*
* segment
*/
int ig_decode_interactive(BITBUFFER *bb, BD_IG_INTERACTIVE *p)
{
BD_PG_SEQUENCE_DESCRIPTOR sd;
pg_decode_video_descriptor(bb, &p->video_descriptor);
pg_decode_composition_descriptor(bb, &p->composition_descriptor);
pg_decode_sequence_descriptor(bb, &sd);
if (!sd.first_in_seq) {
BD_DEBUG(DBG_DECODE, "ig_decode_interactive(): not first in seq\n");
return 0;
}
if (!sd.last_in_seq) {
BD_DEBUG(DBG_DECODE, "ig_decode_interactive(): not last in seq\n");
return 0;
}
if (!bb_is_align(bb, 0x07)) {
BD_DEBUG(DBG_DECODE, "ig_decode_interactive(): alignment error\n");
return 0;
}
return _decode_interactive_composition(bb, &p->interactive_composition);
}
void ig_free_interactive(BD_IG_INTERACTIVE **p)
{
if (p && *p) {
_clean_interactive_composition(&(*p)->interactive_composition);
X_FREE(*p);
}
}