diff --git a/code/grammar_entries.lark b/code/grammar_entries.lark index ff31383a..38bfb9c0 100644 --- a/code/grammar_entries.lark +++ b/code/grammar_entries.lark @@ -3,10 +3,11 @@ title: "#" /(?! ).+(? 1) duplicate_names = set(duplicate_names) # to avoid duplicates in duplicate_names if duplicate_names: print('Warning: duplicate developer names: {}'.format(', '.join(duplicate_names))) + + # check for essential, valid fields + for dev in developers: + # check that essential fields are existing + for field in essential_developer_fields: + if field not in dev: + raise RuntimeError('Essential field "{}" missing in developer {}'.format(field, dev['Name'])) + # check that all fields are valid fields + for field in dev.keys(): + if field not in valid_developer_fields: + raise RuntimeError('Invalid field "{}" in developer {}.'.format(field, dev['Name'])) + # url fields + for field in url_developer_fields: + if field in dev: + content = dev[field] + if any(not (x.startswith('http://') or x.startswith('https://')) for x in content): + raise RuntimeError('Invalid URL in field "{}" in developer {}.'.format(field, dev['Name'])) + return developers @@ -343,7 +347,7 @@ def write_developers(developers): for dev in developers: keys = list(dev.keys()) # developer name - content += '## {} [{}]\n\n'.format(dev['Name'], len(dev['games'])) + content += '## {} [{}]\n\n'.format(dev['Name'], len(dev['Games'])) keys.remove('Name') # all the remaining in alphabetical order, but 'games' first @@ -352,7 +356,6 @@ def write_developers(developers): keys = ['Games'] + keys for field in keys: value = dev[field] - field = field.capitalize() # lists get special treatment if isinstance(value, list): value.sort(key=str.casefold) @@ -377,20 +380,7 @@ def read_inspirations(): grammar_file = os.path.join(code_path, 'grammar_listing.lark') inspirations = osg_parse.read_and_parse(inspirations_file, grammar_file, osg_parse.ListingTransformer) - # now inspirations is a list of dictionaries for every entry with keys (valid_developers_fields) - - # now transform a bit more - for inspiration in inspirations: - # check that keys are valid keys - for field in inspiration.keys(): - if field not in valid_inspiration_fields: - raise RuntimeError('Unknown field "{}" for inspiration: {}.'.format(field, inspiration['Name'])) - # split lists - for field in ('Inspired entries',): - if field in inspiration: - content = inspiration[field] - content = [x.strip() for x in content] - inspiration[field] = content + # now inspirations is a list of dictionaries for every entry with some properties # check for duplicate names entries names = [inspiration['Name'] for inspiration in inspirations] @@ -399,6 +389,23 @@ def read_inspirations(): if duplicate_names: raise RuntimeError('Duplicate inspiration names: {}'.format(', '.join(duplicate_names))) + # check for essential, valid fields + for inspiration in inspirations: + # check that essential fields are existing + for field in essential_inspiration_fields: + if field not in inspiration: + raise RuntimeError('Essential field "{}" missing in inspiration {}'.format(field, inspiration['Name'])) + # check that all fields are valid fields + for field in inspiration.keys(): + if field not in valid_inspiration_fields: + raise RuntimeError('Invalid field "{}" in inspiration {}.'.format(field, inspiration['Name'])) + # url fields + for field in url_inspiration_fields: + if field in inspiration: + content = inspiration[field] + if any(not (x.startswith('http://') or x.startswith('https://')) for x in content): + raise RuntimeError('Invalid URL in field "{}" in inspiration {}.'.format(field, inspiration['Name'])) + # convert to dictionary inspirations = {x['Name']: x for x in inspirations} @@ -417,7 +424,7 @@ def write_inspirations(inspirations): # comment content = '{}\n'.format(generic_comment_string) - # number of developer + # updated number of inspirations content += '# Inspirations [{}]\n\n'.format(len(inspirations)) # sort by name @@ -436,7 +443,6 @@ def write_inspirations(inspirations): keys = ['Inspired entries'] + keys for field in keys: value = inspiration[field] - field = field.capitalize() # lists get special treatment if isinstance(value, list): value.sort(key=str.casefold) # sorted alphabetically @@ -472,15 +478,12 @@ def read_entries(): # parse and transform entry content try: entry = parse(content) - # add file information - entry['File'] = file - - check_entry(entry) - - post_process(entry) + entry = [('File', file),] + entry # add file information to the beginning + entry = check_and_process_entry(entry) except Exception as e: print('{} - {}'.format(file, e)) exception_happened = True + # raise RuntimeError(e) continue # add to list @@ -490,47 +493,44 @@ def read_entries(): return entries -def post_process(entry): - """ - :param entry: - :return: - """ - - # remove all parentheses from developers - if 'Developer' in entry: - devs = entry['Developer'] - devs = [re.sub(r'\([^)]*\)', '', x).strip() for x in devs] - if any(not x for x in devs): - raise RuntimeError('Empty developer') - entry['Developer'] = devs - - - -def check_entry(entry): - """ - - :param entry: - :return: - """ +def check_and_process_entry(entry): message = '' - file = entry['File'] + # check that all fields are valid fields and are existing in that order + index = 0 + for e in entry: + field = e[0] + while index < len(valid_fields) and field != valid_fields[index]: + index += 1 + if index == len(valid_fields): # must be valid fields and must be in the right order + message += 'Field "{}" either misspelled or in wrong order\n'.format(field) - # check canonical file name - canonical_file_name = canonical_entry_name(entry['Title']) + '.md' - # we also allow -X with X =2..9 as possible extension (because of duplicate canonical file names) - if canonical_file_name != file and canonical_file_name != file[:-5] + '.md': - message += 'file name should be {}\n'.format(canonical_file_name) + # order is fine we can convert to dictionary + d = {} + for field, value in entry: + if field in d: + message += 'Field "{}" appears twice\n'.format(field) + d[field] = value + entry = d # check for essential fields for field in essential_fields: if field not in entry: message += 'essential property "{}" missing\n'.format(field) + # check canonical file name + file = entry['File'] + canonical_file_name = canonical_entry_name(entry['Title']) + '.md' + # we also allow -X with X =2..9 as possible extension (because of duplicate canonical file names) + if canonical_file_name != file and canonical_file_name != file[:-5] + '.md': + message += 'file name should be {}\n'.format(canonical_file_name) + if message: raise RuntimeError(message) + return entry + def write_entries(entries): """ diff --git a/code/utils/osg_parse.py b/code/utils/osg_parse.py index 10485207..6a0b2535 100644 --- a/code/utils/osg_parse.py +++ b/code/utils/osg_parse.py @@ -17,7 +17,7 @@ class ListingTransformer(lark.Transformer): return x[0].value def quoted_value(self, x): - return x[0].value[1:-1] # remove quotation marks + return x[0].value[1:-1].strip() # remove quotation marks and strip whitespaces def property(self, x): """ @@ -61,13 +61,23 @@ class EntryTransformer(lark.Transformer): def quoted_value(self, x): return x[0].value[1:-1] # remove quotation marks + def comment_value(self, x): + return x[0].value[1:-1] # remove parenthesis + + def value(self, x): + if len(x) == 1: + v = ValueWithComment(value=x[0]) + else: + v = ValueWithComment(value=x[0], comment=x[1]) + return v + def property(self, x): """ The key of a property will be converted to lower case and the value part is the second part :param x: :return: """ - return x[0], x[1:] + return x[0].value, x[1:] def title(self, x): return 'Title', x[0].value @@ -83,33 +93,33 @@ class EntryTransformer(lark.Transformer): return 'Note', ''.join((x.value for x in x)) def building(self, x): - d = {} - for key, value in x: - if key in d: - raise RuntimeError('Key in entry appears twice') - d[key] = value - return 'Building', d + return 'Building', x def start(self, x): - # we do the essential fields and valid fields checks right here - fields = [x[0] for x in x] - # check for essential fields - for field in c.essential_fields: - if field not in fields: - raise RuntimeError('Essential field "{}" is missing'.format(field)) - # check for valid fields (in that order) - index = 0 - for field in fields: - while index < len(c.valid_fields) and field != c.valid_fields[index]: - index += 1 - if index == len(c.valid_fields): - raise RuntimeError('Field "{}" either not valid or in wrong order'.format(field)) - d = {} - for key, value in x: - if key in d: - raise RuntimeError('Key in entry appears twice') - d[key] = value - return d + return x + + +class ValueWithComment: + """ + All our property values can have (optional) comments. This is the class that represents them to us and implements + equality and 'in' operator functionality purely on the value. + """ + + def __init__(self, value, comment=None): + self.value = value + self.comment = comment + + def __contains__(self, item): + return item in self.value + + def __eq__(self, other): + return self.value == other + + def __repr__(self): + if self.comment: + return '{} ({})'.format(self.value, self.comment) + else: + return '{}'.format(self.value) def parse(parser, transformer, content): diff --git a/developers.md b/developers.md index 6447ee6c..bfb8798c 100644 --- a/developers.md +++ b/developers.md @@ -32,7 +32,7 @@ ## Alex Margarit [1] -- Games: faur +- Games: Faur ## Alexander Lang [1] @@ -590,7 +590,7 @@ - Games: Hack -## Jean-Baptiste Lamy / Nekeme Prod. [1] +## Jean-Baptiste "Jiba" Lamy [1] - Games: Slune diff --git a/entries/ballerburg_sdl.md b/entries/ballerburg_sdl.md index 6ecc82da..0f818812 100644 --- a/entries/ballerburg_sdl.md +++ b/entries/ballerburg_sdl.md @@ -5,7 +5,7 @@ - Inspirations: Ballerburg - State: mature - Keywords: action, artillery, remake -- Code repository: https://git.tuxfamily.org/baller/baller.git, https://gitlab.com/osgames/ballerburg.git (+) (import of original source downloads) +- Code repository: https://git.tuxfamily.org/baller/baller.git, https://gitlab.com/osgames/ballerburg.git (@add, import of original source downloads) - Code language: C - Code license: GPL-3.0 - Code dependencies: SDL2 diff --git a/entries/brain_workshop.md b/entries/brain_workshop.md index e9c913a8..eeb8f65f 100644 --- a/entries/brain_workshop.md +++ b/entries/brain_workshop.md @@ -9,7 +9,7 @@ - Code language: Python - Code license: GPL-2.0 - Code dependencies: pyglet -- Assets license: CC (caprica-letters and all the music in the latest version +- Assets license: CC (caprica-letters and all the music in the latest version) Dual n-back brain training exercise. diff --git a/entries/eadventure.md b/entries/eadventure.md index 40509ac0..69ae1ed5 100644 --- a/entries/eadventure.md +++ b/entries/eadventure.md @@ -4,7 +4,7 @@ - State: mature, inactive since 2014 - Download: https://sourceforge.net/projects/e-adventure/files/ - Keywords: adventure, game engine -- Code repository: https://github.com/e-ucm/eAdventure-legacy.git, https://github.com/e-ucm/eAdventure.git (+), https://github.com/e-ucm/uAdventure.git (+), https://gitlab.com/osgames/e-adventure.git (+) (conversion of svn), https://svn.code.sf.net/p/e-adventure/code (svn) +- Code repository: https://github.com/e-ucm/eAdventure-legacy.git, https://github.com/e-ucm/eAdventure.git (+), https://github.com/e-ucm/uAdventure.git (+), https://gitlab.com/osgames/e-adventure.git (@add, conversion of svn), https://svn.code.sf.net/p/e-adventure/code (svn) - Code language: Java - Code license: GPL-3.0 diff --git a/entries/inexor.md b/entries/inexor.md index dc1ab738..8370d717 100644 --- a/entries/inexor.md +++ b/entries/inexor.md @@ -1,15 +1,12 @@ # Inexor - Home: https://inexor.org/ -- Media: - Inspirations: Cube 2: Sauerbraten - State: beta, inactive since 2018 -- Keywords: remake -- Code repository: https://github.com/inexorgame/vulkan-renderer.git, https://github.com/inexorgame/inexor-core.git (+) (archived) +- Keywords: remake, first person, shooter +- Code repository: https://github.com/inexorgame/vulkan-renderer.git, https://github.com/inexorgame/inexor-core.git (@add, @archived) - Code language: C++, JavaScript - Code license: zlib - Code dependencies: Cube 2 -Remake of Cube 2: Sauerbraten. - ## Building diff --git a/entries/keeperrl.md b/entries/keeperrl.md index 7ba86001..e9b81472 100644 --- a/entries/keeperrl.md +++ b/entries/keeperrl.md @@ -8,7 +8,7 @@ - Code repository: https://github.com/miki151/keeperrl.git - Code language: C, C++ - Code license: GPL-2.0 -- Assets license: Proprietary (partly), CC-BY (partly) (see https://github.com/miki151/keeperrl/blob/master/COPYING-MEDIA.txt) +- Assets license: Proprietary (@partly), CC-BY (@partly, see https://github.com/miki151/keeperrl/blob/master/COPYING-MEDIA.txt) Bungeon builder. diff --git a/entries/lexica.md b/entries/lexica.md index 7aa73c11..ee3f4268 100644 --- a/entries/lexica.md +++ b/entries/lexica.md @@ -8,7 +8,7 @@ - Code repository: https://github.com/lexica/lexica.git - Code language: Java - Code license: GPL-3.0 -- Assets license: Apache-2.0 (word lists), CC-BY-4.0 (icons) (see https://github.com/lexica/lexica/blob/master/LICENSES) +- Assets license: Apache-2.0 (word lists), CC-BY-4.0 (icons, see https://github.com/lexica/lexica/blob/master/LICENSES) Find as many words as possible on a grid of random letters. diff --git a/entries/microracers.md b/entries/microracers.md index 891d19b6..8b3429d6 100644 --- a/entries/microracers.md +++ b/entries/microracers.md @@ -5,7 +5,7 @@ - State: beta, inactive since 2005 - Download: https://sourceforge.net/projects/microracers/files/microracers/ - Keywords: remake, 2D, racing -- Code repository: https://github.com/rpmcruz/microracers.git, https://gitlab.com/osgames/microracers.git (+) (conversion of cvs), http://microracers.cvs.sourceforge.net (cvs) +- Code repository: https://github.com/rpmcruz/microracers.git, https://gitlab.com/osgames/microracers.git (@add, conversion of cvs), http://microracers.cvs.sourceforge.net (cvs) - Code language: C, C++ - Code license: GPL-2.0 - Developer: Ricardo Cruz diff --git a/entries/ohrrpgce.md b/entries/ohrrpgce.md index 2a6ccc9d..305fedf0 100644 --- a/entries/ohrrpgce.md +++ b/entries/ohrrpgce.md @@ -5,7 +5,7 @@ - State: mature - Download: http://rpg.hamsterrepublic.com/ohrrpgce/Downloads - Keywords: framework -- Code repository: https://bitbucket.org/rbv/ohrrpgce-svn.git (mirror), https://github.com/ohrrpgce/ohrrpgce.git (+) (mirror), https://rpg.hamsterrepublic.com/source/wip (+) (svn) +- Code repository: https://bitbucket.org/rbv/ohrrpgce-svn.git (@mirror), https://github.com/ohrrpgce/ohrrpgce.git (@add, @mirror), https://rpg.hamsterrepublic.com/source/wip (@add, svn) - Code language: Basic - Code license: GPL-2.0 diff --git a/entries/oldskool_gravity_game.md b/entries/oldskool_gravity_game.md index 50a57bc5..55e16f5a 100644 --- a/entries/oldskool_gravity_game.md +++ b/entries/oldskool_gravity_game.md @@ -5,7 +5,7 @@ - Download: https://sourceforge.net/projects/osgg/files/ - Platform: Windows, Linux - Keywords: arcade, open content, side-scrolling -- Code repository: https://github.com/DusteDdk/osgg.git, https://gitlab.com/osgames/osgg.git (+) (conversion of svn), https://svn.code.sf.net/p/osgg/code (svn) +- Code repository: https://github.com/DusteDdk/osgg.git, https://gitlab.com/osgames/osgg.git (@add, conversion of svn), https://svn.code.sf.net/p/osgg/code (svn) - Code language: C++ - Code license: GPL-3.0 - Code dependencies: OpenGL, SDL diff --git a/entries/slune.md b/entries/slune.md index 121a1e17..e47d5864 100644 --- a/entries/slune.md +++ b/entries/slune.md @@ -8,7 +8,7 @@ - Code language: Python - Code license: GPL-2.0 - Code dependencies: Cal3D, GLEW, OpenGL, Py2Play, SDL -- Developer: Jean-Baptiste Lamy (Jiba) / Nekeme Prod. +- Developer: Jean-Baptiste "Jiba" Lamy (Nekeme Prod.) Action game. diff --git a/inspirations.md b/inspirations.md index 5a74db5c..17f8d298 100644 --- a/inspirations.md +++ b/inspirations.md @@ -12,7 +12,7 @@ ## Abuse [1] - Inspired entries: Abuse -- Media: +- Media: https://en.wikipedia.org/wiki/Abuse_(video_game) ## Ace Combat: Assault Horizon [1] @@ -22,7 +22,7 @@ ## Ace of Spades [3] - Inspired entries: BetterSpades, Iceball, OpenSpades -- Media: +- Media: https://en.wikipedia.org/wiki/Ace_of_Spades_(video_game) ## Achtung, die Kurve! [4] @@ -37,7 +37,7 @@ ## Age of Empires [2] - Inspired entries: 0 A.D., openage -- Media: +- Media: https://en.wikipedia.org/wiki/Age_of_Empires_(video_game) ## Age of Empires II [2] @@ -56,7 +56,7 @@ ## Allegiance [1] - Inspired entries: Free Allegiance -- Media: +- Media: https://en.wikipedia.org/wiki/Allegiance_(video_game) ## Anno (series) [1] @@ -297,7 +297,7 @@ ## Cadaver [1] - Inspired entries: Cadaver -- Media: +- Media: https://en.wikipedia.org/wiki/Cadaver_(video_game) ## Caesar 3 [2] @@ -462,6 +462,7 @@ ## Cube 2: Sauerbraten [2] - Inspired entries: Inexor, Open Cube +- Media: https://en.wikipedia.org/wiki/Cube_2:_Sauerbraten ## CUBE engine [1] @@ -614,7 +615,7 @@ ## E.T. the Extra-Terrestrial [1] - Inspired entries: javascript-E.T. -- Media: +- Media: https://en.wikipedia.org/wiki/E.T._the_Extra-Terrestrial_(video_game) ## Eat The Whistle [1] @@ -737,7 +738,7 @@ ## Freelancer [1] - Inspired entries: Librelancer -- Media: +- Media: https://en.wikipedia.org/wiki/Freelancer_(video_game) ## Freeserf [1] @@ -778,7 +779,7 @@ ## GoldenEye 007 [2] - Inspired entries: ges-code, GoldenEye: Source -- Media: +- Media: https://en.wikipedia.org/wiki/GoldenEye_007_(1997_video_game) ## Gorillas [2] @@ -944,7 +945,7 @@ ## Krush Kill 'n' Destroy [1] - Inspired entries: KKnD -- Media: +- Media: https://en.wikipedia.org/wiki/KKnD_(video_game) ## Kula World [1] @@ -981,7 +982,7 @@ ## Lemmings [5] - Inspired entries: Lemmings.ts, Lemmini, Lix, Pingus, Rabbit Escape -- Media: +- Media: https://en.wikipedia.org/wiki/Lemmings_(video_game) ## Liero [4] @@ -1056,7 +1057,7 @@ ## Marathon [1] - Inspired entries: Aleph One -- Media: +- Media: https://marathongame.fandom.com/wiki/Marathon_(Game) ## Marathon 2 [1] @@ -1507,7 +1508,7 @@ ## Seven Kingdoms: Ancient Adversaries [1] - Inspired entries: Seven Kingdoms: Ancient Adversaries -- Media: +- Media: https://en.wikipedia.org/wiki/Seven_Kingdoms_(video_game) ## sfxr [1] @@ -1528,17 +1529,17 @@ ## Ship Simulator 2006 [1] - Inspired entries: Bridge Command -- Media: +- Media: https://en.wikipedia.org/wiki/Ship_Simulator_(video_game) ## Ship Simulator 2008 [1] - Inspired entries: Bridge Command -- Media: +- Media: https://en.wikipedia.org/wiki/Ship_Simulator_(video_game) ## Ship Simulator Extremes [1] - Inspired entries: Bridge Command -- Media: +- Media: https://en.wikipedia.org/wiki/Ship_Simulator_(video_game) ## Shobon Action [1] @@ -1571,7 +1572,7 @@ ## SimCity [9] - Inspired entries: 3d.city, Citybound, Cytopia, Divercity, Lincity, LinCity-NG, Micropolis, micropolisJS, OpenCity -- Media: +- Media: https://en.wikipedia.org/wiki/SimCity_(1989_video_game) ## SimCity 2000 [1] @@ -1783,7 +1784,7 @@ ## Super Monkey Ball [4] - Inspired entries: irrlamb, Neverball, Nuncabola, Veraball -- Media: +- Media: https://en.wikipedia.org/wiki/Super_Monkey_Ball_(video_game) ## Super Smash Bros. [3] @@ -2010,7 +2011,7 @@ ## Turmoil [1] - Inspired entries: Data Storm -- Media: +- Media: https://en.wikipedia.org/wiki/Turmoil_(1984_video_game) ## Turok [1] @@ -2146,7 +2147,7 @@ ## Warlords II [2] - Inspired entries: FreeLords, LordsAWar! -- Media: +- Media: https://en.wikipedia.org/wiki/Warlords_(game_series) ## Warrior Kings [1]