class TypeReaderPlugin(Plugin):
    """
    type reader plugins
    """ class BaseTypeReader(object):
    target_type = None
    reader_name = None
    is_value_type = False
    is_generic_type = False
    is_enum_type = False
    file_platform = None
    file_version = None

    def __init__(self, stream=None, version=None):
        self.stream = stream
        self.version = version

    def __str__(self):
        return self.reader_name

    def read(self):
        raise ReaderError("Unimplemented type reader: '{}'".format(self.reader_name))

    def init_reader(self, file_platform=None, file_version=None):
        self.file_platform = file_platform
        self.file_version = file_version class ReaderError(Error):
    pass class EnumTypeReader(ValueTypeReader):
    is_enum_type = True
    enum_type = None
    enum_type4 = None

    def read(self):
        value = self.stream.read_int32()
        if self.file_version == VERSION_40 and self.enum_type4 is not None:
            enum_type = self.enum_type4
        else:
            enum_type = self.enum_type
        if callable(enum_type):
            return enum_type(value)
        else:
            return value class Vector3Reader(ValueTypeReader, TypeReaderPlugin):
    target_type = 'Microsoft.Xna.Framework.Vector3'
    reader_name = 'Microsoft.Xna.Framework.Content.Vector3Reader'

    def read(self):
        return Vector3._make(self.stream.unpack('3f')) class RectangleReader(ValueTypeReader, TypeReaderPlugin):
    target_type = 'Microsoft.Xna.Framework.Rectangle'
    reader_name = 'Microsoft.Xna.Framework.Content.RectangleReader'

    def read(self):
        return Rectangle._make(self.stream.unpack('4i')) class CharReader(ValueTypeReader, TypeReaderPlugin):
    target_type = 'System.Char'
    reader_name = 'Microsoft.Xna.Framework.Content.CharReader'

    def read(self):
        return self.stream.read_char() class StringReader(BaseTypeReader, TypeReaderPlugin):
    target_type = 'System.String'
    reader_name = 'Microsoft.Xna.Framework.Content.StringReader'

    def read(self):
        return self.stream.read_string() class ObjectReader(BaseTypeReader, TypeReaderPlugin):
    target_type = 'System.Object'
    reader_name = 'Microsoft.Xna.Framework.Content.ObjectReader'

    def read(self):
        raise ReaderError('ObjectReader invoked for {}'.format(self.target_type)) class ListReader(GenericTypeReader, TypeReaderPlugin):
    generic_target_type = 'System.Collections.Generic.List`1'
    generic_reader_name = 'Microsoft.Xna.Framework.Content.ListReader`1'

    def read(self):
        elements = self.stream.read_int32()
        if self.readers[0].is_value_type:
            return XNAList([self.readers[0].read() for _ in range(elements)])
        else:
            return XNAList([self.stream.read_object(self.readers[0]) for _ in range(elements)]) class DictionaryReader(GenericTypeReader, TypeReaderPlugin):
    generic_target_type = 'System.Collections.Generic.Dictionary`2'
    generic_reader_name = 'Microsoft.Xna.Framework.Content.DictionaryReader`2'

    def read(self):
        elements = self.stream.read_int32()
        if self.readers[0].is_value_type:
            if self.readers[1].is_value_type:
                return XNADict([(self.readers[0].read(), self.readers[1].read())
                                for _ in range(elements)])
            else:
                return XNADict([(self.readers[0].read(), self.stream.read_object(self.readers[1]))
                                for _ in range(elements)])
        else:
            if self.readers[1].is_value_type:
                return XNADict([(self.stream.read_object(self.readers[0]), self.readers[1].read())
                                for _ in range(elements)])
            else:
                return XNADict([(self.stream.read_object(self.readers[0]), self.stream.read_object(self.readers[1]))
                                for _ in range(elements)]) class Texture2D(object):
    def __init__(self, surface_format, width, height, mip_levels, needs_swap=False):
        self.surface_format = surface_format
        self.width = width
        self.height = height
        self.mip_levels = mip_levels
        self.needs_swap = needs_swap

    def __str__(self):
        return "Texture2D f:{} d:{}x{} m:{} s:{}".format(self.surface_format, self.width, self.height,
                                                         len(self.mip_levels), len(self.mip_levels[0]))

    def export(self, filename):
        if not self.surface_format.reader:
            raise ReaderError("No decoder found: '{}'".format(self.surface_format))
        dirname = os.path.dirname(filename)
        if not os.path.isdir(dirname):
            os.makedirs(dirname)

        # hack for ArtObject/TrileSet alpha channel
        alpha = 'yes'
        if 'art objects' in filename or 'trile sets' in filename:
            alpha = 'no'
            rows = self.surface_format.reader(self.mip_levels[0], self.width, self.height, self.needs_swap,
                                              alpha='only')
            write_png(filename + '_alpha', self.width, self.height, rows)
        rows = self.surface_format.reader(self.mip_levels[0], self.width, self.height, self.needs_swap, alpha=alpha)
        write_png(filename, self.width, self.height, rows)

    def full_data(self, alpha='yes'):
        if not self.surface_format.reader:
            raise ReaderError("No decoder found: '{}'".format(self.surface_format))

        rows = self.surface_format.reader(self.mip_levels[0], self.width, self.height, self.needs_swap, alpha=alpha)
        data = bytearray()
        for row in rows:
            data.extend(row)
        return bytes(data) class Texture3D(object):
    def __init__(self, surface_format, width, height, depth, mip_levels, needs_swap=False):
        self.surface_format = surface_format
        self.width = width
        self.height = height
        self.depth = depth
        self.mip_levels = mip_levels
        self.needs_swap = needs_swap

    def __str__(self):
        return "Texture3D f:{} d:{}x{}x{} m:{} s:{}".format(self.surface_format, self.width, self.height, self.depth,
                                                            len(self.mip_levels), len(self.mip_levels[0])) class TextureCube(object):
    def __init__(self, surface_format, texture_size, sides, needs_swap=False):
        self.surface_format = surface_format
        self.texture_size = texture_size
        self.sides = sides
        self.needs_swap = needs_swap

    def __str__(self):
        return "TextureCube f:{} d:{} m:{} s:{}".format(self.surface_format, self.texture_size, len(self.sides['+x']),
                                                        len(self.sides['+x'][0])) CUBE_SIDES = ['+x', '-x', '+y', '-y', '+z', '-z'] class IndexBuffer(object):
    def __init__(self, index_16, index_data):
        self.index_16 = index_16
        self.index_data = index_data

    def __str__(self):
        return "IndexBuffer t:{} s:{}".format(16 if self.index_16 else 32, len(self.index_data)) class Effect(object):
    def __init__(self, effect_data):
        self.effect_data = effect_data

    def __str__(self):
        return "Effect s:{}".format(len(self.effect_data))

    def export(self, filename):
        out_dir = os.path.dirname(filename)
        if not os.path.isdir(out_dir):
            os.makedirs(out_dir)
        with open(filename + '.fxo', 'wb') as out_handle:
            out_handle.write(self.effect_data) def get_surface_format(xna_version, surface_format):
    try:
        if xna_version >= VERSION_40:
            return SurfaceFormat4(surface_format)
        else:
            return SurfaceFormat(surface_format)
    except KeyError:
        raise ReaderError("Invalid surface format for V{}: {}".format(xna_version, surface_format)) class PrimitiveType(Enum):
    __slots__ = ()
    enum_values = {1: 'PointList', 2: 'LineList', 3: 'LineStrip', 4: 'TriangleList', 5: 'TriangleStrip',
                   6: 'TriangleFan'} class SpriteFont(object):
    def __init__(self, texture, glyphs, cropping, char_map, v_space, h_space, kerning, default_char):
        self.texture = texture
        self.glyphs = glyphs
        self.cropping = cropping
        self.char_map = char_map
        self.v_space = v_space
        self.h_space = h_space
        self.kerning = kerning
        self.default_char = default_char

    def __str__(self):
        return 'SpriteFont c:{} f:{} d:{}x{}'.format(len(self.glyphs), self.texture.surface_format, self.texture.width,
                                                     self.texture.height)

    def xml(self, parent=None):
        if parent is None:
            root = ET.Element('SpriteFont')
        else:
            root = ET.SubElement(parent, 'SpriteFont')
        root.set('width', str(self.texture.width))
        root.set('height', str(self.texture.height))
        root.set('hSpace', str(self.h_space))
        root.set('vSpace', str(self.v_space))
        if self.default_char is not None:
            root.set('defaultChar', self.default_char)
        if self.glyphs is not None:
            self.glyphs.xml(root, 'Glyphs')
        if self.cropping is not None:
            self.cropping.xml(root, 'Cropping')
        if self.kerning is not None:
            self.kerning.xml(root, 'Kerning')
        if self.char_map is not None:
            self.char_map.xml(root, 'CharMap', 'Char', 'c')
        return root

    def export(self, filename):
        if self.texture is not None:
            self.texture.export(filename) class BasicEffect(object):
    def __init__(self, texture, colour_d, colour_e, colour_s, spec, alpha, colour_v):
        self.texture = texture
        self.colour_d = colour_d
        self.colour_e = colour_e
        self.colour_s = colour_s
        self.spec = spec
        self.alpha = alpha
        self.colour_v = colour_v

    def __str__(self):
        return "BasicEffectReader '{}'".format(self.texture)

    def xml(self, parent):
        if parent is None:
            root = ET.Element('BasicEffect')
        else:
            root = ET.SubElement(parent, 'BasicEffect')
        root.set('spec', str(self.spec))
        root.set('alpha', str(self.alpha))
        root.set('colorV', str(self.colour_v))
        self.texture.xml(ET.SubElement(root, 'Texture'))
        self.colour_d.xml(ET.SubElement(root, 'ColorD'))
        self.colour_e.xml(ET.SubElement(root, 'ColorE'))
        self.colour_s.xml(ET.SubElement(root, 'ColorS'))
        return root class PrimitiveType4(Enum):
    __slots__ = ()
    enum_values = {0: 'TriangleList', 1: 'TriangleStrip', 2: 'LineList', 3: 'LineStrip'}
