META_ROOT *meta_parse(BD_DISC *disc) { #ifdef HAVE_LIBXML2 META_ROOT *root = calloc(1, sizeof(META_ROOT)); if (!root) { BD_DEBUG(DBG_CRIT, "out of memory\n"); return NULL; } root->dl_count = 0; xmlDocPtr doc; _findMetaXMLfiles(root, disc); uint8_t i; for (i = 0; i < root->dl_count; i++) { uint8_t *data = NULL; size_t size; size = disc_read_file(disc, "BDMV" DIR_SEP "META" DIR_SEP "DL", root->dl_entries[i].filename, &data); if (!data || size == 0) { BD_DEBUG(DBG_DIR, "Failed to read BDMV/META/DL/%s\n", root->dl_entries[i].filename); } else { doc = xmlReadMemory((char*)data, (int)size, NULL, NULL, 0); if (doc == NULL) { BD_DEBUG(DBG_DIR, "Failed to parse BDMV/META/DL/%s\n", root->dl_entries[i].filename); } else { xmlNode *root_element = NULL; root_element = xmlDocGetRootElement(doc); root->dl_entries[i].di_name = root->dl_entries[i].di_alternative = NULL; root->dl_entries[i].di_num_sets = root->dl_entries[i].di_set_number = -1; root->dl_entries[i].toc_count = root->dl_entries[i].thumb_count = 0; root->dl_entries[i].toc_entries = NULL; root->dl_entries[i].thumbnails = NULL; _parseManifestNode(root_element, &root->dl_entries[i]); xmlFreeDoc(doc); } X_FREE(data); } } xmlCleanupParser(); return root; #else (void)disc; BD_DEBUG(DBG_DIR, "configured without libxml2 - can't parse meta info\n"); return NULL; #endif }
static BD_FILE_H *_file_open(const char* filename, const char *cmode) { BD_FILE_H *file; int fd = -1; int flags = 0; int mode = 0; if (strchr(cmode, 'w')) { flags = O_WRONLY | O_CREAT | O_TRUNC; mode = S_IRUSR | S_IWUSR; } else { flags = O_RDONLY; } #ifdef O_CLOEXEC flags |= O_CLOEXEC; #endif #ifdef O_BINARY flags |= O_BINARY; #endif if (strncmp(filename, "myth://", 7) == 0) return file_open_mythiowrapper(filename, cmode); if ((fd = open(filename, flags, mode)) < 0) { BD_DEBUG(DBG_FILE, "Error opening file %s\n", filename); return NULL; } file = calloc(1, sizeof(BD_FILE_H)); if (!file) { close(fd); BD_DEBUG(DBG_FILE, "Error opening file %s (out of memory)\n", filename); return NULL; } file->close = _file_close; file->seek = _file_seek; file->read = _file_read; file->write = _file_write; file->tell = _file_tell; //file->eof = file_eof_linux; file->internal = (void*)(intptr_t)fd; BD_DEBUG(DBG_FILE, "Opened POSIX file %s (%p)\n", filename, (void*)file); return file; }
BDPLUS_FILE_H *file_open_default(void *handle, const char* file_name) { const char *device_root = handle; char *file_path; BDPLUS_FILE_H *file; FILE *fp; file_path = str_printf("%s"DIR_SEP"%s", device_root, file_name); if (!file_path) { BD_DEBUG(DBG_CRIT, "out of memory\n"); return NULL; } fp = fopen(file_path, "rb"); X_FREE(file_path); if (!fp) { return NULL; } file = calloc(1, sizeof(BDPLUS_FILE_H)); file->internal = fp; file->close = _file_close; file->seek = _file_seek; file->read = _file_read; return file; }
const char *file_get_config_system(const char *dir) { static char *appdir = NULL; wchar_t wdir[MAX_PATH]; if (!dir) { // first call if (appdir) return appdir; /* Get the "Application Data" folder for all users */ if (S_OK == SHGetFolderPathW(NULL, CSIDL_COMMON_APPDATA | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, wdir)) { int len = WideCharToMultiByte (CP_UTF8, 0, wdir, -1, NULL, 0, NULL, NULL); appdir = malloc(len); WideCharToMultiByte (CP_UTF8, 0, wdir, -1, appdir, len, NULL, NULL); return appdir; } else { BD_DEBUG(DBG_FILE, "Can't find common configuration directory !\n"); return NULL; } } else { // next call return NULL; } return dir; }
static void _findMetaXMLfiles(META_ROOT *meta, const char *device_path) { BD_DIR_H *dir; BD_DIRENT ent; char *path = NULL; path = str_printf("%s" DIR_SEP "BDMV" DIR_SEP "META" DIR_SEP "DL", device_path); dir = dir_open(path); if (dir == NULL) { BD_DEBUG(DBG_DIR, "Failed to open meta dir %s\n", path); X_FREE(path); return; } int res; for (res = dir_read(dir, &ent); !res; res = dir_read(dir, &ent)) { if (ent.d_name[0] == '.') continue; else if (ent.d_name != NULL && strncasecmp(ent.d_name, "bdmt_", 5) == 0) { uint8_t i = meta->dl_count; meta->dl_count++; meta->dl_entries = realloc(meta->dl_entries, (meta->dl_count*sizeof(META_DL))); meta->dl_entries[i].filename = str_dup(ent.d_name); strncpy(meta->dl_entries[i].language_code, ent.d_name+5,3); meta->dl_entries[i].language_code[3] = '\0'; str_tolower(meta->dl_entries[i].language_code); } } dir_close(dir); X_FREE(path); }
/* * register hook */ static void _process_psr_event(void *handle, BD_PSR_EVENT *ev) { GRAPHICS_CONTROLLER *gc = (GRAPHICS_CONTROLLER *)handle; if (ev->ev_type == BD_PSR_SAVE) { BD_DEBUG(DBG_GC, "PSR SAVE event\n"); /* save menu page state */ bd_mutex_lock(&gc->mutex); _save_page_state(gc); bd_mutex_unlock(&gc->mutex); return; } if (ev->ev_type == BD_PSR_RESTORE) { switch (ev->psr_idx) { case PSR_SELECTED_BUTTON_ID: return; case PSR_MENU_PAGE_ID: /* restore menus */ bd_mutex_lock(&gc->mutex); _restore_page_state(gc); bd_mutex_unlock(&gc->mutex); return; default: /* others: ignore */ return; } } }
static int _decode_segment(PG_DISPLAY_SET *s, PES_BUFFER *p) { BITBUFFER bb; bb_init(&bb, p->buf, p->len); uint8_t type = bb_read(&bb, 8); /*uint16_t len = */ bb_read(&bb, 16); switch (type) { case PGS_OBJECT: return _decode_ods(s, &bb, p); case PGS_PALETTE: return _decode_pds(s, &bb, p); case PGS_WINDOW: return _decode_wds(s, &bb, p); case PGS_PG_COMPOSITION: return _decode_pcs(s, &bb, p); case PGS_IG_COMPOSITION: return _decode_ics(s, &bb, p); case PGS_END_OF_DISPLAY: s->complete = 1; return 1; default: BD_DEBUG(DBG_DECODE | DBG_CRIT, "unknown segment type 0x%x\n", type); break; } return 0; }
static int _load_aes_keys(bdplus_aes_key_t *aes_keys, const char *base) { char *path = str_printf("%s/" AES_KEYS_FILE, base); uint8_t *keys; uint32_t size = 0; uint32_t num_keys, ii; if (!path) { return -1; } keys = (uint8_t *)file_load(path, &size); X_FREE(path); num_keys = size / 16; if (num_keys > MAX_AES_KEYS) { num_keys = MAX_AES_KEYS; } if (num_keys * 16 != size) { BD_DEBUG(DBG_FILE | DBG_CRIT, "Invalid AES key file size\n"); } for (ii = 0; ii < num_keys; ii++) { memcpy(aes_keys[ii].key, keys + 16*ii, 16); } X_FREE(keys); return num_keys > 6 ? (int)num_keys : -1; }
static int64_t _stream_read(BD_FILE_H *fp, uint8_t *buf, int64_t size) { DEC_STREAM *st = (DEC_STREAM *)fp->internal; int64_t result; if (size != 6144) { BD_DEBUG(DBG_CRIT, "read size != unit size\n"); return 0; } result = st->fp->read(st->fp, buf, size); if (result <= 0) { return result; } if (st->aacs) { if (libaacs_decrypt_unit(st->aacs, buf)) { /* failure is detected from TP header */ } } if (st->bdplus) { if (libbdplus_fixup(st->bdplus, buf, (int)size) < 0) { /* there's no way to verify if the stream was decoded correctly */ } } return result; }
static void _findMetaXMLfiles(META_ROOT *meta, BD_DISC *disc) { BD_DIR_H *dir; BD_DIRENT ent; dir = disc_open_dir(disc, "BDMV" DIR_SEP "META" DIR_SEP "DL"); if (dir == NULL) { BD_DEBUG(DBG_DIR, "Failed to open meta dir BDMV/META/DL/\n"); return; } int res; for (res = dir_read(dir, &ent); !res; res = dir_read(dir, &ent)) { if (ent.d_name[0] == '.') continue; else if (strncasecmp(ent.d_name, "bdmt_", 5) == 0) { META_DL *new_dl_entries = realloc(meta->dl_entries, ((meta->dl_count + 1)*sizeof(META_DL))); if (new_dl_entries) { uint8_t i = meta->dl_count; meta->dl_count++; meta->dl_entries = new_dl_entries; memset(&meta->dl_entries[i], 0, sizeof(meta->dl_entries[i])); meta->dl_entries[i].filename = str_dup(ent.d_name); strncpy(meta->dl_entries[i].language_code, ent.d_name+5,3); meta->dl_entries[i].language_code[3] = '\0'; str_tolower(meta->dl_entries[i].language_code); } } } dir_close(dir); }
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)); for (ii = 0; ii < p->num_pages; ii++) { _decode_page(bb, &p->page[ii]); } return 1; }
static int _mobj_parse_object(BITSTREAM *bs, MOBJ_OBJECT *obj) { int i; obj->resume_intention_flag = bs_read(bs, 1); obj->menu_call_mask = bs_read(bs, 1); obj->title_search_mask = bs_read(bs, 1); bs_skip(bs, 13); /* padding */ obj->num_cmds = bs_read(bs, 16); obj->cmds = calloc(obj->num_cmds, sizeof(MOBJ_CMD)); if (!obj->cmds) { BD_DEBUG(DBG_CRIT, "out of memory\n"); return 0; } for (i = 0; i < obj->num_cmds; i++) { uint8_t buf[12]; bs_read_bytes(bs, buf, 12); mobj_parse_cmd(buf, &obj->cmds[i]); } return 1; }
JNIEXPORT void JNICALL Java_org_videolan_Libbluray_setKeyInterestN(JNIEnv * env, jclass cls, jlong np, jint mask) { BLURAY* bd = (BLURAY*)(intptr_t)np; BD_DEBUG(DBG_JNI, "setKeyInterestN(0x%x)\n", (int)mask); bd_set_bdj_kit(bd, mask); }
JNIEXPORT void JNICALL Java_org_videolan_Libbluray_setUOMaskN(JNIEnv * env, jclass cls, jlong np, jboolean menuCallMask, jboolean titleSearchMask) { BLURAY* bd = (BLURAY*)(intptr_t)np; BD_DEBUG(DBG_JNI, "setUOMaskN(%d,%d)\n", (int)menuCallMask, (int)titleSearchMask); bd_set_bdj_uo_mask(bd, ((!!menuCallMask) * BDJ_MENU_CALL_MASK) | ((!!titleSearchMask) * BDJ_TITLE_SEARCH_MASK)); }
JNIEXPORT jlong JNICALL Java_org_videolan_Libbluray_getUOMaskN(JNIEnv * env, jclass cls, jlong np) { BLURAY* bd = (BLURAY*)(intptr_t)np; BD_DEBUG(DBG_JNI, "getUOMaskN()\n"); return bd_get_uo_mask(bd); }
static int _parse_app_info(BITSTREAM *bs, INDX_APP_INFO *app_info) { uint32_t len; if (bs_seek_byte(bs, 40) < 0) { return 0; } len = bs_read(bs, 32); if (len != 34) { BD_DEBUG(DBG_NAV, "index.bdmv app_info length is %d, expected 34 !\n", len); } bs_skip(bs, 1); app_info->initial_output_mode_preference = bs_read(bs, 1); app_info->content_exist_flag = bs_read(bs, 1); bs_skip(bs, 5); app_info->video_format = bs_read(bs, 4); app_info->frame_rate = bs_read(bs, 4); bs_read_bytes(bs, app_info->user_data, 32); return 1; }
BD_DISC *disc_open(const char *device_path, fs_access *p_fs, struct bd_enc_info *enc_info, const char *keyfile_path, void *regs, void *psr_read, void *psr_write) { BD_DISC *p = _disc_init(); if (p) { if (p_fs && p_fs->open_dir) { p->fs_handle = p_fs->fs_handle; p->pf_file_open_bdrom = p_fs->open_file; p->pf_dir_open_bdrom = p_fs->open_dir; } _set_paths(p, device_path); #ifdef ENABLE_UDF /* check if disc root directory can be opened. If not, treat it as device/image file. */ BD_DIR_H *dp_img = device_path ? dir_open(device_path) : NULL; if (!dp_img) { void *udf = udf_image_open(device_path, p_fs ? p_fs->fs_handle : NULL, p_fs ? p_fs->read_blocks : NULL); if (!udf) { BD_DEBUG(DBG_FILE | DBG_CRIT, "failed opening UDF image %s\n", device_path); } else { p->fs_handle = udf; p->pf_fs_close = udf_image_close; p->pf_file_open_bdrom = udf_file_open; p->pf_dir_open_bdrom = udf_dir_open; p->udf_volid = udf_volume_id(udf); /* root not accessible with stdio */ X_FREE(p->disc_root); } } else { dir_close(dp_img); BD_DEBUG(DBG_FILE, "%s does not seem to be image file or device node\n", device_path); } #endif struct dec_dev dev = { p->fs_handle, p->pf_file_open_bdrom, p, (file_openFp)disc_open_path, p->disc_root, device_path }; p->dec = dec_init(&dev, enc_info, keyfile_path, regs, psr_read, psr_write); } return p; }
static int _parse_index(BITSTREAM *bs, INDX_ROOT *index) { uint32_t index_len, i; index_len = bs_read(bs, 32); /* TODO: check if goes to extension data area */ if ((bs_end(bs) - bs_pos(bs))/8 < (int64_t)index_len) { BD_DEBUG(DBG_NAV | DBG_CRIT, "index.bdmv: invalid index_len %d !\n", index_len); return 0; } if (!_parse_playback_obj(bs, &index->first_play) || !_parse_playback_obj(bs, &index->top_menu)) { return 0; } index->num_titles = bs_read(bs, 16); index->titles = calloc(index->num_titles, sizeof(INDX_TITLE)); for (i = 0; i < index->num_titles; i++) { index->titles[i].object_type = bs_read(bs, 2); index->titles[i].access_type = bs_read(bs, 2); bs_skip(bs, 28); switch (index->titles[i].object_type) { case indx_object_type_hdmv: if (!_parse_hdmv_obj(bs, &index->titles[i].hdmv)) return 0; break; case indx_object_type_bdj: if (!_parse_bdj_obj(bs, &index->titles[i].bdj)) return 0; break; default: BD_DEBUG(DBG_NAV | DBG_CRIT, "index.bdmv: unknown object type %d (#%d)\n", index->titles[i].object_type, i); return 0; } } return 1; }
static void *_load_jvm(const char **p_java_home) { #ifdef HAVE_BDJ_J2ME # ifdef _WIN32 static const char *jvm_path[] = {NULL, JDK_HOME}; static const char jvm_dir[] = "bin"; static const char jvm_lib[] = "cvmi"; # else static const char *jvm_path[] = {NULL, JDK_HOME, "/opt/PhoneME"}; static const char jvm_dir[] = "bin"; static const char jvm_lib[] = "libcvm"; # endif #else # ifdef _WIN32 static const char *jvm_path[] = {NULL, JDK_HOME}; static const char jvm_dir[] = "jre/bin/server"; static const char jvm_lib[] = "jvm"; # else static const char *jvm_path[] = {NULL, JDK_HOME, "/usr/lib/jvm/default-java", "/usr/lib/jvm/java-6-openjdk", "/usr/lib/jvm/java-7-openjdk", "/etc/java-config-2/current-system-vm"}; static const char jvm_dir[] = "jre/lib/" JAVA_ARCH "/server"; static const char jvm_lib[] = "libjvm"; # endif #endif const char *java_home = NULL; unsigned path_ind; void *handle = NULL; /* JAVA_HOME set, use it */ java_home = getenv("JAVA_HOME"); if (java_home) { *p_java_home = java_home; return _jvm_dlopen(java_home, jvm_dir, jvm_lib); } #if defined(_WIN32) && !defined(HAVE_BDJ_J2ME) handle = _load_jvm_win32(p_java_home); if (handle) { return handle; } #endif BD_DEBUG(DBG_BDJ, "JAVA_HOME not set, trying default locations\n"); /* try our pre-defined locations */ for (path_ind = 0; !handle && path_ind < sizeof(jvm_path) / sizeof(jvm_path[0]); path_ind++) { *p_java_home = jvm_path[path_ind]; handle = _jvm_dlopen(jvm_path[path_ind], jvm_dir, jvm_lib); } if (!*p_java_home) { *p_java_home = dl_get_path(); } return handle; }
JNIEXPORT jlong JNICALL Java_org_videolan_Libbluray_seekChapterN(JNIEnv * env, jclass cls, jlong np, jint chapter) { BDJAVA* bdj = (BDJAVA*)(intptr_t)np; BD_DEBUG(DBG_JNI, "seekChapterN(%d)\n", (int)chapter); return bd_seek_chapter(bdj->bd, chapter); }
static int iokit_find_service_matching (MMCDEV *mmc, io_service_t *servp) { CFMutableDictionaryRef matchingDict = IOServiceMatching("IOBDServices"); io_iterator_t deviceIterator; io_service_t service; int rc; assert (NULL != servp); *servp = 0; if (!matchingDict) { BD_DEBUG(DBG_MMC, "Could not create a matching dictionary for IOBDServices\n"); return -1; } /* this call consumes the reference to the matchingDict. we do not need to release it */ rc = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, &deviceIterator); if (kIOReturnSuccess != rc) { BD_DEBUG(DBG_MMC, "Could not create device iterator\n"); return -1; } while (0 != (service = IOIteratorNext (deviceIterator))) { CFStringRef data; char name[128] = ""; data = IORegistryEntrySearchCFProperty (service, kIOServicePlane, CFSTR("BSD Name"), kCFAllocatorDefault, kIORegistryIterateRecursively); if (NULL != data) { rc = CFStringGetCString (data, name, sizeof (name), kCFStringEncodingASCII); CFRelease (data); if (0 == strcmp (name, mmc->bsd_name)) { break; } } (void) IOObjectRelease (service); } IOObjectRelease (deviceIterator); *servp = service; return (service) ? 0 : -1; }
static int64_t _file_write(BD_FILE_H *file, const uint8_t *buf, int64_t size) { if (size > 0 && size < BD_MAX_SSIZE) { return (int64_t)fwrite(buf, 1, (size_t)size, (FILE *)file->internal); } if (size == 0) { if (fflush((FILE *)file->internal)) { BD_DEBUG(DBG_FILE, "fflush() failed (%p)\n", (void*)file); return -1; } return 0; } BD_DEBUG(DBG_FILE | DBG_CRIT, "Ignoring invalid write of size %"PRId64" (%p)\n", size, (void*)file); return 0; }
JNIEXPORT jint JNICALL Java_org_videolan_Libbluray_selectTitleN(JNIEnv * env, jclass cls, jlong np, jint title) { BLURAY* bd = (BLURAY*)(intptr_t)np; BD_DEBUG(DBG_JNI, "selectTitleN(%d)\n", (int)title); return bd_play_title_internal(bd, title); }
JNIEXPORT jlong JNICALL Java_org_videolan_Libbluray_seekN(JNIEnv * env, jclass cls, jlong np, jint playitem, jint playmark, jlong tick) { BLURAY* bd = (BLURAY*)(intptr_t)np; BD_DEBUG(DBG_JNI, "seekN(tick=%"PRId64", mark=%d, playitem=%d)\n", (int64_t)tick, (int)playmark, (int)playitem); return bd_bdj_seek(bd, playitem, playmark, tick); }
JNIEXPORT jint JNICALL Java_org_videolan_Libbluray_writeGPRN(JNIEnv * env, jclass cls, jlong np, jint num, jint value) { BDJAVA* bdj = (BDJAVA*)(intptr_t)np; BD_DEBUG(DBG_JNI, "writeGPRN(%d,%d)\n", (int)num, (int)value); return bd_gpr_write(bdj->reg, num, value); }
static void _error(const char *msg, int errnum, void *dir) { char buf[128]; if (strerror_r(errnum, buf, sizeof(buf))) { strcpy(buf, "?"); } BD_DEBUG(DBG_DIR | DBG_CRIT, "%s: %d %s (%p)\n", msg, errnum, buf, dir); }
JNIEXPORT jlong JNICALL Java_org_videolan_Libbluray_seekPlayItemN(JNIEnv * env, jclass cls, jlong np, jint clip) { BDJAVA* bdj = (BDJAVA*)(intptr_t)np; BD_DEBUG(DBG_JNI, "seekPlayItemN(%d)\n", (int)clip); return bd_seek_playitem(bdj->bd, clip); }
JNIEXPORT jint JNICALL Java_org_videolan_Libbluray_selectPlaylistN( JNIEnv * env, jclass cls, jlong np, jint playlist) { BDJAVA* bdj = (BDJAVA*)(intptr_t)np; BD_DEBUG(DBG_JNI, "selectPlaylistN(%05d.mpls)\n", (int)playlist); return bd_select_playlist(bdj->bd, playlist); }
JNIEXPORT jint JNICALL Java_org_videolan_Libbluray_writeGPRN(JNIEnv * env, jclass cls, jlong np, jint num, jint value) { BLURAY* bd = (BLURAY*)(intptr_t)np; BD_DEBUG(DBG_JNI, "writeGPRN(%d,%d)\n", (int)num, (int)value); return bd_reg_write(bd, 0, num, value, ~0); }
JNIEXPORT jlong JNICALL Java_org_videolan_Libbluray_seekMarkN(JNIEnv * env, jclass cls, jlong np, jint mark) { BDJAVA* bdj = (BDJAVA*)(intptr_t)np; BD_DEBUG(DBG_JNI, "seekMarkN(%d)\n", (int)mark); return bd_seek_mark(bdj->bd, mark); }