1 /*****************************************************************************
2 * rar.h: uncompressed RAR parser
3 *****************************************************************************
4 * Copyright (C) 2008-2010 Laurent Aimar
7 * Author: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
31 #include <vlc_common.h>
32 #include <vlc_plugin.h>
33 #include <vlc_stream.h>
40 static const uint8_t rar_marker[] = {
41 0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00
43 static const int rar_marker_size = sizeof(rar_marker);
45 void RarFileDelete(rar_file_t *file)
47 for (int i = 0; i < file->chunk_count; i++)
63 RAR_BLOCK_MARKER = 0x72,
64 RAR_BLOCK_ARCHIVE = 0x73,
65 RAR_BLOCK_FILE = 0x74,
69 RAR_BLOCK_END_HAS_NEXT = 0x0001,
72 RAR_BLOCK_FILE_HAS_PREVIOUS = 0x0001,
73 RAR_BLOCK_FILE_HAS_NEXT = 0x0002,
74 RAR_BLOCK_FILE_HAS_HIGH = 0x0100,
77 static int PeekBlock(stream_t *s, rar_block_t *hdr)
80 int peek_size = stream_Peek(s, &peek, 11);
85 hdr->crc = GetWLE(&peek[0]);
87 hdr->flags = GetWLE(&peek[3]);
88 hdr->size = GetWLE(&peek[5]);
90 if (hdr->flags & 0x8000) {
93 hdr->add_size = GetDWLE(&peek[7]);
100 static int SkipBlock(stream_t *s, const rar_block_t *hdr)
102 uint64_t size = (uint64_t)hdr->size + hdr->add_size;
105 int skip = __MIN(size, INT_MAX);
106 if (stream_Read(s, NULL, skip) < skip)
114 static int IgnoreBlock(stream_t *s, int block)
118 if (PeekBlock(s, &bk) || bk.type != block)
120 return SkipBlock(s, &bk);
123 static int SkipEnd(stream_t *s, const rar_block_t *hdr)
125 if (!(hdr->flags & RAR_BLOCK_END_HAS_NEXT))
128 if (SkipBlock(s, hdr))
131 /* Now, we need to look for a marker block,
132 * It seems that there is garbage at EOF */
136 if (stream_Peek(s, &peek, rar_marker_size) < rar_marker_size)
139 if (!memcmp(peek, rar_marker, rar_marker_size))
142 if (stream_Read(s, NULL, 1) != 1)
146 /* Skip marker and archive blocks */
147 if (IgnoreBlock(s, RAR_BLOCK_MARKER))
149 if (IgnoreBlock(s, RAR_BLOCK_ARCHIVE))
155 static int SkipFile(stream_t *s, int *count, rar_file_t ***file, const rar_block_t *hdr)
160 if (hdr->flags & RAR_BLOCK_FILE_HAS_HIGH)
162 if (hdr->size < (unsigned)min_size)
165 if (stream_Peek(s, &peek, min_size) < min_size)
169 uint32_t file_size_low = GetDWLE(&peek[7+4]);
170 uint8_t method = peek[7+18];
171 uint16_t name_size = GetWLE(&peek[7+19]);
172 uint32_t file_size_high = 0;
173 if (hdr->flags & RAR_BLOCK_FILE_HAS_HIGH)
174 file_size_high = GetDWLE(&peek[7+25]);
175 const uint64_t file_size = ((uint64_t)file_size_high << 32) | file_size_low;
177 char *name = calloc(1, name_size + 1);
181 const int name_offset = (hdr->flags & RAR_BLOCK_FILE_HAS_HIGH) ? (7+33) : (7+25);
182 if (name_offset + name_size <= hdr->size) {
183 const int max_size = name_offset + name_size;
184 if (stream_Peek(s, &peek, max_size) < max_size) {
188 memcpy(name, &peek[name_offset], name_size);
191 if (method != 0x30) {
192 msg_Warn(s, "Ignoring compressed file %s (method=0x%2.2x)", name, method);
197 rar_file_t *current = *count > 0 ? (*file)[*count - 1] : NULL;
199 (current->is_complete ||
200 current->size != file_size ||
201 strcmp(current->name, name) ||
202 (hdr->flags & RAR_BLOCK_FILE_HAS_PREVIOUS) == 0))
206 current = malloc(sizeof(*current));
209 TAB_APPEND(*count, *file, current);
211 current->name = name;
212 current->size = file_size;
213 current->is_complete = false;
214 current->real_size = 0;
215 TAB_INIT(current->chunk_count, current->chunk);
221 rar_file_chunk_t *chunk = malloc(sizeof(*chunk));
223 chunk->offset = stream_Tell(s) + hdr->size;
224 chunk->size = hdr->add_size;
225 chunk->cummulated_size = 0;
226 if (current->chunk_count > 0) {
227 rar_file_chunk_t *previous = current->chunk[current->chunk_count-1];
229 chunk->cummulated_size += previous->cummulated_size +
233 TAB_APPEND(current->chunk_count, current->chunk, chunk);
235 current->real_size += hdr->add_size;
237 if ((hdr->flags & RAR_BLOCK_FILE_HAS_NEXT) == 0)
238 current->is_complete = true;
244 /* We stop on the first non empty file if we cannot seek */
246 bool can_seek = false;
247 stream_Control(s, STREAM_CAN_SEEK, &can_seek);
248 if (!can_seek && current->size > 0)
252 if (SkipBlock(s, hdr))
257 int RarProbe(stream_t *s)
260 if (stream_Peek(s, &peek, rar_marker_size) < rar_marker_size)
262 if (memcmp(peek, rar_marker, rar_marker_size))
267 int RarParse(stream_t *s, int *count, rar_file_t ***file)
273 if (IgnoreBlock(s, RAR_BLOCK_MARKER))
277 if (IgnoreBlock(s, RAR_BLOCK_ARCHIVE))
285 if (PeekBlock(s, &bk))
290 ret = SkipEnd(s, &bk);
293 ret = SkipFile(s, count, file, &bk);
296 ret = SkipBlock(s, &bk);