1 | /*
|
---|
2 | * Sierra SOL decoder
|
---|
3 | * Copyright Konstantin Shishkov.
|
---|
4 | *
|
---|
5 | * This library is free software; you can redistribute it and/or
|
---|
6 | * modify it under the terms of the GNU Lesser General Public
|
---|
7 | * License as published by the Free Software Foundation; either
|
---|
8 | * version 2 of the License, or (at your option) any later version.
|
---|
9 | *
|
---|
10 | * This library is distributed in the hope that it will be useful,
|
---|
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
---|
13 | * Lesser General Public License for more details.
|
---|
14 | *
|
---|
15 | * You should have received a copy of the GNU Lesser General Public
|
---|
16 | * License along with this library; if not, write to the Free Software
|
---|
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
---|
18 | */
|
---|
19 |
|
---|
20 | /*
|
---|
21 | * Based on documents from Game Audio Player and own research
|
---|
22 | */
|
---|
23 |
|
---|
24 | #include "avformat.h"
|
---|
25 | #include "allformats.h"
|
---|
26 | #include "avi.h"
|
---|
27 | #include "bswap.h"
|
---|
28 |
|
---|
29 | /* if we don't know the size in advance */
|
---|
30 | #define AU_UNKOWN_SIZE ((uint32_t)(~0))
|
---|
31 |
|
---|
32 | static int sol_probe(AVProbeData *p)
|
---|
33 | {
|
---|
34 | /* check file header */
|
---|
35 | uint16_t magic;
|
---|
36 | if (p->buf_size <= 14)
|
---|
37 | return 0;
|
---|
38 | magic=le2me_16(*((uint16_t*)p->buf));
|
---|
39 | if ((magic == 0x0B8D || magic == 0x0C0D || magic == 0x0C8D) &&
|
---|
40 | p->buf[2] == 'S' && p->buf[3] == 'O' &&
|
---|
41 | p->buf[4] == 'L' && p->buf[5] == 0)
|
---|
42 | return AVPROBE_SCORE_MAX;
|
---|
43 | else
|
---|
44 | return 0;
|
---|
45 | }
|
---|
46 |
|
---|
47 | #define SOL_DPCM 1
|
---|
48 | #define SOL_16BIT 4
|
---|
49 | #define SOL_STEREO 16
|
---|
50 |
|
---|
51 | static int sol_codec_id(int magic, int type)
|
---|
52 | {
|
---|
53 | if (magic == 0x0B8D)
|
---|
54 | {
|
---|
55 | if (type & SOL_DPCM) return CODEC_ID_SOL_DPCM;
|
---|
56 | else return CODEC_ID_PCM_U8;
|
---|
57 | }
|
---|
58 | if (type & SOL_DPCM)
|
---|
59 | {
|
---|
60 | if (type & SOL_16BIT) return CODEC_ID_SOL_DPCM;
|
---|
61 | else if (magic == 0x0C8D) return CODEC_ID_SOL_DPCM;
|
---|
62 | else return CODEC_ID_SOL_DPCM;
|
---|
63 | }
|
---|
64 | if (type & SOL_16BIT) return CODEC_ID_PCM_S16LE;
|
---|
65 | return CODEC_ID_PCM_U8;
|
---|
66 | }
|
---|
67 |
|
---|
68 | static int sol_codec_type(int magic, int type)
|
---|
69 | {
|
---|
70 | if (magic == 0x0B8D) return 1;//SOL_DPCM_OLD;
|
---|
71 | if (type & SOL_DPCM)
|
---|
72 | {
|
---|
73 | if (type & SOL_16BIT) return 3;//SOL_DPCM_NEW16;
|
---|
74 | else if (magic == 0x0C8D) return 1;//SOL_DPCM_OLD;
|
---|
75 | else return 2;//SOL_DPCM_NEW8;
|
---|
76 | }
|
---|
77 | return -1;
|
---|
78 | }
|
---|
79 |
|
---|
80 | static int sol_channels(int magic, int type)
|
---|
81 | {
|
---|
82 | if (magic == 0x0B8D || !(type & SOL_STEREO)) return 1;
|
---|
83 | return 2;
|
---|
84 | }
|
---|
85 |
|
---|
86 | static int sol_read_header(AVFormatContext *s,
|
---|
87 | AVFormatParameters *ap)
|
---|
88 | {
|
---|
89 | int size;
|
---|
90 | unsigned int magic,tag;
|
---|
91 | ByteIOContext *pb = &s->pb;
|
---|
92 | unsigned int id, codec, channels, rate, type;
|
---|
93 | AVStream *st;
|
---|
94 |
|
---|
95 | /* check ".snd" header */
|
---|
96 | magic = get_le16(pb);
|
---|
97 | tag = get_le32(pb);
|
---|
98 | if (tag != MKTAG('S', 'O', 'L', 0))
|
---|
99 | return -1;
|
---|
100 | rate = get_le16(pb);
|
---|
101 | type = get_byte(pb);
|
---|
102 | size = get_le32(pb);
|
---|
103 | if (magic != 0x0B8D)
|
---|
104 | get_byte(pb); /* newer SOLs contain padding byte */
|
---|
105 |
|
---|
106 | codec = sol_codec_id(magic, type);
|
---|
107 | channels = sol_channels(magic, type);
|
---|
108 |
|
---|
109 | if (codec == CODEC_ID_SOL_DPCM)
|
---|
110 | id = sol_codec_type(magic, type);
|
---|
111 | else id = 0;
|
---|
112 |
|
---|
113 | /* now we are ready: build format streams */
|
---|
114 | st = av_new_stream(s, 0);
|
---|
115 | if (!st)
|
---|
116 | return -1;
|
---|
117 | st->codec->codec_type = CODEC_TYPE_AUDIO;
|
---|
118 | st->codec->codec_tag = id;
|
---|
119 | st->codec->codec_id = codec;
|
---|
120 | st->codec->channels = channels;
|
---|
121 | st->codec->sample_rate = rate;
|
---|
122 | av_set_pts_info(st, 64, 1, rate);
|
---|
123 | return 0;
|
---|
124 | }
|
---|
125 |
|
---|
126 | #define MAX_SIZE 4096
|
---|
127 |
|
---|
128 | static int sol_read_packet(AVFormatContext *s,
|
---|
129 | AVPacket *pkt)
|
---|
130 | {
|
---|
131 | int ret;
|
---|
132 |
|
---|
133 | if (url_feof(&s->pb))
|
---|
134 | return -EIO;
|
---|
135 | ret= av_get_packet(&s->pb, pkt, MAX_SIZE);
|
---|
136 | pkt->stream_index = 0;
|
---|
137 |
|
---|
138 | /* note: we need to modify the packet size here to handle the last
|
---|
139 | packet */
|
---|
140 | pkt->size = ret;
|
---|
141 | return 0;
|
---|
142 | }
|
---|
143 |
|
---|
144 | static int sol_read_close(AVFormatContext *s)
|
---|
145 | {
|
---|
146 | return 0;
|
---|
147 | }
|
---|
148 |
|
---|
149 | static AVInputFormat sol_demuxer = {
|
---|
150 | "sol",
|
---|
151 | "Sierra SOL Format",
|
---|
152 | 0,
|
---|
153 | sol_probe,
|
---|
154 | sol_read_header,
|
---|
155 | sol_read_packet,
|
---|
156 | sol_read_close,
|
---|
157 | pcm_read_seek,
|
---|
158 | };
|
---|
159 |
|
---|
160 | int sol_init(void)
|
---|
161 | {
|
---|
162 | av_register_input_format(&sol_demuxer);
|
---|
163 | return 0;
|
---|
164 | }
|
---|