def findhubs(wadname): wad = omg.WAD(wadname) mapexits = {} for mapname, mapinfo in wad.maps.items(): mapexits[mapname] = find_exits( mapinfo["LINEDEFS"].data, mapinfo["BEHAVIOR"].data ) mapenters = defaultdict(set) print("Map : Exits to") for mapname, exits in sorted(mapexits.items()): for exit in exits: mapenters[exit].add(mapname) s = ", ".join(str(i) for i in exits) print(f"{mapname}: {s}") mapenters = {k: sorted(v) for k, v in mapenters.items()} print("Map : Comes from") for mapname, entries in sorted(mapenters.items()): s = ", ".join(str(i) for i in entries) print(f"{mapname}: {s}") # TODO: Convert mapexits into hub/spoke graph with open(f"{wadname}.pickle", "wb") as f: pickle.dump((mapexits, mapenters), f) return 0
def main(): parser = argparse.ArgumentParser() parser.add_argument('--input', help='input wad file to process') parser.add_argument( '--mode', choices=['train', 'test'], help='what those maps are going to be used for: train or test?') parser.add_argument('--num_maps', type=int, default=10, help='Number of the mazes to generate.') args = parser.parse_args() in_file = args.input out_file = in_file[:-4] wad = omg.WAD(in_file) if args.mode == 'test': textures = get_textures(TEST_TEXTURES_PATH) else: global FLOOR_TEXTURE, CEILING_TEXTURE FLOOR_TEXTURE, CEILING_TEXTURE = ['MFLR8_1'], ['MFLR8_1'] textures = get_textures(TRAIN_TEXTURES_PATH) for index in range(args.num_maps): print(index) wad.maps['MAP01'] = change_textures(wad.maps['MAP01'], textures) wad.to_file(out_file + '_{}_{}.wad'.format(args.mode, index))
def copy_resources(): for src_file in RES_FILES: # don't copy texture lumps for files that aren't present if src_file.startswith('textures.doom1') and not get_wad_filename('doom'): continue elif src_file == 'textures.doom2' and not get_wad_filename('doom2'): # DO copy if final doom exists and doom2 doesn't if not get_wad_filename('tnt'): continue elif src_file == 'textures.tnt' and not get_wad_filename('tnt'): continue elif src_file == 'textures.plut' and not get_wad_filename('plutonia'): continue logg('Copying %s' % src_file) copyfile(RES_DIR + src_file, DEST_DIR + src_file) # doom2 vs doom2bfg map31/32 names differ, different mapinfos with same name d2wad = omg.WAD() d2_wad_filename = get_wad_filename('doom2') # neither doom2: mapinfo still wants a file for the secret levels if not d2_wad_filename: copyfile(RES_DIR + 'mapinfo/doom2_nonbfg_levels.txt', DEST_DIR + 'mapinfo/doom2_secret_levels.txt') return d2wad.from_file(d2_wad_filename) # bfg version? if d2wad.graphics.get(BFG_ONLY_LUMP, None): copyfile(RES_DIR + 'mapinfo/doom2_bfg_levels.txt', DEST_DIR + 'mapinfo/doom2_secret_levels.txt') else: copyfile(RES_DIR + 'mapinfo/doom2_nonbfg_levels.txt', DEST_DIR + 'mapinfo/doom2_secret_levels.txt')
def main(): parser = argparse.ArgumentParser() parser.add_argument('--input', help='input wad file to process') parser.add_argument('--texture-sparsity', choices=['sparse', 'dense'], help='texture randomization mode - sparse or dense') parser.add_argument('--mode', choices=['train', 'test'], help='what those maps are going to be used for: train or test?') args = parser.parse_args() in_file = args.input out_file = in_file + SUFFIX wad = omg.WAD(in_file) if args.texture_sparsity == 'sparse': generator_class = DmTextureGeneratorWrapper else: generator_class = RandomTextureGenerator if args.mode == 'test': textures = get_textures(TEST_TEXTURES_PATH, []) number_of_maps = NUMBER_OF_IDENTICAL_TEST_MAPS generator_arguments = (textures, GOAL_TEXTURES, LANDMARK_TEXTURES) else: textures = get_textures(TEST_TEXTURES_PATH, GOAL_TEXTURES) number_of_maps = NUMBER_OF_TRAIN_MAPS generator_arguments = (textures, [], []) texture_generator = generator_class(*generator_arguments) for index in xrange(number_of_maps): print(index) set_seed(args.mode) add_map_to_wad(wad, change_textures(wad.maps['MAP01'], texture_generator), index) wad.to_file(out_file)
def extract_iwad_maps(wad_name, map_prefix): in_wad = omg.WAD() wad_filename = get_wad_filename(wad_name) in_wad.from_file(wad_filename) for map_name in in_wad.maps.find('*'): logg(' Extracting map %s...' % map_name) out_wad_filename = DEST_DIR + 'maps/' + map_prefix + map_name + '.wad' extract_map(in_wad, map_name, out_wad_filename)
def add_secret_exit(map_name, line_id): # sets given line # in given map as a secret exit switch wad = omg.WAD() wad_filename = DEST_DIR + 'maps/%s.wad' % map_name wad.from_file(wad_filename) ed = omg.MapEditor(wad.maps[map_name]) ed.linedefs[line_id].__dict__['action'] = 51 wad.maps[map_name] = ed.to_lumps() wad.to_file(wad_filename)
def toudmf(input, output, namespace): """Convert all maps in INPUT.wad to UDMF and write to OUTPUT.wad""" source = omg.WAD(input) dest = omg.WAD(output if os.path.exists(output) else None) try: with tqdm(source.maps.items(), unit="map") as t: for mapname, mapinfo in t: t.write(mapname) dest.udmfmaps[mapname] = omg.UMapEditor(mapinfo, namespace).to_lumps() dest.to_file(output) except AttributeError: click.echo("Incorrect Namespace (-ns) for map format?") return 1 return 0
def extract_lumps(wad_name): if not wad_name in WAD_LUMP_LISTS: return wad = omg.WAD() wad_filename = get_wad_filename(wad_name) wad.from_file(wad_filename) for lump_list in WAD_LUMP_LISTS[wad_name]: # derive subdir from name of lump list try: lump_type = lump_list[:lump_list.index('_')] except ValueError: logg("ERROR: Couldn't identify type of lump list %s" % lump_list, error=True) continue # sigil sky lump isn't in patch namespace if lump_list == 'patches_sigil': lump_type = 'data' lump_table = getattr(wad, lump_type, None) if not lump_table: logg(' ERROR: Lump type %s not found' % lump_type, error=True) continue logg(' extracting %s...' % lump_list) # sigil sky is in data namespace but we want it in patches dir if wad_name == 'sigil' and lump_list == 'patches_sigil': lump_subdir = DEST_DIR + 'patches/' # sigil screens aren't in graphics namespace but belong in that dir elif wad_name == 'sigil' and lump_type == 'data': lump_subdir = DEST_DIR + 'graphics/' # write PLAYPAL, TEXTURE1 etc to pk3 root elif lump_type in ['data', 'txdefs']: lump_subdir = DEST_DIR else: lump_subdir = DEST_DIR + lump_type + '/' # process each item in lump list for line in open(DATA_DIR + lump_list).readlines(): line = line.strip() # ignore comments if line.startswith('//'): continue # no colon: extracted lump uses name from list if line.find(':') == -1: out_filename = line lump_name = line # colon: use filename to right of colon else: # split then strip lump_name, out_filename = line.split(':') lump_name = lump_name.strip() out_filename = out_filename.strip() if not lump_name in lump_table: logg(" ERROR: Couldn't find lump with name %s" % lump_name, error=True) continue lump = lump_table[lump_name] out_filename += '.lmp' if lump_type != 'music' else '.mus' logg(' Extracting %s' % lump_subdir + out_filename) lump.to_file(lump_subdir + out_filename)
def hexenhubmerge(input, output, merged_map_name, hub, maps, udmf): """Merge HUB and MAPS from INPUT.wad into a single map in OUTPUT.wad as NAME""" source = omg.WAD(input) hubname = to_mapname(hub) if hubname not in source.maps: click.echo(f"Error: Hub {hubname} not found in source wad.") return 1 spokes = [to_mapname(s) for s in maps] missing = [mapname for mapname in spokes if mapname not in source.maps] if missing: mstr = ", ".join(missing) click.echo(f"Error: Could not find {mstr}") return 1 click.echo(f"Converting {hubname} to UDMF to use as the starting hub") merger = MapMerger(source.maps[hubname]) for spoke in spokes: click.echo(f"Converting and merging {spoke}") merger.merge(source.maps[spoke]) merged_map_name = to_mapname(merged_map_name) source_hubnum = to_mapnum(hubname) dest_hubnum = to_mapnum(merged_map_name) + 1 spoke_nums = [to_mapnum(s) for s in maps] click.echo( f"Merged map will be {merged_map_name} in {click.format_filename(output)}, will exit into MAP{dest_hubnum:02}." ) merger.fix_teleports(source_hubnum, dest_hubnum, spoke_nums) if udmf is not None: click.echo(f"Dumped UDMF to {click.format_filename(udmf.name)}") udmf.write(merger.to_textmap()) result = omg.WAD(output if os.path.exists(output) else None) result.udmfmaps[merged_map_name] = merger.to_lumps() result.to_file(output) return 0
def add_secret_level(map_src_filename, map_src_name, map_dest_name): global num_maps # copies given map file into dest dir and sets its map lump name src_filename = get_wad_filename(map_src_filename) dest_filename = DEST_DIR + 'maps/%s.wad' % map_dest_name copyfile(src_filename, dest_filename) wad = omg.WAD() wad.from_file(dest_filename) wad.maps.rename(map_src_name, map_dest_name) wad.to_file(dest_filename) num_maps += 1
def load_map_editors(wad_or_wad_file): ''' Function to load map editors from wad (or filename) ''' if isinstance(wad_or_wad_file, str): wad = omg.WAD(wad_or_wad_file) elif isinstance(wad_or_wad_file, omg.WAD): wad = wad_or_wad_file else: raise TypeError( "argument {} is of type {} but expected str or omg.WAD".format( 'wad_or_wad_file', type(wad_or_wad_file))) return {k: MapEditorPreprocessor(wad, v) for k, v in wad.maps.items()}
def dumpacs(wad_filename): """Extract and decompile all BEHAVIOR lumps from a wad""" source = omg.WAD(wad_filename) with tqdm(source.maps.items(), unit="map") as t: for mapname, mapinfo in t: try: dump(mapname, mapinfo["BEHAVIOR"].data) except KeyError: pass return 0
def setUp(self): testwad = omg.WAD('test.wad') self.testcol = testwad.data['COLORMAP'] self.col = omg.colormap.Colormap()
def setUp(self): testwad = omg.WAD('test.wad') self.testmap = omg.mapedit.MapEditor(testwad.maps['MAP01'])
def setUp(self): testwad = omg.WAD('test.wad') self.testgfx = testwad.graphics['HELP1']
def setUp(self): testwad = omg.WAD('test.wad') self.tex = omg.txdef.Textures(testwad.txdefs)
def setUp(self): testwad = omg.WAD('test.wad') self.testpal = testwad.data['PLAYPAL'] self.pal = omg.playpal.Playpal()
import omg from os import listdir from os.path import isfile, join import sys from PIL import Image wadfilespath = "enginewad/" wad = omg.WAD() files = [f for f in listdir(wadfilespath) if isfile(join(wadfilespath, f))] for f in files: ext = f.find('.meta') if ext != -1: continue ext = f[f.find('.') + 1:] if ext == "png": graphic = omg.Graphic() graphic.from_Image(Image.open(wadfilespath + f)) lump = graphic else: with open(wadfilespath + f, "rb") as file: lump = omg.Lump(file.read()) wad.data[f[:f.find('.')]] = lump wad.to_file("../../../nasty.wad")
in_file = '/home/adosovit/work/future_pred/my_scenarios/paper/%s.wad' % map_name out_file = '/home/adosovit/work/future_pred/my_scenarios/paper/%s_manymaps_%s.wad' % (map_name, mode) if mode == 'all': texture_files = ['all_textures.txt'] * 98 elif mode == 'train': texture_files = ['train_textures.txt'] * 98 elif mode == 'test': texture_files = ['test_textures.txt'] * 98 elif mode == 'mix': texture_files = ['train_textures.txt'] * 88 + ['test_textures.txt'] * 10 else: raise Exception('Unknown mode', mode) wad = omg.WAD(in_file) for (nm,texturef) in enumerate(texture_files): wad.maps['MAP%.2d' % (nm+2)] = change_textures(wad.maps['MAP01'], texturef) wad.to_file(out_file) #for a in sys.argv[1:]: #if a == "-o": #break #print "Adding %s..." % a #w += omg.WAD(a) #outpath = "merged.wad" #if "-o" in sys.argv: outpath = sys.argv[-1] #w.to_file(outpath)
for word in words.strip().split("\n"): word = " " + word + " " v = add_vertex(x=0, y=y + 32) add_linedef(vx_a=last_v, vx_b=v, flags=1, front=sd1) last_v = v y += 32 start_v = last_v backpin_v = add_vertex(x=-1, y=y) for offset, npixels in make_phrase(word, letters): sd = add_sidedef(sector=sec1, tx_low="SHAWN1", off_x=offset) v = add_vertex(x=0, y=y + npixels) add_linedef(vx_a=last_v, vx_b=v, front=sd, back=sd2, flags=1 + 4 + 16) # impassible, two-sided, lower-unpeg last_v = v y += npixels add_linedef(vx_a=start_v, vx_b=backpin_v, front=sd2, flags=1) add_linedef(vx_a=backpin_v, vx_b=last_v, front=sd2, flags=1) # Tailing end to join back with first linedef add_linedef(vx_a=last_v, vx_b=v4, front=sd1, flags=1) # Player 1 start add_thing(x=128, y=128, type=1, angle=180) w = omg.WAD() w.maps["MAP01"] = ed.to_lumps() w.to_file("letters.wad")
def get_random_map_shot(self): # search any WAD files for maps using omgifol self.all_wad_maps = [] # remember which file a map lives in (for multi-wad zips) map_wad_mapping = {} for f in self.files_to_load: if f.lower().endswith('wad'): maps = omg.WAD(f).maps.keys() if not maps: continue for m in maps: # don't add MAPINFO lumps if m != 'MAPINFO': self.all_wad_maps.append(m) map_wad_mapping[m] = f if len(self.all_wad_maps) > 0: print('Maps found: %s' % ', '.join(self.all_wad_maps)) else: print("Couldn't find any maps") return # pick a map map_name = random.choice(self.all_wad_maps) self.map_wad = os.path.basename(map_wad_mapping[map_name]) # get correct number(s) to feed -warp CLI switch: map# for doom2, # E# M# for doom1 if map_name.startswith('E'): self.level = '%s %s' % (map_name[1], map_name[3:]) elif map_name.startswith('MAP'): self.level = map_name[3:] else: # something else, gzdoom hopefully opens these by lump name self.level = map_name # determine IWAD to launch with # two numbers = doom1 eXmY map, one number = doom2 mapXX format # if already set, we got it from a url, eg heretic or hexen if self.file_link and self.file_link.startswith(DOWNLOAD_URL_PATTERN + 'levels/heretic/'): self.iwad = 'heretic.wad' elif self.file_link and self.file_link.startswith( DOWNLOAD_URL_PATTERN + 'levels/hexen/'): self.iwad = 'hexen.wad' elif self.map_wad.lower() in ['heretic.wad', 'hexen.wad', 'hexdd.wad']: self.iwad = self.map_wad elif ' ' in self.level: self.iwad = 'doom.wad' else: self.iwad = 'doom2.wad' print('Will try to load map %s' % self.level) # enclose pwad file names in quotes file_list = [] for f in ALWAYS_INCLUDE_PWADS + self.files_to_load: file_list.append('"%s"' % f) # generate a shell script to run gzdoom with all the correct settings cmd = ['python2', 'dmvis.py', ' '.join(self.files_to_load), map_name] SCREENSHOT_FILENAMES[0] = ("%s_%s.gif" % (self.files_to_load[0], map_name)) SCREENSHOT_OUTPUT_FILENAMES[0] = SCREENSHOT_FILENAMES[0] print(' '.join(cmd)) subprocess.call(cmd) self.shot_valid = os.path.exists( SCREENSHOT_FILENAMES[0]) # and os.path.exists(LOG_PATH) if self.shot_valid: self.post()
res = spack("2H2h", *imr.size, 0, 0) cols = [] for x in range(imr.size[0]): res += spack("I", 8 + imr.size[0] * 4 + (3 + imr.size[1]) * len(cols)) col = spack("3B", 0, image.size[1], 0) i = image.crop((x, 0, x + 1, imr.size[1])) col += i.tobytes() cols.append(col) return res + b''.join(cols) if __name__ == "__main__": texnames = sys.argv[3:] outwad = omg.WAD() pal = ImagePalette.ImagePalette( "RGB", bytearray(omg.WAD(from_file=sys.argv[1]).data["PLAYPAL"].data[:768])) texture1 = [] pnames = [] i = 0 print(":: ImagoLux :: Limit-Removing Colored Lighting ::") print("(c)2018 Gustavo R. Rehermann. Code: MIT License. Media:") print("CC0 if owned by Gustavo, respective owners otherwise.") print() if ZDOOM: print("Exporting ZDoom-style textures.")
def extract_map(in_wad, map_name, out_filename): global num_maps out_wad = omg.WAD() out_wad.maps[map_name] = in_wad.maps[map_name] out_wad.to_file(out_filename) num_maps += 1
def main(argv): w = omg.WAD(from_file=omg.WadIO(argv[1], mode='rb')) map_editor = omg.MapEditor(w.maps[argv[2]]) return globals()[argv[3]](map_editor, *argv[4:])
def extract_master_levels(): # check if present first order_file, ml_map_order = get_master_levels_map_order() if len(ml_map_order) == 0: return first_ml_wad = get_wad_filename(ml_map_order[0]) if not first_ml_wad: logg('ERROR: Master Levels not found.', error=True) return logg('Processing Master Levels...') mapinfo = open(ML_MAPINFO_FILENAME, 'w') mapinfo.write(MASTER_LEVELS_MAPINFO_HEADER % order_file) for i,wad_name in enumerate(ml_map_order): in_wad = omg.WAD() wad_filename = get_wad_filename(wad_name) if not wad_filename: logg("ERROR: Couldn't find %s" % wad_name, error=True) continue in_wad.from_file(wad_filename) out_wad_filename = DEST_DIR + 'maps/' + MASTER_LEVELS_MAP_PREFIX + 'map' # extra zero for <10 map numbers, eg map01 out_wad_filename += str(i + 1).rjust(2, '0') + '.wad' logg(' Extracting %s to %s' % (wad_filename, out_wad_filename)) # grab first map we find in each wad map_name = in_wad.maps.find('*')[0] extract_map(in_wad, map_name, out_wad_filename) # write data to mapinfo based on ordering mapinfo.writelines('\n'.join(get_ml_mapinfo(wad_name, i+1))) mapinfo.write('\n\n') # save teeth map32 to map21 wad_filename = get_wad_filename('teeth') out_wad_filename = DEST_DIR + 'maps/' + MASTER_LEVELS_MAP_PREFIX + 'map21' + '.wad' logg(' Extracting %s map32 to %s' % (wad_filename, out_wad_filename)) in_wad = omg.WAD() in_wad.from_file(wad_filename) extract_map(in_wad, in_wad.maps.find('*')[1], out_wad_filename) # write map21 mapinfo if ml_map_order.index('teeth') == 19: next_map = 'EndGameC' else: next_map = '%sMAP%s' % (MASTER_LEVELS_MAP_PREFIX.upper(), ml_map_order.index('teeth') + 2) mapinfo.write(MASTER_LEVELS_SECRET_DEF % (next_map, MASTER_LEVELS_MUSIC['teeth2'], MASTER_LEVELS_AUTHOR_PREFIX, MASTER_LEVELS_AUTHORS['teeth2'])) # finish mapinfo mapinfo.writelines([MASTER_LEVELS_CLUSTER_DEF]) mapinfo.close() # extract sky lumps for wad_name,patch_replace in MASTER_LEVELS_PATCHES.items(): wad = omg.WAD() wad_filename = get_wad_filename(wad_name) wad.from_file(wad_filename) # manor stores sky in patches namespace, combine and virgil don't if patch_replace[0] in wad.data: lump = wad.data[patch_replace[0]] else: lump = wad.patches[patch_replace[0]] out_filename = DEST_DIR + 'patches/' + patch_replace[1] + '.lmp' logg(' Extracting %s lump from %s as %s' % (patch_replace[0], wad_filename, patch_replace[1])) lump.to_file(out_filename)
else: n = str(num) return "MAP" + n def print_map_order(sortedlist, wad): for m in sortedlist: print m.orig_pos def place_maps_in_wad(sortedlist, wad): for i, m in enumerate(sortedlist): wad.maps[map_index(i)] = m return wad if __name__ == "__main__": if len(sys.argv) != 2: print "Usage:" print "" print " doomsort.py [wad]" else: test_wad_path = sys.argv[1] test_wad = omg.WAD(test_wad_path) # test_map = omg.MapEditor(test_wad.maps["MAP01"]) rated_list = rate_map_list(test_wad) sorted_rated_list = sorted(rated_list, key=lambda wmap: wmap.rating) #output_wad = place_maps_in_wad(sorted_rated_list,test_wad) #output_wad.to_file(test_output_path) print_map_order(sorted_rated_list, test_wad)
import omg, sys if (len(sys.argv) < 3): print "\n Omgifol script: merge WADs\n" print " Usage:" print " merge.py input1.wad input2.wad ... [-o output.wad]\n" print " Default output is merged.wad" else: w = omg.WAD() for a in sys.argv[1:]: if a == "-o": break print "Adding %s..." % a w += omg.WAD(a) outpath = "merged.wad" if "-o" in sys.argv: outpath = sys.argv[-1] w.to_file(outpath)
def main(): """Main function""" args = parse_args() with open(args.wad_config[0]) as wad_config_stream: wad_list = yaml.safe_load(wad_config_stream) wad_id_list = [] for wad in wad_list: if 'www.doomworld.com' in wad: file_id = get_file_id_from_idgames_url(wad) wad_id_list.append(file_id) else: file_search = IdgamesAPI.search(wad) if file_search.get('content'): files_info = file_search['content']['file'] if not isinstance(files_info, list): files_info = [files_info] if len(files_info) > 1 and not args.download_all: raise RuntimeError( 'Found multiple matching wads for wad name {} during search.'.format(wad) ) wad_id_list.extend([file_info['id'] for file_info in files_info]) wad_jsons = [] for wad_id in wad_id_list: idgames_response, local_file_path = IdgamesAPI.download_file(file_id=wad_id, overwrite=True) file_info = idgames_response['content'] file_creation_date = None tmp_extract_path = 'tmp_extraction' with ZipFile(local_file_path, 'r') as zip_file: info_list = zip_file.infolist() for file_zip_info in info_list: for resource_re in RESOURCE_FILE_FORMATS: if resource_re.match(file_zip_info.filename): if not file_creation_date: file_creation_date = parse_zip_info_date_time(file_zip_info.date_time) else: cur_file_creation_date = parse_zip_info_date_time( file_zip_info.date_time ) if cur_file_creation_date > file_creation_date: file_creation_date = cur_file_creation_date zip_file.extractall('tmp_extraction') wads_in_zip = glob.glob('{}/*.wad'.format(tmp_extract_path)) num_maps = 0 for wad in wads_in_zip: wad_object = omg.WAD(wad) num_maps += len(wad_object.maps) if num_maps < 1: LOGGER.error('No maps found for wad %s.', file_info['filename']) # idgames date format: YYYY-MM-DD # This expression splits the date by '-', converts each element to int, then passes the # values as args to the datetime constructor idgames_date = datetime(*map(int, file_info['date'].split('-'))) idgames_year = idgames_date.year file_creation_year = file_creation_date.year if idgames_year != file_creation_year: LOGGER.error('Mismatched years in idgames date and file creation date for wad %s!', file_info['filename']) LOGGER.error('Idgames year: %s, file creation year: %s.', idgames_year, file_creation_year) wad_year = file_creation_year if file_creation_date < EARLIEST_WAD_TIME: wad_year = idgames_year wad_json = { 'author': file_info['author'], 'single_map': num_maps == 1, 'iwad': file_info['dir'].split('/')[1], 'name': file_info['title'], 'short_name': file_info['filename'].split('.')[0], 'year': wad_year, 'file': { 'name': local_file_path, 'data': 'placeholder' } } wad_jsons.append(wad_json) shutil.rmtree(tmp_extract_path) wad_jsons = {'wads': wad_jsons} output_json = args.output_json if not output_json: output_json = '{}_upload.json'.format(datetime.today().strftime('%Y%m%d')) if os.path.exists(output_json): filename_no_ext = os.path.splitext(output_json)[0] alt_file_list = glob.glob('{}_*.json'.format(filename_no_ext)) if not alt_file_list: output_json = '{}_2.json'.format(filename_no_ext) else: max_idx = max(int(os.path.splitext(filename)[0].split('_')[-1]) for filename in alt_file_list) output_json = '{}_{}.json'.format(filename_no_ext, max_idx + 1) if os.path.exists(output_json): raise OSError('Output JSON {} already exists!'.format(output_json)) with open(output_json, 'w') as output_stream: json.dump(wad_jsons, output_stream, sort_keys=True, indent=2)