1 /* decrunch.c
2 *
3 * based on load.c from:
4 * Extended Module Player
5 *
6 * Copyright (C) 1996-1999 Claudio Matsuoka and Hipolito Carraro Jr
7 *
8 * CHANGES: (modified for uade by mld)
9 * removed all xmp related code)
10 * added "custom" labels of pp20 files
11 * added support for external unrar decruncher
12 * added support for the external XPK Lib for Unix (the xType usage *g*)
13 *
14 * TODO:
15 * real builtin support for XPK lib for Unix
16 *
17 * This file is part of the Extended Module Player and is distributed
18 * under the terms of the GNU General Public License. See doc/COPYING
19 * for more information.
20 */
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <stdint.h>
26 #include <libgen.h>
27 #include <errno.h>
28 #include <limits.h>
29 #include <unistd.h>
30 #include <assert.h>
31
32 #include "decrunch.h"
33 #include "ppdepack.h"
34 #include "unsqsh.h"
35 #include "mmcmp.h"
36 #include "s404_dec.h"
37 #include "compat.h"
38
39
atomic_fread(void * dst,FILE * f,size_t count)40 size_t atomic_fread(void *dst, FILE *f, size_t count)
41 {
42 uint8_t *p = (uint8_t *) dst;
43 size_t left = count;
44
45 while (left > 0) {
46
47 ssize_t nread = fread(p, 1, left, f);
48
49 if (nread <= 0) {
50 fprintf(stderr, "atomic_fread() failed: %s\n", strerror(errno));
51 return 0;
52 }
53
54 left -= nread;
55 p += nread;
56 }
57
58 return count;
59 }
60
61
atomic_fwrite(FILE * f,void * src,size_t count)62 size_t atomic_fwrite(FILE *f, void *src, size_t count)
63 {
64 uint8_t *p = (uint8_t *) src;
65 size_t left = count;
66
67 while (left > 0) {
68 ssize_t nwrite = fwrite(p, 1, left, f);
69 if (nwrite <= 0) {
70 fprintf(stderr, "atomic_fwrite() failed: %s\n", strerror(errno));
71 return 0;
72 }
73 left -= nwrite;
74 p += nwrite;
75 }
76
77 return count;
78 }
79
80
read_packed_data_to_memory(uint8_t ** buf,size_t * nbytes,uint8_t * header,FILE * in)81 static int read_packed_data_to_memory(uint8_t **buf, size_t *nbytes,
82 uint8_t *header, FILE *in)
83 {
84 if (in == stdin) {
85 size_t bsize = 4096;
86
87 *buf = malloc(bsize);
88 if (*buf == NULL)
89 return -1;
90
91 /* header[] contains nbytes of data initially */
92 memcpy(*buf, header, *nbytes);
93
94 while (1) {
95 size_t n;
96
97 if (*nbytes == bsize) {
98 uint8_t *newbuf;
99
100 bsize *= 2;
101
102 newbuf = realloc(*buf, bsize);
103 if (newbuf == NULL)
104 return -1;
105
106 *buf = newbuf;
107 }
108
109 n = fread(&(*buf)[*nbytes], 1, bsize - *nbytes, in);
110
111 if (n <= 0)
112 break;
113
114 *nbytes += n;
115 }
116
117 } else {
118
119 fseek(in, 0, SEEK_END);
120 *nbytes = ftell(in);
121 fseek(in, 0, SEEK_SET);
122
123 *buf = malloc(*nbytes);
124 if (*buf == NULL)
125 return -1;
126
127 if (atomic_fread(*buf, in, *nbytes) == 0)
128 return -1;
129 }
130
131 return 0;
132 }
133
134
check_header(uint8_t b[16])135 static struct decruncher *check_header(uint8_t b[16])
136 {
137 struct decruncher *decruncher = NULL;
138
139 if ((b[0] == 'P' && b[1] == 'X' && b[2] == '2' && b[3] == '0') ||
140 (b[0] == 'P' && b[1] == 'P' && b[2] == '2' && b[3] == '0')) {
141 decruncher = &decruncher_pp;
142
143 } else if (b[0] == 'X' && b[1] == 'P' && b[2] == 'K' && b[3] == 'F' &&
144 b[8] == 'S' && b[9] == 'Q' && b[10] == 'S' && b[11] == 'H') {
145 decruncher = &decruncher_sqsh;
146
147 } else if (b[0] == 'z' && b[1] == 'i' && b[2] == 'R' && b[3] == 'C' &&
148 b[4] == 'O' && b[5] == 'N' && b[6] == 'i' && b[7] == 'a') {
149 decruncher = &decruncher_mmcmp;
150
151 } else if (b[0] == 'S' && b[1] == '4' && b[2] == '0' && b[3] == '4' &&
152 b[4] < 0x80 && b[8] < 0x80 && b[12] < 0x80) {
153 decruncher = &decruncher_s404;
154 }
155
156 return decruncher;
157 }
158
159
decrunch(FILE * out,const char * filename,int pretend)160 int decrunch(FILE *out, const char *filename, int pretend)
161 {
162 uint8_t b[16];
163 int res;
164 size_t nbytes;
165 FILE *in;
166 char tmpname[PATH_MAX] = "";
167 uint8_t *buf = NULL;
168 struct decruncher *decruncher;
169 int output_to_same_file = (out == NULL);
170
171 if (filename) {
172 in = fopen(filename, "rb");
173 if (in == NULL) {
174 fprintf(stderr, "Unknown file %s\n", filename);
175 goto error;
176 }
177 } else {
178 in = stdin;
179 }
180
181 nbytes = fread(b, 1, sizeof b, in);
182 if (nbytes < sizeof b)
183 goto error;
184
185 decruncher = check_header(b);
186 if (decruncher == NULL)
187 goto error;
188
189 if (filename)
190 fprintf(stderr, "File %s is in %s format.\n", filename, decruncher->name);
191 else
192 fprintf(stderr, "Stream is in %s format.\n", decruncher->name);
193
194 if (pretend)
195 return 0;
196
197 if (read_packed_data_to_memory(&buf, &nbytes, b, in))
198 goto error;
199
200 if (nbytes <= sizeof b)
201 goto error;
202
203 if (output_to_same_file) {
204 int fd;
205
206 snprintf(tmpname, sizeof tmpname, "%s.XXXXXX", filename);
207
208 fd = mkstemp(tmpname);
209 if (fd < 0) {
210 tmpname[0] = 0;
211 fprintf(stderr, "Could not create a temporary file: %s (%s)\n", tmpname, strerror(errno));
212 goto error;
213 }
214
215 out = fdopen(fd, "wb");
216 if (out == NULL) {
217 fprintf(stderr, "Could not fdopen temporary file: %s\n", tmpname);
218 goto error;
219 }
220 }
221
222 res = decruncher->decrunch(buf, nbytes, out);
223
224 if (res < 0)
225 goto error;
226
227 free(buf);
228
229 fclose(in);
230
231 if (output_to_same_file) {
232 fclose(out);
233 out = NULL;
234
235 assert(tmpname[0] != 0);
236
237 #ifdef RENAME_WORKAROUND
238 /* Don't check return value of unlink(). This is for MinGW. */
239 unlink(filename);
240 #endif
241
242 if (rename(tmpname, filename)) {
243 fprintf(stderr, "Rename error: %s -> %s (%s)\n", tmpname, filename, strerror(errno));
244 unlink(tmpname);
245 }
246 }
247
248 return 0;
249
250 error:
251 fclose(in);
252
253 if (tmpname[0]) {
254 if (out != NULL)
255 fclose(out);
256
257 unlink(tmpname);
258 }
259
260 if (buf)
261 free(buf);
262
263 return -1;
264 }
265