From 9edec64de50b3ac89a59283b204164de90cfcd84 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Sat, 9 Apr 2011 03:26:24 +0300 Subject: [PATCH 1/3] Add 'underground' flag to Iron Ball and Light Clay --- pokedex/data/csv/item_flag_map.csv | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pokedex/data/csv/item_flag_map.csv b/pokedex/data/csv/item_flag_map.csv index 7ebe46c..842ccfc 100644 --- a/pokedex/data/csv/item_flag_map.csv +++ b/pokedex/data/csv/item_flag_map.csv @@ -484,6 +484,7 @@ item_id,item_flag_id 245,7 246,5 246,7 +246,8 247,5 247,7 248,5 @@ -501,6 +502,7 @@ item_id,item_flag_id 254,7 255,5 255,7 +255,8 256,5 256,7 257,5 From 11c0c4778b0b273cb5f0c4934d1d155050f9fb2e Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Sat, 9 Apr 2011 16:39:27 +0300 Subject: [PATCH 2/3] Add media accessors --- pokedex/db/media.py | 514 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 514 insertions(+) create mode 100644 pokedex/db/media.py diff --git a/pokedex/db/media.py b/pokedex/db/media.py new file mode 100644 index 0000000..9faf19f --- /dev/null +++ b/pokedex/db/media.py @@ -0,0 +1,514 @@ + +"""Media accessors + +Most media accessor __init__s take an ORM object from the pokedex package. +Their various methods take a number of arguments specifying exactly which +file you want (such as the female sprite, backsprite, etc.). +ValueError is raised when the specified file cannot be found. + +The accessors use fallbacks: for example Bulbasaur's males and females look the +same, so if you request Bulbasaur's female sprite, it will give you the common +image. Or for a Pokemon without individual form sprites, you will get the +common base sprite. Or for versions witout shiny Pokemon, you will always +get the non-shiny version (that's how shiny Pokemon looked there!). +However arguments such as `animated` don't use fallbacks. +You can set `strict` to True to disable these fallbacks and cause ValueError +to be raised when the exact specific file you asked for is not found. This is +useful for listing non-duplicate sprites, for example. + +Use keyword arguments when calling the media-getting methods, unless noted +otherwise. + +The returned "file" objects have useful attributes like relative_path, +path, and open(). + +All images are in the PNG format, except animations (GIF). All sounds are OGGs. +""" + +import os +import pkg_resources + +class MediaFile(object): + """Represents a file: picture, sound, etc. + + Attributes: + relative_path: Filesystem path relative to the media directory + path: Absolute path to the file + + exists: True if the file exists + + open(): Open the file + """ + def __init__(self, *path_elements): + self.path_elements = path_elements + self._dexpath = '/'.join(('data', 'media') + path_elements) + + @property + def relative_path(self): + return os.path.join(*self.path_elements) + + @property + def path(self): + return pkg_resources.resource_filename('pokedex', self._dexpath) + + def open(self): + """Open this file for reading, in the appropriate mode (i.e. binary) + """ + return open(self.path, 'rb') + + @property + def exists(self): + return pkg_resources.resource_exists('pokedex', self._dexpath) + + def __eq__(self, other): + return self.path == other.path + + def __ne__(self, other): + return self.path != other.path + + def __str__(self): + return '' % self.relative_path + +class BaseMedia(object): + def from_path_elements(self, path_elements, basename, extension, + surely_exists=False): + filename = basename + extension + path_elements = [self.toplevel_dir] + path_elements + [filename] + mfile = MediaFile(*path_elements) + if surely_exists or mfile.exists: + return mfile + else: + raise ValueError('File %s not found' % mfile.relative_path) + +class _BasePokemonMedia(BaseMedia): + toplevel_dir = 'pokemon' + has_gender_differences = False + form = None + introduced_in = 0 + + # Info about of what's inside the pokemon main sprite directories, so we + # don't have to check directory existence all the time. + _pokemon_sprite_info = { + 'red-blue': (1, set('back gray'.split())), + 'red-green': (1, set('back gray'.split())), + 'yellow': (1, set('back gray gbc'.split())), + 'gold': (2, set('back shiny'.split())), + 'silver': (2, set('back shiny'.split())), + 'crystal': (2, set('animated back shiny'.split())), + 'ruby-sapphire': (3, set('back shiny'.split())), + 'emerald': (3, set('animated back shiny frame2'.split())), + 'firered-leafgreen': (3, set('back shiny'.split())), + 'diamond-pearl': (4, set('back shiny female frame2'.split())), + 'platinum': (4, set('back shiny female frame2'.split())), + 'heartgold-soulsilver': (4, set('back shiny female frame2'.split())), + 'black-white': (5, set('back shiny female'.split())), + } + + def __init__(self, pokemon_id, form_postfix=None): + BaseMedia.__init__(self) + self.pokemon_id = str(pokemon_id) + self.form_postfix = form_postfix + + def _get_file(self, path_elements, extension, strict, surely_exists=False): + basename = str(self.pokemon_id) + if self.form_postfix: + fullname = basename + self.form_postfix + try: + return self.from_path_elements( + path_elements, fullname, extension, + surely_exists=surely_exists) + except ValueError: + if strict: + raise + return self.from_path_elements(path_elements, basename, extension, + surely_exists=surely_exists) + + def sprite(self, + version='black-white', + + # The media directories are in this order: + animated=False, + back=False, + color=None, + shiny=False, + female=False, + frame=None, + + strict=False, + ): + """Get a main sprite sprite for a pokemon. + + Everything except version should be given as a keyword argument. + + Either specify version as an ORM object, or give the version path as + a string (which is the only way to get 'red-green'). Leave the default + for the latest version. + + animated: get a GIF animation (currently Crystal & Emerald only) + back: get a backsprite instead of a front one + color: can be 'color' (RGBY only) or 'gbc' (Yellow only) + shiny: get a shiny sprite. In old versions, gives a normal sprite unless + `strict` is set + female: get a female sprite instead of male. For pokemon with no sexual + dimorphism, gets the common sprite unless `strict` is set. + frame: set to 2 to get the second frame of the animation + (Emerald, DPP, and HG/SS only) + + If the sprite is not found, raise a ValueError. + """ + if isinstance(version, basestring): + version_dir = version + try: + generation, info = self._pokemon_sprite_info[version_dir] + except KeyError: + raise ValueError('Version directory %s not found', version_dir) + else: + version_dir = version.identifier + try: + generation, info = self._pokemon_sprite_info[version_dir] + except KeyError: + version_group = version.version_group + version_dir = '-'.join( + v.identifier for v in version_group.versions) + generation, info = self._pokemon_sprite_info[version_dir] + if generation < self.introduced_in: + raise ValueError("Pokemon %s didn't exist in %s" % ( + self.pokemon_id, version_dir)) + path_elements = ['main-sprites', version_dir] + if animated: + if 'animated' not in info: + raise ValueError("No animated sprites for %s" % version_dir) + path_elements.append('animated') + extension = '.gif' + else: + extension = '.png' + if back: + if version_dir == 'emerald': + # Emerald backsprites are the same as ruby/sapphire + if strict: + raise ValueError("Emerald uses R/S backsprites") + if animated: + raise ValueError("No animated backsprites for Emerald") + path_elements[1] = version_dir = 'ruby-sapphire' + if version_dir == 'crystal' and animated: + raise ValueError("No animated backsprites for Crystal") + path_elements.append('back') + if color == 'gray': + if 'gray' not in info: + raise ValueError("No grayscale sprites for %s" % version_dir) + path_elements.append('gray') + elif color == 'gbc': + if 'gbc' not in info: + raise ValueError("No GBC sprites for %s" % version_dir) + path_elements.append('gbc') + elif color: + raise ValueError("Unknown color scheme: %s" % color) + if shiny: + if 'shiny' in info: + path_elements.append('shiny') + elif strict: + raise ValueError("No shiny sprites for %s" % version_dir) + if female: + female_sprite = self.has_gender_differences + # Chimecho's female back frame 2 sprite has one hand in + # a slightly different pose, in Platinum and HGSS + # (we have duplicate sprites frame 1, for convenience) + if self.pokemon_id == '358' and back and version_dir in ( + 'platinum', 'heartgold-soulsilver'): + female_sprite = True + female_sprite = female_sprite and 'female' in info + if female_sprite: + path_elements.append('female') + elif strict: + raise ValueError( + 'Pokemon %s has no gender differences' % self.pokemon_id) + if not frame or frame == 1: + pass + elif frame == 2: + if 'frame2' in info: + path_elements.append('frame%s' % frame) + else: + raise ValueError("No frame 2 for %s" % version_dir) + else: + raise ValueError("Bad frame %s" % frame) + return self._get_file(path_elements, extension, strict=strict, + # Avoid a stat in the common case + surely_exists=(self.form and version_dir == 'black-white' + and not back and not female + and not self.form_postfix)) + + def _maybe_female(self, path_elements, female, strict): + if female: + if self.has_gender_differences: + elements = path_elements + ['female'] + try: + return self._get_file(elements, '.png', strict=strict) + except ValueError: + if strict: + raise + elif strict: + raise ValueError( + 'Pokemon %s has no gender differences' % self.pokemon_id) + return self._get_file(path_elements, '.png', strict=strict) + + def icon(self, female=False, strict=False): + """Get the Pokemon's menu icon""" + return self._maybe_female(['icons'], female, strict) + + def sugimori(self, female=False, strict=False): + """Get the Pokemon's official art, drawn by Ken Sugimori""" + return self._maybe_female(['sugimori'], female, strict) + + def overworld(self, + direction='down', + shiny=False, + female=False, + frame=1, + strict=False, + ): + """Get an overworld sprite + + direction: 'up', 'down', 'left', or 'right' + shiny: true for a shiny sprite + female: true for female sprite (or the common one for both M & F) + frame: 2 for the second animation frame + + strict: disable fallback for `female` + """ + path_elements = ['overworld'] + if shiny: + path_elements.append('shiny') + if female: + if self.has_gender_differences: + path_elements.append('female') + elif strict: + raise ValueError('No female overworld sprite') + else: + female = False + path_elements.append(direction) + if frame and frame > 1: + path_elements.append('frame%s' % frame) + try: + return self._get_file(path_elements, '.png', strict=strict) + except ValueError: + if female and not strict: + path_elements.remove('female') + return self._get_file(path_elements, '.png', strict=strict) + else: + raise + + def footprint(self, strict=False): + """Get the Pokemon's footprint""" + return self._get_file(['footprints'], '.png', strict=strict) + + def trozei(self, strict=False): + """Get the Pokemon's animated Trozei sprite""" + return self._get_file(['trozei'], '.gif', strict=strict) + + def cry(self, strict=False): + """Get the Pokemon's cry""" + return self._get_file(['cries'], '.ogg', strict=strict) + + def cropped_sprite(self, strict=False): + """Get the Pokemon's cropped sprite""" + return self._get_file(['cropped'], '.png', strict=strict) + +class PokemonFormMedia(_BasePokemonMedia): + """Media related to a Pokemon form + """ + def __init__(self, pokemon_form): + pokemon_id = pokemon_form.form_base_pokemon_id + if pokemon_form.identifier: + form_postfix = '-' + pokemon_form.identifier + else: + form_postfix = None + _BasePokemonMedia.__init__(self, pokemon_id, form_postfix) + self.form = pokemon_form + pokemon = pokemon_form.form_base_pokemon + self.has_gender_differences = pokemon.has_gender_differences + self.introduced_in = pokemon.generation_id + +class PokemonMedia(_BasePokemonMedia): + """Media related to a Pokemon + """ + def __init__(self, pokemon): + _BasePokemonMedia.__init__(self, pokemon.id) + self.form = pokemon.default_form + self.has_gender_differences = (pokemon.has_gender_differences) + self.introduced_in = pokemon.generation_id + +class UnknownPokemonMedia(_BasePokemonMedia): + """Media related to the unknown Pokemon ("?") + + Note that not a lot of files are available for it. + """ + def __init__(self): + _BasePokemonMedia.__init__(self, '0') + +class EggMedia(_BasePokemonMedia): + """Media related to a pokemon egg + + Note that not a lot of files are available for these. + + Give a Manaphy as `pokemon` to get the Manaphy egg. + """ + def __init__(self, pokemon=None): + if pokemon and pokemon.identifier == 'manaphy': + postfix = '-manaphy' + else: + postfix = None + _BasePokemonMedia.__init__(self, 'egg', postfix) + +class SubstituteMedia(_BasePokemonMedia): + """Media related to the Substitute sprite + + Note that not a lot of files are available for Substitute. + """ + def __init__(self): + _BasePokemonMedia.__init__(self, 'substitute') + +class _BaseItemMedia(BaseMedia): + toplevel_dir = 'items' + def underground(self, rotation=0): + """Get the item's sprite as it appears in the Sinnoh underground + + Rotation can be 0, 90, 180, or 270. + """ + if rotation: + basename = self.identifier + '-%s' % rotation + else: + basename = self.identifier + return self.from_path_elements(['underground'], basename, '.png') + +class ItemMedia(_BaseItemMedia): + """Media related to an item + """ + def __init__(self, item): + self.item = item + self.identifier = item.identifier + + def sprite(self, version=None): + """Get the item's sprite + + If version is not given, use the latest version. + """ + identifier = self.identifier + # Handle machines + # We check the identifier, so that we don't query the machine + # information for any item. + if identifier.startswith(('tm', 'hm')): + try: + int(identifier[2:]) + except ValueError: + # Not really a TM/HM + pass + else: + machines = self.item.machines + if version: + try: + machine = [ + m for m in machines + if m.version_group == version.version_group + ][0] + except IndexError: + raise ValueError("%s doesn't exist in %s" % ( + identifier, version.identifier)) + else: + # They're ordered, so get the last one + machine = machines[-1] + type_identifier = machine.move.type.identifier + identifier = identifier[:2] + '-' + type_identifier + elif identifier.startswith('data-card-'): + try: + int(identifier[10:]) + except ValueError: + # Not a real data card??? + pass + else: + identifier = 'data-card' + if version is not None: + generation_id = version.generation.id + if generation_id <= 3 and identifier == 'dowsing-mchn': + identifier = 'itemfinder' + try: + gen = 'gen%s' % generation_id + return self.from_path_elements([gen], identifier, '.png') + except ValueError: + pass + return self.from_path_elements([], identifier, '.png', + surely_exists=True) + + def underground(self, rotation=0): + """Get the item's sprite as it appears in the Sinnoh underground + + Rotation can be 0, 90, 180, or 270. + """ + if not self.item.appears_underground: + raise ValueError("%s doesn't appear underground" % self.identifier) + return super(ItemMedia, self).underground(rotation=rotation) + + def berry_image(self): + """Get a berry's big sprite + """ + if not self.item.berry: + raise ValueError("%s is not a berry" % self.identifier) + return self.from_path_elements(['berries'], self.identifier, '.png') + +class UndergroundRockMedia(_BaseItemMedia): + """Media related to a rock in the Sinnoh underground + + rock_type can be one of: i, ii, o, o-big, s, t, z + """ + def __init__(self, rock_type): + self.identifier = 'rock-%s' % rock_type + +class UndergroundSphereMedia(_BaseItemMedia): + """Media related to a sphere in the Sinnoh underground + + color can be one of: red, blue, green, pale, prism + """ + def __init__(self, color, big=False): + self.identifier = '%s-sphere' % color + if big: + self.identifier += '-big' + +class _SimpleIconMedia(BaseMedia): + def __init__(self, thing): + self.identifier = thing.identifier + + def icon(self): + return self.from_path_elements([], self.identifier, '.png') + +class DamageClassMedia(_SimpleIconMedia): + toplevel_dir = 'damage-classes' + +class HabitatMedia(_SimpleIconMedia): + toplevel_dir = 'habitats' + +class ShapeMedia(_SimpleIconMedia): + toplevel_dir = 'shapes' + +class ItemPocketMedia(_SimpleIconMedia): + toplevel_dir = 'item-pockets' + def icon(self, selected=False): + if selected: + return self.from_path_elements( + ['selected'], self.identifier, '.png') + else: + return self.from_path_elements([], self.identifier, '.png') + +class _LanguageIconMedia(_SimpleIconMedia): + def icon(self, lang='en'): + return self.from_path_elements([lang], self.identifier, '.png') + +class ContestTypeMedia(_LanguageIconMedia): + toplevel_dir = 'contest-types' + +class TypeMedia(_LanguageIconMedia): + toplevel_dir = 'types' + +''' XXX: No accessors for: +chrome +fonts +ribbons +''' From 7292ede94a12edf03a9fcecf8403797cbdede86a Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Sat, 9 Apr 2011 18:20:58 +0300 Subject: [PATCH 3/3] Test media accessors, and the media organization itself A few tests of the accessors, along with a very dumb, long-running script to ensure everything is in its proper place, and there's nothing but the proper things. For now it still finds some beta form cruft for Burmy, Pichu and Cherrim. --- pokedex/tests/test_media.py | 255 ++++++++++++++++++++++++++++++++++++ 1 file changed, 255 insertions(+) create mode 100644 pokedex/tests/test_media.py diff --git a/pokedex/tests/test_media.py b/pokedex/tests/test_media.py new file mode 100644 index 0000000..e84aad4 --- /dev/null +++ b/pokedex/tests/test_media.py @@ -0,0 +1,255 @@ + +"""Test the media accessors. + +If run directly from the command line, also tests the accessors and the names +of all the media by getting just about everything in a naive brute-force way. +This, of course, takes a lot of time to run. +""" + +import os +import re + +from nose.tools import * +from nose.plugins.skip import SkipTest +import nose +import pkg_resources + +from pokedex.db import tables, connect, media + +session = connect() +basedir = pkg_resources.resource_filename('pokedex', 'data/media') + +path_re = re.compile('^[-a-z0-9./]*$') + +def test_totodile(): + """Totodile's female sprite -- same as male""" + totodile = session.query(tables.Pokemon).filter_by(identifier=u'totodile').one() + accessor = media.PokemonMedia(totodile) + assert accessor.sprite() == accessor.sprite(female=True) + +def test_chimecho(): + """Chimecho's Platinum female backsprite -- diffeent from male""" + chimecho = session.query(tables.Pokemon).filter_by(identifier=u'chimecho').one() + accessor = media.PokemonMedia(chimecho) + male = accessor.sprite('platinum', back=True, frame=2) + female = accessor.sprite('platinum', back=True, female=True, frame=2) + assert male != female + +def test_venonat(): + """Venonat's shiny Yellow sprite -- same as non-shiny""" + venonat = session.query(tables.Pokemon).filter_by(identifier=u'venonat').one() + accessor = media.PokemonMedia(venonat) + assert accessor.sprite('yellow') == accessor.sprite('yellow', shiny=True) + +def test_arceus_icon(): + """Arceus fire-form icon -- same as base icon""" + arceus = session.query(tables.Pokemon).filter_by(identifier=u'arceus').one() + accessor = media.PokemonMedia(arceus) + fire_arceus = [f for f in arceus.forms if f.identifier == 'fire'][0] + fire_accessor = media.PokemonFormMedia(fire_arceus) + assert accessor.icon() == fire_accessor.icon() + +@raises(ValueError) +def test_strict_castform(): + """Castform rainy form overworld with strict -- unavailable""" + castform = session.query(tables.Pokemon).filter_by(identifier=u'castform').first() + rainy_castform = [f for f in castform.forms if f.identifier == 'rainy'][0] + rainy_castform = media.PokemonFormMedia(rainy_castform) + rainy_castform.overworld('up', strict=True) + +@raises(ValueError) +def test_strict_exeggcute(): + """Exeggcutes's female backsprite, with strict -- unavailable""" + exeggcute = session.query(tables.Pokemon).filter_by(identifier=u'exeggcute').one() + accessor = media.PokemonMedia(exeggcute) + accessor.sprite(female=True, strict=True) + + + +def get_all_filenames(): + print 'Reading all filenames...' + + all_filenames = set() + + for dirpath, dirnames, filenames in os.walk(basedir): + for filename in filenames: + path = os.path.join(dirpath, filename) + assert path_re.match(path), path + all_filenames.add(path) + + return all_filenames + +def hit(filenames, method, *args, **kwargs): + """ + Run the given accessor method with args & kwargs; if found remove the + result path from filenames and return True, else return False. + """ + try: + medium = method(*args, **kwargs) + #print 'Hit', medium.relative_path + assert medium.exists + except ValueError, e: + #print 'DNF', e + return False + except: + print 'Error while processing', method, args, kwargs + raise + try: + filenames.remove(medium.path) + except KeyError: + pass + return True + +def check_get_everything(): + """ + For every the accessor method, loop over the Cartesian products of all + possible values for its arguments. + Make sure we get every file in the repo, and that we get a file whenever + we should. + + Well, there are exceptions of course. + """ + + versions = list(session.query(tables.Version).all()) + versions.append('red-green') + + black = session.query(tables.Version).filter_by(identifier=u'black').one() + + filenames = get_all_filenames() + + # Some small stuff first + + for damage_class in session.query(tables.MoveDamageClass).all(): + assert hit(filenames, media.DamageClassMedia(damage_class).icon) + + for habitat in session.query(tables.PokemonHabitat).all(): + assert hit(filenames, media.HabitatMedia(habitat).icon) + + for shape in session.query(tables.PokemonShape).all(): + assert hit(filenames, media.ShapeMedia(shape).icon) + + for item_pocket in session.query(tables.ItemPocket).all(): + assert hit(filenames, media.ItemPocketMedia(item_pocket).icon) + assert hit(filenames, media.ItemPocketMedia(item_pocket).icon, selected=True) + + for contest_type in session.query(tables.ContestType).all(): + assert hit(filenames, media.ContestTypeMedia(contest_type).icon) + + for elemental_type in session.query(tables.Type).all(): + assert hit(filenames, media.TypeMedia(elemental_type).icon) + + # Items + versions_for_items = [ + None, + session.query(tables.Version).filter_by(identifier='emerald').one(), + ] + + for item in session.query(tables.Item).all(): + accessor = media.ItemMedia(item) + assert hit(filenames, accessor.berry_image) or not item.berry + for rotation in (0, 90, 180, 270): + assert hit(filenames, accessor.underground, rotation=rotation) or ( + not item.appears_underground or rotation) + for version in versions_for_items: + success = hit(filenames, accessor.sprite, version=version) + if version is None: + assert success + + for color in 'red green blue pale prism'.split(): + for big in (True, False): + accessor = media.UndergroundSphereMedia(color=color, big=big) + assert hit(filenames, accessor.underground) + + for rock_type in 'i ii o o-big s t z'.split(): + accessor = media.UndergroundRockMedia(rock_type) + for rotation in (0, 90, 180, 270): + success = hit(filenames, accessor.underground, rotation=rotation) + assert success or rotation + + # Pokemon! + accessors = [] + + accessors.append(media.UnknownPokemonMedia()) + accessors.append(media.EggMedia()) + manaphy = session.query(tables.Pokemon).filter_by(identifier=u'manaphy').one() + accessors.append(media.EggMedia(manaphy)) + accessors.append(media.SubstituteMedia()) + + print 'Loading pokemon' + + for form in session.query(tables.PokemonForm).filter(tables.PokemonForm.identifier != '').all(): + accessors.append(media.PokemonFormMedia(form)) + + for pokemon in session.query(tables.Pokemon).all(): + accessors.append(media.PokemonMedia(pokemon)) + + for accessor in accessors: + assert hit(filenames, accessor.footprint) or not accessor.form + assert hit(filenames, accessor.trozei) or not accessor.form or ( + accessor.form.pokemon.generation.id > 3) + assert hit(filenames, accessor.cry) or not accessor.form + assert hit(filenames, accessor.cropped_sprite) or not accessor.form + for female in (True, False): + assert hit(filenames, accessor.icon, female=female) or not accessor.form + assert hit(filenames, accessor.sugimori, female=female) or ( + not accessor.form or accessor.form.pokemon.id >= 647) + for shiny in (True, False): + for frame in (1, 2): + for direction in 'up down left right'.split(): + assert hit(filenames, accessor.overworld, + direction=direction, + shiny=shiny, + female=female, + frame=frame, + ) or not accessor.form or ( + accessor.form.pokemon.generation.id > 4) + for version in versions: + for animated in (True, False): + for back in (True, False): + for color in (None, 'gray', 'gbc'): + success = hit(filenames, + accessor.sprite, + version, + animated=animated, + back=back, + color=color, + shiny=shiny, + female=female, + frame=frame, + ) + if (version == black and not animated + and not back and not color and not + shiny and not female and + frame == 1): + # All pokemon are in Black + assert success or not accessor.form + if (str(accessor.pokemon_id) == '1' + and not animated and not color and + frame == 1): + # Bulbasaur is in all versions + assert success + + # Remove exceptions + exceptions = [os.path.join(basedir, dirname) for dirname in + 'chrome fonts ribbons'.split()] + exceptions.append(os.path.join(basedir, 'items', 'hm-')) + exceptions = tuple(exceptions) + + for filename in tuple(filenames): + if filename.startswith(exceptions): + filenames.remove(filename) + + if len(filenames): + print + print '-----------------' + print 'Unaccessed stuff:' + for filename in sorted(filenames): + print filename + print len(filenames), 'unaccessed files :(' + + return (not filenames) + +if __name__ == '__main__': + result = nose.run(defaultTest=__file__) + result = result and check_get_everything() + exit(not result)