Mercurial > touhou
diff pytouhou/formats/pbg3.py @ 204:88361534c77e
Add some documentation (argh, so much left to document!)
author | Thibaut Girka <thib@sitedethib.com> |
---|---|
date | Tue, 01 Nov 2011 13:46:03 +0100 |
parents | ac2e5e1c2c3c |
children | b5c7369abd7c |
line wrap: on
line diff
--- a/pytouhou/formats/pbg3.py +++ b/pytouhou/formats/pbg3.py @@ -12,6 +12,17 @@ ## GNU General Public License for more details. ## +"""PBG3 archive files handling. + +This module provides classes for handling the PBG3 file format. +The PBG3 format is the archive format used by Touhou: EoSD. + +PBG3 files are merely a bitstream composed of a header, +a file table, and LZSS-compressed files. +""" + +from collections import namedtuple + from pytouhou.utils.bitstream import BitStream import pytouhou.utils.lzss as lzss @@ -21,12 +32,26 @@ logger = get_logger(__name__) class PBG3BitStream(BitStream): + """Helper class to handle strings and integers in PBG3 bitstreams.""" + def read_int(self): + """Read an integer from the bitstream. + + Integers have variable sizes. They begin with a two-bit value indicating + the number of (non-aligned) bytes to read. + """ + size = self.read(2) return self.read((size + 1) * 8) def read_string(self, maxsize): + """Read a string from the bitstream. + + Strings are stored as standard NULL-termianted sequences of bytes. + The only catch is that they are not byte-aligned. + """ + string = [] for i in range(maxsize): byte = self.read(8) @@ -37,9 +62,24 @@ class PBG3BitStream(BitStream): +PBG3Entry = namedtuple('PBG3Entry', 'unknown1 unknown2 checksum offset size') + + + class PBG3(object): - def __init__(self, entries, bitstream=None): - self.entries = entries + """Handle PBG3 archive files. + + PBG3 is a file archive format used in Touhou 6: EoSD. + This class provides a representation of such files, as well as functions to + read and extract files from a PBG3 archive. + + Instance variables: + entries -- list of PBG3Entry objects describing files present in the archive + bitstream -- PBG3BitStream object + """ + + def __init__(self, entries=None, bitstream=None): + self.entries = entries or [] self.bitstream = bitstream #TODO @@ -53,6 +93,12 @@ class PBG3(object): @classmethod def read(cls, file): + """Read a PBG3 file. + + Raise an exception if the file is invalid. + Return a PBG3 instance otherwise. + """ + magic = file.read(4) if magic != b'PBG3': raise Exception #TODO @@ -70,21 +116,31 @@ class PBG3(object): offset = bitstream.read_int() size = bitstream.read_int() name = bitstream.read_string(255).decode('ascii') - entries[name] = (unknown1, unknown2, checksum, offset, size) + entries[name] = PBG3Entry(unknown1, unknown2, checksum, offset, size) return PBG3(entries, bitstream) def list_files(self): + """List files present in the archive.""" return self.entries.keys() def extract(self, filename, check=False): + """Extract a given file. + + If “filename” is in the archive, extract it and return its contents. + Otherwise, raise an exception. + + By default, the checksum of the file won't be verified, + you can however force the verification using the “check” argument. + """ + unkwn1, unkwn2, checksum, offset, size = self.entries[filename] self.bitstream.seek(offset) data = lzss.decompress(self.bitstream, size) if check: - # Checking the checksum + # Verify the checksum compressed_size = self.bitstream.io.tell() - offset self.bitstream.seek(offset) value = 0