new version commit
This commit is contained in:
6
deps/libmpq/bindings/Makefile.am
vendored
Normal file
6
deps/libmpq/bindings/Makefile.am
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
# any directories which should be built and installed.
|
||||
SUBDIRS = d
|
||||
|
||||
if HAVE_PYTHON
|
||||
SUBDIRS += python
|
||||
endif
|
||||
6
deps/libmpq/bindings/d/Makefile.am
vendored
Normal file
6
deps/libmpq/bindings/d/Makefile.am
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
# minimum required automake 1.6
|
||||
AUTOMAKE_OPTIONS = 1.6
|
||||
|
||||
# install D binding to /usr/include/d by default
|
||||
libmpq_includedir = $(includedir)/d
|
||||
libmpq_include_HEADERS = mpq.d
|
||||
2
deps/libmpq/bindings/d/dsss.conf
vendored
Normal file
2
deps/libmpq/bindings/d/dsss.conf
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
[mpq.d]
|
||||
type=sourcelibrary
|
||||
318
deps/libmpq/bindings/d/mpq.d
vendored
Normal file
318
deps/libmpq/bindings/d/mpq.d
vendored
Normal file
@@ -0,0 +1,318 @@
|
||||
/*
|
||||
* mpq.d -- D programming language module for libmpq
|
||||
*
|
||||
* Copyright (c) 2008 Georg Lukas <georg@op-co.de>
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* This module is written to support Phobos. Patches to allow binding to
|
||||
* Tango are welcome.
|
||||
*/
|
||||
|
||||
module mpq;
|
||||
|
||||
/* the following pragma does not work on DMD/Linux, generates a warning on
|
||||
* GDC/Linux and has not been tested on Windows. Commented out for now. */
|
||||
// pragma(lib, "libmpq");
|
||||
|
||||
import std.string; // for format() and toStringz()
|
||||
import std.traits; // for ParameterTypeTuple!()
|
||||
|
||||
/* XXX: this assumes that libmpq is compiled with Large File Support on */
|
||||
alias long off_t;
|
||||
|
||||
/* libmpq error return values */
|
||||
const LIBMPQ_ERROR_OPEN = -1; /* open error on file. */
|
||||
const LIBMPQ_ERROR_CLOSE = -2; /* close error on file. */
|
||||
const LIBMPQ_ERROR_SEEK = -3; /* lseek error on file. */
|
||||
const LIBMPQ_ERROR_READ = -4; /* read error on file. */
|
||||
const LIBMPQ_ERROR_WRITE = -5; /* write error on file. */
|
||||
const LIBMPQ_ERROR_MALLOC = -6; /* memory allocation error. */
|
||||
const LIBMPQ_ERROR_FORMAT = -7; /* format errror. */
|
||||
const LIBMPQ_ERROR_NOT_INITIALIZED = -8; /* init() wasn't called. */
|
||||
const LIBMPQ_ERROR_SIZE = -9; /* buffer size is to small. */
|
||||
const LIBMPQ_ERROR_EXIST = -10; /* file or block does not exist in archive. */
|
||||
const LIBMPQ_ERROR_DECRYPT = -11; /* we don't know the decryption seed. */
|
||||
const LIBMPQ_ERROR_UNPACK = -12; /* error on unpacking file. */
|
||||
|
||||
/** libmpq internal meta-data for an archive */
|
||||
extern struct mpq_archive_s;
|
||||
|
||||
extern(C) {
|
||||
|
||||
/* libmpq__generic information about library. */
|
||||
char *libmpq__version();
|
||||
|
||||
/* libmpq__generic mpq archive information. */
|
||||
int libmpq__archive_open(mpq_archive_s **mpq_archive, char *mpq_filename, off_t archive_offset);
|
||||
int libmpq__archive_close(mpq_archive_s *mpq_archive);
|
||||
int libmpq__archive_packed_size(mpq_archive_s *mpq_archive, off_t *packed_size);
|
||||
int libmpq__archive_unpacked_size(mpq_archive_s *mpq_archive, off_t *unpacked_size);
|
||||
int libmpq__archive_offset(mpq_archive_s *mpq_archive, off_t *offset);
|
||||
int libmpq__archive_version(mpq_archive_s *mpq_archive, uint *version_);
|
||||
int libmpq__archive_files(mpq_archive_s *mpq_archive, uint *files);
|
||||
|
||||
/* libmpq__generic file processing functions. */
|
||||
int libmpq__file_packed_size(mpq_archive_s *mpq_archive, uint file_number, off_t *packed_size);
|
||||
int libmpq__file_unpacked_size(mpq_archive_s *mpq_archive, uint file_number, off_t *unpacked_size);
|
||||
int libmpq__file_offset(mpq_archive_s *mpq_archive, uint file_number, off_t *offset);
|
||||
int libmpq__file_blocks(mpq_archive_s *mpq_archive, uint file_number, uint *blocks);
|
||||
int libmpq__file_encrypted(mpq_archive_s *mpq_archive, uint file_number, uint *encrypted);
|
||||
int libmpq__file_compressed(mpq_archive_s *mpq_archive, uint file_number, uint *compressed);
|
||||
int libmpq__file_imploded(mpq_archive_s *mpq_archive, uint file_number, uint *imploded);
|
||||
int libmpq__file_number(mpq_archive_s *mpq_archive, char *filename, uint *number);
|
||||
int libmpq__file_read(mpq_archive_s *mpq_archive, uint file_number, ubyte *out_buf, off_t out_size, off_t *transferred);
|
||||
|
||||
/* libmpq__generic block processing functions. */
|
||||
int libmpq__block_open_offset(mpq_archive_s *mpq_archive, uint file_number);
|
||||
int libmpq__block_close_offset(mpq_archive_s *mpq_archive, uint file_number);
|
||||
int libmpq__block_unpacked_size(mpq_archive_s *mpq_archive, uint file_number, uint block_number, off_t *unpacked_size);
|
||||
int libmpq__block_read(mpq_archive_s *mpq_archive, uint file_number, uint block_number, ubyte *out_buf, off_t out_size, off_t *transferred);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/** exception class for failed libmpq calls */
|
||||
class MPQException : Exception {
|
||||
const string[] Errors = [
|
||||
"unknown error",
|
||||
"open error on file",
|
||||
"close error on file",
|
||||
"lseek error on file",
|
||||
"read error on file",
|
||||
"write error on file",
|
||||
"memory allocation error",
|
||||
"format errror",
|
||||
"init() wasn't called",
|
||||
"buffer size is to small",
|
||||
"file or block does not exist in archive",
|
||||
"we don't know the decryption seed",
|
||||
"error on unpacking file"];
|
||||
|
||||
public int errno;
|
||||
this(char[] fnname = "unknown_function", int errno = 0) {
|
||||
|
||||
this.errno = errno;
|
||||
if (-errno >= Errors.length)
|
||||
errno = 0;
|
||||
super(std.string.format("Error in %s(): %s (%d)",
|
||||
fnname, Errors[-errno], errno));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** template to wrap function calls and throw exceptions in case of error
|
||||
*
|
||||
* thanks for the idea to while(nan) blog,
|
||||
* http://while-nan.blogspot.com/2007/06/wrapping-functions-for-fun-and-profit.html
|
||||
*
|
||||
* use: MPQ_CHECKERR(libmpq__archive_open)(&m, "foo.mpq", -1);
|
||||
* returns the retval of archive_open on success;
|
||||
* throws an MPQException on failure.
|
||||
*
|
||||
* @param Fn libmpq__function reference
|
||||
* @param args libmpq__function parameters
|
||||
* @return return value of libmpq__function on success
|
||||
* @throw MPQException on error
|
||||
*/
|
||||
int MPQ_CHECKERR(alias Fn)(ParameterTypeTuple!(Fn) args)
|
||||
{
|
||||
int result = Fn(args);
|
||||
if (result < 0) {
|
||||
/* XXX: relying on non-specified stringof() behaviour */
|
||||
throw new MPQException((&Fn).stringof[2..$], result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/** mixin alias to wrap library functions into MPQ_CHECKERR.
|
||||
*
|
||||
* alias mpq.func_name(...) to MPQ_CHECKERR(libmpq__func_name)(...)
|
||||
* @param func_name name of the function to be wrapped
|
||||
*/
|
||||
template MPQ_FUNC(char[] func_name) {
|
||||
const char[] MPQ_FUNC = "alias MPQ_CHECKERR!(libmpq__" ~ func_name ~ ") " ~ func_name ~ ";";
|
||||
}
|
||||
|
||||
alias libmpq__version libversion; /* must be direct alias because it returns char*, not error int */
|
||||
mixin(MPQ_FUNC!("archive_open"));
|
||||
mixin(MPQ_FUNC!("archive_close"));
|
||||
mixin(MPQ_FUNC!("archive_packed_size"));
|
||||
mixin(MPQ_FUNC!("archive_unpacked_size"));
|
||||
mixin(MPQ_FUNC!("archive_offset"));
|
||||
mixin(MPQ_FUNC!("archive_version"));
|
||||
mixin(MPQ_FUNC!("archive_files"));
|
||||
mixin(MPQ_FUNC!("file_packed_size"));
|
||||
mixin(MPQ_FUNC!("file_unpacked_size"));
|
||||
mixin(MPQ_FUNC!("file_offset"));
|
||||
mixin(MPQ_FUNC!("file_blocks"));
|
||||
mixin(MPQ_FUNC!("file_encrypted"));
|
||||
mixin(MPQ_FUNC!("file_compressed"));
|
||||
mixin(MPQ_FUNC!("file_imploded"));
|
||||
mixin(MPQ_FUNC!("file_number"));
|
||||
mixin(MPQ_FUNC!("file_read"));
|
||||
mixin(MPQ_FUNC!("block_open_offset"));
|
||||
mixin(MPQ_FUNC!("block_close_offset"));
|
||||
mixin(MPQ_FUNC!("block_unpacked_size"));
|
||||
mixin(MPQ_FUNC!("block_read"));
|
||||
|
||||
/** getter function named name for returning archive_* single values:
|
||||
*
|
||||
* <type> Archive.<name>() { return libmpq__archive_<name>() }
|
||||
*
|
||||
* @param type return type for the original function reference
|
||||
* @param name name of the original function
|
||||
* @param name2 name for the prototype (defaults to name, used for "version")
|
||||
* @return getter function mixin
|
||||
*/
|
||||
template MPQ_A_GET(char[] type, char[] name, char[] name2 = name) {
|
||||
const char[] MPQ_A_GET = type ~ " " ~ name2 ~ "() { " ~
|
||||
type ~ " ret; " ~
|
||||
"archive_" ~ name ~ "(m, &ret); return ret;" ~
|
||||
"}";
|
||||
}
|
||||
|
||||
/** wrapper class for an MPQ Archive
|
||||
*
|
||||
* syntax: auto a = new mpq.Archive("somefile.mpq");
|
||||
*/
|
||||
class Archive {
|
||||
mpq_archive_s *m;
|
||||
File listfile;
|
||||
char[][] listfiledata;
|
||||
|
||||
this(char[] archivename, off_t offset = -1) {
|
||||
archive_open(&m, toStringz(archivename), offset);
|
||||
}
|
||||
|
||||
mixin(MPQ_A_GET!("off_t", "packed_size"));
|
||||
mixin(MPQ_A_GET!("off_t", "unpacked_size"));
|
||||
mixin(MPQ_A_GET!("off_t", "offset"));
|
||||
mixin(MPQ_A_GET!("uint", "version", "version_"));
|
||||
mixin(MPQ_A_GET!("uint", "files"));
|
||||
|
||||
~this() {
|
||||
archive_close(m);
|
||||
}
|
||||
|
||||
mpq_archive_s* archive() {
|
||||
return m;
|
||||
}
|
||||
|
||||
File opIndex(char[] fname) {
|
||||
return new File(this, fname);
|
||||
}
|
||||
File opIndex(int fno) {
|
||||
return new File(this, fno);
|
||||
}
|
||||
|
||||
char[][] filelist() {
|
||||
try {
|
||||
if (!listfile) {
|
||||
listfile = this["(listfile)"];
|
||||
listfiledata = (cast(char[])listfile.read()).splitlines();
|
||||
}
|
||||
return listfiledata;
|
||||
} catch (MPQException e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/+uint filenumber(char[] filename) {
|
||||
try {
|
||||
if (!listfile) {
|
||||
listfile = this["(listfile)"];
|
||||
listfiledata = (cast(char[])listfile.read()).splitlines();
|
||||
}
|
||||
return listfiledata;
|
||||
} catch (MPQException e) {
|
||||
return [];
|
||||
}
|
||||
}+/
|
||||
|
||||
}
|
||||
|
||||
|
||||
/** getter function named name for returning file_* single values:
|
||||
*
|
||||
* <type> File.<name>() { return libmpq__file_<name>() }
|
||||
*
|
||||
* @param type return type for the original function reference
|
||||
* @param name name of the original function
|
||||
* @param name2 name for the prototype (defaults to name, used for "version")
|
||||
* @return getter function mixin
|
||||
*/
|
||||
template MPQ_F_GET(char[] type, char[] name, char[] name2 = name) {
|
||||
const char[] MPQ_F_GET = type ~ " " ~ name2 ~ "() { " ~
|
||||
type ~ " ret; " ~
|
||||
"file_" ~ name ~ "(am, fileno, &ret); " ~
|
||||
"return ret;" ~
|
||||
"}";
|
||||
}
|
||||
|
||||
/** wrapper class for a single file in an MPQ Archive
|
||||
*
|
||||
* syntax:
|
||||
* auto a = new mpq.Archive("somefile.mpq");
|
||||
* auto f = a["(listfile)"];
|
||||
* auto f2 = a[0];
|
||||
* auto f3 = new File(a, "(listfile)");
|
||||
*/
|
||||
class File {
|
||||
Archive a;
|
||||
mpq_archive_s* am;
|
||||
char[] filename;
|
||||
uint fileno;
|
||||
|
||||
this(Archive a, int fileno) {
|
||||
this.a = a;
|
||||
this.am = a.archive();
|
||||
if (fileno >= a.files) {
|
||||
throw new MPQException(format("File(%d)", fileno),
|
||||
LIBMPQ_ERROR_EXIST);
|
||||
}
|
||||
this.filename = format("file%04d.xxx", fileno);
|
||||
this.fileno = fileno;
|
||||
}
|
||||
|
||||
this(Archive a, char[] filename) {
|
||||
this.a = a;
|
||||
this.am = a.archive();
|
||||
this.filename = filename;
|
||||
/* this line will throw an exception when the file is not there */
|
||||
mpq.file_number(am, toStringz(filename), &this.fileno);
|
||||
}
|
||||
|
||||
mixin(MPQ_F_GET!("off_t", "packed_size"));
|
||||
mixin(MPQ_F_GET!("off_t", "unpacked_size"));
|
||||
mixin(MPQ_F_GET!("off_t", "offset"));
|
||||
mixin(MPQ_F_GET!("uint", "blocks"));
|
||||
mixin(MPQ_F_GET!("uint", "encrypted"));
|
||||
mixin(MPQ_F_GET!("uint", "compressed"));
|
||||
mixin(MPQ_F_GET!("uint", "imploded"));
|
||||
|
||||
uint no() { return fileno; }
|
||||
char[] name() { return filename; }
|
||||
|
||||
ubyte[] read() {
|
||||
ubyte[] content;
|
||||
content.length = this.unpacked_size();
|
||||
off_t trans;
|
||||
mpq.file_read(am, fileno, content.ptr, content.length, &trans);
|
||||
content.length = trans;
|
||||
return content;
|
||||
}
|
||||
}
|
||||
5
deps/libmpq/bindings/python/Makefile.am
vendored
Normal file
5
deps/libmpq/bindings/python/Makefile.am
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
# minimum required automake 1.6
|
||||
AUTOMAKE_OPTIONS = 1.6
|
||||
|
||||
# library information and headers which should not be installed.
|
||||
python_PYTHON = mpq.py
|
||||
16
deps/libmpq/bindings/python/mpq-info
vendored
Normal file
16
deps/libmpq/bindings/python/mpq-info
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from __future__ import division
|
||||
|
||||
import sys
|
||||
|
||||
import mpq
|
||||
|
||||
archive = mpq.Archive(sys.argv[1])
|
||||
|
||||
print "Name: %s" % sys.argv[1]
|
||||
print "Version: %s" % archive.filename
|
||||
print "Offset: %s" % archive.offset
|
||||
print "Packed size: %s" % archive.packed_size
|
||||
print "Unpacked size: %s" % archive.unpacked_size
|
||||
print "Compression ratio: %s" % (archive.packed_size/archive.unpacked_size)
|
||||
322
deps/libmpq/bindings/python/mpq.py
vendored
Normal file
322
deps/libmpq/bindings/python/mpq.py
vendored
Normal file
@@ -0,0 +1,322 @@
|
||||
"""wrapper for libmpq"""
|
||||
|
||||
# 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
import ctypes
|
||||
import ctypes.util
|
||||
import os
|
||||
|
||||
libmpq = ctypes.CDLL(ctypes.util.find_library("mpq"))
|
||||
|
||||
class Error(Exception):
|
||||
pass
|
||||
|
||||
errors = {
|
||||
-1: (IOError, "open"),
|
||||
-2: (IOError, "close"),
|
||||
-3: (IOError, "seek"),
|
||||
-4: (IOError, "read"),
|
||||
-5: (IOError, "write"),
|
||||
-6: (MemoryError,),
|
||||
-7: (Error, "file is not an mpq or is corrupted"),
|
||||
-8: (AssertionError, "not initialized"),
|
||||
-9: (AssertionError, "buffer size too small"),
|
||||
-10: (IndexError, "file not in archive"),
|
||||
-11: (AssertionError, "decrypt"),
|
||||
-12: (AssertionError, "unpack"),
|
||||
}
|
||||
|
||||
def check_error(result, func, arguments, errors=errors):
|
||||
try:
|
||||
error = errors[result]
|
||||
except KeyError:
|
||||
return result
|
||||
else:
|
||||
raise error[0](*error[1:])
|
||||
|
||||
libmpq.libmpq__version.restype = ctypes.c_char_p
|
||||
|
||||
libmpq.libmpq__archive_open.errcheck = check_error
|
||||
libmpq.libmpq__archive_close.errcheck = check_error
|
||||
libmpq.libmpq__archive_packed_size.errcheck = check_error
|
||||
libmpq.libmpq__archive_unpacked_size.errcheck = check_error
|
||||
libmpq.libmpq__archive_offset.errcheck = check_error
|
||||
libmpq.libmpq__archive_version.errcheck = check_error
|
||||
libmpq.libmpq__archive_files.errcheck = check_error
|
||||
|
||||
libmpq.libmpq__file_packed_size.errcheck = check_error
|
||||
libmpq.libmpq__file_unpacked_size.errcheck = check_error
|
||||
libmpq.libmpq__file_offset.errcheck = check_error
|
||||
libmpq.libmpq__file_blocks.errcheck = check_error
|
||||
libmpq.libmpq__file_encrypted.errcheck = check_error
|
||||
libmpq.libmpq__file_compressed.errcheck = check_error
|
||||
libmpq.libmpq__file_imploded.errcheck = check_error
|
||||
libmpq.libmpq__file_number.errcheck = check_error
|
||||
libmpq.libmpq__file_read.errcheck = check_error
|
||||
|
||||
libmpq.libmpq__block_open_offset.errcheck = check_error
|
||||
libmpq.libmpq__block_close_offset.errcheck = check_error
|
||||
libmpq.libmpq__block_unpacked_size.errcheck = check_error
|
||||
libmpq.libmpq__block_read.errcheck = check_error
|
||||
|
||||
__version__ = libmpq.libmpq__version()
|
||||
|
||||
|
||||
class Reader(object):
|
||||
def __init__(self, file, libmpq=libmpq):
|
||||
self._file = file
|
||||
self._pos = 0
|
||||
self._buf = []
|
||||
self._cur_block = 0
|
||||
libmpq.libmpq__block_open_offset(self._file._archive._mpq,
|
||||
self._file.number)
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __repr__(self):
|
||||
return "iter(%r)" % self._file
|
||||
|
||||
def seek(self, offset, whence=os.SEEK_SET, os=os):
|
||||
if whence == os.SEEK_SET:
|
||||
pass
|
||||
elif whence == os.SEEK_CUR:
|
||||
offset += self._pos
|
||||
elif whence == os.SEEK_END:
|
||||
offset += self._file.unpacked_size
|
||||
else:
|
||||
raise ValueError, "invalid whence"
|
||||
|
||||
if offset >= self._pos:
|
||||
self.read(offset - self._pos)
|
||||
else:
|
||||
self._pos = 0
|
||||
self._buf = []
|
||||
self._cur_block = 0
|
||||
self.read(offset)
|
||||
|
||||
def tell(self):
|
||||
return self._pos
|
||||
|
||||
def _read_block(self, ctypes=ctypes, libmpq=libmpq):
|
||||
block_size = ctypes.c_uint64()
|
||||
libmpq.libmpq__block_unpacked_size(self._file._archive._mpq,
|
||||
self._file.number, self._cur_block, ctypes.byref(block_size))
|
||||
block_data = ctypes.create_string_buffer(block_size.value)
|
||||
libmpq.libmpq__block_read(self._file._archive._mpq,
|
||||
self._file.number, self._cur_block,
|
||||
block_data, ctypes.c_uint64(len(block_data)), None)
|
||||
self._buf.append(block_data.raw)
|
||||
self._cur_block += 1
|
||||
|
||||
def read(self, size=-1):
|
||||
while size < 0 or sum(map(len, self._buf)) < size:
|
||||
if self._cur_block == self._file.blocks:
|
||||
break
|
||||
self._read_block()
|
||||
buf = "".join(self._buf)
|
||||
if size < 0:
|
||||
ret = buf
|
||||
self._buf = []
|
||||
else:
|
||||
ret = buf[:size]
|
||||
self._buf = [buf[size:]]
|
||||
self._pos += len(ret)
|
||||
return ret
|
||||
|
||||
def readline(self, os=os):
|
||||
line = []
|
||||
while True:
|
||||
char = self.read(1)
|
||||
if char == "":
|
||||
break
|
||||
if char not in '\r\n' and line and line[-1] in '\r\n':
|
||||
self.seek(-1, os.SEEK_CUR)
|
||||
break
|
||||
line.append(char)
|
||||
return ''.join(line)
|
||||
|
||||
def next(self):
|
||||
line = self.readline()
|
||||
if not line:
|
||||
raise StopIteration
|
||||
return line
|
||||
|
||||
def readlines(self, sizehint=-1):
|
||||
res = []
|
||||
while sizehint < 0 or sum(map(len, res)) < sizehint:
|
||||
line = self.readline()
|
||||
if not line:
|
||||
break
|
||||
res.append(line)
|
||||
return res
|
||||
|
||||
xreadlines = __iter__
|
||||
|
||||
def __del__(self, libmpq=libmpq):
|
||||
libmpq.libmpq__block_close_offset(self._file._archive._mpq,
|
||||
self._file.number)
|
||||
|
||||
|
||||
class File(object):
|
||||
def __init__(self, archive, number, ctypes=ctypes, libmpq=libmpq):
|
||||
self._archive = archive
|
||||
self.number = number
|
||||
|
||||
for name, atype in [
|
||||
("packed_size", ctypes.c_uint64),
|
||||
("unpacked_size", ctypes.c_uint64),
|
||||
("offset", ctypes.c_uint64),
|
||||
("blocks", ctypes.c_uint32),
|
||||
("encrypted", ctypes.c_uint32),
|
||||
("compressed", ctypes.c_uint32),
|
||||
("imploded", ctypes.c_uint32),
|
||||
]:
|
||||
data = atype()
|
||||
func = getattr(libmpq, "libmpq__file_"+name)
|
||||
func(self._archive._mpq, self.number, ctypes.byref(data))
|
||||
setattr(self, name, data.value)
|
||||
|
||||
def __str__(self, ctypes=ctypes, libmpq=libmpq):
|
||||
data = ctypes.create_string_buffer(self.unpacked_size)
|
||||
libmpq.libmpq__file_read(self._archive._mpq, self.number,
|
||||
data, ctypes.c_uint64(len(data)), None)
|
||||
return data.raw
|
||||
|
||||
def __repr__(self):
|
||||
return "%r[%i]" % (self._archive, self.number)
|
||||
|
||||
def __iter__(self, Reader=Reader):
|
||||
return Reader(self)
|
||||
|
||||
|
||||
class Archive(object):
|
||||
def __init__(self, source, ctypes=ctypes, File=File, libmpq=libmpq):
|
||||
self._source = source
|
||||
if isinstance(source, File):
|
||||
assert not source.encrypted
|
||||
assert not source.compressed
|
||||
assert not source.imploded
|
||||
self.filename = source._archive.filename
|
||||
offset = source._archive.offset + source.offset
|
||||
else:
|
||||
self.filename = source
|
||||
offset = -1
|
||||
|
||||
self._mpq = ctypes.c_void_p()
|
||||
libmpq.libmpq__archive_open(ctypes.byref(self._mpq), self.filename,
|
||||
ctypes.c_uint64(offset))
|
||||
self._opened = True
|
||||
|
||||
for field_name, field_type in [
|
||||
("packed_size", ctypes.c_uint64),
|
||||
("unpacked_size", ctypes.c_uint64),
|
||||
("offset", ctypes.c_uint64),
|
||||
("version", ctypes.c_uint32),
|
||||
("files", ctypes.c_uint32),
|
||||
]:
|
||||
func = getattr(libmpq, "libmpq__archive_" + field_name)
|
||||
data = field_type()
|
||||
func(self._mpq, ctypes.byref(data))
|
||||
setattr(self, field_name, data.value)
|
||||
|
||||
def __del__(self, libmpq=libmpq):
|
||||
if getattr(self, "_opened", False):
|
||||
libmpq.libmpq__archive_close(self._mpq)
|
||||
|
||||
def __len__(self):
|
||||
return self.files
|
||||
|
||||
def __contains__(self, item, ctypes=ctypes, libmpq=libmpq):
|
||||
if isinstance(item, str):
|
||||
data = ctypes.c_uint32()
|
||||
try:
|
||||
libmpq.libmpq__file_number(self._mpq, ctypes.c_char_p(item),
|
||||
ctypes.byref(data))
|
||||
except IndexError:
|
||||
return False
|
||||
return True
|
||||
return 0 <= item < self.files
|
||||
|
||||
def __getitem__(self, item, ctypes=ctypes, File=File, libmpq=libmpq):
|
||||
if isinstance(item, str):
|
||||
data = ctypes.c_int()
|
||||
libmpq.libmpq__file_number(self._mpq, ctypes.c_char_p(item),
|
||||
ctypes.byref(data))
|
||||
item = data.value
|
||||
else:
|
||||
if not 0 <= item < self.files:
|
||||
raise IndexError, "file not in archive"
|
||||
return File(self, item)
|
||||
|
||||
def __repr__(self):
|
||||
return "mpq.Archive(%r)" % self._source
|
||||
|
||||
# Remove clutter - everything except Error and Archive.
|
||||
del os, check_error, ctypes, errors, File, libmpq, Reader
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys, random
|
||||
archive = Archive(sys.argv[1])
|
||||
print repr(archive)
|
||||
for k, v in archive.__dict__.iteritems():
|
||||
#if k[0] == '_': continue
|
||||
print " " * (4 - 1), k, v
|
||||
assert '(listfile)' in archive
|
||||
assert 0 in archive
|
||||
assert len(archive) == archive.files
|
||||
files = [x.strip() for x in archive['(listfile)']]
|
||||
files.extend(xrange(archive.files))
|
||||
for key in files: #sys.argv[2:] if sys.argv[2:] else xrange(archive.files):
|
||||
file = archive[key]
|
||||
print
|
||||
print " " * (4 - 1), repr(file)
|
||||
for k, v in file.__dict__.iteritems():
|
||||
#if k[0] == '_': continue
|
||||
print " " * (8 - 1), k, v
|
||||
|
||||
a = str(file)
|
||||
|
||||
b = iter(file).read()
|
||||
|
||||
reader = iter(file)
|
||||
c = []
|
||||
while True:
|
||||
l = random.randrange(1, 10)
|
||||
d = reader.read(l)
|
||||
if not d: break
|
||||
assert len(d) <= l
|
||||
c.append(d)
|
||||
c = "".join(c)
|
||||
|
||||
d = []
|
||||
reader.seek(0)
|
||||
for line in reader:
|
||||
d.append(line)
|
||||
d = "".join(d)
|
||||
|
||||
assert a == b == c == d, map(hash, [a,b,c,d])
|
||||
assert len(a) == file.unpacked_size
|
||||
|
||||
repr(iter(file))
|
||||
|
||||
|
||||
reader.seek(0)
|
||||
a = reader.readlines()
|
||||
|
||||
reader.seek(0)
|
||||
b = list(reader)
|
||||
|
||||
assert a == b
|
||||
Reference in New Issue
Block a user