support multiple files in archives

This commit is contained in:
gen2brain 2013-04-06 11:08:40 +02:00
parent ade2ede291
commit c901c27246
3 changed files with 110 additions and 92 deletions

View file

@ -15,7 +15,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import sys
import bz2
import gzip
import zipfile
@ -23,12 +22,7 @@ import shutil
import tempfile
from subprocess import Popen, PIPE
try:
from m64py.utils import which
except ImportError, err:
sys.stderr.write("Error: Can't import m64py modules%s%s%s" % (
os.linesep, str(err), os.linesep))
sys.exit(1)
from m64py.utils import which
try:
import UnRAR2
@ -64,12 +58,11 @@ class Archive():
def __init__(self, filename):
"""Opens archive."""
self.file = os.path.realpath(filename)
if not os.path.isfile(self.file) \
or not os.access(self.file, os.R_OK):
raise IOError("Cannot open %s. No such file." % (
self.file))
if not os.path.isfile(self.file) or not os.access(self.file, os.R_OK):
raise IOError("Cannot open %s. No such file." % (self.file))
self.filetype = self.get_filetype()
if self.filetype == ZIP:
self.fd = zipfile.ZipFile(self.file, 'r')
elif self.filetype == GZIP:
@ -84,41 +77,39 @@ class Archive():
elif RAR_CMD:
self.fd = RarCmd(self.file)
else:
raise IOError("UnRAR2 module or rar/unrar is needed for %s." % (
self.file))
raise IOError("UnRAR2 module or rar/unrar is needed for %s." % (self.file))
elif self.filetype == LZMA:
if HAS_7Z:
self.fd = Archive7z(open(self.file, 'rb'))
elif LZMA_CMD:
self.fd = LzmaCmd(self.file)
else:
raise IOError("lzma module or 7z is needed for %s." % (
self.file))
raise IOError("lzma module or 7z is needed for %s." % (self.file))
else:
raise IOError("File %s is not a N64 ROM file." % (
self.file))
raise IOError("File %s is not a N64 ROM file." % (self.file))
def read(self):
"""Reads data. If archive has more then one
file the first one is used."""
self.namelist = self.get_namelist()
def read(self, filename=None):
"""Reads data."""
data = None
fname = self.namelist[0] if not filename else filename
if self.filetype == ZIP:
data = self.fd.read(self.fd.infolist()[0])
data = self.fd.read(fname)
elif self.filetype == GZIP:
data = self.fd.read()
elif self.filetype == BZIP:
data = self.fd.read()
elif self.filetype == RAR:
if HAS_RAR:
data = self.fd.read_files()[0][1]
data = self.fd.read_files(fname)[0][1]
elif RAR_CMD:
data = self.fd.read()
data = self.fd.read(fname)
elif self.filetype == LZMA:
if HAS_7Z:
data = self.fd.getmember(
self.fd.getnames()[0]).read()
data = self.fd.getmember(fname).read()
elif LZMA_CMD:
data = self.fd.read()
data = self.fd.read(fname)
elif self.filetype == ROM:
data = self.fd.read()
return data
@ -131,6 +122,29 @@ class Archive():
else:
self.fd.close()
def get_namelist(self):
"""Gets list of files in archive."""
if self.filetype == ZIP:
namelist = [name.filename for name in self.fd.infolist()]
elif self.filetype == GZIP:
namelist = [os.path.basename(self.file)]
elif self.filetype == BZIP:
namelist = [os.path.basename(self.file)]
elif self.filetype == RAR:
if HAS_RAR:
for filename in self.fd.infoiter():
namelist.append(filename.filename)
elif RAR_CMD:
namelist = self.fd.namelist
elif self.filetype == LZMA:
if HAS_7Z:
namelist = self.fd.getnames()
elif LZMA_CMD:
namelist = self.fd.namelist
elif self.filetype == ROM:
namelist = [os.path.basename(self.file)]
return namelist
def get_filetype(self):
"""Gets archive type."""
fd = open(self.file, 'rb')
@ -155,13 +169,10 @@ class RarCmd:
def __init__(self, archive):
"""Opens archive."""
self.fd = None
self.file = archive
self.namelist = self.namelist()
self.filename = self.namelist[0]
self.tempdir = tempfile.mkdtemp(self.file)
self.extract()
self.fd = open(os.path.join(
self.tempdir, self.filename), "rb")
self.tempdir = tempfile.mkdtemp()
def namelist(self):
"""Returns list of filenames in archive."""
@ -170,21 +181,24 @@ class RarCmd:
def extract(self):
"""Extracts archive to temp dir."""
proc = Popen([RAR_CMD, 'x', '-kb', '-p-', '-o-', '-inul', '--',
self.file, self.filename, self.tempdir],
stdin=PIPE, stdout=PIPE, stderr=PIPE)
cmd = [RAR_CMD, 'x', '-kb', '-p-', '-o-', '-inul', '--',
self.file, self.filename, self.tempdir]
proc = Popen(cmd, stdout=PIPE, stderr=PIPE)
out = proc.communicate()
if out[1] != '':
raise IOError("Error extracting file %s: %s." % (
self.file, out[1]))
raise IOError("Error extracting file %s: %s." % (self.file, out[1]))
def read(self):
def read(self, filename=None):
"""Reads data."""
self.filename = self.namelist[0] if not filename else filename
self.extract()
self.fd = open(os.path.join(self.tempdir, self.filename), "rb")
return self.fd.read()
def close(self):
"""Closes file descriptor and clean resources."""
self.fd.close()
if self.fd:
self.fd.close()
shutil.rmtree(self.tempdir)
class LzmaCmd:
@ -192,35 +206,35 @@ class LzmaCmd:
def __init__(self, archive):
"""Opens archive."""
self.fd = None
self.file = archive
self.namelist = self.namelist()
self.filename = self.namelist[0]
self.tempdir = tempfile.mkdtemp(self.filename)
self.extract()
self.fd = open(os.path.join(
self.tempdir, self.filename), "rb")
self.tempdir = tempfile.mkdtemp()
def namelist(self):
"""Returns list of filenames in archive."""
proc1 = Popen([LZMA_CMD, 'l', self.file], stdout=PIPE)
proc2 = Popen(['grep', '-F', '...A'], stdin=proc1.stdout, stdout=PIPE)
lines = [name.rstrip(os.linesep) for name in proc2.stdout.readlines()]
proc = Popen([LZMA_CMD, 'l', self.file], stdout=PIPE)
lines = [name.rstrip(os.linesep) for name in proc.stdout.readlines() if '...A' in name]
return [name[53:] for name in lines]
def extract(self):
"""Extracts archive to temp dir."""
proc = Popen([LZMA_CMD, 'x', '-o'+self.tempdir, self.file, self.filename],
stdin=PIPE, stdout=PIPE, stderr=PIPE)
cmd = [LZMA_CMD, 'x', '-o'+self.tempdir, self.file, self.filename]
proc = Popen(cmd, stdout=PIPE, stderr=PIPE)
out = proc.communicate()
if "Error" in out[0]:
raise IOError("Error extracting file %s: %s." % (
self.file, out[0]))
def read(self):
def read(self, filename=None):
"""Reads data."""
self.filename = self.namelist[0] if not filename else filename
self.extract()
self.fd = open(os.path.join(self.tempdir, self.filename), "rb")
return self.fd.read()
def close(self):
"""Closes file descriptor and clean resources."""
self.fd.close()
if self.fd:
self.fd.close()
shutil.rmtree(self.tempdir)

