1///////////////////////////////////////////////////////////////////////////////
2//
3/// \file       auto_decoder.c
4/// \brief      Autodetect between .xz Stream and .lzma (LZMA_Alone) formats
5//
6//  Author:     Lasse Collin
7//
8//  This file has been put into the public domain.
9//  You can do whatever you want with this file.
10//
11///////////////////////////////////////////////////////////////////////////////
12
13#include "stream_decoder.h"
14#include "alone_decoder.h"
15
16
17typedef struct {
18	/// Stream decoder or LZMA_Alone decoder
19	lzma_next_coder next;
20
21	uint64_t memlimit;
22	uint32_t flags;
23
24	enum {
25		SEQ_INIT,
26		SEQ_CODE,
27		SEQ_FINISH,
28	} sequence;
29} lzma_auto_coder;
30
31
32static lzma_ret
33auto_decode(void *coder_ptr, const lzma_allocator *allocator,
34		const uint8_t *restrict in, size_t *restrict in_pos,
35		size_t in_size, uint8_t *restrict out,
36		size_t *restrict out_pos, size_t out_size, lzma_action action)
37{
38	lzma_auto_coder *coder = coder_ptr;
39
40	switch (coder->sequence) {
41	case SEQ_INIT:
42		if (*in_pos >= in_size)
43			return LZMA_OK;
44
45		// Update the sequence now, because we want to continue from
46		// SEQ_CODE even if we return some LZMA_*_CHECK.
47		coder->sequence = SEQ_CODE;
48
49		// Detect the file format. For now this is simple, since if
50		// it doesn't start with 0xFD (the first magic byte of the
51		// new format), it has to be LZMA_Alone, or something that
52		// we don't support at all.
53		if (in[*in_pos] == 0xFD) {
54			return_if_error(lzma_stream_decoder_init(
55					&coder->next, allocator,
56					coder->memlimit, coder->flags));
57		} else {
58			return_if_error(lzma_alone_decoder_init(&coder->next,
59					allocator, coder->memlimit, true));
60
61			// If the application wants to know about missing
62			// integrity check or about the check in general, we
63			// need to handle it here, because LZMA_Alone decoder
64			// doesn't accept any flags.
65			if (coder->flags & LZMA_TELL_NO_CHECK)
66				return LZMA_NO_CHECK;
67
68			if (coder->flags & LZMA_TELL_ANY_CHECK)
69				return LZMA_GET_CHECK;
70		}
71
72	// Fall through
73
74	case SEQ_CODE: {
75		const lzma_ret ret = coder->next.code(
76				coder->next.coder, allocator,
77				in, in_pos, in_size,
78				out, out_pos, out_size, action);
79		if (ret != LZMA_STREAM_END
80				|| (coder->flags & LZMA_CONCATENATED) == 0)
81			return ret;
82
83		coder->sequence = SEQ_FINISH;
84	}
85
86	// Fall through
87
88	case SEQ_FINISH:
89		// When LZMA_DECODE_CONCATENATED was used and we were decoding
90		// LZMA_Alone file, we need to check check that there is no
91		// trailing garbage and wait for LZMA_FINISH.
92		if (*in_pos < in_size)
93			return LZMA_DATA_ERROR;
94
95		return action == LZMA_FINISH ? LZMA_STREAM_END : LZMA_OK;
96
97	default:
98		assert(0);
99		return LZMA_PROG_ERROR;
100	}
101}
102
103
104static void
105auto_decoder_end(void *coder_ptr, const lzma_allocator *allocator)
106{
107	lzma_auto_coder *coder = coder_ptr;
108	lzma_next_end(&coder->next, allocator);
109	lzma_free(coder, allocator);
110	return;
111}
112
113
114static lzma_check
115auto_decoder_get_check(const void *coder_ptr)
116{
117	const lzma_auto_coder *coder = coder_ptr;
118
119	// It is LZMA_Alone if get_check is NULL.
120	return coder->next.get_check == NULL ? LZMA_CHECK_NONE
121			: coder->next.get_check(coder->next.coder);
122}
123
124
125static lzma_ret
126auto_decoder_memconfig(void *coder_ptr, uint64_t *memusage,
127		uint64_t *old_memlimit, uint64_t new_memlimit)
128{
129	lzma_auto_coder *coder = coder_ptr;
130
131	lzma_ret ret;
132
133	if (coder->next.memconfig != NULL) {
134		ret = coder->next.memconfig(coder->next.coder,
135				memusage, old_memlimit, new_memlimit);
136		assert(*old_memlimit == coder->memlimit);
137	} else {
138		// No coder is configured yet. Use the base value as
139		// the current memory usage.
140		*memusage = LZMA_MEMUSAGE_BASE;
141		*old_memlimit = coder->memlimit;
142		ret = LZMA_OK;
143	}
144
145	if (ret == LZMA_OK && new_memlimit != 0)
146		coder->memlimit = new_memlimit;
147
148	return ret;
149}
150
151
152static lzma_ret
153auto_decoder_init(lzma_next_coder *next, const lzma_allocator *allocator,
154		uint64_t memlimit, uint32_t flags)
155{
156	lzma_next_coder_init(&auto_decoder_init, next, allocator);
157
158	if (memlimit == 0)
159		return LZMA_PROG_ERROR;
160
161	if (flags & ~LZMA_SUPPORTED_FLAGS)
162		return LZMA_OPTIONS_ERROR;
163
164	lzma_auto_coder *coder = next->coder;
165	if (coder == NULL) {
166		coder = lzma_alloc(sizeof(lzma_auto_coder), allocator);
167		if (coder == NULL)
168			return LZMA_MEM_ERROR;
169
170		next->coder = coder;
171		next->code = &auto_decode;
172		next->end = &auto_decoder_end;
173		next->get_check = &auto_decoder_get_check;
174		next->memconfig = &auto_decoder_memconfig;
175		coder->next = LZMA_NEXT_CODER_INIT;
176	}
177
178	coder->memlimit = memlimit;
179	coder->flags = flags;
180	coder->sequence = SEQ_INIT;
181
182	return LZMA_OK;
183}
184
185
186extern LZMA_API(lzma_ret)
187lzma_auto_decoder(lzma_stream *strm, uint64_t memlimit, uint32_t flags)
188{
189	lzma_next_strm_init(auto_decoder_init, strm, memlimit, flags);
190
191	strm->internal->supported_actions[LZMA_RUN] = true;
192	strm->internal->supported_actions[LZMA_FINISH] = true;
193
194	return LZMA_OK;
195}
196