1 /* Extended Module Player
2 * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 * THE SOFTWARE.
21 */
22
23 #include "common.h"
24 #include "list.h"
25 #include "iff.h"
26
27 #include "loader.h"
28
29 struct iff_data {
30 struct list_head iff_list;
31 unsigned id_size;
32 unsigned flags;
33 };
34
iff_process(iff_handle opaque,struct module_data * m,char * id,long size,HIO_HANDLE * f,void * parm)35 static int iff_process(iff_handle opaque, struct module_data *m, char *id, long size,
36 HIO_HANDLE *f, void *parm)
37 {
38 struct iff_data *data = (struct iff_data *)opaque;
39 struct list_head *tmp;
40 struct iff_info *i;
41 int pos;
42
43 pos = hio_tell(f);
44
45 list_for_each(tmp, &data->iff_list) {
46 i = list_entry(tmp, struct iff_info, list);
47 if (id && !memcmp(id, i->id, data->id_size)) {
48 D_(D_WARN "Load IFF chunk %s (%ld) @%d", id, size, pos);
49 if (size > IFF_MAX_CHUNK_SIZE) {
50 return -1;
51 }
52 if (i->loader(m, size, f, parm) < 0) {
53 return -1;
54 }
55 break;
56 }
57 }
58
59 if (hio_seek(f, pos + size, SEEK_SET) < 0) {
60 return -1;
61 }
62
63 return 0;
64 }
65
iff_chunk(iff_handle opaque,struct module_data * m,HIO_HANDLE * f,void * parm)66 static int iff_chunk(iff_handle opaque, struct module_data *m, HIO_HANDLE *f, void *parm)
67 {
68 struct iff_data *data = (struct iff_data *)opaque;
69 unsigned size;
70 char id[17] = "";
71
72 D_(D_INFO "chunk id size: %d", data->id_size);
73 if (hio_read(id, 1, data->id_size, f) != data->id_size) {
74 (void)hio_error(f); /* clear error flag */
75 return 1;
76 }
77 D_(D_INFO "chunk id: [%s]", id);
78
79 if (data->flags & IFF_SKIP_EMBEDDED) {
80 /* embedded RIFF hack */
81 if (!strncmp(id, "RIFF", 4)) {
82 hio_read32b(f);
83 hio_read32b(f);
84 /* read first chunk ID instead */
85 if (hio_read(id, 1, data->id_size, f) != data->id_size){
86 return 1;
87 }
88 }
89 }
90
91 if (data->flags & IFF_LITTLE_ENDIAN) {
92 size = hio_read32l(f);
93 } else {
94 size = hio_read32b(f);
95 }
96 D_(D_INFO "size: %d", size);
97
98 if (hio_error(f)) {
99 return -1;
100 }
101
102 if (data->flags & IFF_CHUNK_ALIGN2) {
103 /* Sanity check */
104 if (size > 0xfffffffe) {
105 return -1;
106 }
107 size = (size + 1) & ~1;
108 }
109
110 if (data->flags & IFF_CHUNK_ALIGN4) {
111 /* Sanity check */
112 if (size > 0xfffffffc) {
113 return -1;
114 }
115 size = (size + 3) & ~3;
116 }
117
118 if (data->flags & IFF_FULL_CHUNK_SIZE) {
119 if (size < data->id_size + 4)
120 return -1;
121 size -= data->id_size + 4;
122 }
123
124 return iff_process(opaque, m, id, size, f, parm);
125 }
126
libxmp_iff_new()127 iff_handle libxmp_iff_new()
128 {
129 struct iff_data *data;
130
131 data = malloc(sizeof(struct iff_data));
132 if (data == NULL) {
133 return NULL;
134 }
135
136 INIT_LIST_HEAD(&data->iff_list);
137 data->id_size = 4;
138 data->flags = 0;
139
140 return (iff_handle)data;
141 }
142
libxmp_iff_load(iff_handle opaque,struct module_data * m,HIO_HANDLE * f,void * parm)143 int libxmp_iff_load(iff_handle opaque, struct module_data *m, HIO_HANDLE *f, void *parm)
144 {
145 int ret;
146
147 while (!hio_eof(f)) {
148 ret = iff_chunk(opaque, m, f, parm);
149
150 if (ret > 0)
151 break;
152
153 if (ret < 0)
154 return -1;
155 }
156
157 return 0;
158 }
159
libxmp_iff_register(iff_handle opaque,const char * id,int (* loader)(struct module_data *,int,HIO_HANDLE *,void *))160 int libxmp_iff_register(iff_handle opaque, const char *id,
161 int (*loader)(struct module_data *, int, HIO_HANDLE *, void *))
162 {
163 struct iff_data *data = (struct iff_data *)opaque;
164 struct iff_info *f;
165 int i = 0;
166
167 f = malloc(sizeof(struct iff_info));
168 if (f == NULL)
169 return -1;
170
171 /* Note: previously was an strncpy */
172 for (; i < 4 && id && id[i]; i++)
173 f->id[i] = id[i];
174 for (; i < 4; i++)
175 f->id[i] = '\0';
176
177 f->loader = loader;
178
179 list_add_tail(&f->list, &data->iff_list);
180
181 return 0;
182 }
183
libxmp_iff_release(iff_handle opaque)184 void libxmp_iff_release(iff_handle opaque)
185 {
186 struct iff_data *data = (struct iff_data *)opaque;
187 struct list_head *tmp;
188 struct iff_info *i;
189
190 /* can't use list_for_each, we free the node before incrementing */
191 for (tmp = (&data->iff_list)->next; tmp != (&data->iff_list);) {
192 i = list_entry(tmp, struct iff_info, list);
193 list_del(&i->list);
194 tmp = tmp->next;
195 free(i);
196 }
197
198 free(data);
199 }
200
201 /* Functions to tune IFF mutations */
202
libxmp_iff_id_size(iff_handle opaque,int n)203 void libxmp_iff_id_size(iff_handle opaque, int n)
204 {
205 struct iff_data *data = (struct iff_data *)opaque;
206
207 data->id_size = n;
208 }
209
libxmp_iff_set_quirk(iff_handle opaque,int i)210 void libxmp_iff_set_quirk(iff_handle opaque, int i)
211 {
212 struct iff_data *data = (struct iff_data *)opaque;
213
214 data->flags |= i;
215 }
216