719 lines
26 KiB
Python
719 lines
26 KiB
Python
import argparse
|
|
import copy
|
|
import os
|
|
import datetime
|
|
from bs4 import BeautifulSoup
|
|
|
|
import rich
|
|
from rich.console import Console
|
|
|
|
# Only "normal storage" is used to insert requested items
|
|
normal_storage_ids = [82, 632]
|
|
|
|
storage_ids = {
|
|
82: {"name": "Small storage", "capacity": 50},
|
|
632: {"name": "Large storage", "capacity": 250},
|
|
3062: {"name": "Body storage", "capacity": 20},
|
|
}
|
|
|
|
char_skills = {
|
|
1: "Piloting", # Likely deprecated - now using code 22
|
|
2: "Mining",
|
|
3: "Botany",
|
|
4: "Construction",
|
|
5: "Industry",
|
|
6: "Medical",
|
|
7: "Gunner",
|
|
8: "Shielding",
|
|
9: "Operations",
|
|
10: "Weapons",
|
|
11: "Unknown-1",
|
|
12: "Logistics", # All characters seem to have this but it doesn't show up in the UI
|
|
13: "Unknown-2-enigma", # All characters seem to have this skill but what is it for?
|
|
14: "Navigation",
|
|
15: "Unknown-3",
|
|
16: "Research",
|
|
22: "Piloting", # Appears to be the new piloting code?
|
|
}
|
|
|
|
char_attributes = {
|
|
210: "Bravery",
|
|
214: "Preception",
|
|
213: "Intelligence",
|
|
212: "Zest",
|
|
}
|
|
|
|
char_traits = {
|
|
"Iron Stomach": 1533,
|
|
"Spacefarer": 1045,
|
|
"Confident": 1046,
|
|
"Hard Working": 1041,
|
|
"Antisocial": 1037,
|
|
"Nyctophilia": 1534,
|
|
"Wimp": 655,
|
|
"Clumsy": 656,
|
|
"Charming": 1048,
|
|
"Bloodlust": 1036,
|
|
"Fast Learner": 1039,
|
|
"Minimalist": 1535,
|
|
"Lazy": 1040,
|
|
"Neurotic": 1047,
|
|
"Alien Lover": 2082,
|
|
"Needy": 1038,
|
|
"Peace-loving": 1043,
|
|
"Suicidal": 1034,
|
|
"Smart": 1035,
|
|
"Psychopath": 1042,
|
|
"Hero": 191,
|
|
"Talkative": 1560,
|
|
"Iron-Willed": 1044,
|
|
"Gourmand": 1562,
|
|
}
|
|
|
|
|
|
class ItemCodeDatabase:
|
|
|
|
def __init__(self, database_filename):
|
|
self.id_dict = {}
|
|
|
|
for line in open(database_filename):
|
|
result = line.split()
|
|
code = int(result[0])
|
|
name = ' '.join(result[1:])
|
|
# print(code, name)
|
|
self.id_dict[code] = name
|
|
|
|
def get_name_from_code(self, code):
|
|
return self.id_dict[code]
|
|
|
|
def validate_code(self, code):
|
|
return code in self.id_dict
|
|
|
|
|
|
# A single item with its code, name, and quantity
|
|
class Item:
|
|
|
|
def __init__(self, code, name, quantity):
|
|
self.code = code
|
|
self.name = name
|
|
self.quantity = quantity
|
|
|
|
|
|
# A single storage area, with a list of contents (Items)
|
|
class StorageArea:
|
|
|
|
def __init__(self, tag):
|
|
self.tag = tag
|
|
self.type_id = None
|
|
self.items = []
|
|
self.is_abnormal_storage = False
|
|
|
|
def add_item(self, item):
|
|
self.items.append(item)
|
|
|
|
# Returns how full the storage area is based on its contents
|
|
def get_total_occupancy(self):
|
|
total_quantity = 0
|
|
for item in self.items:
|
|
total_quantity += item.quantity
|
|
return total_quantity
|
|
|
|
|
|
class Character:
|
|
|
|
def __init__(self, name, tag):
|
|
self.name = name
|
|
self.tag = tag
|
|
self.is_clone = False
|
|
|
|
self.skills = []
|
|
self.attributes = []
|
|
|
|
def set_skills(self, skill_list):
|
|
# Expected format is a list of dictionaries
|
|
# Each dictionary has keys id, mxn, exp, expd, level
|
|
self.skills = skill_list
|
|
|
|
def set_attributes(self, attribute_list):
|
|
# Expected format is a list of dictionaries
|
|
# Each dictionary has keys id, points
|
|
self.attributes = attribute_list
|
|
|
|
def maximize_skills(self):
|
|
# Max LEVEL is 5
|
|
# Max MXN should be 8 for some reason
|
|
# Exp* keys are probably for a future experience system but I don't know how they work so I won't touch them
|
|
for skill in self.skills:
|
|
skill['level'] = 5
|
|
skill['mxn'] = 8
|
|
|
|
def maximize_attributes(self):
|
|
# Max POINTS is 6
|
|
for attribute in self.attributes:
|
|
attribute['points'] = 6
|
|
|
|
def clone(self, new_name):
|
|
# How to deep copy the skills/attributes appropriately?
|
|
new_char = Character(new_name, copy.copy(self.tag))
|
|
new_char.is_clone = True
|
|
new_char.tag['name'] = new_name
|
|
|
|
new_skills = [s.copy() for s in self.skills]
|
|
new_attributes = [a.copy() for a in self.attributes]
|
|
new_char.skills = new_skills
|
|
new_char.attributes = new_attributes
|
|
return new_char
|
|
|
|
def print_summary(self):
|
|
console = Console(highlight=False)
|
|
console.print("Name: [bright_cyan]{}[/]".format(self.name))
|
|
console.print()
|
|
console.print("Skills:")
|
|
for i, skill in enumerate(self.skills):
|
|
row_string = "{:20} [bright_cyan]{:2}[/]".format(char_skills[skill['id']], skill['level'])
|
|
if i % 2 == 0:
|
|
console.print(" [on #222222]{}[/]".format(row_string))
|
|
else:
|
|
console.print(" {}".format(row_string))
|
|
console.print()
|
|
console.print("Attributes:")
|
|
for i, attribute in enumerate(self.attributes):
|
|
row_string = "{:12} [bright_cyan]{:2}[/]".format(char_attributes[attribute['id']], attribute['points'])
|
|
if i % 2 == 0:
|
|
console.print(" [on #222222]{}[/]".format(row_string))
|
|
else:
|
|
console.print(" {}".format(row_string))
|
|
|
|
def __repr__(self):
|
|
return "{} - Skills: {} - Attributes: {}".format(self.name, repr(self.skills), repr(self.attributes))
|
|
|
|
|
|
class Ship:
|
|
|
|
def __init__(self, name, owner, state, tag):
|
|
self.name = name
|
|
self.owner = owner
|
|
self.state = state
|
|
self.tag = tag # Soup tag corresponding to this ship's node
|
|
|
|
self.storage_areas = []
|
|
self.characters = []
|
|
|
|
def add_storage_area(self, storage_area):
|
|
self.storage_areas.append(storage_area)
|
|
|
|
def add_character(self, character):
|
|
self.characters.append(character)
|
|
|
|
def add_item(self, item):
|
|
normal_storage_areas = [sa for sa in self.storage_areas if not sa.is_abnormal_storage]
|
|
min_storage_area = normal_storage_areas[0]
|
|
min_occupancy = min_storage_area.get_total_occupancy()
|
|
for storage_area in normal_storage_areas:
|
|
if storage_area.get_total_occupancy() < min_occupancy:
|
|
min_storage_area = storage_area
|
|
min_occupancy = min_storage_area.get_total_occupancy()
|
|
min_storage_area.add_item(item)
|
|
|
|
|
|
class Player:
|
|
|
|
def __init__(self, currency, tag):
|
|
self.currency = currency
|
|
self.tag = tag
|
|
|
|
|
|
class GameData:
|
|
|
|
def __init__(self, soup, item_database):
|
|
self.player = None
|
|
self.ships = []
|
|
|
|
self.soup = soup
|
|
self.item_database = item_database
|
|
|
|
def populate(self):
|
|
# Step 1 - Player data
|
|
char_tag = self.soup.find('playerBank')
|
|
currency = int(char_tag['ca'])
|
|
self.player = Player(currency, char_tag)
|
|
|
|
# Step 2 - Ship data
|
|
ship_tags = self.soup.find_all('ship')
|
|
|
|
for ship_tag in ship_tags:
|
|
# Something strange is happening here
|
|
# In some unexplored ships/derelicts (?fog="true" in the tag) the name appears in the game but NOWHERE in the file
|
|
ship_name = "UNNAMED (?fogged)"
|
|
if ship_tag.has_attr('sname'):
|
|
ship_name = ship_tag['sname']
|
|
# print(ship_name)
|
|
|
|
# Forgotten - why did I specify owner as required here - what ships don't have owners?
|
|
owner_node = ship_tag.find('settings', owner=True)
|
|
ship_owner = owner_node['owner']
|
|
ship_state = owner_node['state']
|
|
|
|
ship = Ship(ship_name, ship_owner, ship_state, ship_tag)
|
|
self.ships.append(ship)
|
|
|
|
# Step 3 - Storage area data
|
|
for inv_tag in ship_tag.find_all('feat', eatAllowed=True):
|
|
storage_area = StorageArea(inv_tag.find('inv'))
|
|
# 632 is the "m" id code for LARGE storage
|
|
storage_area.type_id = int(inv_tag.parent.parent['m'])
|
|
if storage_area.type_id not in normal_storage_ids: # TODO: Un-hardcode this AND take into account SMALL storage
|
|
# rich.print("[on #222222]Found abnormal storage id [#666666]{}[/][/]".format(inv_tag.parent.parent['m']))
|
|
storage_area.is_abnormal_storage = True
|
|
ship.add_storage_area(storage_area)
|
|
# Items within storage area
|
|
for s_tag in inv_tag.find_all('s'):
|
|
item_code = int(s_tag['elementaryId'])
|
|
item_name = self.item_database.get_name_from_code(item_code)
|
|
item_quantity = int(s_tag['inStorage'])
|
|
item = Item(item_code, item_name, item_quantity)
|
|
storage_area.add_item(item)
|
|
# print("{:4}: {} - {}".format(item_code, item_name, item_quantity))
|
|
|
|
# Step 4 - Character data
|
|
for character_list in ship_tag.find_all('characters'):
|
|
character_tags = character_list.find_all('c', attrs={'name': True})
|
|
for character_tag in character_tags:
|
|
char_name = character_tag['name']
|
|
character = Character(char_name, character_tag)
|
|
ship.add_character(character)
|
|
|
|
skills = []
|
|
skill_tag = character_tag.find('skills')
|
|
for sk_tag in skill_tag.find_all('s'):
|
|
skill_id = sk_tag['sk']
|
|
skill_level = sk_tag['level']
|
|
skill_mxn = sk_tag['mxn']
|
|
skill_exp = sk_tag['exp']
|
|
skill_expd = sk_tag['expd']
|
|
skill_dict = {
|
|
'id': int(skill_id),
|
|
'level': int(skill_level),
|
|
'mxn': int(skill_mxn),
|
|
'exp': int(skill_exp),
|
|
'expd': int(skill_expd),
|
|
}
|
|
skills.append(skill_dict)
|
|
character.set_skills(skills)
|
|
|
|
attributes = []
|
|
attribute_tag = character_tag.find('attr')
|
|
for a_tag in attribute_tag.find_all('a'):
|
|
attribute_id = a_tag['id']
|
|
attribute_points = a_tag['points']
|
|
attribute_dict = {
|
|
'id': int(attribute_id),
|
|
'points': int(attribute_points),
|
|
}
|
|
attributes.append(attribute_dict)
|
|
character.set_attributes(attributes)
|
|
|
|
def writeback(self):
|
|
def replace_id(dict, old_key, new_key):
|
|
dict_copy = dict.copy()
|
|
dict_copy[new_key] = dict_copy[old_key]
|
|
del dict_copy[old_key]
|
|
return dict_copy
|
|
|
|
# Purpose of this mission is to take all our data and replace the relevant parts of the soup
|
|
# Suspect this may be harder than it sounds - original saved game editor more or less deleted and rewrote some sections (e.g. item lists)
|
|
|
|
# Shouldn't need to update player tag as this will be done directly
|
|
# Only need to update things with structure / lists
|
|
|
|
"""
|
|
Step 1 - Update characters
|
|
Step 2 - Update storage areas
|
|
"""
|
|
|
|
# Step 1 - Update characters
|
|
# Names etc will be done automatically - just need to reconstruct skills and attributes
|
|
|
|
self.player.tag['ca'] = self.player.currency
|
|
|
|
for ship in self.ships:
|
|
for character in ship.characters:
|
|
# Cloned character tags have to be added to the list
|
|
if character.is_clone:
|
|
print("ADDING CLONED CHARACTER")
|
|
charlist_tag = ship.tag.find('characters')
|
|
charlist_tag.append(character.tag)
|
|
|
|
skill_tag = character.tag.find('skills')
|
|
skill_tag.clear()
|
|
for skill in character.skills:
|
|
skill_copy = replace_id(skill, 'id', 'sk')
|
|
new_tag = self.soup.new_tag('s', attrs=skill_copy)
|
|
skill_tag.append(new_tag)
|
|
|
|
attribute_tag = character.tag.find('attr')
|
|
attribute_tag.clear()
|
|
for attribute in character.attributes:
|
|
new_tag = self.soup.new_tag('a', attrs=attribute)
|
|
attribute_tag.append(new_tag)
|
|
|
|
for storage_area in ship.storage_areas:
|
|
area_tag = storage_area.tag
|
|
area_tag.clear()
|
|
for item in storage_area.items:
|
|
tag_dict = {'elementaryId': item.code, 'inStorage': item.quantity, 'onTheWayIn': 0, 'onTheWayOut': 0}
|
|
new_tag = self.soup.new_tag('s', attrs=tag_dict)
|
|
area_tag.append(new_tag)
|
|
|
|
def add_item(self, item_code, item_quantity):
|
|
item_name = self.item_database.get_name_from_code(item_code)
|
|
item = Item(item_code, item_name, item_quantity)
|
|
for ship in self.ships:
|
|
if ship.owner == "Player":
|
|
ship.add_item(item)
|
|
break
|
|
|
|
def buff_characters(self):
|
|
for ship in self.ships:
|
|
if ship.owner == "Player":
|
|
for character in ship.characters:
|
|
character.maximize_skills()
|
|
character.maximize_attributes()
|
|
|
|
def add_currency(self, amount):
|
|
self.player.currency += amount
|
|
|
|
def clone_character(self, character_name, new_name):
|
|
for ship in self.ships:
|
|
for character in ship.characters:
|
|
if character.name == character_name:
|
|
new_char = character.clone(new_name)
|
|
ship.characters.append(new_char)
|
|
return
|
|
|
|
def print_detailed_character_summary(self):
|
|
console = Console(highlight=False)
|
|
for ship in self.ships:
|
|
console.print("Listing characters for ship [magenta]{}[/] (owned by [bright_cyan]{}[/], state [bright_cyan]{}[/]):".format(ship.name, ship.owner, ship.state))
|
|
if len(ship.characters) == 0:
|
|
console.print(" This ship has no characters, skipping...")
|
|
console.print()
|
|
console.print()
|
|
continue
|
|
|
|
for character in ship.characters:
|
|
character.print_summary()
|
|
console.print('-----')
|
|
|
|
console.print()
|
|
|
|
def print_detailed_item_summary(self):
|
|
console = Console(highlight=False)
|
|
for ship in self.ships:
|
|
rich.print("Inventory for ship [#aa00ff]{}[/] (owned by [bright_cyan]{}[/], state [bright_cyan]{}[/]):".format(ship.name, ship.owner, ship.state))
|
|
if len(ship.storage_areas) == 0:
|
|
console.print(" This ship has no storage areas, skipping...")
|
|
console.print()
|
|
console.print()
|
|
continue
|
|
|
|
for index, storage_area in enumerate(ship.storage_areas):
|
|
console.print(" Storage area [bright_cyan]{}[/] (type: [bright_cyan]{}[/], abnormal storage: [bright_cyan]{}[/]):".format(index, storage_ids[storage_area.type_id]["name"], storage_area.is_abnormal_storage))
|
|
if len(storage_area.items) == 0:
|
|
console.print(" This storage area is empty.")
|
|
console.print()
|
|
continue
|
|
|
|
console.print("[bold] {:>4} {:30} {:>3}[/]".format("ID", "Name", "#"))
|
|
total_quantity = 0
|
|
for i, item in enumerate(storage_area.items):
|
|
row_string = " {:4} {:30} {:3}".format(item.code, item.name, item.quantity)
|
|
total_quantity += item.quantity
|
|
if i % 2 == 0:
|
|
console.print(" [on #222222]{}[/]".format(row_string))
|
|
else:
|
|
console.print(" {}".format(row_string))
|
|
storage_area_capacity = storage_ids[storage_area.type_id]["capacity"]
|
|
console.print(" Total quantity: [bright_cyan]{}[/]/[bright_cyan]{}[/]".format(total_quantity, storage_area_capacity))
|
|
if total_quantity > storage_area_capacity:
|
|
console.print(" [dark_orange]WARNING: Storage capacity exceeded[/]")
|
|
console.print()
|
|
console.print()
|
|
|
|
def print_summary(self):
|
|
print("Start game summary:")
|
|
print(" Player currency: {}".format(self.player.currency))
|
|
print(" Number of ships: {}".format(len(self.ships)))
|
|
|
|
for ship in self.ships:
|
|
print(" {} (owner {}, state {})".format(ship.name, ship.owner, ship.state))
|
|
print(" Contains {} storage area(s):".format(len(ship.storage_areas)))
|
|
for index, storage_area in enumerate(ship.storage_areas):
|
|
print(" Storage area {} - contains {} item(s) - occupancy {} unit(s)".format(index, len(storage_area.items), storage_area.get_total_occupancy()))
|
|
print(" Has {} character(s):".format(len(ship.characters)))
|
|
for char in ship.characters:
|
|
print(" {}".format(char.name))
|
|
# char.print_summary()
|
|
|
|
|
|
def characters(soup):
|
|
for character in soup.find_all('characters'):
|
|
c_elems = character.find_all('c')
|
|
if len(c_elems) > 0:
|
|
print('Found some appropriate c-tags')
|
|
for char_c in c_elems:
|
|
# print(char_c['name'])
|
|
if 'name' in char_c.attrs:
|
|
print(char_c['name'])
|
|
# We have found a character tag!
|
|
|
|
# ---- SKILL UPRGRADING
|
|
skill_tag = char_c.find('skills')
|
|
print(skill_tag)
|
|
# Experimental changing
|
|
for sk_tag in skill_tag.find_all('s'):
|
|
sk_tag['level'] = '5'
|
|
sk_tag['mxn'] = '8'
|
|
if 'mxp' in sk_tag.attrs:
|
|
sk_tag['mxp'] = '8'
|
|
print(skill_tag)
|
|
|
|
# ---- ATTRIBUTE UPGRADING
|
|
attribute_tag = char_c.find('attr')
|
|
print(attribute_tag)
|
|
for a_tag in attribute_tag.find_all('a'):
|
|
a_tag['points'] = '6'
|
|
print(attribute_tag)
|
|
|
|
|
|
def inventory(soup, add_code, add_quantity):
|
|
# Load tag names first:
|
|
id_dict = {}
|
|
|
|
filename = "item_ids.txt"
|
|
for line in open(filename):
|
|
result = line.split()
|
|
code = int(result[0])
|
|
name = ' '.join(result[1:])
|
|
# print(code, name)
|
|
id_dict[code] = name
|
|
|
|
# print(id_dict)
|
|
|
|
print("You have requested that {} unit(s) of {} be added to existing storage of this item (storage site will be selected at random)".format(add_quantity, id_dict[add_code]))
|
|
|
|
item_tracking = {}
|
|
|
|
print('-----')
|
|
|
|
storage_space_counter = 1
|
|
|
|
# This line is a hack to prevent finding alien ship inventories
|
|
# Unfortunately at the moment it will only likely work if the player has only one ship
|
|
# I do not yet know how to fix this problem if the player has a fleet
|
|
ship_tag = soup.find('ship')
|
|
|
|
added_quantity = False
|
|
|
|
for inv_tag in ship_tag.find_all('inv'):
|
|
if inv_tag.parent.name != 'feat':
|
|
continue
|
|
print('Storage space {}'.format(storage_space_counter))
|
|
print()
|
|
quantity_total = 0
|
|
for s_tag in inv_tag.find_all('s'):
|
|
item_code = int(s_tag['elementaryId'])
|
|
item_quantity = int(s_tag['inStorage'])
|
|
item_name = id_dict[item_code]
|
|
print("{:4}: {} - {}".format(item_code, item_name, item_quantity))
|
|
if item_code == add_code and not added_quantity:
|
|
print(" Updating quantity with requested amount...")
|
|
item_quantity += add_quantity
|
|
s_tag['inStorage'] = item_quantity
|
|
added_quantity = True
|
|
print(" Item quantity is now {}".format(s_tag['inStorage']))
|
|
quantity_total += item_quantity
|
|
|
|
if item_code not in item_tracking:
|
|
item_tracking[item_code] = item_quantity
|
|
else:
|
|
item_tracking[item_code] = item_tracking[item_code] + item_quantity
|
|
|
|
print()
|
|
print('Total use of storage space {}: {}'.format(storage_space_counter, quantity_total))
|
|
storage_space_counter += 1
|
|
print('-----')
|
|
|
|
print('Item total summary:')
|
|
print()
|
|
for item in item_tracking.items():
|
|
item_code = item[0]
|
|
item_name = id_dict[item_code]
|
|
item_quantity = item[1]
|
|
print('{:4} - {} - {}'.format(item_code, item_name, item_quantity))
|
|
|
|
|
|
def give_money(soup, amount):
|
|
|
|
bank_tag = soup.find('playerBank')
|
|
bank_tag['ca'] = amount
|
|
|
|
|
|
def list_ships(soup):
|
|
|
|
ship_tags = soup.find_all('ship')
|
|
|
|
for ship_tag in ship_tags:
|
|
ship_name = ship_tag['sname']
|
|
|
|
"""
|
|
settings_nodes = ship_tag.find_all('settings')
|
|
for settings_node in settings_nodes:
|
|
if settings_node.has_attr('owner'):
|
|
ship_owner = settings_node['owner']
|
|
else:
|
|
continue
|
|
"""
|
|
owner_node = ship_tag.find('settings', owner=True)
|
|
ship_owner = owner_node['owner']
|
|
ship_state = owner_node['state']
|
|
print('Ship found: {} owned by {} (state: {})'.format(ship_name, ship_owner, ship_state))
|
|
# print(settings_node)
|
|
|
|
|
|
def parse_item_file(filename):
|
|
|
|
results = []
|
|
|
|
for line in open(filename):
|
|
line = line.strip()
|
|
if line[0] == '#':
|
|
continue
|
|
components = line.split()
|
|
item_code = int(components[0])
|
|
item_quantity = int(components[1])
|
|
results.append((item_code, item_quantity))
|
|
|
|
return results
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(prog="Space Haven Saved Game Inspector", description="As above.")
|
|
|
|
parser.add_argument('filename', metavar='SAVEGAME_GAME_FILE')
|
|
parser.add_argument('--add_item', required=False, metavar=('ITEM_CODE', 'ITEM_QUANTITY'), type=int, nargs=2, help="Add more of an existing item to storage by CODE - refer to accompanying data file reference for codes. First number is the code, second is the desired quantity.")
|
|
parser.add_argument('--buff_chars', required=False, action='store_true', help="For all characters, increases all skills and attributes to maximum. Use wisely.")
|
|
parser.add_argument('--money', required=False, type=int, nargs=1, metavar='AMOUNT', help="Give the player credits of the specified amount")
|
|
parser.add_argument('--list_ships', required=False, action='store_true', help="List all ships with names and their respective owners")
|
|
parser.add_argument('--test_gamedata', required=False, action='store_true', help="Test of new class-based system of storing game information")
|
|
parser.add_argument('--clone_character', required=False, type=str, nargs=2, metavar=('OLD_NAME', 'NEW_NAME'), help="Clones a character of one name into another")
|
|
parser.add_argument('--add_item_set', required=False, type=str, nargs=1, metavar='PACK_FILENAME', help="Takes a file containing a list of item codes and quantities (whitespace separated) and adds all of these to player storage")
|
|
parser.add_argument('--detailed_items', required=False, action='store_true', help='Print a detailed item listing from player inventory')
|
|
parser.add_argument('--detailed_chars', required=False, action='store_true', help='Print a comprehensive listing of player character details')
|
|
parser.add_argument('--replace_original', required=False, action='store_true', help='Replace original file instead of creating edited alternative. Renames original for backup, but USE WITH CAUTION')
|
|
|
|
args = parser.parse_args()
|
|
# print(args)
|
|
|
|
print("--- Space Haven Saved Game Inspector ---")
|
|
print()
|
|
|
|
filename = args.filename
|
|
# print(filename)
|
|
|
|
full_text = ""
|
|
|
|
for line in open(filename):
|
|
full_text += line
|
|
|
|
# print(full_text)
|
|
|
|
soup = BeautifulSoup(full_text, "xml")
|
|
|
|
item_code_database = ItemCodeDatabase('item_ids.txt')
|
|
print("Item code database successfully loaded.")
|
|
print()
|
|
|
|
game_data = GameData(soup, item_code_database)
|
|
game_data.populate()
|
|
|
|
edits_made = False
|
|
|
|
if args.buff_chars:
|
|
game_data.buff_characters()
|
|
edits_made = True
|
|
|
|
if args.add_item:
|
|
game_data.add_item(args.add_item[0], args.add_item[1])
|
|
edits_made = True
|
|
|
|
if args.money:
|
|
game_data.add_currency(args.money[0])
|
|
edits_made = True
|
|
|
|
if args.list_ships:
|
|
game_data.print_summary()
|
|
|
|
if args.clone_character:
|
|
game_data.clone_character(args.clone_character[0], args.clone_character[1])
|
|
edits_made = True
|
|
|
|
if args.add_item_set:
|
|
item_list = parse_item_file(args.add_item_set[0])
|
|
# print(item_list)
|
|
print("Items to be added:")
|
|
for (item_code, item_quantity) in item_list:
|
|
print("{} : {}".format(item_code_database.get_name_from_code(item_code), item_quantity))
|
|
game_data.add_item(item_code, item_quantity)
|
|
edits_made = True
|
|
|
|
if args.detailed_items:
|
|
game_data.print_detailed_item_summary()
|
|
|
|
if args.detailed_chars:
|
|
game_data.print_detailed_character_summary()
|
|
|
|
game_data.writeback()
|
|
|
|
if args.test_gamedata:
|
|
game_data = GameData(soup, item_code_database)
|
|
game_data.populate()
|
|
# game_data.add_item(1759, 100)
|
|
# game_data.buff_characters()
|
|
# game_data.add_currency(10000)
|
|
game_data.print_summary()
|
|
game_data.print_detailed_item_summary()
|
|
# game_data.clone_character("Byron", "Anthony")
|
|
# game_data.print_detailed_character_summary()
|
|
game_data.writeback()
|
|
|
|
if edits_made:
|
|
if args.replace_original:
|
|
print('Renaming original file')
|
|
datetime_suffix = datetime.datetime.now().strftime('%Y-%m-%d-%H%M_%S_%f')
|
|
new_filename = args.filename + "-" + datetime_suffix
|
|
# print(datetime_suffix)
|
|
os.rename(args.filename, new_filename)
|
|
|
|
print('Now rewriting game file with new information')
|
|
text = soup.prettify()
|
|
# Delete XML header - game doesn't have it
|
|
sansfirstline = '\n'.join(text.split('\n')[1:])
|
|
|
|
f = open(args.filename, 'w')
|
|
f.write(sansfirstline)
|
|
f.close()
|
|
print('Complete.')
|
|
else:
|
|
text = soup.prettify()
|
|
# Delete XML header - game doesn't have it
|
|
sansfirstline = '\n'.join(text.split('\n')[1:])
|
|
|
|
f = open('game.xml', 'w')
|
|
f.write(sansfirstline)
|
|
f.close()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|