View file

@ -14,28 +14,23 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import QDialog, QMessageBox, QListWidgetItem
from PyQt4.QtGui import QDialog, QMessageBox
try:
from m64py.core.defs import FRONTEND_VERSION
from m64py.ui.about_ui import Ui_AboutDialog
from m64py.ui.license_ui import Ui_LicenseDialog
except ImportError, err:
sys.stderr.write("Error: Can't import m64py modules%s%s%s" % (
os.linesep, str(err), os.linesep))
sys.exit(1)
from m64py.utils import version_split
from m64py.core.defs import FRONTEND_VERSION
from m64py.ui.about_ui import Ui_AboutDialog
from m64py.ui.license_ui import Ui_LicenseDialog
from m64py.ui.archive_ui import Ui_ArchiveDialog
class AboutDialog(QDialog, Ui_AboutDialog):
def __init__(self, parent):
QDialog.__init__(self, parent)
self.setupUi(self)
text = self.labelAbout.text()
text.replace("FRONTEND_VERSION", FRONTEND_VERSION)
text.replace("CORE_VERSION",
str(parent.worker.m64p.core_version))
text = text.replace("FRONTEND_VERSION", FRONTEND_VERSION)
text = text.replace("CORE_VERSION",
version_split(parent.worker.m64p.core_version))
self.labelAbout.setText(text)
self.show()
@ -51,3 +46,17 @@ class InfoDialog(QMessageBox):
self.setText(text)
self.setWindowTitle("Info")
self.show()
class ArchiveDialog(QDialog, Ui_ArchiveDialog):
def __init__(self, parent, files):
QDialog.__init__(self, parent)
self.setupUi(self)
self.build_list(files)
def build_list(self, files):
self.listWidget.clear()
for fname in files:
item = QListWidgetItem(fname)
item.setData(Qt.UserRole, fname)
self.listWidget.addItem(item)
self.listWidget.setCurrentRow(0)

View file

