-static OggVorbis_File *oggvorbis_file;
-static FILE *infile;
-static int header_len, oggbuf_len, vi_channels;
-static char *header, *oggbuf;
-static ssize_t *chunk_table, max_chunk_len;
-static struct audio_format_handler *af;
-static long vi_sampling_rate, vi_bitrate, vi_bitrate_nominal,
- num_chunks;
+/** describes a memory-mapped ogg vorbis file */
+struct ogg_datasource {
+ /** the memory mapping */
+ char *map;
+ /** this size of the mapping */
+ off_t numbytes;
+ /** the current position in the mapping */
+ off_t fpos;
+};
+
+static size_t cb_read(void *buf, size_t size, size_t nmemb, void *datasource)
+{
+ struct ogg_datasource *ods = datasource;
+ size_t copy = PARA_MIN(ods->numbytes - ods->fpos, size * nmemb),
+ ret = copy / size;
+ if (!ret)
+ return 0;
+ memcpy(buf, ods->map + ods->fpos, copy);
+// PARA_INFO_LOG("size: %zd, nmemb: %zd, ret: %zd\n", size, nmemb, ret);
+ ods->fpos += ret * size;
+ return ret;
+}
+
+static int cb_seek(void *datasource, ogg_int64_t offset,
+ int whence)
+{
+ struct ogg_datasource *ods = datasource;
+ switch (whence) {
+ case SEEK_SET:
+ if (offset >= 0 && offset <= ods->numbytes) {
+ ods->fpos = offset;
+ return 0;
+ }
+ errno = EINVAL;
+ return -1;
+ break;
+ case SEEK_END:
+ if (offset <= 0 && -offset <= ods->numbytes) {
+ ods->fpos = ods->numbytes + offset;
+ return 0;
+ }
+ errno = EINVAL;
+ return -1;
+ break;
+ case SEEK_CUR:
+ if ((offset >= 0 && offset + ods->fpos > ods->numbytes) ||
+ (offset < 0 && offset + ods->fpos < 0)) {
+ errno = EINVAL;
+ return -1;
+ }
+ ods->fpos += offset;
+ return 0;
+ }
+ errno = EINVAL;
+ return -1;
+}
+
+/* don't do anything as vss still needs the open filehandle */
+static int cb_close(__a_unused void *datasource)
+{
+ return 0;
+}
+
+static long cb_tell(void *datasource)
+{
+ struct ogg_datasource *ods = datasource;
+ return (unsigned long)ods->fpos;
+}
+
+static int ogg_open_callbacks(void *datasource, OggVorbis_File *vf, ov_callbacks c)
+{
+ int ret = ov_open_callbacks(datasource, vf,
+ NULL, /* no initial buffer */
+ 0, /* no initial bytes */
+ c); /* the ov_open_callbacks */
+
+ if (ret == OV_EREAD)
+ return -E_OGG_READ;
+ if (ret == OV_ENOTVORBIS)
+ return -E_VORBIS;
+ if (ret == OV_EVERSION)
+ return -E_OGG_VERSION;
+ if (ret == OV_EBADHEADER)
+ return -E_OGG_BAD_HEADER;
+ if (ret < 0)
+ return -E_OGG_UNKNOWN_ERROR;
+ return 1;