jphastings-unrar 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/unrar.rb +147 -0
- metadata +53 -0
data/unrar.rb
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
# = Unrar
|
2
|
+
# A pure ruby implementation of unrar.
|
3
|
+
# == Features
|
4
|
+
# * Allows you to stream data from archives, even if they're not complete
|
5
|
+
# * Cross-Platform
|
6
|
+
# * No dependencies
|
7
|
+
#
|
8
|
+
# == Drawbacks
|
9
|
+
# * Doesn't yet support compression
|
10
|
+
# * Doesn't yet support encryption (though the frame work is in place - does anybody know the spec?)
|
11
|
+
# * Doesn't quite support multi-part archives (nearly there tho!)
|
12
|
+
|
13
|
+
class Unrar
|
14
|
+
# Flags (in little endian)
|
15
|
+
# Archive flags
|
16
|
+
ARCH_VOLUME = 0x0100
|
17
|
+
ARCH_LOCKED = 0x0400
|
18
|
+
ARCH_IS_SOLID = 0x0800
|
19
|
+
ARCH_NEW_NAMES = 0x1000
|
20
|
+
ARCH_RECOVERY = 0x4000
|
21
|
+
ARCH_ENC_HEAD = 0x8000
|
22
|
+
ARCH_FIRST_PART = 0x0001
|
23
|
+
# File flags
|
24
|
+
FILE_CONTINUED = 0x0100
|
25
|
+
FILE_CONTINUES = 0x0200
|
26
|
+
FILE_PASSWORDED = 0x0400
|
27
|
+
FILE_IS_SOLID = 0x1000
|
28
|
+
FILE_HIGH_PACK = 0x0001
|
29
|
+
FILE_UNICODED = 0x0002
|
30
|
+
FILE_IS_SALTED = 0x0004
|
31
|
+
FILE_EXT_TIME = 0x0010
|
32
|
+
FILE_MORE_SIZE = 0x0080
|
33
|
+
# OSes
|
34
|
+
OSes = ['MS DOS','OS/2','Win32','Unix','Mac OS','BeOS']
|
35
|
+
# Attributes
|
36
|
+
Attr = [:packed_size,:real_size,:os,:filename]
|
37
|
+
|
38
|
+
# Opens a RAR file and parses the header data
|
39
|
+
def initialize(filename)
|
40
|
+
@fh = open(File.expand_path(filename),"r")
|
41
|
+
@eof = false
|
42
|
+
# check marker header
|
43
|
+
parse_header
|
44
|
+
end
|
45
|
+
|
46
|
+
# Lists the files in this archive
|
47
|
+
def list_contents
|
48
|
+
@fh.seek(7)
|
49
|
+
parse_header # The archive header
|
50
|
+
@files = []
|
51
|
+
begin
|
52
|
+
while
|
53
|
+
file = parse_header(true)
|
54
|
+
@files.push file
|
55
|
+
# Speed past any data in the file
|
56
|
+
@fh.seek(file[:packed_size],IO::SEEK_CUR)
|
57
|
+
end
|
58
|
+
rescue EOFError
|
59
|
+
end
|
60
|
+
@files
|
61
|
+
end
|
62
|
+
|
63
|
+
# Gets the file id of the filename given in the archive
|
64
|
+
def getid(fname)
|
65
|
+
list_contents if @files.nil?
|
66
|
+
@files.each_index do |n|
|
67
|
+
if @files[n][:filename] == fname
|
68
|
+
fid = n
|
69
|
+
break
|
70
|
+
end
|
71
|
+
end
|
72
|
+
raise StandardError, "That file does not exist" if (fid >= @files.length) or fid.nil?
|
73
|
+
fid
|
74
|
+
end
|
75
|
+
|
76
|
+
# Gets you the data from a given file (by file id). Allows you to specify to start from a specific point (so you can access the contained file from any point for streaming) and how many bytes you want extracted
|
77
|
+
def extract(fid,offset = 0,amount = nil)
|
78
|
+
list_contents if @files.nil?
|
79
|
+
fid = getid(fid) if fid.class != Fixnum
|
80
|
+
raise StandardError, "That file does not exist" if fid >= @files.length
|
81
|
+
amount = @files[fid][:packed_size] - offset if amount.nil?
|
82
|
+
@fh.seek(@files[fid][:datastart] + offset)
|
83
|
+
@fh.read(amount)
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
def parse_header(full = false)
|
88
|
+
@fh.seek(2,IO::SEEK_CUR) # I don't use the CRC
|
89
|
+
details = {}
|
90
|
+
|
91
|
+
case @fh.readpartial(1)[0]
|
92
|
+
when 0x72
|
93
|
+
raise StandardError, "Not a valid RAR file" if @fh.readpartial(4).unpack("vv") != [6689,7]
|
94
|
+
return true
|
95
|
+
when 0x73
|
96
|
+
block = :archive
|
97
|
+
full = false
|
98
|
+
when 0x74
|
99
|
+
block = :file
|
100
|
+
when 0x7b
|
101
|
+
block = :eof
|
102
|
+
raise EOFError
|
103
|
+
return
|
104
|
+
else
|
105
|
+
raise NotImplementedError, "The HEAD_TYPE encountered is not one this library supports"
|
106
|
+
end
|
107
|
+
|
108
|
+
details[:blockstart] = @fh.pos - 3
|
109
|
+
@flags = @fh.readpartial(2).unpack("v")[0]
|
110
|
+
details[:head_size] = @fh.readpartial(2).unpack("v")[0]
|
111
|
+
details[:datastart] = details[:blockstart] + details[:head_size]
|
112
|
+
|
113
|
+
if ((@flags & FILE_MORE_SIZE != 0) or block == :file)
|
114
|
+
details[:packed_size] = @fh.readpartial(4).unpack("V")[0]
|
115
|
+
if (@flags & FILE_HIGH_PACK != 0)
|
116
|
+
# High packing used (over 2Gb size)
|
117
|
+
@fh.seek(21,IO::SEEK_CUR)
|
118
|
+
details[:packed_size] += @fh.readpartial(4).unpack("V")[0]
|
119
|
+
@fh.seek(-25,IO::SEEK_CUR)
|
120
|
+
end
|
121
|
+
else
|
122
|
+
details[:packed_size] = 0
|
123
|
+
end
|
124
|
+
|
125
|
+
if full
|
126
|
+
details[:real_size] = @fh.readpartial(4).unpack("V")[0]
|
127
|
+
details[:os] = OSes[@fh.readpartial(1).unpack("h")[0].to_i]
|
128
|
+
@fh.seek(4,IO::SEEK_CUR) # The file CRC, not used at the moment...
|
129
|
+
# DOS date/time
|
130
|
+
@fh.readpartial(4).unpack("v").collect{|t| ((t & 0xF800) >> 11) + ((t & 0x07E0) >> 5)*60 + ((t & 0x001F) * 3600)}[0] # - just the time section atm
|
131
|
+
@fh.seek(1,IO::SEEK_CUR) # RAR version, ought to check this
|
132
|
+
details[:compression_level] = @fh.readpartial(1)[0] - 0x30
|
133
|
+
fnamelength = @fh.readpartial(2).unpack("v")[0]
|
134
|
+
details[:attributes] = @fh.readpartial(4).unpack("V")[0]
|
135
|
+
if (@flags & FILE_HIGH_PACK != 0)
|
136
|
+
@fh.seek(4,IO::SEEK_CUR) # We already got the full pack size above
|
137
|
+
details[:size] += @fh.readpartial(4).unpack("V")[0] * 0x100000000
|
138
|
+
end
|
139
|
+
details[:filename] = @fh.readpartial(fnamelength).unpack("a*")[0]
|
140
|
+
|
141
|
+
end
|
142
|
+
|
143
|
+
# ff to the end of the header
|
144
|
+
@fh.seek(details[:blockstart] + details[:head_size])
|
145
|
+
return details if full
|
146
|
+
end
|
147
|
+
end
|
metadata
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jphastings-unrar
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- JP Hastings-Spital
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-05-16 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Pure ruby implementation of RarLabs' Unrar software. Doesn't yet support encryption or compression. Written to allow streaming of data from archives.
|
17
|
+
email: unrar@projects.kedakai.co.uk
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- unrar.rb
|
26
|
+
has_rdoc: true
|
27
|
+
homepage: http://projects.kedakai.co.uk/unrar/
|
28
|
+
post_install_message:
|
29
|
+
rdoc_options: []
|
30
|
+
|
31
|
+
require_paths:
|
32
|
+
- .
|
33
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
34
|
+
requirements:
|
35
|
+
- - ">="
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: "0"
|
38
|
+
version:
|
39
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: "0"
|
44
|
+
version:
|
45
|
+
requirements: []
|
46
|
+
|
47
|
+
rubyforge_project:
|
48
|
+
rubygems_version: 1.2.0
|
49
|
+
signing_key:
|
50
|
+
specification_version: 2
|
51
|
+
summary: Pure ruby implementation of RarLabs' Unrar software. Doesn't yet support encryption or compression. Written to allow streaming of data from archives.
|
52
|
+
test_files: []
|
53
|
+
|