@ -15,21 +15,15 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import sys
import fnmatch
import ConfigParser
from PyQt4.QtGui import *
from PyQt4.QtCore import *
try:
from m64py.utils import log, md5sum
from m64py.archive import Archive, EXT_FILTER
from m64py.ui.romlist_ui import Ui_ROMList
except ImportError, err:
sys.stderr.write("Error: Can't import m64py modules%s%s%s" % (
os.linesep, str(err), os.linesep))
sys.exit(1)
from m64py.utils import log, md5sum
from m64py.archive import Archive, EXT_FILTER
from m64py.ui.romlist_ui import Ui_ROMList
try:
from m64py.ui import title_rc
@ -75,8 +69,8 @@ class ROMList(QMainWindow, Ui_ROMList):
def init(self):
self.read_rom_list()
self.roms = self.qset.value("rom_list", []).toPyObject()
if self.qset.value("show_available", 0).toInt()[0]:
self.roms = self.qset.value("rom_list", [])
if bool(self.qset.value("show_available", 0)):
self.add_available_items(self.roms)
else:
self.add_items()
@ -119,7 +113,7 @@ class ROMList(QMainWindow, Ui_ROMList):
except KeyError:
md5 = key
list_item = QListWidgetItem(rom['goodname'])
list_item.setData(Qt.UserRole, (md5, None))
list_item.setData(Qt.UserRole, (md5, None, None))
list_item.setFlags(Qt.ItemIsEnabled)
self.listWidget.addItem(list_item)
self.pushOpen.setEnabled(False)
@ -135,11 +129,11 @@ class ROMList(QMainWindow, Ui_ROMList):
self.qset.setValue("rom_list", self.roms)
self.qset.sync()
self.listWidget.clear()
for md5, path in self.roms:
for md5, path, fname in self.roms:
if md5 in self.romlist:
goodname = self.romlist[md5]['goodname']
list_item = QListWidgetItem(goodname)
list_item.setData(Qt.UserRole, (md5, path))
list_item.setData(Qt.UserRole, (md5, path, fname))
self.listWidget.addItem(list_item)
self.progressBar.setValue(0)
self.progressBar.hide()
@ -150,7 +144,7 @@ class ROMList(QMainWindow, Ui_ROMList):
def refresh_items(self):
"""Refreshes available ROMs list"""
path_roms = self.qset.value("Paths/ROM").toString()
path_roms = self.qset.value("Paths/ROM")
if not path_roms:
self.parent.emit(SIGNAL(
"info_dialog(PyQt_PyObject)"),
@ -162,31 +156,31 @@ class ROMList(QMainWindow, Ui_ROMList):
self.reader.set_path(path_roms)
self.reader.start()
def file_open(self, path):
def file_open(self, path, fname):
"""Opens ROM file."""
self.close()
if self.parent.isMinimized():
self.parent.activateWindow()
self.parent.emit(SIGNAL(
"file_open(PyQt_PyObject)"), str(path))
"file_open(PyQt_PyObject, PyQt_PyObject)"), path, fname)
def on_progress_bar_changed(self, value):
self.progressBar.setValue(value)
def on_item_open(self):
item = self.listWidget.currentItem()
md5, path = item.data(Qt.UserRole).toPyObject()
md5, path, fname = item.data(Qt.UserRole)
if path:
self.file_open(path)
self.file_open(path, fname)
def on_item_activated(self, item):
md5, path = item.data(Qt.UserRole).toPyObject()
md5, path, fname = item.data(Qt.UserRole)
if path:
self.file_open(path)
self.file_open(path, fname)
def on_item_changed(self, current, previous):
if not current: return
md5, path = current.data(Qt.UserRole).toPyObject()
md5, path, fname = current.data(Qt.UserRole)
title = QPixmap(os.path.join(
self.user_data_path, "title", "%s.png") % md5)
@ -241,7 +235,7 @@ class ROMReader(QThread):
def set_path(self, path):
"""Sets ROM directory path."""
self.rom_path = str(path)
self.rom_path = path
def get_roms(self):
"""Returns ROM list."""
@ -265,13 +259,14 @@ class ROMReader(QThread):
fullpath = os.path.join(self.rom_path, filename)
try:
archive = Archive(fullpath)
romfile = archive.read()
archive.close()
for fname in archive.namelist:
romfile = archive.read(fname)
archive.close()
rom_md5 = md5sum(filedata=romfile)
self.roms.append((rom_md5.upper(), fullpath, fname))
except Exception, err:
log.warn(str(err))
continue
rom_md5 = md5sum(filedata=romfile)
self.roms.append((rom_md5.upper(), fullpath))
percent = float(filenum) / float(num_files) * 100
self.parent.progressBar.emit(
SIGNAL("valueChanged(int)"), percent)