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