def gameFromRow(self, row): game = Game(row['name'], int(row['wos_id'])) game.setPublisher(row['publisher']) if 'author' in row.keys(): game.setAuthor(row['author']) game.setYear(row['year']) game.setGenre(row['genre']) game.setNumberOfPlayers(row['number_of_players']) game.setMachineType(row['machine_type']) game.setLanguage(row['language']) game.setAvailability(row['availability']) game.x_rated = row['x_rated'] game.addRelease(self.releaseFromRow(row, game)) if 'multiplayer_type' in row.keys(): game.setMultiplayerType(row['multiplayer_type']) if 'tipshop_page' in row.keys(): game.tipshop_page = row['tipshop_page'] game.tipshop_multiface_pokes_section = row[ 'tipshop_multiface_pokes_section'] if game.tipshop_page or row['pok_file_contents']: game.importPokFile(text=str(row['pok_file_contents'])) if row['md5']: file = self.fileFromRow(row) game.addFile(file) if file.part > game.parts: game.parts = file.part return game
def getGamesFromRawData(self, raw_data): games = [] game = Game() release = GameRelease() for row in raw_data: if game.zxdb_id != row['wos_id']: if game.zxdb_id: games.append(game) game = self.gameFromRow(row) game.releases = [] release = self.releaseFromRow(row, game) game.addRelease(release) if release.release_seq != row['release_seq']: release = self.releaseFromRow(row, game) game.addRelease(release) file = self.fileFromRow(row) if file: game.addFile(file, release_seq=row['release_seq']) if file.part > game.parts: game.parts = file.part if game.zxdb_id: games.append(game) return games
def getGames(self, sql_where='', sql_limit=9999999): sql = 'SELECT entries.id AS zxdb_id, ' \ 'releases.release_seq AS release_seq, ' \ 'entries.title AS name, ' \ 'entries.library_title AS tosec_compliant_name, ' \ 'entries.is_xrated AS x_rated, ' \ 'webrefs.link AS tipshop_page, ' \ 'genretypes.text AS genre, ' \ 'entries.max_players AS number_of_players, ' \ 'entries.idiom_id AS language, ' \ 'entries.availabletype_id AS availability, ' \ 'downloads.file_link AS file_link, ' \ 'downloads.file_size AS file_size, ' \ 'downloads.filetype_id AS file_type_id, ' \ 'downloads.idiom_id AS file_language, ' \ 'downloads.comments AS file_version, ' \ 'filetypes.text AS file_type, ' \ 'entry_machinetype.text AS machine_type, ' \ 'download_machinetype.text AS file_machine_type, ' \ 'schemetypes.text AS protection_scheme, ' \ 'releases.release_seq AS release_id, ' \ 'aliases.library_title AS alt_name, ' \ 'aliases.idiom_id AS alt_language, ' \ 'publisher_labels.name AS publisher, ' \ 'publisher_labels.labeltype_id AS publisher_type, ' \ 'author_labels.name AS author, ' \ 'author_labels.labeltype_id AS author_type, ' \ 'author_teams_labels.name AS author_team, ' \ 'author_teams_labels.labeltype_id AS author_team_type, ' \ 'releases.release_year AS year,' \ 'publisher_labels.country_id AS country ' \ 'FROM entries ' \ 'LEFT JOIN webrefs ON entries.id=webrefs.entry_id AND webrefs.website_id=9 ' \ 'LEFT JOIN releases ON entries.id=releases.entry_id ' \ 'LEFT JOIN downloads ON downloads.entry_id=entries.id AND downloads.release_seq=releases.release_seq ' \ 'LEFT JOIN publishers ON publishers.entry_id=entries.id AND publishers.release_seq=releases.release_seq ' \ 'LEFT JOIN labels AS publisher_labels ON publisher_labels.id=publishers.label_id ' \ 'LEFT JOIN authors ON authors.entry_id=entries.id ' \ 'LEFT JOIN labels AS author_labels ON author_labels.id=authors.label_id ' \ 'LEFT JOIN labels AS author_teams_labels ON author_teams_labels.id=authors.team_id ' \ 'LEFT JOIN aliases ON aliases.entry_id=entries.id AND aliases.release_seq=releases.release_seq ' \ 'LEFT JOIN filetypes ON downloads.filetype_id=filetypes.id ' \ 'LEFT JOIN genretypes ON genretypes.id=entries.genretype_id ' \ 'LEFT JOIN machinetypes download_machinetype ON download_machinetype.id=downloads.machinetype_id ' \ 'LEFT JOIN machinetypes entry_machinetype ON entry_machinetype.id=entries.machinetype_id ' \ 'LEFT JOIN schemetypes ON schemetypes.id=downloads.schemetype_id ' \ 'WHERE (entries.id>4000000 OR entries.id<2000000) AND ' \ '(downloads.filetype_id IS NULL OR downloads.filetype_id!=-1) and '\ '(authors.author_seq<=3 OR authors.author_seq IS NULL) ' if sql_where: sql += sql_where + ' ' sql +='ORDER BY zxdb_id, release_seq, entries.title IS NOT NULL ' \ 'LIMIT '+str(sql_limit) self.cur.execute(sql) game = Game() release = GameRelease() games = [] for row in self.cur: #Skipping ZX80/ZX81 files # print(row) if row['machine_type'] and row['machine_type'].startswith('ZX8'): continue if row['genre'] == 'Box Set': if row['zxdb_id'] in [8030]: row['genre'] = 'Compilation - Applications' elif row['zxdb_id'] in [11472]: row['genre'] = 'Compilation - Educational' else: row['genre'] = 'Compilation - Games' if row['publisher'] == 'Creative.Radical.Alternative.Production Games': row['publisher'] = 'Creative Radical Alternative Production Games' if row['author_team']: row['author'] = row['author_team'] row['author_type'] = row['author_team_type'] if row['zxdb_id'] and row[ 'name'] and row['zxdb_id'] != game.zxdb_id: game = self.gameFromRow(row) release = self.releaseFromRow(row, game) game.addRelease(release) games.append(game) if row['release_seq'] and row['release_seq'] != release.release_seq: release = self.releaseFromRow(row, game) game.addRelease(release) if release.release_seq == 0 and game.publisher and row[ 'publisher'] and game.raw_publisher and row[ 'publisher'] not in game.raw_publisher: game.raw_publisher.append(row['publisher']) publisher = self.publisherNameFromRow(row) publishers = game.publisher.split(' - ') publishers.append(publisher) game.publisher = ' - '.join(sorted(publishers, key=str.lower)) if release.publisher and row['publisher'] and row[ 'publisher'] not in release.raw_publisher: release.raw_publisher.append(row['publisher']) publisher = self.publisherNameFromRow(row) release_publishers = release.publisher.split(' - ') release_publishers.append(publisher) release.publisher = ' - '.join( sorted(release_publishers, key=str.lower)) if game.author and row['author'] and game.raw_author and row[ 'author'] not in game.raw_author: game.raw_author.append(row['author']) author = self.authorNameFromRow(row) authors = game.author.split(' - ') authors.append(author) game.author = ' - '.join(sorted(authors, key=str.lower)) if row['file_link'] and not (row['file_link'].endswith('.mlt')) and \ not row['file_link'].endswith('pdf') and not row['file_link'].endswith('.jpg'): if row['file_type'] == 'Loading screen': if row['file_link'].endswith('.gif'): if release.loading_screen_gif_filepath and \ release.loading_screen_gif_filepath!=row['file_link']: pass else: release.loading_screen_gif_filepath = row[ 'file_link'] release.loading_screen_gif_filesize = row[ 'file_size'] elif row['file_link'].endswith('scr'): if release.loading_screen_scr_filepath and \ release.loading_screen_scr_filepath!=row['file_link']: pass else: release.loading_screen_scr_filepath = row[ 'file_link'] release.loading_screen_scr_filesize = row[ 'file_size'] elif row['file_type'] == 'In-game screen': if row['file_link'].endswith('gif'): if release.ingame_screen_gif_filepath and \ release.ingame_screen_gif_filepath!=row['file_link']: pass else: release.ingame_screen_gif_filepath = row[ 'file_link'] release.ingame_screen_gif_filesize = row[ 'file_size'] elif row['file_link'].endswith('scr'): if release.ingame_screen_scr_filepath and \ release.ingame_screen_scr_filepath != row['file_link']: pass else: release.ingame_screen_scr_filepath = row[ 'file_link'] release.ingame_screen_scr_filesize = row[ 'file_size'] elif row['file_type'] == 'Instructions' and row[ 'file_link'].endswith('.txt'): if release.manual_filepath and \ release.manual_filepath!=row['file_link']: pass else: release.manual_filepath = row['file_link'] release.manual_filesize = row['file_size'] else: if row['publisher'] and \ ('Nyitrai' in row['publisher'] or 'Jatekgyaros' in row['publisher']) and \ row['genre'] and row['genre'] == 'Compilation': pass else: file_type = row['file_type'].lower() if 'snapshot' in file_type or \ 'disk' in file_type or \ 'tape' in file_type or \ 'rom' in file_type or \ 'covertape' in file_type or \ 'tr-dos' in file_type or \ 'electronic magazine' in file_type or \ 'cartridge' in file_type: game_file = self.gameFileFromRow(row, game) release.addFile(game_file) if row['alt_name'] and row['alt_language'] in (None, 'en'): alias = self.sanitizeAlias(row['alt_name']) release.addAlias(alias) games.append(game) return games
class GameFile(object): path = '' game = None release = None release_seq = 0 wos_name = '' wos_zipped_name = '' zfname = '' wos_path = '' tosec_path = '' content_desc = '' game_name_differentiator = '' is_demo = 0 release_date = '' machine_type = '' language = '' part = 0 side = 0 mod_flags = '' alt_mod_flag = '' notes = '' format = '' size = 0 size_zipped = 0 md5 = '' crc32 = '' sha1 = '' src='' # alt_src = [] #for deleting dest='' alt_dest='' is_tosec_compliant = False is_alternate = False # path_in_archive = None file_is_alone_in_archive = False file_in_archive = None # alt_files_in_archives = [] def __init__(self, path='', size=0, game=None, release=None, source=None): self.bundled_times = 0 self.dest = '' self.alt_dest = '' self.alt_mod_flag = '' self.alt_src = [] self.alt_files_in_archives = [] self.game_name_differentiator = '' self.is_alternate = False if not path: return filename = os.path.basename(path) self.setSize(size) self.format = self.getFormat(path) self.game = game self.release = release if game: if filename.endswith('.zip'): self.wos_zipped_name = filename self.wos_path = path else: self.wos_name = filename else: self.getGameFromFileName(path) if source == 'wos': self.wos_path = path elif source == 'tosec': self.getParamsFromTOSECPath(path) else: self.path = path if type(size)==str: size = int(size.replace(',','')) def __repr__(self): return '<GameFile: '+self.getPath()+' md5:'+self.md5+'>' def __eq__(self, other): if self.wos_name and \ self.wos_name == other.wos_name and \ self.size == other.size and \ self.format == other.format: return True if self.md5 and self.md5==other.md5: return True return False def getPath(self): for path in [self.alt_dest, self.dest, self.path, self.wos_path, self.tosec_path]: if path: return path def importCredentialsFromGame(self, game, overwrite=False): self.game = game other_file = game.findFileByMD5(self.md5) if other_file: self.importCredentialsFromFile(other_file, overwrite=overwrite) self.release = game.findReleaseByFile(self) def importCredentialsFromFile(self, other_file, overwrite=False): if other_file.format: self.format = other_file.format if overwrite or other_file.tosec_path: self.tosec_path = other_file.tosec_path if overwrite or other_file.content_desc: self.content_desc = other_file.content_desc if overwrite or other_file.is_demo: self.is_demo = other_file.is_demo if overwrite or other_file.release_date: self.release_date = other_file.release_date if overwrite or other_file.size: self.size = other_file.size if overwrite or other_file.language: self.language = other_file.language if overwrite or other_file.machine_type: self.machine_type = other_file.machine_type if overwrite or other_file.part: self.part = other_file.part if overwrite or other_file.side: self.side = other_file.side if overwrite or other_file.mod_flags: self.mod_flags = other_file.mod_flags if overwrite or other_file.notes: self.notes = other_file.notes def countFilesWithSameDestIn(self, collection=[]): count = 0 for other_file in collection: if self.dest: if self.dest == other_file.dest: count += 1 continue return count def countAlternateDumpsIn(self, collection=[]): count = 0 for other_file in collection: if self.game.name != other_file.game.name: continue if self.getYear() != other_file.getYear(): continue if self.getPublisher() != other_file.getPublisher(): continue if self.getReleaseSeq() != other_file.getReleaseSeq(): continue if self.getMachineType() != other_file.getMachineType(): continue if self.getMedia() != other_file.getMedia(): continue if self.getLanguage() != other_file.getLanguage(): continue if self.mod_flags.lower() != other_file.mod_flags.lower(): continue if self.is_demo != other_file.is_demo: continue if self.getContentDesc().lower() != other_file.getContentDesc().lower(): continue if self.format.lower() != other_file.format.lower(): continue if self.getNotes().lower() != other_file.getNotes().lower(): continue count += 1 if count: self.is_alternate = True return count def getEquals(self, collection): equals = [] for other_file in collection: if self.game.zxdb_id == other_file.game.zxdb_id and \ self.getYear() == other_file.getYear() and \ self.getPublisher() == other_file.getPublisher() and \ self.getReleaseSeq() == other_file.getReleaseSeq() and \ self.content_desc == other_file.content_desc and \ self.machine_type == other_file.machine_type and \ self.side == other_file.side and \ self.part == other_file.part and \ self.getLanguage() == other_file.getLanguage() and \ self.mod_flags == other_file.mod_flags and \ self.notes == other_file.notes: equals.append(other_file) return equals def addAlternateModFlag(self, copies_count, tosec_compliant, short_filenames): if not copies_count: return dest = os.path.splitext(self.dest if self.dest else self.getTOSECName()) if short_filenames: alt_mod_flag = '_'+str(copies_count+1) dir_path = os.path.split(dest[0]) self.alt_dest = os.path.join(dir_path[0], dir_path[1][:(8-len(alt_mod_flag))]+alt_mod_flag+dest[1]) elif not self.game.zxdb_id: dir_path = os.path.split(dest[0]) alt_mod_flag = '_'+str(copies_count+1) self.alt_dest = os.path.join(dir_path[0], dir_path[1]+alt_mod_flag+dest[1]) elif tosec_compliant: if copies_count == 1: self.alt_mod_flag = '[a]' else: self.alt_mod_flag = '[a' + str(copies_count) + ']' dest_dir = os.path.dirname(dest[0]) dest_filename = os.path.basename(dest[0]) everything_except_notes = dest_filename.replace(self.getNotes(), '') self.alt_dest = os.path.join(dest_dir, everything_except_notes+self.alt_mod_flag+self.getNotes()+'.'+self.format) else: alt_mod_flag = '_'+str(copies_count+1) self.alt_dest = dest[0]+alt_mod_flag+dest[1] def isAlternate(self): if self.game.zxdb_id and self.is_alternate: return True else: return False def isHack(self): if '[c' in self.mod_flags or \ '[h' in self.mod_flags or \ '[m' in self.mod_flags or \ '[f' in self.mod_flags or \ '[t' in self.mod_flags: return True else: return False def isBadDump(self): if '[b' in self.mod_flags: return True else: return False def isXRated(self): if self.game.x_rated: return True else: return False def copy(self): new = GameFile(self.wos_path, self.size, self.game) return new def getFileName(self): return self.tosec_path if self.tosec_path else os.path.basename(self.wos_path) def getGameFromFileName(self, path): if '(demo' in path: self.is_demo = True filename = os.path.splitext(os.path.basename(path).replace('(demo)', ''))[0] matches = re.findall(ROUND_BRACKETS_REGEX, filename) game_name = re.sub(TOSEC_REGEX, '', filename).strip() version = re.findall('v[0-9].*', game_name) if version: game_name = game_name.replace(version[0], '').strip() self.content_desc = ' '+version[0].strip() if not self.game: self.game = Game(game_name) else: self.game.name = game_name self.release = GameRelease(game=self.game) self.game.addRelease(self.release) self.game.addFile(self) if len(matches)>0: self.setReleaseDate(matches[0]) if len(matches)>1: self.game.setPublisher(matches[1]) self.game.setGenreFromFilePath(path) self.getParamsFromTOSECPath(path) def sortPublishers(self): publisher = self.release.getPublisher() if ' - ' in publisher: publisher = ' - '.join(sorted(publisher.split(' - '), key=str.lower)) # self.game.publisher = publisher self.release.publisher = publisher def getParamsFromTOSECPath(self, tosec_path): self.tosec_path = tosec_path filename = os.path.splitext(os.path.basename(tosec_path).replace('(demo)', ''))[0] self.setMachineType(filename) round_brackets_matches = re.findall(ROUND_BRACKETS_REGEX, filename) for each in round_brackets_matches[2:]: if len(re.findall('^M[0-9]$', each)): self.setLanguage(each) elif len(re.findall('^[a-z][a-z]-[a-z][a-z]$', each)): self.setLanguage(each) elif len(each)==2 and each.isalpha(): if each.islower(): self.setLanguage(each) elif each.isupper(): self.setCountry(each) elif 'Part' in each or 'Disk' in each or 'Tape' in each: self.setPart(each) elif 'Side' in each: self.setSide(each) square_brackets_matches = re.findall(SQUARE_BRACKETS_REGEX, filename) for each in square_brackets_matches: if len(re.findall('^a[0-9]?[0-9]?$', each)): continue elif len(re.findall('^[0-9\-]+K?$', each)): self.setMachineType(each) elif self.isModFlag(each): mod_flag = '[{}]'.format(each) self.addModFlag(mod_flag) elif each.startswith('aka '): aka = getSearchStringFromGameName(each[4:]) if aka == getSearchStringFromGameName(self.game.name): continue elif each in self.notes: continue elif 're-release' in each: continue elif 'ZXDB=' in each: continue elif each.startswith('48'): continue elif each.startswith('Pentagon'): continue elif self.machine_type and self.machine_type in each: continue elif each=='adult': self.game.x_rated = 1 else: note = '[{}]'.format(each) # if note not in self.notes: self.addNotes(note) # self.sortModFlags() if '(demo' in tosec_path.lower(): self.is_demo = 1 def isModFlag(self, match): for each in MOD_FLAGS_ORDER: if match==each: return True elif match.startswith(each) and \ match[len(each)] in (' 0123456789'): return True return False def addModFlag(self, new_mod_flag): if new_mod_flag not in self.mod_flags: mod_flags_array = re.findall(SQUARE_BRACKETS_REGEX, self.mod_flags) for i, flag in enumerate(mod_flags_array): if len(flag)>1 and new_mod_flag[1:-1].startswith(flag): self.mod_flags = self.mod_flags.replace('['+flag+']', new_mod_flag) return elif len(new_mod_flag)>1 and flag.startswith(new_mod_flag[1:-1]): self.mod_flags = self.mod_flags.replace(new_mod_flag, '['+flag+']') return self.mod_flags += new_mod_flag def sortModFlags(self): mod_flags_array = re.findall(SQUARE_BRACKETS_REGEX, self.mod_flags) mod_flags_array = sorted(set(mod_flags_array)) if 'b' in mod_flags_array: mod_flags_array.append(mod_flags_array.pop(mod_flags_array.index('b'))) self.mod_flags = ''.join('[{}]'.format(x) for x in mod_flags_array) def sortNotes(self): if self.notes.startswith('NONE') or self.notes.startswith('ALT'): return notes_array = re.findall(SQUARE_BRACKETS_REGEX, self.notes) notes_array = sorted(set(notes_array)) self.notes = ''.join('[{}]'.format(x) for x in notes_array) def setContentDesc(self, filename): if self.game and self.game.zxdb_id: game_name = filename.split('(')[0].strip() aliases = self.game.getAliases() alias_found = False if not self.content_desc and not alias_found: if ' - ' in game_name: self.content_desc = ' - '+' - '.join(game_name.split(' - ')[1:]) elif ' + ' in game_name: self.content_desc = ' + '+' + '.join(game_name.split(' + ')[1:]) if not self.content_desc.startswith(' '): self.content_desc = '' ss_content_desc = getSearchStringFromGameName(self.content_desc) if ss_content_desc in getSearchStringFromGameName(self.getGameName()): self.content_desc = '' for alias in aliases: if ss_content_desc in getSearchStringFromGameName(alias): self.content_desc = '' return self.content_desc = self.content_desc.replace(alias+' - ', '') self.content_desc = self.content_desc.replace(' - '+alias, '') if ss_content_desc in getSearchStringFromGameName(self.notes): self.content_desc = '' elif ' - Issue' in self.content_desc: self.content_desc = '' elif ' - Part ' in self.content_desc: split_content_desc = self.content_desc.split(' - Part ') part = split_content_desc[1][0] if part.isdigit(): self.part = int(part) split_content_desc[1] = split_content_desc[1][1:] self.content_desc = ''.join(split_content_desc).strip() def setReleaseDate(self, release_date): release_date = re.findall('([12][90][x0-9][x0-9](-[x01][x0-9](-[x0-3][x0-9])?)?)', release_date) if not release_date: return else: release_date = release_date[0][0] if len(release_date)>4: self.release_date = release_date year = release_date[:4] self.game.setYear(year) self.release.setYear(year) def setMachineType(self, filename): if not filename: return filename = filename.lower() if 'pentagon 128' in filename or '(pentagon' in filename or '[pentagon' in filename: self.machine_type = 'Pentagon 128K' elif 'timex' in filename or 'tc2048' in filename or 'ts2068' in filename: if self.format == 'dck': self.machine_type = 'TC2068-TS2068' else: self.machine_type = 'TC2048-TS2068' elif '+2a-+3' in filename: self.machine_type = '+2A-+3' elif '[+2a' in filename or '(+2a' in filename: self.machine_type = '+2A' elif '[+2' in filename or '(+2' in filename: self.machine_type = '+2' elif '[+3' in filename or '(+3' in filename or self.format=='dsk': self.machine_type = '+3' elif '128k' in filename and '48' in filename: self.machine_type = '48K-128K' elif '128k' in filename: self.machine_type = '128K' elif '48k' in filename: self.machine_type = '48K' elif '16k' in filename: self.machine_type = '16K' elif 'zx-uno' in filename or 'zxuno' in filename: self.machine_type = 'ZX-UNO' elif '(vega)' in filename: self.machine_type = 'Vega' elif '(next)' in filename: self.machine_type = 'Next' elif '(atm' in filename: self.machine_type = 'ATM' def setProtectionScheme(self, protection_scheme): if protection_scheme and protection_scheme not in self.notes and \ protection_scheme not in ('None', 'Undetermined', 'Unknown', 'Unspecified custom loader'): protection_scheme = protection_scheme.replace('Firebird ', '') protection_scheme = remove_brackets_regex.sub('', protection_scheme).strip() self.addNotes('['+protection_scheme+']') def getMachineType(self): if self.machine_type: return self.machine_type else: return self.game.machine_type def setLanguage(self, language): if language and (len(language)==2 or len(language)==5): self.language = language def setCountry(self, country): country = country.upper() if len(country)==2: if not self.release: print(self, 'has no self.release') self.release.country = country self.language = COUNTRY_LANGUAGE_DICT.get(country, country.lower()) def getCountry(self): if self.release and self.release.country: return self.release.country return '' def setPart(self, part): part = part.replace(' ', '').lower() for word in ('part', 'disk', 'tape'): if word in part: index = part.index(word)+len(word)-1 if part[index] in ['-']: index+=1 if len(part)>index+1 and part[index+1].isdigit(): index += 1 part_num = part[index] while True: index += 1 if index>=len(part) or not part[index].isdigit(): break else: part_num += part[index] self.part = int(part_num) if 'side' in part: self.setSide(part) def setLanguageFromWosName(self): name = self.wos_name.lower() for each in INCLUDED_LANGUAGES_LIST: if '('+each[0]+')' in name or '('+each[1].lower()+')' in name: self.language = each[0] break def getMedia(self): return ' '.join((self.getPart(), self.getSide())).strip() def getPart(self): if not self.part: return '' if self.game.parts<2: return '' if self.format in DISK_FORMATS: label = 'Disk' elif self.format in TAPE_FORMATS: label = 'Tape' else: label = 'Part' if self.game.parts>1: return '%s %d of %d' % (label, self.part, self.game.parts) def getLanguage(self): if self.language: return self.language elif '[tr ' in self.mod_flags: return self.mod_flags.split('[tr ')[1][:2] else: return self.game.getLanguage() def getTOSECName(self, game_name_length=MAX_GAME_NAME_LENGTH): output_name = self.getOutputName(game_name_length=game_name_length) return output_name def getOutputName(self, structure=TOSEC_COMPLIANT_FILENAME_STRUCTURE, game_name_length=MAX_GAME_NAME_LENGTH): if not structure: structure = TOSEC_COMPLIANT_FILENAME_STRUCTURE structure = structure.replace('{TOSECName}', TOSEC_COMPLIANT_FILENAME_STRUCTURE) if not structure.endswith('.{Format}'): structure += '.{Format}' kwargs = self.getOutputPathFormatKwargs(game_name_length=game_name_length, for_filename=True) output_name = structure.format(**kwargs) if structure==TOSEC_COMPLIANT_FILENAME_STRUCTURE+'.{Format}': country = self.getCountry().lower() if country==self.getLanguage() or \ COUNTRY_LANGUAGE_DICT.get(country.upper())==self.getLanguage() or \ '[tr ' in self.mod_flags: output_name = output_name.replace('(%s)' % self.getLanguage(), '') if self.getLanguage() == 'en' or \ (self.getCountry()=='GB' and '[tr ' in self.mod_flags): output_name = output_name.replace('(%s)' % self.getCountry(), '') output_name = output_name.replace('(%s)' % DEFAULT_MACHINE_TYPE, '') output_name = output_name.replace('()', '').replace('[]', '') output_name = ' '.join([x for x in output_name.split(' ') if x]) output_name = filepath_regex.sub('', output_name.replace('/', '-').replace(':', ' -')) filename, ext = os.path.splitext(output_name) output_name = filename.strip()+ext return output_name def setSide(self, side): side = side.lower().replace(' ', '') if 'sidea' in side or 'side1' in side: self.side = SIDE_A elif 'sideb' in side or 'side2' in side: self.side = SIDE_B elif 'side3' in side: self.side = SIDE_A self.part = 2 elif 'side4' in side: self.side = SIDE_B self.part = 2 elif 'side5' in side: self.side = SIDE_A self.part = 3 elif 'side6' in side: self.side = SIDE_B self.part = 3 def getSide(self): if self.side == SIDE_A: return 'Side A' elif self.side == SIDE_B: return 'Side B' else: return '' def setSize(self, size=0): if type(size)==str: size = int(size.replace(',','')) self.size = size def getSize(self): return '{:,}'.format(self.size) def getFormat(self, path=None): if not path: path = self.getPath() filename = os.path.basename(path) format = filename.replace('.zip','').split('.')[-1].lower() if format in GAME_EXTENSIONS: return format format = os.path.split(os.path.dirname(path))[-1].replace('[', '').replace(']', '').lower() if format in GAME_EXTENSIONS: return format if path.endswith('.zip') and os.path.exists(path): with zipfile.ZipFile(path, 'r') as zf: for zfname in zf.namelist(): zfname_ext = zfname.split('.')[-1].lower() if zfname_ext in GAME_EXTENSIONS: return zfname_ext return None def getMD5(self): if self.md5: return self.md5 local_path = self.getLocalPath() if os.path.exists(local_path): file_handle = self.getFileHandle(local_path) if not file_handle: print(self, 'has no valid file handle!') return '' md5 = hashlib.md5(file_handle).hexdigest() self.md5 = md5 return md5 def getCRC32(self): if self.crc32: return self.crc32 local_path = self.getLocalPath() if os.path.exists(local_path): file_handle = self.getFileHandle(local_path) if not file_handle: print(self, 'has no valid file handle!') return '' crc32 = hex(zlib.crc32(file_handle))[2:].zfill(8) self.crc32 = crc32 return crc32 def getSHA1(self): if self.sha1: return self.sha1 local_path = self.getLocalPath() if os.path.exists(local_path): file_handle = self.getFileHandle(local_path) if not file_handle: print(self, 'has no valid file handle!') return '' self.sha1 = hashlib.sha1(file_handle).hexdigest() return self.sha1 def getFileHandle(self, local_path, zipped=False): if local_path.endswith('.zip') and not zipped: with zipfile.ZipFile(local_path, 'r') as zf: for zfname in zf.namelist(): zfname_ext = zfname.split('.')[-1].lower() if zfname_ext != self.format and zfname_ext in GAME_EXTENSIONS: if self.format: print('FORMAT MISMATCH:', self, zfname) self.format = zfname_ext if zfname_ext == self.format: if not self.size: self.setSize(zf.getinfo(zfname).file_size) if not self.crc32: self.crc32 = hex(zf.getinfo(zfname).CRC)[2:].zfill(8) return zf.read(zfname) else: with open(local_path, 'rb')as f: return f.read() def getOutputPathFormatKwargs(self, game_name_length=MAX_GAME_NAME_LENGTH, for_filename=False): game_name = self.getGameName(game_name_length=game_name_length, for_filename=for_filename) publisher = self.getPublisher(restrict_length=game_name_length<MAX_GAME_NAME_LENGTH) if publisher == '-' and not for_filename: publisher = 'Unknown Publisher' author = self.getAuthor(restrict_length=game_name_length<MAX_GAME_NAME_LENGTH) if author == '-' and not for_filename: author = 'Unknown Author' return { 'Type':self.getType(), 'Genre':self.getGenre(), 'Year':self.getYear(for_filename=for_filename), 'Letter':getWosSubfolder(game_name), 'MachineType':self.getMachineType(), 'Publisher':publisher, 'Author':author, 'MaxPlayers':self.getMaxPlayers(), 'GameName':game_name, 'Country':self.getCountry(), 'Language':self.getLanguage(), 'Format':self.format, 'Media':self.getMedia(), 'Part':self.getPart(), 'Side':self.getSide(), 'ModFlags':self.mod_flags+self.alt_mod_flag, 'ZXDB_ID':self.game.getWosID(), 'Notes':self.getNotes(), 'OriginalName':self.getOriginalName(), } def getOriginalName(self): if self.src: return os.path.splitext(os.path.basename(self.src))[0] def getNotes(self): if self.notes == 'NONE': return '' if self.notes.startswith('ALT '): notes = self.notes[4:] else: notes = self.notes if self.game.x_rated: return notes + X_RATED_FLAG else: return notes def addNotes(self, note): if self.notes == 'NONE' or self.notes.startswith('ALT ') or note in self.notes: return else: self.notes += note def getTOSECDatName(self): parts = ['Sinclair ZX Spectrum'] #Hardcoded until ZX81 and other machines' support is about to be added parts += self.getType().split('\\') parts.append('[{}]'.format(self.format.upper())) return ' - '.join(parts) def getGenre(self): if self.game.genre: return self.game.getGenre() else: return 'Unknown' def getType(self): if self.game.name.startswith('ZZZ-UNK'): self.type = 'Unknown' return self.type genre = self.getGenre() self.type = '' if 'Compilation' in genre: self.type += 'Compilations' if 'Utilit' in genre: self.type += os.sep+'Applications' elif 'Education' in genre: self.type += os.sep+'Educational' elif 'Demo' in genre: self.type += os.sep+'Demos' elif 'Magazine' in genre: self.type += os.sep+'Magazines' elif 'Book' in genre: self.type += os.sep+'Books' elif 'Music' in genre: self.type += os.sep+'Music' elif 'Mixed' in genre: self.type += os.sep+'Mixed' else: self.type += os.sep+'Games' elif ('Firmware' in genre or 'Hardware' in genre) and self.format!='rom': self.type += 'Applications' elif 'Education' in genre: self.type += 'Educational' elif genre.startswith('Utilit') or \ 'Programming' in genre or \ 'General' in genre or \ 'Emulator' in genre: self.type += 'Applications' elif 'Magazine' in genre: self.type += 'Magazines' elif 'Covertape' in genre: self.type += 'Covertapes' elif 'Demo' in genre: self.type += 'Demos' elif 'Book' in genre: self.type += 'Books' elif 'Documentation' in genre: self.type += 'Documentation' elif 'Game' in genre: self.type += 'Games' elif 'Music' in genre: self.type += 'Music' else: self.type = 'Unknown' if self.format == 'rom' and 'Games' not in self.type: self.type = 'Firmware' return self.type def setReRelease(self): if self.release.release_seq>0: for release in self.game.releases: if self.release.release_seq != release.release_seq and \ self.release.getYear()==release.getYear(): self.addNotes('[re-release]') break def setAka(self): if '[aka' not in self.notes: aliases_search_strings = [] game_name = self.getGameName(for_filename=True) game_name = replaceRomanNumbers(game_name) game_name_search_string = getSearchStringFromGameName(game_name) aliases_search_strings.append(game_name_search_string) for alias in self.release.getAllAliases(): alias = replaceRomanNumbers(alias) alias_search_string = getSearchStringFromGameName(alias) if not [x for x in aliases_search_strings if x in alias_search_string or alias_search_string in x]: alias = putPrefixToEnd(alias) self.addNotes('[aka '+alias+']') aliases_search_strings.append(alias_search_string) def getGameName(self, game_name_length=MAX_GAME_NAME_LENGTH, for_filename=False, short=False): if for_filename and game_name_length<=70: self.removeAka() release_aliases = self.release.getAllAliases() game_name = release_aliases[0] if release_aliases else self.game.name if not game_name: print('No game_name for file:', self) return '' game_name = filepath_regex.sub('', game_name.replace('/', '-').replace(':', ' -')).strip() if not for_filename: while game_name.endswith('.'): game_name = game_name[:-1] if short: return getMeaningfulEightLetterName(game_name) if for_filename and self.content_desc: game_name += self.getContentDesc() if self.game_name_differentiator: game_name += '('+self.game_name_differentiator+')' while len(game_name)>game_name_length: game_name = [x for x in game_name.split(' ') if x][:-1] game_name = ' '.join(game_name) game_name = [x for x in game_name.split(' ') if x] while len(game_name[-1])<2 and \ not game_name[-1][-1].isalnum(): game_name = ' '.join(game_name[:-1]) game_name = [x for x in game_name.split(' ') if x] game_name = ' '.join(game_name).strip() if for_filename and self.is_demo: game_name += ' (demo)' return game_name def removeAka(self): if '[aka' in self.notes: self.notes = re.sub('(\[aka.+?\])', '', self.notes, count=1) def getContentDesc(self): if self.content_desc=='NONE': return '' elif self.content_desc.startswith('ALT'): return self.content_desc[3:] elif self.content_desc: return self.content_desc else: return '' def getYear(self, for_filename=True): if for_filename and self.release_date: return self.release_date if self.release: year = self.release.getYear() else: year = self.game.getYear() if not for_filename: year = year[:4] return year def getPublisher(self, restrict_length=False): if self.release: publisher = self.release.getPublisher() else: publisher = self.game.getPublisher() if restrict_length: publisher = [x for x in publisher.split(' ') if x][:3] if len(publisher)==3 and len(publisher[-1])<3: publisher = publisher[:-1] publisher = ' '.join(publisher).strip() return publisher def getAuthor(self, restrict_length = False): author = self.game.getAuthor() if restrict_length: author = [x for x in author.split(' - ') if x][:3] if len(author)==3 and len(author[-1])<3: author = author[:-1] author = ' '.join(author).strip() return author def getMaxPlayers(self): result = str(self.game.number_of_players) + 'P' return result #FUTURE: # if self.game.multiplayer_type: # result += ' (%s)' % self.game.getMultiplayerType() # return result def getReleaseSeq(self): return self.release.release_seq if self.release else 0 def getSortIndex(self, preferred_formats_list=GAME_EXTENSIONS): try: return preferred_formats_list.index(self.format) except IndexError: return 99 def getWosPath(self, wos_mirror_root = WOS_SITE_ROOT): return wos_mirror_root+self.wos_path def getLocalPath(self): if self.wos_path: local_path = os.path.abspath(self.getWosPath(wos_mirror_root=LOCAL_FTP_ROOT)) return local_path elif self.path: return self.path elif self.tosec_path: return self.tosec_path def getDestPath(self): dest = self.alt_dest if self.alt_dest else self.dest return dest def getSrcPathForLog(self): src = self.src if self.zfname and src.lower().endswith('.zip'): src = self.zfname + ' from ' + src return src def getBundleName(self, depth_level, bundle_key_length=3): root_dir, bundled_part = self.getSplitDest(depth_level) bundle_name = bundled_part.split('\\')[0] bundle_name = ''.join([x for x in bundle_name if x.isalnum()])[:bundle_key_length].lower() return bundle_name def setBundle(self, bundle_name, depth_level): root_dir, bundled_part = self.getSplitDest(depth_level) dest = os.path.join(root_dir, bundle_name, bundled_part) self.bundled_times += 1 self.alt_dest = dest def getSplitDest(self, depth_level): if not depth_level: depth_level += 1 dest = self.getDestPath() dest = dest.replace('/', '\\').split('\\') root_dir = os.sep.join(dest[:-depth_level]) depth_level -= self.getBundledTimes(dest) bundled_part = os.sep.join(dest[-depth_level:]) # dest = [re.sub('^[0-9a-z]{,3}\-[0-9a-z]{,3}[0-9]?$', '', x) for x in dest] # dest = [x for x in dest if x] # bundled_part = os.sep.join(dest) return root_dir, bundled_part def getBundledTimes(self, dest): # return self.bundled_times bundled_times = 0 for folder in dest: if re.match('^[0-9a-z]{,3}\-[0-9a-z]{,3}[0-9]?$', folder): bundled_times += 1 return bundled_times def getAbsoluteDestPath(self): return os.path.abspath(self.getDestPath())