lavf/brstm: support little-endian files (BCSTM; 3DS)
authorRodger Combs <rodger.combs@gmail.com>
Sat, 20 Jun 2015 10:01:15 +0000 (05:01 -0500)
committerMichael Niedermayer <michaelni@gmx.at>
Sat, 20 Jun 2015 14:50:47 +0000 (16:50 +0200)
Signed-off-by: Michael Niedermayer <michaelni@gmx.at>
Changelog
libavformat/brstm.c

index cfe3e93..a5c6b82 100644 (file)
--- a/Changelog
+++ b/Changelog
@@ -3,7 +3,7 @@ releases are sorted from youngest to oldest.
 
 version <next>:
 - colorkey video filter
-- BFSTM demuxer
+- BFSTM/BCSTM demuxer
 - little-endian ADPCM_THP decoder
 
 
index 1eba943..15e63b8 100644 (file)
@@ -33,6 +33,7 @@ typedef struct BRSTMDemuxContext {
     uint8_t     *table;
     uint8_t     *adpc;
     int         bfstm;
+    int         little_endian;
 } BRSTMDemuxContext;
 
 static int probe(AVProbeData *p)
@@ -46,7 +47,8 @@ static int probe(AVProbeData *p)
 
 static int probe_bfstm(AVProbeData *p)
 {
-    if (AV_RL32(p->buf) == MKTAG('F','S','T','M') &&
+    if ((AV_RL32(p->buf) == MKTAG('F','S','T','M') ||
+         AV_RL32(p->buf) == MKTAG('C','S','T','M')) &&
         (AV_RL16(p->buf + 4) == 0xFFFE ||
          AV_RL16(p->buf + 4) == 0xFEFF))
         return AVPROBE_SCORE_MAX / 3 * 2;
@@ -63,6 +65,24 @@ static int read_close(AVFormatContext *s)
     return 0;
 }
 
+static av_always_inline unsigned int read16(AVFormatContext *s)
+{
+    BRSTMDemuxContext *b = s->priv_data;
+    if (b->little_endian)
+        return avio_rl16(s->pb);
+    else
+        return avio_rb16(s->pb);
+}
+
+static av_always_inline unsigned int read32(AVFormatContext *s)
+{
+    BRSTMDemuxContext *b = s->priv_data;
+    if (b->little_endian)
+        return avio_rl32(s->pb);
+    else
+        return avio_rb32(s->pb);
+}
+
 static int read_header(AVFormatContext *s)
 {
     BRSTMDemuxContext *b = s->priv_data;
@@ -86,16 +106,15 @@ static int read_header(AVFormatContext *s)
         av_log(s, AV_LOG_ERROR, "invalid byte order: %X\n", bom);
         return AVERROR_INVALIDDATA;
     }
-    if (bom == 0xFFFE) {
-        avpriv_request_sample(s, "little endian byte order");
-        return AVERROR_PATCHWELCOME;
-    }
+
+    if (bom == 0xFFFE)
+        b->little_endian = 1;
 
     if (!b->bfstm) {
         major = avio_r8(s->pb);
         minor = avio_r8(s->pb);
         avio_skip(s->pb, 4); // size of file
-        size = avio_rb16(s->pb);
+        size = read16(s);
         if (size < 14)
             return AVERROR_INVALIDDATA;
 
@@ -107,30 +126,31 @@ static int read_header(AVFormatContext *s)
         uint32_t info_offset = 0, info_size;
         uint16_t section_count, header_size, i;
 
-        header_size = avio_rb16(s->pb); // 6
+        header_size = read16(s); // 6
 
         avio_skip(s->pb, 4); // Unknown constant 0x00030000
         avio_skip(s->pb, 4); // size of file
-        section_count = avio_rb16(s->pb);
+        section_count = read16(s);
         avio_skip(s->pb, 2); // padding
         for (i = 0; avio_tell(s->pb) < header_size
                     && !(data_offset && info_offset)
                     && i < section_count; i++) {
-            uint32_t flag = avio_rb32(s->pb);
+            uint16_t flag = read16(s);
+            avio_skip(s->pb, 2);
             switch (flag) {
-            case 0x40000000:
-                info_offset = avio_rb32(s->pb);
-                info_size   = avio_rb32(s->pb);
+            case 0x4000:
+                info_offset = read32(s);
+                info_size   = read32(s);
                 break;
-            case 0x40010000:
+            case 0x4001:
                 avio_skip(s->pb, 4); // seek offset
                 avio_skip(s->pb, 4); // seek size
                 break;
-            case 0x40020000:
-                data_offset = avio_rb32(s->pb);
-                avio_skip(s->pb, 4); //data_size = avio_rb32(s->pb);
+            case 0x4002:
+                data_offset = read32(s);
+                avio_skip(s->pb, 4); //data_size = read32(s);
                 break;
-            case 0x40030000:
+            case 0x4003:
                 avio_skip(s->pb, 4); // REGN offset
                 avio_skip(s->pb, 4); // REGN size
                 break;
@@ -148,15 +168,15 @@ static int read_header(AVFormatContext *s)
             return AVERROR_INVALIDDATA;
     }
 
-    size = avio_rb32(s->pb);
+    size = read32(s);
     if (size < 192)
         return AVERROR_INVALIDDATA;
     avio_skip(s->pb, 4); // unknown
-    h1offset = avio_rb32(s->pb);
+    h1offset = read32(s);
     if (h1offset > size)
         return AVERROR_INVALIDDATA;
     avio_skip(s->pb, 12);
-    toffset = avio_rb32(s->pb) + 16LL;
+    toffset = read32(s) + 16LL;
     if (toffset > size)
         return AVERROR_INVALIDDATA;
 
@@ -166,7 +186,9 @@ static int read_header(AVFormatContext *s)
     switch (codec) {
     case 0: codec = AV_CODEC_ID_PCM_S8_PLANAR;    break;
     case 1: codec = AV_CODEC_ID_PCM_S16BE_PLANAR; break;
-    case 2: codec = AV_CODEC_ID_ADPCM_THP;        break;
+    case 2: codec = b->little_endian ?
+                    AV_CODEC_ID_ADPCM_THP_LE :
+                    AV_CODEC_ID_ADPCM_THP;        break;
     default:
         avpriv_request_sample(s, "codec %d", codec);
         return AVERROR_PATCHWELCOME;
@@ -179,10 +201,8 @@ static int read_header(AVFormatContext *s)
         return AVERROR_INVALIDDATA;
 
     avio_skip(s->pb, 1); // padding
-    if (b->bfstm)
-        avio_skip(s->pb, 2); // padding
 
-    st->codec->sample_rate = avio_rb16(s->pb);
+    st->codec->sample_rate = b->bfstm ? read32(s) : read16(s);
     if (!st->codec->sample_rate)
         return AVERROR_INVALIDDATA;
 
@@ -190,25 +210,25 @@ static int read_header(AVFormatContext *s)
         avio_skip(s->pb, 2); // padding
     avio_skip(s->pb, 4); // loop start sample
     st->start_time = 0;
-    st->duration = avio_rb32(s->pb);
+    st->duration = read32(s);
     avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate);
 
     if (!b->bfstm)
-        start = avio_rb32(s->pb);
+        start = read32(s);
     b->current_block = 0;
-    b->block_count = avio_rb32(s->pb);
+    b->block_count = read32(s);
     if (b->block_count > UINT16_MAX) {
         av_log(s, AV_LOG_WARNING, "too many blocks: %u\n", b->block_count);
         return AVERROR_INVALIDDATA;
     }
 
-    b->block_size = avio_rb32(s->pb);
+    b->block_size = read32(s);
     if (b->block_size > UINT16_MAX / st->codec->channels)
         return AVERROR_INVALIDDATA;
     b->block_size *= st->codec->channels;
 
-    b->samples_per_block = avio_rb32(s->pb);
-    b->last_block_used_bytes = avio_rb32(s->pb);
+    b->samples_per_block = read32(s);
+    b->last_block_used_bytes = read32(s);
     if (b->last_block_used_bytes > UINT16_MAX / st->codec->channels)
         return AVERROR_INVALIDDATA;
     b->last_block_used_bytes *= st->codec->channels;
@@ -216,14 +236,14 @@ static int read_header(AVFormatContext *s)
     avio_skip(s->pb, 4); // last block samples
     avio_skip(s->pb, 4); // last block size
 
-    if (codec == AV_CODEC_ID_ADPCM_THP) {
+    if (codec == AV_CODEC_ID_ADPCM_THP || codec == AV_CODEC_ID_ADPCM_THP_LE) {
         int ch;
 
         avio_skip(s->pb, pos + toffset - avio_tell(s->pb));
         if (!b->bfstm)
-            toffset = avio_rb32(s->pb) + 16LL;
+            toffset = read32(s) + 16LL;
         else
-            toffset = toffset + avio_rb32(s->pb) + st->codec->channels * 8 - 8;
+            toffset = toffset + read32(s) + st->codec->channels * 8 - 8;
         if (toffset > size)
             return AVERROR_INVALIDDATA;
 
@@ -261,7 +281,7 @@ static int read_header(AVFormatContext *s)
 
     while (!avio_feof(s->pb)) {
         chunk = avio_rl32(s->pb);
-        size  = avio_rb32(s->pb);
+        size  = read32(s);
         if (size < 8) {
             ret = AVERROR_INVALIDDATA;
             goto fail;
@@ -269,7 +289,8 @@ static int read_header(AVFormatContext *s)
         size -= 8;
         switch (chunk) {
         case MKTAG('A','D','P','C'):
-            if (codec != AV_CODEC_ID_ADPCM_THP)
+            if (codec != AV_CODEC_ID_ADPCM_THP &&
+                codec != AV_CODEC_ID_ADPCM_THP_LE)
                 goto skip;
 
             asize = b->block_count * st->codec->channels * 4;
@@ -292,7 +313,9 @@ static int read_header(AVFormatContext *s)
             break;
         case MKTAG('D','A','T','A'):
             if ((start < avio_tell(s->pb)) ||
-                (!b->adpc && codec == AV_CODEC_ID_ADPCM_THP && !b->bfstm)) {
+                (!b->adpc && (codec == AV_CODEC_ID_ADPCM_THP ||
+                              codec == AV_CODEC_ID_ADPCM_THP_LE)
+                          && !b->bfstm)) {
                 ret = AVERROR_INVALIDDATA;
                 goto fail;
             }
@@ -335,7 +358,8 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt)
         return AVERROR_EOF;
     }
 
-    if (codec->codec_id == AV_CODEC_ID_ADPCM_THP && !codec->extradata) {
+    if ((codec->codec_id == AV_CODEC_ID_ADPCM_THP ||
+         codec->codec_id == AV_CODEC_ID_ADPCM_THP_LE) && !codec->extradata) {
         uint8_t *dst;
 
         if (av_new_packet(pkt, 8 + (32 + 4) * codec->channels + size) < 0)
@@ -382,5 +406,5 @@ AVInputFormat ff_bfstm_demuxer = {
     .read_header    = read_header,
     .read_packet    = read_packet,
     .read_close     = read_close,
-    .extensions     = "bfstm",
+    .extensions     = "bfstm,bcstm",
 };