|
Packit |
5e46da |
/*
|
|
Packit |
5e46da |
* This file is part of libbluray
|
|
Packit |
5e46da |
* Copyright (C) 2010-2017 Petri Hintukainen <phintuka@users.sourceforge.net>
|
|
Packit |
5e46da |
*
|
|
Packit |
5e46da |
* This library is free software; you can redistribute it and/or
|
|
Packit |
5e46da |
* modify it under the terms of the GNU Lesser General Public
|
|
Packit |
5e46da |
* License as published by the Free Software Foundation; either
|
|
Packit |
5e46da |
* version 2.1 of the License, or (at your option) any later version.
|
|
Packit |
5e46da |
*
|
|
Packit |
5e46da |
* This library is distributed in the hope that it will be useful,
|
|
Packit |
5e46da |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
5e46da |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
5e46da |
* Lesser General Public License for more details.
|
|
Packit |
5e46da |
*
|
|
Packit |
5e46da |
* You should have received a copy of the GNU Lesser General Public
|
|
Packit |
5e46da |
* License along with this library. If not, see
|
|
Packit |
5e46da |
* <http://www.gnu.org/licenses/>.
|
|
Packit |
5e46da |
*/
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
#if HAVE_CONFIG_H
|
|
Packit |
5e46da |
#include "config.h"
|
|
Packit |
5e46da |
#endif
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
#include "mobj_parse.h"
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
#include "mobj_data.h"
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
#include "bdnav/bdmv_parse.h"
|
|
Packit |
5e46da |
#include "disc/disc.h"
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
#include "file/file.h"
|
|
Packit |
5e46da |
#include "util/bits.h"
|
|
Packit |
5e46da |
#include "util/logging.h"
|
|
Packit |
5e46da |
#include "util/macro.h"
|
|
Packit |
5e46da |
#include "util/strutl.h"
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
#include <stdlib.h>
|
|
Packit |
5e46da |
#include <string.h>
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
#define MOBJ_SIG1 ('M' << 24 | 'O' << 16 | 'B' << 8 | 'J')
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static int _mobj_parse_header(BITSTREAM *bs, int *extension_data_start, uint32_t *mobj_version)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
if (!bdmv_parse_header(bs, MOBJ_SIG1, mobj_version)) {
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
*extension_data_start = bs_read(bs, 32);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
return 1;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
void mobj_parse_cmd(uint8_t *buf, MOBJ_CMD *cmd)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
BITBUFFER bb;
|
|
Packit |
5e46da |
bb_init(&bb, buf, 12);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
cmd->insn.op_cnt = bb_read(&bb, 3);
|
|
Packit |
5e46da |
cmd->insn.grp = bb_read(&bb, 2);
|
|
Packit |
5e46da |
cmd->insn.sub_grp = bb_read(&bb, 3);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
cmd->insn.imm_op1 = bb_read(&bb, 1);
|
|
Packit |
5e46da |
cmd->insn.imm_op2 = bb_read(&bb, 1);
|
|
Packit |
5e46da |
bb_skip(&bb, 2); /* reserved */
|
|
Packit |
5e46da |
cmd->insn.branch_opt = bb_read(&bb, 4);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
bb_skip(&bb, 4); /* reserved */
|
|
Packit |
5e46da |
cmd->insn.cmp_opt = bb_read(&bb, 4);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
bb_skip(&bb, 3); /* reserved */
|
|
Packit |
5e46da |
cmd->insn.set_opt = bb_read(&bb, 5);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
cmd->dst = bb_read(&bb, 32);
|
|
Packit |
5e46da |
cmd->src = bb_read(&bb, 32);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static int _mobj_parse_object(BITSTREAM *bs, MOBJ_OBJECT *obj)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
int i;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
obj->resume_intention_flag = bs_read(bs, 1);
|
|
Packit |
5e46da |
obj->menu_call_mask = bs_read(bs, 1);
|
|
Packit |
5e46da |
obj->title_search_mask = bs_read(bs, 1);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
bs_skip(bs, 13); /* padding */
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
obj->num_cmds = bs_read(bs, 16);
|
|
Packit |
5e46da |
if (!obj->num_cmds) {
|
|
Packit |
5e46da |
BD_DEBUG(DBG_HDMV|DBG_CRIT, "MovieObject.bdmv: empty object\n");
|
|
Packit |
5e46da |
return 1;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
obj->cmds = calloc(obj->num_cmds, sizeof(MOBJ_CMD));
|
|
Packit |
5e46da |
if (!obj->cmds) {
|
|
Packit |
5e46da |
BD_DEBUG(DBG_CRIT, "out of memory\n");
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
for (i = 0; i < obj->num_cmds; i++) {
|
|
Packit |
5e46da |
uint8_t buf[12];
|
|
Packit |
5e46da |
if (bs_avail(bs) < 12*8) {
|
|
Packit |
5e46da |
BD_DEBUG(DBG_HDMV|DBG_CRIT, "MovieObject.bdmv: unexpected EOF\n");
|
|
Packit |
5e46da |
return 0;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
bs_read_bytes(bs, buf, 12);
|
|
Packit |
5e46da |
mobj_parse_cmd(buf, &obj->cmds[i]);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
return 1;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
void mobj_free(MOBJ_OBJECTS **p)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
if (p && *p) {
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if ((*p)->objects) {
|
|
Packit |
5e46da |
int i;
|
|
Packit |
5e46da |
for (i = 0 ; i < (*p)->num_objects; i++) {
|
|
Packit |
5e46da |
X_FREE((*p)->objects[i].cmds);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
X_FREE((*p)->objects);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
X_FREE(*p);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static MOBJ_OBJECTS *_mobj_parse(BD_FILE_H *fp)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
BITSTREAM bs;
|
|
Packit |
5e46da |
MOBJ_OBJECTS *objects = NULL;
|
|
Packit |
5e46da |
uint16_t num_objects;
|
|
Packit |
5e46da |
uint32_t data_len;
|
|
Packit |
5e46da |
int extension_data_start, i;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (bs_init(&bs, fp) < 0) {
|
|
Packit |
5e46da |
BD_DEBUG(DBG_NAV, "MovieObject.bdmv: read error\n");
|
|
Packit |
5e46da |
goto error;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
objects = calloc(1, sizeof(MOBJ_OBJECTS));
|
|
Packit |
5e46da |
if (!objects) {
|
|
Packit |
5e46da |
BD_DEBUG(DBG_CRIT, "out of memory\n");
|
|
Packit |
5e46da |
goto error;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (!_mobj_parse_header(&bs, &extension_data_start, &objects->mobj_version)) {
|
|
Packit |
5e46da |
BD_DEBUG(DBG_NAV | DBG_CRIT, "MovieObject.bdmv: invalid header\n");
|
|
Packit |
5e46da |
goto error;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (extension_data_start) {
|
|
Packit |
5e46da |
BD_DEBUG(DBG_NAV | DBG_CRIT, "MovieObject.bdmv: unknown extension data at %d\n", extension_data_start);
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if (bs_seek_byte(&bs, 40) < 0) {
|
|
Packit |
5e46da |
BD_DEBUG(DBG_NAV, "MovieObject.bdmv: read error\n");
|
|
Packit |
5e46da |
goto error;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
data_len = bs_read(&bs, 32);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
if ((bs_end(&bs) - bs_pos(&bs))/8 < (int64_t)data_len) {
|
|
Packit |
5e46da |
BD_DEBUG(DBG_NAV | DBG_CRIT, "MovieObject.bdmv: invalid data_len %d !\n", data_len);
|
|
Packit |
5e46da |
goto error;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
bs_skip(&bs, 32); /* reserved */
|
|
Packit |
5e46da |
num_objects = bs_read(&bs, 16);
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
objects->num_objects = num_objects;
|
|
Packit |
5e46da |
objects->objects = calloc(num_objects, sizeof(MOBJ_OBJECT));
|
|
Packit |
5e46da |
if (!objects->objects) {
|
|
Packit |
5e46da |
BD_DEBUG(DBG_CRIT, "out of memory\n");
|
|
Packit |
5e46da |
goto error;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
for (i = 0; i < objects->num_objects; i++) {
|
|
Packit |
5e46da |
if (!_mobj_parse_object(&bs, &objects->objects[i])) {
|
|
Packit |
5e46da |
BD_DEBUG(DBG_NAV | DBG_CRIT, "MovieObject.bdmv: error parsing object %d\n", i);
|
|
Packit |
5e46da |
goto error;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
return objects;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
error:
|
|
Packit |
5e46da |
mobj_free(&objects);
|
|
Packit |
5e46da |
return NULL;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
MOBJ_OBJECTS *mobj_parse(const char *file_name)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
BD_FILE_H *fp;
|
|
Packit |
5e46da |
MOBJ_OBJECTS *objects;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
fp = file_open(file_name, "rb");
|
|
Packit |
5e46da |
if (!fp) {
|
|
Packit |
5e46da |
BD_DEBUG(DBG_NAV | DBG_CRIT, "error opening %s\n", file_name);
|
|
Packit |
5e46da |
return NULL;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
objects = _mobj_parse(fp);
|
|
Packit |
5e46da |
file_close(fp);
|
|
Packit |
5e46da |
return objects;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
static MOBJ_OBJECTS *_mobj_get(BD_DISC *disc, const char *path)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
BD_FILE_H *fp;
|
|
Packit |
5e46da |
MOBJ_OBJECTS *objects;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
fp = disc_open_path(disc, path);
|
|
Packit |
5e46da |
if (!fp) {
|
|
Packit |
5e46da |
return NULL;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
objects = _mobj_parse(fp);
|
|
Packit |
5e46da |
file_close(fp);
|
|
Packit |
5e46da |
return objects;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
MOBJ_OBJECTS *mobj_get(BD_DISC *disc)
|
|
Packit |
5e46da |
{
|
|
Packit |
5e46da |
MOBJ_OBJECTS *objects;
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
objects = _mobj_get(disc, "BDMV" DIR_SEP "MovieObject.bdmv");
|
|
Packit |
5e46da |
if (objects) {
|
|
Packit |
5e46da |
return objects;
|
|
Packit |
5e46da |
}
|
|
Packit |
5e46da |
|
|
Packit |
5e46da |
/* if failed, try backup file */
|
|
Packit |
5e46da |
objects = _mobj_get(disc, "BDMV" DIR_SEP "BACKUP" DIR_SEP "MovieObject.bdmv");
|
|
Packit |
5e46da |
return objects;
|
|
Packit |
5e46da |
}
|