Now included all wanted function. Auto update, change settings, change repo and more.

New
Class for function used across all other classes.
Automatically check for new updates.
Change repo url, handy for development.
Backup profile file, change some settings in the program and more. See changelog for greater detail.
This commit is contained in:
Mr Finchum 2024-12-10 13:33:28 +00:00
parent 65e6f5a4d7
commit 98731a534d
4 changed files with 391 additions and 150 deletions

View file

@ -1,32 +1,74 @@
# Changelog
# 0.5.x
## 0.5.3
- Merged to stable; all desired functions are now included.
- Next steps: optimizing classes and functions to reduce dependencies and improve modularity.
## 0.5.2
### FIXES
- Fixed an issue where loading i.e. restore save, did not work.
### New
- **Utility class**
- Introduced a new Utility class to organize functions and improve code cleanliness. More functions may be moved in future updates.
- **Two new menus**
- Added a settings menu, allowing (some) settings to be changed directly within the program.
- Added a menu to backup and restore FTL profile files. It now checks if the destination is newer to prevent overwriting progress by restoring an outdated profile.
### Improvments
- Improved flexibility of some functions.
## 0.5.1
### Improvments
- Settings Enhancements:
- Extended settings to include LAST_ONLINE_VERSION, LAST_UPDATE_CHECK, and AUTO_UPDATE_CHECK_INTERVAL_H.
Added support for default values to be automatically added to the settings file if missing, simplifying updates with new settings.
### New functions
- Auto Update Check:
- Introduced automatic update checks, defaulting to every 72 hours (configurable via the settings file).
- The latest online version number is now saved in the local settings file.
- Update Notification:
- When a new version is available online, the latest version number is displayed next to the update button.
### Planned Enhancements:
- Add a function allowing users to configure all settings manually.
## 0.5.0
- Refactored functions related to loading and writing settings into a dedicated class, enhancing flexibility and maintainability for managing settings files.
- Improved settings file handling, enabling more robust and adaptable loading and saving.
- Updated the README to include details on how LLMs were utilized during development.
# 0.4.x
## 0.4.2
- Cleaning: Removed old, unused code.
- The program now replicates the functionality of the original Visual Basic program but rewritten in Python with a terminal-based UI:
- Features include: Backup, Restore, and Update.
### 0.4.1
## 0.4.1
- Added a check to ensure the system is Linux and all required dependencies are installed; exits gracefully if not.
- Removed the old UI, as maintaining two versions is unnecessary.
- Updated file selection to use simple_term_menu for improved functionality.
### 0.4.0
## 0.4.0
- Introduced a new terminal UI using simple_term_menu for the main menu.
- This adds a new dependency (simple_term_menu) that must be installed for the UI to function.
- The program checks if the package is available. If it is, the new UI is loaded; otherwise, it falls back to the simpler version.
# 0.3.x
## 0.3.1
- Fixed an issue where the online version was always considered newer, regardless of the actual version numbers.
- Improved menu layout with minor changes for better usability.
### 0.3.0
## 0.3.0
- Added an update function. The program now checks the latest_version.txt file from GitLab and can download and replace the local ftl-savemanager.py file with the latest version.
- If a .gitignore file is detected in the same folder, the update process is blocked.
- The program prompts the user for confirmation before performing the update.
# 0.2.x
## 0.2.1
- Adopted semantic versioning (MAJOR.MINOR.PATCH) for better version tracking.
### 0.2
## 0.2
- Reorganized the codebase: functions have been split into two classes, Backup and UI.
## 0.1
# 0.1
- Initial working script.

View file

