2 * Square Enix SCD demuxer
3 * Copyright (C) 2021 Zane van Iperen (zane@zanevaniperen.com)
5 * Based off documentation:
6 * http://ffxivexplorer.fragmenterworks.com/research/scd%20files.txt
8 * This file is part of FFmpeg.
10 * FFmpeg is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * FFmpeg is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with FFmpeg; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
27 #include "libavutil/intreadwrite.h"
28 #include "libavutil/internal.h"
29 #include "libavutil/macros.h"
30 #include "libavutil/mem.h"
31 #include "libavformat/internal.h"
33 #include "avio_internal.h"
36 #define SCD_MAGIC ((uint64_t)MKBETAG('S', 'E', 'D', 'B') << 32 | \
37 MKBETAG('S', 'S', 'C', 'F'))
38 #define SCD_MIN_HEADER_SIZE 20
39 #define SCD_OFFSET_HEADER_SIZE 28
40 #define SCD_TRACK_HEADER_SIZE 32
42 #define SCD_TRACK_ID_PCM 0
43 #define SCD_TRACK_ID_OGG 6
44 #define SCD_TRACK_ID_MP3 7
45 #define SCD_TRACK_ID_MS_ADPCM 12
47 typedef struct SCDOffsetTable
{
53 typedef struct SCDHeader
{
54 uint64_t magic
; /* SEDBSSCF */
55 uint32_t version
; /* Version number. We only know about 3. */
56 uint16_t unk1
; /* Unknown, 260 in Drakengard 3, 1024 in FFXIV. */
57 uint16_t header_size
; /* Total size of this header. */
58 uint32_t file_size
; /* Is often 0, just ignore it. */
60 SCDOffsetTable table0
; /* Table 0, no idea. 56 uint32's/entry. */
61 SCDOffsetTable table1
; /* Table 1, contains the track info. */
62 SCDOffsetTable table2
; /* Table 2, no idea. 40 uint32's/entry. */
63 uint16_t unk2
; /* Unknown, not a count. */
64 uint32_t unk3
; /* Unknown, not an offset. */
65 uint32_t unk4
; /* Unknown, offset to offset. */
68 typedef struct SCDTrackHeader
{
70 uint32_t num_channels
;
75 uint32_t data_offset
; /* Offset to data + this header. */
78 uint32_t absolute_offset
;
82 typedef struct SCDDemuxContext
{
84 SCDTrackHeader
*tracks
;
88 static int scd_probe(const AVProbeData
*p
)
90 if (AV_RB64(p
->buf
) != SCD_MAGIC
)
93 return AVPROBE_SCORE_MAX
;
96 static int scd_read_table(AVFormatContext
*s
, SCDOffsetTable
*table
)
100 if ((ret
= avio_seek(s
->pb
, table
->offset
, SEEK_SET
)) < 0)
103 if ((table
->entries
= av_calloc(table
->count
, sizeof(uint32_t))) == NULL
)
106 if ((ret
= avio_read(s
->pb
, (unsigned char*)table
->entries
, table
->count
* sizeof(uint32_t))) < 0)
109 for (size_t i
= 0; i
< table
->count
; i
++)
110 table
->entries
[i
] = AV_RB32(table
->entries
+ i
);
112 av_log(s
, AV_LOG_TRACE
, "Table, size = %u, offset = %u\n", table
->count
, table
->offset
);
113 for (size_t i
= 0; i
< table
->count
; i
++)
114 av_log(s
, AV_LOG_TRACE
, " [%02zu]: %u\n", i
, table
->entries
[i
]);
119 static int scd_read_offsets(AVFormatContext
*s
)
122 SCDDemuxContext
*ctx
= s
->priv_data
;
123 uint8_t buf
[SCD_OFFSET_HEADER_SIZE
];
125 if ((ret
= avio_read(s
->pb
, buf
, SCD_OFFSET_HEADER_SIZE
)) < 0)
128 ctx
->hdr
.table0
.count
= AV_RB16(buf
+ 0);
129 ctx
->hdr
.table1
.count
= AV_RB16(buf
+ 2);
130 ctx
->hdr
.table2
.count
= AV_RB16(buf
+ 4);
131 ctx
->hdr
.unk2
= AV_RB16(buf
+ 6);
132 ctx
->hdr
.table0
.offset
= AV_RB32(buf
+ 8);
133 ctx
->hdr
.table1
.offset
= AV_RB32(buf
+ 12);
134 ctx
->hdr
.table2
.offset
= AV_RB32(buf
+ 16);
135 ctx
->hdr
.unk3
= AV_RB32(buf
+ 20);
136 ctx
->hdr
.unk4
= AV_RB32(buf
+ 24);
138 if ((ret
= scd_read_table(s
, &ctx
->hdr
.table0
)) < 0)
141 if ((ret
= scd_read_table(s
, &ctx
->hdr
.table1
)) < 0)
144 if ((ret
= scd_read_table(s
, &ctx
->hdr
.table2
)) < 0)
150 static int scd_read_track(AVFormatContext
*s
, SCDTrackHeader
*track
, int index
)
155 AVCodecParameters
*par
;
156 SCDDemuxContext
*ctx
= s
->priv_data
;
157 uint8_t buf
[SCD_TRACK_HEADER_SIZE
];
159 /* Mark as experimental until I find more files from more than just one game. */
160 if (s
->strict_std_compliance
> FF_COMPLIANCE_EXPERIMENTAL
) {
161 av_log(s
, AV_LOG_ERROR
, "SCD demuxing is experimental, "
162 "add '-strict %d' if you want to use it.\n",
163 FF_COMPLIANCE_EXPERIMENTAL
);
164 return AVERROR_EXPERIMENTAL
;
167 hoffset
= ctx
->hdr
.table1
.entries
[index
];
169 if ((ret
= avio_seek(s
->pb
, hoffset
, SEEK_SET
)) < 0)
172 if ((ret
= avio_read(s
->pb
, buf
, SCD_TRACK_HEADER_SIZE
)) < 0)
175 track
->length
= AV_RB32(buf
+ 0);
176 track
->num_channels
= AV_RB32(buf
+ 4);
177 track
->sample_rate
= AV_RB32(buf
+ 8);
178 track
->data_type
= AV_RB32(buf
+ 12);
179 track
->loop_start
= AV_RB32(buf
+ 16);
180 track
->loop_end
= AV_RB32(buf
+ 20);
181 track
->data_offset
= AV_RB32(buf
+ 24);
182 track
->aux_count
= AV_RB32(buf
+ 28);
185 if (track
->num_channels
> 8 || track
->sample_rate
>= 192000 ||
186 track
->loop_start
> track
->loop_end
)
187 return AVERROR_INVALIDDATA
;
189 track
->absolute_offset
= hoffset
+ SCD_TRACK_HEADER_SIZE
+ track
->data_offset
;
190 track
->bytes_read
= 0;
192 /* Not sure what to do with these, it seems to be fine to ignore them. */
193 if (track
->aux_count
!= 0)
194 av_log(s
, AV_LOG_DEBUG
, "[%d] Track has %u auxiliary chunk(s).\n", index
, track
->aux_count
);
196 if ((st
= avformat_new_stream(s
, NULL
)) == NULL
)
197 return AVERROR(ENOMEM
);
200 par
->codec_type
= AVMEDIA_TYPE_AUDIO
;
201 par
->ch_layout
.nb_channels
= (int)track
->num_channels
;
202 par
->sample_rate
= (int)track
->sample_rate
;
206 /* TODO: Check this with other types. Drakengard 3 MP3s have 47999 instead of 48000. */
207 if (track
->data_type
== SCD_TRACK_ID_MP3
)
208 par
->sample_rate
+= 1;
210 avpriv_set_pts_info(st
, 64, 1, par
->sample_rate
);
212 if (av_dict_set_int(&st
->metadata
, "start", track
->absolute_offset
, 0) < 0)
213 return AVERROR(ENOMEM
);
215 if (av_dict_set_int(&st
->metadata
, "loop_start", track
->loop_start
, 0) < 0)
216 return AVERROR(ENOMEM
);
218 if (av_dict_set_int(&st
->metadata
, "loop_end", track
->loop_end
, 0) < 0)
219 return AVERROR(ENOMEM
);
221 switch(track
->data_type
) {
222 case SCD_TRACK_ID_PCM
:
223 par
->codec_id
= AV_CODEC_ID_PCM_S16BE
;
224 par
->bits_per_coded_sample
= 16;
225 par
->block_align
= par
->bits_per_coded_sample
* par
->ch_layout
.nb_channels
/ 8;
227 case SCD_TRACK_ID_MP3
:
228 par
->codec_id
= AV_CODEC_ID_MP3
;
229 ffstream(st
)->need_parsing
= AVSTREAM_PARSE_FULL_RAW
;
231 case SCD_TRACK_ID_OGG
:
232 case SCD_TRACK_ID_MS_ADPCM
:
234 par
->codec_id
= AV_CODEC_ID_NONE
;
235 avpriv_request_sample(s
, "data type %u", track
->data_type
);
241 static int scd_read_header(AVFormatContext
*s
)
244 SCDDemuxContext
*ctx
= s
->priv_data
;
245 uint8_t buf
[SCD_MIN_HEADER_SIZE
];
247 if ((ret
= ffio_read_size(s
->pb
, buf
, SCD_MIN_HEADER_SIZE
)) < 0)
250 ctx
->hdr
.magic
= AV_RB64(buf
+ 0);
251 ctx
->hdr
.version
= AV_RB32(buf
+ 8);
252 ctx
->hdr
.unk1
= AV_RB16(buf
+ 12);
253 ctx
->hdr
.header_size
= AV_RB16(buf
+ 14);
254 ctx
->hdr
.file_size
= AV_RB32(buf
+ 16);
256 if (ctx
->hdr
.magic
!= SCD_MAGIC
)
257 return AVERROR_INVALIDDATA
;
259 if (ctx
->hdr
.version
!= 3) {
260 avpriv_request_sample(s
, "SCD version %u", ctx
->hdr
.version
);
261 return AVERROR_PATCHWELCOME
;
264 if (ctx
->hdr
.header_size
< SCD_MIN_HEADER_SIZE
)
265 return AVERROR_INVALIDDATA
;
267 if ((ret
= avio_skip(s
->pb
, ctx
->hdr
.header_size
- SCD_MIN_HEADER_SIZE
)) < 0)
270 if ((ret
= scd_read_offsets(s
)) < 0)
273 ctx
->tracks
= av_calloc(ctx
->hdr
.table1
.count
, sizeof(SCDTrackHeader
));
274 if (ctx
->tracks
== NULL
)
275 return AVERROR(ENOMEM
);
277 for (int i
= 0; i
< ctx
->hdr
.table1
.count
; i
++) {
278 if ((ret
= scd_read_track(s
, ctx
->tracks
+ i
, i
)) < 0)
282 if (ctx
->hdr
.table1
.count
== 0)
285 if ((ret
= avio_seek(s
->pb
, ctx
->tracks
[0].absolute_offset
, SEEK_SET
)) < 0)
291 static int scd_read_packet(AVFormatContext
*s
, AVPacket
*pkt
)
293 SCDDemuxContext
*ctx
= s
->priv_data
;
294 AVCodecParameters
*par
;
296 /* Streams aren't interleaved, round-robin them. */
297 for (int i
= 0; i
< ctx
->hdr
.table1
.count
; i
++) {
302 ctx
->current_track
%= ctx
->hdr
.table1
.count
;
304 trk
= ctx
->tracks
+ ctx
->current_track
;
305 par
= s
->streams
[ctx
->current_track
]->codecpar
;
307 if (trk
->bytes_read
>= trk
->length
)
310 if ((ret
= avio_seek(s
->pb
, trk
->absolute_offset
+ trk
->bytes_read
, SEEK_SET
)) < 0)
313 switch(trk
->data_type
) {
314 case SCD_TRACK_ID_PCM
:
315 size
= par
->block_align
;
317 case SCD_TRACK_ID_MP3
:
319 size
= FFMIN(trk
->length
- trk
->bytes_read
, 4096);
323 ret
= av_get_packet(s
->pb
, pkt
, size
);
324 if (ret
== AVERROR_EOF
) {
325 trk
->length
= trk
->bytes_read
;
327 } else if (ret
< 0) {
331 if (trk
->data_type
== SCD_TRACK_ID_PCM
) {
332 pkt
->pts
= trk
->bytes_read
/ (par
->ch_layout
.nb_channels
* sizeof(uint16_t));
333 pkt
->duration
= size
/ (par
->ch_layout
.nb_channels
* sizeof(int16_t));
336 trk
->bytes_read
+= ret
;
337 pkt
->flags
&= ~AV_PKT_FLAG_CORRUPT
;
338 pkt
->stream_index
= ctx
->current_track
;
340 ctx
->current_track
++;
347 static int scd_seek(AVFormatContext
*s
, int stream_index
,
348 int64_t pts
, int flags
)
350 SCDDemuxContext
*ctx
= s
->priv_data
;
353 return AVERROR(EINVAL
);
355 for(int i
= 0; i
< ctx
->hdr
.table1
.count
; ++i
)
356 ctx
->tracks
[i
].bytes_read
= 0;
361 static int scd_read_close(AVFormatContext
*s
)
363 SCDDemuxContext
*ctx
= s
->priv_data
;
365 av_freep(&ctx
->hdr
.table0
.entries
);
366 av_freep(&ctx
->hdr
.table1
.entries
);
367 av_freep(&ctx
->hdr
.table2
.entries
);
368 av_freep(&ctx
->tracks
);
372 const FFInputFormat ff_scd_demuxer
= {
374 .p
.long_name
= NULL_IF_CONFIG_SMALL("Square Enix SCD"),
375 .priv_data_size
= sizeof(SCDDemuxContext
),
376 .flags_internal
= FF_INFMT_FLAG_INIT_CLEANUP
,
377 .read_probe
= scd_probe
,
378 .read_header
= scd_read_header
,
379 .read_packet
= scd_read_packet
,
380 .read_seek
= scd_seek
,
381 .read_close
= scd_read_close
,