mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
libunrar.py with support for extracting files from archive
This commit is contained in:
parent
99e11d3693
commit
bc4e05a417
190
src/libprs500/libunrar.py
Normal file
190
src/libprs500/libunrar.py
Normal file
@ -0,0 +1,190 @@
|
||||
## Copyright (C) 2006 Kovid Goyal kovid@kovidgoyal.net
|
||||
## This program is free software; you can redistribute it and/or modify
|
||||
## it under the terms of the GNU General Public License as published by
|
||||
## the Free Software Foundation; either version 2 of the License, or
|
||||
## (at your option) any later version.
|
||||
##
|
||||
## This program is distributed in the hope that it will be useful,
|
||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU General Public License for more details.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License along
|
||||
## with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
"""
|
||||
This module provides a thin ctypes based wrapper around libunrar.
|
||||
|
||||
See ftp://ftp.rarlabs.com/rar/unrarsrc-3.7.5.tar.gz
|
||||
"""
|
||||
from ctypes import Structure, c_char_p, c_uint, c_void_p, POINTER, \
|
||||
byref, c_wchar_p, CFUNCTYPE, c_int, c_long, c_char, c_wchar
|
||||
from StringIO import StringIO
|
||||
|
||||
from libprs500 import iswindows
|
||||
|
||||
_librar_name = 'libunrar.so'
|
||||
if iswindows:
|
||||
Structure._pack_ = 1
|
||||
_librar_name = 'unrar'
|
||||
from ctypes import windll
|
||||
_libunrar = windll.LoadLibrary('unrar')
|
||||
else:
|
||||
from ctypes import cdll
|
||||
_libunrar = cdll.LoadLibrary(_librar_name)
|
||||
|
||||
RAR_OM_LIST = 0
|
||||
RAR_OM_EXTRACT = 1
|
||||
|
||||
ERAR_END_ARCHIVE = 10
|
||||
ERAR_NO_MEMORY = 11
|
||||
ERAR_BAD_DATA = 12
|
||||
ERAR_BAD_ARCHIVE = 13
|
||||
ERAR_UNKNOWN_FORMAT = 14
|
||||
ERAR_EOPEN = 15
|
||||
ERAR_ECREATE = 16
|
||||
ERAR_ECLOSE = 17
|
||||
ERAR_EREAD = 18
|
||||
ERAR_EWRITE = 19
|
||||
ERAR_SMALL_BUF = 20
|
||||
ERAR_UNKNOWN = 21
|
||||
ERAR_MISSING_PASSWORD = 22
|
||||
|
||||
RAR_VOL_ASK = 0
|
||||
RAR_VOL_NOTIFY = 1
|
||||
|
||||
RAR_SKIP = 0
|
||||
RAR_TEST = 1
|
||||
RAR_EXTRACT = 2
|
||||
|
||||
class UnRARException(Exception):
|
||||
pass
|
||||
|
||||
class RAROpenArchiveDataEx(Structure):
|
||||
_fields_ = [
|
||||
('ArcName', c_char_p),
|
||||
('ArcNameW', c_wchar_p),
|
||||
('OpenMode', c_uint),
|
||||
('OpenResult', c_uint),
|
||||
('CmtBuf', c_char_p),
|
||||
('CmtBufSize', c_uint),
|
||||
('CmtSize', c_uint),
|
||||
('CmtState', c_uint),
|
||||
('Flags', c_uint),
|
||||
('Reserved', c_uint * 32)
|
||||
]
|
||||
|
||||
class RARHeaderDataEx(Structure):
|
||||
_fields_ = [
|
||||
('ArcName', c_char*1024),
|
||||
('ArcNameW', c_wchar*1024),
|
||||
('FileName', c_char*1024),
|
||||
('FileNameW', c_wchar*1024),
|
||||
('Flags', c_uint),
|
||||
('PackSize', c_uint),
|
||||
('PackSizeHigh', c_uint),
|
||||
('UnpSize', c_uint),
|
||||
('UnpSizeHigh', c_uint),
|
||||
('HostOS', c_uint),
|
||||
('FileCRC', c_uint),
|
||||
('FileTime', c_uint),
|
||||
('UnpVer', c_uint),
|
||||
('Method', c_uint),
|
||||
('FileAttr', c_uint),
|
||||
('CmtBuf', c_char_p),
|
||||
('CmtBufSize', c_uint),
|
||||
('CmtSize', c_uint),
|
||||
('CmtState', c_uint),
|
||||
('Reserved', c_uint*1024)
|
||||
]
|
||||
|
||||
# Define a callback function
|
||||
#CALLBACK_FUNC = CFUNCTYPE(c_int, c_uint, c_long, c_char_p, c_long)
|
||||
#def py_callback_func(msg, user_data, p1, p2):
|
||||
# return 0
|
||||
|
||||
#callback_func = CALLBACK_FUNC(py_callback_func)
|
||||
|
||||
_libunrar.RAROpenArchiveEx.argtypes = [POINTER(RAROpenArchiveDataEx)]
|
||||
_libunrar.RAROpenArchiveEx.restype = c_void_p
|
||||
_libunrar.RARReadHeaderEx.argtypes = [c_void_p, POINTER(RARHeaderDataEx)]
|
||||
_libunrar.RARReadHeaderEx.restype = c_int
|
||||
_libunrar.RARProcessFileW.argtypes = [c_void_p, c_int, c_wchar_p, c_wchar_p]
|
||||
_libunrar.RARProcessFileW.restype = c_int
|
||||
_libunrar.RARCloseArchive.argtypes = [c_void_p]
|
||||
_libunrar.RARCloseArchive.restype = c_int
|
||||
_libunrar.RARSetPassword.argtypes = [c_void_p, c_char_p]
|
||||
#_libunrar.RARSetCallback.argtypes = [c_void_p, CALLBACK_FUNC, c_long]
|
||||
|
||||
|
||||
def _interpret_open_error(code, path):
|
||||
msg = 'Unknown error.'
|
||||
if code == ERAR_NO_MEMORY:
|
||||
msg = "Not enough memory to process " + path
|
||||
elif code == ERAR_BAD_DATA:
|
||||
msg = "Archive header broken: " + path
|
||||
elif code == ERAR_BAD_ARCHIVE:
|
||||
msg = path + ' is not a RAR archive.'
|
||||
elif code == ERAR_EOPEN:
|
||||
msg = 'Cannot open ' + path
|
||||
return msg
|
||||
|
||||
def _interpret_process_file_error(code):
|
||||
msg = 'Unknown Error'
|
||||
if code == ERAR_UNKNOWN_FORMAT:
|
||||
msg = 'Unknown archive format'
|
||||
elif code == ERAR_BAD_ARCHIVE:
|
||||
msg = 'Bad volume'
|
||||
elif code == ERAR_ECREATE:
|
||||
msg = 'File create error'
|
||||
elif code == ERAR_EOPEN:
|
||||
msg = 'Volume open error'
|
||||
elif code == ERAR_ECLOSE:
|
||||
msg = 'File close error'
|
||||
elif code == ERAR_EREAD:
|
||||
msg = 'Read error'
|
||||
elif code == ERAR_EWRITE:
|
||||
msg = 'Write error'
|
||||
elif code == ERAR_BAD_DATA:
|
||||
msg = 'CRC error'
|
||||
elif code == ERAR_MISSING_PASSWORD:
|
||||
msg = 'Password is required.'
|
||||
return msg
|
||||
|
||||
def get_archive_info(flags):
|
||||
ios = StringIO()
|
||||
print >>ios, 'Volume:\t\t', 'yes' if (flags & 1) else 'no'
|
||||
print >>ios, 'Comment:\t', 'yes' if (flags & 2) else 'no'
|
||||
print >>ios, 'Locked:\t\t', 'yes' if (flags & 4) else 'no'
|
||||
print >>ios, 'Solid:\t\t', 'yes' if (flags & 8) else 'no'
|
||||
print >>ios, 'New naming:\t', 'yes' if (flags & 16) else 'no'
|
||||
print >>ios, 'Authenticity:\t', 'yes' if (flags & 32) else 'no'
|
||||
print >>ios, 'Recovery:\t', 'yes' if (flags & 64) else 'no'
|
||||
print >>ios, 'Encr.headers:\t', 'yes' if (flags & 128) else 'no'
|
||||
print >>ios, 'First Volume:\t', 'yes' if (flags & 256) else 'no or older than 3.0'
|
||||
return ios.getvalue()
|
||||
|
||||
def extract(path):
|
||||
open_archive_data = RAROpenArchiveDataEx(ArcName=path, OpenMode=RAR_OM_EXTRACT, CmtBuf=None)
|
||||
arc_data = _libunrar.RAROpenArchiveEx(byref(open_archive_data))
|
||||
try:
|
||||
if open_archive_data.OpenResult != 0:
|
||||
raise UnRARException(_interpret_open_error(open_archive_data.OpenResult, path))
|
||||
print 'Archive:', path
|
||||
print get_archive_info(open_archive_data.Flags)
|
||||
header_data = RARHeaderDataEx(CmtBuf=None)
|
||||
#_libunrar.RARSetCallback(arc_data, callback_func, mode)
|
||||
while True:
|
||||
RHCode = _libunrar.RARReadHeaderEx(arc_data, byref(header_data))
|
||||
if RHCode != 0:
|
||||
break
|
||||
PFCode = _libunrar.RARProcessFileW(arc_data, RAR_EXTRACT, None, None)
|
||||
if PFCode != 0:
|
||||
raise UnRARException(_interpret_process_file_error(PFCode))
|
||||
if RHCode == ERAR_BAD_DATA:
|
||||
raise UnRARException('File header broken')
|
||||
finally:
|
||||
_libunrar.RARCloseArchive(arc_data)
|
||||
|
||||
extract(r'z:\home\test.rar')
|
||||
#extract('/home/kovid/ero/Fansadox Collections/C21 Ponygirl Inferno.rar')
|
Loading…
x
Reference in New Issue
Block a user