0

textures: greatly simplify find_file and friends

find_file contained a lot of code for searching textures in ways that
haven't been relevant for years. For example, it had workarounds for
MCPatcher, a tool that hasn't been updated since 2015.

Instead of searching in multiple paths that will never succeed in a
resource pack and Minecraft version that actually work, just look
for the filename that the block functions ask for.

Also, if we already have a manually specified resource pack open,
we shouldn't repeatedly re-open it. Furthermore, it's best if
the very first thing find_file does is try to find the file in
the resource pack or client jar we've already opened previously.

We can now also remove chest.png, ender.png, foliage.png, grass.png
and normal_double.png because Minecraft ships them, so we don't
need to ship them ourselves too. Us shipping them actually hid the
whole find_file garbagefire.

Concerns issue #1604.
This commit is contained in:
Nicolas F
2019-07-04 18:24:57 +02:00
parent 864be71667
commit 5b0430f94b
6 changed files with 53 additions and 109 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -151,6 +151,7 @@ class Textures(object):
"""Searches for the given file and returns an open handle to it. """Searches for the given file and returns an open handle to it.
This searches the following locations in this order: This searches the following locations in this order:
* In an already open resource pack or client jar file
* In the directory textures_path given in the initializer * In the directory textures_path given in the initializer
* In the resource pack given by textures_path * In the resource pack given by textures_path
* The program dir (same dir as overviewer.py) for extracted textures * The program dir (same dir as overviewer.py) for extracted textures
@@ -167,58 +168,40 @@ class Textures(object):
* The overviewer_core/data/textures dir * The overviewer_core/data/textures dir
In all of these, files are searched for in '.', 'anim', 'misc/', and
'environment/'.
""" """
if verbose: logging.info("Starting search for {0}".format(filename)) if verbose: logging.info("Starting search for {0}".format(filename))
# a list of subdirectories to search for a given file, # we've sucessfully loaded something from here before, so let's quickly try
# after the obvious '.' # this before searching again
search_dirs = ['anim', 'misc', 'environment', 'item', 'item/chests', 'entity', 'entity/chest'] if self.jar is not None:
search_zip_paths = [filename,] + [d + '/' + filename for d in search_dirs] try:
def search_dir(base): self.jar.getinfo(filename)
"""Search the given base dir for filename, in search_dirs.""" if verbose: logging.info("Found (cached) %s in '%s'", filename, self.jarpath)
for path in [os.path.join(base, d, filename) for d in ['',] + search_dirs]: return self.jar.open(filename)
if verbose: logging.info('filename: ' + filename + ' ; path: ' + path) except (KeyError, IOError) as e:
if os.path.isfile(path): pass
return path
return None
if verbose: logging.info('search_zip_paths: ' + ', '.join(search_zip_paths))
# A texture path was given on the command line. Search this location # A texture path was given on the command line. Search this location
# for the file first. # for the file first.
if self.find_file_local_path: if self.find_file_local_path:
if os.path.isdir(self.find_file_local_path): if os.path.isdir(self.find_file_local_path):
path = search_dir(self.find_file_local_path) full_path = os.path.join(self.find_file_local_path, filename)
if path: if os.path.isfile(full_path):
if verbose: logging.info("Found %s in '%s'", filename, path) if verbose: logging.info("Found %s in '%s'", filename, full_path)
return open(path, mode) return open(full_path, mode)
elif os.path.isfile(self.find_file_local_path): elif os.path.isfile(self.find_file_local_path):
# Must be a resource pack. Look for the requested file within # Must be a resource pack. Look for the requested file within
# it. # it.
try: try:
pack = zipfile.ZipFile(self.find_file_local_path) pack = zipfile.ZipFile(self.find_file_local_path)
for packfilename in search_zip_paths: # pack.getinfo() will raise KeyError if the file is
try: # not found.
# pack.getinfo() will raise KeyError if the file is pack.getinfo(filename)
# not found. if verbose: logging.info("Found %s in '%s'", filename,
pack.getinfo(packfilename) self.find_file_local_path)
if verbose: logging.info("Found %s in '%s'", packfilename, self.find_file_local_path) self.jar, self.jarpath = pack, self.find_file_local_path
return pack.open(packfilename) return pack.open(filename)
except (KeyError, IOError): except (zipfile.BadZipfile, KeyError, IOError):
pass
try:
# 2nd try with completed path.
packfilename = 'assets/minecraft/textures/' + packfilename
pack.getinfo(packfilename)
if verbose: logging.info("Found %s in '%s'", packfilename, self.find_file_local_path)
return pack.open(packfilename)
except (KeyError, IOError):
pass
except (zipfile.BadZipfile, IOError):
pass pass
# If we haven't returned at this point, then the requested file was NOT # If we haven't returned at this point, then the requested file was NOT
@@ -228,31 +211,20 @@ class Textures(object):
# Look in the location of the overviewer executable for the given path # Look in the location of the overviewer executable for the given path
programdir = util.get_program_path() programdir = util.get_program_path()
path = search_dir(programdir) path = os.path.join(programdir, filename)
if path: if os.path.isfile(path):
if verbose: logging.info("Found %s in '%s'", filename, path) if verbose: logging.info("Found %s in '%s'", filename, path)
return open(path, mode) return open(path, mode)
if sys.platform.startswith("darwin"): if sys.platform.startswith("darwin"):
path = search_dir("/Applications/Minecraft") path = os.path.join("/Applications/Minecraft", filename)
if path: if os.path.isfile(path):
if verbose: logging.info("Found %s in '%s'", filename, path) if verbose: logging.info("Found %s in '%s'", filename, path)
return open(path, mode) return open(path, mode)
if verbose: logging.info("Did not find the file in overviewer executable directory") if verbose: logging.info("Did not find the file in overviewer executable directory")
if verbose: logging.info("Looking for installed minecraft jar files...") if verbose: logging.info("Looking for installed minecraft jar files...")
# we've sucessfully loaded something from here before, so let's quickly try
# this before searching again
if self.jar is not None:
for jarfilename in search_zip_paths:
try:
self.jar.getinfo(jarfilename)
if verbose: logging.info("Found (cached) %s in '%s'", jarfilename, self.jarpath)
return self.jar.open(jarfilename)
except (KeyError, IOError) as e:
pass
# Find an installed minecraft client jar and look in it for the texture # Find an installed minecraft client jar and look in it for the texture
# file we need. # file we need.
versiondir = "" versiondir = ""
@@ -308,14 +280,13 @@ class Textures(object):
if os.path.isfile(jarpath): if os.path.isfile(jarpath):
jar = zipfile.ZipFile(jarpath) jar = zipfile.ZipFile(jarpath)
for jarfilename in search_zip_paths: try:
try: jar.getinfo(filename)
jar.getinfo(jarfilename) if verbose: logging.info("Found %s in '%s'", filename, jarpath)
if verbose: logging.info("Found %s in '%s'", jarfilename, jarpath) self.jar, self.jarpath = jar, jarpath
self.jar, self.jarpath = jar, jarpath return jar.open(filename)
return jar.open(jarfilename) except (KeyError, IOError) as e:
except (KeyError, IOError) as e: pass
pass
if verbose: logging.info("Did not find file {0} in jar {1}".format(filename, jarpath)) if verbose: logging.info("Did not find file {0} in jar {1}".format(filename, jarpath))
@@ -326,14 +297,14 @@ class Textures(object):
# believe that's not true, but we still have a few files distributed # believe that's not true, but we still have a few files distributed
# with overviewer. # with overviewer.
if verbose: logging.info("Looking for texture in overviewer_core/data/textures") if verbose: logging.info("Looking for texture in overviewer_core/data/textures")
path = search_dir(os.path.join(programdir, "overviewer_core", "data", "textures")) path = os.path.join(programdir, "overviewer_core", "data", "textures", filename)
if path: if os.path.isfile(path):
if verbose: logging.info("Found %s in '%s'", filename, path) if verbose: logging.info("Found %s in '%s'", filename, path)
return open(path, mode) return open(path, mode)
elif hasattr(sys, "frozen") or imp.is_frozen("__main__"): elif hasattr(sys, "frozen") or imp.is_frozen("__main__"):
# windows special case, when the package dir doesn't exist # windows special case, when the package dir doesn't exist
path = search_dir(os.path.join(programdir, "textures")) path = os.path.join(programdir, "textures", filename)
if path: if os.path.isfile(path):
if verbose: logging.info("Found %s in '%s'", filename, path) if verbose: logging.info("Found %s in '%s'", filename, path)
return open(path, mode) return open(path, mode)
@@ -380,67 +351,40 @@ class Textures(object):
def load_water(self): def load_water(self):
"""Special-case function for loading water, handles """Special-case function for loading water."""
MCPatcher-compliant custom animated water."""
watertexture = getattr(self, "watertexture", None) watertexture = getattr(self, "watertexture", None)
if watertexture: if watertexture:
return watertexture return watertexture
try: watertexture = self.load_image_texture("assets/minecraft/textures/block/water_still.png")
# try the MCPatcher case first
watertexture = self.load_image("custom_water_still.png")
watertexture = watertexture.crop((0, 0, watertexture.size[0], watertexture.size[0]))
except TextureException:
watertexture = self.load_image_texture("assets/minecraft/textures/block/water_still.png")
self.watertexture = watertexture self.watertexture = watertexture
return watertexture return watertexture
def load_lava(self): def load_lava(self):
"""Special-case function for loading lava, handles """Special-case function for loading lava."""
MCPatcher-compliant custom animated lava."""
lavatexture = getattr(self, "lavatexture", None) lavatexture = getattr(self, "lavatexture", None)
if lavatexture: if lavatexture:
return lavatexture return lavatexture
try: lavatexture = self.load_image_texture("assets/minecraft/textures/block/lava_still.png")
# try the MCPatcher lava first, in case it's present
lavatexture = self.load_image("custom_lava_still.png")
lavatexture = lavatexture.crop((0, 0, lavatexture.size[0], lavatexture.size[0]))
except TextureException:
lavatexture = self.load_image_texture("assets/minecraft/textures/block/lava_still.png")
self.lavatexture = lavatexture self.lavatexture = lavatexture
return lavatexture return lavatexture
def load_fire(self): def load_fire(self):
"""Special-case function for loading fire, handles """Special-case function for loading fire."""
MCPatcher-compliant custom animated fire."""
firetexture = getattr(self, "firetexture", None) firetexture = getattr(self, "firetexture", None)
if firetexture: if firetexture:
return firetexture return firetexture
try: fireNS = self.load_image_texture("assets/minecraft/textures/block/fire_0.png")
# try the MCPatcher case first fireEW = self.load_image_texture("assets/minecraft/textures/block/fire_1.png")
firetextureNS = self.load_image("custom_fire_n_s.png") firetexture = (fireNS, fireEW)
firetextureNS = firetextureNS.crop((0, 0, firetextureNS.size[0], firetextureNS.size[0]))
firetextureEW = self.load_image("custom_fire_e_w.png")
firetextureEW = firetextureEW.crop((0, 0, firetextureEW.size[0], firetextureEW.size[0]))
firetexture = (firetextureNS,firetextureEW)
except TextureException:
fireNS = self.load_image_texture("assets/minecraft/textures/block/fire_0.png")
fireEW = self.load_image_texture("assets/minecraft/textures/block/fire_1.png")
firetexture = (fireNS, fireEW)
self.firetexture = firetexture self.firetexture = firetexture
return firetexture return firetexture
def load_portal(self): def load_portal(self):
"""Special-case function for loading portal, handles """Special-case function for loading portal."""
MCPatcher-compliant custom animated portal."""
portaltexture = getattr(self, "portaltexture", None) portaltexture = getattr(self, "portaltexture", None)
if portaltexture: if portaltexture:
return portaltexture return portaltexture
try: portaltexture = self.load_image_texture("assets/minecraft/textures/block/nether_portal.png")
# try the MCPatcher case first
portaltexture = self.load_image("custom_portal.png")
portaltexture = portaltexture.crop((0, 0, portaltexture.size[0], portaltexture.size[1]))
except TextureException:
portaltexture = self.load_image_texture("assets/minecraft/textures/block/nether_portal.png")
self.portaltexture = portaltexture self.portaltexture = portaltexture
return portaltexture return portaltexture
@@ -459,13 +403,13 @@ class Textures(object):
def load_grass_color(self): def load_grass_color(self):
"""Helper function to load the grass color texture.""" """Helper function to load the grass color texture."""
if not hasattr(self, "grasscolor"): if not hasattr(self, "grasscolor"):
self.grasscolor = list(self.load_image("grass.png").getdata()) self.grasscolor = list(self.load_image("assets/minecraft/textures/colormap/grass.png").getdata())
return self.grasscolor return self.grasscolor
def load_foliage_color(self): def load_foliage_color(self):
"""Helper function to load the foliage color texture.""" """Helper function to load the foliage color texture."""
if not hasattr(self, "foliagecolor"): if not hasattr(self, "foliagecolor"):
self.foliagecolor = list(self.load_image("foliage.png").getdata()) self.foliagecolor = list(self.load_image("assets/minecraft/textures/colormap/foliage.png").getdata())
return self.foliagecolor return self.foliagecolor
#I guess "watercolor" is wrong. But I can't correct as my texture pack don't define water color. #I guess "watercolor" is wrong. But I can't correct as my texture pack don't define water color.
@@ -2091,12 +2035,12 @@ def chests(self, blockid, data):
# ancilData = 2,3,4,5 are used for this blockids # ancilData = 2,3,4,5 are used for this blockids
if data & 24 == 0: if data & 24 == 0:
if blockid == 130: t = self.load_image("ender.png") if blockid == 130: t = self.load_image("assets/minecraft/textures/entity/chest/ender.png")
else: else:
try: try:
t = self.load_image("normal.png") t = self.load_image("assets/minecraft/textures/entity/chest/normal.png")
except (TextureException, IOError): except (TextureException, IOError):
t = self.load_image("chest.png") t = self.load_image("assets/minecraft/textures/entity/chest/chest.png")
# the textures is no longer in terrain.png, get it from # the textures is no longer in terrain.png, get it from
# item/chest.png and get by cropping all the needed stuff # item/chest.png and get by cropping all the needed stuff
@@ -2151,7 +2095,7 @@ def chests(self, blockid, data):
# large chest # large chest
# the textures is no longer in terrain.png, get it from # the textures is no longer in terrain.png, get it from
# item/chest.png and get all the needed stuff # item/chest.png and get all the needed stuff
t = self.load_image("normal_double.png") t = self.load_image("assets/minecraft/textures/entity/chest/normal_double.png")
if t.size != (128,64): t = t.resize((128,64), Image.ANTIALIAS) if t.size != (128,64): t = t.resize((128,64), Image.ANTIALIAS)
# top # top
top = t.crop((14,0,44,14)) top = t.crop((14,0,44,14))