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