/** * fd, buffer, offset, length, position */ static int es_file_write(duk_context *ctx) { es_fd_t *efd = es_fd_get(ctx, 0); duk_size_t bufsize; char *buf = duk_to_buffer(ctx, 1, &bufsize); int len; const int offset = duk_to_int(ctx, 2); if(duk_is_null(ctx, 3)) { len = bufsize; } else { len = duk_to_int(ctx, 3); } // Don't read past buffer end if(offset + len > bufsize) len = bufsize - offset; if(!duk_is_null(ctx, 4)) { // Seek fa_seek(efd->efd_fh, duk_require_number(ctx, 4), SEEK_SET); } int r = fa_write(efd->efd_fh, buf + offset, len); if(r < 0) duk_error(ctx, DUK_ERR_ERROR, "Write error to '%s'", efd->efd_path); duk_push_int(ctx, r); return 1; }
static int jpeginfo_reader(void *handle, void *buf, off_t offset, size_t size) { if(fa_seek(handle, offset, SEEK_SET) != offset) return -1; return fa_read(handle, buf, size); }
/** * fd, buffer, offset, length, position */ static int es_file_read(duk_context *ctx) { es_fd_t *efd = es_fd_get(ctx, 0); duk_size_t bufsize; char *buf = duk_require_buffer_data(ctx, 1, &bufsize); const int offset = duk_to_int(ctx, 2); const int len = duk_to_int(ctx, 3); if(offset + len > bufsize) duk_error(ctx, DUK_ERR_ERROR, "Buffer too small %zd < %d + %d", bufsize, offset + len); if(!duk_is_null(ctx, 4)) { // Seek fa_seek(efd->efd_fh, duk_require_number(ctx, 4), SEEK_SET); } int r = fa_read(efd->efd_fh, buf + offset, len); if(r < 0) duk_error(ctx, DUK_ERR_ERROR, "Read error from '%s'", efd->efd_path); duk_push_int(ctx, r); return 1; }
static int cookie_seek(void *fh, off64_t *offsetp, int whence) { int64_t s = fa_seek(fh, *offsetp, whence); *offsetp = s; return 0; }
static int64_t fa_libav_seek(void *opaque, int64_t offset, int whence) { fa_handle_t *fh = opaque; if(whence == AVSEEK_SIZE) return fa_fsize(fh); return fa_seek(fh, offset, whence & ~AVSEEK_FORCE); }
/** * Check if file is an iso image * pb is guaranteed to point at 64k of data */ int fa_probe_iso(metadata_t *md, fa_handle_t *fh) { uint8_t pb[128]; if(fa_seek(fh, 0x8000, SEEK_SET) != 0x8000) return -1; if(fa_read(fh, pb, sizeof(pb)) != sizeof(pb)) return -1; return fa_probe_iso0(md, pb); }
static int64_t cmp_seek(fa_handle_t *handle, int64_t pos, int whence) { cmp_t *s = (cmp_t *)handle; int64_t r1 = fa_seek(s->s_src, pos, whence); int64_t r2 = lseek(s->s_fd, pos, whence); if(r1 != r2) { TRACE(TRACE_ERROR, "FACMP", "seek(%"PRId64", %d) failed fa:%"PRId64" local:%"PRId64, pos, whence, r1, r2); exit(1); } return r1; }
AVIOContext * fa_libav_reopen(fa_handle_t *fh) { AVIOContext *avio; if(fa_seek(fh, 0, SEEK_SET) != 0) return NULL; int buf_size = 32768; void *buf = av_malloc(buf_size); avio = avio_alloc_context(buf, buf_size, 0, fh, fa_libav_read, NULL, fa_libav_seek); if(avio != NULL && fa_fsize(fh) == -1) avio->seekable = 0; return avio; }
metadata_t * fa_probe_metadata(const char *url, char *errbuf, size_t errsize, const char *filename) { AVFormatContext *fctx; fa_handle_t *fh = fa_open_ex(url, errbuf, errsize, FA_BUFFERED_SMALL, NULL); if(fh == NULL) return NULL; metadata_t *md = metadata_create(); #if ENABLE_LIBGME if(gme_probe(md, url, fh)) return md; #endif #if ENABLE_XMP if(xmp_probe(md, url, fh)) return md; #endif fa_seek(fh, 0, SEEK_SET); if(fa_probe_header(md, url, fh, filename)) { fa_close(fh); return md; } AVIOContext *avio = fa_libav_reopen(fh); if((fctx = fa_libav_open_format(avio, url, errbuf, errsize, NULL)) == NULL) { fa_libav_close(avio); metadata_destroy(md); return NULL; } fa_lavf_load_meta(md, fctx, filename); fa_libav_close_format(fctx); return md; }
static int fab_read(fa_handle_t *handle, void *buf, size_t size) { buffered_file_t *bf = (buffered_file_t *)handle; int64_t pre = bf->bf_fpos; printf("fab_read(%ld + %zd) <eof = %ld> = ", bf->bf_fpos, size, bf->bf_size); int r = fab_read0(handle, buf, size); printf("%d <now at %ld d=%ld>\n", r, bf->bf_fpos, bf->bf_fpos - pre); unsigned char *m1 = malloc(size); unsigned char *m2 = buf; if(fa_seek(bf->bf_chk, pre, SEEK_SET) != pre) { printf("Seek to %ld failed\n", pre); abort(); } int r2 = fa_read(bf->bf_chk, m1, size); printf(" r=%d r2=%d\n", r, r2); assert(r == r2); int i; for(i = 0; i < r; i++) { if(m1[i] != m2[i]) { printf("Mismatch at byte %d %02x != %02x\n", i, m1[i], m2[i]); abort(); } } free(m1); return r; }
static event_t * openspc_play(media_pipe_t *mp, AVIOContext *avio, char *errbuf, size_t errlen) { media_queue_t *mq = &mp->mp_audio; #error fa_fsize can return -1 .. deal with it size_t r, siz = fa_fsize(fh); uint8_t *buf = malloc(siz); media_buf_t *mb = NULL; event_t *e; int hold = 0, lost_focus = 0; int sample = 0; unsigned int duration = INT32_MAX; mp_set_playstatus_by_hold(mp, hold, NULL); mp->mp_audio.mq_stream = 0; fa_seek(fh, 0, SEEK_SET); r = fa_read(fh, buf, siz); fa_close(fh); if(r != siz) { free(buf); snprintf(errbuf, errlen, "openspc: Unable to read file"); return NULL; } if(OSPC_Init(buf, siz)) { free(buf); snprintf(errbuf, errlen, "openspc: Unable to initialize file"); return NULL; } if(!memcmp("v0.30", buf + 0x1c, 4) && buf[0x23] == 0x1a) { char str[4]; memcpy(str, buf + 0xa9, 3); str[3] = 0; duration = atoi(str) * 32000; } mp_set_play_caps(mp, MP_PLAY_CAPS_PAUSE); mp_become_primary(mp); while(1) { if(mb == NULL) { if(sample > duration) { while((e = mp_wait_for_empty_queues(mp, 0)) != NULL) { if(event_is_type(e, EVENT_PLAYQUEUE_JUMP) || event_is_action(e, ACTION_PREV_TRACK) || event_is_action(e, ACTION_NEXT_TRACK) || event_is_action(e, ACTION_STOP)) { mp_flush(mp, 0); break; } event_release(e); } if(e == NULL) e = event_create_type(EVENT_EOF); break; } mb = media_buf_alloc(); mb->mb_data_type = MB_AUDIO; mb->mb_size = sizeof(int16_t) * 2048 * 2; mb->mb_data = malloc(mb->mb_size); mb->mb_size = OSPC_Run(-1, mb->mb_data, mb->mb_size); mb->mb_channels = 2; mb->mb_rate = 32000; mb->mb_time = sample * 1000000LL / mb->mb_rate; sample += 2048; } if((e = mb_enqueue_with_events(mp, mq, mb)) == NULL) { mb = NULL; /* Enqueue succeeded */ continue; } if(event_is_type(e, EVENT_PLAYQUEUE_JUMP)) { mp_flush(mp, 0); break; } else if(event_is_action(e, ACTION_PLAYPAUSE) || event_is_action(e, ACTION_PLAY) || event_is_action(e, ACTION_PAUSE)) { hold = action_update_hold_by_event(hold, e); mp_send_cmd_head(mp, mq, hold ? MB_CTRL_PAUSE : MB_CTRL_PLAY); lost_focus = 0; mp_set_playstatus_by_hold(mp, hold, NULL); } else if(event_is_type(e, EVENT_MP_NO_LONGER_PRIMARY)) { hold = 1; lost_focus = 1; mp_send_cmd_head(mp, mq, MB_CTRL_PAUSE); mp_set_playstatus_by_hold(mp, hold, e->e_payload); } else if(event_is_type(e, EVENT_MP_IS_PRIMARY)) { if(lost_focus) { hold = 0; lost_focus = 0; mp_send_cmd_head(mp, mq, MB_CTRL_PLAY); mp_set_playstatus_by_hold(mp, hold, NULL); } } else if(event_is_type(e, EVENT_INTERNAL_PAUSE)) { hold = 1; lost_focus = 0; mp_send_cmd_head(mp, mq, MB_CTRL_PAUSE); mp_set_playstatus_by_hold(mp, hold, e->e_payload); } else if(event_is_action(e, ACTION_PREV_TRACK) || event_is_action(e, ACTION_NEXT_TRACK) || event_is_action(e, ACTION_STOP)) { mp_flush(mp, 0); break; } event_release(e); } free(buf); return e; }
pixmap_t * fa_imageloader(const char *url, const struct image_meta *im, const char **vpaths, char *errbuf, size_t errlen, int *cache_control, cancellable_t *c) { uint8_t p[16]; int r; int width = -1, height = -1, orientation = 0; fa_handle_t *fh; pixmap_t *pm; pixmap_type_t fmt; #if ENABLE_LIBAV if(strchr(url, '#')) return fa_image_from_video(url, im, errbuf, errlen, cache_control, c); #endif if(!im->im_want_thumb) return fa_imageloader2(url, vpaths, errbuf, errlen, cache_control, c); fa_open_extra_t foe = { .foe_c = c }; if((fh = fa_open_vpaths(url, vpaths, errbuf, errlen, FA_BUFFERED_SMALL, &foe)) == NULL) return NULL; if(ONLY_CACHED(cache_control)) { snprintf(errbuf, errlen, "Not cached"); return NULL; } if(fa_read(fh, p, sizeof(p)) != sizeof(p)) { snprintf(errbuf, errlen, "File too short"); fa_close(fh); return NULL; } /* Probe format */ if((p[6] == 'J' && p[7] == 'F' && p[8] == 'I' && p[9] == 'F') || (p[6] == 'E' && p[7] == 'x' && p[8] == 'i' && p[9] == 'f')) { jpeginfo_t ji; if(jpeg_info(&ji, jpeginfo_reader, fh, JPEG_INFO_DIMENSIONS | JPEG_INFO_ORIENTATION | (im->im_want_thumb ? JPEG_INFO_THUMBNAIL : 0), p, sizeof(p), errbuf, errlen)) { fa_close(fh); return NULL; } if(im->im_want_thumb && ji.ji_thumbnail) { pixmap_t *pm = pixmap_dup(ji.ji_thumbnail); fa_close(fh); jpeg_info_clear(&ji); return pm; } fmt = PIXMAP_JPEG; width = ji.ji_width; height = ji.ji_height; orientation = ji.ji_orientation; jpeg_info_clear(&ji); } else if(!memcmp(pngsig, p, 8)) { fmt = PIXMAP_PNG; } else if(!memcmp(gif87sig, p, sizeof(gif87sig)) || !memcmp(gif89sig, p, sizeof(gif89sig))) { fmt = PIXMAP_GIF; } else if(!memcmp(svgsig1, p, sizeof(svgsig1)) || !memcmp(svgsig2, p, sizeof(svgsig2))) { fmt = PIXMAP_SVG; } else { snprintf(errbuf, errlen, "Unknown format"); fa_close(fh); return NULL; } int64_t s = fa_fsize(fh); if(s < 0) { snprintf(errbuf, errlen, "Can't read from non-seekable file"); fa_close(fh); return NULL; } pm = pixmap_alloc_coded(NULL, s, fmt); if(pm == NULL) { snprintf(errbuf, errlen, "Out of memory"); fa_close(fh); return NULL; } pm->pm_width = width; pm->pm_height = height; pm->pm_orientation = orientation; fa_seek(fh, SEEK_SET, 0); r = fa_read(fh, pm->pm_data, pm->pm_size); fa_close(fh); if(r != pm->pm_size) { pixmap_release(pm); snprintf(errbuf, errlen, "Read error"); return NULL; } return pm; }
static int64_t bwlimit_seek(fa_handle_t *handle, int64_t pos, int whence) { bwlimit_t *s = (bwlimit_t *)handle; return fa_seek(s->s_src, pos, whence); }
static int gme_probe(metadata_t *md, const char *url, fa_handle_t *fh) { uint8_t b4[4], *buf; gme_err_t err; Music_Emu *emu; gme_info_t *info; int tracks; size_t size; const char *type; if(fa_read(fh, b4, 4) != 4) return 0; type = gme_identify_header(b4); if(*type == 0) return 0; size = fa_fsize(fh); if(size == -1) return -1; buf = malloc(size); fa_seek(fh, 0, SEEK_SET); if(fa_read(fh, buf, size) != size) { free(buf); return 0; } err = gme_open_data(buf, size, &emu, gme_info_only); free(buf); if(err != NULL) return 0; err = gme_track_info(emu, &info, 0); if(err != NULL) { gme_delete(emu); return 0; } tracks = gme_track_count(emu); #if 0 printf("tracks : %d\n", tracks); printf("system : %s\n", info->system); printf("game : %s\n", info->game); printf("song : %s\n", info->song); printf("author : %s\n", info->author); printf("copyright: %s\n", info->copyright); printf("comment : %s\n", info->comment); printf("dumper : %s\n", info->dumper); #endif if(tracks == 1) { md->md_title = info->song[0] ? rstr_alloc(info->song) : NULL; md->md_album = info->game[0] ? rstr_alloc(info->game) : NULL; md->md_artist = info->author[0] ? rstr_alloc(info->author) : NULL; md->md_duration = info->play_length / 1000.0; md->md_contenttype = CONTENT_AUDIO; } else { md->md_title = info->game[0] ? rstr_alloc(info->game) : NULL; md->md_artist = info->author[0] ? rstr_alloc(info->author) : NULL; md->md_contenttype = CONTENT_ALBUM; metdata_set_redirect(md, "gmefile://%s/", url); } gme_free_info(info); gme_delete(emu); return 1; }
image_t * fa_imageloader(const char *url, const struct image_meta *im, const char **vpaths, char *errbuf, size_t errlen, int *cache_control, cancellable_t *c) { uint8_t p[16]; int r; int width = -1, height = -1, orientation = 0; fa_handle_t *fh; image_t *img; image_coded_type_t fmt; #if ENABLE_LIBAV if(strchr(url, '#')) return fa_image_from_video(url, im, errbuf, errlen, cache_control, c); #endif if(!im->im_want_thumb) return fa_imageloader2(url, vpaths, errbuf, errlen, cache_control, c); fa_open_extra_t foe = { .foe_cancellable = c }; if((fh = fa_open_vpaths(url, vpaths, errbuf, errlen, FA_BUFFERED_SMALL, &foe)) == NULL) return NULL; if(ONLY_CACHED(cache_control)) { snprintf(errbuf, errlen, "Not cached"); return NULL; } if(fa_read(fh, p, sizeof(p)) != sizeof(p)) { snprintf(errbuf, errlen, "File too short"); fa_close(fh); return NULL; } /* Probe format */ if(p[0] == 0xff && p[1] == 0xd8 && p[2] == 0xff) { jpeginfo_t ji; if(jpeg_info(&ji, jpeginfo_reader, fh, JPEG_INFO_DIMENSIONS | JPEG_INFO_ORIENTATION | (im->im_want_thumb ? JPEG_INFO_THUMBNAIL : 0), p, sizeof(p), errbuf, errlen)) { fa_close(fh); return NULL; } if(im->im_want_thumb && ji.ji_thumbnail) { image_t *im = image_retain(ji.ji_thumbnail); fa_close(fh); jpeg_info_clear(&ji); im->im_flags |= IMAGE_ADAPTED; return im; } fmt = IMAGE_JPEG; width = ji.ji_width; height = ji.ji_height; orientation = ji.ji_orientation; jpeg_info_clear(&ji); } else if(!memcmp(pngsig, p, 8)) { fmt = IMAGE_PNG; } else if(!memcmp(gif87sig, p, sizeof(gif87sig)) || !memcmp(gif89sig, p, sizeof(gif89sig))) { fmt = IMAGE_GIF; } else if(p[0] == 'B' && p[1] == 'M') { fmt = IMAGE_BMP; } else if(!memcmp(svgsig1, p, sizeof(svgsig1)) || !memcmp(svgsig2, p, sizeof(svgsig2))) { fmt = IMAGE_SVG; } else { snprintf(errbuf, errlen, "Unknown format"); fa_close(fh); return NULL; } int64_t s = fa_fsize(fh); if(s < 0) { snprintf(errbuf, errlen, "Can't read from non-seekable file"); fa_close(fh); return NULL; } void *ptr; img = image_coded_alloc(&ptr, s, fmt); if(img == NULL) { snprintf(errbuf, errlen, "Out of memory"); fa_close(fh); return NULL; } img->im_width = width; img->im_height = height; img->im_orientation = orientation; fa_seek(fh, SEEK_SET, 0); r = fa_read(fh, ptr, s); fa_close(fh); if(r != s) { image_release(img); snprintf(errbuf, errlen, "Read error"); return NULL; } return img; }