1cabdff1aSopenharmony_ci/*
2cabdff1aSopenharmony_ci * Rayman 2 APM (De)muxer
3cabdff1aSopenharmony_ci *
4cabdff1aSopenharmony_ci * Copyright (C) 2020 Zane van Iperen (zane@zanevaniperen.com)
5cabdff1aSopenharmony_ci *
6cabdff1aSopenharmony_ci * This file is part of FFmpeg.
7cabdff1aSopenharmony_ci *
8cabdff1aSopenharmony_ci * FFmpeg is free software; you can redistribute it and/or
9cabdff1aSopenharmony_ci * modify it under the terms of the GNU Lesser General Public
10cabdff1aSopenharmony_ci * License as published by the Free Software Foundation; either
11cabdff1aSopenharmony_ci * version 2.1 of the License, or (at your option) any later version.
12cabdff1aSopenharmony_ci *
13cabdff1aSopenharmony_ci * FFmpeg is distributed in the hope that it will be useful,
14cabdff1aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
15cabdff1aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16cabdff1aSopenharmony_ci * Lesser General Public License for more details.
17cabdff1aSopenharmony_ci *
18cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public
19cabdff1aSopenharmony_ci * License along with FFmpeg; if not, write to the Free Software
20cabdff1aSopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21cabdff1aSopenharmony_ci */
22cabdff1aSopenharmony_ci
23cabdff1aSopenharmony_ci#include "config_components.h"
24cabdff1aSopenharmony_ci
25cabdff1aSopenharmony_ci#include "avformat.h"
26cabdff1aSopenharmony_ci#include "internal.h"
27cabdff1aSopenharmony_ci#include "rawenc.h"
28cabdff1aSopenharmony_ci#include "libavutil/channel_layout.h"
29cabdff1aSopenharmony_ci#include "libavutil/internal.h"
30cabdff1aSopenharmony_ci#include "libavutil/intreadwrite.h"
31cabdff1aSopenharmony_ci
32cabdff1aSopenharmony_ci#define APM_FILE_HEADER_SIZE    20
33cabdff1aSopenharmony_ci#define APM_FILE_EXTRADATA_SIZE 80
34cabdff1aSopenharmony_ci#define APM_EXTRADATA_SIZE      28
35cabdff1aSopenharmony_ci
36cabdff1aSopenharmony_ci#define APM_MAX_READ_SIZE       4096
37cabdff1aSopenharmony_ci
38cabdff1aSopenharmony_ci#define APM_TAG_CODEC           0x2000
39cabdff1aSopenharmony_ci#define APM_TAG_VS12            MKTAG('v', 's', '1', '2')
40cabdff1aSopenharmony_ci#define APM_TAG_DATA            MKTAG('D', 'A', 'T', 'A')
41cabdff1aSopenharmony_ci
42cabdff1aSopenharmony_citypedef struct APMState {
43cabdff1aSopenharmony_ci    int32_t     has_saved;
44cabdff1aSopenharmony_ci    int32_t     predictor_r;
45cabdff1aSopenharmony_ci    int32_t     step_index_r;
46cabdff1aSopenharmony_ci    int32_t     saved_r;
47cabdff1aSopenharmony_ci    int32_t     predictor_l;
48cabdff1aSopenharmony_ci    int32_t     step_index_l;
49cabdff1aSopenharmony_ci    int32_t     saved_l;
50cabdff1aSopenharmony_ci} APMState;
51cabdff1aSopenharmony_ci
52cabdff1aSopenharmony_citypedef struct APMExtraData {
53cabdff1aSopenharmony_ci    uint32_t    magic;
54cabdff1aSopenharmony_ci    uint32_t    file_size;
55cabdff1aSopenharmony_ci    uint32_t    data_size;
56cabdff1aSopenharmony_ci    uint32_t    unk1;
57cabdff1aSopenharmony_ci    uint32_t    unk2;
58cabdff1aSopenharmony_ci    APMState    state;
59cabdff1aSopenharmony_ci    uint32_t    unk3[7];
60cabdff1aSopenharmony_ci    uint32_t    data;
61cabdff1aSopenharmony_ci} APMExtraData;
62cabdff1aSopenharmony_ci
63cabdff1aSopenharmony_ci#if CONFIG_APM_DEMUXER
64cabdff1aSopenharmony_cistatic void apm_parse_extradata(APMExtraData *ext, const uint8_t *buf)
65cabdff1aSopenharmony_ci{
66cabdff1aSopenharmony_ci    ext->magic              = AV_RL32(buf + 0);
67cabdff1aSopenharmony_ci    ext->file_size          = AV_RL32(buf + 4);
68cabdff1aSopenharmony_ci    ext->data_size          = AV_RL32(buf + 8);
69cabdff1aSopenharmony_ci    ext->unk1               = AV_RL32(buf + 12);
70cabdff1aSopenharmony_ci    ext->unk2               = AV_RL32(buf + 16);
71cabdff1aSopenharmony_ci
72cabdff1aSopenharmony_ci    ext->state.has_saved    = AV_RL32(buf + 20);
73cabdff1aSopenharmony_ci    ext->state.predictor_r  = AV_RL32(buf + 24);
74cabdff1aSopenharmony_ci    ext->state.step_index_r = AV_RL32(buf + 28);
75cabdff1aSopenharmony_ci    ext->state.saved_r      = AV_RL32(buf + 32);
76cabdff1aSopenharmony_ci    ext->state.predictor_l  = AV_RL32(buf + 36);
77cabdff1aSopenharmony_ci    ext->state.step_index_l = AV_RL32(buf + 40);
78cabdff1aSopenharmony_ci    ext->state.saved_l      = AV_RL32(buf + 44);
79cabdff1aSopenharmony_ci
80cabdff1aSopenharmony_ci    for (int i = 0; i < FF_ARRAY_ELEMS(ext->unk3); i++)
81cabdff1aSopenharmony_ci        ext->unk3[i]        = AV_RL32(buf + 48 + (i * 4));
82cabdff1aSopenharmony_ci
83cabdff1aSopenharmony_ci    ext->data               = AV_RL32(buf + 76);
84cabdff1aSopenharmony_ci}
85cabdff1aSopenharmony_ci
86cabdff1aSopenharmony_cistatic int apm_probe(const AVProbeData *p)
87cabdff1aSopenharmony_ci{
88cabdff1aSopenharmony_ci    if (AV_RL16(p->buf) != APM_TAG_CODEC)
89cabdff1aSopenharmony_ci        return 0;
90cabdff1aSopenharmony_ci
91cabdff1aSopenharmony_ci    if (p->buf_size < 100)
92cabdff1aSopenharmony_ci        return 0;
93cabdff1aSopenharmony_ci
94cabdff1aSopenharmony_ci    if (AV_RL32(p->buf + 20) != APM_TAG_VS12)
95cabdff1aSopenharmony_ci        return 0;
96cabdff1aSopenharmony_ci
97cabdff1aSopenharmony_ci    if (AV_RL32(p->buf + 96) != APM_TAG_DATA)
98cabdff1aSopenharmony_ci        return 0;
99cabdff1aSopenharmony_ci
100cabdff1aSopenharmony_ci    return AVPROBE_SCORE_MAX - 1;
101cabdff1aSopenharmony_ci}
102cabdff1aSopenharmony_ci
103cabdff1aSopenharmony_cistatic int apm_read_header(AVFormatContext *s)
104cabdff1aSopenharmony_ci{
105cabdff1aSopenharmony_ci    int64_t ret;
106cabdff1aSopenharmony_ci    AVStream *st;
107cabdff1aSopenharmony_ci    APMExtraData extradata;
108cabdff1aSopenharmony_ci    AVCodecParameters *par;
109cabdff1aSopenharmony_ci    uint8_t buf[APM_FILE_EXTRADATA_SIZE];
110cabdff1aSopenharmony_ci    int channels;
111cabdff1aSopenharmony_ci
112cabdff1aSopenharmony_ci    if (!(st = avformat_new_stream(s, NULL)))
113cabdff1aSopenharmony_ci        return AVERROR(ENOMEM);
114cabdff1aSopenharmony_ci
115cabdff1aSopenharmony_ci    /*
116cabdff1aSopenharmony_ci     * This is 98% a WAVEFORMATEX, but there's something screwy with the extradata
117cabdff1aSopenharmony_ci     * that ff_get_wav_header() can't (and shouldn't) handle properly.
118cabdff1aSopenharmony_ci     */
119cabdff1aSopenharmony_ci    if (avio_rl16(s->pb) != APM_TAG_CODEC)
120cabdff1aSopenharmony_ci        return AVERROR_INVALIDDATA;
121cabdff1aSopenharmony_ci
122cabdff1aSopenharmony_ci    par = st->codecpar;
123cabdff1aSopenharmony_ci    channels                   = avio_rl16(s->pb);
124cabdff1aSopenharmony_ci    par->sample_rate           = avio_rl32(s->pb);
125cabdff1aSopenharmony_ci
126cabdff1aSopenharmony_ci    /* Skip the bitrate, it's usually wrong anyway. */
127cabdff1aSopenharmony_ci    if ((ret = avio_skip(s->pb, 4)) < 0)
128cabdff1aSopenharmony_ci        return ret;
129cabdff1aSopenharmony_ci
130cabdff1aSopenharmony_ci    par->block_align           = avio_rl16(s->pb);
131cabdff1aSopenharmony_ci    par->bits_per_coded_sample = avio_rl16(s->pb);
132cabdff1aSopenharmony_ci
133cabdff1aSopenharmony_ci    if (avio_rl32(s->pb) != APM_FILE_EXTRADATA_SIZE)
134cabdff1aSopenharmony_ci        return AVERROR_INVALIDDATA;
135cabdff1aSopenharmony_ci
136cabdff1aSopenharmony_ci    /* 8 = bits per sample * max channels */
137cabdff1aSopenharmony_ci    if (par->sample_rate > (INT_MAX / 8))
138cabdff1aSopenharmony_ci        return AVERROR_INVALIDDATA;
139cabdff1aSopenharmony_ci
140cabdff1aSopenharmony_ci    if (par->bits_per_coded_sample != 4)
141cabdff1aSopenharmony_ci        return AVERROR_INVALIDDATA;
142cabdff1aSopenharmony_ci
143cabdff1aSopenharmony_ci    if (channels > 2 || channels == 0)
144cabdff1aSopenharmony_ci        return AVERROR_INVALIDDATA;
145cabdff1aSopenharmony_ci
146cabdff1aSopenharmony_ci    av_channel_layout_default(&par->ch_layout, channels);
147cabdff1aSopenharmony_ci    par->codec_type            = AVMEDIA_TYPE_AUDIO;
148cabdff1aSopenharmony_ci    par->codec_id              = AV_CODEC_ID_ADPCM_IMA_APM;
149cabdff1aSopenharmony_ci    par->format                = AV_SAMPLE_FMT_S16;
150cabdff1aSopenharmony_ci    par->bit_rate              = par->ch_layout.nb_channels *
151cabdff1aSopenharmony_ci                                 (int64_t)par->sample_rate *
152cabdff1aSopenharmony_ci                                 par->bits_per_coded_sample;
153cabdff1aSopenharmony_ci
154cabdff1aSopenharmony_ci    if ((ret = avio_read(s->pb, buf, APM_FILE_EXTRADATA_SIZE)) < 0)
155cabdff1aSopenharmony_ci        return ret;
156cabdff1aSopenharmony_ci    else if (ret != APM_FILE_EXTRADATA_SIZE)
157cabdff1aSopenharmony_ci        return AVERROR(EIO);
158cabdff1aSopenharmony_ci
159cabdff1aSopenharmony_ci    apm_parse_extradata(&extradata, buf);
160cabdff1aSopenharmony_ci
161cabdff1aSopenharmony_ci    if (extradata.magic != APM_TAG_VS12 || extradata.data != APM_TAG_DATA)
162cabdff1aSopenharmony_ci        return AVERROR_INVALIDDATA;
163cabdff1aSopenharmony_ci
164cabdff1aSopenharmony_ci    if (extradata.state.has_saved) {
165cabdff1aSopenharmony_ci        avpriv_request_sample(s, "Saved Samples");
166cabdff1aSopenharmony_ci        return AVERROR_PATCHWELCOME;
167cabdff1aSopenharmony_ci    }
168cabdff1aSopenharmony_ci
169cabdff1aSopenharmony_ci    if ((ret = ff_alloc_extradata(par, APM_EXTRADATA_SIZE)) < 0)
170cabdff1aSopenharmony_ci        return ret;
171cabdff1aSopenharmony_ci
172cabdff1aSopenharmony_ci    /* Use the entire state as extradata. */
173cabdff1aSopenharmony_ci    memcpy(par->extradata, buf + 20, APM_EXTRADATA_SIZE);
174cabdff1aSopenharmony_ci
175cabdff1aSopenharmony_ci    avpriv_set_pts_info(st, 64, 1, par->sample_rate);
176cabdff1aSopenharmony_ci    st->start_time  = 0;
177cabdff1aSopenharmony_ci    st->duration    = extradata.data_size *
178cabdff1aSopenharmony_ci                      (8 / par->bits_per_coded_sample) /
179cabdff1aSopenharmony_ci                      par->ch_layout.nb_channels;
180cabdff1aSopenharmony_ci    return 0;
181cabdff1aSopenharmony_ci}
182cabdff1aSopenharmony_ci
183cabdff1aSopenharmony_cistatic int apm_read_packet(AVFormatContext *s, AVPacket *pkt)
184cabdff1aSopenharmony_ci{
185cabdff1aSopenharmony_ci    int ret;
186cabdff1aSopenharmony_ci    AVCodecParameters *par = s->streams[0]->codecpar;
187cabdff1aSopenharmony_ci
188cabdff1aSopenharmony_ci    /*
189cabdff1aSopenharmony_ci     * For future reference: if files with the `has_saved` field set ever
190cabdff1aSopenharmony_ci     * surface, `saved_l`, and `saved_r` will each contain 8 "saved" samples
191cabdff1aSopenharmony_ci     * that should be sent to the decoder before the actual data.
192cabdff1aSopenharmony_ci     */
193cabdff1aSopenharmony_ci
194cabdff1aSopenharmony_ci    if ((ret = av_get_packet(s->pb, pkt, APM_MAX_READ_SIZE)) < 0)
195cabdff1aSopenharmony_ci        return ret;
196cabdff1aSopenharmony_ci
197cabdff1aSopenharmony_ci    pkt->flags          &= ~AV_PKT_FLAG_CORRUPT;
198cabdff1aSopenharmony_ci    pkt->stream_index   = 0;
199cabdff1aSopenharmony_ci    pkt->duration       = ret * (8 / par->bits_per_coded_sample) / par->ch_layout.nb_channels;
200cabdff1aSopenharmony_ci
201cabdff1aSopenharmony_ci    return 0;
202cabdff1aSopenharmony_ci}
203cabdff1aSopenharmony_ci
204cabdff1aSopenharmony_ciconst AVInputFormat ff_apm_demuxer = {
205cabdff1aSopenharmony_ci    .name           = "apm",
206cabdff1aSopenharmony_ci    .long_name      = NULL_IF_CONFIG_SMALL("Ubisoft Rayman 2 APM"),
207cabdff1aSopenharmony_ci    .read_probe     = apm_probe,
208cabdff1aSopenharmony_ci    .read_header    = apm_read_header,
209cabdff1aSopenharmony_ci    .read_packet    = apm_read_packet
210cabdff1aSopenharmony_ci};
211cabdff1aSopenharmony_ci#endif
212cabdff1aSopenharmony_ci
213cabdff1aSopenharmony_ci#if CONFIG_APM_MUXER
214cabdff1aSopenharmony_cistatic int apm_write_init(AVFormatContext *s)
215cabdff1aSopenharmony_ci{
216cabdff1aSopenharmony_ci    AVCodecParameters *par;
217cabdff1aSopenharmony_ci
218cabdff1aSopenharmony_ci    if (s->nb_streams != 1) {
219cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "APM files have exactly one stream\n");
220cabdff1aSopenharmony_ci        return AVERROR(EINVAL);
221cabdff1aSopenharmony_ci    }
222cabdff1aSopenharmony_ci
223cabdff1aSopenharmony_ci    par = s->streams[0]->codecpar;
224cabdff1aSopenharmony_ci
225cabdff1aSopenharmony_ci    if (par->codec_id != AV_CODEC_ID_ADPCM_IMA_APM) {
226cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "%s codec not supported\n",
227cabdff1aSopenharmony_ci               avcodec_get_name(par->codec_id));
228cabdff1aSopenharmony_ci        return AVERROR(EINVAL);
229cabdff1aSopenharmony_ci    }
230cabdff1aSopenharmony_ci
231cabdff1aSopenharmony_ci    if (par->ch_layout.nb_channels > 2) {
232cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "APM files only support up to 2 channels\n");
233cabdff1aSopenharmony_ci        return AVERROR(EINVAL);
234cabdff1aSopenharmony_ci    }
235cabdff1aSopenharmony_ci
236cabdff1aSopenharmony_ci    if (par->sample_rate > (INT_MAX / 8)) {
237cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "Sample rate too large\n");
238cabdff1aSopenharmony_ci        return AVERROR(EINVAL);
239cabdff1aSopenharmony_ci    }
240cabdff1aSopenharmony_ci
241cabdff1aSopenharmony_ci    if (par->extradata_size != APM_EXTRADATA_SIZE) {
242cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "Invalid/missing extradata\n");
243cabdff1aSopenharmony_ci        return AVERROR(EINVAL);
244cabdff1aSopenharmony_ci    }
245cabdff1aSopenharmony_ci
246cabdff1aSopenharmony_ci    if (!(s->pb->seekable & AVIO_SEEKABLE_NORMAL)) {
247cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "Stream not seekable, unable to write output file\n");
248cabdff1aSopenharmony_ci        return AVERROR(EINVAL);
249cabdff1aSopenharmony_ci    }
250cabdff1aSopenharmony_ci
251cabdff1aSopenharmony_ci    return 0;
252cabdff1aSopenharmony_ci}
253cabdff1aSopenharmony_ci
254cabdff1aSopenharmony_cistatic int apm_write_header(AVFormatContext *s)
255cabdff1aSopenharmony_ci{
256cabdff1aSopenharmony_ci    uint8_t buf[APM_FILE_EXTRADATA_SIZE] = { 0 };
257cabdff1aSopenharmony_ci    AVCodecParameters *par = s->streams[0]->codecpar;
258cabdff1aSopenharmony_ci
259cabdff1aSopenharmony_ci    /*
260cabdff1aSopenharmony_ci     * Bodge a WAVEFORMATEX manually, ff_put_wav_header() can't
261cabdff1aSopenharmony_ci     * be used because of the extra 2 bytes.
262cabdff1aSopenharmony_ci     */
263cabdff1aSopenharmony_ci    avio_wl16(s->pb, APM_TAG_CODEC);
264cabdff1aSopenharmony_ci    avio_wl16(s->pb, par->ch_layout.nb_channels);
265cabdff1aSopenharmony_ci    avio_wl32(s->pb, par->sample_rate);
266cabdff1aSopenharmony_ci    /* This is the wrong calculation, but it's what the orginal files have. */
267cabdff1aSopenharmony_ci    avio_wl32(s->pb, par->sample_rate * par->ch_layout.nb_channels * 2);
268cabdff1aSopenharmony_ci    avio_wl16(s->pb, par->block_align);
269cabdff1aSopenharmony_ci    avio_wl16(s->pb, par->bits_per_coded_sample);
270cabdff1aSopenharmony_ci    avio_wl32(s->pb, APM_FILE_EXTRADATA_SIZE);
271cabdff1aSopenharmony_ci
272cabdff1aSopenharmony_ci    /*
273cabdff1aSopenharmony_ci     * Build the extradata. Assume the codec's given us correct data.
274cabdff1aSopenharmony_ci     * File and data sizes are fixed later.
275cabdff1aSopenharmony_ci     */
276cabdff1aSopenharmony_ci    AV_WL32(buf +  0, APM_TAG_VS12); /* magic */
277cabdff1aSopenharmony_ci    AV_WL32(buf + 12, 0xFFFFFFFF);   /* unk1  */
278cabdff1aSopenharmony_ci    memcpy( buf + 20, par->extradata, APM_EXTRADATA_SIZE);
279cabdff1aSopenharmony_ci    AV_WL32(buf + 76, APM_TAG_DATA); /* data */
280cabdff1aSopenharmony_ci
281cabdff1aSopenharmony_ci    avio_write(s->pb, buf, APM_FILE_EXTRADATA_SIZE);
282cabdff1aSopenharmony_ci    return 0;
283cabdff1aSopenharmony_ci}
284cabdff1aSopenharmony_ci
285cabdff1aSopenharmony_cistatic int apm_write_trailer(AVFormatContext *s)
286cabdff1aSopenharmony_ci{
287cabdff1aSopenharmony_ci    int64_t file_size, data_size;
288cabdff1aSopenharmony_ci
289cabdff1aSopenharmony_ci    file_size = avio_tell(s->pb);
290cabdff1aSopenharmony_ci    data_size = file_size - (APM_FILE_HEADER_SIZE + APM_FILE_EXTRADATA_SIZE);
291cabdff1aSopenharmony_ci
292cabdff1aSopenharmony_ci    if (file_size >= UINT32_MAX) {
293cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR,
294cabdff1aSopenharmony_ci               "Filesize %"PRId64" invalid for APM, output file will be broken\n",
295cabdff1aSopenharmony_ci               file_size);
296cabdff1aSopenharmony_ci        return AVERROR(ERANGE);
297cabdff1aSopenharmony_ci    }
298cabdff1aSopenharmony_ci
299cabdff1aSopenharmony_ci    avio_seek(s->pb, 24, SEEK_SET);
300cabdff1aSopenharmony_ci    avio_wl32(s->pb, (uint32_t)file_size);
301cabdff1aSopenharmony_ci    avio_wl32(s->pb, (uint32_t)data_size);
302cabdff1aSopenharmony_ci
303cabdff1aSopenharmony_ci    return 0;
304cabdff1aSopenharmony_ci}
305cabdff1aSopenharmony_ci
306cabdff1aSopenharmony_ciconst AVOutputFormat ff_apm_muxer = {
307cabdff1aSopenharmony_ci    .name           = "apm",
308cabdff1aSopenharmony_ci    .long_name      = NULL_IF_CONFIG_SMALL("Ubisoft Rayman 2 APM"),
309cabdff1aSopenharmony_ci    .extensions     = "apm",
310cabdff1aSopenharmony_ci    .audio_codec    = AV_CODEC_ID_ADPCM_IMA_APM,
311cabdff1aSopenharmony_ci    .video_codec    = AV_CODEC_ID_NONE,
312cabdff1aSopenharmony_ci    .init           = apm_write_init,
313cabdff1aSopenharmony_ci    .write_header   = apm_write_header,
314cabdff1aSopenharmony_ci    .write_packet   = ff_raw_write_packet,
315cabdff1aSopenharmony_ci    .write_trailer  = apm_write_trailer
316cabdff1aSopenharmony_ci};
317cabdff1aSopenharmony_ci#endif
318