from xnb_parse.type_reader import TypeReaderPlugin, BaseTypeReader, ReaderError, EnumTypeReader
from xnb_parse.type_readers.xna_math import Vector3Reader, RectangleReader
from xnb_parse.type_readers.xna_primitive import CharReader, StringReader, ObjectReader
from xnb_parse.type_readers.xna_system import ListReader, DictionaryReader
from xnb_parse.xna_types.xna_graphics import (Texture2D, Texture3D, TextureCube, CUBE_SIDES, IndexBuffer, Effect,
                                              get_surface_format, PrimitiveType, SpriteFont, BasicEffect,
                                              PrimitiveType4)
        surface_format_raw = self.stream.read_int32()
        surface_format = get_surface_format(self.stream.file_version, surface_format_raw)
        width = self.stream.read_int32()
        height = self.stream.read_int32()
        depth = self.stream.read_int32()
        mip_count = self.stream.read_int32()
        mip_levels = []
        for _ in range(mip_count):
            size = self.stream.read_int32()
            data = self.stream.read(size)
            mip_levels.append(data)
        return Texture3D(surface_format, width, height, depth, mip_levels, self.stream.needs_swap)


class TextureCubeReader(BaseTypeReader, TypeReaderPlugin):
    target_type = 'Microsoft.Xna.Framework.Graphics.TextureCube'
    reader_name = 'Microsoft.Xna.Framework.Content.TextureCubeReader'

    def read(self):
        surface_format_raw = self.stream.read_int32()
        surface_format = get_surface_format(self.stream.file_version, surface_format_raw)
        texture_size = self.stream.read_int32()
        mip_count = self.stream.read_int32()
        sides = {}
        for side in CUBE_SIDES:
            mip_levels = []
            for _ in range(mip_count):
                size = self.stream.read_int32()
                data = self.stream.read(size)
                mip_levels.append(data)
            sides[side] = mip_levels
        return TextureCube(surface_format, texture_size, sides, self.stream.needs_swap)


