Beispiel #1
0
    def write_cache(self):
        """
        Write the cache data to the .cache[index] files.
        """
        if generic.cache_root_dir is None:
            # Writing cache files will fail, so bail early.
            return

        index_data = []
        sprite_data = array.array('B')
        offset = 0

        old_cache_valid = True
        for key, value in self.cached_sprites.items():
            # Unpack key/value
            rgb_file, rgb_rect, mask_file, mask_rect, do_crop, mask_pal = key
            data, info, crop_rect, pixel_stats, in_old_cache, in_use = value
            assert do_crop == (crop_rect is not None)
            assert (mask_file is None) == (mask_pal is None)

            # If this cache information is exactly the same as the old cache, then we don't bother writing later on
            if not in_use and not keep_orphaned:
                old_cache_valid = False
                continue
            if not in_old_cache:
                old_cache_valid = False

            # Create dictionary with information
            sprite = {}
            if rgb_file is not None:
                sprite['rgb_file'] = rgb_file
                sprite['rgb_rect'] = tuple(rgb_rect)
            if mask_file is not None:
                sprite['mask_file'] = mask_file
                sprite['mask_rect'] = tuple(mask_rect)
                sprite['mask_pal'] = mask_pal

            size = len(data)
            sprite['offset'] = offset
            sprite['size'] = size
            sprite['info'] = info
            if do_crop: sprite['crop'] = tuple(crop_rect)
            sprite['pixel_stats'] = pixel_stats

            index_data.append(sprite)
            sprite_data.extend(data)
            offset += size

        if old_cache_valid: return

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

        try:
            with generic.open_cache_file(self.sources, ".cache", 'wb') as cache_file, \
                    generic.open_cache_file(self.sources, ".cacheindex", 'w') as index_file:
                index_file.write(index_output)
                sprite_data.tofile(cache_file)
        except OSError:
            return
Beispiel #2
0
    def read_cache(self):
        """
        Read the *.grf.cache[index] files.
        """

        try:
            with generic.open_cache_file(self.sources, ".cache", "rb") as cache_file:
                cache_data = array.array("B")
                cache_size = os.fstat(cache_file.fileno()).st_size
                cache_data.fromfile(cache_file, cache_size)
                assert cache_size == len(cache_data)
                self.cache_time = os.path.getmtime(cache_file.name)

            with generic.open_cache_file(self.sources, ".cacheindex", "r") as index_file:
                index_file_name = index_file.name
                sprite_index = json.load(index_file)
        except OSError:
            # Cache files don't exist (or otherwise aren't readable)
            return
        except json.JSONDecodeError:
            generic.print_warning(
                generic.Warning.GENERIC,
                "{} contains invalid data, ignoring.".format(index_file_name)
                + " Please remove the file and file a bug report if this warning keeps appearing",
            )
            self.cached_sprites = {}
            return

        source_mtime = {}

        try:
            # Just assert and print a generic message on errors, as the cache data should be correct
            # Not asserting could lead to errors later on
            # Also, it doesn't make sense to inform the user about things he shouldn't know about and can't fix
            assert isinstance(sprite_index, list)
            for sprite in sprite_index:
                assert isinstance(sprite, dict)
                # load RGB (32bpp) data
                rgb_key = (None, None)
                if "rgb_file" in sprite and "rgb_rect" in sprite:
                    assert isinstance(sprite["rgb_file"], str)
                    assert isinstance(sprite["rgb_rect"], list) and len(sprite["rgb_rect"]) == 4
                    assert all(isinstance(num, int) for num in sprite["rgb_rect"])
                    rgb_key = (sprite["rgb_file"], tuple(sprite["rgb_rect"]))

                # load Mask (8bpp) data
                mask_key = (None, None)
                if "mask_file" in sprite and "mask_rect" in sprite:
                    assert isinstance(sprite["mask_file"], str)
                    assert isinstance(sprite["mask_rect"], list) and len(sprite["mask_rect"]) == 4
                    assert all(isinstance(num, int) for num in sprite["mask_rect"])
                    mask_key = (sprite["mask_file"], tuple(sprite["mask_rect"]))

                palette_key = None
                if "mask_pal" in sprite:
                    palette_key = sprite["mask_pal"]

                # Compose key
                assert any(i is not None for i in rgb_key + mask_key)
                key = rgb_key + mask_key + ("crop" in sprite, palette_key)
                assert key not in self.cached_sprites

                # Read size/offset from cache
                assert "offset" in sprite and "size" in sprite
                offset, size = sprite["offset"], sprite["size"]
                assert isinstance(offset, int) and isinstance(size, int)
                assert offset >= 0 and size > 0
                assert offset + size <= cache_size
                data = cache_data[offset : offset + size]

                # Read info / cropping data from cache
                assert "info" in sprite and isinstance(sprite["info"], int)
                info = sprite["info"]
                if "crop" in sprite:
                    assert isinstance(sprite["crop"], list) and len(sprite["crop"]) == 4
                    assert all(isinstance(num, int) for num in sprite["crop"])
                    crop = tuple(sprite["crop"])
                else:
                    crop = None

                if "pixel_stats" in sprite:
                    assert isinstance(sprite["pixel_stats"], dict)
                    pixel_stats = sprite["pixel_stats"]
                else:
                    pixel_stats = {}

                # Compose value
                value = (data, info, crop, pixel_stats, True, False)

                # Check if cache item is still valid
                is_valid = True
                if rgb_key[0] is not None:
                    mtime = source_mtime.get(rgb_key[0])
                    if mtime is None:
                        mtime = os.path.getmtime(generic.find_file(rgb_key[0]))
                        source_mtime[rgb_key[0]] = mtime

                    if mtime > self.cache_time:
                        is_valid = False

                if mask_key[0] is not None:
                    mtime = source_mtime.get(mask_key[0])
                    if mtime is None:
                        mtime = os.path.getmtime(generic.find_file(mask_key[0]))
                        source_mtime[mask_key[0]] = mtime

                    if mtime > self.cache_time:
                        is_valid = False

                # Drop items from older spritecache format without palette entry
                if (mask_key[0] is None) != (palette_key is None):
                    is_valid = False

                if is_valid:
                    self.cached_sprites[key] = value
        except Exception:
            generic.print_warning(
                generic.Warning.GENERIC,
                "{} contains invalid data, ignoring.".format(index_file_name)
                + " Please remove the file and file a bug report if this warning keeps appearing",
            )
            self.cached_sprites = {}  # Clear cache
