1 /*
2  * Copyright (C) 2000-2018 the xine project
3  *
4  * This file is part of xine, a free video player.
5  *
6  * xine is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * xine is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
19  */
20 
21 /*
22  * MOD File "demuxer" by Paul Eggleton (bluelightning@bluelightning.org)
23  * This is really just a loader for Amiga MOD (and similar) music files
24  * which reads an entire MOD file and passes it over to the ModPlug library
25  * for playback.
26  *
27  * This file was based on demux_nsf.c by Mike Melanson.
28  */
29 
30 #ifdef HAVE_CONFIG_H
31 #include "config.h"
32 #endif
33 
34 #include <stdio.h>
35 #include <fcntl.h>
36 #include <unistd.h>
37 #include <string.h>
38 #include <stdlib.h>
39 
40 /********** logging **********/
41 #define LOG_MODULE "demux_mod"
42 /* #define LOG_VERBOSE */
43 /* #define LOG */
44 
45 #include <xine/xine_internal.h>
46 #include <xine/xineutils.h>
47 #include <xine/compat.h>
48 #include <xine/demux.h>
49 #include <libmodplug/modplug.h>
50 #include "bswap.h"
51 
52 #define MOD_SAMPLERATE 44100
53 #define MOD_BITS 16
54 #define MOD_CHANNELS 2
55 
56 #define OUT_BYTES_PER_SECOND (MOD_SAMPLERATE * MOD_CHANNELS * (MOD_BITS >> 3))
57 
58 #define BLOCK_SIZE 4096
59 
60 
61 typedef struct {
62   demux_plugin_t       demux_plugin;
63 
64   xine_stream_t       *stream;
65   fifo_buffer_t       *audio_fifo;
66   int                  status;
67 
68   char                *title;
69   char                *artist;
70   char                *copyright;
71 
72   char                *buffer;
73 
74   int64_t              current_pts;
75 
76   ModPlug_Settings     settings;
77   ModPlugFile         *mpfile;
78   int                  mod_length;
79   int                  seek_flag;  /* this is set when a seek just occurred */
80 
81 } demux_mod_t;
82 
83 
84 #define FOURCC_32(a, b, c, d) (d + (c<<8) + (b<<16) + (a<<24))
85 
86 /**
87  * @brief Probes if the given file can be demuxed using modplug or not
88  * @retval 0 The file is not a valid modplug file (or the probe isn't complete yet)
89  * @retval 1 The file has been identified as a valid modplug file
90  * @todo Just Protracker files are detected right now.
91  */
probe_mod_file(input_plugin_t * input)92 static int probe_mod_file(input_plugin_t *input) {
93   /* We need the value present at offset 1080, of size 4 */
94   union {
95     uint8_t buffer[1080+4]; /* The raw buffer */
96     uint32_t values[(1080+4)/sizeof(uint32_t)];
97   } header;
98 
99   if (_x_demux_read_header(input, header.buffer, 1080+4) != 1080+4)
100       return 0;
101 
102   /* Magic numbers taken from GNU file's magic description */
103   switch( _X_ABE_32(header.values + (1080/sizeof(uint32_t))) ) {
104   case FOURCC_32('M', '.', 'K', '.'): /* 4-channel Protracker module sound data */
105   case FOURCC_32('M', '!', 'K', '!'): /* 4-channel Protracker module sound data */
106   case FOURCC_32('F', 'L', 'T', '4'): /* 4-channel Startracker module sound data */
107   case FOURCC_32('F', 'L', 'T', '8'): /* 8-channel Startracker module sound data */
108   case FOURCC_32('4', 'C', 'H', 'N'): /* 4-channel Fasttracker module sound data */
109   case FOURCC_32('6', 'C', 'H', 'N'): /* 6-channel Fasttracker module sound data */
110   case FOURCC_32('8', 'C', 'H', 'N'): /* 8-channel Fasttracker module sound data */
111   case FOURCC_32('C', 'D', '8', '1'): /* 8-channel Octalyser module sound data */
112   case FOURCC_32('O', 'K', 'T', 'A'): /* 8-channel Oktalyzer module sound data */
113   case FOURCC_32('1', '6', 'C', 'N'): /* 16-channel Taketracker module sound data */
114   case FOURCC_32('3', '2', 'C', 'N'): /* 32-channel Taketracker module sound data */
115     return 1;
116   }
117 
118   /* ScreamTracker 2 */
119   if (!memcmp (header.buffer + 20, "!Scream!", 7))
120     return 1;
121 
122   /* ScreamTracker 3 */
123   if (_X_ABE_32(header.values + 0x2C / sizeof (uint32_t)) == FOURCC_32('S', 'C', 'R', 'M'))
124     return 1;
125 
126   return 0;
127 }
128 
129 /* returns 1 if the MOD file was opened successfully, 0 otherwise */
open_mod_file(demux_mod_t * this,input_plugin_t * input)130 static int open_mod_file(demux_mod_t *this, input_plugin_t *input) {
131   off_t total_read;
132   off_t input_length;
133 
134   /* Get size and create buffer */
135   input_length = input->get_length(input);
136   /* Avoid potential issues with signed variables and e.g. read() returning -1 */
137   if (input_length > 0x7FFFFFFF || input_length < 0) {
138     xine_log(this->stream->xine, XINE_LOG_PLUGIN, "modplug - size overflow\n");
139     return 0;
140   }
141   this->buffer = malloc(input_length);
142   if(!this->buffer) {
143     xine_log(this->stream->xine, XINE_LOG_PLUGIN, "modplug - allocation failure\n");
144     return 0;
145   }
146 
147   /* Seek to beginning */
148   input->seek(input, 0, SEEK_SET);
149 
150   /* Read data */
151   total_read = input->read(input, this->buffer, input_length);
152 
153   if (total_read != input_length) {
154     xine_log(this->stream->xine, XINE_LOG_PLUGIN, "modplug - filesize error\n");
155     return 0;
156   }
157 
158   /* Set up modplug engine */
159   ModPlug_GetSettings(&this->settings);
160   this->settings.mResamplingMode = MODPLUG_RESAMPLE_FIR; /* RESAMP */
161   this->settings.mChannels = MOD_CHANNELS;
162   this->settings.mBits = MOD_BITS;
163   this->settings.mFrequency = MOD_SAMPLERATE;
164   ModPlug_SetSettings(&this->settings);
165 
166   this->mpfile = ModPlug_Load(this->buffer, input_length);
167   if (this->mpfile==NULL) {
168     xine_log(this->stream->xine, XINE_LOG_PLUGIN, "modplug - load error\n");
169     return 0;
170   }
171 
172   this->title = strdup(ModPlug_GetName(this->mpfile));
173   this->artist = strdup("");
174   this->copyright = strdup("");
175 
176   this->mod_length = ModPlug_GetLength(this->mpfile);
177   if (this->mod_length < 1)
178     this->mod_length = 1; /* avoids -ve & div-by-0 */
179 
180   return 1;
181 }
182 
demux_mod_send_chunk(demux_plugin_t * this_gen)183 static int demux_mod_send_chunk(demux_plugin_t *this_gen) {
184   demux_mod_t *this = (demux_mod_t *) this_gen;
185   buf_element_t *buf;
186   int mlen;
187 
188   buf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo);
189   buf->type = BUF_AUDIO_LPCM_LE;
190 
191   mlen = ModPlug_Read(this->mpfile, buf->content, buf->max_size);
192   if (mlen == 0) {
193     this->status = DEMUX_FINISHED;
194     buf->free_buffer(buf);
195   }
196   else {
197     buf->size = mlen;
198     buf->pts = this->current_pts;
199     buf->extra_info->input_time = buf->pts / 90;
200 
201     buf->extra_info->input_normpos = buf->extra_info->input_time * 65535 / this->mod_length;
202     buf->decoder_flags = BUF_FLAG_FRAME_END;
203 
204     if (this->seek_flag) {
205       _x_demux_control_newpts(this->stream, buf->pts, BUF_FLAG_SEEK);
206       this->seek_flag = 0;
207     }
208 
209     this->audio_fifo->put (this->audio_fifo, buf);
210 
211     this->current_pts += 90000 * mlen / OUT_BYTES_PER_SECOND;
212   }
213 
214   return this->status;
215 }
216 
demux_mod_send_headers(demux_plugin_t * this_gen)217 static void demux_mod_send_headers(demux_plugin_t *this_gen) {
218   demux_mod_t *this = (demux_mod_t *) this_gen;
219   buf_element_t *buf;
220   char copyright[100];
221 
222   this->audio_fifo = this->stream->audio_fifo;
223 
224   this->status = DEMUX_OK;
225 
226   /* load stream information */
227   _x_stream_info_set(this->stream, XINE_STREAM_INFO_HAS_VIDEO, 0);
228   _x_stream_info_set(this->stream, XINE_STREAM_INFO_HAS_AUDIO, 1);
229   _x_stream_info_set(this->stream, XINE_STREAM_INFO_AUDIO_CHANNELS, MOD_CHANNELS);
230   _x_stream_info_set(this->stream, XINE_STREAM_INFO_AUDIO_SAMPLERATE, MOD_SAMPLERATE);
231   _x_stream_info_set(this->stream, XINE_STREAM_INFO_AUDIO_BITS, MOD_BITS);
232 
233   _x_meta_info_set(this->stream, XINE_META_INFO_TITLE, this->title);
234   _x_meta_info_set(this->stream, XINE_META_INFO_ARTIST, this->artist);
235   snprintf(copyright, 100, "(C) %s", this->copyright);
236   _x_meta_info_set(this->stream, XINE_META_INFO_COMMENT, copyright);
237 
238   /* send start buffers */
239   _x_demux_control_start(this->stream);
240 
241   /* send init info to the audio decoder */
242   buf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo);
243   buf->type = BUF_AUDIO_LPCM_LE;
244   buf->decoder_flags = BUF_FLAG_HEADER|BUF_FLAG_STDHEADER|BUF_FLAG_FRAME_END;
245   buf->decoder_info[0] = 0;
246   buf->decoder_info[1] = MOD_SAMPLERATE;
247   buf->decoder_info[2] = MOD_BITS;
248   buf->decoder_info[3] = MOD_CHANNELS;
249   buf->size = 0;
250   this->audio_fifo->put (this->audio_fifo, buf);
251 }
252 
demux_mod_seek(demux_plugin_t * this_gen,off_t start_pos,int start_time,int playing)253 static int demux_mod_seek (demux_plugin_t *this_gen,
254                            off_t start_pos, int start_time, int playing) {
255 
256   demux_mod_t *this = (demux_mod_t *) this_gen;
257   int64_t seek_millis;
258 
259   (void)playing;
260 
261   if (start_pos) {
262     seek_millis = this->mod_length;
263     seek_millis *= start_pos;
264     seek_millis /= 65535;
265   } else {
266     seek_millis = start_time;
267   }
268 
269   _x_demux_flush_engine(this->stream);
270   ModPlug_Seek(this->mpfile, seek_millis);
271   this->current_pts = seek_millis * 90;
272 
273   this->seek_flag = 1;
274   return this->status;
275 }
276 
demux_mod_dispose(demux_plugin_t * this_gen)277 static void demux_mod_dispose (demux_plugin_t *this_gen) {
278   demux_mod_t *this = (demux_mod_t *) this_gen;
279 
280   if (this->mpfile)
281     ModPlug_Unload(this->mpfile);
282   _x_freep(&this->buffer);
283   _x_freep(&this->title);
284   _x_freep(&this->artist);
285   _x_freep(&this->copyright);
286   free(this);
287 }
288 
demux_mod_get_status(demux_plugin_t * this_gen)289 static int demux_mod_get_status (demux_plugin_t *this_gen) {
290   demux_mod_t *this = (demux_mod_t *) this_gen;
291   return this->status;
292 }
293 
294 /* return the approximate length in miliseconds */
demux_mod_get_stream_length(demux_plugin_t * this_gen)295 static int demux_mod_get_stream_length (demux_plugin_t *this_gen) {
296   demux_mod_t *this = (demux_mod_t *) this_gen;
297   return ModPlug_GetLength(this->mpfile);
298 }
299 
demux_mod_get_capabilities(demux_plugin_t * this_gen)300 static uint32_t demux_mod_get_capabilities(demux_plugin_t *this_gen) {
301   (void)this_gen;
302   return DEMUX_CAP_NOCAP;
303 }
304 
demux_mod_get_optional_data(demux_plugin_t * this_gen,void * data,int data_type)305 static int demux_mod_get_optional_data(demux_plugin_t *this_gen,
306                                        void *data, int data_type) {
307   (void)this_gen;
308   (void)data;
309   (void)data_type;
310 
311   return DEMUX_OPTIONAL_UNSUPPORTED;
312 }
313 
open_plugin(demux_class_t * class_gen,xine_stream_t * stream,input_plugin_t * input)314 static demux_plugin_t *open_plugin (demux_class_t *class_gen, xine_stream_t *stream,
315                                     input_plugin_t *input) {
316 
317   demux_mod_t   *this;
318 
319   if (!INPUT_IS_SEEKABLE(input)) {
320     xprintf(stream->xine, XINE_VERBOSITY_DEBUG, "input not seekable, can not handle!\n");
321     return NULL;
322   }
323 
324   switch (stream->content_detection_method) {
325     case METHOD_EXPLICIT:
326     case METHOD_BY_MRL:
327     case METHOD_BY_CONTENT:
328       if (!probe_mod_file(input))
329         return NULL;
330       break;
331     default:
332       return NULL;
333   }
334 
335   this = calloc(1, sizeof(demux_mod_t));
336   if (!this)
337     return NULL;
338 
339   this->stream = stream;
340 
341   this->demux_plugin.send_headers      = demux_mod_send_headers;
342   this->demux_plugin.send_chunk        = demux_mod_send_chunk;
343   this->demux_plugin.seek              = demux_mod_seek;
344   this->demux_plugin.dispose           = demux_mod_dispose;
345   this->demux_plugin.get_status        = demux_mod_get_status;
346   this->demux_plugin.get_stream_length = demux_mod_get_stream_length;
347   this->demux_plugin.get_capabilities  = demux_mod_get_capabilities;
348   this->demux_plugin.get_optional_data = demux_mod_get_optional_data;
349   this->demux_plugin.demux_class       = class_gen;
350 
351   this->status = DEMUX_FINISHED;
352 
353   xprintf(stream->xine, XINE_VERBOSITY_DEBUG, "TEST mod decode\n");
354 
355   if (!open_mod_file(this, input)) {
356     demux_mod_dispose(&this->demux_plugin);
357     return NULL;
358   }
359 
360   return &this->demux_plugin;
361 }
362 
demux_mod_init_plugin(xine_t * xine,const void * data)363 static void *demux_mod_init_plugin (xine_t *xine, const void *data) {
364 
365   static const demux_class_t demux_mod_class = {
366     .open_plugin     = open_plugin,
367     .description     = N_("ModPlug Amiga MOD Music file demux plugin"),
368     .identifier      = "mod",
369     .mimetypes       =
370          "audio/x-mod: mod: SoundTracker/NoiseTracker/ProTracker Module;"
371          "audio/mod: mod: SoundTracker/NoiseTracker/ProTracker Module;"
372          "audio/it: it: ImpulseTracker Module;"
373          "audio/x-it: it: ImpulseTracker Module;"
374          "audio/x-stm: stm: ScreamTracker 2 Module;"
375          "audio/x-s3m: s3m: ScreamTracker 3 Module;"
376          "audio/s3m: s3m: ScreamTracker 3 Module;"
377          "application/playerpro: 669: 669 Tracker Module;"
378          "application/adrift: amf: ADRIFT Module File;"
379          "audio/med: med: Amiga MED/OctaMED Tracker Module Sound File;"
380          "audio/x-amf: amf: ADRIFT Module File;"
381          "audio/x-xm: xm: FastTracker II Audio;"
382          "audio/xm: xm: FastTracker II Audio;",
383     .extensions      = "mod it stm s3m 669 amf med mdl xm",
384     .dispose         = NULL,
385   };
386 
387   (void)xine;
388   (void)data;
389 
390   return (void *)&demux_mod_class;
391 }
392 
393 static const demuxer_info_t demux_info_mod = {
394   .priority = 10,
395 };
396 
397 const plugin_info_t xine_plugin_info[] EXPORTED = {
398   { PLUGIN_DEMUX, 27, "modplug", XINE_VERSION_CODE, &demux_info_mod, demux_mod_init_plugin },
399   { PLUGIN_NONE, 0, NULL, 0, NULL, NULL }
400 };
401