import argparse from decimal import Decimal import sys from typing import Union import simfile from simfile.dir import SimfilePack 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: # 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 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)