diff --git a/src/gex/lib/tasks/impl/mvscc/__init__.py b/src/gex/lib/tasks/impl/mvscc/__init__.py new file mode 100644 index 0000000..0f2af66 --- /dev/null +++ b/src/gex/lib/tasks/impl/mvscc/__init__.py @@ -0,0 +1,235 @@ +'''Implementation of mvscc: MARVEL vs. CAPCOM Fighting Collection: Arcade Classics''' +import logging +import os + +from gex.lib.archive import arc +from gex.lib.utils.vendor import capcom +from gex.lib.tasks.basetask import BaseTask +from gex.lib.tasks import helpers + +logger = logging.getLogger('gextoolbox') + +class MVSCCTask(BaseTask): + '''Implements mvscc: MARVEL vs. CAPCOM Fighting Collection: Arcade Classics''' + _task_name = "mvscc" + _title = "MARVEL vs. CAPCOM Fighting Collection: Arcade Classics" + _details_markdown = ''' +This is reverse-engineered based on the CBEYB work from https://web.archive.org/web/20220213232104/http://blog.livedoor.jp/scrap_a/archives/23114141.html. +This was a Japanese set of shell scripts and odd generic operation executables. There is some weird encoding here too. + +This script will extract and prep the ROMs. Some per-rom errata are in the notes below. All CRCs are currently mismatched. + +TODO: Implement the rest of the games: The Punisher and MvC2. +''' + _default_input_folder = helpers.gen_steam_app_default_folder("MARVEL vs. CAPCOM Fighting Collection: Arcade Classics") + _input_folder_desc = "MVSCC Steam folder" + + def get_out_file_info(self): + '''Return a list of output files''' + return { + "files": self._metadata['out']['files'], + "notes": self._metadata['out']['notes'] + } + + def execute(self, in_dir, out_dir): + # for each output file entry + for out_file_entry in self._metadata['out']['files']: + pkg_name = out_file_entry['in_file'] + # Check the status of it + if out_file_entry['status'] == 'no-rom': + logger.info(f"Skipping {pkg_name} - cannot extract...") + else: + logger.info(f"Extracting {pkg_name}...") + + # read the matching input file + in_file_entry = self._metadata['in']['files'][pkg_name] + loaded_file = self.read_datafile(in_dir, in_file_entry) + + # extract the input file + arc_contents = arc.extract(loaded_file['contents']) + + # Get the bin entry + merged_rom_contents = None + for _, arc_content in arc_contents.items(): + if arc_content['path'].startswith('bin'): + merged_rom_contents = arc_content['contents'] + + # run the handler + handler_func = self.find_handler_func(pkg_name) + if merged_rom_contents is not None and handler_func is not None: + output_contents = handler_func(merged_rom_contents) + + _ = self.verify_out_file(out_file_entry['filename'], output_contents) + + with open(os.path.join(out_dir, out_file_entry['filename']), "wb") as out_file: + out_file.write(output_contents) + elif merged_rom_contents is None: + logger.warning("Could not find merged rom data in arc.") + elif handler_func is None: + logger.warning("Could not find matching handler function.") + logger.info("Processing complete.") + + ################################################################################ + # X-Men: Children of the Atom # + ################################################################################ + def _handle_xmcotau(self, merged_contents): + _xmn_gfx_filenames = [f'xmn.{i}m' for i in range(13, 21)] + _xmn_audiocpu_filenames = [ + 'xmn.01a', + 'xmn.02a' + ] + _vam_qsound_filenames = [ + 'xmn.11m', + 'xmn.12m' + ] + func_map = {} + maincpu_filenames = ["xmnu.03e","xmnu.04e"] + [f"xmn.{i:02}a" for i in range(5, 11)] + func_map['maincpu'] = capcom.maincpu_cps2( + 0x40, 0x400000, 8, maincpu_filenames) + func_map['gfx'] = capcom.gfx_cps2(_xmn_gfx_filenames, helpers.slice_helper( + 0x0800040, length=0x2000000), split=[0x400000, 0x400000]) + func_map['audiocpu'] = capcom.audiocpu_cps2( + 0x2800040, _xmn_audiocpu_filenames) + func_map['qsound'] = capcom.qsound_cps2( + 0x2850040, 0x400000, _vam_qsound_filenames) + + return helpers.build_rom(merged_contents, func_map) + + ################################################################################ + # Marvel Super Heroes # + ################################################################################ + def _handle_mshh(self, merged_contents): + gfx_filenames = [f'msh.{i}m' for i in range(13, 21)] + audiocpu_filenames = [ + 'msh.01', + 'msh.02' + ] + qsound_filenames = [ + 'msh.11m', + 'msh.12m' + ] + func_map = {} + maincpu_filenames = [ + "mshh.03c", + "mshh.04c", + "msh.05a", + "msh.06b", + "msh.07a", + "msh.08a", + "msh.09a", + "msh.10b", + ] + func_map['maincpu'] = capcom.maincpu_cps2( + 0x40, 0x400000, 8, maincpu_filenames) + func_map['gfx'] = capcom.gfx_cps2(gfx_filenames, helpers.slice_helper( + 0x0800040, length=0x2000000), split=[0x400000, 0x400000]) + func_map['audiocpu'] = capcom.audiocpu_cps2( + 0x2800040, audiocpu_filenames) + func_map['qsound'] = capcom.qsound_cps2( + 0x2850040, 0x400000, qsound_filenames) + + return helpers.build_rom(merged_contents, func_map) + + ################################################################################ + # X-Men Vs. Street Fighter # + ################################################################################ + def _handle_xmvsfu(self, merged_contents): + gfx_filenames = [f'xvs.{i}m' for i in range(13, 21)] + audiocpu_filenames = [ + 'xvs.01', + 'xvs.02' + ] + qsound_filenames = [ + 'xvs.11m', + 'xvs.12m' + ] + func_map = {} + maincpu_filenames = [ + "xvsu.03k", + "xvsu.04k", + "xvs.05a", + "xvs.06a", + "xvs.07", + "xvs.08", + "xvs.09", + ] + func_map['maincpu'] = capcom.maincpu_cps2( + 0x40, 0x400000, 8, maincpu_filenames) + func_map['gfx'] = capcom.gfx_cps2(gfx_filenames, helpers.slice_helper( + 0x0800040, length=0x2000000), split=[0x400000, 0x400000]) + func_map['audiocpu'] = capcom.audiocpu_cps2( + 0x2800040, audiocpu_filenames) + func_map['qsound'] = capcom.qsound_cps2( + 0x2850040, 0x400000, qsound_filenames) + + return helpers.build_rom(merged_contents, func_map) + + ################################################################################ + # Marvel Super Heroes vs. Street Fighter # + ################################################################################ + def _handle_mshvsfu(self, merged_contents): + gfx_filenames = [f'mvs.{i}m' for i in range(13, 21)] + audiocpu_filenames = [ + 'mvs.01', + 'mvs.02' + ] + qsound_filenames = [ + 'mvs.11m', + 'mvs.12m' + ] + func_map = {} + maincpu_filenames = [ + "mvsu.03g", + "mvsu.04g", + "mvs.05d", + "mvs.06a", + "mvs.07b", + "mvs.08a", + "mvs.09b", + "mvs.10b", + ] + func_map['maincpu'] = capcom.maincpu_cps2( + 0x40, 0x400000, 8, maincpu_filenames) + func_map['gfx'] = capcom.gfx_cps2(gfx_filenames, helpers.slice_helper( + 0x0800040, length=0x2000000), split=[0x400000, 0x400000]) + func_map['audiocpu'] = capcom.audiocpu_cps2( + 0x2800040, audiocpu_filenames) + func_map['qsound'] = capcom.qsound_cps2( + 0x2850040, 0x800000, qsound_filenames) + + return helpers.build_rom(merged_contents, func_map) + + ################################################################################ + # Marvel vs. Capcom: Clash of Super Heroes # + ################################################################################ + def _handle_mvscu(self, merged_contents): + gfx_filenames = [f'mvc.{i}m' for i in range(13, 21)] + audiocpu_filenames = [ + 'mvc.01', + 'mvc.02' + ] + qsound_filenames = [ + 'mvc.11m', + 'mvc.12m' + ] + func_map = {} + maincpu_filenames = [ + "mvcu.03d", + "mvcu.04d", + "mvc.05a", + "mvc.06a", + "mvc.07", + "mvc.08", + "mvc.09", + "mvc.10", + ] + func_map['maincpu'] = capcom.maincpu_cps2( + 0x40, 0x400000, 8, maincpu_filenames) + func_map['gfx'] = capcom.gfx_cps2(gfx_filenames, helpers.slice_helper( + 0x0800040, length=0x2000000), split=[0x400000, 0x400000]) + func_map['audiocpu'] = capcom.audiocpu_cps2( + 0x2800040, audiocpu_filenames) + func_map['qsound'] = capcom.qsound_cps2( + 0x2850040, 0x800000, qsound_filenames) + + return helpers.build_rom(merged_contents, func_map) diff --git a/src/gex/lib/tasks/impl/mvscc/metadata.json b/src/gex/lib/tasks/impl/mvscc/metadata.json new file mode 100644 index 0000000..f03f9ca --- /dev/null +++ b/src/gex/lib/tasks/impl/mvscc/metadata.json @@ -0,0 +1,217 @@ +{ + "in": { + "files": { + "xmcotau": { + "rel_path": ["nativeDX11x64", "arc", "pc"], + "filename": "game_01.arc", + "versions": { + "Steam": { + "size": 16880099, + "crc": "9308D9C8" + } + } + }, + "mshh": { + "rel_path": ["nativeDX11x64", "arc", "pc"], + "filename": "game_11.arc", + "versions": { + "Steam": { + "size": 17054449, + "crc": "C7FD9AD7" + } + } + }, + "xmvsfu": { + "rel_path": ["nativeDX11x64", "arc", "pc"], + "filename": "game_21.arc", + "versions": { + "Steam": { + "size": 16657278, + "crc": "8011E464" + } + } + }, + "mshvsfu": { + "rel_path": ["nativeDX11x64", "arc", "pc"], + "filename": "game_31.arc", + "versions": { + "Steam": { + "size": 20105362, + "crc": "863150A2" + } + } + }, + "mvscu": { + "rel_path": ["nativeDX11x64", "arc", "pc"], + "filename": "game_41.arc", + "versions": { + "Steam": { + "size": 19679177, + "crc": "DF6AC377" + } + } + } + } + }, + "out": { + "files": [ + { + "game": "X-Men: Children of the Atom", + "system": "Arcade", + "filename": "xmcotau.zip", + "in_file": "xmcotau", + "status": "playable", + "verify": { + "type": "zip", + "entries": { + "xmn.01a": { "size": 131072, "crc": "40F479EA" }, + "xmn.02a": { "size": 131072, "crc": "39D9B5AD" }, + "xmnu.03e": { "size": 524288, "crc": "0BAFEB0E" }, + "xmnu.04e": { "size": 524288, "crc": "C29BDAE3" }, + "xmn.06a": { "size": 524288, "crc": "1B86A328" }, + "xmn.07a": { "size": 524288, "crc": "2C142A44" }, + "xmn.08a": { "size": 524288, "crc": "F712D44F" }, + "xmn.09a": { "size": 524288, "crc": "9241CAE8" }, + "xmn.11m": { "size": 2097152, "crc": "C848A6BC" }, + "xmn.12m": { "size": 2097152, "crc": "729C188F" }, + "xmn.13m": { "size": 4194304, "crc": "BF4DF073" }, + "xmn.14m": { "size": 4194304, "crc": "778237B7" }, + "xmn.15m": { "size": 4194304, "crc": "4D7E4CEF" }, + "xmn.16m": { "size": 4194304, "crc": "67B36948" }, + "xmn.17m": { "size": 4194304, "crc": "513EEA17" }, + "xmn.18m": { "size": 4194304, "crc": "015A7C4C" }, + "xmn.19m": { "size": 4194304, "crc": "D23897FC" }, + "xmn.20m": { "size": 4194304, "crc": "9DDE2758" } + } + } + }, + { + "game": "Marvel Super Heroes", + "system": "Arcade", + "filename": "mshh.zip", + "in_file": "mshh", + "status": "playable", + "verify": { + "type": "zip", + "entries": { + "msh.01": { "size": 131072, "crc": "C976E6F9" }, + "msh.02": { "size": 131072, "crc": "CE67D0D9" }, + "mshh.03c": { "size": 524288, "crc": "8D84B0FA" }, + "mshh.04c": { "size": 524288, "crc": "D638F601" }, + "msh.05a": { "size": 524288, "crc": "F37539E6" }, + "msh.06b": { "size": 524288, "crc": "803E3FA4" }, + "msh.07a": { "size": 524288, "crc": "C45F8E27" }, + "msh.08a": { "size": 524288, "crc": "9CA6F12C" }, + "msh.09a": { "size": 524288, "crc": "82EC27AF" }, + "msh.10b": { "size": 524288, "crc": "8D931196" }, + "msh.11m": { "size": 2097152, "crc": "37AC6D30" }, + "msh.12m": { "size": 2097152, "crc": "DE092570" }, + "msh.13m": { "size": 4194304, "crc": "09D14566" }, + "msh.14m": { "size": 4194304, "crc": "4197973E" }, + "msh.15m": { "size": 4194304, "crc": "EE962057" }, + "msh.16m": { "size": 4194304, "crc": "438DA4A0" }, + "msh.17m": { "size": 4194304, "crc": "604ECE14" }, + "msh.18m": { "size": 4194304, "crc": "4DB92D94" }, + "msh.19m": { "size": 4194304, "crc": "94A731E8" }, + "msh.20m": { "size": 4194304, "crc": "A2B0C6C0" } + } + } + }, + { + "game": "X-Men Vs. Street Fighter", + "system": "Arcade", + "filename": "xmvsfu.zip", + "in_file": "xmvsfu", + "status": "playable", + "verify": { + "type": "zip", + "entries": { + "xvs.01": { "size": 131072, "crc": "3999E93A" }, + "xvs.02": { "size": 131072, "crc": "101BDEE9" }, + "xvsu.03k": { "size": 524288, "crc": "8739EF61" }, + "xvsu.04k": { "size": 524288, "crc": "E11D35C1" }, + "xvs.05a": { "size": 524288, "crc": "7DB6025D" }, + "xvs.06a": { "size": 524288, "crc": "E8E2C75C" }, + "xvs.07": { "size": 524288, "crc": "08F0ABED" }, + "xvs.08": { "size": 524288, "crc": "81929675" }, + "xvs.09": { "size": 524288, "crc": "9641F36B" }, + "xvs.11m": { "size": 2097152, "crc": "9CADCDBC" }, + "xvs.12m": { "size": 2097152, "crc": "7B11E460" }, + "xvs.13m": { "size": 4194304, "crc": "F6684EFD" }, + "xvs.14m": { "size": 4194304, "crc": "BCAC2E41" }, + "xvs.15m": { "size": 4194304, "crc": "29109221" }, + "xvs.16m": { "size": 4194304, "crc": "EA04A272" }, + "xvs.17m": { "size": 4194304, "crc": "92DB3474" }, + "xvs.18m": { "size": 4194304, "crc": "B0DEF86A" }, + "xvs.19m": { "size": 4194304, "crc": "3733473C" }, + "xvs.20m": { "size": 4194304, "crc": "4B40FF9f" } + } + } + }, + { + "game": "Marvel Super Heroes vs. Street Fighter", + "system": "Arcade", + "filename": "mshvsfu.zip", + "in_file": "mshvsfu", + "status": "playable", + "verify": { + "type": "zip", + "entries": { + "mvs.01": { "size": 131072, "crc": "68252324" }, + "mvs.02": { "size": 131072, "crc": "b34e773d" }, + "mvsu.03g": { "size": 524288, "crc": "0664ab15" }, + "mvsu.04g": { "size": 524288, "crc": "97e060ee" }, + "mvs.05d": { "size": 524288, "crc": "921fc542" }, + "mvs.06a": { "size": 524288, "crc": "959f3030" }, + "mvs.07b": { "size": 524288, "crc": "7f915bdb" }, + "mvs.08a": { "size": 524288, "crc": "c2813884" }, + "mvs.09b": { "size": 524288, "crc": "3ba08818" }, + "mvs.10b": { "size": 524288, "crc": "cf0dba98" }, + "mvs.11m": { "size": 4194304, "crc": "86219770" }, + "mvs.12m": { "size": 4194304, "crc": "f2fd7f68" }, + "mvs.13m": { "size": 4194304, "crc": "29b05fd9" }, + "mvs.14m": { "size": 4194304, "crc": "b3b1972d" }, + "mvs.15m": { "size": 4194304, "crc": "faddccf1" }, + "mvs.16m": { "size": 4194304, "crc": "08aadb5d" }, + "mvs.17m": { "size": 4194304, "crc": "97aaf4c7" }, + "mvs.18m": { "size": 4194304, "crc": "c1228b35" }, + "mvs.19m": { "size": 4194304, "crc": "cb70e915" }, + "mvs.20m": { "size": 4194304, "crc": "366cc6c2" } + } + } + }, + { + "game": "Marvel vs. Capcom: Clash of Super Heroes", + "system": "Arcade", + "filename": "mvscu.zip", + "in_file": "mvscu", + "status": "playable", + "verify": { + "type": "zip", + "entries": { + "mvc.01": { "size": 131072, "crc": "41629e95" }, + "mvc.02": { "size": 131072, "crc": "963abf6b" }, + "mvcu.03d": { "size": 524288, "crc": "c6007557" }, + "mvcu.04d": { "size": 524288, "crc": "724b2b20" }, + "mvc.05a": { "size": 524288, "crc": "2d8c8e86" }, + "mvc.06a": { "size": 524288, "crc": "8528e1f5" }, + "mvc.07": { "size": 524288, "crc": "c3baa32b" }, + "mvc.08": { "size": 524288, "crc": "bc002fcd" }, + "mvc.09": { "size": 524288, "crc": "c67b26df" }, + "mvc.10": { "size": 524288, "crc": "0fdd1e26" }, + "mvc.11m": { "size": 4194304, "crc": "850fe663" }, + "mvc.12m": { "size": 4194304, "crc": "7ccb1896" }, + "mvc.13m": { "size": 4194304, "crc": "fa5f74bc" }, + "mvc.14m": { "size": 4194304, "crc": "7f1df4e4" }, + "mvc.15m": { "size": 4194304, "crc": "71938a8f" }, + "mvc.16m": { "size": 4194304, "crc": "90bd3203" }, + "mvc.17m": { "size": 4194304, "crc": "92741d07" }, + "mvc.18m": { "size": 4194304, "crc": "67aaf727" }, + "mvc.19m": { "size": 4194304, "crc": "bcb72fc6" }, + "mvc.20m": { "size": 4194304, "crc": "8b0bade8" } + } + } + } + ] + } +}