''' 2021-06-04 I had hoped to be able to Make & Modify CIV IV World Builder Files I did not get very far # # # Created on Mar 8, 2018 @author: Brett Paufler Copyright Brett Paufler This is messy dictionary of lists but it shall be for another module to clean up parse_worldbuildersave returns a single dictionary: version = # map = {} = single dictionary game = {} players = [{}, {}...] = list of dictionaries teams = [{}, {}...] signs = [{}, {}...] plots = [{}, {}...] units cities units and cities are sub entries of plots ''' import re from os import listdir from os.path import join as path_join #from skimage.io import imsave def get_wb_save_files(): '''Returns list of path to all World Builder save files in input directory.''' dir_in = '.\\input\\' save_files = [ path_join(dir_in, f) for f in listdir(dir_in) if f.endswith('CivBeyondSwordWBSave')] return save_files def preprocess_text(text_raw): '''Preprocesses text to clean it up make it more in keeping with my mindset.''' #Left align all text text_in = '\n'.join( line.strip() for line in text_raw.split('\n')) #List of Numerous Text Replacements #One entry per line #Every entry a key=value pair text_replacements = [ #Newlines (',y=', '\ny='), (', StartingY=', '\nStartingY='), (', UnitOwner=', '\nUnitOwner='), (', Experience=', '\nExperience='), #Condense Civic Options to one line ('CivicOption=CIVICOPTION_', ''), (', Civic=CIVIC_', '='), #Condenses Espionage to one line ('EspionageTeam=', 'EspionageTeam_'), (', EspionageAmount', ''), #Condenses Attitude to one Attribute ('AttitudePlayer=', 'AttitudePlayer_'), (', AttitudeExtra=', '='), #Condenses Terrain type to one item (', FeatureVariety=', '_'), #Convert Singleton Values to Tuples ('OfRiver', 'OfRiver=True'), ('Sleep', 'Sleep=True'), ('StartingPlot', 'StartingPlot=') ] #Loop to effect the text replacment for old_text, new_text in text_replacements: text_in = text_in.replace( old_text, new_text) return text_in def find_sections(working_text, section): '''Returns a list comprised of the text contained in each section of named type, If section='Plot', a list comprehensive listing of all plots for the game is returned So, a list of text, each bit of text being between the noted tag types''' begin = 'Begin%s' % section end = 'End%s' % section pattern = re.compile( '%s(.*?)%s' % (begin, end), re.IGNORECASE | re.DOTALL) section_list = re.findall(pattern, working_text) return section_list def parse_section(section_text): '''parses each section into a dictionary Given a single slice of raw section_text from find_section converts into a dictionary ''' section_dict = dict() section_text = section_text.strip() section_text = section_text.split('\n') for line in section_text: if line == '': break #print file_path #print line #print section_text key, value = line.strip().split('=') #If key already in use, #Converts values into a list if key in section_dict: if type(section_dict[key]) != type([]): value_to_list = [section_dict.get(key)] section_dict[key] = value_to_list section_dict[key].append(value) else: section_dict[key] = value return section_dict def section_to_list_of_dicts( working_text, section='Player'): '''Returns a list of section item dictionaries A list of dictionaries is returned One dictionary per pair of section tags Each dictonary contains all items betwen tags''' #Creates a List of Text for each Section Tag section_list = find_sections( working_text, section) #We are returning a list of dictionaries list_of_dicts = [] for section_text in section_list: units = None city = None #Plots are the only nested section type if section == 'Plot': if 'BeginUnit' in section_text: units = section_to_list_of_dicts( section_text, 'Unit') # -1 removes preceeding newlin begin = -1 + section_text.find('BeginUnit') # +7 removes 'EndUnit' in slice end = 7 + section_text.rfind('EndUnit') section_text = section_text[:begin] + section_text[end:] if 'BeginCity' in section_text: city = section_to_list_of_dicts( section_text, 'City') #magic numbers same as above begin = -1 + section_text.find('BeginCity') end = 7 + section_text.rfind('EndCity') section_text = section_text[:begin] + section_text[end:] #Standard work #Everything else is for plots section_dict = parse_section(section_text) #Add unit/cit dictonary back into plot if units: section_dict['Units'] = units if city: section_dict['City'] = city list_of_dicts.append(section_dict) return list_of_dicts def parse_worldbuildersave(file_path): '''Parses WorldBuilderSave into Dictionary of Lists See top of module for more information ''' with open(file_path) as f: text_raw = f.read() text_in = preprocess_text(text_raw) mapping = { 'version': re.findall( 'Version=(\d+)', text_in)[0], 'map': section_to_list_of_dicts( text_in, 'Map')[0], 'game': section_to_list_of_dicts( text_in, 'Game')[0], 'players': section_to_list_of_dicts( text_in, 'Player'), 'teams': section_to_list_of_dicts( text_in, 'Team'), 'plots': section_to_list_of_dicts( text_in, 'Plot'), 'signs': section_to_list_of_dicts( text_in, 'Sign') } return mapping def print_all(mapping): print mapping['version'] print mapping['map'] print mapping['game'] for player in mapping['players']: print player for team in mapping['teams']: print team for plot in mapping['plots']: print plot for sign in mapping['signs']: print sign def test(): save_files = get_wb_save_files() for file_path in save_files: print file_path mapping = parse_worldbuildersave(file_path) #print_all(mapping) ''' for plot in mapping['plots']: if plot['PlotType'] == '3': #not in ['0', '1', '2']: print plot['TerrainType'] ''' if __name__ == '__main__': test() #file_path = '.\\input\\WorldBuilderSave.CivBeyondSwordWBSave' #mapping = parse_worldbuildersave(file_path) #print_all(mapping)