class IndexBufferReader(BaseTypeReader, TypeReaderPlugin):
    target_type = 'Microsoft.Xna.Framework.Graphics.IndexBuffer'
    reader_name = 'Microsoft.Xna.Framework.Content.IndexBufferReader'

    def read(self):
        index_16 = self.stream.read_boolean()
        size = self.stream.read_int32()
        data = self.stream.read(size)
        return IndexBuffer(index_16, data)


class VertexBufferReader(BaseTypeReader, TypeReaderPlugin):
    target_type = 'Microsoft.Xna.Framework.Graphics.VertexBuffer'
    reader_name = 'Microsoft.Xna.Framework.Content.VertexBufferReader'

    def read(self):
        size = self.stream.read_int32()
        data = self.stream.read(size)
        return data


class VertexDeclarationReader(BaseTypeReader, TypeReaderPlugin):
    target_type = 'Microsoft.Xna.Framework.Graphics.VertexDeclaration'
    reader_name = 'Microsoft.Xna.Framework.Content.VertexDeclarationReader'

    def read(self):
        element_count = self.stream.read_int32()
        elements = []
        for _ in range(element_count):
            v_stream = self.stream.read_int16()
            v_offset = self.stream.read_int16()
            v_format = self.stream.read_byte()
            v_method = self.stream.read_byte()
            v_usage = self.stream.read_byte()
            v_usage_index = self.stream.read_byte()
            element = v_stream, v_offset, v_format, v_method, v_usage, v_usage_index
            elements.append(element)
        return elements


