├── README.md ├── script_manager.py └── scripts ├── change_team.py ├── custom_calendar.py ├── edit_drs.py ├── edit_grip.py ├── edit_stats.py ├── extractor.py ├── set_race.py ├── set_sprint.py ├── shuffle_calendar.py └── shuffle_engines.py /README.md: -------------------------------------------------------------------------------- 1 | # F1M Script Manager 2 | 3 | **Requirements :** 4 | Python 3 5 | 6 | **Safety :** 7 | Always make a copy of your save before running a script in case something goes wrong. 8 | 9 | **How to install :** 10 | Extract the contents of this archive in your save folder (it should be `C:\Users\\AppData\Local\F1Manager22\Saved\SaveGames`). 11 | 12 | **How to use :** 13 | 1. Launch `script_manager.py` and save your game (the order doesn't matter). 14 | 2. Choose which script to run, the save you want to run it on and specify an argument for the scripts that require one. 15 | 3. Load your save. 16 | 17 | Feel free to make your own scripts and send them to me if you want them to be included in this repository. 18 | 19 | =============================================== 20 | 21 | ### How to use the scripts 22 | 23 | `change_team.py` : specify the name of the new team you want to manage as an argument (ex : Ferrari, McLaren, Alpha Tauri). 24 | `edit_drs.py` : specify the drs strength you want as an argument. Accepted values are "default", "reduced" and "disabled". 25 | `edit_grip.py` : specifiy the tyre behaviour you want as an argument. Accepted values are "default", "grip-", "durability-" and "both-". 26 | `shuffle_calendar.py` : run it before the first race of the season to shuffle the race calendar. 27 | `shuffle_engines.py` : run it before starting the new season if you want to know the new values before choosing a new engine or before the first race of the season if you want to be surprised. 28 | 29 | `set_sprint.py` and `set_race.py`: 30 | 1. Save your game before starting the Imola/Austria/Brazil race weekends. 31 | 2. Run `set_sprint.py`. 32 | 3. Play the weekend with the sprint race. 33 | 4. Start the next race weekend at the same track but DO NOT START FP1 and save your game. 34 | 5. Run `set_race.py`. 35 | 6. Load your save and play the race. 36 | 37 | **Known issues :** 38 | Any driver not using 2 different tyre compounds during the sprint will show up as disqualified. 39 | The standings are corrected properly when `set_race.py` is ran. 40 | 41 | =============================================== 42 | 43 | The database unpacker/repacker was made by xAranaktu : 44 | https://github.com/xAranaktu/F1-Manager-2022-SaveFile-Repacker 45 | 46 | MIT License 47 | 48 | Copyright (c) 2022 Paweł 49 | 50 | Permission is hereby granted, free of charge, to any person obtaining a copy 51 | of this software and associated documentation files (the "Software"), to deal 52 | in the Software without restriction, including without limitation the rights 53 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 54 | copies of the Software, and to permit persons to whom the Software is 55 | furnished to do so, subject to the following conditions: 56 | 57 | The above copyright notice and this permission notice shall be included in all 58 | copies or substantial portions of the Software. 59 | 60 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 61 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 62 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 63 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 64 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 65 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 66 | SOFTWARE. 67 | -------------------------------------------------------------------------------- /script_manager.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | import tkinter.font as tkFont 3 | import os 4 | from datetime import datetime 5 | from scripts.extractor import process_unpack, process_repack 6 | 7 | def script_select(event): 8 | if len(script_listbox.curselection()) > 0: 9 | script = scripts[script_listbox.curselection()[0]] 10 | chosen_script_entry.configure(state='normal') 11 | chosen_script_entry.delete(0, "end") 12 | chosen_script_entry.insert(0, script) 13 | chosen_script_entry.configure(state='disabled') 14 | desc_script_text.configure(state='normal') 15 | desc_script_text.delete('1.0', "end") 16 | desc_script_text.insert('1.0', script_map[script].get_description()) 17 | desc_script_text.configure(state='disabled') 18 | run_label_var.set("") 19 | 20 | def save_select(event): 21 | if len(save_listbox.curselection()) > 0: 22 | save = saves[save_listbox.curselection()[0]] 23 | chosen_save_entry.configure(state='normal') 24 | chosen_save_entry.delete(0, "end") 25 | chosen_save_entry.insert(0, save) 26 | chosen_save_entry.configure(state='disabled') 27 | run_label_var.set("") 28 | 29 | def run_script(): 30 | script = chosen_script_entry.get() 31 | save = chosen_save_entry.get() 32 | if len(script) > 0 and len(save) > 0: 33 | process_unpack(save, "scripts/result") 34 | try: 35 | script_map[script].run_script(option_entry.get()) 36 | log.write("[" + str(datetime.now()) + "] Execution - Script : " + script + " - Argument : " + option_entry.get() + " - Savefile : " + save + "\n") 37 | except: 38 | script_map[script].run_script() 39 | log.write("[" + str(datetime.now()) + "] Execution - Script : " + script + " - Savefile : " + save + "\n") 40 | log.flush() 41 | process_repack("scripts/result", save) 42 | run_label_var.set("Ran " + script + " on " + save) 43 | 44 | # Listing all the scripts and saves 45 | scripts = [element for element in os.listdir("./scripts") if ".py" in element] 46 | if "extractor.py" in scripts: 47 | scripts.remove("extractor.py") 48 | if "script_manager.py" in scripts: 49 | scripts.remove("script_manager.py") 50 | saves = [element for element in os.listdir(".") if ".sav" in element] 51 | if "player.sav" in saves: 52 | saves.remove("player.sav") 53 | 54 | # Loading the content and the description of each script 55 | script_map = {elt : __import__("scripts." + elt[:-3], fromlist=[None]) for elt in scripts} 56 | 57 | # Start logging 58 | log = open("scripts/log.txt", 'a', encoding='utf-8') 59 | 60 | window = tk.Tk() 61 | window.title("F1 Manager 22 Script Manager") 62 | window.columnconfigure(0) 63 | window.columnconfigure(1) 64 | window.columnconfigure(2, weight=5) 65 | window.geometry('770x430') 66 | window.resizable(False, False) 67 | font = tkFont.Font(family="Sans Serif", size=12) 68 | font_small = tkFont.Font(family="Sans Serif", size=10) 69 | 70 | LIST_HEIGHT = 22 71 | 72 | script_label = tk.Label(master=window, text="Script list", font=font) 73 | script_label.grid(column=0, row=0, rowspan=1, padx=10, pady=5) 74 | script_listbox = tk.Listbox(master=window, height=LIST_HEIGHT, width=25, listvariable=tk.StringVar(value=scripts), font=font_small) 75 | script_listbox.grid(column=0, row=1, rowspan=LIST_HEIGHT, padx=10, pady=5) 76 | script_listbox.bind('<>', script_select) 77 | 78 | save_label = tk.Label(master=window, text="Savefile list", font=font) 79 | save_label.grid(column=1, row=0, rowspan=1, padx=10, pady=5) 80 | save_listbox = tk.Listbox(master=window, height=LIST_HEIGHT, width=25, listvariable=tk.StringVar(value=saves), font=font_small) 81 | save_listbox.grid(column=1, row=1, rowspan=LIST_HEIGHT, padx=10, pady=5) 82 | save_listbox.bind('<>', save_select) 83 | 84 | chosen_script_label = tk.Label(master=window, text="Selected script :", font=font) 85 | chosen_script_label.grid(column=2, row=1, padx=10, pady=0) 86 | chosen_script_entry = tk.Entry(master=window, width=45, font=font_small) 87 | chosen_script_entry.grid(column=2, row=2, padx=10, pady=0) 88 | chosen_script_entry.configure(state='disabled') 89 | 90 | desc_script_label = tk.Label(master=window, text="Script description :", font=font) 91 | desc_script_label.grid(column=2, row=4, padx=10, pady=0) 92 | desc_script_text = tk.Text(master=window, height=8, width=45, wrap="word", font=font_small) 93 | desc_script_text.grid(column=2, row=5, rowspan=6, padx=10, pady=0) 94 | desc_script_text.configure(state='disabled') 95 | 96 | chosen_save_label = tk.Label(master=window, text="Selected savefile :", font=font) 97 | chosen_save_label.grid(column=2, row=12, padx=10, pady=0) 98 | chosen_save_entry = tk.Entry(master=window, width=45, font=font_small) 99 | chosen_save_entry.grid(column=2, row=13, padx=10, pady=0) 100 | chosen_save_entry.configure(state='disabled') 101 | 102 | option_label = tk.Label(master=window, text="Script argument (if needed) :", font=font) 103 | option_label.grid(column=2, row=15, padx=10, pady=0) 104 | option_entry = tk.Entry(master=window, width=45, font=font_small) 105 | option_entry.grid(column=2, row=16, padx=10, pady=0) 106 | 107 | run_button = tk.Button(master=window, text="Run script", command=run_script, font=font) 108 | run_button.grid(column=2, row=18, padx=10, pady=0) 109 | run_label_var = tk.StringVar() 110 | run_label = tk.Label(master=window, textvariable=run_label_var, width=45, font=font_small) 111 | run_label.grid(column=2, row=19, padx=10, pady=0) 112 | 113 | window.mainloop() 114 | 115 | log.close() -------------------------------------------------------------------------------- /scripts/change_team.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | 3 | def run_script(option=""): 4 | conn = sqlite3.connect("scripts/result/main.db") 5 | cursor = conn.cursor() 6 | 7 | option = option.lower() 8 | if "ferrari" in option: new_team = 1 9 | elif "mclaren" in option: new_team = 2 10 | elif "red bull" in option or "redbull" in option or "rb" in option: new_team = 3 11 | elif "merc" in option: new_team = 4 12 | elif "alpine" in option: new_team = 5 13 | elif "williams" in option: new_team = 6 14 | elif "haas" in option: new_team = 7 15 | elif "alphatauri" in option or "alpha tauri" in option or "at" in option: new_team = 8 16 | elif "alfa" in option or "romeo" in option: new_team = 9 17 | elif "aston" in option or "martin" in option: new_team = 10 18 | else: new_team = -1 19 | 20 | if new_team > 0: 21 | cursor.execute("UPDATE Player SET TeamID = " + str(new_team)) 22 | print("Successfully changed player team") 23 | else: 24 | print(option, "was not recognized as a team") 25 | 26 | conn.commit() 27 | conn.close() 28 | 29 | def get_description(): 30 | return "Choose a new team to manage by typing its name as an argument.\nAuthor: Tahkare" 31 | 32 | if __name__ == '__main__': 33 | run_script() -------------------------------------------------------------------------------- /scripts/custom_calendar.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | import random 3 | 4 | 5 | 6 | def run_script(option=""): 7 | conn = sqlite3.connect("scripts/result/main.db") 8 | cursor = conn.cursor() 9 | 10 | default_tracks = [2, 1, 11, 24, 22, 5, 6, 4, 7, 10, 9, 8, 12, 13, 14, 15, 17, 19, 18, 20, 21, 23] 11 | track_ids = [] 12 | 13 | calendar = option.lower() 14 | races = calendar.split() 15 | 16 | for i in range(len(races)): 17 | if (races[i]) == "bah": 18 | track_ids.insert(i, 2) 19 | elif (races[i]) == "aus": 20 | track_ids.insert(i, 1) 21 | elif (races[i]) == "sau": 22 | track_ids.insert(i, 11) 23 | elif (races[i]) == "imo": 24 | track_ids.insert(i, 24) 25 | elif (races[i]) == "mia": 26 | track_ids.insert(i, 22) 27 | elif (races[i]) == "bar": 28 | track_ids.insert(i, 5) 29 | elif (races[i]) == "mon": 30 | track_ids.insert(i, 6) 31 | elif (races[i]) == "bak": 32 | track_ids.insert(i, 4) 33 | elif (races[i]) == "can": 34 | track_ids.insert(i, 7) 35 | elif (races[i]) == "gbr": 36 | track_ids.insert(i, 10) 37 | elif (races[i]) == "aut": 38 | track_ids.insert(i, 9) 39 | elif (races[i]) == "fra": 40 | track_ids.insert(i, 8) 41 | elif (races[i]) == "hun": 42 | track_ids.insert(i, 12) 43 | elif (races[i]) == "bel": 44 | track_ids.insert(i, 13) 45 | elif (races[i]) == "ita": 46 | track_ids.insert(i, 14) 47 | elif (races[i]) == "sin": 48 | track_ids.insert(i, 15) 49 | elif (races[i]) == "jap": 50 | track_ids.insert(i, 17) 51 | elif (races[i]) == "usa": 52 | track_ids.insert(i, 19) 53 | elif (races[i]) == "mex": 54 | track_ids.insert(i, 18) 55 | elif (races[i]) == "bra": 56 | track_ids.insert(i, 20) 57 | elif (races[i]) == "uae": 58 | track_ids.insert(i, 21) 59 | elif (races[i]) == "ned": 60 | track_ids.insert(i, 23) 61 | 62 | # Getting all the current season races 63 | day_season = cursor.execute("SELECT Day, CurrentSeason FROM Player_State").fetchone() 64 | season_events = cursor.execute("SELECT RaceID FROM Races WHERE SeasonID = " + str(day_season[1])).fetchall() 65 | 66 | season_first_event = season_events[0][0] 67 | season_last_event = season_events[-1][0] 68 | curr_event = season_last_event + 1 69 | 70 | #Inserting new race calendar 71 | for i in range (len(track_ids)): 72 | data_race = cursor.execute("SELECT * FROM Races WHERE TrackID = "+ str(track_ids[i]) + " AND SeasonID = " + str(day_season[1])).fetchone() 73 | cursor.execute("INSERT INTO Races VALUES (" + str(curr_event) + ", " + str(data_race[1]) + ", " + str(data_race[2]) + ", " + str(data_race[3]) + ", " + str(data_race[4]) + ", " + str(data_race[5]) + ", " + str(data_race[6]) + ", " + str(data_race[7]) + ", " + str(data_race[8]) + ", " + str(data_race[9]) + ", " + str(data_race[10]) + ", " + str(data_race[11]) + ", " + str(data_race[12]) + ", " + str(data_race[13]) + ")") 74 | curr_event += 1 75 | 76 | 77 | curr_event = season_last_event + 1 78 | race_blanks = 22 - len(track_ids) 79 | random_blanks = [] 80 | race_cont = 0 81 | 82 | #Random race days that will no longer host a GP 83 | for i in range (0, race_blanks): 84 | n = random.randint(0,21) 85 | while(n in random_blanks): 86 | n = random.randint(0,21) 87 | random_blanks.append(n) 88 | 89 | 90 | # Updating race days to spread them 91 | for i in range(len(default_tracks)): 92 | if (race_cont not in random_blanks): 93 | new_date = cursor.execute("SELECT Day FROM Races WHERE RaceID = " + str(season_first_event)).fetchone()[0] 94 | cursor.execute("UPDATE Races SET Day = " + str(new_date) + " WHERE RaceID = " + str(curr_event)) 95 | curr_event += 1 96 | season_first_event += 1 97 | race_cont += 1 98 | 99 | #Deletion of previous race calendar 100 | cursor.execute("DELETE FROM Races WHERE SeasonID = " + str(day_season[1]) + " AND RaceID <= " + str(season_last_event)) 101 | 102 | 103 | conn.commit() 104 | conn.close() 105 | 106 | 107 | def get_description(): 108 | return "Creates a custom calendar. Available options are: bah, sau, aus, imo, mia, bar, mon, bak, can, gbr, aut, fra, hun, bel, ned, ita, sin, jap, usa, mex, bra, uae.\nAuthor: u/ignaciourreta" 109 | 110 | if __name__ == '__main__': 111 | run_script() -------------------------------------------------------------------------------- /scripts/edit_drs.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | 3 | def run_script(option=""): 4 | conn = sqlite3.connect("scripts/result/main.db") 5 | cursor = conn.cursor() 6 | 7 | option = option.lower() 8 | if option == "default": 9 | cursor.execute("UPDATE Parts_RaceSimConstants SET MinDRSTopSpeedMultiplier = 1.005") 10 | cursor.execute("UPDATE Parts_RaceSimConstants SET MaxDRSTopSpeedMultiplier = 1.044") 11 | cursor.execute("UPDATE Parts_RaceSimConstants SET MinDRSAccelerationMultiplier = 1.05") 12 | cursor.execute("UPDATE Parts_RaceSimConstants SET MaxDRSAccelerationMultiplier = 1.65") 13 | print("Default DRS values set") 14 | elif option == "reduced": 15 | cursor.execute("UPDATE Parts_RaceSimConstants SET MinDRSTopSpeedMultiplier = 1.005") 16 | cursor.execute("UPDATE Parts_RaceSimConstants SET MaxDRSTopSpeedMultiplier = 1.044") 17 | cursor.execute("UPDATE Parts_RaceSimConstants SET MinDRSAccelerationMultiplier = 1.05") 18 | cursor.execute("UPDATE Parts_RaceSimConstants SET MaxDRSAccelerationMultiplier = 1.35") 19 | print("Reduced DRS values set") 20 | elif option == "disabled": 21 | cursor.execute("UPDATE Parts_RaceSimConstants SET MinDRSTopSpeedMultiplier = 1") 22 | cursor.execute("UPDATE Parts_RaceSimConstants SET MaxDRSTopSpeedMultiplier = 1") 23 | cursor.execute("UPDATE Parts_RaceSimConstants SET MinDRSAccelerationMultiplier = 1") 24 | cursor.execute("UPDATE Parts_RaceSimConstants SET MaxDRSAccelerationMultiplier = 1") 25 | print("Disabled DRS") 26 | 27 | conn.commit() 28 | conn.close() 29 | 30 | def get_description(): 31 | return "Choose the DRS behaviour you want. Available options are: default, reduced, disabled.\nAuthor: Tahkare" 32 | 33 | if __name__ == '__main__': 34 | run_script() -------------------------------------------------------------------------------- /scripts/edit_grip.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | 3 | def run_script(option=""): 4 | conn = sqlite3.connect("scripts/result/main.db") 5 | cursor = conn.cursor() 6 | 7 | # For each option, the values are for C1/C2/C3/C4/C5/inter/wets 8 | option = option.lower() 9 | if option == "default": 10 | grip_values = [0.85, 0.908, 0.93, 0.968, 1, 0.799, 0.726] 11 | durability_values = [1.75, 1.5, 1.089, 0.733, 0.578, 1.25, 2.25] 12 | for i in range(7): 13 | cursor.execute("UPDATE Tyres SET Grip = " + str(grip_values[i]) + " WHERE Type = " + str(i)) 14 | cursor.execute("UPDATE Tyres SET Durability = " + str(durability_values[i]) + " WHERE Type = " + str(i)) 15 | print("Default grip and durability values set") 16 | elif option == "grip-": 17 | grip_values = [0.57, 0.69, 0.78, 0.89, 1, 0.5, 0.4] 18 | durability_values = [1.75, 1.5, 1.089, 0.733, 0.578, 1.25, 2.25] 19 | for i in range(7): 20 | cursor.execute("UPDATE Tyres SET Grip = " + str(grip_values[i]) + " WHERE Type = " + str(i)) 21 | cursor.execute("UPDATE Tyres SET Durability = " + str(durability_values[i]) + " WHERE Type = " + str(i)) 22 | print("Reduced grip and default durability values set") 23 | elif option == "durability-": 24 | grip_values = [0.85, 0.908, 0.93, 0.968, 1, 0.799, 0.726] 25 | durability_values = [1.15, 1, 0.73, 0.50, 0.38, 0.83, 1.50] 26 | for i in range(7): 27 | cursor.execute("UPDATE Tyres SET Grip = " + str(grip_values[i]) + " WHERE Type = " + str(i)) 28 | cursor.execute("UPDATE Tyres SET Durability = " + str(durability_values[i]) + " WHERE Type = " + str(i)) 29 | print("Default grip and reduced durability values set") 30 | elif option == "both-": 31 | grip_values = [0.57, 0.69, 0.78, 0.89, 1, 0.5, 0.4] 32 | durability_values = [1.15, 1, 0.73, 0.50, 0.38, 0.83, 1.50] 33 | for i in range(7): 34 | cursor.execute("UPDATE Tyres SET Grip = " + str(grip_values[i]) + " WHERE Type = " + str(i)) 35 | cursor.execute("UPDATE Tyres SET Durability = " + str(durability_values[i]) + " WHERE Type = " + str(i)) 36 | print("Reduced grip and durability values set") 37 | 38 | conn.commit() 39 | conn.close() 40 | 41 | def get_description(): 42 | return "Choose the tyre behaviour you want. Available options are: default, grip-, durability-, both-.\nAuthor: Tahkare" 43 | 44 | if __name__ == '__main__': 45 | run_script() -------------------------------------------------------------------------------- /scripts/edit_stats.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | import random 3 | 4 | 5 | 6 | def run_script(option=""): 7 | conn = sqlite3.connect("scripts/result/main.db") 8 | cursor = conn.cursor() 9 | 10 | text = option.lower() 11 | params = text.split() 12 | 13 | driver = params[0].capitalize() 14 | multiple_drivers = ["Perez", "Raikkonen", "Hulkenberg", "Toth", "Stanek", "Villagomez", "Bolukbasi", "Marti"] 15 | for i in range(len(multiple_drivers)): 16 | if(driver == multiple_drivers[i]): 17 | driver = driver + "1" 18 | if(driver == "Aleclerc"): 19 | driver_id = (132,) 20 | else: 21 | driver_id = cursor.execute('SELECT StaffID FROM Staff_CommonData WHERE StaffType = 0 AND LastName = "[StaffName_Surname_' + str(driver) + ']"').fetchone(); 22 | 23 | print(driver_id, driver) 24 | 25 | if(params[1] != "all" and params[1] != "aggression" and params[1] != "growth"): 26 | stat_id = cursor.execute("SELECT Value FROM Staff_Enum_PerformanceStatTypes WHERE Name = " + '"' + params[1].capitalize() + '"').fetchone() 27 | if(params[2] == "++"): 28 | ability = cursor.execute("SELECT Val FROM Staff_performanceStats WHERE StatID = " + str(stat_id[0]) + " AND StaffID = " + str(driver_id[0])).fetchone() 29 | cursor.execute("UPDATE Staff_PerformanceStats SET Val = " + str(ability[0]+5) + " WHERE StatID = " + str(stat_id[0]) + " AND StaffID = " + str(driver_id[0])) 30 | elif(params[2] == "+"): 31 | ability = cursor.execute("SELECT Val FROM Staff_performanceStats WHERE StatID = " + str(stat_id[0]) + " AND StaffID = " + str(driver_id[0])).fetchone() 32 | cursor.execute("UPDATE Staff_PerformanceStats SET Val = " + str(ability[0]+2) + " WHERE StatID = " + str(stat_id[0]) + " AND StaffID = " + str(driver_id[0])) 33 | elif(params[2] == "-"): 34 | ability = cursor.execute("SELECT Val FROM Staff_performanceStats WHERE StatID = " + str(stat_id[0]) + " AND StaffID = " + str(driver_id[0])).fetchone() 35 | cursor.execute("UPDATE Staff_PerformanceStats SET Val = " + str(ability[0]-2) + " WHERE StatID = " + str(stat_id[0]) + " AND StaffID = " + str(driver_id[0])) 36 | elif(params[2] == "--"): 37 | ability = cursor.execute("SELECT Val FROM Staff_performanceStats WHERE StatID = " + str(stat_id[0]) + " AND StaffID = " + str(driver_id[0])).fetchone() 38 | cursor.execute("UPDATE Staff_PerformanceStats SET Val = " + str(ability[0]-5) + " WHERE StatID = " + str(stat_id[0]) + " AND StaffID = " + str(driver_id[0])) 39 | else: 40 | cursor.execute("UPDATE Staff_PerformanceStats SET Val = " + params[2] + " WHERE StatID = " + str(stat_id[0]) + " AND StaffID = " + str(driver_id[0])) 41 | elif(params[1] == "aggression"): 42 | if(params[2] == "high"): 43 | cursor.execute("UPDATE Staff_DriverData SET Aggression = 85 WHERE StaffID = " + str(driver_id[0])) 44 | elif(params[2] == "medium"): 45 | cursor.execute("UPDATE Staff_DriverData SET Aggression = 50 WHERE StaffID = " + str(driver_id[0])) 46 | elif(params[2] == "low"): 47 | cursor.execute("UPDATE Staff_DriverData SET Aggression = 20 WHERE StaffID = " + str(driver_id[0])) 48 | elif(params[1] == "growth"): 49 | if(params[2] == "high"): 50 | cursor.execute("UPDATE Staff_DriverData SET Improvability = 85 WHERE StaffID = " + str(driver_id[0])) 51 | elif(params[2] == "medium"): 52 | cursor.execute("UPDATE Staff_DriverData SET Improvability = 50 WHERE StaffID = " + str(driver_id[0])) 53 | elif(params[2] == "low"): 54 | cursor.execute("UPDATE Staff_DriverData SET Improvability = 20 WHERE StaffID = " + str(driver_id[0])) 55 | elif(params[1] == "all"): 56 | if(params[2] == "++"): 57 | for i in range (2, 11): 58 | ability = cursor.execute("SELECT Val FROM Staff_PerformanceStats WHERE StatID = " + str(i) + " AND StaffID = " + str(driver_id[0])).fetchone() 59 | cursor.execute("UPDATE Staff_PerformanceStats SET Val = " + str(ability[0]+5) + " WHERE StatID = " + str(i) + " AND StaffID = " + str(driver_id[0])) 60 | elif(params[2] == "+"): 61 | for i in range (2, 11): 62 | ability = cursor.execute("SELECT Val FROM Staff_PerformanceStats WHERE StatID = " + str(i) + " AND StaffID = " + str(driver_id[0])).fetchone() 63 | cursor.execute("UPDATE Staff_PerformanceStats SET Val = " + str(ability[0]+2) + " WHERE StatID = " + str(i) + " AND StaffID = " + str(driver_id[0])) 64 | elif(params[2] == "-"): 65 | for i in range (2, 11): 66 | ability = cursor.execute("SELECT Val FROM Staff_PerformanceStats WHERE StatID = " + str(i) + " AND StaffID = " + str(driver_id[0])).fetchone() 67 | cursor.execute("UPDATE Staff_PerformanceStats SET Val = " + str(ability[0]-2) + " WHERE StatID = " + str(i) + " AND StaffID = " + str(driver_id[0])) 68 | elif(params[2] == "--"): 69 | for i in range (2, 11): 70 | ability = cursor.execute("SELECT Val FROM Staff_PerformanceStats WHERE StatID = " + str(i) + " AND StaffID = " + str(driver_id[0])).fetchone() 71 | cursor.execute("UPDATE Staff_PerformanceStats SET Val = " + str(ability[0]-5) + " WHERE StatID = " + str(i) + " AND StaffID = " + str(driver_id[0])) 72 | else: 73 | for i in range (2, 11): 74 | cursor.execute("UPDATE Staff_PerformanceStats SET Val = " + params[2] + " WHERE StatID = " + str(i) + " AND StaffID = " + str(driver_id[0])) 75 | 76 | 77 | 78 | 79 | conn.commit() 80 | conn.close() 81 | 82 | 83 | def get_description(): 84 | return "Allows you to edit the ratings of each individual aspect of every driver in the game. \n More info on how to use in the stats_readme.txt \nAuthor: u/ignaciourreta" 85 | 86 | if __name__ == '__main__': 87 | run_script() -------------------------------------------------------------------------------- /scripts/extractor.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import os 3 | import zlib 4 | import struct 5 | import mmap 6 | 7 | CHNUK1_NAME = "chunk1" 8 | MAIN_DB_NAME = "main.db" 9 | BACKUP_DB_NAME = "backup1.db" 10 | BACKUP_DB2_NAME = "backup2.db" 11 | 12 | 13 | def create_db(fname, decompressed_db, _start, _end): 14 | with open(fname, 'wb') as f: 15 | f.write(decompressed_db[_start:_end]) 16 | 17 | 18 | def get_db_mmap(path): 19 | if not os.path.exists(path): 20 | return b'' 21 | 22 | with open(path, 'rb') as f: 23 | return mmap.mmap(f.fileno(), length=0, access=mmap.ACCESS_READ) 24 | 25 | 26 | def pack_databases(_dir): 27 | result = list() 28 | 29 | mmaps = [ 30 | get_db_mmap(os.path.join(_dir, MAIN_DB_NAME)), 31 | get_db_mmap(os.path.join(_dir, BACKUP_DB_NAME)), 32 | get_db_mmap(os.path.join(_dir, BACKUP_DB2_NAME)) 33 | ] 34 | 35 | to_zlib = b'' 36 | 37 | db_sizes = list() 38 | for mmap in mmaps: 39 | _sz = len(mmap) 40 | db_sizes.append(_sz) 41 | if _sz == 0: 42 | continue 43 | 44 | to_zlib += mmap.read() 45 | compressed = zlib.compress(to_zlib) 46 | result.append(struct.pack("I", len(compressed))) 47 | 48 | for db_size in db_sizes: 49 | result.append(struct.pack("I", db_size)) 50 | result.append(compressed) 51 | return result 52 | 53 | 54 | def do_pack(from_folder, to_file): 55 | chunk1_path = os.path.join(from_folder, CHNUK1_NAME) 56 | if not os.path.exists(chunk1_path): 57 | print(f"Can't find {chunk1_path}") 58 | return 59 | 60 | new_file_content = b'' 61 | with open(chunk1_path, 'rb') as f: 62 | new_file_content += f.read() 63 | 64 | for _bytes in pack_databases(from_folder): 65 | new_file_content += _bytes 66 | 67 | with open(to_file, 'wb') as f: 68 | f.write(new_file_content) 69 | 70 | 71 | def do_unpack(from_file, to_folder): 72 | with open(from_file, 'rb') as f: 73 | mm = mmap.mmap(f.fileno(), length=0, access=mmap.ACCESS_READ) 74 | 75 | # None None just before the packed DB Section. 76 | none_none_sig = b'\x00\x05\x00\x00\x00\x4E\x6F\x6E\x65\x00\x05\x00\x00\x00\x4E\x6F\x6E\x65\x00' 77 | 78 | db_section_off = mm.find(none_none_sig) + len(none_none_sig) 79 | db_section_off += 4 # Unk 4 Bytes 80 | 81 | # Part of the file that we ignore as it's not database 82 | # But we need it later to "pack" new save 83 | with open(os.path.join(to_folder, CHNUK1_NAME), 'wb') as f: 84 | f.write(mm.read(db_section_off)) 85 | 86 | zlib_sz = struct.unpack('i', mm.read(4))[0] 87 | 88 | databases = { 89 | os.path.join(to_folder, MAIN_DB_NAME): struct.unpack('i', mm.read(4))[0], 90 | os.path.join(to_folder, BACKUP_DB_NAME): struct.unpack('i', mm.read(4))[0], 91 | os.path.join(to_folder, BACKUP_DB2_NAME): struct.unpack('i', mm.read(4))[0] 92 | } 93 | 94 | decompressed_dbs = zlib.decompress(mm.read()) 95 | 96 | _start = 0 97 | for dump_name, db_size in databases.items(): 98 | # print(f'{dump_name}:{db_size}') 99 | if db_size == 0: 100 | break 101 | create_db(dump_name, decompressed_dbs, _start, _start+db_size) 102 | _start += db_size 103 | 104 | 105 | def process_unpack(input_file, result_dir): 106 | if not os.path.exists(input_file): 107 | print(f"Can't find {input_file}") 108 | return 109 | 110 | if not os.path.exists(result_dir): 111 | os.makedirs(result_dir) 112 | 113 | do_unpack(input_file, result_dir) 114 | 115 | 116 | def process_repack(input_dir, result_file): 117 | do_pack(input_dir, result_file) 118 | -------------------------------------------------------------------------------- /scripts/set_race.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | 3 | def run_script(): 4 | conn = sqlite3.connect("scripts/result/main.db") 5 | cursor = conn.cursor() 6 | 7 | # Getting infos to set up the race properly 8 | day_season = cursor.execute("SELECT Day, CurrentSeason FROM Player_State").fetchone() 9 | next_event = cursor.execute("SELECT RaceID FROM Races WHERE Day >= "+str(day_season[0])+" ORDER BY Day").fetchone() 10 | last_event = cursor.execute("SELECT RaceID, TrackID FROM Races WHERE Day <= "+str(day_season[0])+" ORDER BY Day DESC").fetchone() 11 | 12 | # Skipping ahead to the race 13 | cursor.execute("UPDATE Player_State SET Day = "+str(day_season[0]+2)) 14 | cursor.execute("UPDATE Save_Weekend SET WeekendStage = 9") 15 | cursor.execute("UPDATE Save_Weekend SET SimulatedQ1 = 1") 16 | cursor.execute("UPDATE Save_Weekend SET SimulatedQ2 = 1") 17 | cursor.execute("UPDATE Save_Weekend SET SimulatedQ3 = 1") 18 | 19 | # Restoring the proper lap count 20 | cursor.execute("UPDATE Races_Tracks SET Laps = 63 WHERE TrackID = 24") 21 | cursor.execute("UPDATE Races_Tracks SET Laps = 71 WHERE TrackID = 9") 22 | cursor.execute("UPDATE Races_Tracks SET Laps = 71 WHERE TrackID = 20") 23 | print("Main race has been set up") 24 | 25 | # Restoring the point scheme 26 | cursor.execute("UPDATE Regulations_Enum_Changes SET CurrentValue = 1 WHERE Name = 'PointScheme'") 27 | cursor.execute("UPDATE Regulations_Enum_Changes SET CurrentValue = 1 WHERE Name = 'FastestLapBonusPoint'") 28 | print("Point scheme has been set up") 29 | 30 | conn.commit() 31 | 32 | # Correcting sprint finish order 33 | sprint_result = cursor.execute("SELECT Season, FinishingPos, DriverID, TeamID, Laps, Time, Points FROM Races_Results WHERE RaceID = "+str(last_event[0])).fetchall() 34 | adjusted_result = sorted(sprint_result, key=lambda x:x[4]*10000-x[5], reverse=True) 35 | for i in range(20): 36 | cursor.execute("UPDATE Races_Results SET FinishingPos = FinishingPos + 20 WHERE RaceID = " + str(last_event[0]) + " AND DriverID = " + str(adjusted_result[i][2])) 37 | 38 | for i in range(20): 39 | if adjusted_result[i][1] != i+1: 40 | point_change = adjusted_result[i][6] - max(0, 8-i) 41 | else: 42 | point_change = 0 43 | cursor.execute("UPDATE Races_Results SET Points = "+ str(max(0, 8-i)) + " WHERE RaceID = " + str(last_event[0]) + " AND DriverID = " + str(adjusted_result[i][2])) 44 | cursor.execute("UPDATE Races_Results SET DNF = 0 WHERE RaceID = " + str(last_event[0]) + " AND DriverID = " + str(adjusted_result[i][2])) 45 | cursor.execute("UPDATE Races_Results SET FinishingPos = " + str(i+1) +" WHERE RaceID = " + str(last_event[0]) + " AND DriverID = " + str(adjusted_result[i][2])) 46 | cursor.execute("UPDATE Races_DriverStandings SET Points = Points + " + str(point_change) + " WHERE SeasonID = " + str(adjusted_result[i][0]) + " AND DriverID = " + str(adjusted_result[i][2])) 47 | cursor.execute("UPDATE Races_DriverStandings SET LastPointsChange = LastPointsChange + " + str(point_change) + " WHERE SeasonID = " + str(adjusted_result[i][0]) + " AND DriverID = " + str(adjusted_result[i][2])) 48 | cursor.execute("UPDATE Races_DriverStandings SET LastPositionChange = 0 WHERE SeasonID = " + str(adjusted_result[i][0]) + " AND DriverID = " + str(adjusted_result[i][2])) 49 | cursor.execute("UPDATE Races_TeamStandings SET Points = Points + " + str(point_change) + " WHERE SeasonID = " + str(adjusted_result[i][0]) + " AND TeamID = " + str(adjusted_result[i][3])) 50 | cursor.execute("UPDATE Races_TeamStandings SET LastPointsChange = LastPointsChange + " + str(point_change) + " WHERE SeasonID = " + str(adjusted_result[i][0]) + " AND TeamID = " + str(adjusted_result[i][3])) 51 | cursor.execute("UPDATE Races_TeamStandings SET LastPositionChange = 0 WHERE SeasonID = " + str(adjusted_result[i][0]) + " AND TeamID = " + str(adjusted_result[i][3])) 52 | 53 | conn.commit() 54 | print("Sprint race results have been validated") 55 | 56 | 57 | # Setting up the grid properly 58 | sprint_result = cursor.execute("SELECT Season, FinishingPos, DriverID, TeamID, Laps, Time, Points FROM Races_Results WHERE RaceID = "+str(last_event[0])).fetchall() 59 | if last_event[1] == 24: 60 | lap_count = 21 61 | else: 62 | lap_count = 24 63 | for element in sprint_result: 64 | cursor.execute("INSERT INTO Races_QualifyingResults VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", (element[0], next_event[0], 1, element[1], element[2], element[3], element[5], 0, lap_count)) 65 | if element[1] <= 15: 66 | cursor.execute("INSERT INTO Races_QualifyingResults VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", (element[0], next_event[0], 2, element[1], element[2], element[3], element[5], 0, lap_count)) 67 | if element[1] <= 10: 68 | cursor.execute("INSERT INTO Races_QualifyingResults VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", (element[0], next_event[0], 3, element[1], element[2], element[3], element[5], 0, lap_count)) 69 | print("Starting grid has been set up") 70 | 71 | conn.commit() 72 | conn.close() 73 | 74 | def get_description(): 75 | return "Sets up the main race at Imola, Spielberg and Interlagos. Use it after starting the second weekend and before FP1.\nAuthor: Tahkare" 76 | 77 | if __name__ == '__main__': 78 | run_script() 79 | -------------------------------------------------------------------------------- /scripts/set_sprint.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | 3 | def run_script(): 4 | conn = sqlite3.connect("scripts/result/main.db") 5 | cursor = conn.cursor() 6 | 7 | # Getting infos to create a duplicate weekend 8 | day_season = cursor.execute("SELECT Day, CurrentSeason FROM Player_State").fetchone() 9 | next_event = cursor.execute("SELECT * FROM Races WHERE Day >= ? AND SeasonID = ? ORDER BY Day", day_season).fetchone() 10 | race_count = cursor.execute("SELECT MAX(RaceID) FROM Races").fetchone() 11 | 12 | # Duplicating the weekend 13 | new_event = (race_count[0]+1, next_event[1], next_event[2]+3, next_event[3], next_event[4], next_event[5], next_event[6], next_event[7], next_event[8], next_event[9], next_event[10], next_event[11], next_event[12], next_event[13]) 14 | cursor.execute("INSERT INTO Races VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", new_event) 15 | 16 | # Updating lap count 17 | cursor.execute("UPDATE Races_Tracks SET Laps = 21 WHERE TrackID = 24") 18 | cursor.execute("UPDATE Races_Tracks SET Laps = 24 WHERE TrackID = 9") 19 | cursor.execute("UPDATE Races_Tracks SET Laps = 24 WHERE TrackID = 20") 20 | print("Sprint race has been set up") 21 | 22 | # Updating point system 23 | cursor.execute("UPDATE Regulations_NonTechnical_PointSchemes SET Points = 8 WHERE PointScheme = 3 AND RacePos = 1") 24 | cursor.execute("UPDATE Regulations_NonTechnical_PointSchemes SET Points = 7 WHERE PointScheme = 3 AND RacePos = 2") 25 | cursor.execute("UPDATE Regulations_NonTechnical_PointSchemes SET Points = 6 WHERE PointScheme = 3 AND RacePos = 3") 26 | cursor.execute("UPDATE Regulations_NonTechnical_PointSchemes SET Points = 5 WHERE PointScheme = 3 AND RacePos = 4") 27 | cursor.execute("UPDATE Regulations_NonTechnical_PointSchemes SET Points = 4 WHERE PointScheme = 3 AND RacePos = 5") 28 | cursor.execute("UPDATE Regulations_NonTechnical_PointSchemes SET Points = 3 WHERE PointScheme = 3 AND RacePos = 6") 29 | cursor.execute("UPDATE Regulations_NonTechnical_PointSchemes SET Points = 2 WHERE PointScheme = 3 AND RacePos = 7") 30 | cursor.execute("UPDATE Regulations_NonTechnical_PointSchemes SET Points = 1 WHERE PointScheme = 3 AND RacePos = 8") 31 | cursor.execute("UPDATE Regulations_Enum_Changes SET CurrentValue = 3 WHERE Name = 'PointScheme'") 32 | cursor.execute("UPDATE Regulations_Enum_Changes SET CurrentValue = 0 WHERE Name = 'FastestLapBonusPoint'") 33 | print("Point scheme has been set up") 34 | 35 | conn.commit() 36 | conn.close() 37 | 38 | def get_description(): 39 | return "Sets up the sprint race at Imola, Spielberg and Interlagos. Use it before starting the weekend.\nAuthor: Tahkare" 40 | 41 | if __name__ == '__main__': 42 | run_script() -------------------------------------------------------------------------------- /scripts/shuffle_calendar.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | import random 3 | 4 | def run_script(): 5 | conn = sqlite3.connect("scripts/result/main.db") 6 | cursor = conn.cursor() 7 | 8 | track_ids = [i for i in range(1,25) if i != 3 and i != 16] 9 | random.shuffle(track_ids) 10 | 11 | # Getting all the current season races 12 | day_season = cursor.execute("SELECT Day, CurrentSeason FROM Player_State").fetchone() 13 | season_events = cursor.execute("SELECT RaceID FROM Races WHERE SeasonID = " + str(day_season[1])).fetchall() 14 | 15 | # Inserting the new calendar 16 | for i in range(len(track_ids)): 17 | cursor.execute("UPDATE Races SET TrackID = " + str(track_ids[i]) + " WHERE RaceID = " + str(season_events[i][0])) 18 | print("Shuffled the calendar") 19 | 20 | conn.commit() 21 | conn.close() 22 | 23 | def get_description(): 24 | return "Shuffles the current season calendar randomly. Use it before the first race of the season.\nAuthor: Tahkare" 25 | 26 | if __name__ == '__main__': 27 | run_script() -------------------------------------------------------------------------------- /scripts/shuffle_engines.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | import random 3 | 4 | def run_script(): 5 | conn = sqlite3.connect("scripts/result/main.db") 6 | cursor = conn.cursor() 7 | 8 | # The engine id, ers id and gearbox id for each powertrain 9 | design_ids = [[], [1,2,3], [10,11,12], [4,5,6], [7,8,9]] 10 | 11 | for i in range(1, 5): 12 | powertrain_cost = cursor.execute("SELECT Cost FROM Parts_Enum_EngineManufacturers WHERE Value = " + str(i)).fetchone() 13 | engine_stats = cursor.execute("SELECT DesignID, StatID, Value, UnitValue FROM Parts_DesignStatValues WHERE DesignID = " + str(design_ids[i][0])).fetchall() 14 | ers_stat = cursor.execute("SELECT DesignID, StatID, Value, UnitValue FROM Parts_DesignStatValues WHERE DesignID = " + str(design_ids[i][1])).fetchone() 15 | gearbox_stat = cursor.execute("SELECT DesignID, StatID, Value, UnitValue FROM Parts_DesignStatValues WHERE DesignID = " + str(design_ids[i][2])).fetchone() 16 | stats = engine_stats + [ers_stat] + [gearbox_stat] 17 | 18 | perf_difference = 0 19 | for j in range(7): 20 | change = max(min(random.randint(-3, 3), 100-stats[j][3]), -stats[j][3]) 21 | print(change) 22 | perf_difference += change 23 | if stats[j][1] == 12: 24 | change = change/10 25 | if change >= 0: 26 | cursor.execute("UPDATE Parts_DesignStatValues SET Value = Value + " + str(change*10) + " WHERE DesignID = " + str(stats[j][0]) + " AND StatID = " + str(stats[j][1])) 27 | cursor.execute("UPDATE Parts_DesignStatValues SET UnitValue = UnitValue + " + str(change) + " WHERE DesignID = " + str(stats[j][0]) + " AND StatID = " + str(stats[j][1])) 28 | else: 29 | change = -change 30 | cursor.execute("UPDATE Parts_DesignStatValues SET Value = Value - " + str(change*10) + " WHERE DesignID = " + str(stats[j][0]) + " AND StatID = " + str(stats[j][1])) 31 | cursor.execute("UPDATE Parts_DesignStatValues SET UnitValue = UnitValue - " + str(change) + " WHERE DesignID = " + str(stats[j][0]) + " AND StatID = " + str(stats[j][1])) 32 | 33 | cost_change = abs(perf_difference) // 3 34 | print(cost_change) 35 | if perf_difference >= 0: 36 | cursor.execute("UPDATE Parts_Enum_EngineManufacturers SET Cost = Cost + " + str(cost_change * 500000) + " WHERE Value = " + str(i)) 37 | else: 38 | cursor.execute("UPDATE Parts_Enum_EngineManufacturers SET Cost = Cost - " + str(cost_change * 500000) + " WHERE Value = " + str(i)) 39 | 40 | conn.commit() 41 | print("Successfully updated engine", i) 42 | 43 | conn.commit() 44 | conn.close() 45 | 46 | def get_description(): 47 | return "Randomly updates the engines performances. Use it before the first race of the season.\nAuthor: Tahkare" 48 | 49 | if __name__ == '__main__': 50 | run_script() --------------------------------------------------------------------------------