Skip to content

Commit

Permalink
Fix #139: Don't create cache directories if --no-cache is passed.
Browse files Browse the repository at this point in the history
  • Loading branch information
FLHerne committed May 12, 2020
1 parent 539c30e commit 4a36d68
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 69 deletions.
38 changes: 0 additions & 38 deletions nml/generic.py
Expand Up @@ -481,41 +481,3 @@ def find_file(filepath):
_paths[childpath] = path

return path

cache_root_dir = ".nmlcache"

def set_cache_root_dir(dir):
global cache_root_dir
cache_root_dir = os.path.abspath(dir)
os.makedirs(cache_root_dir, exist_ok=True)

def get_cache_file(sources, extension):
"""
Compose a filename for a cache file.
@param sources: List of source files, the cache file depends on / belongs to.
@type sources: C{list} or C{tuple} of C{str} or similar.
@param extension: File extension for the cache file including leading ".".
@type extension: C{str}
@return: Filename for cache file.
@rtype: C{str}
"""
result = ""

for part in sources:
if part is not None:
path, name = os.path.split(part)

if len(result) == 0:
# Make sure that the path does not leave the cache dir
path = os.path.normpath(path).replace(os.path.pardir, "__")
path = os.path.join(cache_root_dir, path)
os.makedirs(path, exist_ok=True)
result = os.path.join(path, name)
else:
# In case of multiple soure files, ignore the path component for all but the first
result += "_" + name

return result + extension
13 changes: 5 additions & 8 deletions nml/main.py
Expand Up @@ -75,10 +75,10 @@ def parse_cli(argv):
opt_parser.add_option("-D", "--debug-parser", action="store_true", dest="debug_parser", help="Enable debug mode for parser.")

opts, args = opt_parser.parse_args(argv)

generic.set_verbosity(0 if opts.quiet else opts.verbosity)
generic.set_cache_root_dir(opts.cache_dir)

spritecache.keep_orphaned = opts.keep_orphaned
spritecache.cache_root_dir = None if opts.no_cache else os.path.abspath(opts.cache_dir)

opts.outputfile_given = (opts.grf_filename or opts.nfo_filename or opts.nml_filename or opts.dep_filename or opts.outputs)

Expand Down Expand Up @@ -167,15 +167,15 @@ def main(argv):
generic.print_error("Unknown output format {}".format(outext))
sys.exit(2)

ret = nml(input, input_filename, opts.debug, outputs, opts.start_sprite_num, opts.compress, opts.crop, not opts.no_cache, opts.forced_palette, opts.md5_filename, opts.rebuild_parser, opts.debug_parser)
ret = nml(input, input_filename, opts.debug, outputs, opts.start_sprite_num, opts.compress, opts.crop, opts.forced_palette, opts.md5_filename, opts.rebuild_parser, opts.debug_parser)

input.close()
sys.exit(ret)

def filename_output_from_input(name, ext):
return os.path.splitext(name)[0] + ext

def nml(inputfile, input_filename, output_debug, outputfiles, start_sprite_num, compress_grf, crop_sprites, enable_cache, forced_palette, md5_filename, rebuild_parser, debug_parser):
def nml(inputfile, input_filename, output_debug, outputfiles, start_sprite_num, compress_grf, crop_sprites, forced_palette, md5_filename, rebuild_parser, debug_parser):
"""
Compile an NML file.
Expand All @@ -197,9 +197,6 @@ def nml(inputfile, input_filename, output_debug, outputfiles, start_sprite_num,
@param crop_sprites: Enable sprite cropping.
@type crop_sprites: C{bool}
@param enable_cache: Enable sprite cache.
@type enable_cache: C{bool}
@param forced_palette: Palette to use for the file.
@type forced_palette: C{str}
Expand Down Expand Up @@ -358,7 +355,7 @@ def nml(inputfile, input_filename, output_debug, outputfiles, start_sprite_num,
outputfile.palette = used_palette # used by RecolourSpriteAction
if isinstance(outputfile, output_grf.OutputGRF):
if encoder is None:
encoder = spriteencoder.SpriteEncoder(compress_grf, crop_sprites, enable_cache, used_palette)
encoder = spriteencoder.SpriteEncoder(compress_grf, crop_sprites, used_palette)
outputfile.encoder = encoder

generic.clear_progress()
Expand Down
79 changes: 66 additions & 13 deletions nml/spritecache.py
Expand Up @@ -17,6 +17,7 @@
from nml import generic

keep_orphaned = True
cache_root_dir = None

class SpriteCache:
"""
Expand Down Expand Up @@ -77,12 +78,17 @@ class SpriteCache:
meta-information or padding. Offsets and sizes for the various sprites
are in the cacheindex file.
"""
def __init__(self, filename):
self.cache_filename = filename + ".cache"
self.cache_index_filename = filename + ".cacheindex"
def __init__(self, sources=None):
self.sources = sources
self.cache_time = 0
self.cached_sprites = {}

def open_cache_index_file(self, write=False):
return _open_cache_file(self.sources, ".cacheindex", 'w' if write else 'r')

def open_cache_file(self, write=False):
return _open_cache_file(self.sources, ".cache", 'wb' if write else 'rb')

def get_item(self, cache_key, palette):
"""
Get item from cache.
Expand Down Expand Up @@ -137,12 +143,11 @@ def read_cache(self):
"""
Read the *.grf.cache[index] files.
"""
if not (os.access(self.cache_filename, os.R_OK) and os.access(self.cache_index_filename, os.R_OK)):
# Cache files don't exist
index_file = self.open_cache_index_file()
cache_file = self.open_cache_file()
if not (index_file and cache_file):
return