class EffectReader(BaseTypeReader, TypeReaderPlugin):
    target_type = 'Microsoft.Xna.Framework.Graphics.Effect'
    reader_name = 'Microsoft.Xna.Framework.Content.EffectReader'

    def read(self):
        size = self.stream.read_int32()
        data = self.stream.read(size)
        return Effect(data)


class EffectMaterialReader(BaseTypeReader, TypeReaderPlugin):
    target_type = 'Microsoft.Xna.Framework.Graphics.EffectMaterial'
    reader_name = 'Microsoft.Xna.Framework.Content.EffectMaterialReader'

    def read(self):
        effect = self.stream.read_external_reference(EffectReader)
        parameters = self.stream.read_object(DictionaryReader, [StringReader, ObjectReader])
        return effect, parameters


class BasicEffectReader(BaseTypeReader, TypeReaderPlugin):
    target_type = 'Microsoft.Xna.Framework.Graphics.BasicEffect'
    reader_name = 'Microsoft.Xna.Framework.Content.BasicEffectReader'

    def read(self):
        texture = self.stream.read_external_reference(TextureReader)
        colour_d = self.stream.read_vector3()
        colour_e = self.stream.read_vector3()
        colour_s = self.stream.read_vector3()
        spec = self.stream.read_single()
        alpha = self.stream.read_single()
        colour_v = self.stream.read_boolean()
        return BasicEffect(texture, colour_d, colour_e, colour_s, spec, alpha, colour_v)


class SpriteFontReader(BaseTypeReader, TypeReaderPlugin):
    target_type = 'Microsoft.Xna.Framework.Graphics.SpriteFont'
    reader_name = 'Microsoft.Xna.Framework.Content.SpriteFontReader'

    def read(self):
        texture = self.stream.read_object(Texture2DReader)
        glyphs = self.stream.read_object(ListReader, [RectangleReader])
        cropping = self.stream.read_object(ListReader, [RectangleReader])
        char_map = self.stream.read_object(ListReader, [CharReader])
        v_space = self.stream.read_int32()
        h_space = self.stream.read_single()

        kerning = self.stream.read_object(ListReader, [Vector3Reader])