1 /*
2 
3 Copyright (c) 2011, 2012, Simon Howard
4 
5 Permission to use, copy, modify, and/or distribute this software
6 for any purpose with or without fee is hereby granted, provided
7 that the above copyright notice and this permission notice appear
8 in all copies.
9 
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11 WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12 WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13 AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
14 CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15 LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
16 NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
17 CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 
19  */
20 
21 
22 #include <stdlib.h>
23 #include <string.h>
24 #include <ctype.h>
25 #include <errno.h>
26 
27 #include "lha_arch.h"
28 #include "lha_input_stream.h"
29 
30 // Maximum length of the self-extractor header.
31 // If we don't find an LHA file header after this many bytes, give up.
32 
33 #define MAX_SFX_HEADER_LEN 65536
34 
35 // Size of the lead-in buffer used to skip the self-extractor.
36 
37 #define LEADIN_BUFFER_LEN 24
38 
39 // Magic string to detect an Amiga LhASFX self-extracting file.
40 // This type of self-extractor is special because the program itself
41 // contains a mini-LHA file that must be skipped over to get to
42 // the real one.
43 
44 #define AMIGA_LHASFX_ID "LhASFX V1.2,"
45 
46 typedef enum {
47 	LHA_INPUT_STREAM_INIT,
48 	LHA_INPUT_STREAM_READING,
49 	LHA_INPUT_STREAM_FAIL
50 } LHAInputStreamState;
51 
52 struct _LHAInputStream {
53 	const LHAInputStreamType *type;
54 	void *handle;
55 	LHAInputStreamState state;
56 	uint8_t leadin[LEADIN_BUFFER_LEN];
57 	size_t leadin_len;
58 };
59 
lha_input_stream_new(const LHAInputStreamType * type,void * handle)60 LHAInputStream *lha_input_stream_new(const LHAInputStreamType *type,
61                                      void *handle)
62 {
63 	LHAInputStream *result;
64 
65 	result = calloc(1, sizeof(LHAInputStream));
66 
67 	if (result == NULL) {
68 		return NULL;
69 	}
70 
71 	result->type = type;
72 	result->handle = handle;
73 	result->leadin_len = 0;
74 	result->state = LHA_INPUT_STREAM_INIT;
75 
76 	return result;
77 }
78 
lha_input_stream_free(LHAInputStream * stream)79 void lha_input_stream_free(LHAInputStream *stream)
80 {
81 	// Close the input stream.
82 
83 	if (stream->type->close != NULL) {
84 		stream->type->close(stream->handle);
85 	}
86 
87 	free(stream);
88 }
89 
90 // Check if the specified buffer is the start of a file header.
91 
file_header_match(uint8_t * buf)92 static int file_header_match(uint8_t *buf)
93 {
94 	if (buf[2] != '-' || buf[6] != '-') {
95 		return 0;
96 	}
97 
98 	// LHA algorithm?
99 
100 	if (buf[3] == 'l' && buf[4] == 'h') {
101 		return 1;
102 	}
103 
104 	// LArc algorithm (lz4, lz5, lzs)?
105 
106 	if (buf[3] == 'l' && buf[4] == 'z'
107 	 && (buf[5] == '4' || buf[5] == '5' || buf[5] == 's')) {
108 		return 1;
109 	}
110 
111 	// PMarc algorithm? (pm0, pm2)
112 	// Note: PMarc SFX archives have a -pms- string in them that must
113 	// be ignored.
114 
115 	if (buf[3] == 'p' && buf[4] == 'm' && buf[5] != 's') {
116 		return 1;
117 	}
118 
119 	return 0;
120 }
121 
122 // Empty some of the bytes from the start of the lead-in buffer.
123 
empty_leadin(LHAInputStream * stream,size_t bytes)124 static void empty_leadin(LHAInputStream *stream, size_t bytes)
125 {
126 	memmove(stream->leadin, stream->leadin + bytes,
127 	        stream->leadin_len - bytes);
128 	stream->leadin_len -= bytes;
129 }
130 
131 // Read bytes from the input stream into the specified buffer.
132 
do_read(LHAInputStream * stream,void * buf,size_t buf_len)133 static int do_read(LHAInputStream *stream, void *buf, size_t buf_len)
134 {
135 	return stream->type->read(stream->handle, buf, buf_len);
136 }
137 
138 // Skip the self-extractor header at the start of the file.
139 // Returns non-zero if a header was found.
140 
skip_sfx(LHAInputStream * stream)141 static int skip_sfx(LHAInputStream *stream)
142 {
143 	size_t filepos;
144 	unsigned int i;
145 	int skip_files;
146 	int read;
147 
148 	filepos = 0;
149 	skip_files = 0;
150 
151 	while (filepos < MAX_SFX_HEADER_LEN) {
152 
153 		// Add some more bytes to the lead-in buffer:
154 
155 		read = do_read(stream, stream->leadin + stream->leadin_len,
156 		               LEADIN_BUFFER_LEN - stream->leadin_len);
157 
158 		if (read <= 0) {
159 			break;
160 		}
161 
162 		stream->leadin_len += (unsigned int) read;
163 
164 		// Check the lead-in buffer for a file header.
165 
166 		for (i = 0; i + 12 < stream->leadin_len; ++i) {
167 			if (file_header_match(stream->leadin + i)) {
168 				if (skip_files == 0) {
169 					empty_leadin(stream, i);
170 					return 1;
171 				} else {
172 					--skip_files;
173 				}
174 			}
175 
176 			// Detect Amiga self-extractor.
177 
178 			if (!memcmp(stream->leadin + i, AMIGA_LHASFX_ID,
179 			            strlen(AMIGA_LHASFX_ID))) {
180 				skip_files = 1;
181 			}
182 		}
183 
184 		empty_leadin(stream, i);
185 		filepos += i;
186 	}
187 
188 	return 0;
189 }
190 
lha_input_stream_read(LHAInputStream * stream,void * buf,size_t buf_len)191 int lha_input_stream_read(LHAInputStream *stream, void *buf, size_t buf_len)
192 {
193 	size_t total_bytes, n;
194 	int result;
195 
196 	// Start of the stream?  Skip self-extract header, if there is one.
197 
198 	if (stream->state == LHA_INPUT_STREAM_INIT) {
199 		if (skip_sfx(stream)) {
200 			stream->state = LHA_INPUT_STREAM_READING;
201 		} else {
202 			stream->state = LHA_INPUT_STREAM_FAIL;
203 		}
204 	}
205 
206 	if (stream->state == LHA_INPUT_STREAM_FAIL) {
207 		return 0;
208 	}
209 
210 	// Now fill the result buffer. Start by emptying the lead-in buffer.
211 
212 	total_bytes = 0;
213 
214 	if (stream->leadin_len > 0) {
215 		if (buf_len < stream->leadin_len) {
216 			n = buf_len;
217 		} else {
218 			n = stream->leadin_len;
219 		}
220 
221 		memcpy(buf, stream->leadin, n);
222 		empty_leadin(stream, n);
223 		total_bytes += n;
224 	}
225 
226 	// Read from the input stream.
227 
228 	if (total_bytes < buf_len) {
229 		result = do_read(stream, (uint8_t *) buf + total_bytes,
230 		                 buf_len - total_bytes);
231 
232 		if (result > 0) {
233 			total_bytes += (unsigned int) result;
234 		}
235 	}
236 
237 	// Only successful if the complete buffer is filled.
238 
239 	return total_bytes == buf_len;
240 }
241 
lha_input_stream_skip(LHAInputStream * stream,size_t bytes)242 int lha_input_stream_skip(LHAInputStream *stream, size_t bytes)
243 {
244 	// If we have a dedicated skip function, use it; otherwise,
245 	// the read function can be used to perform a skip.
246 
247 	if (stream->type->skip != NULL) {
248 		return stream->type->skip(stream->handle, bytes);
249 	} else {
250 		uint8_t data[32];
251 		unsigned int len;
252 		int result;
253 
254 		while (bytes > 0) {
255 
256 			// Read as many bytes left as possible to fit in
257 			// the buffer:
258 
259 			if (bytes > sizeof(data)) {
260 				len = sizeof(data);
261 			} else {
262 				len = bytes;
263 			}
264 
265 			result = do_read(stream, data, len);
266 
267 			if (result < 0) {
268 				return 0;
269 			}
270 
271 			bytes -= (unsigned int) result;
272 		}
273 
274 		return 1;
275 	}
276 }
277 
278 // Read data from a FILE * source.
279 
file_source_read(void * handle,void * buf,size_t buf_len)280 static int file_source_read(void *handle, void *buf, size_t buf_len)
281 {
282 	size_t bytes_read;
283 	FILE *fh = handle;
284 
285 	bytes_read = fread(buf, 1, buf_len, fh);
286 
287 	// If an error occurs, zero is returned; however, it may also
288 	// indicate end of file.
289 
290 	if (bytes_read == 0 && !feof(fh)) {
291 		return -1;
292 	}
293 
294 	return (int) bytes_read;
295 }
296 
297 // "Fallback" skip for file source that uses fread(), for unseekable
298 // streams.
299 
file_source_skip_fallback(FILE * handle,size_t bytes)300 static int file_source_skip_fallback(FILE *handle, size_t bytes)
301 {
302 	uint8_t data[32];
303 	unsigned int len;
304 	int result;
305 
306 	while (bytes > 0) {
307 		if (bytes > sizeof(data)) {
308 			len = sizeof(data);
309 		} else {
310 			len = bytes;
311 		}
312 
313 		result = fread(data, 1, len, handle);
314 
315 		if (result != (int) len) {
316 			return 0;
317 		}
318 
319 		bytes -= len;
320 	}
321 
322 	return 1;
323 }
324 
325 // Seek forward in a FILE * input stream.
326 
file_source_skip(void * handle,size_t bytes)327 static int file_source_skip(void *handle, size_t bytes)
328 {
329 	int result;
330 
331 	// If this is an unseekable stream of some kind, always use the
332 	// fallback behavior, as at least this is guaranteed to work.
333 	// This is to work around problems on Windows, where fseek() can
334 	// seek half-way on a stream and *then* fail, leaving us in an
335 	// unworkable situation.
336 
337 	if (ftell(handle) < 0) {
338 		return file_source_skip_fallback(handle, bytes);
339 	}
340 
341 	result = fseek(handle, (long) bytes, SEEK_CUR);
342 
343 	if (result < 0) {
344 		if (errno == EBADF || errno == ESPIPE) {
345 			return file_source_skip_fallback(handle, bytes);
346 		} else {
347 			return 0;
348 		}
349 	}
350 
351 	return 1;
352 }
353 
354 // Close a FILE * input stream.
355 
file_source_close(void * handle)356 static void file_source_close(void *handle)
357 {
358 	fclose(handle);
359 }
360 
361 // "Owned" file source - the stream will be closed when the input
362 // stream is freed.
363 
364 static const LHAInputStreamType file_source_owned = {
365 	file_source_read,
366 	file_source_skip,
367 	file_source_close
368 };
369 
370 // "Unowned" file source - the stream is owned by the calling code.
371 
372 static const LHAInputStreamType file_source_unowned = {
373 	file_source_read,
374 	file_source_skip,
375 	NULL
376 };
377 
lha_input_stream_from(char * filename)378 LHAInputStream *lha_input_stream_from(char *filename)
379 {
380 	LHAInputStream *result;
381 	FILE *fstream;
382 
383 	fstream = fopen(filename, "rb");
384 
385 	if (fstream == NULL) {
386 		return NULL;
387 	}
388 
389 	result = lha_input_stream_new(&file_source_owned, fstream);
390 
391 	if (result == NULL) {
392 		fclose(fstream);
393 	}
394 
395 	return result;
396 }
397 
lha_input_stream_from_FILE(FILE * stream)398 LHAInputStream *lha_input_stream_from_FILE(FILE *stream)
399 {
400 	lha_arch_set_binary(stream);
401 	return lha_input_stream_new(&file_source_unowned, stream);
402 }
403 
404