R""" Add or subtract the standard ITG sync bias (9 milliseconds) to all of the sync offsets in a pack. This script updates the offsets of both SM and SSC simfiles, including any SSC charts with their own timing data. If you actually intend to use this script in practice, you may want to keep track of which packs you've already adjusted using a text file in each pack directory or some other system. Usage examples: # Convert a pack from "null sync" to "ITG sync" python change_sync_bias.py +9 "C:\StepMania\Songs\My Pack" # Convert a pack from "ITG sync" to "null sync" python change_sync_bias.py -9 "C:\StepMania\Songs\My Pack" """ import argparse from decimal import Decimal import sys from typing import Union import simfile import simfile.dir class ChangeSyncBiasArgs: """Stores the command-line arguments for this script.""" pack: str itg_to_null: bool null_to_itg: bool def argparser(): """Get an ArgumentParser instance for this command-line script.""" parser = argparse.ArgumentParser(prefix_chars="-+") parser.add_argument("pack", type=str, help="path to the pack to modify") group = parser.add_mutually_exclusive_group(required=True) group.add_argument( "-9", "--itg-to-null", action="store_true", help="subtract 9ms from offsets" ) group.add_argument( "+9", "--null-to-itg", action="store_true", help="add 9ms to offsets" ) return parser def adjust_offset( obj: Union[simfile.types.Simfile, simfile.ssc.SSCChart], delta: Decimal, ): """Add the delta to the simfile or SSC chart's offset, if present.""" if obj.offset is not None: obj.offset = str(Decimal(obj.offset) + delta) def change_sync_bias(simfile_path: str, args: ChangeSyncBiasArgs): """ Add or subtract 9 milliseconds to the simfile's offset, as well as any SSC charts with their own timing data. This saves the updated simfile to its original location and writes a backup copy with a ~ appended to the filename. """ # Map the +9 or -9 arg to the actual offset delta. # # We don't have to check both itg_to_null and null_to_itg # because the mutually exclusive & required argument group # ensures that exactly one of them will be True. delta = Decimal("-0.009" if args.itg_to_null else "+0.009") # You could specify output_filename here to write the updated file elsewhere with simfile.mutate( input_filename=f"{simfile_path}", backup_filename=f"{simfile_path}~", ) as sf: print(f"Processing {simfile_path}") # Always adjust the simfile's offset adjust_offset(sf, delta) # Additionally try to adjust SSC charts' offsets. # This won't do anything unless the chart has its own timing data. if isinstance(sf, simfile.ssc.SSCSimfile): for chart in sf.charts: adjust_offset(chart, delta) def main(argv): # Parse command-line arguments args = argparser().parse_args(argv[1:], namespace=ChangeSyncBiasArgs()) # Iterate over SimfileDirectory objects from the pack # so that we can easily get the .sm and/or .ssc paths for simfile_dir in simfile.dir.SimfilePack(args.pack).simfile_dirs(): # Try to update whichever formats exist for simfile_path in [simfile_dir.sm_path, simfile_dir.ssc_path]: if simfile_path: change_sync_bias(simfile_path, args) if __name__ == "__main__": main(sys.argv)