index_file = open(self.cache_index_filename, 'r')
cache_file = open(self.cache_filename, 'rb')
cache_data = array.array('B')
cache_size = os.fstat(cache_file.fileno()).st_size
cache_data.fromfile(cache_file, cache_size)
Expand Down Expand Up @@ -248,6 +253,10 @@ def write_cache(self):
"""
Write the cache data to the .cache[index] files.
"""

if cache_root_dir is None:
return

index_data = []
sprite_data = array.array('B')
offset = 0
Expand Down Expand Up @@ -292,9 +301,53 @@ def write_cache(self):

index_output = json.JSONEncoder(sort_keys = True).encode(index_data)

index_file = open(self.cache_index_filename, 'w')
index_file.write(index_output)
index_file.close()
cache_file = open(self.cache_filename, 'wb')
sprite_data.tofile(cache_file)
cache_file.close()
with self.open_cache_index_file(write=True) as index_file:
index_file.write(index_output)

with self.open_cache_file(write=True) as cache_file:
sprite_data.tofile(cache_file)


def _open_cache_file(sources, extension, mode):
"""
Open a file in the cache directory.
@param sources: List of source files, the cache file depends on / belongs to.
@type sources: C{list} or C{tuple} of C{str} or similar.
@param extension: File extension for the cache file including leading ".".
@type extension: C{str}
@param mode: Mode to use in open() - 'r', 'w', 'rb' or 'wb'
@type mode: C{str}
@return: Filename for cache file.
@rtype: C{io.IOBase}, or C{None} if the file can't be opened.
"""
if cache_root_dir = None or not sources:
return None

filepath = ""

for part in sources:
if part is None:
continue
path, name = os.path.split(part)
if len(filename) == 0:
# Make sure that the path does not leave the cache dir
path = os.path.normpath(path).replace(os.path.pardir, "__")
filepath = os.path.join(cache_root_dir, path, name)
else:
# In case of multiple source files, ignore the path component for all but the first
filepath += "_" + name

filepath += extension

try:
if 'w' in mode:
os.makedirs(os.dirname(filepath), exist_ok=True)
return open(filepath, mode)
except OSError:
if 'w' in mode:
generic.print_warning(f"Can't create cache file {filepath}. Check permissions, or use --cache-dir or --no-cache.")
return None
15 changes: 5 additions & 10 deletions nml/spriteencoder.py
Expand Up @@ -65,21 +65,17 @@ class SpriteEncoder:
@ivar crop_sprites: Crop sprites if possible.
@type crop_sprites: C{bool}
@ivar enable_cache: Read/write cache from/to disk.
@type enable_cache: C{bool}
@ivar palette: Palette for encoding, see L{palette.palette_name}.
@type palette: C{str}
@ivar cached_image_files: Currently opened source image files.
@type cached_image_files: C{dict} mapping C{str} to C{Image}
"""
def __init__(self, compress_grf, crop_sprites, enable_cache, palette):
def __init__(self, compress_grf, crop_sprites, palette):
self.compress_grf = compress_grf
self.crop_sprites = crop_sprites
self.enable_cache = enable_cache
self.palette = palette
self.sprite_cache = spritecache.SpriteCache("")
self.sprite_cache = spritecache.SpriteCache()
self.cached_image_files = {}

def open(self, sprite_files):
Expand All @@ -105,9 +101,8 @@ def open(self, sprite_files):

source_name = "_".join(src for src in sources if src is not None)

local_cache = spritecache.SpriteCache(generic.get_cache_file(sources, ""))
if self.enable_cache:
local_cache.read_cache()
local_cache = spritecache.SpriteCache(sources)
local_cache.read_cache()

for sprite_info in sprite_list:
count_sprites += 1
Expand Down Expand Up @@ -140,7 +135,7 @@ def open(self, sprite_files):
num_orphaned += local_cache.count_orphaned()

# Only write cache if compression is enabled. Uncompressed data is not worth to be cached.
if self.enable_cache and self.compress_grf:
if self.compress_grf:
local_cache.write_cache()

# Transfer data to global cache for later usage
Expand Down

0 comments on commit 4a36d68

Please sign in to comment.