view pytouhou/options.py @ 791:a29122662cde

utils: Simplify translate_2d and align Mat4 to 16 bytes This lowers the amount of instructions from 61 to 32 on PowerPC with AltiVec, and from 25 to 14 on amd64 with AVX2.
author Link Mauve <linkmauve@linkmauve.fr>
date Sat, 17 Jan 2026 14:19:58 +0100
parents 317e93b7d586
children
line wrap: on
line source

# -*- encoding: utf-8 -*-
##
## Copyright (C) 2014 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published
## by the Free Software Foundation; version 3 only.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##

import os
from configparser import RawConfigParser, NoOptionError

from pytouhou.utils.xdg import load_config_paths, save_config_path


class Options:
    def __init__(self, name, defaults):
        load_paths = list(reversed([os.path.join(directory, '%s.cfg' % name)
                                    for directory
                                    in load_config_paths(name)]))
        self.save_path = os.path.join(save_config_path(name), '%s.cfg' % name)

        self.config = RawConfigParser(defaults)
        self.paths = self.config.read(load_paths)
        self.section = name if self.config.has_section(name) else 'DEFAULT'

    def get(self, option):
        try:
            return self.config.get(self.section, option)
        except NoOptionError:
            return None

    def set(self, option, value):
        if value is not None:
            self.config.set(self.section, option, value)
        else:
            self.config.remove_option(self.section, option)

        defaults = self.config._defaults
        self.config._defaults = None
        with open(self.save_path, 'w') as save_file:
            self.config.write(save_file)
        self.config._defaults = defaults


def patch_argument_parser():
    from argparse import ArgumentParser, _ActionsContainer

    original_method = _ActionsContainer.add_argument

    def add_argument(self, *args, **kwargs):
        if 'default' not in kwargs:
            dest = kwargs.get('dest')
            if dest is None:
                for dest in args:
                    dest = dest.lstrip('-')
                    value = self.default.get(dest)
                    if value is not None:
                        break
            else:
                dest = dest.replace('_', '-')
                value = self.default.get(dest)
            if value is not None:
                argument_type = kwargs.get('type')
                if argument_type is not None:
                    value = argument_type(value)
                action = kwargs.get('action')
                if action == 'store_true':
                    value = value.lower() == 'true'
                elif action == 'store_false':
                    value = value.lower() != 'true'
                if kwargs.get('nargs') == '*' and isinstance(value, str):
                    value = value.split()
                kwargs['default'] = value
            elif dest == 'double-buffer':
                kwargs['default'] = None
        return original_method(self, *args, **kwargs)
    _ActionsContainer.add_argument = add_argument

    class Parser(ArgumentParser):
        def __init__(self, *args, **kwargs):
            self.default = kwargs.pop('default')
            ArgumentParser.__init__(self, *args, **kwargs)

        def add_argument_group(self, *args, **kwargs):
            group = ArgumentParser.add_argument_group(self, *args, **kwargs)
            group.default = self.default
            group.add_argument_group = self.add_argument_group
            group.add_mutually_exclusive_group = self.add_mutually_exclusive_group
            return group

        def add_mutually_exclusive_group(self, *args, **kwargs):
            group = ArgumentParser.add_mutually_exclusive_group(self, *args, **kwargs)
            group.default = self.default
            group.add_argument_group = self.add_argument_group
            group.add_mutually_exclusive_group = self.add_mutually_exclusive_group
            return group

    return Parser


ArgumentParser = patch_argument_parser()


def parse_config(section, defaults):
    return Options(section, defaults)


def parse_arguments(defaults):
    parser = ArgumentParser(description='Libre reimplementation of the Touhou 6 engine.', default=defaults)

    parser.add_argument('data', metavar='DAT', nargs='*', help='Game’s data files')
    parser.add_argument('-p', '--path', metavar='DIRECTORY', help='Game directory path.')
    parser.add_argument('--debug', action='store_true', help='Set unlimited continues, and perhaps other debug features.')
    parser.add_argument('--verbosity', metavar='VERBOSITY', choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'], help='Select the wanted logging level.')

    game_group = parser.add_argument_group('Game options')
    game_group.add_argument('-s', '--stage', metavar='STAGE', type=int, help='Stage, 1 to 7 (Extra), nothing means story mode.')
    game_group.add_argument('-r', '--rank', metavar='RANK', type=int, help='Rank, from 0 (Easy, default) to 3 (Lunatic).')
    game_group.add_argument('-c', '--character', metavar='CHARACTER', type=int, help='Select the character to use, from 0 (ReimuA, default) to 3 (MarisaB).')
    game_group.add_argument('-b', '--boss-rush', action='store_true', help='Fight only bosses.')
    game_group.add_argument('--game', metavar='GAME', help='Select the game engine to use.')
    game_group.add_argument('--interface', metavar='INTERFACE', help='Select the interface to use.')
    game_group.add_argument('--hints', metavar='HINTS', help='Hints file, to display text while playing.')

    replay_group = parser.add_argument_group('Replay options')
    replay_group.add_argument('--replay', metavar='REPLAY', help='Select a file to replay.')
    replay_group.add_argument('--save-replay', metavar='REPLAY', help='Save the upcoming game into a replay file.')
    replay_group.add_argument('--skip-replay', action='store_true', help='Skip the replay and start to play when it’s finished.')

    netplay_group = parser.add_argument_group('Netplay options')
    netplay_group.add_argument('--port', metavar='PORT', type=int, help='Local port to use.')
    netplay_group.add_argument('--remote', metavar='REMOTE', help='Remote address.')
    netplay_group.add_argument('--friendly-fire', action='store_true', help='Allow friendly-fire during netplay.')

    graphics_group = parser.add_argument_group('Graphics options')
    graphics_group.add_argument('--frontend', metavar='FRONTEND', choices=['glfw', 'sdl'], help='Which windowing library to use (glfw or sdl).')
    graphics_group.add_argument('--backend', metavar='BACKEND', choices=['opengl', 'glide', 'sdl'], nargs='*', help='Which backend to use (opengl, glide or sdl).')
    graphics_group.add_argument('--fps-limit', metavar='FPS', type=int, help='Set fps limit. A value of 0 disables fps limiting, while a negative value limits to 60 fps if and only if vsync doesn’t work.')
    graphics_group.add_argument('--frameskip', metavar='FRAMESKIP', type=int, help='Set the frameskip, as 1/FRAMESKIP, or disabled if 0.')
    graphics_group.add_argument('--no-background', action='store_false', help='Disable background display (huge performance boost on slow systems).')
    graphics_group.add_argument('--no-particles', action='store_false', help='Disable particles handling (huge performance boost on slow systems).')
    graphics_group.add_argument('--no-sound', action='store_false', help='Disable music and sound effects.')

    opengl_group = parser.add_argument_group('OpenGL backend options')
    opengl_group.add_argument('--gl-flavor', choices=['core', 'es', 'compatibility', 'legacy'], help='OpenGL profile to use.')
    opengl_group.add_argument('--gl-version', type=float, help='OpenGL version to use.')

    double_buffer = opengl_group.add_mutually_exclusive_group()
    double_buffer.add_argument('--double-buffer', dest='double_buffer', action='store_true', help='Enable double buffering.')
    double_buffer.add_argument('--single-buffer', dest='double_buffer', action='store_false', help='Disable double buffering.')

    return parser.parse_args()