FFmpeg
Loading...
Searching...
No Matches
argo_cvg.c
Go to the documentation of this file.
1/*
2 * Argonaut Games CVG (de)muxer
3 *
4 * Copyright (C) 2021 Zane van Iperen (zane@zanevaniperen.com)
5 *
6 * This file is part of FFmpeg.
7 *
8 * FFmpeg is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * FFmpeg is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with FFmpeg; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23#include "config_components.h"
24
25#include "libavutil/avstring.h"
27#include "avformat.h"
28#include "avio_internal.h"
29#include "demux.h"
30#include "internal.h"
31#include "mux.h"
32#include "libavutil/opt.h"
34
35/*
36 * .CVG files are essentially PSX ADPCM wrapped with a size and checksum.
37 * Found in the PSX versions of the game.
38 */
39
40#define ARGO_CVG_HEADER_SIZE 12
41#define ARGO_CVG_BLOCK_ALIGN 0x10
42#define ARGO_CVG_NB_BLOCKS 32
43#define ARGO_CVG_SAMPLES_PER_BLOCK 28
44
45typedef struct ArgoCVGHeader {
46 uint32_t size; /*< File size -8 (this + trailing checksum) */
47 uint32_t loop; /*< Loop flag. */
48 uint32_t reverb; /*< Reverb flag. */
50
57
58typedef struct ArgoCVGMuxContext {
59 const AVClass *class;
61 int loop;
62 int reverb;
63 uint32_t checksum;
64 size_t size;
66
67#if CONFIG_ARGO_CVG_DEMUXER
68/* "Special" files that are played at a different rate. */
69// FILE(NAME, SIZE, LOOP, REVERB, CHECKSUM, SAMPLE_RATE)
70#define OVERRIDE_FILES(FILE) \
71 FILE(CRYS, 23592, 0, 1, 2495499, 88200) /* Beta */ \
72 FILE(REDCRY88, 38280, 0, 1, 4134848, 88200) /* Beta */ \
73 FILE(DANLOOP1, 54744, 1, 0, 5684641, 37800) /* Beta */ \
74 FILE(PICKUP88, 12904, 0, 1, 1348091, 48000) /* Beta */ \
75 FILE(SELECT1, 5080, 0, 1, 549987, 44100) /* Beta */ \
76
77#define MAX_FILENAME_SIZE(NAME, SIZE, LOOP, REVERB, CHECKSUM, SAMPLE_RATE) \
78 MAX_SIZE_BEFORE_ ## NAME, \
79 MAX_SIZE_UNTIL_ ## NAME ## _MINUS1 = FFMAX(sizeof(#NAME ".CVG"), MAX_SIZE_BEFORE_ ## NAME) - 1,
80enum {
81 OVERRIDE_FILES(MAX_FILENAME_SIZE)
82 MAX_OVERRIDE_FILENAME_SIZE
83};
84
85typedef struct ArgoCVGOverride {
86 const char name[MAX_OVERRIDE_FILENAME_SIZE];
87 ArgoCVGHeader header;
88 uint32_t checksum;
89 int sample_rate;
90} ArgoCVGOverride;
91
92#define FILE(NAME, SIZE, LOOP, REVERB, CHECKSUM, SAMPLE_RATE) \
93 { #NAME ".CVG", { SIZE, LOOP, REVERB }, CHECKSUM, SAMPLE_RATE },
94static const ArgoCVGOverride overrides[] = {
95 OVERRIDE_FILES(FILE)
96};
97
98static int argo_cvg_probe(const AVProbeData *p)
99{
100 ArgoCVGHeader cvg;
101
102 /*
103 * It's almost impossible to detect these files based
104 * on the header alone. File extension is (unfortunately)
105 * the best way forward.
106 */
107 if (!av_match_ext(p->filename, "cvg"))
108 return 0;
109
110 if (p->buf_size < ARGO_CVG_HEADER_SIZE)
111 return 0;
112
113 cvg.size = AV_RL32(p->buf + 0);
114 cvg.loop = AV_RL32(p->buf + 4);
115 cvg.reverb = AV_RL32(p->buf + 8);
116
117 if (cvg.size < 8)
118 return 0;
119
120 if (cvg.loop != 0 && cvg.loop != 1)
121 return 0;
122
123 if (cvg.reverb != 0 && cvg.reverb != 1)
124 return 0;
125
126 return AVPROBE_SCORE_MAX / 4 + 1;
127}
128
129static int argo_cvg_read_checksum(AVIOContext *pb, const ArgoCVGHeader *cvg, uint32_t *checksum)
130{
131 int ret;
132 uint8_t buf[4];
133
134 if (!(pb->seekable & AVIO_SEEKABLE_NORMAL)) {
135 *checksum = 0;
136 return 0;
137 }
138
139 if ((ret = avio_seek(pb, cvg->size + 4, SEEK_SET)) < 0)
140 return ret;
141
142 /* NB: Not using avio_rl32() because no error checking. */
143 if ((ret = ffio_read_size(pb, buf, sizeof(buf))) < 0)
144 return ret;
145
146 if ((ret = avio_seek(pb, ARGO_CVG_HEADER_SIZE, SEEK_SET)) < 0)
147 return ret;
148
149 *checksum = AV_RL32(buf);
150 return 0;
151}
152
153static int argo_cvg_read_header(AVFormatContext *s)
154{
155 int ret;
156 AVStream *st;
158 uint8_t buf[ARGO_CVG_HEADER_SIZE];
159 const char *filename = av_basename(s->url);
160 ArgoCVGDemuxContext *ctx = s->priv_data;
161
162 if (!(st = avformat_new_stream(s, NULL)))
163 return AVERROR(ENOMEM);
164
165 if ((ret = ffio_read_size(s->pb, buf, ARGO_CVG_HEADER_SIZE)) < 0)
166 return ret;
167
168 ctx->header.size = AV_RL32(buf + 0);
169 ctx->header.loop = AV_RL32(buf + 4);
170 ctx->header.reverb = AV_RL32(buf + 8);
171
172 if (ctx->header.size < 8)
173 return AVERROR_INVALIDDATA;
174
175 if ((ret = argo_cvg_read_checksum(s->pb, &ctx->header, &ctx->checksum)) < 0)
176 return ret;
177
178 if ((ret = av_dict_set_int(&st->metadata, "loop", ctx->header.loop, 0)) < 0)
179 return ret;
180
181 if ((ret = av_dict_set_int(&st->metadata, "reverb", ctx->header.reverb, 0)) < 0)
182 return ret;
183
184 if ((ret = av_dict_set_int(&st->metadata, "checksum", ctx->checksum, 0)) < 0)
185 return ret;
186
187 par = st->codecpar;
190 par->sample_rate = 22050;
191
192 for (size_t i = 0; i < FF_ARRAY_ELEMS(overrides); i++) {
193 const ArgoCVGOverride *ovr = overrides + i;
194 if (ovr->header.size != ctx->header.size ||
195 ovr->header.loop != ctx->header.loop ||
196 ovr->header.reverb != ctx->header.reverb ||
197 ovr->checksum != ctx->checksum ||
198 av_strcasecmp(filename, ovr->name) != 0)
199 continue;
200
201 av_log(s, AV_LOG_TRACE, "found override, name = %s\n", ovr->name);
202 par->sample_rate = ovr->sample_rate;
203 break;
204 }
205
207
208 par->bits_per_coded_sample = 4;
210 par->bit_rate = par->sample_rate * par->bits_per_coded_sample;
211
212 ctx->num_blocks = (ctx->header.size - 8) / ARGO_CVG_BLOCK_ALIGN;
213
214 av_log(s, AV_LOG_TRACE, "num blocks = %u\n", ctx->num_blocks);
215
216 avpriv_set_pts_info(st, 64, 1, par->sample_rate);
217
218 st->start_time = 0;
219 st->duration = ctx->num_blocks * ARGO_CVG_SAMPLES_PER_BLOCK;
220 st->nb_frames = ctx->num_blocks;
221 return 0;
222}
223
224static int argo_cvg_read_packet(AVFormatContext *s, AVPacket *pkt)
225{
226 int ret;
227 AVStream *st = s->streams[0];
228 ArgoCVGDemuxContext *ctx = s->priv_data;
229
230 if (ctx->blocks_read >= ctx->num_blocks)
231 return AVERROR_EOF;
232
233 ret = av_get_packet(s->pb, pkt, st->codecpar->block_align *
234 FFMIN(ARGO_CVG_NB_BLOCKS, ctx->num_blocks - ctx->blocks_read));
235
236 if (ret < 0)
237 return ret;
238
239 if (ret % st->codecpar->block_align != 0)
240 return AVERROR_INVALIDDATA;
241
242 pkt->stream_index = 0;
243 pkt->duration = ARGO_CVG_SAMPLES_PER_BLOCK * (ret / st->codecpar->block_align);
244 pkt->pts = ctx->blocks_read * ARGO_CVG_SAMPLES_PER_BLOCK;
245 pkt->flags &= ~AV_PKT_FLAG_CORRUPT;
246
247 ctx->blocks_read += ret / st->codecpar->block_align;
248
249 return 0;
250}
251
252static int argo_cvg_seek(AVFormatContext *s, int stream_index,
253 int64_t pts, int flags)
254{
255 int64_t ret;
256 ArgoCVGDemuxContext *ctx = s->priv_data;
257
258 if (pts != 0 || stream_index != 0)
259 return AVERROR(EINVAL);
260
261 if ((ret = avio_seek(s->pb, ARGO_CVG_HEADER_SIZE, SEEK_SET)) < 0)
262 return ret;
263
264 ctx->blocks_read = 0;
265 return 0;
266}
267
269 .p.name = "argo_cvg",
270 .p.long_name = NULL_IF_CONFIG_SMALL("Argonaut Games CVG"),
271 .priv_data_size = sizeof(ArgoCVGDemuxContext),
272 .read_probe = argo_cvg_probe,
273 .read_header = argo_cvg_read_header,
274 .read_packet = argo_cvg_read_packet,
275 .read_seek = argo_cvg_seek,
276};
277#endif
278
279#if CONFIG_ARGO_CVG_MUXER
280static int argo_cvg_write_init(AVFormatContext *s)
281{
282 ArgoCVGMuxContext *ctx = s->priv_data;
283 const AVCodecParameters *par = s->streams[0]->codecpar;
284
285 if (par->ch_layout.nb_channels != 1) {
286 av_log(s, AV_LOG_ERROR, "CVG files only support 1 channel\n");
287 return AVERROR(EINVAL);
288 }
289
291 return AVERROR(EINVAL);
292
293 if (!ctx->skip_rate_check && par->sample_rate != 22050) {
294 av_log(s, AV_LOG_ERROR, "Sample rate must be 22050\n");
295 return AVERROR(EINVAL);
296 }
297
298 if (!(s->pb->seekable & AVIO_SEEKABLE_NORMAL)) {
299 av_log(s, AV_LOG_ERROR, "Stream not seekable, unable to write output file\n");
300 return AVERROR(EINVAL);
301 }
302
303 return 0;
304}
305
306static int argo_cvg_write_header(AVFormatContext *s)
307{
308 ArgoCVGMuxContext *ctx = s->priv_data;
309
310 avio_wl32(s->pb, 0); /* Size, fixed later. */
311 avio_wl32(s->pb, !!ctx->loop);
312 avio_wl32(s->pb, !!ctx->reverb);
313
314 ctx->checksum = !!ctx->loop + !!ctx->reverb;
315 ctx->size = 8;
316 return 0;
317}
318
319static int argo_cvg_write_packet(AVFormatContext *s, AVPacket *pkt)
320{
321 ArgoCVGMuxContext *ctx = s->priv_data;
322 AVCodecParameters *par = s->streams[0]->codecpar;
323
324 if (pkt->size % par->block_align != 0)
325 return AVERROR_INVALIDDATA;
326
327 avio_write(s->pb, pkt->data, pkt->size);
328
329 ctx->size += pkt->size;
330
331 if (ctx->size > UINT32_MAX)
332 return AVERROR_INVALIDDATA;
333
334 for (int i = 0; i < pkt->size; i++)
335 ctx->checksum += pkt->data[i];
336
337 return 0;
338}
339
340static int argo_cvg_write_trailer(AVFormatContext *s)
341{
342 ArgoCVGMuxContext *ctx = s->priv_data;
343 int64_t ret;
344
345 ctx->checksum += (ctx->size & 255)
346 + ((ctx->size>> 8) & 255)
347 + ((ctx->size>>16) & 255)
348 + (ctx->size>>24);
349
350 av_log(s, AV_LOG_TRACE, "size = %zu\n", ctx->size);
351 av_log(s, AV_LOG_TRACE, "checksum = %u\n", ctx->checksum);
352
353 avio_wl32(s->pb, ctx->checksum);
354
355 if ((ret = avio_seek(s->pb, 0, SEEK_SET)) < 0)
356 return ret;
357
358 avio_wl32(s->pb, (uint32_t)ctx->size);
359 return 0;
360}
361
362static const AVOption argo_cvg_options[] = {
363 {
364 .name = "skip_rate_check",
365 .help = "skip sample rate check",
366 .offset = offsetof(ArgoCVGMuxContext, skip_rate_check),
367 .type = AV_OPT_TYPE_BOOL,
368 .default_val = {.i64 = 0},
369 .min = 0,
370 .max = 1,
372 },
373 {
374 .name = "loop",
375 .help = "set loop flag",
376 .offset = offsetof(ArgoCVGMuxContext, loop),
377 .type = AV_OPT_TYPE_BOOL,
378 .default_val = {.i64 = 0},
379 .min = 0,
380 .max = 1,
382 },
383 {
384 .name = "reverb",
385 .help = "set reverb flag",
386 .offset = offsetof(ArgoCVGMuxContext, reverb),
387 .type = AV_OPT_TYPE_BOOL,
388 .default_val = {.i64 = 1},
389 .min = 0,
390 .max = 1,
392 },
393 { NULL }
394};
395
396static const AVClass argo_cvg_muxer_class = {
397 .class_name = "argo_cvg_muxer",
398 .item_name = av_default_item_name,
399 .option = argo_cvg_options,
400 .version = LIBAVUTIL_VERSION_INT
401};
402
404 .p.name = "argo_cvg",
405 .p.long_name = NULL_IF_CONFIG_SMALL("Argonaut Games CVG"),
406 .p.extensions = "cvg",
407 .p.audio_codec = AV_CODEC_ID_ADPCM_PSX,
408 .p.video_codec = AV_CODEC_ID_NONE,
409 .p.subtitle_codec = AV_CODEC_ID_NONE,
410 .p.priv_class = &argo_cvg_muxer_class,
411 .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH |
413 .init = argo_cvg_write_init,
414 .write_header = argo_cvg_write_header,
415 .write_packet = argo_cvg_write_packet,
416 .write_trailer = argo_cvg_write_trailer,
417 .priv_data_size = sizeof(ArgoCVGMuxContext),
418};
419#endif
const FFInputFormat ff_argo_cvg_demuxer
const FFOutputFormat ff_argo_cvg_muxer
static AVFormatContext * ctx
#define ARGO_CVG_BLOCK_ALIGN
Definition argo_cvg.c:41
#define ARGO_CVG_NB_BLOCKS
Definition argo_cvg.c:42
#define ARGO_CVG_SAMPLES_PER_BLOCK
Definition argo_cvg.c:43
#define ARGO_CVG_HEADER_SIZE
Definition argo_cvg.c:40
void avpriv_set_pts_info(AVStream *st, int pts_wrap_bits, unsigned int pts_num, unsigned int pts_den)
Set the time base and wrapping info for a given stream.
Definition avformat.c:834
Main libavformat public API header.
#define AVPROBE_SCORE_MAX
maximum score
Definition avformat.h:483
int av_get_packet(AVIOContext *s, AVPacket *pkt, int size)
Allocate and read the payload of a packet and initialize its fields with default values.
Definition utils.c:98
int64_t avio_seek(AVIOContext *s, int64_t offset, int whence)
fseek() equivalent for AVIOContext.
Definition aviobuf.c:236
#define AVIO_SEEKABLE_NORMAL
Seeking works like for a local file.
Definition avio.h:41
void avio_wl32(AVIOContext *s, unsigned int val)
Definition aviobuf.c:360
void avio_write(AVIOContext *s, const unsigned char *buf, int size)
Definition aviobuf.c:206
int ffio_read_size(AVIOContext *s, unsigned char *buf, int size)
Read size bytes from AVIOContext into buf.
Definition aviobuf.c:665
static int read_packet(void *opaque, uint8_t *buf, int buf_size)
#define flags(name, subs,...)
Definition cbs_h264.c:74
#define i(width, name, range_min, range_max)
Definition cbs_h264.c:63
#define s(width, name)
Definition cbs_vp9.c:198
static int read_probe(const AVProbeData *p)
Definition cdg.c:30
Public libavutil channel layout APIs header.
#define NULL
Definition coverity.c:32
long long int64_t
Definition coverity.c:34
static AVPacket * pkt
static int loop
Definition ffplay.c:338
static int read_header(FFV1Context *f, RangeCoder *c)
Definition ffv1dec.c:578
#define AV_OPT_FLAG_ENCODING_PARAM
A generic parameter which can be set by the user for muxing or encoding.
Definition opt.h:351
@ AV_OPT_TYPE_BOOL
Underlying C type is int.
Definition opt.h:326
@ AV_CODEC_ID_NONE
Definition codec_id.h:48
@ AV_CODEC_ID_ADPCM_PSX
Definition codec_id.h:407
#define AV_PKT_FLAG_CORRUPT
The packet content is corrupted.
Definition packet.h:651
AVStream * avformat_new_stream(AVFormatContext *s, const struct AVCodec *c)
Add a new stream to a media file.
int av_match_ext(const char *filename, const char *extensions)
Return a positive value if the given filename has one of the given extensions, 0 otherwise.
Definition format.c:41
#define AV_CHANNEL_LAYOUT_MONO
int av_dict_set_int(AVDictionary **pm, const char *key, int64_t value, int flags)
Convenience wrapper for av_dict_set() that converts the value to a string and stores it.
Definition dict.c:177
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition error.h:61
#define AVERROR_EOF
End of file.
Definition error.h:57
#define AVERROR(e)
Definition error.h:45
#define AV_LOG_TRACE
Extremely verbose debugging, useful for libav* development.
Definition log.h:236
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition log.h:210
const char * av_default_item_name(void *ptr)
Return the context name.
Definition log.c:241
@ AVMEDIA_TYPE_AUDIO
Definition avutil.h:201
int av_strcasecmp(const char *a, const char *b)
Locale-independent case-insensitive compare.
Definition avstring.c:208
const char * av_basename(const char *path)
Thread safe basename.
Definition avstring.c:253
#define LIBAVUTIL_VERSION_INT
Definition version.h:85
#define AV_RL32(p)
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition internal.h:88
static int read_seek(AVFormatContext *ctx, int stream_index, int64_t timestamp, int flags)
Definition libcdio.c:151
#define FFMIN(a, b)
Definition macros.h:49
#define FF_OFMT_FLAG_MAX_ONE_OF_EACH
If this flag is set, it indicates that for each codec type whose corresponding default codec (i....
Definition mux.h:50
#define FF_OFMT_FLAG_ONLY_DEFAULT_CODECS
If this flag is set, then the only permitted audio/video/subtitle codec ids are AVOutputFormat....
Definition mux.h:59
IDirect3DDxgiInterfaceAccess _COM_Outptr_ void ** p
AVOptions.
const char * name
Definition qsvenc.c:142
static const uint8_t header[24]
Definition sdr2.c:68
#define FF_ARRAY_ELEMS(a)
An AVChannelLayout holds information about the channel layout of audio data.
int nb_channels
Number of channels in this layout.
Describe the class of an AVClass context structure.
Definition log.h:76
This struct describes the properties of an encoded stream.
Definition codec_par.h:49
int bits_per_coded_sample
The number of bits per sample in the codedwords.
Definition codec_par.h:113
AVChannelLayout ch_layout
The channel layout and number of channels.
Definition codec_par.h:207
int64_t bit_rate
The average bitrate of the encoded data (in bits per second).
Definition codec_par.h:99
enum AVMediaType codec_type
General type of the encoded data.
Definition codec_par.h:53
int block_align
The number of bytes per coded audio frame, required by some formats.
Definition codec_par.h:221
enum AVCodecID codec_id
Specific type of the encoded data (the codec used).
Definition codec_par.h:57
int sample_rate
The number of audio samples per second.
Definition codec_par.h:213
Format I/O context.
Definition avformat.h:1333
Bytestream IO Context.
Definition avio.h:160
int seekable
A combination of AVIO_SEEKABLE_ flags or 0 when the stream is not seekable.
Definition avio.h:261
AVOption.
Definition opt.h:428
This structure stores compressed data.
Definition packet.h:580
This structure contains the data a format has to probe a file.
Definition avformat.h:471
Stream structure.
Definition avformat.h:766
AVCodecParameters * codecpar
Codec parameters associated with this stream.
Definition avformat.h:789
int64_t nb_frames
number of frames in this stream if known or 0
Definition avformat.h:827
int64_t duration
Decoding: duration of the stream, in stream time base.
Definition avformat.h:825
AVDictionary * metadata
Definition avformat.h:846
int64_t start_time
Decoding: pts of the first frame of the stream in presentation order, in stream time base.
Definition avformat.h:815
ArgoCVGHeader header
Definition argo_cvg.c:52
uint32_t checksum
Definition argo_cvg.c:53
uint32_t blocks_read
Definition argo_cvg.c:55
uint32_t num_blocks
Definition argo_cvg.c:54
uint32_t loop
Definition argo_cvg.c:47
uint32_t reverb
Definition argo_cvg.c:48
uint32_t size
Definition argo_cvg.c:46
uint32_t checksum
Definition argo_cvg.c:63
#define av_log(a,...)
static int64_t pts