@ -48,3 +48,9 @@ Operating System: Linux
Dependencies:
- requests
- simple-term-menu
Can be installed with pip or using miniconda (aka conda/mamba)
# Additional Note
This project was developed with the assistance of LLMs (including, but not limited to, OpenAI's ChatGPT, Ollama models like OpenCoder and LLaMA 3.2).
The assistance provided includes, but is not limited to, spelling corrections, function suggestions, and discussions of debugging, which are not explicitly labeled. Direct optimizations of the code are noted within the code itself.

View file

@ -2,7 +2,8 @@ import os
import time
import sys
from datetime import datetime
version = "0.4.2"
version = "0.5.2" # actully 0.5.2 because 0.5.1 was just pushed
gitlab_url = "https://gitlab.com/python_projects3802849/ftl-save-manager/-/raw/main"
settings_location = "settings.txt"
dependencies = ("simple_term_menu", "requests")
@ -20,161 +21,128 @@ def check_package_installed(package_name):
print(f"{package_name} not found. Error: {e}")
return False
for item in dependencies:
if check_package_installed(item) == False:
print(f"One or more dependencies are missing, please ensure follwing packaged are installed:\nrequests\nsimple-term-menu")
exit()
import requests # todo: add check if installed, if not disable update function.
import requests
from simple_term_menu import TerminalMenu
class BackupApp:
""""Backup class, copy FTL continue file to backup location and restores it."""
def __init__(self, settings_file_path):
def __init__(self):
self.backup_files = {"1": "initiating"} # initiating variable
self.settings_path = settings_file_path
self.settings = self.read_settings()
if self.settings:
self.game_path = self.settings[0]
self.backup_path = self.settings[1]
else:
self.game_path, self.backup_path = self.ask_for_path()
self.write_settings([self.game_path, self.backup_path])
print(f"Game location: {self.game_path}.\nSave location: {self.backup_path}")
def read_settings(self):
"""Reading the settings file."""
if os.path.exists(self.settings_path):
with open(self.settings_path, "r") as f:
data = f.readlines()
for i in range(len(data)):
data[i] = data[i].strip() #stripping white space
return(data)
else:
print("settings file does not exit.")
return False
def ask_for_path(self):
"""Uses get_path to request path from the user."""
game_path = self.get_path("Game save path", True) #
backup_path = self.get_path("Backup path")
return(game_path, backup_path)
def get_path(self, str, ftl = False): # improvment: function that checks path or file
"""Get path from user and check if exists."""
while True:
path = input(f"please enter {str}: ")
if ftl:
profile_file = path + "/ae_prof.sav"
if os.path.isfile(profile_file):
return path
else:
print("Path is not valid, try again.")
continue
if os.path.exists(path):
return path
break
else:
print("Path not valid, try again.")
continue
return path
def write_settings(self, list): # improvment: more generic name, write_file or write_list_as_file
"""Write the settings (path)"""
with open(self.settings_path, "w") as f:
for item in list:
f.write(item + "\n")
print("write settings.")
def copy_file(self, src, dst):
""""Simply copies a file from a to b."""
if not os.path.isfile(src):
print(f"File to copy {src} does not exist.")
return False
os.system(f"cp {src} {dst}")
print(f"Wrote: {dst}")
self.game_path = configuration.settings["GAME_PATH"]
self.save_path = configuration.settings["SAVE_PATH"]
def backup(self):
"""Copies the game file to the backup folder and adds date-time stamp."""
now = datetime.now()
formatted_now = now.strftime("%Y-%m-%d_%H-%M")
self.copy_file(f"{self.game_path}/continue.sav", f"{self.backup_path}/{formatted_now}.bkup")
formatted_now = now.strftime("%Y-%m-%d_%H-%M-%S")
u.copy_file(f"{self.game_path}/continue.sav", f"{self.save_path}/{formatted_now}.bkup")
time.sleep(0.5)
def restore_backup(self, file):
self.copy_file(f"{self.backup_path}/{file}", f"{self.game_path}/continue.sav")
u.copy_file(f"{self.save_path}/{file}", f"{self.game_path}/continue.sav")
def delete_backup_file(self): # improvment: here also, more generic, delete_file.
def delete_backup_file(self): # improvment: here also, more generic, delete_files with file type .something
"""Deltes all .bkup files in the backup folder."""
if self.yes_no("Backup"):
for file in os.listdir(self.backup_path):
if u.yes_no("Are you sure you want to delete all save files?"):
for file in os.listdir(self.save_path):
if file[-5:] == ".bkup":
try:
print(f"Deleting {file}.")
os.remove(f"{self.backup_path}/{file}")
os.remove(f"{self.save_path}/{file}")
time.sleep(0.3)
except Exception as e:
print(f"Failed to delete {file}. Reason: {e}.")
#time.sleep(2)
time.sleep(1)
else:
print("Deleting save files canceled")
time.sleep(0.5)
def yes_no(self, str): # improvment: more generic, take two string.
"""Ask user y/n question regaring deletion of a string."""
while True:
choice = input(f"Are you sure you want to delete all {str} files? (y/n)")
if choice == "y":
return True
elif choice == "n":
print(f"Deleting {str} files aborted")
#time.sleep(1)
return False
else:
print("Not a valid option, try again")
class Updater: # improvment: if dependency not aviable disable this
class Updater:
"""Update local version from gitlab repo."""
def __init__(self, version, update_url, local_file):
def __init__(self, version, local_file):
self.current_version = version
self.update_url = update_url
self.update_url = configuration.settings["REPO_URL"]
self.local_file = local_file
self.git = os.path.exists(".gitignore") # Checking if the program inside git env
self.auto_check()
def check_for_update(self):
def auto_check(self):
print("Auto update check")
check_interval = configuration.settings["AUTO_UPDATE_CHECK_INTERVAL_H"]
if check_interval == 0:
print("Auto update disabeld")
return # Exiting
if "LAST_UPDATE_CHECK" in configuration.settings:
last_time = configuration.settings["LAST_UPDATE_CHECK"]
t_object = datetime.strptime(last_time, "%Y-%m-%d %H-%M")
try:
h_since_update_check = u.get_difference_in_hours(datetime.now(), t_object)
if h_since_update_check > int(configuration.settings["AUTO_UPDATE_CHECK_INTERVAL_H"]):
print(f"More then {configuration.settings["AUTO_UPDATE_CHECK_INTERVAL_H"]} hours since last update check, checking now...")
self.check_for_update(0)
except ValueError:
print("Invalid timestamp format for LAST_UPDATE_CHECK.")
input("Error checking for updates:\nEnter to continue...")
else:
self.check_for_update(0)
def check_for_update(self, verbose = 1):
"""Check if a new version is available."""
try:
print("Checking for updates...")
if verbose: print("Checking for updates...")
response = requests.get(f"{self.update_url}/latest_version.txt")
response.raise_for_status()
latest_version = response.text.strip()
if self.compare_SemVer(latest_version):
print(f"New version {latest_version} available online.\nCurrent version: {self.current_version}")
self.update_info = self.read_version_file(response)
configuration.settings["LAST_ONLINE_VERSION"] = self.update_info["LATEST_VERSION"] # updating the version number from update class to global var
now = datetime.now()
formatted_now = now.strftime("%Y-%m-%d %H-%M")
configuration.settings["LAST_UPDATE_CHECK"] = formatted_now
if self.compare_str_SemVer(self.current_version, self.update_info["LATEST_VERSION"]):
if verbose:
print(f"New version {self.update_info["LATEST_VERSION"]} available online.\nCurrent version: {self.current_version}")
print(f"\nUpdate comment:\n{self.update_info["COMMENT"]}\n")
return True
else:
print(f"You are up-to-date, the latest version is: {latest_version}.")
time.sleep(2)
if verbose:
print(f"You are up-to-date ({self.current_version}).\nLatest version is: {self.update_info["LATEST_VERSION"]}.")
input("Enter to continue...")
return False
except Exception as e:
print(f"Error checking for updates: {e}")
if verbose: print(f"Error checking for updates: {e}")
input("UPDATE ERROR Press Enter to continue...")
return False
def compare_SemVer(self, latest_version):
def read_version_file(self, response): # does basicly the same as the read settings function in settingmanger class, optimizing would combine them.
settings = {}
lines = response.text.splitlines()
for line in lines:
if line.startswith("#") or "=" not in line:
continue
key, value = map(str.strip, line.split("=", 1))
if value.startswith('"') and value.endswith('"'):
settings[key] = value[1:-1]
return settings
def compare_str_SemVer(self, first, second):
"""Compare two Semantic versioning strings."""
local_version = self.convert_str_list_to_int(self.current_version.split("."))
online_version = self.convert_str_list_to_int(latest_version.split("."))
local_version = self.convert_str_list_to_int(first.split("."))
online_version = self.convert_str_list_to_int(second.split("."))
return local_version < online_version
def convert_str_list_to_int(self, list_str):
"""Converts a list with strings to a list with intengers."""
return [int(i) for i in list_str]
def download_update(self, latest_version): # improvment: silly it takes the latest_version as variable
def download_update(self): # improvment: silly it takes the latest_version as variable
"""Download the new version and overwrite the current file."""
try:
print(f"Downloading version {latest_version}...")
print(f"Downloading version {self.update_info["LATEST_VERSION"]}...")
time.sleep(2)
response = requests.get(f"{self.update_url}/{self.local_file}")
response.raise_for_status()
with open(self.local_file, "wb") as file:
@ -196,27 +164,15 @@ class Updater: # improvment: if dependency not aviable disable this
print("Updating only works outside git env.")
time.sleep(2)
else:
latest_version = self.check_for_update()
if latest_version:
if self.yes_no("Do you want to update the program?"):
success = self.download_update(latest_version)
if self.check_for_update():
if u.yes_no("Do you want to update the program?"):
success = self.download_update()
if success:
self.restart_program()
else:
print("Update cancelled")
return
def yes_no(self, str): # improvment: have two yes_no function, should merge them
"""Ask user y/n question"""
while True:
choice = input(f"{str} (y/n)")
if choice == "y":
return True
elif choice == "n":
return False
else:
print("Not a valid option, try again")
class TerminalUI:
def __init__(self, title, options):
self.main_menu_title = title
@ -229,31 +185,40 @@ class TerminalUI:
menu_cursor_style = ("fg_gray", "bold"),
menu_highlight_style = ("bg_gray", "fg_black"),
cycle_cursor = True,
clear_screen = True,
clear_screen = True
)
def show_main_menu(self):
while True:
selected_index = self.main_menu.show()
selected_key = None
if type(selected_index) == int: selected_key = self.main_menu_keys[selected_index]
if selected_index == None:
break
elif selected_key == "[r] Restore backup":
self.restore_file_menu(app.backup_path)
elif selected_key in self.main_menu_options:
self.main_menu_options[selected_key]()
# Adding try makes it possiable to add code after the True loop
try:
while True:
selected_index = self.main_menu.show()
selected_key = None
if isinstance(selected_index, int):
selected_key = self.main_menu_keys[selected_index]
if selected_index == None:
break
elif selected_key == "[l] Load":
self.restore_file_menu(app.save_path)
elif selected_key == "[c] Change settings":
self.change_settings_menu()
elif selected_key == "[p] Profile managment":
self.profile_menu()
elif selected_key in self.main_menu_options:
self.main_menu_options[selected_key]()
finally:
configuration.write_settings_file() # writing settings when exiting.
def restore_file_menu(self, folder):
menu_options = list(self.list_files(folder))
folder_menu = TerminalMenu(menu_entries = menu_options,
title = f"Content of {folder} (Q or Esc to go back.)\n",
title = f"Content of {folder} (Q or Esc to go back).\n",
menu_cursor = "> ",
menu_cursor_style = ("fg_red", "bold"),
menu_highlight_style = ("bg_gray", "fg_black"),
cycle_cursor = True,
clear_screen = True)
clear_screen = False,
)
menu_entry_index = folder_menu.show()
if menu_entry_index == None:
@ -272,28 +237,255 @@ class TerminalUI:
menu_cursor_style = ("fg_red", "bold"),
menu_highlight_style = ("bg_gray", "fg_black"),
cycle_cursor = True,
clear_screen = True)
clear_screen = False)
menu_entry_index = menu.show()
if menu_entry_index == 0:
return True
elif menu_entry_index == 1:
return False
up = Updater(version, gitlab_url, "ftl-savemanager.py")
app = BackupApp(settings_location)
def change_settings_menu(self):
menu_options = [f"[g] Game path ({configuration.settings["GAME_PATH"]})",
f"[s] Save path ({configuration.settings["SAVE_PATH"]})",
f"[a] Auto update interval ({configuration.settings["AUTO_UPDATE_CHECK_INTERVAL_H"]})",
"[q] Quite"]
settings_option = ["GAME_PATH", "SAVE_PATH", "AUTO_UPDATE_CHECK_INTERVAL_H"]
settings_menu = TerminalMenu(menu_entries = menu_options,
title = "Change settings (Q or Esc to go back).\n",
menu_cursor = "> ",
menu_cursor_style = ("fg_red", "bold"),
menu_highlight_style = ("bg_gray", "fg_black"),
cycle_cursor = True,
clear_screen = False
)
try:
while True:
selected_index = settings_menu.show()
selected_key = None
if selected_index == 3:
break
if isinstance(selected_index, int):
selected_key = settings_option[selected_index]
if selected_index == None:
break
elif selected_key in settings_option:
if self.yes_no_menu(f"Do you want to change {selected_key} from {configuration.settings[selected_key]}?"):
new_value = input(f"Please enter new value for {selected_key}: ")
configuration.settings[selected_key] = new_value
configuration.validate_paths_in_settings()
configuration.convert_str_to_int_settings()
else:
print("No changed were made.")
finally:
configuration.write_settings_file() # writing settings when exiting.
def profile_menu(self):
menu_options = ["[b] Backup profile", "[r] Restore profile", "[q] Quite"]
menu = TerminalMenu(menu_entries = menu_options,
title = f"Backup or restore the profile file, which includes complete progress (Q or Esc to go back).\n",
menu_cursor = "> ",
menu_cursor_style = ("fg_red", "bold"),
menu_highlight_style = ("bg_gray", "fg_black"),
cycle_cursor = True,
clear_screen = False)
menu_entry_index = menu.show()
profile_path = f"{configuration.settings["GAME_PATH"]}/ae_prof.sav"
backup_path = f"{configuration.settings["SAVE_PATH"]}/ae_prof.sav"
if menu_entry_index == 0:
u.copy_file_overwrite_if_newer(profile_path, backup_path)
elif menu_entry_index == 1:
u.copy_file_overwrite_if_newer(backup_path, profile_path)
else:
return
class SettingManager:
"""Handles program settings, reading, validating, and writing them."""
# Refactored this class with suggestions from ChatGPT for better readability (code and language) and efficiency
def __init__(self, settings_path, repo_url):
self.accepted_settings_keys = ["GAME_PATH", "SAVE_PATH", "LAST_ONLINE_VERSION", "REPO_URL", "LAST_UPDATE_CHECK", "AUTO_UPDATE_CHECK_INTERVAL_H"]
self.settings_file_path = settings_path
self.settings = self.read_settings_file()
# Initialize settings
if self.settings:
self.validate_paths_in_settings()
self.set_default_values(repo_url)
else:
self.initialize_settings(repo_url)
self.convert_str_to_int_settings()
def set_default_values(self, repo_url):
default_values = {"REPO_URL": repo_url,
"AUTO_UPDATE_CHECK_INTERVAL_H": "72"
}
for key in default_values:
if key not in self.settings:
self.settings[key] = default_values[key]
print(f"Setting default value: {default_values[key]} for: {key}.")
#time.sleep(0.5)
def convert_str_to_int_settings(self):
"""Change str to int for a given keys in the settings, if not convertable then replace with default value"""
int_values = {"AUTO_UPDATE_CHECK_INTERVAL_H": 72} # Dict for all variables which sould be intinger, and their default values. Scable for future
for key in int_values:
try:
self.settings[key] = int(self.settings[key])
except ValueError:
print(f"Value: {self.settings[key]} could not be changed to an int.")
self.settings[key] = int_values[key]
print(f"Changing {key} to default value value: {int_values[key]}\nAcknowledged...")
time.sleep(1)
def initialize_settings(self, repo_url): # URL in settings file has higher priority, is good for feature branches.
"""Initialize settings when the file doesn't exist or is empty."""
print("Initializing settings...")
self.set_default_values(repo_url)
self.get_paths()
self.write_settings_file()
def validate_paths_in_settings(self):
"""Validate and set paths from the settings file."""
required_paths = ["GAME_PATH", "SAVE_PATH"]
if all(self.settings.get(path) for path in required_paths):
valid_game_path = u.check_path_existence(self.settings["GAME_PATH"], "ae_prof.sav")
valid_save_path = u.check_path_existence(self.settings["SAVE_PATH"])
if valid_game_path and valid_save_path:
print("Paths validated successfully.")
return
print("Invalid paths detected. Requesting user input.")
self.get_paths()
self.write_settings_file()
def read_settings_file(self):
"""Read settings from the file."""
settings = {}
if os.path.exists(self.settings_file_path):
try:
with open(self.settings_file_path, "r") as file:
for line in file:
if line.startswith("#") or "=" not in line:
continue
key, value = map(str.strip, line.split("=", 1))
if value.startswith('"') and value.endswith('"'):
settings[key] = value[1:-1]
return settings
except Exception as e:
print(f"Error reading settings file: {e}")
print("Settings file does not exist or is invalid.")
return settings
def write_settings_file(self):
"""Write the settings dictionary to the file."""
print("Writing settings")
try:
with open(self.settings_file_path, "w") as file:
file.write("# This file is rewritten before program closes. Comments are ignored.\n# Setting AUTO_UPDATE_CHECK_INTERVAL_H = '0' disables automatic update checks.\n# DO NOT modify REPO_URL if you don't know what value to set, no function to fix incorrect input.\n")
for key in self.accepted_settings_keys:
if key in self.settings:
value = self.settings[key]
file.write(f'{key} = "{value}"\n')
except Exception as e:
input(f"Error writing settings file: {e}\nEnter to continue")
def get_paths(self):
"""Request and validate paths from the user."""
self.settings["GAME_PATH"] = self.ask_for_path("Game path", "ae_prof.sav")
self.settings["SAVE_PATH"] = self.ask_for_path("Save path")
def ask_for_path(self, description, required_file = None):
"""Prompt the user for a valid path."""
while True:
path = input(f"Please enter the {description}: ")
if u.check_path_existence(path, required_file):
return path
print("Invalid path. Please try again.")
class Utility():
@staticmethod
def check_path_existence(path, required_file = None):
"""Check if a path exists and optionally if it contains a specific file."""
if required_file:
full_path = os.path.join(path, required_file)
return os.path.isfile(full_path)
return os.path.isdir(path)
@staticmethod
def get_difference_in_hours(dt1, dt2):
diff = abs(dt1 - dt2)
hours_difference = int(diff.total_seconds() / 3600)
return hours_difference
@staticmethod
def yes_no(str): # improvment: have two yes_no function, should merge them
"""Ask user y/n question"""
while True:
choice = input(f"{str} (y/n): ")
if choice == "y":
return True
elif choice == "n":
return False
else:
print(f"Not a valid option, try again.")
@staticmethod
def copy_file(src, dst):
""""Simply copies a file from a to b."""
if not os.path.isfile(src):
print(f"File to copy {src} does not exist.")
return False
os.system(f"cp {src} {dst}")
print(f"Wrote: {dst}")
@staticmethod
def copy_file_overwrite_if_newer(source, destination):
# Main part arrived from opencoder ollama, modified to fit my needs
# check if source file exists
if not os.path.exists(source):
print(f"Source file {source} does not exist.")
return False
# get modification times of source and destination files
src_mtime = os.path.getmtime(source)
dest_mtime = 0
if os.path.exists(destination):
dest_mtime = os.path.getmtime(destination)
if src_mtime < dest_mtime:
print(f"Destination file is newer than the source.\n({destination} vs {source})")
# ask user permission to overwrite
if u.yes_no("Do you want to replace?"):
os.system(f"cp {source} {destination}")
print(f"File {source} has been copied to {destination}")
else:
print(f"User chose not to replace {destination}.")
else:
print(f"Source file is older than or equal to the destination.\n ({source} vs {destination})")
os.system(f"cp {source} {destination}")
print(f"File {source} has been copied to {destination}")
time.sleep(1)
return True
u = Utility()
configuration = SettingManager(settings_location, gitlab_url)
up = Updater(version, "ftl-savemanager.py")
app = BackupApp()
update_key = "[u] Update"
if "LAST_ONLINE_VERSION" in configuration.settings:
if up.compare_str_SemVer(version, configuration.settings["LAST_ONLINE_VERSION"]):
update_key += f" (v{configuration.settings["LAST_ONLINE_VERSION"]})"
main_menu_options = {
"[b] Backup now": app.backup,
"[r] Restore backup": "restore_file_menu",
"[d] Delete backup files": app.delete_backup_file,
"[u] Look for update": up.run_update,
"[s] Save": app.backup,
"[l] Load": "restore_file_menu",
"[d] Delete save files": app.delete_backup_file,
f"{update_key}": up.run_update,
"[p] Profile managment": "backup and restore profile",
"[c] Change settings": "change:_settings_menu",
"[q] Quit": exit
}
title = f"ftl-savemanager v{version} (Press Q or Esc to quit).\n"
tui = TerminalUI(title, main_menu_options)
tui.show_main_menu()

View file

@ -1 +1,2 @@
0.4.2
LATEST_VERSION="0.5.3"
COMMENT="With this version,the program should contain all needed functions."