source: mainline/uspace/lib/trackmod/xm.c@ 1d2f85e

Last change on this file since 1d2f85e was 08e103d4, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

Use clearer naming for string length functions

This and the following commit change the names of functions, as well as
their documentation, to use unambiguous terms "bytes" and "code points"
instead of ambiguous terms "size", "length", and "characters".

  • Property mode set to 100644
File size: 11.0 KB
Line 
1/*
2 * Copyright (c) 2014 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
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.
16 *
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.
27 */
28
29/** @addtogroup trackmod
30 * @{
31 */
32/**
33 * @file Extended Module (.xm).
34 */
35
36#include <errno.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include <mem.h>
40#include <types/common.h>
41
42#include "byteorder.h"
43#include "xm.h"
44#include "trackmod.h"
45#include "types/xm.h"
46
47static char xm_id_text[] = "Extended Module: ";
48
49/** Load XM order list
50 *
51 * @param xm_hdr XM header
52 * @param module Module
53 * @return EOK on success, EIO on format error, ENOMEM if out of memory.
54 */
55static errno_t trackmod_xm_load_order_list(xm_hdr_t *xm_hdr, trackmod_module_t *module)
56{
57 errno_t rc;
58 size_t i;
59
60 /* Order list */
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 */
64 rc = EIO;
65 goto error;
66 }
67
68 module->ord_list = calloc(sizeof(size_t), module->ord_list_len);
69 if (module->ord_list == NULL) {
70 printf("Out of memory.\n");
71 rc = ENOMEM;
72 goto error;
73 }
74
75 for (i = 0; i < module->ord_list_len; i++) {
76 module->ord_list[i] = xm_hdr->pat_ord_table[i];
77 }
78
79 module->restart_pos = uint16_t_le2host(xm_hdr->restart_pos);
80 if (module->restart_pos >= module->ord_list_len) {
81 rc = EIO;
82 goto error;
83 }
84
85 return EOK;
86error:
87 return rc;
88}
89
90/** Decode XM pattern.
91 *
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.
95 */
96static errno_t trackmod_xm_decode_pattern(uint8_t *data, size_t dsize,
97 trackmod_pattern_t *pattern)
98{
99 size_t cells;
100 size_t i;
101 size_t si;
102 uint8_t mask;
103
104 cells = pattern->rows * pattern->channels;
105 si = 0;
106
107 for (i = 0; i < cells; i++) {
108 if (si >= dsize)
109 return EINVAL;
110
111 if ((data[si] & 0x80) != 0) {
112 mask = data[si++] & 0x1f;
113 } else {
114 mask = 0x1f;
115 }
116
117 /* Note */
118 if ((mask & 0x1) != 0) {
119 if (si >= dsize)
120 return EINVAL;
121 pattern->data[i].note = data[si++] & 0x7f;
122 }
123
124 /* Instrument */
125 if ((mask & 0x2) != 0) {
126 if (si >= dsize)
127 return EINVAL;
128 pattern->data[i].instr = data[si++];
129 }
130
131 /* Volume */
132 if ((mask & 0x4) != 0) {
133 if (si >= dsize)
134 return EINVAL;
135 pattern->data[i].volume = data[si++];
136 }
137
138 /* Effect type */
139 if ((mask & 0x8) != 0) {
140 if (si >= dsize)
141 return EINVAL;
142 pattern->data[i].effect = (unsigned)data[si++] << 8;
143 }
144
145 /* Effect parameter */
146 if ((mask & 0x10) != 0) {
147 if (si >= dsize)
148 return EINVAL;
149 pattern->data[i].effect |= data[si++];
150 }
151 }
152
153 /* Note: Ignoring any extra data */
154
155 return EOK;
156}
157
158/** Load XM patterns
159 *
160 * @param file File
161 * @param module Module
162 * @return EOK on success, EIO on format error, ENOMEM if out of memory.
163 */
164static errno_t trackmod_xm_load_patterns(FILE *f, trackmod_module_t *module)
165{
166 size_t i;
167 size_t hdr_size;
168 uint8_t pack_type;
169 size_t rows;
170 size_t data_size;
171 ssize_t nread;
172 xm_pattern_t pattern;
173 uint8_t *buf = NULL;
174 long seek_amount;
175 errno_t rc;
176 int ret;
177
178 module->pattern = calloc(sizeof(trackmod_pattern_t), module->patterns);
179 if (module->pattern == NULL) {
180 rc = ENOMEM;
181 goto error;
182 }
183
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)) {
187 rc = EIO;
188 goto error;
189 }
190
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);
195
196 if (pack_type != 0) {
197 rc = EIO;
198 goto error;
199 }
200
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) {
204 rc = EIO;
205 goto error;
206 }
207
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);
212
213 if (module->pattern[i].data == NULL) {
214 rc = ENOMEM;
215 goto error;
216 }
217
218 buf = calloc(1, data_size);
219 if (buf == NULL) {
220 rc = ENOMEM;
221 goto error;
222 }
223
224 nread = fread(buf, 1, data_size, f);
225 if (nread != (ssize_t)data_size) {
226 rc = EIO;
227 goto error;
228 }
229
230 rc = trackmod_xm_decode_pattern(buf, data_size,
231 &module->pattern[i]);
232 if (rc != EOK)
233 goto error;
234
235 free(buf);
236 buf = NULL;
237 }
238
239 return EOK;
240error:
241 free(buf);
242 return rc;
243}
244
245/** Decode XM sample data.
246 *
247 * XM sample data is delta-encoded. Undo the delta encoding and convert
248 * byte order.
249 */
250static void trackmod_xm_decode_sample_data(trackmod_sample_t *sample)
251{
252 size_t i;
253 int8_t *i8p;
254 int16_t *i16p;
255 int8_t cur8;
256 int16_t cur16;
257
258 if (sample->bytes_smp == 1) {
259 cur8 = 0;
260 i8p = (int8_t *)sample->data;
261
262 for (i = 0; i < sample->length; i++) {
263 cur8 = cur8 + i8p[i];
264 i8p[i] = cur8;
265 }
266 } else {
267 cur16 = 0;
268 i16p = (int16_t *)sample->data;
269
270 for (i = 0; i < sample->length; i++) {
271 cur16 = cur16 + (int16_t)uint16_t_le2host(i16p[i]);
272 i16p[i] = cur16;
273 }
274 }
275}
276
277/** Load XM instruments
278 *
279 * @param file File
280 * @param module Module
281 * @return EOK on success, EIO on format error, ENOMEM if out of memory.
282 */
283static errno_t trackmod_xm_load_instruments(xm_hdr_t *xm_hdr, FILE *f,
284 trackmod_module_t *module)
285{
286 size_t i, j;
287 xm_instr_t instr;
288 xm_instr_ext_t instrx;
289 xm_smp_t smp;
290 size_t samples;
291 size_t instr_bytes;
292 size_t smp_size;
293 size_t smp_hdr_size = 0; /* GCC false alarm on uninitialized */
294 ssize_t nread;
295 uint8_t ltype;
296 trackmod_sample_t *sample;
297 void *smp_data;
298 long pos;
299 errno_t rc;
300 int ret;
301
302 module->instrs = uint16_t_le2host(xm_hdr->instruments);
303 module->instr = calloc(module->instrs, sizeof(trackmod_instr_t));
304 if (module->instr == NULL)
305 return ENOMEM;
306
307 for (i = 0; i < module->instrs; i++) {
308 pos = ftell(f);
309 ret = fread(&instr, 1, sizeof(xm_instr_t), f);
310 if (ret != sizeof(xm_instr_t)) {
311 rc = EIO;
312 goto error;
313 }
314
315 samples = uint16_t_le2host(instr.samples);
316 instr_bytes = (size_t)uint32_t_le2host(instr.size);
317
318 if (samples > 0) {
319 ret = fread(&instrx, 1, sizeof(xm_instr_ext_t), f);
320 if (ret != sizeof(xm_instr_ext_t)) {
321 rc = EIO;
322 goto error;
323 }
324
325 smp_hdr_size = uint32_t_le2host(instrx.smp_hdr_size);
326
327 for (j = 0; j < xm_smp_note_size; j++) {
328 module->instr[i].key_smp[j] =
329 instrx.smp_note[j];
330 }
331
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) {
336 rc = ENOMEM;
337 goto error;
338 }
339 }
340
341 if (fseek(f, pos + instr_bytes, SEEK_SET) < 0) {
342 rc = EIO;
343 goto error;
344 }
345
346 for (j = 0; j < samples; j++) {
347 sample = &module->instr[i].sample[j];
348 pos = ftell(f);
349
350 ret = fread(&smp, 1, sizeof(xm_smp_t), f);
351 if (ret != sizeof(xm_smp_t)) {
352 rc = EIO;
353 goto error;
354 }
355
356 smp_size = (size_t)uint32_t_le2host(smp.length);
357
358 smp_data = calloc(smp_size, 1);
359 if (smp_data == NULL) {
360 rc = ENOMEM;
361 goto error;
362 }
363
364 if (fseek(f, pos + smp_hdr_size, SEEK_SET) < 0) {
365 rc = EIO;
366 goto error;
367 }
368
369 nread = fread(smp_data, 1, smp_size, f);
370 if (nread != (ssize_t)smp_size) {
371 rc = EIO;
372 goto error;
373 }
374
375 if (smp.smp_type & (1 << xmst_16_bit)) {
376 sample->bytes_smp = 2;
377 } else {
378 sample->bytes_smp = 1;
379 }
380
381 sample->data = smp_data;
382 sample->length = smp_size / sample->bytes_smp;
383
384 ltype = smp.smp_type & 0x3;
385 switch (ltype) {
386 case xmsl_no_loop:
387 sample->loop_type = tl_no_loop;
388 break;
389 case xmsl_forward_loop:
390 sample->loop_type = tl_forward_loop;
391 break;
392 case xmsl_pingpong_loop:
393 sample->loop_type = tl_pingpong_loop;
394 break;
395 default:
396 rc = EIO;
397 goto error;
398 }
399
400 sample->loop_start =
401 uint32_t_le2host(smp.loop_start) / sample->bytes_smp;
402 sample->loop_len =
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;
407
408 trackmod_xm_decode_sample_data(sample);
409 }
410 }
411
412 return EOK;
413error:
414 return rc;
415}
416
417/** Load extended module.
418 *
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.
423 */
424errno_t trackmod_xm_load(char *fname, trackmod_module_t **rmodule)
425{
426 FILE *f = NULL;
427 trackmod_module_t *module = NULL;
428 xm_hdr_t xm_hdr;
429 size_t nread;
430 size_t hdr_size;
431 errno_t rc;
432
433 f = fopen(fname, "rb");
434 if (f == NULL) {
435 printf("Error opening file.\n");
436 rc = EIO;
437 goto error;
438 }
439
440 nread = fread(&xm_hdr, 1, sizeof(xm_hdr_t), f);
441 if (nread < sizeof(xm_hdr_t)) {
442 printf("File too small.\n");
443 rc = EIO;
444 goto error;
445 }
446
447 if (memcmp(xm_hdr.id_text, xm_id_text, xm_id_text_size) != 0) {
448 rc = EIO;
449 goto error;
450 }
451
452 module = trackmod_module_new();
453 if (module == NULL) {
454 printf("Out of memory.\n");
455 rc = ENOMEM;
456 goto error;
457 }
458
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);
462
463 hdr_size = (size_t)uint32_t_le2host(xm_hdr.hdr_size) +
464 offsetof(xm_hdr_t, hdr_size);
465
466 module->def_bpm = uint16_t_le2host(xm_hdr.def_bpm);
467 module->def_tpr = uint16_t_le2host(xm_hdr.def_tempo);
468
469 /* Jump to end of file header */
470 if (fseek(f, hdr_size, SEEK_SET) < 0) {
471 rc = EIO;
472 goto error;
473 }
474
475 rc = trackmod_xm_load_order_list(&xm_hdr, module);
476 if (rc != EOK)
477 goto error;
478
479 rc = trackmod_xm_load_patterns(f, module);
480 if (rc != EOK)
481 goto error;
482
483 rc = trackmod_xm_load_instruments(&xm_hdr, f, module);
484 if (rc != EOK)
485 goto error;
486
487 (void) fclose(f);
488
489 *rmodule = module;
490 return EOK;
491error:
492 if (module != NULL)
493 trackmod_module_destroy(module);
494 if (f != NULL)
495 (void) fclose(f);
496 return rc;
497}
498
499/** @}
500 */
Note: See TracBrowser for help on using the repository browser.