changeset 780:1ada4036ab88

Remove now-unneeded PBG3 Python implementation
author Link Mauve <linkmauve@linkmauve.fr>
date Sat, 08 Nov 2025 19:42:16 +0100
parents ee09657d3789
children 5b43c42fa680
files pytouhou/formats/pbg3.py pytouhou/utils/bitstream.pxd pytouhou/utils/bitstream.pyx pytouhou/utils/lzss.pyx
diffstat 4 files changed, 0 insertions(+), 313 deletions(-) [+]
line wrap: on
line diff
deleted file mode 100644
--- a/pytouhou/formats/pbg3.py
+++ /dev/null
@@ -1,155 +0,0 @@
-# -*- encoding: utf-8 -*-
-##
-## Copyright (C) 2011 Thibaut Girka <thib@sitedethib.com>
-##
-## 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; version 3 only.
-##
-## 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.
-##
-
-"""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 io import BytesIO
-
-from pytouhou.utils.bitstream import BitStream
-from pytouhou.utils import lzss
-
-from pytouhou.utils.helpers import get_logger
-
-from pytouhou.formats import WrongFormatError
-
-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)
-            if byte == 0:
-                break
-            string.append(byte)
-        return ''.join(chr(byte) for byte in string)
-
-
-
-PBG3Entry = namedtuple('PBG3Entry', 'unknown1 unknown2 checksum offset size')
-
-
-
-class PBG3:
-    """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
-
-
-    def __enter__(self):
-        return self
-
-
-    def __exit__(self, type, value, traceback):
-        return self.bitstream.__exit__(type, value, traceback)
-
-
-    @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 WrongFormatError(magic)
-
-        bitstream = PBG3BitStream(file)
-        entries = {}
-
-        nb_entries = bitstream.read_int()
-        offset = bitstream.read_int()
-        bitstream.seek(offset)
-        for i in range(nb_entries):
-            unknown1 = bitstream.read_int()
-            unknown2 = bitstream.read_int()
-            checksum = bitstream.read_int() # Checksum of *compressed data*
-            offset = bitstream.read_int()
-            size = bitstream.read_int()
-            name = bitstream.read_string(255)
-            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 get_file(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:
-            # Verify the checksum
-            compressed_size = self.bitstream.io.tell() - offset
-            self.bitstream.seek(offset)
-            value = 0
-            for c in self.bitstream.io.read(compressed_size):
-                value += c
-                value &= 0xFFFFFFFF
-            if value != checksum:
-                logger.warning('corrupted data!')
-        return BytesIO(data)
deleted file mode 100644
--- a/pytouhou/utils/bitstream.pxd
+++ /dev/null
@@ -1,10 +0,0 @@
-cdef class BitStream:
-    cdef public object io
-    cdef unsigned int bits
-    cdef unsigned char byte
-
-    cdef bint read_bit(self) except -1
-    cpdef unsigned int read(self, unsigned int nb_bits) except? 4242
-    cpdef write_bit(self, bint bit)
-    cpdef write(self, unsigned int bits, unsigned int nb_bits)
-    cpdef flush(self)
deleted file mode 100644
--- a/pytouhou/utils/bitstream.pyx
+++ /dev/null
@@ -1,83 +0,0 @@
-# -*- encoding: utf-8 -*-
-##
-## Copyright (C) 2011 Thibaut Girka <thib@sitedethib.com>
-##
-## 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; version 3 only.
-##
-## 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.
-##
-
-cdef class BitStream:
-    def __init__(self, io):
-        self.io = io
-        self.bits = 0
-        self.byte = 0
-
-
-    def __enter__(self):
-        return self
-
-
-    def __exit__(self, type, value, traceback):
-        return self.io.__exit__(type, value, traceback)
-
-
-    def seek(self, offset, whence=0):
-        self.io.seek(offset, whence)
-        self.byte = 0
-        self.bits = 0
-
-
-    cdef bint read_bit(self) except -1:
-        cdef bytes byte
-        if not self.bits:
-            byte = self.io.read(1)
-            self.byte = (<unsigned char*>byte)[0]
-            self.bits = 8
-        self.bits -= 1
-        return (self.byte >> self.bits) & 0x01
-
-
-    cpdef unsigned int read(self, unsigned int nb_bits) except? 4242:
-        cdef unsigned int value = 0, read = 0
-        cdef unsigned int nb_bits2 = nb_bits
-        cdef bytes byte
-
-        while nb_bits2:
-            if not self.bits:
-                byte = self.io.read(1)
-                self.byte = (<unsigned char*>byte)[0]
-                self.bits = 8
-            read = self.bits if nb_bits2 > self.bits else nb_bits2
-            nb_bits2 -= read
-            self.bits -= read
-            value |= (self.byte >> self.bits) << nb_bits2
-        return value & ((1 << nb_bits) - 1)
-
-
-    cpdef write_bit(self, bint bit):
-        if self.bits == 8:
-            self.io.write(chr(self.byte))
-            self.bits = 0
-            self.byte = 0
-        self.byte &= ~(1 << (7 - self.bits))
-        self.byte |= bit << (7 - self.bits)
-        self.bits += 1
-
-
-    cpdef write(self, unsigned int bits, unsigned int nb_bits):
-        for i in range(nb_bits):
-            self.write_bit(bits >> (nb_bits - 1 - i) & 0x01)
-
-
-    cpdef flush(self):
-        self.io.write(chr(self.byte))
-        self.bits = 0
-        self.byte = 0
-        self.io.flush()
-
deleted file mode 100644
--- a/pytouhou/utils/lzss.pyx
+++ /dev/null
@@ -1,65 +0,0 @@
-# -*- encoding: utf-8 -*-
-##
-## Copyright (C) 2011 Thibaut Girka <thib@sitedethib.com>
-##
-## 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; version 3 only.
-##
-## 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.
-##
-
-cimport cython
-from libc.stdlib cimport calloc, malloc, free
-
-from .bitstream cimport BitStream
-
-
-@cython.cdivision(True)
-cpdef bytes decompress(BitStream bitstream,
-                       Py_ssize_t size,
-                       unsigned int dictionary_size=0x2000,
-                       unsigned int offset_size=13,
-                       unsigned int length_size=4,
-                       unsigned int minimum_match_length=3):
-    cdef Py_ssize_t ptr, length
-    cdef unsigned int dictionary_head
-    cdef unsigned char byte
-    cdef char *out_data
-    cdef char *dictionary
-
-    out_data = <char*> malloc(size)
-    dictionary = <char*> calloc(dictionary_size, 1)
-    dictionary_head, ptr = 1, 0
-
-    while ptr < size:
-        if bitstream.read_bit():
-            # The `flag` bit is set, indicating the upcoming chunk of data is a literal
-            # Add it to the uncompressed file, and store it in the dictionary
-            byte = bitstream.read(8)
-            dictionary[dictionary_head] = byte
-            dictionary_head = (dictionary_head + 1) % dictionary_size
-            out_data[ptr] = byte
-            ptr += 1
-        else:
-            # The `flag` bit is not set, the upcoming chunk is a (offset, length) tuple
-            offset = bitstream.read(offset_size)
-            length = bitstream.read(length_size) + minimum_match_length
-            if ptr + length > size:
-                raise Exception
-            if offset == 0 and length == 0:
-                break
-            for i in range(offset, offset + length):
-                out_data[ptr] = dictionary[i % dictionary_size]
-                dictionary[dictionary_head] = dictionary[i % dictionary_size]
-                dictionary_head = (dictionary_head + 1) % dictionary_size
-                ptr += 1
-
-    _out_data = out_data[:size]
-    free(out_data)
-    free(dictionary)
-    return _out_data
-