Beispiel #3
0
    def read_cache(self):
        """
        Read the *.grf.cache[index] files.
        """

        try:
            with generic.open_cache_file(self.sources, ".cache", 'rb') as cache_file:
                index_file = generic.open_cache_file(self.sources, ".cacheindex", 'r')

                cache_data = array.array('B')
                cache_size = os.fstat(cache_file.fileno()).st_size
                cache_data.fromfile(cache_file, cache_size)
                assert cache_size == len(cache_data)
                self.cache_time = os.path.getmtime(cache_file.name)
        except OSError:
            # Cache files don't exist (or otherwise aren't readable)
            return

        # cache_file is closed by `with` block, but index_file is still open.

        source_mtime = dict()

        try:
            # Just assert and print a generic message on errors, as the cache data should be correct
            # Not asserting could lead to errors later on
            # Also, it doesn't make sense to inform the user about things he shouldn't know about and can't fix
            sprite_index = json.load(index_file)
            assert isinstance(sprite_index, list)
            for sprite in sprite_index:
                assert isinstance(sprite, dict)
                # load RGB (32bpp) data
                rgb_key = (None, None)
                if 'rgb_file' in sprite and 'rgb_rect' in sprite:
                    assert isinstance(sprite['rgb_file'], str)
                    assert isinstance(sprite['rgb_rect'], list) and len(sprite['rgb_rect']) == 4
                    assert all(isinstance(num, int) for num in sprite['rgb_rect'])
                    rgb_key = (sprite['rgb_file'], tuple(sprite['rgb_rect']))

                # load Mask (8bpp) data
                mask_key = (None, None)
                if 'mask_file' in sprite and 'mask_rect' in sprite:
                    assert isinstance(sprite['mask_file'], str)
                    assert isinstance(sprite['mask_rect'], list) and len(sprite['mask_rect']) == 4
                    assert all(isinstance(num, int) for num in sprite['mask_rect'])
                    mask_key = (sprite['mask_file'], tuple(sprite['mask_rect']))

                palette_key = None
                if 'mask_pal' in sprite:
                    palette_key = sprite['mask_pal']

                # Compose key
                assert any(i is not None for i in rgb_key + mask_key)
                key = rgb_key + mask_key + ('crop' in sprite, palette_key)
                assert key not in self.cached_sprites

                # Read size/offset from cache
                assert 'offset' in sprite and 'size' in sprite
                offset, size = sprite['offset'], sprite['size']
                assert isinstance(offset, int) and isinstance(size, int)
                assert offset >= 0 and size > 0
                assert offset + size <= cache_size
                data = cache_data[offset:offset+size]

                # Read info / cropping data from cache
                assert 'info' in sprite and isinstance(sprite['info'], int)
                info = sprite['info']
                if 'crop' in sprite:
                    assert isinstance(sprite['crop'], list) and len(sprite['crop']) == 4
                    assert all(isinstance(num, int) for num in sprite['crop'])
                    crop = tuple(sprite['crop'])
                else:
                    crop = None

                if 'pixel_stats' in sprite:
                    assert isinstance(sprite['pixel_stats'], dict)
                    pixel_stats = sprite['pixel_stats']
                else:
                    pixel_stats = {}

                # Compose value
                value = (data, info, crop, pixel_stats, True, False)

                # Check if cache item is still valid
                is_valid = True
                if rgb_key[0] is not None:
                    mtime = source_mtime.get(rgb_key[0])
                    if mtime is None:
                        mtime = os.path.getmtime(generic.find_file(rgb_key[0]))
                        source_mtime[rgb_key[0]] = mtime
                    
                    if mtime > self.cache_time:
                        is_valid = False

                if mask_key[0] is not None:
                    mtime = source_mtime.get(mask_key[0])
                    if mtime is None:
                        mtime = os.path.getmtime(generic.find_file(mask_key[0]))
                        source_mtime[mask_key[0]] = mtime
                    
                    if mtime > self.cache_time:
                        is_valid = False

                # Drop items from older spritecache format without palette entry
                if (mask_key[0] is None) != (palette_key is None):
                    is_valid = False

                if is_valid:
                    self.cached_sprites[key] = value
        except:
            generic.print_warning(index_file.name + " contains invalid data, ignoring. Please remove the file and file a bug report if this warning keeps appearing")
            self.cached_sprites = {} # Clear cache

        index_file.close()