506 lines
21 KiB
Python
506 lines
21 KiB
Python
import argparse
|
|
from os import sep, getcwd
|
|
from os.path import join, exists, basename, dirname, expanduser
|
|
from sys import argv, version_info
|
|
from re import match
|
|
from pprint import pformat
|
|
|
|
if version_info[0] >= 3:
|
|
from configparser import RawConfigParser
|
|
else:
|
|
from ConfigParser import RawConfigParser
|
|
|
|
class Configuration(RawConfigParser):
|
|
|
|
default_project_file_rel_path = "config"
|
|
default_resource_paths = [".", "resource"]
|
|
|
|
def __init__(self, project_file_rel_path=None, resource_path=None, type_declarations=None):
|
|
RawConfigParser.__init__(self)
|
|
self.project_file_rel_path = project_file_rel_path
|
|
self.resource_path = resource_path
|
|
self.modifiable = {}
|
|
self.order = []
|
|
self.set_type_declarations(type_declarations)
|
|
self.set_defaults()
|
|
self.read_project_config_file()
|
|
self.merge_command_line()
|
|
self.modify_defaults()
|
|
self.print_debug(self)
|
|
|
|
def set_type_declarations(self, type_declarations):
|
|
if type_declarations is None:
|
|
type_declarations = TypeDeclarations()
|
|
self.type_declarations = type_declarations
|
|
|
|
def translate_path(self, path):
|
|
new = ""
|
|
if path and path[0] == sep:
|
|
new += sep
|
|
return expanduser("{0}{1}".format(new, join(*path.split(sep))))
|
|
|
|
def set_defaults(self):
|
|
add_section = self.add_section
|
|
set_option = self.set
|
|
section = "setup"
|
|
add_section(section)
|
|
set_option(section, "package-root", basename(getcwd()), False)
|
|
set_option(section, "additional-packages", "", False)
|
|
set_option(section, "title", "", False)
|
|
set_option(section, "classifiers", "", False)
|
|
set_option(section, "resource-search-path", "./, resource/", False)
|
|
set_option(section, "installation-dir", "/usr/local/share/games/", False)
|
|
set_option(section, "changelog", "changelog", False)
|
|
set_option(section, "description-file", "", False)
|
|
set_option(section, "init-script", "", False)
|
|
set_option(section, "version", "", False)
|
|
set_option(section, "summary", "", False)
|
|
set_option(section, "license", "", False)
|
|
set_option(section, "platforms", "", False)
|
|
set_option(section, "contact-name", "", False)
|
|
set_option(section, "contact-email", "", False)
|
|
set_option(section, "url", "", False)
|
|
set_option(section, "requirements", "", False)
|
|
set_option(section, "main-object", "pgfw/Game.py", False)
|
|
set_option(section, "resource-path-identifier", "resource_path", False)
|
|
set_option(section, "special-char-placeholder", "_", False)
|
|
set_option(section, "whitespace-placeholder", "-", False)
|
|
set_option(section, "windows-dist-path", "dist/win/", False)
|
|
set_option(section, "windows-icon-path", "", False)
|
|
set_option(section, "boolean-true-lowercase", "yes, true, t, 1", False)
|
|
set_option(section, "osx-includes", "", False)
|
|
section = "display"
|
|
add_section(section)
|
|
set_option(section, "dimensions", "480, 360", False)
|
|
set_option(section, "frame-duration", "40", False)
|
|
set_option(section, "wait-duration", "2", False)
|
|
set_option(section, "caption", "", False)
|
|
set_option(section, "centered", "yes", False)
|
|
set_option(section, "icon-path", "", False)
|
|
set_option(section, "skip-frames", "no", False)
|
|
set_option(section, "fullscreen", "no", False)
|
|
set_option(section, "windowed-flag", "wi", False)
|
|
set_option(section, "show-framerate", "no", False)
|
|
set_option(section, "framerate-display-flag", "fr", False)
|
|
set_option(section, "framerate-text-size", "16", False)
|
|
set_option(section, "framerate-text-color", "0, 0, 0", False)
|
|
set_option(section, "framerate-text-background", "255, 255, 255", False)
|
|
set_option(section, "framerate-position", "-1, 0", False)
|
|
set_option(section, "use-framebuffer", "no", False)
|
|
section = "input"
|
|
add_section(section)
|
|
set_option(section, "release-suffix", "-release", False)
|
|
set_option(section, "confirm-quit", "no", False)
|
|
section = "sprite"
|
|
add_section(section)
|
|
set_option(section, "transparent-color", "magenta", False)
|
|
section = "screen-captures"
|
|
add_section(section)
|
|
set_option(section, "rel-path", "caps", False)
|
|
set_option(section, "file-name-format", "%Y%m%d%H%M%S", False)
|
|
set_option(section, "file-extension", "png", False)
|
|
section = "video-recordings"
|
|
add_section(section)
|
|
set_option(section, "enable", "no", False)
|
|
set_option(section, "rel-path", "vids", False)
|
|
set_option(section, "directory-name-format", "%Y%m%d%H%M%S", False)
|
|
set_option(section, "file-extension", "png", False)
|
|
set_option(section, "frame-format", "RGB", False)
|
|
set_option(section, "framerate", "40", False)
|
|
set_option(section, "temp-directory", "", False)
|
|
set_option(section, "record-audio", "yes", False)
|
|
set_option(section, "filename-digits", "6", False)
|
|
section = "mouse"
|
|
add_section(section)
|
|
set_option(section, "visible", "yes", False)
|
|
set_option(section, "double-click-time-limit", ".5", False)
|
|
section = "keys"
|
|
add_section(section)
|
|
set_option(section, "up", "K_UP", False)
|
|
set_option(section, "right", "K_RIGHT", False)
|
|
set_option(section, "down", "K_DOWN", False)
|
|
set_option(section, "left", "K_LEFT", False)
|
|
set_option(section, "capture-screen", "K_F9", False)
|
|
set_option(section, "toggle-fullscreen", "K_F11", False)
|
|
set_option(section, "reset-game", "K_F8", False)
|
|
set_option(section, "record-video", "K_F10", False)
|
|
set_option(section, "volume-down", "K_F1", False)
|
|
set_option(section, "volume-up", "K_F2", False)
|
|
set_option(section, "volume-mute", "K_F3", False)
|
|
set_option(section, "toggle-interpolator", "K_F7", False)
|
|
set_option(section, "toggle-audio-panel", "K_F6", False)
|
|
section = "joy"
|
|
add_section(section)
|
|
set_option(section, "advance", "7", False)
|
|
set_option(section, "pause", "7", False)
|
|
set_option(section, "select", "6", False)
|
|
set_option(section, "single-xy", "no", False)
|
|
set_option(section, "delay-axis", "0", False)
|
|
set_option(section, "vertical-axis", "1", False)
|
|
set_option(section, "horizontal-axis", "0", False)
|
|
section = "event"
|
|
add_section(section)
|
|
set_option(section, "user-event-id", "USEREVENT", False)
|
|
set_option(section, "command-id-offset", "1", False)
|
|
set_option(section, "command-key", "command", False)
|
|
set_option(section, "cancel-flag-key", "cancel", False)
|
|
section = "audio"
|
|
add_section(section)
|
|
set_option(section, "sfx-default-path", "~/storage/audio/sfx/default", False)
|
|
set_option(section, "sfx-repository-path", "~/storage/audio/sfx/all", False)
|
|
set_option(section, "sfx-project-path", "sfx", False)
|
|
set_option(section, "sfx-extensions", "wav, ogg, mp3", False)
|
|
set_option(section, "bgm-repository-path", "~/storage/audio/bgm", False)
|
|
set_option(section, "bgm-project-path", "bgm", False)
|
|
set_option(section, "sfx-volume", "1.0", False)
|
|
set_option(section, "bgm-volume", "1.0", False)
|
|
set_option(section, "volume", "1.0", False)
|
|
set_option(section, "panel-enabled", "no", False)
|
|
set_option(section, "panel-font", None, False)
|
|
set_option(section, "auto-load", "yes", False)
|
|
section = "interpolator-gui"
|
|
add_section(section)
|
|
set_option(section, "margin", "80", False)
|
|
set_option(section, "marker-color", "255, 0, 0", False)
|
|
set_option(section, "marker-size", "11", False)
|
|
set_option(section, "curve-color", "0, 255, 0", False)
|
|
set_option(section, "label-size", "16", False)
|
|
set_option(section, "label-precision", "2", False)
|
|
set_option(section, "axis-label-count", "8", False)
|
|
set_option(section, "prompt-size", "380, 60", False)
|
|
set_option(section, "prompt-border-color", "255, 0, 0", False)
|
|
set_option(section, "prompt-border-width", "3", False)
|
|
set_option(section, "prompt-character-limit", "21", False)
|
|
set_option(section, "prompt-text-size", "42", False)
|
|
set_option(section, "template-nodeset", "L 0 0, 1000 1", False)
|
|
set_option(section, "template-nodeset-name", "template", False)
|
|
set_option(section, "flat-y-range", "1", False)
|
|
|
|
def add_section(self, name):
|
|
if name not in self.order:
|
|
self.order.append(name)
|
|
RawConfigParser.add_section(self, name)
|
|
|
|
def set(self, section, option, value, modifiable=True):
|
|
if modifiable:
|
|
if section not in self.order:
|
|
self.order.append(section)
|
|
if section not in self.modifiable:
|
|
self.modifiable[section] = []
|
|
if option not in self.modifiable[section]:
|
|
self.modifiable[section].append(option)
|
|
RawConfigParser.set(self, section, option, value)
|
|
|
|
def read_project_config_file(self):
|
|
path = self.locate_project_config_file()
|
|
if path:
|
|
fp = open(path)
|
|
self.set_modifiable(fp)
|
|
fp.seek(0)
|
|
self.read_file(fp)
|
|
fp.seek(0)
|
|
self.set_order(fp)
|
|
fp.close()
|
|
else:
|
|
self.print_debug("No configuration file found")
|
|
|
|
def locate_project_config_file(self):
|
|
rel_path = self.project_file_rel_path
|
|
if not rel_path:
|
|
rel_path = self.default_project_file_rel_path
|
|
if exists(rel_path) and not self.is_shared_mode():
|
|
return rel_path
|
|
if self.resource_path:
|
|
installed_path = join(self.resource_path, rel_path)
|
|
if exists(installed_path):
|
|
return installed_path
|
|
|
|
def set_order(self, fp):
|
|
self.order = order = []
|
|
for line in open(self.locate_project_config_file()):
|
|
result = match("^\s*\[(.*)\]\s*$", line)
|
|
if result:
|
|
order.append(result.group(1))
|
|
|
|
def set_modifiable(self, fp):
|
|
config = RawConfigParser()
|
|
config.read_file(fp)
|
|
modifiable = self.modifiable
|
|
for section in config._sections:
|
|
if section not in modifiable:
|
|
modifiable[section] = []
|
|
for option in config._sections[section]:
|
|
if option != "__name__" and option not in modifiable[section]:
|
|
modifiable[section].append(option)
|
|
|
|
def is_shared_mode(self):
|
|
return "-s" in argv
|
|
|
|
def print_debug(self, statement):
|
|
if self.is_debug_mode():
|
|
print(statement)
|
|
|
|
def is_debug_mode(self):
|
|
return "-d" in argv
|
|
|
|
def modify_defaults(self):
|
|
self.set_installation_path()
|
|
self.set_resource_search_path()
|
|
self.set_screen_captures_path()
|
|
self.set_video_recordings_path()
|
|
self.set_data_exclusion_list()
|
|
self.set_requirements()
|
|
|
|
def set_installation_path(self):
|
|
self.set("setup", "installation-path",
|
|
join(self.get("setup", "installation-dir"),
|
|
self.get("setup", "package-root")), False)
|
|
|
|
def set_resource_search_path(self):
|
|
section, option = "setup", "resource-search-path"
|
|
search_path = self.get(section, option)
|
|
if self.resource_path:
|
|
search_path.append(self.resource_path)
|
|
else:
|
|
search_path.append(self.get("setup", "installation-path"))
|
|
self.set(section, option, search_path, False)
|
|
|
|
def get(self, section, option):
|
|
value = RawConfigParser.get(self, section, option)
|
|
if value is None:
|
|
value = self.get_substitute(section, option)
|
|
return self.cast_value(section, option, value)
|
|
|
|
def get_substitute(self, section, option):
|
|
if section == "display":
|
|
if option == "caption":
|
|
return self.get("setup", "title")
|
|
|
|
def cast_value(self, section, option, value):
|
|
pair = section, option
|
|
types = self.type_declarations
|
|
# if type(value) == str or type(value) == unicode:
|
|
if type(value) == str:
|
|
if pair in types["bool"]:
|
|
return value.lower() in self.get("setup", "boolean-true-lowercase")
|
|
elif pair in types["int"]:
|
|
return int(value)
|
|
elif pair in types["float"]:
|
|
return float(value)
|
|
elif pair in types["path"]:
|
|
return self.translate_path(value)
|
|
elif pair in types["list"]:
|
|
if value == "":
|
|
return []
|
|
else:
|
|
return [member.strip() for member in value.split(types.list_member_sep)]
|
|
elif pair in types["int-list"]:
|
|
return [int(member) for member in value.split(types.list_member_sep)]
|
|
elif pair in types["float-list"]:
|
|
return [float(member) for member in value.split(types.list_member_sep)]
|
|
elif pair in types["path-list"]:
|
|
return [self.translate_path(member.strip()) for member in value.split(types.list_member_sep)]
|
|
return value
|
|
|
|
def set_screen_captures_path(self):
|
|
section, option = "screen-captures", "path"
|
|
if not self.has_option(section, option):
|
|
self.set(section, option, join(self.build_home_path(), self.get(section, "rel-path")), False)
|
|
|
|
def build_home_path(self):
|
|
return join("~", "." + self.get("setup", "package-root"))
|
|
|
|
def set_video_recordings_path(self):
|
|
section, option = "video-recordings", "path"
|
|
if not self.has_option(section, option):
|
|
self.set(section, option, join(self.build_home_path(), self.get(section, "rel-path")), False)
|
|
|
|
def set_data_exclusion_list(self):
|
|
section, option = "setup", "data-exclude"
|
|
exclude = []
|
|
if self.has_option(section, option):
|
|
exclude = self.get(section, option)
|
|
exclude += [".git*", "README", "build/", "dist/", "*.egg-info",
|
|
"*.py", "MANIFEST*", "PKG-INFO", "*.pyc", "*.swp", "*~",
|
|
self.get("setup", "changelog"),
|
|
self.get("setup", "package-root"),
|
|
self.get("setup", "init-script")]
|
|
for location in self.get("setup", "additional-packages"):
|
|
exclude.append(location)
|
|
self.set(section, option, exclude, False)
|
|
|
|
def set_requirements(self):
|
|
section, option = "setup", "requirements"
|
|
requirements = []
|
|
if self.has_option(section, option):
|
|
requirements = self.get(section, option)
|
|
if "pygame" not in requirements:
|
|
requirements.append("pygame")
|
|
self.set(section, option, requirements, False)
|
|
|
|
def get_section(self, section):
|
|
assignments = {}
|
|
for option in self.options(section):
|
|
assignments[option] = self.get(section, option)
|
|
return assignments
|
|
|
|
def __repr__(self):
|
|
config = {}
|
|
for section in self.sections():
|
|
config[section] = self.get_section(section)
|
|
return pformat(config, 2, 1)
|
|
|
|
def items(self, section):
|
|
items = []
|
|
for option in self.options(section):
|
|
items.append((option, self.get(section, option)))
|
|
return items
|
|
|
|
def write(self, fp=None):
|
|
modifiable = self.modifiable
|
|
use_main = fp is None
|
|
if use_main:
|
|
path = self.locate_project_config_file()
|
|
if not path:
|
|
path = join(self.resource_path or "", self.default_project_file_rel_path)
|
|
fp = open(path, "w")
|
|
break_line = False
|
|
for section in self.order:
|
|
if section in modifiable:
|
|
if break_line:
|
|
fp.write("\n")
|
|
fp.write("[%s]\n" % section)
|
|
for option in modifiable[section]:
|
|
if self.has_option(section, option):
|
|
raw_value = self.get_raw_value(self.get(section, option))
|
|
# handle multiline entries
|
|
if "\n" in raw_value:
|
|
lines = raw_value.split("\n")
|
|
fp.write("{} = {}\n".format(option, lines[0]))
|
|
for line in lines[1:]:
|
|
# have to indent lines after the first to be compatible with configparser
|
|
fp.write(" {}\n".format(line))
|
|
else:
|
|
fp.write("{} = {}\n".format(option, raw_value))
|
|
break_line = True
|
|
if use_main:
|
|
fp.close()
|
|
|
|
def get_raw_value(self, value):
|
|
if isinstance(value, list):
|
|
raw = ""
|
|
for ii, value in enumerate(value):
|
|
if ii:
|
|
raw += ", "
|
|
raw += str(value)
|
|
else:
|
|
raw = str(value)
|
|
return raw
|
|
|
|
def clear_section(self, section):
|
|
if self.has_section(section):
|
|
for option in self.options(section):
|
|
self.remove_option(section, option)
|
|
|
|
def merge_command_line(self, command_line=None):
|
|
"""
|
|
Instantiate an argument parser to parse the command line. Load all settings submitted through the `--config` flag. Each setting
|
|
is a string starting with the chosen delimiter, followed by the section name, option name, and option value each separated by the
|
|
chosen delimiter.
|
|
|
|
For example, set the screen dimensions using "|" as the delimiter and set the Q key to the quit command using "," as the
|
|
delimiter,
|
|
|
|
$ ./OPEN-GAME --config "|display|dimensions|960,540" ",keys,quit,K_q"
|
|
|
|
@param command_line A list of strings to use as the command line. If this is `None`, `sys.argv` is used instead.
|
|
"""
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("--config", nargs="*", default=[])
|
|
# Loop over all assignment strings found
|
|
for assignment in parser.parse_known_args(command_line)[0].config:
|
|
# Split each assigment string by the first character in the string and pass along the parsed values as arguments
|
|
# to the set member function
|
|
self.set(*assignment.split(assignment[0])[1:])
|
|
|
|
|
|
class TypeDeclarations(dict):
|
|
|
|
list_member_sep = ','
|
|
|
|
defaults = {
|
|
|
|
"display": {"int": ["frame-duration", "wait-duration", "framerate-text-size"],
|
|
|
|
"bool": ["centered", "skip-frames", "fullscreen", "show-framerate", "use-framebuffer"],
|
|
|
|
"int-list": ["dimensions", "framerate-text-color", "framerate-text-background", "framerate-position"]},
|
|
|
|
"input": {"bool": "confirm-quit"},
|
|
|
|
"screen-captures": {"path": ["rel-path", "path"]},
|
|
|
|
"video-recordings": {"path": ["rel-path", "path"],
|
|
|
|
"int": ["framerate", "filename-digits"],
|
|
|
|
"bool": ["enable", "record-audio"]},
|
|
|
|
"setup": {"list": ["classifiers", "resource-search-path", "requirements", "data-exclude", "additional-packages", "osx-includes",
|
|
"boolean-true-lowercase"],
|
|
|
|
"path": ["installation-dir", "changelog", "description-file", "main-object", "icon-path", "windows-dist-path", "package-root"]},
|
|
|
|
"mouse": {"float": "double-click-time-limit",
|
|
|
|
"bool": "visible"},
|
|
|
|
"keys": {"list": ["up", "right", "down", "left"]},
|
|
|
|
"joy": {"int": ["advance", "pause", "select", "vertical-axis", "horizontal-axis"],
|
|
|
|
"float": "delay-axis",
|
|
|
|
"bool": "single-xy"},
|
|
|
|
"audio": {
|
|
|
|
"list": "sfx-extensions",
|
|
|
|
"path": "panel-font",
|
|
|
|
"path-list": ["sfx-default-path", "sfx-repository-path", "sfx-project-path", "bgm-repository-path", "bgm-project-path"],
|
|
|
|
"float": ["sfx-volume", "bgm-volume", "volume"],
|
|
|
|
"bool": ["panel-enabled", "auto-load"]
|
|
|
|
},
|
|
|
|
"event": {"int": "command-id-offset"},
|
|
|
|
"interpolator-gui": {"int": ["margin", "marker-size", "label-size", "axis-label-count", "label-precision", "prompt-border-width",
|
|
"prompt-character-limit", "prompt-text-size", "flat-y-range"],
|
|
|
|
"int-list": ["marker-color", "curve-color", "prompt-size", "prompt-border-color"]},
|
|
|
|
}
|
|
|
|
additional_defaults = {}
|
|
|
|
def __init__(self):
|
|
dict.__init__(self, {"bool": [], "int": [], "float": [], "path": [], "list": [], "int-list": [], "float-list": [], "path-list": []})
|
|
self.add_chart(self.defaults)
|
|
self.add_chart(self.additional_defaults)
|
|
|
|
def add(self, cast, section, option):
|
|
self[cast].append((section, option))
|
|
|
|
def add_chart(self, chart):
|
|
for section, declarations in chart.items():
|
|
for cast, options in declarations.items():
|
|
if type(options) != list:
|
|
options = [options]
|
|
for option in options:
|
|
self.add(cast, section, option)
|