2 * Copyright (c) 2014 Jiri Svoboda
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 /** @addtogroup trackmod
33 * @file Extended Module (.xm).
40 #include <types/common.h>
42 #include "byteorder.h"
47 static char xm_id_text
[] = "Extended Module: ";
49 /** Load XM order list
51 * @param xm_hdr XM header
52 * @param module Module
53 * @return EOK on success, EIO on format error, ENOMEM if out of memory.
55 static errno_t
trackmod_xm_load_order_list(xm_hdr_t
*xm_hdr
, trackmod_module_t
*module
)
61 module
->ord_list_len
= uint16_t_le2host(xm_hdr
->song_len
);
62 if (module
->ord_list_len
> xm_pat_ord_table_size
) {
63 /* Invalid song length */
68 module
->ord_list
= calloc(sizeof(size_t), module
->ord_list_len
);
69 if (module
->ord_list
== NULL
) {
70 printf("Out of memory.\n");
75 for (i
= 0; i
< module
->ord_list_len
; i
++) {
76 module
->ord_list
[i
] = xm_hdr
->pat_ord_table
[i
];
79 module
->restart_pos
= uint16_t_le2host(xm_hdr
->restart_pos
);
80 if (module
->restart_pos
>= module
->ord_list_len
) {
90 /** Decode XM pattern.
92 * @param data Packed pattern data
93 * @param pattern Pattern to load to
94 * @return EOK on success, EINVAL if there is error in the coded data.
96 static errno_t
trackmod_xm_decode_pattern(uint8_t *data
, size_t dsize
,
97 trackmod_pattern_t
*pattern
)
104 cells
= pattern
->rows
* pattern
->channels
;
107 for (i
= 0; i
< cells
; i
++) {
111 if ((data
[si
] & 0x80) != 0) {
112 mask
= data
[si
++] & 0x1f;
118 if ((mask
& 0x1) != 0) {
121 pattern
->data
[i
].note
= data
[si
++] & 0x7f;
125 if ((mask
& 0x2) != 0) {
128 pattern
->data
[i
].instr
= data
[si
++];
132 if ((mask
& 0x4) != 0) {
135 pattern
->data
[i
].volume
= data
[si
++];
139 if ((mask
& 0x8) != 0) {
142 pattern
->data
[i
].effect
= (unsigned)data
[si
++] << 8;
145 /* Effect parameter */
146 if ((mask
& 0x10) != 0) {
149 pattern
->data
[i
].effect
|= data
[si
++];
153 /* Note: Ignoring any extra data */
161 * @param module Module
162 * @return EOK on success, EIO on format error, ENOMEM if out of memory.
164 static errno_t
trackmod_xm_load_patterns(FILE *f
, trackmod_module_t
*module
)
172 xm_pattern_t pattern
;
178 module
->pattern
= calloc(sizeof(trackmod_pattern_t
), module
->patterns
);
179 if (module
->pattern
== NULL
) {
184 for (i
= 0; i
< module
->patterns
; i
++) {
185 ret
= fread(&pattern
, 1, sizeof(xm_pattern_t
), f
);
186 if (ret
!= sizeof(xm_pattern_t
)) {
191 hdr_size
= (size_t)uint32_t_le2host(pattern
.hdr_size
);
192 pack_type
= pattern
.pack_type
;
193 rows
= uint16_t_le2host(pattern
.rows
);
194 data_size
= uint16_t_le2host(pattern
.data_size
);
196 if (pack_type
!= 0) {
201 /* Jump to end of pattern header */
202 seek_amount
= (long)hdr_size
- (long)sizeof(xm_pattern_t
);
203 if (fseek(f
, seek_amount
, SEEK_CUR
) < 0) {
208 module
->pattern
[i
].rows
= rows
;
209 module
->pattern
[i
].channels
= module
->channels
;
210 module
->pattern
[i
].data
= calloc(sizeof(trackmod_cell_t
),
211 rows
* module
->channels
);
213 if (module
->pattern
[i
].data
== NULL
) {
218 buf
= calloc(1, data_size
);
224 nread
= fread(buf
, 1, data_size
, f
);
225 if (nread
!= (ssize_t
)data_size
) {
230 rc
= trackmod_xm_decode_pattern(buf
, data_size
,
231 &module
->pattern
[i
]);
245 /** Decode XM sample data.
247 * XM sample data is delta-encoded. Undo the delta encoding and convert
250 static void trackmod_xm_decode_sample_data(trackmod_sample_t
*sample
)
258 if (sample
->bytes_smp
== 1) {
260 i8p
= (int8_t *)sample
->data
;
262 for (i
= 0; i
< sample
->length
; i
++) {
263 cur8
= cur8
+ i8p
[i
];
268 i16p
= (int16_t *)sample
->data
;
270 for (i
= 0; i
< sample
->length
; i
++) {
271 cur16
= cur16
+ (int16_t)uint16_t_le2host(i16p
[i
]);
277 /** Load XM instruments
280 * @param module Module
281 * @return EOK on success, EIO on format error, ENOMEM if out of memory.
283 static errno_t
trackmod_xm_load_instruments(xm_hdr_t
*xm_hdr
, FILE *f
,
284 trackmod_module_t
*module
)
288 xm_instr_ext_t instrx
;
293 size_t smp_hdr_size
= 0; /* GCC false alarm on uninitialized */
296 trackmod_sample_t
*sample
;
302 module
->instrs
= uint16_t_le2host(xm_hdr
->instruments
);
303 module
->instr
= calloc(module
->instrs
, sizeof(trackmod_instr_t
));
304 if (module
->instr
== NULL
)
307 for (i
= 0; i
< module
->instrs
; i
++) {
309 ret
= fread(&instr
, 1, sizeof(xm_instr_t
), f
);
310 if (ret
!= sizeof(xm_instr_t
)) {
315 samples
= uint16_t_le2host(instr
.samples
);
316 instr_size
= (size_t)uint32_t_le2host(instr
.size
);
319 ret
= fread(&instrx
, 1, sizeof(xm_instr_ext_t
), f
);
320 if (ret
!= sizeof(xm_instr_ext_t
)) {
325 smp_hdr_size
= uint32_t_le2host(instrx
.smp_hdr_size
);
327 for (j
= 0; j
< xm_smp_note_size
; j
++) {
328 module
->instr
[i
].key_smp
[j
] =
332 module
->instr
[i
].samples
= samples
;
333 module
->instr
[i
].sample
= calloc(samples
,
334 sizeof(trackmod_sample_t
));
335 if (module
->instr
[i
].sample
== NULL
) {
341 if (fseek(f
, pos
+ instr_size
, SEEK_SET
) < 0) {
346 for (j
= 0; j
< samples
; j
++) {
347 sample
= &module
->instr
[i
].sample
[j
];
350 ret
= fread(&smp
, 1, sizeof(xm_smp_t
), f
);
351 if (ret
!= sizeof(xm_smp_t
)) {
356 smp_size
= (size_t)uint32_t_le2host(smp
.length
);
358 smp_data
= calloc(smp_size
, 1);
359 if (smp_data
== NULL
) {
364 if (fseek(f
, pos
+ smp_hdr_size
, SEEK_SET
) < 0) {
369 nread
= fread(smp_data
, 1, smp_size
, f
);
370 if (nread
!= (ssize_t
)smp_size
) {
375 if (smp
.smp_type
& (1 << xmst_16_bit
)) {
376 sample
->bytes_smp
= 2;
378 sample
->bytes_smp
= 1;
381 sample
->data
= smp_data
;
382 sample
->length
= smp_size
/ sample
->bytes_smp
;
384 ltype
= smp
.smp_type
& 0x3;
387 sample
->loop_type
= tl_no_loop
;
389 case xmsl_forward_loop
:
390 sample
->loop_type
= tl_forward_loop
;
392 case xmsl_pingpong_loop
:
393 sample
->loop_type
= tl_pingpong_loop
;
401 uint32_t_le2host(smp
.loop_start
) / sample
->bytes_smp
;
403 uint32_t_le2host(smp
.loop_len
) / sample
->bytes_smp
;
404 sample
->def_vol
= 0x40;
405 sample
->rel_note
= smp
.rel_note
;
406 sample
->finetune
= smp
.finetune
/ 2;
408 trackmod_xm_decode_sample_data(sample
);
417 /** Load extended module.
419 * @param fname File name
420 * @param rmodule Place to store pointer to newly loaded module.
421 * @return EOK on success, ENONEM if out of memory, EIO on I/O error
422 * or if any error is found in the format of the file.
424 errno_t
trackmod_xm_load(char *fname
, trackmod_module_t
**rmodule
)
427 trackmod_module_t
*module
= NULL
;
433 f
= fopen(fname
, "rb");
435 printf("Error opening file.\n");
440 nread
= fread(&xm_hdr
, 1, sizeof(xm_hdr_t
), f
);
441 if (nread
< sizeof(xm_hdr_t
)) {
442 printf("File too small.\n");
447 if (memcmp(xm_hdr
.id_text
, xm_id_text
, xm_id_text_size
) != 0) {
452 module
= trackmod_module_new();
453 if (module
== NULL
) {
454 printf("Out of memory.\n");
459 module
->channels
= uint16_t_le2host(xm_hdr
.channels
);
460 module
->patterns
= uint16_t_le2host(xm_hdr
.patterns
);
461 module
->ord_list_len
= uint16_t_le2host(xm_hdr
.song_len
);
463 hdr_size
= (size_t)uint32_t_le2host(xm_hdr
.hdr_size
) +
464 offsetof(xm_hdr_t
, hdr_size
);
466 module
->def_bpm
= uint16_t_le2host(xm_hdr
.def_bpm
);
467 module
->def_tpr
= uint16_t_le2host(xm_hdr
.def_tempo
);
469 /* Jump to end of file header */
470 if (fseek(f
, hdr_size
, SEEK_SET
) < 0) {
475 rc
= trackmod_xm_load_order_list(&xm_hdr
, module
);
479 rc
= trackmod_xm_load_patterns(f
, module
);
483 rc
= trackmod_xm_load_instruments(&xm_hdr
, f
, module
);
493 trackmod_module_destroy(module
);