Source code for resource_packs

# -*- coding: utf-8 -*-
#!# If the comman line parameter '--debug-packs' is given, the logging level is set to debug.
#!# Otherwise, it is set to critical.
from PIL import Image
import zipfile
import directories
import os
import shutil
from config import config
from cStringIO import StringIO

import locale
import traceback
from utilities.misc import Singleton

DEF_ENC = locale.getdefaultlocale()[1]
if DEF_ENC is None:
    DEF_ENC = "UTF-8"

try:
    import resource  # @UnresolvedImport
    resource.setrlimit(resource.RLIMIT_NOFILE, (500,-1))
except:
    pass

#!# Debugging .zip resource pack not loaded bug.

import logging

level = 50
if '--debug-packs' in os.sys.argv:
    level = 10

log = logging.getLogger(__name__)
log.setLevel(level)

#!#


[docs]def step(slot): ''' Utility method for multiplying the slot by 16 :param slot: Texture slot :type slot: int ''' return slot << 4
''' Empty comment lines like: # are for texture spaces that I don't know what should go there ''' textureSlots = { # Start Top Row "grass_top": (step(0),step(0)), "stone": (step(1),step(0)), "dirt": (step(2),step(0)), "grass_side": (step(3),step(0)), "planks_oak": (step(4),step(0)), "stone_slab_side": (step(5),step(0)), "stone_slab_top": (step(6),step(0)), "brick": (step(7),step(0)), "tnt_side": (step(8),step(0)), "tnt_top": (step(9),step(0)), "tnt_bottom": (step(10),step(0)), "web": (step(11),step(0)), "flower_rose": (step(12),step(0)), "flower_dandelion": (step(13),step(0)), # "sapling_oak": (step(15),step(0)), "flower_blue_orchid": (step(16),step(0)), "flower_allium": (step(17),step(0)), "flower_houstonia": (step(18),step(0)), "flower_tulip_red": (step(19),step(0)), "sapling_roofed_oak": (step(20),step(0)), # End Top Row # Start Second Row "cobblestone": (step(0),step(1)), "bedrock": (step(1),step(1)), "sand": (step(2),step(1)), "gravel": (step(3),step(1)), "log_oak": (step(4),step(1)), "log_oak_top": (step(5),step(1)), "iron_block": (step(6),step(1)), "gold_block": (step(7),step(1)), "diamond_block": (step(8),step(1)), "emerald_block": (step(9),step(1)), # "red_sand": (step(11),step(1)), "mushroom_red": (step(12),step(1)), "mushroom_brown": (step(13),step(1)), "sapling_jungle": (step(14),step(1)), "fire_layer_0": (step(15),step(1)), "flower_tulip_orange": (step(16),step(1)), "flower_tulip_white": (step(17),step(1)), "flower_tulip_pink": (step(18),step(1)), "flower_oxeye_daisy": (step(19),step(1)), "sapling_acacia": (step(20),step(1)), # End Second Row # Start Third Row "gold_ore": (step(0),step(2)), "iron_ore": (step(1),step(2)), "coal_ore": (step(2),step(2)), "bookshelf": (step(3),step(2)), "cobblestone_mossy": (step(4),step(2)), "obsidian": (step(5),step(2)), # "tallgrass": (step(7),step(2)), # "beacon": (step(9),step(2)), "dropper_front_horizontal": (step(10),step(2)), "crafting_table_top": (step(11),step(2)), "furnace_front_off": (step(12),step(2)), "furnace_side": (step(13),step(2)), "dispenser_front_horizontal": (step(14),step(2)), "fire_layer_1": (step(15),step(2)), # # # # "daylight_detector_side": (step(20),step(2)), # End Third Row # Start Fourth Row "sponge": (step(0), step(3)), "glass": (step(1), step(3)), "diamond_ore": (step(2), step(3)), "redstone_ore": (step(3), step(3)), # # "stonebrick": (step(6), step(3)), "deadbush": (step(7), step(3)), "fern": (step(8), step(3)), "dirt_podzol_top": (step(9), step(3)), "dirt_podzol_side": (step(10), step(3)), "crafting_table_side": (step(11), step(3)), "crafting_table_front": (step(12), step(3)), "furnace_front_on": (step(13), step(3)), "furnace_top": (step(14), step(3)), "sapling_spruce": (step(15), step(3)), # # # # # End Fourth Row # Start Fifth Row "wool_colored_white": (step(0), step(4)), "mob_spawner": (step(1), step(4)), "snow": (step(2), step(4)), "ice": (step(3), step(4)), "grass_side_snowed": (step(4), step(4)), "cactus_top": (step(5), step(4)), "cactus_side": (step(6), step(4)), "cactus_bottom": (step(7), step(4)), "clay": (step(8), step(4)), "reeds": (step(9), step(4)), "jukebox_side": (step(10), step(4)), "jukebox_top": (step(11), step(4)), "waterlily": (step(12), step(4)), "mycelium_side": (step(13), step(4)), "mycelium_top": (step(14), step(4)), "sapling_birch": (step(15), step(4)), # # "dropper_front_vertical": (step(18), step(4)), "daylight_detector_inverted_top": (step(19), step(4)), # End Fifth Row # Start Sixth Row "torch_on": (step(0), step(5)), "door_wood_upper": (step(1), step(5)), "door_iron_upper": (step(2), step(5)), "ladder": (step(3), step(5)), "trapdoor": (step(4), step(5)), "iron_bars": (step(5), step(5)), "farmland_wet": (step(6), step(5)), "farmland_dry": (step(7), step(5)), "wheat_stage_0": (step(8), step(5)), "wheat_stage_1": (step(9), step(5)), "wheat_stage_2": (step(10), step(5)), "wheat_stage_3": (step(11), step(5)), "wheat_stage_4": (step(12), step(5)), "wheat_stage_5": (step(13), step(5)), "wheat_stage_6": (step(14), step(5)), "wheat_stage_7": (step(15), step(5)), # # "dispenser_front_vertical": (step(18), step(5)), # # End Sixth Row # Start Seventh Row "lever": (step(0), step(6)), "door_wood_lower": (step(1), step(6)), "door_iron_lower": (step(2), step(6)), "redstone_torch_on": (step(3), step(6)), "stonebrick_mossy": (step(4), step(6)), "stonebrick_cracked": (step(5), step(6)), "pumpkin_top": (step(6), step(6)), "netherrack": (step(7), step(6)), "soul_sand": (step(8), step(6)), "glowstone": (step(9), step(6)), "piston_top_sticky": (step(10), step(6)), "piston_top_normal": (step(11), step(6)), "piston_side": (step(12), step(6)), "piston_bottom": (step(13), step(6)), "piston_inner": (step(14), step(6)), "pumpkin_stem_disconnected": (step(15), step(6)), # # # # End Seventh Row # Start Eigth Row "rail_normal_turned": (step(0),step(7)), "wool_colored_black": (step(1),step(7)), "wool_colored_gray": (step(2),step(7)), "redstone_torch_off": (step(3),step(7)), "log_spruce": (step(4),step(7)), "log_birch": (step(5),step(7)), "pumpkin_side": (step(6),step(7)), "pumpkin_face_off": (step(7),step(7)), "pumpkin_face_on": (step(8),step(7)), "cake_top": (step(9),step(7)), "cake_side": (step(10),step(7)), "cake_inner": (step(11),step(7)), "cake_bottom": (step(12),step(7)), "mushroom_block_skin_red": (step(13),step(7)), "mushroom_block_skin_brown": (step(14),step(7)), "pumpkin_stem_connected": (step(15),step(7)), # # "repeater_off_west": (step(18),step(7)), # # End Eigth Row # Start Ninth Row "rail_normal": (step(0),step(8)), "wool_colored_red": (step(1),step(8)), "wool_colored_magenta": (step(2),step(8)), "repeater_off_south": (step(3),step(8)), "leaves_spruce": (step(4),step(8)), # "bed_feet_top": (step(6),step(8)), "bed_head_top": (step(7),step(8)), "melon_side": (step(8),step(8)), "melon_top": (step(9),step(8)), # # # "mushroom_block_skin_stem": (step(13),step(8)), "mushroom_block_inside": (step(14),step(8)), "vine": (step(15),step(8)), # # "repeater_off_north": (step(18),step(8)), # # End Ninth Row # Start Tenth Row "lapis_block": (step(0),step(9)), "wool_colored_green": (step(1),step(9)), "wool_colored_lime": (step(2),step(9)), "repeater_on_south": (step(3),step(9)), # "bed_feet_end": (step(5),step(9)), "bed_feet_side": (step(6),step(9)), "bed_head_side": (step(7),step(9)), "bed_head_end": (step(8),step(9)), "log_jungle": (step(9),step(9)), "cauldron_side": (step(10),step(9)), "cauldron_bottom": (step(11),step(9)), "brewing_stand_base": (step(12),step(9)), "brewing_stand": (step(13),step(9)), "endframe_top": (step(14),step(9)), "endframe_side": (step(15),step(9)), "double_plant_sunflower_bottom": (step(16),step(9)), # "repeater_off_east": (step(18),step(9)), "structure_block_data": (step(19),step(9)), "structure_block_corner": (step(20),step(9)), # End Tenth Row # Start Eleventh Row "lapis_ore": (step(0),step(10)), "wool_colored_brown": (step(1),step(10)), "wool_colored_yellow": (step(2),step(10)), "rail_golden": (step(3),step(10)), "redstone_dust_cross": (step(4),step(10)), # "enchanting_table_top": (step(6),step(10)), "dragon_egg": (step(7),step(10)), "cocoa_stage_2": (step(8),step(10)), "cocoa_stage_1": (step(9),step(10)), "cocoa_stage_0": (step(10),step(10)), "emerald_ore": (step(11),step(10)), "trip_wire_source": (step(12),step(10)), "trip_wire": (step(13),step(10)), "endframe_eye": (step(14),step(10)), "end_stone": (step(15),step(10)), "double_plant_syringa_bottom": (step(16),step(10)), "double_plant_syringa_top": (step(17),step(10)), "repeater_on_west": (step(18),step(10)), "structure_block_save": (step(19),step(10)), "structure_block_load": (step(20),step(10)), # End Eleventh Row # Start Twelfth Row "sandstone_top": (step(0),step(11)), "wool_colored_blue": (step(1),step(11)), "wool_colored_light_blue": (step(2),step(11)), "rail_golden_powered": (step(3),step(11)), # "redstone_dust_line": (step(5),step(11)), "enchanting_table_side": (step(6),step(11)), "enchanting_table_bottom": (step(7),step(11)), "command_block": (step(8),step(11)), "itemframe_backround": (step(9),step(11)), "flower_pot": (step(10),step(11)), "comparator_off_south": (step(11),step(11)), "comparator_on_south": (step(12),step(11)), "daylight_detector_top": (step(13),step(11)), "redstone_block": (step(14),step(11)), "quartz_ore": (step(15),step(11)), "double_plant_grass_bottom": (step(16),step(11)), "double_plant_grass_top": (step(17),step(11)), "repeater_on_north": (step(18),step(11)), "command_block_back": (step(19),step(11)), "command_block_conditional": (step(20),step(11)), "command_block_front": (step(21),step(11)), "command_block_side": (step(22),step(11)), "bone_block_top": (step(19),step(11)), # End Twelfth Row # Start Thriteenth Row "sandstone_normal": (step(0),step(12)), "wool_colored_purple": (step(1),step(12)), "wool_colored_pink": (step(2),step(12)), "rail_detector": (step(3),step(12)), "leaves_jungle": (step(4),step(12)), # "planks_spruce": (step(6),step(12)), "planks_jungle": (step(7),step(12)), "carrots_stage_0": (step(8),step(12)), "carrots_stage_1": (step(9),step(12)), "carrots_stage_2": (step(10),step(12)), "carrots_stage_3": (step(11),step(12)), "potatoes_stage_3": (step(12),step(12)), # "piston_right": (step(14),step(12)), "piston_down": (step(15),step(12)), "double_plant_fern_bottom": (step(16),step(12)), "double_plant_fern_top": (step(17),step(12)), "repeater_on_east": (step(18),step(12)), "repeating_command_block_back": (step(19),step(12)), "repeating_command_block_conditional": (step(20),step(12)), "repeating_command_block_front": (step(21),step(12)), "repeating_command_block_side": (step(22),step(12)), "bone_block_side": (step(19),step(12)), # End Thriteenth Row # Start Fourteenth Row "sandstone_bottom": (step(0),step(13)), "wool_colored_cyan": (step(1),step(13)), "wool_colored_orange": (step(2),step(13)), "redstone_lamp_off": (step(3),step(13)), "redstone_lamp_on": (step(4),step(13)), "stonebrick_carved": (step(5),step(13)), "planks_birch": (step(6),step(13)), "anvil_base": (step(7),step(13)), "anvil_top_damaged_1": (step(8),step(13)), "quatrz_block_top": (step(9),step(13)), "rail_activator": (step(10),step(13)), "rail_activator_powered": (step(11),step(13)), "coal_block": (step(12),step(13)), "log_acacia_top": (step(13),step(13)), "piston_left": (step(14),step(13)), "magma": (step(18),step(13)), # "double_plant_rose_bottom": (step(16),step(13)), "double_plant_rose_top": (step(17),step(13)), "chain_command_block_back": (step(19),step(11)), "chain_command_block_conditional": (step(20),step(11)), "chain_command_block_front": (step(21),step(11)), "chain_command_block_side": (step(22),step(11)), # End Fourteenth Row # Start Fifteenth Row "nether_brick": (step(0),step(14)), "wool_colored_silver": (step(1),step(14)), "nether_wart_stage_0": (step(2),step(14)), "nether_wart_stage_1": (step(3),step(14)), "nether_wart_stage_2": (step(4),step(14)), "sandstone_carved": (step(5),step(14)), "sandstone_smooth": (step(6),step(14)), "anvil_top_damaged_0": (step(7),step(14)), "anvil_top_damaged_2": (step(8),step(14)), "log_spruce_top": (step(9),step(14)), "log_birch_top": (step(10),step(14)), "log_jungle_top": (step(11),step(14)), "log_big_oak_top": (step(12),step(14)), "lava_still": (step(13),step(14)), # # "double_plant_paeonia_bottom": (step(16),step(14)), "double_plant_paeonia_top": (step(17),step(14)), "nether_wart_block": (step(18),step(14)), # End Fifteenth Row # Start Sixteenth Row "planks_acacia": (step(0),step(15)), "planks_big_oak": (step(1),step(15)), # "log_acacia": (step(3),step(15)), "log_big_oak": (step(4),step(15)), "hardened_clay": (step(5),step(15)), "portal": (step(6),step(15)), # "quatrz_block_chiseled": (step(8),step(15)), "quartz_block_chiseled_top": (step(9),step(15)), "quartz_block_lines": (step(10),step(15)), "quartz_block_lines_top": (step(11),step(15)), # # # # # "slime": (step(17),step(15)), "red_nether_brick": (step(18),step(15)), # End Sixteenth Row # Start Seventeenth Row "ice_packed": (step(0),step(16)), "hay_block_side": (step(1),step(16)), "hay_block_top": (step(2),step(16)), "iron_trapdoor": (step(3),step(16)), "stone_granite": (step(4),step(16)), "stone_grantie_smooth": (step(5),step(16)), "stone_diorite": (step(6),step(16)), "stone_diorite_smooth": (step(7),step(16)), "stone_andesite": (step(8),step(16)), "stone_andesite_smooth": (step(9),step(16)), # # # # # # # # # "frosted_ice_0": (step(19), step(16)), # End Seventeenth Row # Start Eigteenth Row # # # # # # # # # # # # # # # # "prismarine_bricks": (step(16),step(17)), "prismarine_dark": (step(17),step(17)), "prismarine_rough": (step(18),step(17)), "purpur_pillar": (step(19),step(17)), # End Eigteenth Row # Start Ninteenth Row "hardened_clay_stained_white": (step(0),step(18)), "hardened_clay_stained_orange": (step(1),step(18)), "hardened_clay_stained_magenta": (step(2),step(18)), "hardened_clay_stained_light_blue": (step(3),step(18)), "hardened_clay_stained_yellow": (step(4),step(18)), "hardened_clay_stained_lime": (step(5),step(18)), "hardened_clay_stained_pink": (step(6),step(18)), "hardened_clay_stained_gray": (step(7),step(18)), "hardened_clay_stained_silver": (step(8),step(18)), "hardened_clay_stained_cyan": (step(9),step(18)), "hardened_clay_stained_purple": (step(10),step(18)), "hardened_clay_stained_blue": (step(11),step(18)), "hardened_clay_stained_brown": (step(12),step(18)), "hardened_clay_stained_green": (step(13),step(18)), "hardened_clay_stained_red": (step(14),step(18)), "hardened_clay_stained_black": (step(15),step(18)), "sponge_wet": (step(16),step(18)), "sea_lantern": (step(17),step(18)), "end_bricks": (step(18),step(18)), "purpur_pillar_top": (step(19),step(18)), # End Ninteenth Row # Start Twentieth Row # # # # # # # # # # # # # # # # "hay_block_side_rotated": (step(16),step(19)), "quartz_block_lines_rotated": (step(17),step(19)), "purpur_block": (step(18),step(19)), # End Twentieth Row # Start Twentyfirst Row "red_sandstone_bottom": (step(0),step(20)), "red_sandstone_carved": (step(1),step(20)), "red_sandstone_normal": (step(2),step(20)), "red_sandstone_smooth": (step(3),step(20)), "red_sandstone_top": (step(4),step(20)), "door_spruce_upper": (step(5),step(20)), "door_birch_upper": (step(6),step(20)), "door_jungle_upper": (step(7),step(20)), "door_acacia_upper": (step(8),step(20)), "door_dark_oak_upper": (step(9),step(20)), # # # # # # # # "chorus_plant": (step(13),step(20)), "chorus_flower_dead": (step(14),step(20)), "chorus_flower": (step(15),step(20)), "end_rod": (step(16),step(20)), # End Twentyfirst Row # Start MISC # Start More Bed Textures "bed_head_side_flipped": (step(1),step(21)), "bed_feet_side_flipped": (step(2),step(21)), "bed_head_top_flipped": (step(1),step(22)), "bed_feet_top_flipped": (step(2),step(22)), "bed_feet_top_bottom": (step(3),step(21)), "bed_head_top_bottom": (step(3),step(22)), "bed_feet_top_top": (step(4),step(22)), "bed_head_top_top": (step(4),step(21)), # End More Bed Textures # Start Comparator Block }
[docs]class MultipartTexture(object): def __init__(self, texture_objects): self.subclasses = [] self.runAnyways = [] self.texture_dict = {} for subcls in self.__class__.__subclasses__(): # This is why I love Python self.subclasses.append(subcls) for cls in self.subclasses: instance = cls(texture_objects) if instance.runAnyway: self.runAnyways.append(instance) else: self.texture_dict[instance.target] = instance
[docs]class LeverTexture(MultipartTexture): target = "lever" runAnyway = False def __init__(self, texture_objects): self.texture_objects = texture_objects
[docs] def parse_texture(self): if "lever" not in self.texture_objects or "cobblestone" not in self.texture_objects: return None lever = self.texture_objects["lever"].copy() cobblestone = self.texture_objects["cobblestone"].copy() base_1 = cobblestone.crop((5, 4, 11, 12)) lever.paste(base_1, (10, 0, 16, 8)) base_2 = cobblestone.crop((5, 0, 11, 3)) lever.paste(base_2, (10, 8, 16, 11)) base_3 = cobblestone.crop((4, 0, 12, 3)) lever.paste(base_3, (2, 0, 10, 3)) return lever
[docs]class StandingSignTexture(MultipartTexture): target = "" runAnyway = True position = (step(20), step(5)) def __init__(self, texture_objects): self.texture_objects = texture_objects
[docs] def parse_texture(self): if "planks_oak" not in self.texture_objects or "log_oak" not in self.texture_objects: return None planks = self.texture_objects["planks_oak"].copy() log_tex = self.texture_objects["log_oak"].copy() sign = planks.crop((0, 7, 16, 16)) log_tex.paste(sign, (0, 7, 16, 16)) if log_tex.mode != "RGBA": log_tex = log_tex.convert("RGBA") return log_tex
[docs]class IResourcePack(object): ''' Sets all base variables for a Resource Pack ''' def __init__(self): self.__stop = False texture_path = os.path.join(directories.parentDir, "textures", self._pack_name) self.texture_path = texture_path self._isEmpty = False self._too_big = False self.big_textures_counted = 0 self.big_textures_max = 10 self.block_image = {} self.propogated_textures = [] self.all_texture_slots = [] self.old_terrain = Image.open(os.path.join(directories.getDataDir(), 'terrain.png')) for texx in xrange(0,33): for texy in xrange(0,33): self.all_texture_slots.append((step(texx),step(texy))) self._terrain_name = self._pack_name.replace(" ", "_")+".png" self._terrain_path = os.path.join("terrain-textures", self._terrain_name.replace(" ", "_")) @property def pack_name(self): ''' The name of the Resource Pack ''' return self._pack_name @property def terrain_name(self): ''' Name of the parsed texture PNG file ''' return self._terrain_name
[docs] def terrain_path(self): ''' Path to the parsed PNG file ''' return self._terrain_path
@property def isEmpty(self): ''' Returns true if the Resource Pack doesn't replace the minimum amount of textures ''' return self._isEmpty @property def tooBig(self): ''' Returns true if the Resource Pack has a greater resolution than 32x32 ''' return self._too_big
[docs] def parse_terrain_png(self): ''' Parses each block texture into a usable PNG file like terrain.png ''' multiparts = MultipartTexture(self.block_image) log.debug("Parsing terrain.png") new_terrain = Image.new("RGBA", (512, 512), None) for tex in self.block_image.keys(): if not self.__stop and tex in textureSlots.keys(): try: if tex not in multiparts.texture_dict: image = self.block_image[tex] else: image = multiparts.texture_dict[tex].parse_texture() if image is None: continue log.debug(" Image is %s"%tex) log.debug(" Image mode: %s"%image.mode) if image.mode != "RGBA": try: image = image.convert("RGBA") log.debug(" Image converted to RGBA.") except Exception, ee: print "* * *", tex, ee slot = textureSlots[tex] try: new_terrain.paste(image, slot, image) except Exception, eee: print "* * * new_terrain error:", eee self.propogated_textures.append(slot) log.debug(" Image pasted and propagated.") except Exception as e: try: # Print the resource pack 'raw' name. print "An Exception occurred while trying to parse textures for {}".format(self._pack_name) log.debug("An Exception occurred while trying to parse textures for {}".format(self._pack_name)) except: # I for a reason it fails, print the 'representation' of it. print "An Exception occurred while trying to parse textures for {}".format(repr(self._pack_name)) log.debug("An Exception occurred while trying to parse textures for {}".format(repr(self._pack_name))) traceback.print_stack() print "Exception Message: "+str(e) log.debug("Exception Message: "+str(e)) print "Exception type: "+str(type(e)) log.debug("Exception type: "+str(type(e))) print e self.__stop = True self._isEmpty = True log.debug("Parsing stopped.") log.debug("Resource pack considered as empty.") pass for runAnyway in multiparts.runAnyways: parsed_texture = runAnyway.parse_texture() if parsed_texture is not None: new_terrain.paste(parsed_texture, runAnyway.position, parsed_texture) self.propogated_textures.append(runAnyway.position) copy = self.old_terrain.copy() log.debug("Correcting textures...") for t in self.all_texture_slots: if t not in self.propogated_textures: old_tex = copy.crop((t[0],t[1],t[0]+16,t[1]+16)) new_terrain.paste(old_tex, t, old_tex) log.debug(" Done.") log.debug("Saving %s."%self._terrain_path) new_terrain.save(self._terrain_path) log.debug(" Done.") try: os.remove(self._pack_name.replace(" ", "_")+".png") except: pass if not self.propogated_textures: os.remove(self._terrain_path) self._isEmpty = True log.debug("No propagated textures.\nTexture pack considered as empty.") #print u"{} did not replace any textures".format(self._pack_name) del self.block_image if hasattr(self, 'fps'): log.debug(" Closing file descriptors.") for f in self.fps: f.close() log.debug(" Done") log.debug("Parsing terrain.png ended.")
[docs] def handle_too_big_packs(self): ''' Removes the parsed PNG file ''' self._too_big = True log.debug("Resource pack is too big.") #print u"{} seems to be a higher resolution than supported".format(self._pack_name) try: os.remove(self._terrain_path) except: pass del self.block_image
[docs]class ZipResourcePack(IResourcePack): ''' Represents a single Resource Pack that is in a zip file ''' def __init__(self, zipfileFile, noEncConvert=False): self.zipfile = zipfileFile log.debug("Zip file: %s"%zipfileFile) self._pack_name = os.path.splitext(os.path.split(zipfileFile)[-1])[0] log.debug("Pack name: %s"%self._pack_name) # Define a list of opened textures file objects to be cleaned when operations are finished. self.fps = [] IResourcePack.__init__(self) if not os.path.exists(self._terrain_path): try: self.open_pack() except Exception, e: if 'seek' not in e: print "Error while trying to load one of the resource packs: {}".format(e)
[docs] def open_pack(self): ''' Opens the zip file and puts texture data into a dictionary, where the key is the texture file name, and the value is a PIL.Image instance ''' zfile = zipfile.ZipFile(self.zipfile) self.fps = [] for name in zfile.infolist(): if name.filename.endswith(".png") and not name.filename.split(os.path.sep)[-1].startswith("._"): # log.debug("Image found: %s"%name.filename) filename = "assets/minecraft/textures/blocks" if name.filename.startswith(filename) and name.filename.replace(filename+"/", "").replace(".png","") in textureSlots: log.debug(" Is a possible texture.") block_name = os.path.normpath(name.filename).split(os.path.sep)[-1] block_name = block_name.split(".")[0] #zfile.extract(name.filename, self.texture_path) log.debug(" Block name: %s"%block_name) log.debug(" Opening %s"%name) fp = zfile.open(name) #!# Sending this 'fp' file descriptor to PIL.Image does not work, because such #!# descriptors are not seekable. #!# But, reading the fd data and writing it to a temporary file seem to work... log.debug(" Done. (%s, seekable: %s, readable: %s)"%(type(fp), fp.seekable(), fp.readable())) log.debug(" Saving fp data to temp file.") fp1 = StringIO() fp1.write(fp.read()) fp.close() fp1.seek(0) log.debug(" Done.") #possible_texture = Image.open(os.path.join(self.texture_path, os.path.normpath(name.filename))) try: possible_texture = Image.open(fp1) log.debug(" File descriptor for %s opened."%block_name) log.debug(" Is %s."%repr(possible_texture.size)) except Exception, e: log.debug(" Can't open descriptor for %s"%block_name) log.debug(" System said:") log.debug(" %s"%repr(e)) if possible_texture.size == (16, 16): self.block_image[block_name] = possible_texture if block_name.startswith("repeater_") or block_name.startswith("comparator_"): self.block_image[block_name+"_west"] = possible_texture.rotate(-90) self.block_image[block_name+"_north"] = possible_texture.rotate(180) self.block_image[block_name+"_east"] = possible_texture.rotate(90) self.block_image[block_name+"_south"] = possible_texture if block_name == "piston_side": self.block_image["piston_up"] = possible_texture self.block_image["piston_left"] = possible_texture.rotate(90) self.block_image["piston_down"] = possible_texture.rotate(180) self.block_image["piston_right"] = possible_texture.rotate(-90) if block_name == "hay_block_side": self.block_image["hay_block_side_rotated"] = possible_texture.rotate(-90) if block_name == "quartz_block_lines": self.block_image["quartz_block_lines_rotated"] = possible_texture.rotate(-90) if block_name.startswith("bed_"): if block_name == "bed_head_side": self.block_image["bed_head_side_flipped"] = possible_texture.transpose(Image.FLIP_LEFT_RIGHT) if block_name == "bed_feet_side": self.block_image["bed_feet_side_flipped"] = possible_texture.transpose(Image.FLIP_LEFT_RIGHT) if block_name == "bed_head_top": self.block_image["bed_head_top_flipped"] = possible_texture.transpose(Image.FLIP_LEFT_RIGHT) self.block_image["bed_head_top_bottom"] = possible_texture.rotate(-90) self.block_image["bed_head_top_top"] = possible_texture.rotate(90) if block_name == "bed_feet_top": self.block_image["bed_feet_top_flipped"] = possible_texture.transpose(Image.FLIP_LEFT_RIGHT) self.block_image["bed_feet_top_bottom"] = possible_texture.rotate(-90) self.block_image["bed_feet_top_top"] = possible_texture.rotate(90) log.debug(" Is loaded.") else: if possible_texture.size == (32, 32): self.block_image[block_name] = possible_texture.resize((16, 16)) log.debug(" Is loaded.") elif possible_texture.size == (64, 64) or possible_texture.size == (128, 128) or possible_texture.size == (256, 256): self.big_textures_counted += 1 log.debug(" Is too big.") else: self.block_image[block_name] = possible_texture.crop((0,0,16,16)) log.debug(" Is loaded.") self.fps.append(fp1) if self.big_textures_counted >= self.big_textures_max: self.handle_too_big_packs() else: self.parse_terrain_png()
[docs]class FolderResourcePack(IResourcePack): def __init__(self, folder, noEncConvert=False): self._folder = folder self._pack_name = self._folder.replace(" ", "_") IResourcePack.__init__(self) self._full_path = os.path.join(directories.getMinecraftProfileDirectory(directories.getSelectedProfile()), "resourcepacks", self._folder) self.texture_path = os.path.join(directories.parentDir, "textures", self._pack_name) if not os.path.exists(self._terrain_path): self.add_textures()
[docs] def add_textures(self): ''' Scraps the block textures folder and puts texture data into a dictionary with exactly identical structure as ZipResourcePack ''' base_path = os.path.join(self._full_path, "assets", "minecraft", "textures", "blocks") if os.path.exists(base_path): files = os.listdir(base_path) for tex_file in files: if tex_file.endswith(".png") and not tex_file.startswith("._") and tex_file.replace(".png","") in textureSlots: possible_texture = Image.open(os.path.join(base_path, tex_file)) block_name = tex_file[:-4] if possible_texture.size == (16, 16): self.block_image[block_name] = possible_texture if block_name.startswith("repeater_") or block_name.startswith("comparator_"): self.block_image[block_name+"_west"] = possible_texture.rotate(-90) self.block_image[block_name+"_north"] = possible_texture.rotate(180) self.block_image[block_name+"_east"] = possible_texture.rotate(90) self.block_image[block_name+"_south"] = possible_texture if block_name == "piston_side": self.block_image["piston_up"] = possible_texture self.block_image["piston_left"] = possible_texture.rotate(90) self.block_image["piston_down"] = possible_texture.rotate(180) self.block_image["piston_right"] = possible_texture.rotate(-90) if block_name == "hay_block_side": self.block_image["hay_block_side_rotated"] = possible_texture.rotate(-90) if block_name == "quartz_block_lines": self.block_image["quartz_block_lines_rotated"] = possible_texture.rotate(-90) if block_name.startswith("bed_"): if block_name == "bed_head_side": self.block_image["bed_head_side_flipped"] = possible_texture.transpose(Image.FLIP_LEFT_RIGHT) if block_name == "bed_feet_side": self.block_image["bed_feet_side_flipped"] = possible_texture.transpose(Image.FLIP_LEFT_RIGHT) if block_name == "bed_head_top": self.block_image["bed_head_top_flipped"] = possible_texture.transpose(Image.FLIP_LEFT_RIGHT) self.block_image["bed_head_top_bottom"] = possible_texture.rotate(-90) self.block_image["bed_head_top_top"] = possible_texture.rotate(90) if block_name == "bed_feet_top": self.block_image["bed_feet_top_flipped"] = possible_texture.transpose(Image.FLIP_LEFT_RIGHT) self.block_image["bed_feet_top_bottom"] = possible_texture.rotate(-90) self.block_image["bed_feet_top_top"] = possible_texture.rotate(90) else: if possible_texture.size == (32, 32): self.block_image[block_name] = possible_texture.resize((16, 16)) if possible_texture.size == (64, 64) or possible_texture.size == (128, 128) or possible_texture.size == (256, 256): self.big_textures_counted += 1 else: self.block_image[block_name] = possible_texture.crop((0,0,16,16)) if self.big_textures_counted >= self.big_textures_max: self.handle_too_big_packs() else: self.parse_terrain_png()
[docs]class DefaultResourcePack(IResourcePack): ''' Represents the default Resource Pack that is always present ''' def __init__(self): self._isEmpty = False self._too_big = False self._terrain_path = os.path.join(directories.getDataDir(), "terrain.png") self._pack_name = "Default"
[docs] def terrain_path(self): return self._terrain_path
@property def isEmpty(self): return self._isEmpty @property def tooBig(self): return self._too_big
@Singleton class ResourcePackHandler: ''' A single point to manage which Resource Pack is being used and to provide the paths to each parsed PNG ''' Instance = None def setup_reource_packs(self): ''' Handles parsing of Resource Packs and removing ones that are either have to0 high of a resolution, or don't replace any textures ''' log.debug("Setting up the resource packs.") self._resource_packs = {} try: os.mkdir("terrain-textures") except OSError: pass self._resource_packs["Default Resource Pack"] = DefaultResourcePack() if os.path.exists(os.path.join(directories.getMinecraftProfileDirectory(directories.getSelectedProfile()), "resourcepacks")): log.debug("Gathering zipped packs...") zipResourcePacks = directories.getAllOfAFile(unicode(os.path.join(directories.getMinecraftProfileDirectory(directories.getSelectedProfile()), "resourcepacks")), ".zip") log.debug("Gatering folder packs...") folderResourcePacks = os.listdir(unicode(os.path.join(directories.getMinecraftProfileDirectory(directories.getSelectedProfile()), "resourcepacks"))) log.debug("Processing zipped packs...") for zip_tex_pack in zipResourcePacks: zrp = ZipResourcePack(zip_tex_pack) if not zrp.isEmpty: if not zrp.tooBig: self._resource_packs[zrp.pack_name] = zrp log.debug("Processing folder packs...") for folder_tex_pack in folderResourcePacks: if os.path.isdir(os.path.join(directories.getMinecraftProfileDirectory(directories.getSelectedProfile()), "resourcepacks", folder_tex_pack)): frp = FolderResourcePack(folder_tex_pack) if not frp.isEmpty: if not frp.tooBig: self._resource_packs[frp.pack_name] = frp for tex in self._resource_packs.keys(): pack = self._resource_packs[tex] if not os.path.exists(pack.terrain_path()): del self._resource_packs[tex] try: shutil.rmtree(os.path.join(directories.parentDir, "textures")) except: print "Could not remove \"textures\" directory" pass def __init__(self): try: os.mkdir(os.path.join(directories.parentDir, "textures")) except OSError: pass self.setup_reource_packs() self._selected_resource_pack = config.settings.resourcePack.get() if self._selected_resource_pack not in self._resource_packs.keys(): self.set_selected_resource_pack_name("Default Resource Pack") @property def resource_packs(self): ''' A dictionary of Resource Packs, where the key is the pack's file/folder name, and the value is the path to the parsed PNG ''' return self._resource_packs def get_available_resource_packs(self): ''' Returns the names of all the Resource Packs that can be used ''' return self._resource_packs.keys() def reload_resource_packs(self): ''' Reparses all Resource Packs ''' self.setup_resource_packs() def reparse_resource_pack(self, packName): if packName in self._resource_packs: pack = self._resource_packs[packName] if isinstance(pack, FolderResourcePack): pack.add_textures() elif isinstance(pack, ZipResourcePack): pack.open_pack() def get_selected_resource_pack_name(self): ''' Returns the currently selected Resource Pack's name ''' return self._selected_resource_pack def set_selected_resource_pack_name(self, name): ''' Sets the currently selected Resource Pack :param name: Name of the Resource Pack ''' config.settings.resourcePack.set(name) self._selected_resource_pack = name def get_selected_resource_pack(self): ''' Returns the selected Resource Pack instance. Can be an instance of either DefaultResourcePack, ZipResourcePack or FolderResourcePack ''' return self._resource_packs[self._selected_resource_pack] #packs = ResourcePackHandler()