-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathslupdate.py
executable file
·654 lines (580 loc) · 29.4 KB
/
slupdate.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
#!/usr/bin/env python3
""" slupdate.py: Interactively Update Optical Media based Software Lists
against[Redump](http://redump.org/) dats.
https://github.com/ldolse/slupdate
"""
import os
import re
import sys
import inquirer
import builtins
try:
# get the script location directory to ensure settings are saved and update environment var
script_dir = os.path.abspath(os.path.dirname(__file__))
except:
# set it to current working dir for this scenario, which is most likely when running from interpreter
script_dir = os.getcwd()
# bit of a hack to pass the script dir to the chd module
builtins.script_dir = script_dir
from modules.utils import save_data,restore_dict,convert_xml
from modules.dat import *
from modules.chd import *
from modules.mapping import *
__version__ = '.1'
# Require at least Python 3.2
assert sys.version_info >= (3, 2)
settings = restore_dict('settings')
user_answers = restore_dict('user_answers')
softlist_dict = {}
dat_dict = {}
# disabled by default, allows the script to populate chd sha1s on subsequent runs
# only enable if CHD destination folder ONLY contains chds created by this script
get_sha_from_existing_chd = False
mapping_stage = { 'source_map' : [],
'name_serial_map' : [],
'tosec_remap' : [],
'manual_map' : []
}
consoles = { 'Amiga CDTV' : 'cdtv',
'Amiga CD32' : 'cd32',
'FM Towns CD' : 'fmtowns_cd',
'IBM PC/AT CD-ROM' : 'ibm5170_cdrom',
'NEC PC-9801 CD-ROMs' : 'pc98_cd',
'PC Engine / TurboGrafx CD' : 'pcecd',
'Philips CD-i' : 'cdi',
'Pippin CD-ROMs' : 'pippin',
'NEC PC-FX' : 'pcfx',
'Sega CD' : 'segacd',
'Sega Mega CD (Europe)' : 'megacd',
'Sega Mega CD (Japan)' : 'megacdj',
'Sega Saturn' : 'saturn',
'Sega Dreamcast' : 'dc',
'SNK NeoGeo CD' : 'neocd',
'Sony Playstation' : 'psx',
'3DO' : '3do_m2'
}
# key for the menu correlates to the key for the menu's item list
menu_msgs = {'0' : 'Main Menu, select an option',
'map' : 'These functions will process software lists and dat files, mapping source files to build chds',
'map-2' : 'Next step after auto-mapping',
'2' : 'Choose a console to build CHD\'s for current match list',
'3' : 'Choose an assisted mapping task to identify other sources for softlist entries',
'new' : 'This function will look for DAT entries which don\'t appear in software lists and assist with creating Software List records, continue?',
'5' : 'MAME hash directory and dat directories for at least one platform must be configured.',
'save' : 'Save asisted mapping answers and other user generated data? This will overwrite anything previously written to disk',
'2a' : 'Begin building CHDs?',
'2b' : 'Update the Software List with new hashes?',
'4a' : 'Do you want to create alternate softlist entries for both redump and TOSEC, or do you want to avoid duplicating entries? In order to avoid duplicating entries TOSEC sourced entries must first be matched to redump.',
'soft' : 'Please select the MAME Software List XML directory',
'chd' : 'CHD Builder Destination Directory',
'dat' : 'DAT Source Directories',
'rom' : 'ROM Source Directories',
'5b' : 'Select a console to configure ',
'5c' : 'Select a DAT to remove',
'dir_d' : 'Select a Directory to Remove',
'romvault' : 'Are you using ROMVault to manage DATs and ROMs?'
}
# unless a list entry maps to a function it should always map to another menu in the tree
# This allows completed functions to go back to their parent menu automatically
menu_lists = {'0' : [('1. Mapping Functions', 'map'),
('2. CHD Builder', '2'),
#('3. Assisted Title/Disc Mapping', '3'),
#('4. Create New Entries','entry_create_function'),
('5. Settings','5'),
#('6. Save Session','save_function'),
('7. Exit', 'Exit')],
'map' : [('a. Automatically map based on source rom info','automap_function'),
#('b. Map Based on Disc Serial & Name','name_serial_automap_function'),
#('c. Remap entries with TOSEC sources to Redump','tosec_map_function'),
#('d. Map entries with no source reference to Redump','no_src_map_function'),
('e. Back', '0')],
'map-2' : [('a. List missing DAT entries','list_missing_function'),
('b. Build CHDs','chd_build_function'),
#('c. Remap entries with TOSEC sources to Redump','tosec_map_function'),
#('d. Map entries with no source reference to Redump','no_src_map_function'),
('e. Back', '0')],
'2' : [('a. Console List','chd_build_function'),
('b. Back', '0')],
'3' : [('Map entries with no source information to Redump sources','no_src_map_function'),
('Remap entries with TOSEC sources to Redump sources','tosec_map_function'),
('Back', '0')],
'5' : [('a. MAME Software List XML Directory', 'slist_dir_function'),
('b. Configure Root DAT/ROM Directories (ROMvault)', 'root_dirs_function'),
('c. Configure DAT/ROM Platform Directories', 'dat'),
('d. Destination folder for CHDs', 'chd_dir_function'),
('e. Back', '0')],
'dat' : [('Add Directories','platform_dat_rom_function'),
('Remove DATs','del_dats_function'),
('Back', '5')],
}
def list_missing_function(platform):
get_missing_zips(softlist_dict[platform],dat_dict[platform])
return '0'
def first_run():
print('Please Configure the MAME Softlist hash directory location.\n')
slist_dir_function()
print('\nPlease Confgure the root directory locations for your DAT and ROM files')
print('This will be used to simplify navigation for DAT and ROM directory selection')
print('RomVault compatibility can also be enabled here to automate ROM directory configuration\n')
root_dirs_function()
print('\nPlease configure DATs for the first Platform (more can be configured later)\n')
platform = platform_select('dat')
platform_dat_rom_function(platform['platforms'])
print('\nPlease configure the destination directory for created CHDs - note this directory should not contain CHDs from other sources')
chd_dir_function()
def main_menu(exit):
'''
return a list answer value from any function called by this menu to get back to the
chosen message / list based on the menu_msgs and menu_lists dicts
'''
global settings
# any menus that have functions which should send the current platform are added here
send_platform = ('dat','rom','map','map-2')
menu_sel = '0'
while not exit:
print('\n')
answer = list_menu(menu_sel,menu_lists[menu_sel],menu_msgs[menu_sel])
if any(file in answer.values() for file in send_platform):
platform = platform_select(answer[menu_sel])
if answer[menu_sel].endswith('function'):
if any(f in answer for f in send_platform):
next_step = globals()[answer[menu_sel]](platform['platforms'])
if next_step:
# next menu chosen based on return value from the function
menu_sel = next_step
else:
# return to the previous menu after completing the function
menu_sel = list(answer)[0]
else:
globals()[answer[menu_sel]]()
# return to the previous menu after completing the function
menu_sel = list(answer)[0]
elif menu_sel == '5' and answer[menu_sel] == '0':
# save settings when exiting settings and returning to main menu
save_data(settings,'settings',script_dir)
menu_sel = answer[menu_sel]
elif answer[menu_sel] == 'Exit':
exit = True
return exit
else:
menu_sel = answer[menu_sel]
def list_menu(key, options, prompt):
optconfirm = [
inquirer.List(key,
message = prompt,
choices = options,
carousel = True),
]
answer = inquirer.prompt(optconfirm)
return answer
def find_dat_matches(platform,sl_platform_dict,dathash_platform_dict):
'''
matches source hash fingerprints to the dat fingerprint dicts
updates the softlist dict to point to the dat for that source
'''
for datfile, dathashdict in dathash_platform_dict['hashes'].items():
dat_group = get_dat_group(datfile)
for sl_title, sl_data in sl_platform_dict.items():
dat_name_list = []
chds_exist = False
for disc, disc_data in sl_data['parts'].items():
if 'source_rom' in disc_data:
continue # skip when a source ROM was identified from an earlier DAT
if 'chd_filename' in disc_data:
chd_path = settings['chd']+os.sep+platform+os.sep+sl_title+os.sep+disc_data['chd_filename']+'.chd'
if os.path.isfile(chd_path):
# add chd path to a list, check for unique files later
disc_data.update({'chd_found':True})
chds_exist = True
# get source hash key based on crc or sha
if 'source_sha' in disc_data:
sourcehash = disc_data['source_sha']
else:
continue
if sourcehash in dathashdict:
# add the dat source to the entry
disc_data['source_dat'] = datfile
# add the dat group to the entry
disc_data['source_group'] = dat_group
dat_name_list.append(dathashdict[sourcehash]['name'])
if 'softlist_matches' not in dathashdict[sourcehash]:
dathashdict[sourcehash]['softlist_matches'] = []
dathashdict[sourcehash]['softlist_matches'].append(sl_title)
# set boolean flag at the softlist level to flag a match
sl_platform_dict[sl_title].update({'source_found':True})
# pop this from the redump list to enable future mapping of remaining entries to redump
if dathashdict[sourcehash]['name'] in dathash_platform_dict['redump_unmatched'][datfile]:
dathash_platform_dict['redump_unmatched'][datfile].pop(dathashdict[sourcehash]['name'])
# check to see if there are valid zips for this softlist entry, creates 'source_rom' key(s) if so
zip_name = find_rom_zips(datfile,sl_data,dathashdict,settings[platform])
if zip_name:
dat_zips = dict(zip(dat_name_list,zip_name))
print('\nMatch Found:\n Softlist: '+sl_data['description'])
for datname,zipname in dat_zips.items():
print(' Dat: '+datname+'\n Zip: '+zipname)
if chds_exist:
print(' CHD(s) for this title found')
# Count the total number of softlist entries
total_softlist_entries = len(sl_platform_dict)
# count the total number of entries with source references
total_source_ref = sum(1 for softlist_entry in sl_platform_dict.values() for part in softlist_entry['parts'].values() if 'source_sha' in part)
# Count the number of entries where 'source_found' is True
total_source_found = sum(1 for softlist_entry in sl_platform_dict.values() if softlist_entry['source_found'])
# Count the total number of CHDs found
chd_count = sum(1 for softlist_entry in sl_platform_dict.values() for part in softlist_entry['parts'].values() if 'chd_found' in part and part['chd_found'])
# Count the total number of 'parts' across all entries
total_parts = sum(len(softlist_entry['parts']) for softlist_entry in sl_platform_dict.values())
# Count the number of parts that have a 'source_dat' entry
total_source_dat = sum(1 for softlist_entry in sl_platform_dict.values() for part in softlist_entry['parts'].values() if 'source_dat' in part)
# Count the number of parts that have a 'source_rom' entry
total_source_rom = len(list(part['source_rom'] for softlist_entry in sl_platform_dict.values() for part in softlist_entry['parts'].values() if 'source_rom' in part))
print(f'found:\n {total_source_ref} / {total_parts} individual discs contain source references')
print(f' {total_source_dat} individual discs can be matched to dat sources')
print(f' {total_source_found} / {total_softlist_entries} Software List Entries have DAT matches')
print(f' {total_source_rom} valid zip files')
print(f' {chd_count} chds already exist in the destination directory\n')
print('\nDAT Groups:')
# get the stats on source groups
source_stats = get_source_stats(sl_platform_dict)
print_source_stats(source_stats,total_source_ref)
def get_configured_platforms(action_type):
'''
Builds a tuple list of the configured platforms
action_type variable is based on what type pre-config
for the sub-function
'''
configured = []
for name, platform in consoles.items():
if platform in settings and len(settings[platform]) > 0:
configured.append((name,platform))
return configured
def platform_select(list_type):
if list_type == 'dat':
platforms = [(k, v) for k, v in consoles.items()]
else:
platforms = get_configured_platforms(list_type)
answer = list_menu('platforms', platforms, menu_msgs['5b']+menu_msgs[list_type])
return answer
# placeholder functions
def no_src_map_function(platform):
print('no source mapping placeholder')
def tosec_map_function(platform):
print('tosec to redump placeholder')
# flag that this stage is completed for this platform
#if platform not in mapping_stage['tosec_remap']:
# mapping_stage['tosec_remap'].append(platform)
def entry_create_function(platform):
print('new entry placeholder')
proceed = inquirer.confirm(menu_msgs['new'], default=False)
def automap_function(platform):
# process each DAT to build a list of fingerprints
print('processing '+platform+' DAT Files')
if platform not in dat_dict:
dat_dict.update({platform:{}})
for dat in settings[platform]:
raw_dat_dict = convert_xml(dat)
build_dat_dict(dat,raw_dat_dict['datafile'],dat_dict[platform])
# hashes may be identical across DAT groups, prioritise redump hashes and delete dupes in others
remove_dupe_dat_entries(dat_dict[platform])
# process the software list into a dict, creating hash based fingerprints from comments
print('processing '+platform+' software list')
process_comments = True
raw_sl_dict = convert_xml(settings['sl_dir']+os.sep+platform+'.xml',process_comments)
softdict = dict(raw_sl_dict['softwarelist'])
if platform not in softlist_dict:
softlist_dict.update({platform:{}})
# build the dict object with relevant softlist data for this script, return bool whether crc source keys are needed
build_sl_dict(softdict['software'], softlist_dict[platform])
# iterate through each fingerprint in the software list and search for matching hashes
find_dat_matches(platform,softlist_dict[platform],dat_dict[platform])
# flag that this stage is completed for this platform
if platform not in mapping_stage['source_map']:
mapping_stage['source_map'].append(platform)
'''
print('Next step will be to map entries based on name and disc serial number')
print('This will require connecting to redump once for each platform to get additional info')
print('If you want to proceed just based on the hash matches already found then this')
print('step can be skipped')
name_serial = inquirer.confirm('Begin Serial/Name Mapping?', default=False)
if name_serial:
name_serial_automap_function(platform)
'''
# map-2 menu prompts the user to build chds or list missing dat entries
return 'map-2'
def name_serial_automap_function(platform):
from modules.mapping import name_serial_map
name_serial_map(platform, softlist_dict[platform],dat_dict[platform])
# flag that this stage is completed for this platform
if platform not in mapping_stage['name_serial_map']:
mapping_stage['name_serial_map'].append(platform)
def chd_builder(platform):
'''
checks each soft list entry for a matched source rom and builds chds using those ROM
sources. CHD hash is added to the soft-dict. If a CHD already exists in the build
directory it's skipped, but there is a flag to enable grabbing hashes for built CDs.
'''
new_hashes = False
built_sources = {}
discontinue = False
for soft, soft_data in softlist_dict[platform].items():
if discontinue:
break
for disc_data in soft_data['parts'].values():
if 'source_rom' in disc_data:
# create platform directory
if not os.path.exists(os.path.join(settings['chd'],platform)):
os.mkdir(os.path.join(settings['chd'],platform))
chd_dir = os.path.join(settings['chd'],platform,soft)
if not os.path.exists(chd_dir):
os.mkdir(chd_dir)
chd_name = disc_data['chd_filename']+'.chd'
chd_path = os.path.join(chd_dir,chd_name)
if not os.path.isfile(chd_path):
print('\nbuilding chd for '+soft_data['description']+':')
print(' CHD: '+chd_name)
print(' Source Zip: '+os.path.basename(disc_data['source_rom']))
if disc_data['source_rom'] not in built_sources:
'''
check the dat group here for any special handling that will be needed
known things to handle:
- Redump and cdi - need to rewrite the cue file (todo)
- No-Intro - Cue file data doesn't match filenames (partial support)
'''
dat_group = get_dat_group(disc_data['source_dat'])
special_logic = {'dat_group':dat_group}
if dat_group == 'no-intro':
game_entry = dat_dict[platform]['hashes'][disc_data['source_dat']][disc_data['source_sha']]
special_logic.update(game_entry)
elif dat_group == 'redump' and platform == 'cdi':
# placeholder
pass
else:
special_logic = None
try:
error = create_chd_from_zip(disc_data['source_rom'],chd_path,settings,special_logic)
if not error:
built_sources.update({disc_data['source_rom']:chd_path})
else:
print(error)
continue
except:
print('CHD Creation Failed')
if os.path.isfile(chd_path):
try:
os.remove(chd_path)
except:
print('Failed to delete partial file:\n'+chd_path)
print('Please ensure this is deleted to avoid corrupted files/hashes')
continue_build = inquirer.confirm('Do you want to continue?', default=False)
if continue_build:
continue
else:
discontinue = True
break
# if the exact same chd was built earlier then just symlink to it
# these symlinks aren't cross platform, will revisit this
elif disc_data['source_rom'] in built_sources:
os.symlink(built_sources[disc_data['source_rom']],chd_path)
built_sources.update({disc_data['source_rom']:chd_path})
else:
continue
#print('chd for '+soft_data['description']+' already exists, skipping')
# if the chd was created as a part of this run or if the flag to trust existing chds is enabled check the sha1 against the softlist
if os.path.isfile(chd_path):
if disc_data['source_rom'] in built_sources or get_sha_from_existing_chd:
new_chd_hash = chdman_info(chd_path)
if new_chd_hash == disc_data['chd_sha1']:
print('\nHash matches softlist: '+chd_name+'\n')
else:
new_hashes = True
print('\nUpdated hash for softlist: '+chd_name+'\n')
disc_data.update({'new_sha1':new_chd_hash})
else:
continue
if new_hashes:
write_new_hashes = inquirer.confirm('Update the Software List with new CHD Hashes?', default=False)
if write_new_hashes:
update_softlist_chd_sha1s(settings['sl_dir']+os.sep+platform+'.xml',softlist_dict[platform])
def chd_build_function(platform=None):
if not is_greater_than_0_176(chdman_info()):
print('Outdated Chdman, please upgrade to a recent version')
return None
if not platform:
# get configured platforms and map selected from the returned key
platform = platform_select('chd')['platforms']
if platform not in softlist_dict:
print('No mapping has been run for this platform yet, please go back and run a mapping function\n')
return None
build = inquirer.confirm('Begin Creating CHDs?', default=False)
if build:
chd_builder(platform)
def save_function():
confirm_message = menu_msgs['save']
save = inquirer.confirm(confirm_message, default=False)
if save:
save_data(user_answers,'answers',script_dir)
return
def restore_function():
confirm_message = menu_msgs['restore']
load = inquirer.confirm(confirm_message, default=False)
if load:
user_answers = restore_dict('user_answers')
'''
directory selection functions
'''
def root_dirs_function():
single_dir_function('datroot','Root DAT Directory')
single_dir_function('romroot','Root ROM Directory')
confirm_message = menu_msgs['romvault']
romvault = inquirer.confirm(confirm_message, default=False)
settings.update({'romvault' : romvault})
def del_dats_function(platform):
datlist = []
for dat in settings[platform].keys():
print('deleting '+settings['datroot']+'\nfrom '+dat)
datlist.append(dat.replace(settings['datroot'], ''))
datlist.append('back')
answer = list_menu('dat', datlist, menu_msgs['5c'])
if answer['dat'] == 'back':
return
else:
dat_path = settings['datroot']+answer['dat']
print(dat_path)
settings[platform].pop(dat_path)
def slist_dir_function():
single_dir_function('sl_dir','MAME Software List')
def chd_dir_function():
single_dir_function('chd','CHD Destination Directory')
single_dir_function('zip_temp','Temporary Directory for uncompressed ZIP data')
def single_dir_function(dirtype,prompt):
# queries and stores the software list hash directory
directory = select_directory(prompt)
settings.update({dirtype : directory})
def get_platform_dir(dirtype,platform):
if dirtype not in settings[platform]:
settings[platform].update({dirtype:[]})
directory = select_directory(dirtype)
if directory not in settings[platform][dirtype]:
settings[platform][dirtype].append(directory)
def romvault_dat_to_romfolder(dat_directory,dat_files):
'''
RomVault maps the ROM directories based on the file structure in DATroot
DAT directories with multiple DATs will create subfolders based on the DAT title
returns a dict with the DAT to ROM folder mapping
'''
#print('dat dir is '+dat_directory)
#rom_dir = settings['romroot']+re.sub(settings['datroot'],'',dat_directory)
rom_dir = settings['romroot']+dat_directory.replace(settings['datroot'], '')
#print('rom dir is '+rom_dir)
if len(dat_files) == 1:
if os.path.isdir(rom_dir):
return {dat_directory+os.sep+dat_files[0] : rom_dir}
else:
print('Unable to locate '+rom_dir+' directory in ROMroot')
if inquirer.confirm('Do you want to create the appropriate directory?', default=False):
os.makedirs(rom_dir)
return {dat_directory+os.sep+dat_files[0] : rom_dir}
else:
return {}
else:
dat_rom_dict = {}
for dat in dat_files:
if not dat.endswith('.xml'):
dat_path = dat_directory+os.sep+dat
# rom subfolder is based on dat name, get name
print('checking dat '+dat)
name = get_dat_name(dat_path)
full_rom_dir = rom_dir+os.sep+name
if not os.path.isdir(full_rom_dir):
print('Unable to locate "'+name+'" directory in platform ROM folder')
if inquirer.confirm('Do you want to create the appropriate directory?', default=False):
os.makedirs(full_rom_dir)
else:
continue
dat_rom_dict.update({dat_path:full_rom_dir})
return dat_rom_dict
def platform_dat_rom_function(platform):
if platform not in settings:
settings.update({platform : {}})
# get the dat dir first
if 'datroot' not in settings:
single_dir_function('datroot','Root DAT Directory')
dat_directory = select_directory('dat',settings['datroot'])
dat_files = [f for f in os.listdir(dat_directory) if f.endswith('.dat') or f.endswith('.xml')]
print(f"DAT files in {dat_directory}: {dat_files}")
# if using romvault map the ROM directores for the DATs automatically
if settings['romvault']:
datrom_dirmap = romvault_dat_to_romfolder(dat_directory, dat_files)
else:
datrom_dirmap = {}
if 'romroot' not in settings:
single_dir_function('romroot','Root ROM Directory')
for dat in dat_files:
if not dat.endswith('.xml'):
dat_path = dat_directory+os.sep+dat
print('Select ROM Directory for DAT:\n'+dat+'\n')
rom_directory = select_directory('rom',settings['romroot'])
datrom_dirmap.update({dat_path:rom_directory})
settings[platform].update(datrom_dirmap)
def get_os_dirs(path):
"""
Returns a list of directories in the given path
"""
directories = [d for d in os.listdir(path) if os.path.isdir(os.path.join(path, d))]
directories.sort()
directories.insert(0,'Parent Directory')
directories.append('Select the current directory')
return directories
def get_start_dir(filetype=None):
start_path = None
while not start_path:
path_query = [
inquirer.Path(name='path', message=filetype+" Path (or starting point to browse filesystem)")]
path_entry = inquirer.prompt(path_query)
# remove any trailing slash, if the user enters
pattern = os.sep+'$'
path_entry = re.sub(pattern,'',path_entry['path'])
if not os.path.exists(path_entry):
print('invalid path, try again')
# could potentially count failures and switch to working dir
#current_path = os.getcwd()
continue
else:
start_path = path_entry
return start_path
def select_directory(filetype=None,start_dir=None):
"""
Displays a list of directories in the current directory and prompts the user to select one
"""
selected = False
origin_path = os.getcwd()
if not start_dir:
current_path = get_start_dir(filetype)
else:
current_path = start_dir
while not selected:
questions = [
inquirer.List(filetype,
message = "Select a directory - current: ["+current_path+"]",
choices = get_os_dirs(current_path))
]
answers = inquirer.prompt(questions)
if answers[filetype] == 'Select the current directory':
selected = True
os.chdir(origin_path)
return current_path
elif answers[filetype] == 'Parent Directory':
current_path = os.path.dirname(current_path)
os.chdir(os.path.dirname(current_path))
else:
parent_path = current_path
current_path = current_path+os.sep+answers[filetype]
os.chdir(current_path)
if __name__ == '__main__':
if len(settings) == 0:
# walk through all the mandatory settings one by one on the first run
first_run()
save_data(settings,'settings',script_dir)
complete = False
while not complete:
complete = main_menu(complete)