mirror of
https://github.com/SickGear/SickGear.git
synced 2024-12-04 18:33:38 +00:00
980e05cc99
Backported 400 revisions from rev 1de4961-8897c5b (2018-2014). Move core/benchmark, core/cmd_line, core/memory, core/profiler and core/timeout to core/optional/* Remove metadata/qt* PORT: Version 2.0a3 (inline with 3.0a3 @ f80c7d5). Basic Support for XMP Packets. tga: improvements to adhere more closely to the spec. pdf: slightly improved parsing. rar: fix TypeError on unknown block types. Add MacRoman win32 codepage. tiff/exif: support SubIFDs and tiled images. Add method to export metadata in dictionary. mpeg_video: don't attempt to parse Stream past length. mpeg_video: parse ESCR correctly, add SCR value. Change centralise CustomFragments. field: don't set parser class if class is None, to enable autodetect. field: add value/display for CustomFragment. parser: inline warning to enable tracebacks in debug mode. Fix empty bytestrings in makePrintable. Fix contentSize in jpeg.py to account for image_data blocks. Fix the ELF parser. Enhance the AR archive parser. elf parser: fix wrong wrong fields order in parsing little endian section flags. elf parser: add s390 as a machine type. Flesh out mp4 parser. PORT: Version 2.0a1 (inline with 3.0a1). Major refactoring and PEP8. Fix ResourceWarning warnings on files. Add a close() method and support for the context manager protocol ("with obj: ...") to parsers, input and output streams. metadata: get comment from ZIP. Support for InputIOStream.read(0). Fix sizeGe when size is None. Remove unused new_seekable_field_set file. Remove parser Mapsforge .map. Remove parser Parallel Realities Starfighter .pak files. sevenzip: fix for newer archives. java: update access flags and modifiers for Java 1.7 and update description text for most recent Java. Support ustar prefix field in tar archives. Remove file_system* parsers. Remove misc parsers 3d0, 3ds, gnome_keyring, msoffice*, mstask, ole*, word*. Remove program parsers macho, nds, prc. Support non-8bit Character subclasses. Python parser supports Python 3.7. Enhance mpeg_ts parser to support MTS/M2TS. Support for creation date in tiff. Change don't hardcode errno constant. PORT: 1.9.1 Internal Only: The following are legacy reference to upstream commit messages. Relevant changes up to b0a115f8. Use integer division. Replace HACHOIR_ERRORS with Exception. Fix metadata.Data: make it sortable. Import fixes from e7de492. PORT: Version 2.0a1 (inline with 3.0a1 @ e9f8fad). Replace hachoir.core.field with hachoir.field Replace hachoir.core.stream with hachoir.stream Remove the compatibility module for PY1.5 to PY2.5. metadata: support TIFF picture. metadata: fix string normalization. metadata: fix datetime regex Fix hachoir bug #57. FileFromInputStream: fix comparison between None and an int. InputIOStream: open the file in binary mode.
158 lines
4.9 KiB
Python
158 lines
4.9 KiB
Python
"""
|
|
Parser of FastTrackerII Extended Module (XM) version 1.4
|
|
|
|
Documents:
|
|
- Modplug source code (file modplug/soundlib/Load_mod.cpp)
|
|
http://sourceforge.net/projects/modplug
|
|
- Dumb source code (files include/dumb.h and src/it/readmod.c
|
|
http://dumb.sf.net/
|
|
- Documents on "MOD" format on Wotsit
|
|
http://www.wotsit.org
|
|
|
|
Compressed formats (i.e. starting with "PP20" or having "PACK" as type
|
|
are not handled. Also NoiseTracker's NST modules aren't handled, although
|
|
it might be possible: no file format and 15 samples
|
|
|
|
Author: Christophe GISQUET <christophe.gisquet@free.fr>
|
|
Creation: 18th February 2007
|
|
"""
|
|
|
|
from math import log10
|
|
from hachoir.parser import Parser
|
|
from hachoir.field import (FieldSet,
|
|
Bits, UInt16, UInt8,
|
|
RawBytes, String, GenericVector)
|
|
from hachoir.core.endian import BIG_ENDIAN
|
|
from hachoir.core.text_handler import textHandler
|
|
|
|
# Old NoiseTracker 15-samples modules can have anything here.
|
|
MODULE_TYPE = {
|
|
"M.K.": ("Noise/Pro-Tracker", 4),
|
|
"M!K!": ("Noise/Pro-Tracker", 4),
|
|
"M&K&": ("Noise/Pro-Tracker", 4),
|
|
"RASP": ("StarTrekker", 4),
|
|
"FLT4": ("StarTrekker", 4),
|
|
"FLT8": ("StarTrekker", 8),
|
|
"6CHN": ("FastTracker", 6),
|
|
"8CHN": ("FastTracker", 8),
|
|
"CD81": ("Octalyser", 8),
|
|
"OCTA": ("Octalyser", 8),
|
|
"FA04": ("Digital Tracker", 4),
|
|
"FA06": ("Digital Tracker", 6),
|
|
"FA08": ("Digital Tracker", 8),
|
|
}
|
|
|
|
|
|
def getFineTune(val):
|
|
return ("0", "1", "2", "3", "4", "5", "6", "7", "8",
|
|
"-8", "-7", "-6", "-5", "-4", "-3", "-2", "-1")[val.value]
|
|
|
|
|
|
def getVolume(val):
|
|
return "%.1f dB" % (20.0 * log10(val.value / 64.0))
|
|
|
|
|
|
class SampleInfo(FieldSet):
|
|
static_size = 30 * 8
|
|
|
|
def createFields(self):
|
|
yield String(self, "name", 22, strip='\0')
|
|
yield UInt16(self, "sample_count")
|
|
yield textHandler(UInt8(self, "fine_tune"), getFineTune)
|
|
yield textHandler(UInt8(self, "volume"), getVolume)
|
|
yield UInt16(self, "loop_start", "Loop start offset in samples")
|
|
yield UInt16(self, "loop_len", "Loop length in samples")
|
|
|
|
def createValue(self):
|
|
return self["name"].value
|
|
|
|
|
|
class Header(FieldSet):
|
|
static_size = 1084 * 8
|
|
|
|
def createFields(self):
|
|
yield String(self, "name", 20, strip='\0')
|
|
yield GenericVector(self, "samples", 31, SampleInfo, "info")
|
|
yield UInt8(self, "length")
|
|
yield UInt8(self, "played_patterns_count")
|
|
yield GenericVector(self, "patterns", 128, UInt8, "position")
|
|
yield String(self, "type", 4)
|
|
|
|
def getNumChannels(self):
|
|
return MODULE_TYPE[self["type"].value][1]
|
|
|
|
|
|
class Note(FieldSet):
|
|
static_size = 8 * 4
|
|
|
|
def createFields(self):
|
|
yield Bits(self, 4, "note_hi_nibble")
|
|
yield Bits(self, 12, "period")
|
|
yield Bits(self, 4, "note_low_nibble")
|
|
yield Bits(self, 4, "effect")
|
|
yield UInt8(self, "parameter")
|
|
|
|
|
|
class Row(FieldSet):
|
|
def __init__(self, parent, name, channels, desc=None):
|
|
FieldSet.__init__(self, parent, name, description=desc)
|
|
self.channels = channels
|
|
self._size = 8 * self.channels * 4
|
|
|
|
def createFields(self):
|
|
for index in xrange(self.channels):
|
|
yield Note(self, "note[]")
|
|
|
|
|
|
class Pattern(FieldSet):
|
|
def __init__(self, parent, name, channels, desc=None):
|
|
FieldSet.__init__(self, parent, name, description=desc)
|
|
self.channels = channels
|
|
self._size = 64 * 8 * self.channels * 4
|
|
|
|
def createFields(self):
|
|
for index in xrange(64):
|
|
yield Row(self, "row[]", self.channels)
|
|
|
|
|
|
class AmigaModule(Parser):
|
|
PARSER_TAGS = {
|
|
"id": "mod",
|
|
"category": "audio",
|
|
"file_ext": ("mod", "nst", "wow", "oct", "sd0"),
|
|
"mime": (u'audio/mod', u'audio/x-mod', u'audio/mod', u'audio/x-mod'),
|
|
"min_size": 1084 * 8,
|
|
"description": "Uncompressed amiga module"
|
|
}
|
|
endian = BIG_ENDIAN
|
|
|
|
def validate(self):
|
|
modtype = self.stream.readBytes(1080 * 8, 4)
|
|
if modtype not in MODULE_TYPE:
|
|
return "Invalid module type %a" % modtype
|
|
self.createValue = lambda modtype: "%s module, %u channels" % MODULE_TYPE[modtype]
|
|
return True
|
|
|
|
def createFields(self):
|
|
header = Header(self, "header")
|
|
yield header
|
|
channels = header.getNumChannels()
|
|
|
|
# Number of patterns
|
|
patterns = 0
|
|
for index in xrange(128):
|
|
patterns = max(patterns,
|
|
header["patterns/position[%u]" % index].value)
|
|
patterns += 1
|
|
|
|
# Yield patterns
|
|
for index in xrange(patterns):
|
|
yield Pattern(self, "pattern[]", channels)
|
|
|
|
# Yield samples
|
|
for index in xrange(31):
|
|
count = header["samples/info[%u]/sample_count" % index].value
|
|
if count:
|
|
self.info("Yielding sample %u: %u samples" % (index, count))
|
|
yield RawBytes(self, "sample_data[]", 2 * count, \
|
|
"Sample %u" % index)
|