barotrauma-sunken-tapes/main.py

343 lines
11 KiB
Python

import os
import tkinter as tk
from tkinter import filedialog as fd
from tkinter.messagebox import showinfo, askyesno, askokcancel
from tkinter import ttk
from tooltip import Hovertip
from pathlib import Path
from deploy import download_ffmpeg, download_git, deploy, get_ffmpeg_version, update
import logging
import sys
import re
import unicodedata
def slugify(value, allow_unicode=False):
"""
Taken from https://github.com/django/django/blob/master/django/utils/text.py
Convert to ASCII if 'allow_unicode' is False. Convert spaces or repeated
dashes to single dashes. Remove characters that aren't alphanumerics,
underscores, or hyphens. Convert to lowercase. Also strip leading and
trailing whitespace, dashes, and underscores.
"""
value = str(value)
if allow_unicode:
value = unicodedata.normalize('NFKC', value)
else:
value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore').decode('ascii')
value = re.sub(r'[^\w\s-]', '', value.lower())
return re.sub(r'[-\s]+', '-', value).strip('-_')
def get_curr_screen_geometry():
"""
Workaround to get the size of the current screen in a multi-screen setup.
Returns:
geometry (str): The standard Tk geometry string.
[width]x[height]+[left]+[top]
"""
root = tk.Tk()
root.update_idletasks()
root.attributes('-fullscreen', True)
root.state('iconic')
geometry = root.winfo_geometry()
root.destroy()
return geometry
def create_name_frame(container):
frame = ttk.LabelFrame(container, text='Mod Name')
# Override Workshop checkbox
override_workshop = tk.StringVar()
override_workshop.set("0")
override_workshop_check = ttk.Checkbutton(
frame,
variable=override_workshop,
text='Override Workshop',
command=lambda: override_action())
override_workshop_check.pack(side="left", padx=3, pady=3)
name = tk.StringVar()
name.set("Sunken Tapez")
name_entry = ttk.Entry(frame, width=30, textvariable=name)
name_entry.pack(side="left", padx=3, pady=3, expand=True, fill="x")
slug = tk.StringVar()
slug.set(slugify("Sunken Tapez"))
slug_entry = ttk.Entry(frame, width=30, textvariable=slug)
slug_entry["state"] = "disable"
slug_entry.pack(side="left", padx=3, pady=3, expand=True, fill="x")
Hovertip(override_workshop_check,
'Keep this unchecked to prevent Steam Workshop'
'\noverriding your custom installation.'
'\n\nIt exists only for the author\'s convenience.')
Hovertip(name_entry,
'Name of the deploy mod. You can customise this'
'\nif you want to create your own version.')
Hovertip(slug_entry,
'Slug of the deployed mod name that is'
'\nused for file paths and references.')
def update_slug(*args):
slug.set(slugify(name.get()))
name.trace("w", update_slug)
def override_action():
if override_workshop.get() == "1":
name.set("Sunken Tapes")
name_entry["state"] = "disable"
slug.set("sunken_tapes")
elif override_workshop.get() == "0":
name.set("Sunken Tapez")
name_entry["state"] = "normal"
return frame, name, slug
def create_install_frame(container):
def find_barotrauma_directory():
first_guess = Path("C:/Program Files (x86)/Steam/steamapps/common/Barotrauma")
second_guess = Path("D:/Steam/steamapps/common/Barotrauma")
if first_guess.is_dir():
return first_guess.as_posix()
elif second_guess.is_dir():
return second_guess.as_posix()
else:
return False
def select_barotrauma_directory():
return_dir = fd.askdirectory(initialdir="C:/")
install_dir.set(return_dir)
frame = ttk.LabelFrame(container, text='Barotrauma installation directory')
find_button = ttk.Button(frame, text='Guess',
command=lambda: install_dir.set(find_barotrauma_directory()))
find_button.pack(side="left", padx=3, pady=3)
install_dir = tk.StringVar()
install_dir.set(find_barotrauma_directory())
directory_entry = ttk.Entry(frame, width=60, textvariable=install_dir)
directory_entry.pack(side="left", padx=3, pady=3, expand=True, fill="x")
browse_button = ttk.Button(frame, text='Browse', command=select_barotrauma_directory)
browse_button.pack(side="left", padx=3, pady=3)
return frame, install_dir
def create_options_frame(container):
frame = ttk.LabelFrame(container, text='Options')
# Use ITA checkbox
use_ita = tk.StringVar()
use_ita.set("0")
use_ita_check = ttk.Checkbutton(
frame,
variable=use_ita,
text='Use ITA mod',
command=lambda: print(use_ita.get()))
# Buffs checkbox
buffs = tk.StringVar()
buffs.set("1")
buffs_check = ttk.Checkbutton(
frame,
variable=buffs,
text='Buffs',
command=lambda: print(buffs.get()))
Hovertip(use_ita_check,
'Check this box if you use Into The Abyss mod.'
'\nITA and Sunken Tapes override the same style file.')
Hovertip(buffs_check,
'Check this box to enable some songs causing strange effects'
'\nThis is the intended default behaviour.')
for widget in frame.winfo_children():
widget.pack(side="top", padx=3, pady=3, fill="x")
return frame, use_ita, buffs
def create_resolution_frame(container):
frame = ttk.LabelFrame(container, text='Resolution')
def detect_screen_resolution():
w, h = get_curr_screen_geometry().split("+")[0].split("x")
width.set(w)
height.set(h)
width = tk.StringVar()
width.set("1920")
width_label = ttk.Label(frame, text='Width:')
width_frame = ttk.Entry(frame, width=12, textvariable=width)
height = tk.StringVar()
height.set("1080")
height_label = ttk.Label(frame, text='Height:')
height_frame = ttk.Entry(frame, width=12, textvariable=height)
detect_button = ttk.Button(frame, text='Detect', command=detect_screen_resolution)
for widget in frame.winfo_children():
widget.pack(side="left", padx=3, pady=3)
return frame, width, height
def create_deploy_frame(container, config):
def deploy_action():
config_values = {"installdir": config["installdir"].get(),
"use_ita": config["use_ita"].get() == "1",
"buffs": config["buffs"].get() == "1",
"name": config["name"].get(),
"slug": config["slug"].get(),
"resolution_x": int(config["resolution_x"].get()),
"resolution_y": int(config["resolution_y"].get())}
logging.info(f"deploying with config: {config_values}")
deploy(config_values)
if config_values["name"] != "Sunken Tapes":
note = f'\n\nThis is a custom version and is named {config_values["name"]} to differentiate it from ' \
+ 'the Steam Workshop version that would overwrite it otherwise.' \
+ '\n\nPlease include a link to the original mod if you publish it to Steam Workshop.'
else:
note = ""
showinfo(title='Success!',
message=f'{config_values["name"]} was successfully installed to:'
f'\n\n{config_values["installdir"]}/Mods/{config_values["slug"]}'
f'\n\nThis installer will now close.'
f'{note}')
container.destroy()
def download_ffmpeg_action():
def finish():
does_ffmpeg_exists()
showinfo(title='Success!',
message=f'ffmpeg downloaded. You can now deploy Sunken Tapes:')
if not Path("./utils/ffmpeg-" + get_ffmpeg_version() + "-full_build/bin/ffmpeg.exe").exists():
download_ffmpeg()
finish()
return
else:
answer = askyesno(title='ffmpeg already downloaded',
message='ffmpeg is already in utils directory!'
'\n\nDelete the existing one and download again?')
if answer:
download_ffmpeg(clean=True)
finish()
else:
return
def update_action():
if not Path("./utils/git").exists():
answer = askokcancel(
title='Confirmation',
message='Portable version of git will be downloaded.'
'\nThis is needed to check for updates in the repository')
if answer:
download_git()
else:
return
else:
logging.info(f"git already in utils")
update()
def does_ffmpeg_exists():
if Path("./utils/ffmpeg-" + get_ffmpeg_version() + "-full_build/bin/ffmpeg.exe").exists():
deploy_button["state"] = "normal"
download_tools_button["state"] = "disable"
else:
deploy_button["state"] = "disable"
download_tools_button["state"] = "normal"
frame = ttk.LabelFrame(container, text='Install')
frame_1 = ttk.Frame(frame)
download_tools_label = ttk.Label(frame_1, text='Download and unpack ffmpeg:')
download_tools_label.pack(side="left", padx=3, pady=3)
download_tools_button = ttk.Button(frame_1, text='Download', command=download_ffmpeg_action)
download_tools_button.pack(side="right", padx=3, pady=3)
frame_1.pack(side="top", fill="x")
frame_2 = ttk.Frame(frame)
update_label = ttk.Label(frame_2, text='Download update from git.kompot.si/jaka/barotrauma-sunken-tapes:')
update_label.pack(side="left", padx=3, pady=3)
update_button = ttk.Button(frame_2, text='Update', command=update_action)
update_button.pack(side="right", padx=3, pady=3)
frame_2.pack(side="top", fill="x")
frame_3 = ttk.Frame(frame)
deploy_label = ttk.Label(frame_3, text='Download songs and deploy mod to Barotrauma Mods directory:')
deploy_label.pack(side="left", padx=3, pady=3)
deploy_button = ttk.Button(frame_3, text='Deploy', command=deploy_action)
deploy_button.pack(side="right", padx=3, pady=3)
frame_3.pack(side="top", fill="x")
does_ffmpeg_exists()
return frame
def create_main_window():
# root window
root = tk.Tk()
root.title('Sunken Tapes')
middle_frame = ttk.Frame(root)
name_frame, name, slug = create_name_frame(root)
install_frame, install_dir = create_install_frame(root)
options_frame, use_ita, buffs = create_options_frame(middle_frame)
resolution_frame, width, height = create_resolution_frame(middle_frame)
config = {"installdir": install_dir,
"use_ita": use_ita,
"buffs": buffs,
"name": name,
"slug": slug,
"resolution_x": width,
"resolution_y": height}
deploy_frame = create_deploy_frame(root, config)
name_frame.pack(side="top", fill="x", pady=3, padx=3)
install_frame.pack(side="top", fill="x", pady=3, padx=3)
options_frame.pack(side="left", fill="y", pady=3, padx=3)
resolution_frame.pack(side="left", fill="both", pady=3, padx=3, expand=True)
middle_frame.pack(side="top", fill="x")
deploy_frame.pack(side="top", fill="x", pady=3, padx=3)
root.mainloop()
if __name__ == "__main__":
os.chdir(Path(__file__).parents[0])
logging.basicConfig(stream=sys.stdout, level=logging.INFO)
create_main_window()