1# FIXME: send fsgs as parameter to ValueConfigLoader instead 2from fsgs.context import fsgs 3import os 4import json 5from fsbc.paths import Paths 6from fsgs.amiga.amiga import Amiga 7from fsgs.filedatabase import FileDatabase 8from fsgs.network import openretro_url_prefix 9from fsgs.option import Option 10from fsgs.amiga import whdload 11 12 13class ValueConfigLoader(object): 14 DB_VERSION_MAX = 1 15 16 def __init__(self, uuid=""): 17 self.config = {} 18 self.options = {} 19 self.viewport = [] 20 self.values = {} 21 self.uuid = uuid 22 self._file_list = None 23 self._cue_sheets = None 24 if uuid: 25 self.options["database_url"] = "{0}/game/{1}".format( 26 openretro_url_prefix(), uuid 27 ) 28 29 def get_config(self): 30 return self.config.copy() 31 32 def load_values(self, values): 33 if values.get("db_version"): 34 try: 35 version = int(values.get("db_version")) 36 except ValueError: 37 version = 10000 38 if version > self.DB_VERSION_MAX: 39 self.config["__config_name"] = ( 40 "Unsupported Database " 41 "Version (Please upgrade FS-UAE Launcher)" 42 ) 43 return self.get_config() 44 45 self.values = values 46 # from pprint import pprint 47 # pprint(values) 48 49 cd_based = False 50 amiga_model = "A500" 51 platform = self.values.get("platform", "Amiga") 52 self.options["platform"] = platform.lower() 53 54 platform = platform.lower() 55 if platform == "amiga": 56 pass 57 elif platform == "cd32": 58 amiga_model = "CD32" 59 cd_based = True 60 self.options["joystick_port_1_mode"] = "cd32 gamepad" 61 elif platform == "cdtv": 62 amiga_model = "CDTV" 63 cd_based = True 64 else: 65 raise Exception("unknown platform") 66 self.config["amiga_model"] = amiga_model 67 68 # self.config["x_game_uuid"] = self.values["uuid"] 69 70 self.viewport = [] 71 # game_node = self.root.find("game") 72 # if game_node is not None: 73 # game_uuid = game_node.get("uuid", "") 74 # self.config["x_game_uuid"] = game_uuid 75 # self.load_game_info(game_uuid) 76 77 # self.load_options()) 78 79 sort_list = [] 80 for key in values: 81 if key == "chip_memory": 82 # chip memory is checked at the end because of support for 83 # the 1024+ value, and others keys can change amiga_model.. 84 sort_list.append(("x_chip_memory", key)) 85 else: 86 sort_list.append((key, key)) 87 sort_list.sort() 88 89 for dummy, key in sort_list: 90 self.load_option(key, values[key]) 91 92 if self.viewport: 93 self.options["viewport"] = ", ".join(self.viewport) 94 95 if cd_based: 96 if self.check_all_files(): 97 self.load_cdroms() 98 else: 99 self.load_floppies() 100 101 self.load_hard_drives() 102 103 # if not self.check_all_files(): 104 # print(" -- some files are missing --") 105 # self.options["x_missing_files"] = "1" 106 107 download_page = values.get("download_page", "") 108 if download_page: 109 self.options["download_page"] = download_page 110 download_file = values.get("download_file", "") 111 if download_file: 112 self.options["download_file"] = download_file 113 download_terms = values.get("download_terms", "") 114 if download_terms: 115 self.options["download_terms"] = download_terms 116 download_notice = values.get("download_notice", "") 117 if download_notice: 118 self.options["download_notice"] = download_notice 119 120 # for now, just copy all options to config without checking 121 for key, value in self.options.items(): 122 self.config[key] = value 123 124 if self.config.get(Option.X_WHDLOAD_ARGS): 125 whdload.override_config(self.config) 126 127 if ( 128 self.config.get("amiga_model", "") == "A500" 129 and self.config.get("slow_memory") == "0" 130 ): 131 self.config["amiga_model"] = "A500/512K" 132 self.config["slow_memory"] = "" 133 134 self.set_name_and_uuid() 135 self.contract_paths() 136 return self.get_config() 137 138 def check_all_files(self): 139 file_list_json = self.values.get("file_list", "[]") 140 file_list = json.loads(file_list_json) 141 for file in file_list: 142 if file["name"].endswith("/"): 143 # this is a folder 144 continue 145 # print(repr(file)) 146 if not file["sha1"]: 147 return False 148 if not fsgs.file.find_by_sha1(file["sha1"]): 149 return False 150 return True 151 152 def contract_paths(self): 153 def fix(key): 154 if self.config.get(key): 155 self.config[key] = Paths.contract_path( 156 self.config.get(key), default_dir, force_real_case=False 157 ) 158 159 default_dir = fsgs.amiga.get_floppies_dir() 160 for i in range(Amiga.MAX_FLOPPY_DRIVES): 161 fix("floppy_drive_{0}".format(i)) 162 for i in range(Amiga.MAX_FLOPPY_IMAGES): 163 fix("floppy_image_{0}".format(i)) 164 165 default_dir = fsgs.amiga.get_cdroms_dir() 166 for i in range(Amiga.MAX_CDROM_DRIVES): 167 fix("cdrom_drive_{0}".format(i)) 168 for i in range(Amiga.MAX_CDROM_IMAGES): 169 fix("cdrom_image_{0}".format(i)) 170 171 def set_name_and_uuid(self): 172 # self.config["x_config_uuid"] = self.root.get("uuid", "") 173 174 self.config["game_uuid"] = self.values.get("game_uuid", "") 175 self.config["variant_uuid"] = self.values.get("variant_uuid", "") 176 177 game_name = self.values.get("game_name", "") 178 platform_name = self.values.get("platform", "") 179 variant_name = self.values.get("variant_name", "") 180 parts = [] 181 if platform_name: 182 parts.append(platform_name) 183 if variant_name: 184 parts.append(variant_name) 185 if game_name and variant_name: 186 config_name = "{0} ({1})".format(game_name, ", ".join(parts)) 187 self.config["__config_name"] = config_name 188 else: 189 self.config["__config_name"] = self.values.get("config_name", "") 190 191 def load_option(self, key, value): 192 model = self.options.get("amiga_model", "") 193 if key in ["variant_viewport", "viewport"]: 194 if "=" in value: 195 parts = value.split(",") 196 for i in range(len(parts)): 197 parts[i] = parts[i].split("#", 1)[0].strip() 198 # parts[i] = parts[i].replace("=", "=>") 199 # parts[i] = parts[i].replace("==>", "=>") 200 value = ", ".join(parts) 201 while " " in value: 202 value = value.replace(" ", " ") 203 else: 204 value = "* * * * = " + value 205 self.viewport.append(value) 206 # if key.startswith("viewport_"): 207 # parts = key.split("_") 208 # if len(parts) == 5: 209 # parts = parts[1:] 210 # value = " ".join(parts) + " => " + value 211 # value = value.replace("x", "*") 212 # self.viewport.append(value) 213 elif key == "hd_startup": 214 self.options["hd_startup"] = value 215 if whdload.should_disable_drive_click(): 216 self.options[Option.FLOPPY_DRIVE_VOLUME_EMPTY] = "0" 217 elif key == "whdload_args": 218 self.options[Option.X_WHDLOAD_ARGS] = value 219 elif key == "whdload_quit_key": 220 self.options[Option.WHDLOAD_QUIT_KEY] = value 221 elif key == "hdinst_args": 222 self.options["x_hdinst_args"] = value 223 if whdload.should_disable_drive_click(): 224 self.options[Option.FLOPPY_DRIVE_VOLUME_EMPTY] = "0" 225 elif key == "hd_requirements": 226 self.options["hd_requirements"] = value 227 elif key == "save_disk": 228 self.options["save_disk"] = value 229 elif key == "whdload_version": 230 self.options["x_whdload_version"] = value 231 # elif key == "whdload_icon": 232 # self.options["__whdload_icon"] = value 233 elif key == "kickstart": 234 if value == "1.2": 235 self.options["amiga_model"] = "A1000" 236 elif value == "2.0": 237 if model in ["A500+", "A600"]: 238 pass 239 else: 240 self.options["amiga_model"] = "A600" 241 elif value == "2.0+": 242 if model in [ 243 "A500+", 244 "A600", 245 "A1200", 246 "A1200/020", 247 "A3000", 248 "A4000/040", 249 ]: 250 pass 251 else: 252 self.options["amiga_model"] = "A600" 253 elif value in ["3.0+", "3.1", "3.1+"]: 254 if model in ["A1200", "A1200/020", "A3000", "A4000/040"]: 255 pass 256 else: 257 self.options["amiga_model"] = "A1200" 258 elif value == "AROS": 259 self.options["kickstart_file"] = "internal" 260 else: 261 # FIXME: print warning 262 pass 263 elif key == "chipset": 264 if value == "ECS": 265 if model in ["A500+", "A600", "A3000"]: 266 pass 267 else: 268 self.options["amiga_model"] = "A600" 269 elif value == "AGA": 270 if model in ["A1200", "A1200/020", "A4000/040"]: 271 pass 272 else: 273 self.options["amiga_model"] = "A1200" 274 elif key == "cpu": 275 if value == "68020+" or value == "68020": 276 if model in ["A1200", "A1200/020", "A3000", "A4000/040"]: 277 pass 278 else: 279 self.options["amiga_model"] = "A1200" 280 elif key == "fast_memory": 281 ivalue = int(value) 282 if ivalue > 8192: 283 self.options["zorro_iii_memory"] = value 284 self.options["amiga_model"] = "A1200/020" 285 else: 286 self.options["fast_memory"] = value 287 elif key == "chip_memory": 288 model = self.options.get("amiga_model", "") 289 if value == "1024+": 290 if model in ["A1200", "A1200/020", "A4000/040"]: 291 pass 292 else: 293 self.options["chip_memory"] = "1024" 294 elif value == "2048+": 295 if model in ["A1200", "A1200/020", "A4000/040"]: 296 pass 297 else: 298 # self.options["amiga_model"] = "A1200" 299 self.options["chip_memory"] = "2048" 300 else: 301 self.options["chip_memory"] = value 302 elif key == "video_standard": 303 if value == "NTSC": 304 self.options["ntsc_mode"] = "1" 305 elif key == "cracktro": 306 # FIXME: handle 307 pass 308 elif key in [ 309 "joystick_port_0_mode", 310 "joystick_port_1_mode", 311 "joystick_port_2_mode", 312 "joystick_port_3_mode", 313 "joystick_port_4_mode", 314 ]: 315 self.load_joystick_port_x_mode_option(key, value) 316 elif key in [ 317 "amiga_model", 318 "accuracy", 319 "cdrom_drive_0_delay", 320 "floppy_drive_count", 321 "slow_memory", 322 "front_sha1", 323 "screen1_sha1", 324 "screen2_sha1", 325 "screen3_sha1", 326 "screen4_sha1", 327 "screen5_sha1", 328 "title_sha1", 329 "year", 330 "publisher", 331 "developer", 332 "hol_url", 333 "lemon_url", 334 "wikipedia_url", 335 "mobygames_url", 336 "whdload_url", 337 "amigamemo_url", 338 "longplay_url", 339 "thelegacy_url", 340 "homepage_url", 341 "languages", 342 "dongle_type", 343 ]: 344 self.options[key] = value 345 elif key == "requirements": 346 if "wb" in value.lower(): 347 self.options["hard_drive_0"] = "hd://template/workbench/DH0" 348 self.options["hard_drive_0_priority"] = "6" 349 elif "hd" in value.lower(): 350 self.options["hard_drive_0"] = "hd://template/empty/DH0" 351 elif key == "players": 352 self.options["players"] = value 353 elif key == "protection": 354 self.options["protection"] = value 355 elif key == "game_notice": 356 self.options["x_game_notice"] = value 357 elif key == "variant_notice": 358 self.options["x_variant_notice"] = value 359 elif key == "variant_warning": 360 self.options["x_variant_warning"] = value 361 elif key == "variant_error": 362 self.options["x_variant_error"] = value 363 elif key == "joy_emu_conflict": 364 self.options["x_joy_emu_conflict"] = value 365 # elif key == "languages": 366 # self.options["x_languages"] = value 367 368 def load_joystick_port_x_mode_option(self, key, value): 369 value = value.lower() 370 if "," not in value: 371 self.options[key] = value 372 return 373 parts = value.split(",") 374 assert "=" not in parts[0] 375 self.options[key] = parts[0] 376 port = ["0", "1", "2", "3", "4"].index(key[14]) 377 for part in parts[1:]: 378 k, v = part.split("=") 379 k = k.strip() 380 v = v.strip() 381 if not v.startswith("action_"): 382 v = "action_" + v 383 k = "joystick_port_{0}_{1}".format(port, k) 384 self.options[k] = v 385 386 def get_file_list(self): 387 if self._file_list is None: 388 file_list_json = self.values.get("file_list", "[]") 389 self._file_list = json.loads(file_list_json) 390 return self._file_list 391 392 def get_cue_sheets(self): 393 if self._cue_sheets is None: 394 cue_sheets_json = self.values.get("cue_sheets", "[]") 395 self._cue_sheets = json.loads(cue_sheets_json) 396 return self._cue_sheets 397 398 def build_media_list(self, floppies=False, cds=False, hds=False): 399 media_list = [] 400 added = set() 401 for file_item in self.get_file_list(): 402 name = file_item["name"] 403 url = file_item.get("url", "") 404 405 if name.startswith("DH0/"): 406 if hds: 407 # p = os.path.join(self.path, "HardDrive") 408 p = "hd://game/" + self.uuid + "/DH0" 409 if p in added: 410 # already added 411 continue 412 added.add(p) 413 # FIXME: hack for now 414 sha1 = self.values.get("dh0_sha1", "") 415 media_list.append((p, sha1)) 416 else: 417 continue 418 419 sha1 = file_item["sha1"] 420 base, ext = os.path.splitext(name) 421 ext = ext.lower() 422 423 if hds: 424 if ext not in [".zip"]: 425 continue 426 elif cds: 427 if ext not in [".cue", ".iso"]: 428 continue 429 if "(Track" in base: 430 # probably part of a split multi-track cue 431 continue 432 elif floppies: 433 if ext not in [".adf", ".adz", ".dms", ".ipf"]: 434 continue 435 436 path = "" 437 found_sha1 = "" 438 if sha1: 439 print(sha1) 440 file = FileDatabase.get_instance().find_file(sha1=sha1) 441 if file: 442 found_sha1 = sha1 443 path = file["path"] 444 if url and not path: 445 path = url 446 found_sha1 = sha1 447 448 if path: 449 media_list.append((path, found_sha1)) 450 else: 451 pass 452 # return False 453 # FIXME: handle it with a visible error message 454 # raise Exception("could not find file " + repr(name)) 455 return media_list 456 457 def load_floppies_from_floppy_list(self): 458 media_list = [] 459 for item in self.values.get("floppy_list").split(","): 460 name, sha1 = item.split(":") 461 name = name.strip() 462 sha1 = sha1.strip() 463 path = "sha1://{0}/{1}/{2}".format(sha1, "", name) 464 media_list.append((path, sha1)) 465 return media_list 466 467 def load_floppies(self): 468 floppy_drive_count = 4 469 if "floppy_drive_count" in self.options: 470 try: 471 floppy_drive_count = int(self.options["floppy_drive_count"]) 472 except ValueError: 473 floppy_drive_count = 1 474 floppy_drive_count = max(0, min(4, floppy_drive_count)) 475 476 if self.values.get("floppy_list", ""): 477 media_list = self.load_floppies_from_floppy_list() 478 else: 479 media_list = self.build_media_list(floppies=True) 480 481 for i, values in enumerate(media_list): 482 path, sha1 = values 483 if i < floppy_drive_count: 484 self.config["floppy_drive_{0}".format(i)] = path 485 self.config["x_floppy_drive_{0}_sha1".format(i)] = sha1 486 self.config["floppy_image_{0}".format(i)] = path 487 self.config["x_floppy_image_{0}_sha1".format(i)] = sha1 488 if floppy_drive_count < 4: 489 self.config["floppy_drive_count"] = floppy_drive_count 490 491 def cdrom_sha1_to_uri(self, sha1): 492 for file_item in self.get_file_list(): 493 if file_item["sha1"] == sha1: 494 return "game://{}/{}".format(self.uuid, file_item["name"]) 495 raise Exception("cdrom_sha1_to_uri: could not find " + sha1) 496 497 def load_cdroms(self): 498 # media_list = self.build_media_list(cds=True) 499 cue_sheets = self.get_cue_sheets() 500 if cue_sheets: 501 return self.load_cdroms_from_cue_sheets(cue_sheets) 502 cue = False 503 ccd = False 504 # iso = False 505 for file_item in self.get_file_list(): 506 if file_item["name"].endswith(".cue"): 507 cue = True 508 if file_item["name"].endswith(".ccd"): 509 ccd = True 510 # if file_item["name"].endswith(".iso"): 511 # iso = True 512 media_list = [] 513 if ccd: 514 ext = ".ccd" 515 elif cue: 516 ext = ".cue" 517 else: 518 ext = ".iso" 519 for file_item in self.get_file_list(): 520 if file_item["name"].endswith(ext): 521 media_list.append((file_item["name"], file_item["sha1"])) 522 print("load_cdroms media_list =", media_list) 523 for i, values in enumerate(media_list): 524 path, sha1 = values 525 path = self.cdrom_sha1_to_uri(sha1) 526 if i < 1: 527 self.config["cdrom_drive_{0}".format(i)] = path 528 self.config["x_cdrom_drive_{0}_sha1".format(i)] = sha1 529 self.config["cdrom_image_{0}".format(i)] = path 530 self.config["x_cdrom_image_{0}_sha1".format(i)] = sha1 531 532 def load_cdroms_from_cue_sheets(self, cue_sheets): 533 for i, item in enumerate(cue_sheets): 534 path = "game://{}/{}".format(self.uuid, item["name"]) 535 if i < 1: 536 self.config["cdrom_drive_{0}".format(i)] = path 537 self.config["cdrom_image_{0}".format(i)] = path 538 539 def load_hard_drives(self): 540 media_list = self.build_media_list(hds=True) 541 print(media_list) 542 for i, values in enumerate(media_list): 543 path, sha1 = values 544 self.config["hard_drive_{0}".format(i)] = path 545 self.config["x_hard_drive_{0}_sha1".format(i)] = sha1 546