7294 lines
233 KiB
Python
Executable File
7294 lines
233 KiB
Python
Executable File
# QA Functions Test
|
|
# Started 28/08/08
|
|
# TKP
|
|
|
|
import os
|
|
import sys
|
|
import shutil
|
|
import time
|
|
import subprocess
|
|
import filecmp
|
|
import msvcrt
|
|
import random
|
|
import copy
|
|
import webbrowser
|
|
import traceback
|
|
import string
|
|
import hashlib
|
|
#import WConio
|
|
import re
|
|
import codecs
|
|
import socket
|
|
|
|
|
|
|
|
#====Settings Stuff=============================================================
|
|
class GID:
|
|
""" Container for holding all the settings associated with a GID """
|
|
def __init__(self,gid, __unicode__ = False):
|
|
self.settings = {}
|
|
self.gid = gid
|
|
self.lists = []
|
|
|
|
self.__unicode__ = __unicode__
|
|
|
|
def __getitem__(self,name):
|
|
return self.GetSetting(name)
|
|
|
|
def __delitem__(self, name):
|
|
del self.settings[name]
|
|
|
|
def __setitem__(self,name,data):
|
|
self.AddSetting(name, data)
|
|
|
|
def __iter__(self):
|
|
return self.SettingsGenerator()
|
|
|
|
def __contains__(self,item):
|
|
return item in self.settings
|
|
|
|
def __len__(self):
|
|
return len(self.settings)
|
|
|
|
def SettingsGenerator(self):
|
|
""" Generator object for use by __iter__ """
|
|
for name in self.ListSettings():
|
|
yield name
|
|
|
|
def ListSettings(self):
|
|
""" Return a list of all the setting names present in this GID """
|
|
setting_names = []
|
|
for key, setting in self.settings.iteritems():
|
|
setting_names.append(key)
|
|
setting_names.sort()
|
|
return setting_names
|
|
|
|
def AddSetting(self,name,data):
|
|
""" Add a setting to the settings dictionary """
|
|
if type(data) == str and self.__unicode__:
|
|
data = unicode(data)
|
|
self.settings[name.lower()] = data
|
|
|
|
def AddList(self,name):
|
|
""" Mark a setting as a 'list' setting (comma seperated). Used by
|
|
GetSetting to determine how to return a requested setting """
|
|
if name not in self.lists:
|
|
self.lists.append(name)
|
|
|
|
def CSVToList(self,settings):
|
|
""" Convert a string into a list of objects, seperated by a comma """
|
|
untidy_list = settings.split(',')
|
|
|
|
#remove whitespace
|
|
tidy_list = []
|
|
for item in untidy_list:
|
|
stripped = item.strip()
|
|
if stripped == '':
|
|
continue
|
|
else:
|
|
tidy_list.append(stripped)
|
|
|
|
return tidy_list
|
|
|
|
def GetSetting(self,name):
|
|
""" Return the value of the requested setting. Exit if setting not found """
|
|
if not self.SettingExists(name):
|
|
error_exit('Setting not found\nGAME_ID: %s\nSETTING: %s'
|
|
% (self.gid,name))
|
|
|
|
if name in self.lists:
|
|
return self.CSVToList(self.settings[name])
|
|
else:
|
|
return self.settings[name]
|
|
|
|
def GetRawSetting(self,name):
|
|
""" Return a setting without processing it. Used to retrieve flat
|
|
string versions of 'list' settings """
|
|
if not self.SettingExists(name):
|
|
error_exit('Setting not found\nGAME_ID: %s\nSETTING: %s'
|
|
% (self.gid,name))
|
|
return self.settings[name]
|
|
|
|
def SettingExists(self,name):
|
|
""" Determines whether the setting name exists in this GID """
|
|
if name in self.ListSettings():
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
def Platform(self):
|
|
""" Returns the platform associated with this GID """
|
|
if self.SettingExists('xex'):
|
|
return 'xbx'
|
|
elif self.SettingExists('self'):
|
|
return 'ps3'
|
|
|
|
|
|
class SettingsLoader:
|
|
def __init__(self, __unicode__=False):
|
|
self.game_ids = {}
|
|
self.debug = True
|
|
self.categories = {}
|
|
|
|
self.__unicode__ = __unicode__
|
|
|
|
def __call__(self,gid,name):
|
|
return self.GetSetting(gid,name)
|
|
|
|
def __getitem__(self, gid):
|
|
return self.GetGIDObject(gid)
|
|
|
|
def __delitem__(self, gid):
|
|
del self.game_ids[gid]
|
|
|
|
def __setitem__(self, gid, data):
|
|
self.game_ids[gid] = data
|
|
#this is inefficient and bad
|
|
#self.GatherCategories()
|
|
#just remember to refresh categories after an insert
|
|
|
|
def __iter__(self):
|
|
return self.GIDGenerator()
|
|
|
|
def __len__(self):
|
|
return len(self.game_ids)
|
|
|
|
def __contains__(self,item):
|
|
return item in self.game_ids
|
|
|
|
def GIDGenerator(self):
|
|
for gid in self.ListGIDs():
|
|
yield self.GetGIDObject(gid)
|
|
|
|
|
|
def GetGIDObject(self,gid):
|
|
""" Returns a GID object if the object can be found, exits otherwise """
|
|
self.CheckForGID(gid)
|
|
return self.game_ids[gid]
|
|
|
|
def ListGIDs(self):
|
|
""" Lists all GIDs currently contained in the loader object """
|
|
gids = []
|
|
for gid,data in self.game_ids.iteritems():
|
|
gids.append(gid)
|
|
gids.sort()
|
|
|
|
return gids
|
|
|
|
def ListSettings(self,gid):
|
|
""" Returns a list of the names of all the settings currently in a GID """
|
|
self.CheckForGID(gid)
|
|
return self.game_ids[gid].ListSettings()
|
|
|
|
|
|
def IsValidGID(self,gid):
|
|
""" Returns True if a GID is found, False if not """
|
|
if gid in self.ListGIDs():
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
def CheckForGID(self,gid):
|
|
""" Checks for a game_id, exits if it doesn't exist """
|
|
if not self.IsValidGID(gid):
|
|
error_exit('GID not found: %s' % gid)
|
|
|
|
|
|
def SettingExists(self,gid,name):
|
|
""" Check for presence of gid, then check for presence of setting name
|
|
in gid """
|
|
if not self.IsValidGID(gid):
|
|
return False
|
|
|
|
return self.game_ids[gid].SettingExists(name)
|
|
|
|
def GetSetting(self,gid,name):
|
|
""" Check to see if the GID exists, then call the GIDs setting return
|
|
function """
|
|
self.CheckForGID(gid)
|
|
|
|
return self.game_ids[gid].GetSetting(name)
|
|
|
|
|
|
def LoadFile(self,filename):
|
|
""" Populate the list of GIDs with values found in the filename
|
|
provided """
|
|
if not os.path.exists(filename):
|
|
error_exit('Failed to load settings from "%s". File could not '
|
|
'be found.' % filename)
|
|
|
|
if not os.access(filename,os.R_OK):
|
|
error_exit('Failed to load settings from "%s". File could not '
|
|
'be read.' % filename)
|
|
|
|
GIDcount = 0
|
|
settingscount = 0
|
|
|
|
try:
|
|
if self.__unicode__:
|
|
set_file = codecs.open(filename, 'r', 'utf-8')
|
|
gid_start = u'['
|
|
gid_end = u']'
|
|
else:
|
|
set_file = open(filename,'r')
|
|
gid_start = '['
|
|
gid_end = ']'
|
|
except IOError:
|
|
error_exit('Failed to load settings from "%s". File could not '
|
|
'be read.' % filename)
|
|
|
|
|
|
current_gid = None
|
|
for line in set_file:
|
|
#skip the line if it starts with a comment
|
|
if line.startswith('#'):
|
|
continue
|
|
|
|
#strip newlines and comments
|
|
line = line.strip('\n')
|
|
if '#' in line:
|
|
line = line[:line.find('#')]
|
|
|
|
#convert <hash> to hash
|
|
while '<hash>' in line:
|
|
line = line[:line.find('<hash>')] + '#' + line[line.find('<hash>')+6:]
|
|
|
|
line = line.strip()
|
|
|
|
#print line
|
|
#is this a new GID?
|
|
#if len(line) > 2: print line[0], line[-1]
|
|
if line.startswith(gid_start) and line.endswith(gid_end):
|
|
GIDcount += 1
|
|
#print 'gid found: %s' % line
|
|
current_gid = line.strip('[]').lower()
|
|
#print 'current_gid: %s' % current_gid
|
|
if current_gid not in self.ListGIDs():
|
|
self.game_ids[current_gid] = GID(current_gid, self.__unicode__)
|
|
#print 'gid created: %s' % self.game_ids[current_gid].gid
|
|
continue
|
|
|
|
#ditch the current line if we don't have a GID yet
|
|
if current_gid is None:
|
|
#if self.debug: print 'no gids yet'
|
|
continue
|
|
|
|
#ditch the current line if it doesn't have '=' in it
|
|
if '=' not in line:
|
|
continue
|
|
|
|
|
|
#build normal entry
|
|
line_part = line.partition('=')
|
|
name = line_part[0].strip()
|
|
data = line_part[2].strip()
|
|
|
|
if name.startswith('LIST:'):
|
|
name = name[5:].strip()
|
|
self.game_ids[current_gid].AddList(name)
|
|
|
|
#write entry
|
|
self.game_ids[current_gid].AddSetting(name,data)
|
|
settingscount += 1
|
|
|
|
set_file.close()
|
|
|
|
|
|
logging('%s settings in %s gameids loaded from %s' %
|
|
(str(settingscount),str(GIDcount),filename))
|
|
|
|
self.GatherCategories()
|
|
|
|
def GatherCategories(self):
|
|
self.categories = {}
|
|
for key, gid in self.game_ids.iteritems():
|
|
if 'category' in gid:
|
|
if gid['category'] not in self.categories:
|
|
self.categories[gid['category']] = [gid]
|
|
else:
|
|
self.categories[gid['category']].append(gid)
|
|
|
|
## print '======SettingsLoader======'
|
|
## for key, item in self.categories.iteritems():
|
|
## print key
|
|
## print '=========================='
|
|
|
|
def GetCategoryItems(self, category):
|
|
if category not in self.categories:
|
|
error_exit('GetCategoryItems: Category not found in SettingsLoader '
|
|
'instance')
|
|
|
|
return self.categories[category]
|
|
|
|
|
|
|
|
def IsHidden(self, gid):
|
|
""" DON'T FORGET: Pass me a GID object, not a GID string
|
|
returns true if a gid is 'hidden', false otherwise """
|
|
error_exit('IsHidden broken, do not use')
|
|
if 'hidden' not in gid:
|
|
return False
|
|
else:
|
|
if gid['hidden'] == '1':
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
|
|
def RemoveHiddenGIDs(self):
|
|
""" delete any GID with a 'hidden' setting with a value of '1' """
|
|
for gid in self.ListGIDs():
|
|
if not self.game_ids[gid].SettingExists('hidden'):
|
|
continue
|
|
|
|
if self.game_ids[gid].GetSetting('hidden') == '1':
|
|
del self.game_ids[gid]
|
|
|
|
logging('Hiding %s' % gid)
|
|
|
|
def DoStringReplacement(self, variables):
|
|
""" Do string replacement on all settings in all GIDs in loader """
|
|
for gid in self.ListGIDs():
|
|
for name in self.game_ids[gid].ListSettings():
|
|
if '$$' in self.game_ids[gid].GetRawSetting(name):
|
|
setting_value = self.game_ids[gid].GetRawSetting(name)
|
|
replacement_setting = ReplaceStringVariables(setting_value,
|
|
variables)
|
|
self.game_ids[gid].AddSetting(name,replacement_setting)
|
|
|
|
|
|
|
|
def ReplaceStringVariables(line,variables):
|
|
""" Takes a GID container full of variables """
|
|
max_levels = 100 #used to prevent infinite replacement loops
|
|
while line.find('$$') is not -1:
|
|
if line.find('$$') is line.rfind('$$'):
|
|
return line
|
|
v_start = line.find('$$') + 2
|
|
v_end = line[v_start:].find('$$') + v_start
|
|
v_name = line[v_start:v_end]
|
|
|
|
v_string = variables[v_name]
|
|
|
|
line = line[:v_start-2]+v_string+line[v_end+2:]
|
|
max_levels -= 1
|
|
if max_levels is 0:
|
|
error_exit('Potentially infinite loop detected in ReplaceString'
|
|
'Variables(). Looping on line: "%s"' % line)
|
|
|
|
return line
|
|
|
|
|
|
|
|
|
|
def get_setting(gid, name, exitstatus='EXIT'):
|
|
""" old style get_setting, replace this as sooooon as i can """
|
|
if gid == 'defaults':
|
|
return settings.GetSetting('defaults',name)
|
|
if exitstatus == 'NO_EXIT':
|
|
if game_versions.SettingExists(gid,name):
|
|
return game_versions.GetSetting(gid,name)
|
|
else:
|
|
return False
|
|
return game_versions.GetSetting(gid,name)
|
|
|
|
|
|
def SettingsFiles():
|
|
files_to_load = []
|
|
for switch in command_line_switches():
|
|
if switch.lower().startswith('-addsettings='):
|
|
files_to_load.append(switch[13:])
|
|
|
|
return files_to_load
|
|
|
|
def command_line_switches():
|
|
""" gathers the command line switches """
|
|
args = []
|
|
for switch in sys.argv[1:]:
|
|
args.append(switch)
|
|
|
|
return args
|
|
|
|
|
|
|
|
|
|
#===============================================================================
|
|
|
|
|
|
|
|
|
|
#====Menu Stuff=================================================================
|
|
##def get_categories():
|
|
## """Gets all unique categories from game_version dict, returns them in
|
|
## a sorted list"""
|
|
## categories=[]
|
|
## #store unique categories in list
|
|
## for game_id in game_version:
|
|
## if get_setting(game_id,'category') not in categories:
|
|
## categories.append(get_setting(game_id,'category'))
|
|
## categories.sort()
|
|
## return categories
|
|
##
|
|
##
|
|
##def category_menu():
|
|
## """ Creates a menu from all categories found in game_versions.ini """
|
|
## categories = get_categories()
|
|
## item_comments = [None]*len(categories)
|
|
## item_comments.append('Exit the QA Menu')
|
|
## comment = 'Good '+time_of_day()+', '+windows_username()[0]+'. What would you like to play today?'
|
|
##
|
|
## selection = menu_maker_2('Main Menu', categories,comment,item_comments,back_text='Quit')
|
|
##
|
|
## if selection == -1:
|
|
## selected_category = '_BACK'
|
|
## else:
|
|
## selected_category = categories[selection]
|
|
##
|
|
## return selected_category
|
|
##
|
|
##
|
|
##def auto_menu(category):
|
|
## """automatically generates a menu from game_version containing all
|
|
## items that have the supplied category"""
|
|
## menu_id=[]
|
|
## clean_name=[]
|
|
## item_comments=[]
|
|
## for game_id in game_version:
|
|
## if get_setting(game_id,'category').lower() == category.lower():
|
|
## menu_id.append(game_id)
|
|
## clean_name.append(get_setting(game_id,'name'))
|
|
## item_comments.append(get_setting(game_id,'name'))
|
|
##
|
|
## selection = menu_maker_2(category, clean_name, item_comments=item_comments,NO_SORT=False)
|
|
##
|
|
## if selection == -1:
|
|
## selected_id = '_BACK'
|
|
## else:
|
|
## selected_id = menu_id[selection]
|
|
##
|
|
## return selected_id
|
|
|
|
|
|
def print_button_menu(menu_title,
|
|
menu_items,
|
|
selection,
|
|
comment=None,
|
|
NO_BACK=False,
|
|
left_selector='--> ',
|
|
right_selector=' <--',
|
|
back_text='Back',
|
|
show_numbers=False):
|
|
"""Screen updating function used by menu_maker_2()"""
|
|
#os.system('cls')
|
|
|
|
spacer = ' '*len(left_selector)
|
|
|
|
## print '='*len(menu_title)
|
|
## print menu_title
|
|
## print '='*len(menu_title)
|
|
os.system('cls')
|
|
print box_text(menu_title,1)
|
|
|
|
|
|
for key,item in enumerate(menu_items):
|
|
if key==selection:
|
|
#reverse_colour()
|
|
if show_numbers:
|
|
print str(key+1)+'. '+left_selector+item+right_selector
|
|
else:
|
|
print left_selector+item+right_selector
|
|
#text_colour()
|
|
else:
|
|
if show_numbers:
|
|
print str(key+1)+'. '+spacer+item
|
|
else:
|
|
print spacer+item
|
|
|
|
|
|
if not NO_BACK:
|
|
print
|
|
if selection == len(menu_items):
|
|
#reverse_colour()
|
|
if show_numbers:
|
|
print '0. '+left_selector+back_text+right_selector
|
|
else:
|
|
print left_selector+back_text+right_selector
|
|
#text_colour()
|
|
else:
|
|
if show_numbers:
|
|
print '0. '+spacer+back_text
|
|
else:
|
|
print spacer+back_text
|
|
|
|
|
|
if comment:
|
|
print
|
|
print word_wrap(comment)
|
|
|
|
def valid_keys():
|
|
""" returns a list of valid responses from get_key()
|
|
TODO: argument to make only certain key types appear in return """
|
|
numbers = ['0','1','2','3','4','5','6','7','8','9']
|
|
letters = ['a','A',
|
|
'b','B',
|
|
'c','C',
|
|
'd','D',
|
|
'e','E',
|
|
'f','F',
|
|
'g','G',
|
|
'h','H',
|
|
'i','I',
|
|
'j','J',
|
|
'k','K',
|
|
'l','L',
|
|
'm','M',
|
|
'n','N',
|
|
'o','O',
|
|
'p','P',
|
|
'q','Q',
|
|
'r','R',
|
|
's','S',
|
|
't','T',
|
|
'u','U',
|
|
'v','V',
|
|
'w','W',
|
|
'x','X',
|
|
'y','Y',
|
|
'z','Z',]
|
|
|
|
|
|
everything_valid = numbers+letters
|
|
|
|
return everything_valid
|
|
|
|
def menu_maker_2(menu_title,
|
|
menu_items,
|
|
comment='',
|
|
item_comments=None,
|
|
NO_BACK=False,
|
|
NO_SORT=True,
|
|
back_text='Back',
|
|
cursor_pos=0):
|
|
""" Creates a menu from a title and a list of items that can be operated
|
|
using the cursor keys """
|
|
|
|
screen_update_required = True
|
|
|
|
if item_comments == None:
|
|
item_comments = [comment]*len(menu_items)
|
|
else:
|
|
for key in range(len(menu_items)):
|
|
if key+1 > len(item_comments):
|
|
item_comments.append(comment)
|
|
else:
|
|
if not item_comments[key]:
|
|
item_comments[key] = comment
|
|
|
|
if not NO_BACK and len(item_comments) < len(menu_items)+1:
|
|
item_comments.append('Return to previous screen')
|
|
|
|
|
|
if not NO_SORT:
|
|
sorted_items = sort_menu_items(menu_items)
|
|
menu_items = []
|
|
menu_index = []
|
|
|
|
for item in sorted_items:
|
|
menu_items.append(item[0])
|
|
menu_index.append(item[1])
|
|
else:
|
|
menu_index = []
|
|
for key in range(len(menu_items)):
|
|
menu_index.append(key)
|
|
|
|
menu_index.append(len(menu_items))
|
|
show_numbers = False
|
|
while True:
|
|
if screen_update_required:
|
|
print_comment = item_comments[menu_index[cursor_pos]]
|
|
print_button_menu(menu_title,menu_items,cursor_pos,print_comment,NO_BACK,back_text=back_text,show_numbers=show_numbers)
|
|
screen_update_required = False
|
|
|
|
key = get_keypress()
|
|
if key == 'CURSOR_DOWN':
|
|
if not NO_BACK:
|
|
if cursor_pos == len(menu_items):
|
|
cursor_pos = 0
|
|
else:
|
|
cursor_pos +=1
|
|
screen_update_required = True
|
|
elif NO_BACK:
|
|
if cursor_pos == len(menu_items)-1:
|
|
cursor_pos = 0
|
|
else:
|
|
cursor_pos += 1
|
|
screen_update_required = True
|
|
elif key == 'CURSOR_UP':
|
|
if cursor_pos == 0:
|
|
if not NO_BACK:
|
|
cursor_pos = len(menu_items)
|
|
elif NO_BACK:
|
|
cursor_pos = len(menu_items)-1
|
|
else:
|
|
cursor_pos -= 1
|
|
screen_update_required = True
|
|
elif key =='RETURN':
|
|
break
|
|
elif key == 'ESCAPE' and not NO_BACK or \
|
|
key == 'BACKSPACE' and not NO_BACK:
|
|
cursor_pos = -1
|
|
break
|
|
elif key in valid_keys():
|
|
if key.isdigit():
|
|
if not int(key) > len(menu_items):
|
|
if NO_BACK and key == '0':
|
|
pass
|
|
else:
|
|
cursor_pos = int(key)-1
|
|
break
|
|
elif key == 'n' or key =='N':
|
|
if show_numbers: show_numbers = False
|
|
elif not show_numbers: show_numbers = True
|
|
screen_update_required = True
|
|
|
|
|
|
if cursor_pos == len(menu_items):
|
|
cursor_pos = -1
|
|
|
|
if not NO_SORT and cursor_pos != -1:
|
|
cursor_pos = menu_index[cursor_pos]
|
|
|
|
return cursor_pos
|
|
|
|
|
|
def sort_menu_items(menuitems):
|
|
""" schwartzian(ish) transform of menu items. Return the menu items, plus
|
|
their original sort order. """
|
|
decorated=[]
|
|
for key, item in enumerate(menuitems):
|
|
decorated.append([item, key])
|
|
|
|
decorated.sort()
|
|
|
|
return decorated
|
|
|
|
|
|
|
|
def get_valid_number():
|
|
""" Keep asking the user to enter a number until they do. Then return the
|
|
number as an integer."""
|
|
selection = raw_input('Please enter a number: ')
|
|
|
|
while selection.isdigit() == False:
|
|
print 'ERROR: "'+selection+'" is not a valid number. Please only enter numbers.\n'
|
|
selection = raw_input('Please enter a number: ')
|
|
|
|
return int(selection)
|
|
|
|
|
|
def print_menu(menutitle, menuitems, comment=None,NO_BACK=False,NO_NUMBERING=False):
|
|
"""Prints a standardised numerical menu, menutitle is a
|
|
string, menuitems an array of strings.
|
|
format: qa.print_menu('Menu Title', ['Item 1', 'Item 2', 'Item 3'])"""
|
|
print '\n','='*len(menutitle)
|
|
print menutitle
|
|
print '='*len(menutitle)
|
|
for item in range(0, len(menuitems)):
|
|
if NO_NUMBERING:
|
|
print menuitems[item]
|
|
else:
|
|
print str(item+1) + '.', menuitems[item]
|
|
if not NO_BACK:
|
|
print '\n0. Back\n'
|
|
else:
|
|
print
|
|
if not comment == None:
|
|
print word_wrap(comment+"\n")
|
|
|
|
|
|
|
|
|
|
|
|
#===============================================================================
|
|
|
|
|
|
|
|
#==== Stuff for running external programs ======================================
|
|
|
|
def auto_run_items():
|
|
autorun_events = []
|
|
auto_run_fn = settings['defaults']['autorun']
|
|
if os.path.exists(auto_run_fn):
|
|
autorun = open(auto_run_fn,'r')
|
|
|
|
for line in autorun:
|
|
line = line.rstrip('\n')
|
|
if line in game_versions:
|
|
autorun_events.append([game_versions[line]['name'],
|
|
'select_game_page',
|
|
line,
|
|
'Let\'s play %s!' %
|
|
game_versions[line]['name']])
|
|
|
|
if len(autorun_events) > 0:
|
|
autorun_events.append(['Main Menu',
|
|
'top_menu',
|
|
0,
|
|
'Return to your normal scheduled QA Menu'])
|
|
return autorun_events
|
|
else:
|
|
return False
|
|
|
|
|
|
|
|
def run_watson(game_id=None):
|
|
""" Searches for 'xbwatson.exe'. Prompts the user to close xbwatson if
|
|
it is found running. Runs watson if it isn't running. """
|
|
|
|
if game_id == None:
|
|
watsonpath = settings['defaults']['watsonpath']
|
|
watsonprocess = os.path.basename(watsonpath)
|
|
|
|
else:
|
|
if 'gametype' in game_versions[game_id]:
|
|
if game_versions[game_id]['gametype'].lower() == 'lan':
|
|
settingname = 'lanwatson'
|
|
elif game_versions[game_id]['gametype'].lower() == 'gta_v':
|
|
settingname = 'gtavwatson'
|
|
else:
|
|
settingname = 'watsonpath'
|
|
else:
|
|
settingname = 'watsonpath'
|
|
|
|
watsonpath = settings['defaults'][settingname]
|
|
watsonprocess = os.path.basename(watsonpath)
|
|
|
|
if not os.path.exists(watsonpath):
|
|
error_exit('Watson not found on local machine')
|
|
|
|
while ProcessRunning(watsonprocess):
|
|
print 'Attempting to kill Watson...'
|
|
## for number in range(3,0,-1):
|
|
## print str(number)+'... ',
|
|
## time.sleep(1)
|
|
|
|
KillAllTaskName(watsonprocess)
|
|
time.sleep(0.1)
|
|
#watsonrunning = find_running_process(watsonprocess)
|
|
|
|
subprocess.Popen(watsonpath)
|
|
|
|
|
|
def run_rag_and_rfs(game_id=None):
|
|
""" looks for rag and rfs, if they aren't running, runs them. """
|
|
if game_id == None:
|
|
ragpath = settings['defaults']['ragpath']
|
|
rfspath = settings['defaults']['rfspath']
|
|
else:
|
|
|
|
if 'gametype' not in game_versions[game_id]:
|
|
ragpath = settings['defaults']['ragpath']
|
|
rfspath = settings['defaults']['rfspath']
|
|
|
|
elif game_versions[game_id]['gametype'].lower() == 'jimmy' or \
|
|
game_versions[game_id]['gametype'].lower() == 'rdr2':
|
|
ragpath = settings['defaults']['jimmyrag']
|
|
rfspath = settings['defaults']['jimmyrfs']
|
|
elif game_versions[game_id]['gametype'].lower() == 'gta_v':
|
|
ragpath = settings['defaults']['gtavrag']
|
|
rfspath = settings['defaults']['gtavrfs']
|
|
|
|
else:
|
|
ragpath = settings['defaults']['ragpath']
|
|
rfspath = settings['defaults']['rfspath']
|
|
|
|
|
|
if not os.path.exists(ragpath):
|
|
error_exit('RAG not found on local machine, looked in: '+ragpath)
|
|
|
|
if not os.path.exists(rfspath):
|
|
error_exit('SystrayRFS not found on local machine,'
|
|
'looked in: '+rfspath)
|
|
|
|
ragprocess = os.path.basename(ragpath)
|
|
rfsprocess = os.path.basename(rfspath)
|
|
|
|
while ProcessRunning(ragprocess):
|
|
print 'Killing RAG'
|
|
KillRag()
|
|
|
|
if ProcessRunning(rfsprocess):
|
|
print 'Killing RFS'
|
|
KillAllTaskName(rfsprocess)
|
|
|
|
|
|
subprocess.Popen(ragpath)
|
|
|
|
subprocess.Popen([rfspath, '-trusted'])
|
|
|
|
def DoLaunchPreparations(game_id):
|
|
game = game_versions[game_id]
|
|
|
|
if 'init_copy_folder' in game:
|
|
print 'init_copy_folder found, doing folder copy'
|
|
FancyCopyFolder(game['init_copy_folder'][0],
|
|
game['init_copy_folder'][1])
|
|
|
|
if 'write_ip' in game:
|
|
print 'Attempting to write ip address to file...'
|
|
WriteIPToFile(game_versions[game_id]['write_ip'])
|
|
|
|
if 'write_switches' in game:
|
|
print 'Attempting to write switches to file...'
|
|
print game['gametype']
|
|
print 'separator: "%s"' % GetCommandLineSeparator(game['gametype'])
|
|
|
|
|
|
if 'lan_dlc_disabled' in game_versions[game_id]:
|
|
switches = GetLANSwitches(game_id)
|
|
else:
|
|
if 'switches' in game_versions[game_id]:
|
|
switches = game['switches']
|
|
else:
|
|
switches = []
|
|
|
|
if 'add_switches' in game_versions[game_id]:
|
|
switches.extend(game_versions[game_id]['add_switches'])
|
|
|
|
|
|
WriteSwitches(game['write_switches'],
|
|
switches,
|
|
GetCommandLineSeparator(game['gametype']))
|
|
|
|
|
|
|
|
|
|
def launch_gta_iv(game_id, switches=None):
|
|
""" do all the things the old batfile did before running the game """
|
|
DoLaunchPreparations(game_id)
|
|
if 'init_copy_folder' in game_versions[game_id]:
|
|
print 'init_copy_folder found, doing folder copy'
|
|
FancyCopyFolder(game_versions[game_id]['init_copy_folder'][0],
|
|
game_versions[game_id]['init_copy_folder'][1])
|
|
#game specific settings
|
|
game_setting = False #Don't forget: game_setting is used by the switches section
|
|
if 'gametype' in game_versions[game_id]:
|
|
game_setting = game_versions[game_id]['gametype'].lower()
|
|
if game_setting == 'gtaiv':
|
|
map_ext = 'map'
|
|
folder = 'xE:\\gta4_2005\\'
|
|
movie_destination = folder+'xbox360\\movies\\'
|
|
GetIVxex(game_versions[game_id])
|
|
elif game_setting == 'rdr2':
|
|
map_ext = 'cmp'
|
|
folder = 'xE:\\rdr2\\'
|
|
movie_destination = folder
|
|
elif game_setting == 'jimmy':
|
|
map_ext = 'cmp'
|
|
folder = 'xE:\\jimmy\\'
|
|
movie_destination = folder+'xbox360\\movies\\'
|
|
elif game_setting == 'gta_v':
|
|
map_ext = 'cmp'
|
|
folder = 'xE:\\game\\'
|
|
else:
|
|
#default to gta4 settings, just incase
|
|
map_ext = 'map'
|
|
folder = 'xE:\\gta4_2005\\'
|
|
movie_destination = folder+'xbox360\\movies\\'
|
|
else:
|
|
#default to gta4 settings if no 'game' entry is found for this gameid
|
|
map_ext = 'map'
|
|
folder = 'xE:\\gta4_2005\\'
|
|
movie_destination = folder+'xbox360\\movies\\'
|
|
GetIVxex(game_versions[game_id])
|
|
|
|
wait_for_xbox()
|
|
|
|
#check_script_warning(game_id) <-- nobody keeping scripts up to date, removed
|
|
|
|
if 'cab_folder_source' in game_versions[game_id] and \
|
|
'cab_folder_destination' in game_versions[game_id]:
|
|
if not using_latest_cab_file(game_id):
|
|
|
|
items = []
|
|
items.append(['Skip Transfer',
|
|
'final_event',
|
|
'skip',
|
|
'Continue to load %s without transferring CAB' %
|
|
game_versions[game_id]['name']])
|
|
|
|
items.append(['Transfer CAB',
|
|
'final_event',
|
|
'transfer',
|
|
'Transfer the CAB file on your pc to the 360'])
|
|
|
|
selection = MiniMenu('CAB file mismatch detected',items)
|
|
if selection == 'skip': pass
|
|
elif selection == 'transfer': cab_transfer(game_id)
|
|
elif selection == '_BACK': return False
|
|
|
|
|
|
xdk_vars()
|
|
|
|
#get temp location from environment variables
|
|
temp = os.environ.get('temp')
|
|
|
|
xex = game_versions[game_id]['xex']
|
|
mapfile = xex.rsplit('.')[0]+'.'+map_ext
|
|
|
|
|
|
#Check if 'folder' is a file, erase if it is.
|
|
if len(xbdir_list(folder)) <= 1:
|
|
XBDel(folder.strip('\\'))
|
|
|
|
|
|
backup_pstats(game_id)
|
|
|
|
run_rag_and_rfs(game_id)
|
|
run_watson(game_id)
|
|
|
|
#copy xex and map files to console
|
|
XBCopy(xex,folder)
|
|
XBCopy(mapfile,folder)
|
|
|
|
|
|
#hack in movie copying support
|
|
if 'movie_source' in game_versions[game_id]:
|
|
print '\n\nCopying movies, this may take some time...\n'
|
|
movies = game_versions[game_id]['movie_source']+'\\*.bik'
|
|
XBCopyTree(movies, movie_destination, newer_only=True)
|
|
|
|
|
|
## if switches is None:
|
|
## switches = ''
|
|
## if 'switches' in game_versions[game_id]:
|
|
## for item in game_versions[game_id]['switches']:
|
|
## switches = switches+' '+item
|
|
|
|
if switches is None:
|
|
switches = ' '
|
|
if 'switches' in game_versions[game_id]:
|
|
switch_list = game_versions[game_id]['switches']
|
|
for switch in switch_list:
|
|
switches += switch+' '
|
|
|
|
if 'lan_dlc_disabled' in game_versions[game_id]:
|
|
switch_list = GetLANSwitches(game_id)
|
|
switches = ' '
|
|
for switch in switch_list:
|
|
switches += switch+' '
|
|
|
|
if 'add_switches' in game_versions[game_id]:
|
|
for switch in game_versions[game_id]['add_switches']:
|
|
switches += switch+' '
|
|
|
|
if 'gametype' in game_versions[game_id]:
|
|
if game_versions[game_id]['gametype'] == 'gta_v':
|
|
if 'write_switches' in game_versions[game_id]:
|
|
switches = '"@%s"' % game_versions[game_id]['write_switches']
|
|
|
|
|
|
#special rule for RDR cos it needs 50 bazillion command line switches
|
|
if game_setting == 'rdr2':
|
|
if 'rdr_cmd_txt' in game_versions[game_id]:
|
|
rdr_cmd_txt = game_versions[game_id]['rdr_cmd_txt']
|
|
else:
|
|
rdr_cmd_txt = 'X:\\rdr2\\build\\commandline.txt'
|
|
|
|
rdr_commandlines(game_id,'x:\\rdr2\\rdr_cmd.txt',rdr_cmd_txt)
|
|
switches = '@x:\\rdr2\\rdr_cmd.txt'
|
|
|
|
#os.chdir(os.path.dirname(xex)) <-- don't think i need to do this
|
|
|
|
delete_logs(game_id)
|
|
|
|
#copy rfs.dat across
|
|
XBCopy(temp+'\\rfs.dat',folder)
|
|
|
|
logging('launch_gta_iv: xbReboot /w '+folder+os.path.basename(xex)+' '+switches)
|
|
|
|
os.system('xbReboot /w '+folder+os.path.basename(xex)+' '+switches)
|
|
|
|
|
|
def launch_ps3(game_id, switches=None):
|
|
""" Launch a game on PS3. Requires a 'self' setting in game_id """
|
|
DoLaunchPreparations(game_id)
|
|
if 'init_copy_folder' in game_versions[game_id]:
|
|
print 'init_copy_folder found, doing folder copy'
|
|
FancyCopyFolder(game_versions[game_id]['init_copy_folder'][0],
|
|
game_versions[game_id]['init_copy_folder'][1])
|
|
|
|
if not target_manager_installed():
|
|
error_exit('TargetManager appears to not be installed, PS3 requires'
|
|
' TargetManager')
|
|
|
|
if not ProcessRunning('ps3tm.exe'):
|
|
run_targetmanager()
|
|
|
|
|
|
#shut down any versions of ps3run, to close any PSNotsons
|
|
print 'Shutting down ps3runs to close PSNotsons...'
|
|
KillAllTaskName('ps3run.exe')
|
|
|
|
ps3run = os.path.join(os.path.join(os.getenv('SN_PS3_PATH'),'bin'),
|
|
'ps3run.exe')
|
|
|
|
ps3run = '"'+ps3run+'"'
|
|
|
|
if 'write_ip' in game_versions[game_id]:
|
|
print 'Attempting to write ip address to file...'
|
|
WriteIPToFile(game_versions[game_id]['write_ip'])
|
|
|
|
|
|
if 'homedir' in game_versions[game_id]:
|
|
print 'Attempting to set home directory...'
|
|
homedir = game_versions[game_id]['homedir']
|
|
os.system(ps3run+' -h '+homedir)
|
|
|
|
if 'fileserv' in game_versions[game_id]:
|
|
print 'Attempting to set fileserv directory...'
|
|
fileserv = game_versions[game_id]['fileserv']
|
|
os.system(ps3run+' -f '+fileserv)
|
|
|
|
self = game_versions[game_id]['self']
|
|
|
|
## if switches is None:
|
|
## switches = ' '
|
|
## if 'switches' in game_versions[game_id]:
|
|
## switch_list = game_versions[game_id]['switches']
|
|
##
|
|
## if 'lan_dlc_disabled' in game_versions[game_id]:
|
|
## switch_list = GetLANSwitches(game_id)
|
|
##
|
|
## for switch in switch_list:
|
|
## switches += switch+' '
|
|
|
|
if switches is None:
|
|
switches = ' '
|
|
if 'switches' in game_versions[game_id]:
|
|
switch_list = game_versions[game_id]['switches']
|
|
for switch in switch_list:
|
|
switches += switch+' '
|
|
|
|
if 'lan_dlc_disabled' in game_versions[game_id]:
|
|
switch_list = GetLANSwitches(game_id)
|
|
switches = ' '
|
|
for switch in switch_list:
|
|
switches += switch+' '
|
|
|
|
if 'add_switches' in game_versions[game_id]:
|
|
for switch in game_versions[game_id]['add_switches']:
|
|
switches += switch+' '
|
|
|
|
if 'gametype' in game_versions[game_id]:
|
|
if game_versions[game_id]['gametype'] == 'gta_v':
|
|
if 'write_switches' in game_versions[game_id]:
|
|
switches = '"@%s"' % game_versions[game_id]['write_switches']
|
|
|
|
|
|
backup_pstats(game_id)
|
|
delete_logs(game_id)
|
|
|
|
if 'gametype' in game_versions[game_id]:
|
|
if game_versions[game_id]['gametype'].lower() != 'lan':
|
|
run_rag_and_rfs(game_id)
|
|
else:
|
|
run_rag_and_rfs(game_id)
|
|
|
|
|
|
|
|
print '\n\nAttempting to reboot PS3...'
|
|
logging('launch_ps3: command: '+ps3run+' -r '+self+' '+switches)
|
|
ps3runner = subprocess.Popen(ps3run+' -r '+self+' '+switches,
|
|
stdout=subprocess.PIPE)
|
|
|
|
error_message = 'failed'
|
|
|
|
response = ps3runner.communicate()[0]
|
|
|
|
if error_message in response.lower():
|
|
print '\n\nERROR:'
|
|
print word_wrap('No response from PS3! Please check that TargetManager '
|
|
'is running, and you are connected to your PS3!\n\n'
|
|
'TargetManager Says: %s\n\n'
|
|
'Press any key to return to Game Page.' % response)
|
|
wait_for_key()
|
|
return False
|
|
|
|
run_notson(game_id)
|
|
|
|
def WriteIPToFile(fname):
|
|
try:
|
|
ipfile = open(fname, 'w')
|
|
except IOError:
|
|
error_exit('Unable to open %s for writing IP' % fname)
|
|
|
|
ip = socket.gethostbyname(socket.gethostname())
|
|
|
|
ipfile.write('%s' % ip)
|
|
ipfile.close()
|
|
|
|
def run_targetmanager():
|
|
tm_path = os.path.join(os.getenv('SN_PS3_PATH'),
|
|
os.path.join('bin','ps3tm.exe'))
|
|
|
|
tm_path = '"'+tm_path+'"'
|
|
subprocess.Popen(tm_path)
|
|
|
|
def run_notson(game_id):
|
|
logfolder = os.path.dirname(game_versions[game_id]['self'])
|
|
#os.system('start PSNotson.py '+logfolder)
|
|
#command: "start "window title" "notson path" "logs folder"
|
|
os.system('start "%s" "%s" "%s"' % (
|
|
settings['defaults']['notson_title'],
|
|
settings['defaults']['psnotson'],
|
|
logfolder))
|
|
|
|
|
|
|
|
def target_manager_installed():
|
|
ps3run = os.path.join('bin','ps3run.exe')
|
|
if os.getenv('SN_PS3_PATH'):
|
|
if os.path.exists(os.path.join(os.getenv('SN_PS3_PATH'),ps3run)):
|
|
return True
|
|
else:
|
|
return False
|
|
else:
|
|
return False
|
|
|
|
|
|
|
|
def get_paths():
|
|
unsplit_paths = os.getenv('path')
|
|
paths = unsplit_paths.split(';')
|
|
return paths
|
|
|
|
|
|
def xdk_vars():
|
|
xedk = os.getenv('xedk')
|
|
path = os.getenv('path')
|
|
os.putenv('PATH',xedk+'\\bin\\win32;'+path+';')
|
|
|
|
|
|
def generate_date_foldername():
|
|
""" use the current date/time to generate a string that can be used
|
|
as a foldername """
|
|
foldername = time.strftime('%Y-%m-%d %H-%M-%S')
|
|
return foldername
|
|
|
|
def date_from_file(filename):
|
|
if not os.path.exists(filename):
|
|
error_exit('date_from_file(): File for generating foldername does not exist')
|
|
modtime = os.path.getmtime(filename)
|
|
modtime = time.localtime(modtime)
|
|
return time.strftime('%Y-%m-%d %H-%M-%S',modtime)
|
|
|
|
|
|
def backup_pstats(game_id):
|
|
""" backup existing pstats to a local folder """
|
|
logging('backup_pstats() called for '+game_id)
|
|
|
|
if 'logsource' not in game_versions[game_id]:
|
|
logging('No logsource found for %s' % game_id)
|
|
return False
|
|
|
|
#logging('backup_pstats() called for '+game_id)
|
|
print '\nBacking up pstats files...'
|
|
|
|
backup_location = settings['defaults']['pstats_backup']
|
|
|
|
if not os.path.exists(backup_location):
|
|
os.makedirs(backup_location)
|
|
|
|
|
|
logsources = game_versions[game_id]['logsource']
|
|
|
|
log_file_paths = []
|
|
for logsource in logsources:
|
|
for log_path in directory_list(logsource,'*.pstats'):
|
|
if log_path not in log_file_paths: log_file_paths.append(log_path)
|
|
|
|
for mod_path in log_file_paths:
|
|
newest_file = 0
|
|
if os.path.getmtime(mod_path) > newest_file:
|
|
newest_file = mod_path
|
|
|
|
if len(log_file_paths) == 0:
|
|
print ' none found'
|
|
return False
|
|
|
|
backup_location_final = os.path.join(backup_location,
|
|
date_from_file(newest_file))
|
|
if not os.path.exists(backup_location_final):
|
|
os.makedirs(backup_location_final)
|
|
|
|
for path in log_file_paths:
|
|
destination = os.path.join(backup_location_final,os.path.basename(path))
|
|
|
|
if os.path.exists(destination):
|
|
add_number = 1
|
|
split_dest = os.path.splitext(destination)
|
|
while os.path.exists(split_dest[0]+' ('+str(add_number)+')'+split_dest[1]):
|
|
add_number += 1
|
|
destination = split_dest[0]+' ('+str(add_number)+')'+split_dest[1]
|
|
print path
|
|
logging('backup_pstats() attempting to move: '+path+' to '+destination)
|
|
try:
|
|
shutil.copy2(path, destination)
|
|
except IOError:
|
|
error_exit('IOError in backup_pstats()')
|
|
|
|
print '... complete'
|
|
|
|
|
|
|
|
def delete_logs(game_id):
|
|
""" deletes logs and dmps using the stuff laid out in game_versions"""
|
|
if 'logfiles' not in game_versions[game_id]:
|
|
if 'xex' not in game_versions[game_id]:
|
|
logpath = os.environ.get('RAGE_CRASHDUMP_DIR')
|
|
if logpath == None:
|
|
logpath = 'x:\\gta\\build\\'
|
|
else:
|
|
logpath = os.path.dirname(game_versions[game_id]['xex'])
|
|
if os.path.exists(logpath):
|
|
os.chdir(logpath)
|
|
os.system('erase *.log')
|
|
os.system('erase *.dmp')
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
for location in game_versions[game_id]['logsource']:
|
|
for filename in game_versions[game_id]['logfiles']:
|
|
logging('delete_logs: %s triggered erase of %s files in %s' %
|
|
(game_id, filename, location))
|
|
if os.path.exists(location):
|
|
logging('erasing %s' % os.path.join(location, filename))
|
|
os.system('erase '+os.path.join(location,filename))
|
|
|
|
if 'homedir' in game_versions[game_id]:
|
|
for fname in FindPS3Dumps([game_versions[game_id]['homedir']]):
|
|
os.remove(fname)
|
|
|
|
|
|
|
|
return True
|
|
|
|
def cab_transfer(game_id):
|
|
""" replacing old cab transfer.py and it's over-complexity """
|
|
|
|
wait_for_xbox()
|
|
xdk_vars()
|
|
cab_folder_source = game_versions[game_id]['cab_folder_source']
|
|
cab_folder_destination = game_versions[game_id]['cab_folder_destination']
|
|
rpf_data_destination = 'devkit:\\gta4_2005' #should maybe replace this with something in game_versions
|
|
|
|
|
|
comment = ('If you continue, this will transfer or overwrite the %s cab '
|
|
'file without erasing any other CABs on the 360' %
|
|
game_versions[game_id]['name'])
|
|
|
|
|
|
if not os.path.exists(os.path.dirname(cab_folder_source)):
|
|
error_exit('Unable to find CAB file: '+cab_folder_source)
|
|
|
|
#try and prevent uploading to a folder that's actually a filename
|
|
if os.path.basename(cab_folder_source) == os.path.basename(cab_folder_destination):
|
|
if os.path.isfile(cab_folder_source):
|
|
cab_folder_destination = os.path.dirname(cab_folder_destination)
|
|
|
|
|
|
files_on_360 = xbdir_list(cab_folder_destination)
|
|
|
|
if len(files_on_360) > 0:
|
|
|
|
items = []
|
|
|
|
items.append(['Erase CABs',
|
|
'final_event',
|
|
'erase',
|
|
'Erase existing CAB files before transferring the new CAB file over'])
|
|
|
|
items.append(['Continue',
|
|
'final_event',
|
|
'continue',
|
|
'Transfer new CAB without erasing old CAB'])
|
|
|
|
selection = MiniMenu('Existing CAB files found',items,NO_BACK=True)
|
|
|
|
if selection == 'continue':
|
|
pass
|
|
elif selection == 'erase':
|
|
for cab_filename in files_on_360:
|
|
print 'Erasing:',cab_folder_destination.strip('\\')+'\\'+cab_filename[0]
|
|
#os.system('xbdel /f '+cab_folder_destination.strip('\\')+'\\'+cab_filename[0])
|
|
XBDel(cab_folder_destination.strip('\\')+'\\'+cab_filename[0])
|
|
|
|
#delete and remake the cab folder destination and rpf destination
|
|
## os.system('xbdel /r /f "HDD:\\$TitleUpdate"')
|
|
## os.system('xbdel /r /f "'+cab_folder_destination+'"')
|
|
print_slow_dots()
|
|
mkdir_error = os.system('xbmkdir "'+cab_folder_destination+'"')
|
|
if mkdir_error == 1:
|
|
#print 'making folders'
|
|
split_destination = cab_folder_destination.split('\\')
|
|
walking_path = ''
|
|
for path_part in split_destination:
|
|
if path_part.endswith(':'):
|
|
path_part += '\\'
|
|
walking_path = os.path.join(walking_path,path_part)
|
|
#print walking_path
|
|
#os.system('xbmkdir "'+walking_path+'"')
|
|
## os.system('xbdel /r /f "'+rpf_data_destination+'"')
|
|
print_slow_dots()
|
|
## os.system('xbmkdir "'+rpf_data_destination+'"')
|
|
|
|
|
|
#copy rfs.dat file across
|
|
#XBCopy(os.getenv('temp')+'\\rfs.dat',rpf_data_destination)
|
|
|
|
print '\n\nCopying CAB files, this may take some time\n\n'
|
|
XBCopyTree(cab_folder_source,cab_folder_destination)
|
|
|
|
print '\n\nCAB Transfer Complete!\n'
|
|
print 'Press any key to continue'
|
|
wait_for_key()
|
|
return True
|
|
|
|
|
|
def using_latest_cab_file(game_id):
|
|
"""Uses xbdir to determine the modified dates of a pair of files or folders
|
|
and returns True if all files appearing on the xbox folder also appear in the
|
|
cab_folder_source, and the modified dates match."""
|
|
if 'no_cab_check' in game_versions[game_id]:
|
|
return True
|
|
|
|
wait_for_xbox()
|
|
#xbdir = '"C:\\Program Files\\Microsoft Xbox 360 SDK\\bin\\win32\\xbdir.exe"'
|
|
|
|
xbdir = '"%s\\xbdir.exe"' % settings['defaults']['360sdk']
|
|
|
|
xbox_dir = subprocess.Popen(xbdir+' "'+game_versions[game_id]['cab_folder_destination']
|
|
+'"', stdout=subprocess.PIPE, universal_newlines=True)
|
|
network_dir = subprocess.Popen(xbdir+' "'+game_versions[game_id]['cab_folder_source'],
|
|
stdout=subprocess.PIPE, universal_newlines=True)
|
|
|
|
xbox_files = format_xbdir_output(xbox_dir.communicate()[0])
|
|
network_files = format_xbdir_output(network_dir.communicate()[0])
|
|
|
|
if len(xbox_files) != len(network_files):
|
|
return False
|
|
|
|
#see which filenames appearing in xbox_files match any filenames appearing in network_files
|
|
filename_matches = {} #[None]*len(xbox_files)
|
|
|
|
for pos, file_info in enumerate(xbox_files):
|
|
for net_pos, net_file_info in enumerate(network_files):
|
|
if file_info[0] == net_file_info[0]:
|
|
filename_matches[pos] = net_pos
|
|
|
|
|
|
#compare the matching files
|
|
file_matches = {}
|
|
for pos, net_pos in filename_matches.iteritems():
|
|
file_match = True
|
|
for e_pos, file_info in enumerate(xbox_files[pos]):
|
|
if file_info != network_files[net_pos][e_pos]:
|
|
file_match = False
|
|
if file_match: file_matches[xbox_files[pos][0]]=True
|
|
if not file_match: file_matches[xbox_files[pos][0]]=False
|
|
|
|
if len(file_matches) is 0:
|
|
return False
|
|
|
|
all_match = True
|
|
for filename,valid in file_matches.iteritems():
|
|
if not valid: all_match = False
|
|
|
|
return all_match
|
|
|
|
def xbdir_list(target,directories=False):
|
|
wait_for_xbox()
|
|
""" Returns formatted directory list for a directory (for xbox, but works
|
|
for pc as well """
|
|
#xbdir = '"C:\\Program Files\\Microsoft Xbox 360 SDK\\bin\\win32\\xbdir.exe"'
|
|
|
|
xbdir = '"%s\\xbdir.exe"' % settings['defaults']['360sdk']
|
|
xbox_dir = subprocess.Popen(xbdir+' "'+target+'"',
|
|
stdout=subprocess.PIPE, universal_newlines=True)
|
|
formatted = format_xbdir_output(xbox_dir.communicate()[0])
|
|
|
|
final_list = []
|
|
for item in formatted:
|
|
if item[3] == directories:
|
|
final_list.append(item)
|
|
|
|
|
|
|
|
return final_list #formatted
|
|
|
|
|
|
def format_xbdir_output(output,directories=False):
|
|
""" format output from xbdir to associate filenames with edited dates.
|
|
returns a list full of lists.
|
|
0: Year
|
|
1: Month
|
|
2: Day
|
|
3: Hour
|
|
4: Minute
|
|
5: Filename
|
|
6: filesize
|
|
7: directory? (if true, it's a file, if false, it's a directory) """
|
|
|
|
list_out = output.split('\n')
|
|
file_info = []
|
|
|
|
for line in list_out:
|
|
current = [None]*4
|
|
if len(line) > 28:
|
|
if line[0].isdigit():
|
|
#filename
|
|
current[0] = line[39:].rstrip('\n')
|
|
|
|
#filesize
|
|
if line[24:29] == '<DIR>':
|
|
current[1] = 0
|
|
else:
|
|
current[1] = int(line[18:38].replace(',','').strip())
|
|
|
|
#modified date in seconds since the epoch
|
|
time_line = line[0:18]+'m'
|
|
if time_line[12:14] == '00':
|
|
time_line = time_line[:11]+'12'+time_line[14:]
|
|
|
|
current[2]=time.mktime(time.strptime(time_line,'%m/%d/%Y %I:%M%p'))
|
|
#print 'stored time',current[1]
|
|
|
|
#file or directory?
|
|
if line[24:29] == '<DIR>':
|
|
current[3] = True
|
|
else:
|
|
current[3] = False
|
|
|
|
#time test
|
|
## timestring = line[0:18]+'m'
|
|
## print timestring,
|
|
## utime = time.strptime(timestring,'%m/%d/%Y %I:%M%p'),
|
|
## print utime[0],
|
|
## print time.strftime('%m/%d/%Y %I:%M%p',utime[0])
|
|
## print time.mktime(utime[0]),
|
|
## print time.ctime(time.mktime(utime[0]))
|
|
##
|
|
##
|
|
## raw_input()
|
|
|
|
file_info.append(current)
|
|
|
|
return file_info
|
|
|
|
|
|
def prepare_for_emulation(game_id):
|
|
"""transfer cab file and titleupdate to default xbox"""
|
|
title_update_dest = 'HDD:\\$TitleUpdate'
|
|
title_update_source = game_versions[game_id]['title_update']
|
|
|
|
#abandon ship if the titleupdate source can't be found
|
|
if not os.path.exists(title_update_source):
|
|
error_exit('$TitleUpdate could not be found. Please check '+title_update_source)
|
|
|
|
#do a regular cab transfer, if we have a cab file
|
|
if 'cab_folder_source' in game_versions[game_id]:
|
|
if not cab_transfer(game_id):
|
|
print 'Cancelled...'
|
|
return False
|
|
|
|
xdk_vars()
|
|
#destroy old titleupdate folder
|
|
#os.system('xbdel /r /f "'+title_update_dest+'"')
|
|
XBDel(title_update_dest)
|
|
|
|
print '\nCopying $TitleUpdate...'
|
|
#copy titleupdate
|
|
XBCopyTree(title_update_source,title_update_dest)
|
|
|
|
print '\n\nXBox is now ready for use with Disc Emulator'
|
|
print '\nPress any key to return to menu'
|
|
wait_for_key()
|
|
|
|
#===============================================================================
|
|
|
|
|
|
#==== File Updating Stuff ======================================================
|
|
def update_sync_ini():
|
|
""" Updates the local Synchronize It! profile with an ini file from
|
|
the network, if the file on the network has a newer 'modified on' date.
|
|
Locations of the two files are retrieved from game_versions.ini"""
|
|
for switch in command_line_switches():
|
|
if switch == '-focus':
|
|
print 'skipping sync ini update'
|
|
return False
|
|
|
|
localfile = settings['defaults']['syncitlocal']
|
|
remotefile = settings['defaults']['syncitremote']
|
|
|
|
|
|
if os.path.exists(localfile) and os.path.exists(remotefile):
|
|
files_match = filecmp.cmp(localfile, remotefile)
|
|
else:
|
|
files_match = True #oh god this is a horrible way of doing this, rewrite it immediately
|
|
|
|
|
|
if not files_match:
|
|
selection = 1
|
|
if selection == 1:
|
|
try:
|
|
shutil.copy2(remotefile, localfile)
|
|
logging('Updating Synchronize It! profile.')
|
|
except IOError:
|
|
print 'ERROR: Problem updating sync ini, local file may not exist.'
|
|
logging('Problem updating sync ini, local file may not exist, or may be in use.','ERROR')
|
|
time.sleep(0.5)
|
|
return True
|
|
return False
|
|
|
|
|
|
def check_menu_update():
|
|
""" Checks the local qa menu files against remote qa menu files, tells the
|
|
user to update (how will they update?) if the remote files are newer. """
|
|
for switch in command_line_switches():
|
|
if switch == '-focus':
|
|
print 'skipping menu update'
|
|
return False
|
|
|
|
local = settings['defaults']['qamenulocal']
|
|
remote = settings['defaults']['qamenuremote']
|
|
menufiles = settings['defaults']['qamenufiles']
|
|
|
|
menunotfound = 0
|
|
if not os.path.exists(local):
|
|
print 'ERROR: QA Menu not found in '+local+' please rerun installer script',
|
|
print_slow_dots(5)
|
|
menunotfound += 1
|
|
if not os.path.exists(remote):
|
|
print 'ERROR: QA Menu update files not found in '+remote,
|
|
print_slow_dots(5)
|
|
menunotfound += 1
|
|
|
|
files_updated = 0
|
|
updaterequired = 0
|
|
filenames_updated = []
|
|
if menunotfound == 0:
|
|
for filename in menufiles:
|
|
localpath = os.path.join(local,filename)
|
|
remotepath = os.path.join(remote,filename)
|
|
if not os.path.exists(localpath) and os.path.exists(remotepath):
|
|
shutil.copy2(remotepath, localpath)
|
|
logging('Created file '+localpath+' from '+remotepath)
|
|
files_updated += 1
|
|
filenames_updated.append(filename)
|
|
elif update_file_if_different(localpath, remotepath) == True:
|
|
files_updated += 1
|
|
logging('Updated file '+localpath+' from '+remotepath)
|
|
filenames_updated.append(filename)
|
|
#time.sleep(0.5)
|
|
## if settings_update_required() == True:
|
|
## settingsfile = 'settings.ini'
|
|
## shutil.copy2(os.path.join(remote, settingsfile),os.path.join(local,settingsfile))
|
|
## logging('Updated file '+os.path.join(local,settingsfile)+' from '+os.path.join(remote, settingsfile))
|
|
## files_updated += 1
|
|
## filenames_updated.append(settingsfile)
|
|
## settings_updated = True
|
|
## else:
|
|
## settings_updated = False
|
|
|
|
if files_updated > 0:
|
|
os.system('cls')
|
|
print 'The following file(s) succesfully updated:'
|
|
for filename in filenames_updated:
|
|
print ' '*3, filename
|
|
## if settings_updated:
|
|
## print word_wrap('\nSORRY: The settings.ini file had to be updated, meaning you will have been returned to default settings. I apologise!')
|
|
|
|
print '\n'
|
|
print_changes()
|
|
error_exit('\nComponents updated, please restart QA Menu!')
|
|
|
|
|
|
##def update_file_if_newer(localfile, remotefile):
|
|
## """ checks localfile against remotefile, if remotefile is newer it
|
|
## attempts to overwrite localfile with remotefile. Returns true if
|
|
## the file is updated succesfully"""
|
|
##
|
|
## file_updated = False
|
|
## if is_file2_newer(localfile, remotefile):
|
|
## try:
|
|
## shutil.copy2(remotefile,localfile)
|
|
## #print os.path.basename(remotefile)+' update succesful!'
|
|
## file_updated = True
|
|
## #time.sleep(2)
|
|
## except IOError:
|
|
## print '\nERROR: Please make sure '+localfile+' is not in use or write protected'
|
|
## raw_input('\nPress ENTER to continue without update')
|
|
## return file_updated
|
|
|
|
|
|
def update_file_if_different(localfile, remotefile):
|
|
""" checks localfile against remotefile, if remotefile is different it
|
|
attempts to overwrite localfile with remotefile. Returns true if
|
|
the file is updated succesfully"""
|
|
|
|
file_updated = False
|
|
if not os.path.exists(remotefile):
|
|
error_exit(remotefile+' not found.')
|
|
if not filecmp.cmp(localfile, remotefile):
|
|
try:
|
|
shutil.copy2(remotefile,localfile)
|
|
#print os.path.basename(remotefile)+' update succesful!'
|
|
file_updated = True
|
|
#time.sleep(2)
|
|
except IOError:
|
|
print '\nERROR: Please make sure '+localfile+' is not in use or write protected'
|
|
raw_input('\nPress ENTER to continue without update')
|
|
return file_updated
|
|
|
|
|
|
def is_file2_newer(file1,file2):
|
|
""" Returns True if file2 is newer than file1. Prints an error and
|
|
returns False if either file is missing """
|
|
if os.path.exists(file1):
|
|
file1found = True
|
|
else:
|
|
file1found = False
|
|
file2_is_newer = False
|
|
print 'ERROR: Attempting to compare two files, unable to find '+file1
|
|
time.sleep(0.5)
|
|
|
|
if os.path.exists(file2):
|
|
file2found = True
|
|
else:
|
|
file2found = False
|
|
file2_is_newer = False
|
|
print 'ERROR: Attempting to compare two files, unable to find '+file2
|
|
time.sleep(0.5)
|
|
|
|
if file1found and file2found:
|
|
if int(os.path.getmtime(file1)) < int(os.path.getmtime(file2)):
|
|
file2_is_newer = True
|
|
elif os.path.getmtime(file1) == os.path.getmtime(file2):
|
|
file2_is_newer = False
|
|
else:
|
|
file2_is_newer = False
|
|
|
|
return file2_is_newer
|
|
|
|
|
|
def print_changes(number_of_changes = 1):
|
|
""" Prints changes found in the changes log on the network. Prints
|
|
only the number of changes supplied (one "change" is determined by
|
|
the date in brackets above the changes from that day).
|
|
By default, only prints the latest change.
|
|
If number_of_changes is 0, prints all changes found."""
|
|
|
|
if not os.path.exists('N:\\RSGEDI\\QA\\QA Menu\\changes.txt'):
|
|
return False
|
|
|
|
changes_file = open('N:\\RSGEDI\\QA\\QA Menu\\changes.txt')
|
|
|
|
brackets_found = 0
|
|
changes = []
|
|
|
|
for line in changes_file:
|
|
if line.startswith('['):
|
|
brackets_found += 1
|
|
changes.append(line)
|
|
else:
|
|
if line != '\n':
|
|
changes[brackets_found-1] += line
|
|
|
|
if number_of_changes == 0:
|
|
for line in changes:
|
|
print line
|
|
else:
|
|
changes_printed = 0
|
|
for line in changes:
|
|
print line
|
|
changes_printed += 1
|
|
|
|
if changes_printed == number_of_changes:
|
|
break
|
|
return True
|
|
|
|
##def settings_update_required():
|
|
## """ checks the local settings.ini file with the one on the network.
|
|
## if the remote file has any settings that the local file does,
|
|
## return True"""
|
|
#### remote_settings_file = os.path.join(get_setting('defaults','qamenuremote'),'settings.ini')
|
|
#### remote_defaults = load_settings(remote_settings_file)
|
|
####
|
|
#### local_settingnames = []
|
|
#### remote_settingnames = []
|
|
#### for name in defaults:
|
|
#### local_settingnames.append(name)
|
|
#### for name in remote_defaults:
|
|
#### remote_settingnames.append(name)
|
|
####
|
|
###### if len(local_settingnames) != len(remote_settingnames):
|
|
###### print 'mismatch length'
|
|
###### update_required = True
|
|
###### else:
|
|
#### missing_from_local = 0
|
|
#### for name in remote_settingnames:
|
|
#### if name not in local_settingnames:
|
|
#### missing_from_local += 1
|
|
#### if missing_from_local > 0:
|
|
#### update_required = True
|
|
#### if missing_from_local == 0:
|
|
#### update_required = False
|
|
####
|
|
#### return update_required
|
|
## return False
|
|
|
|
#===============================================================================
|
|
|
|
|
|
#====Log Uploader===============================================================
|
|
def upload_logs(game_id):
|
|
""" User interface for all the log copying stuff. Uses settings found in
|
|
game_id in game_versions.ini """
|
|
|
|
if not os.path.exists(game_versions[game_id]['logdestination']):
|
|
error_exit('upload_logs: log destination does not exist')
|
|
|
|
if 'multiplayer' in game_versions[game_id]['logdestination'].lower():
|
|
multiplayer = True
|
|
elif 'singleplayer' in game_versions[game_id]['logdestination'].lower():
|
|
multiplayer = False
|
|
else:
|
|
selection = menu_maker_2('Singleplayer or Multiplayer',['Singleplayer','Multiplayer'],'Were you playing a single player game, or a multiplayer game?\n\nNOTE: Multiplayer option left in for legacy. If playing multiplayer, please use MPLogGrabber instead.',NO_BACK=True)
|
|
|
|
if selection == 0:
|
|
multiplayer = False
|
|
elif selection == 1:
|
|
multiplayer = True
|
|
|
|
|
|
files_with_console = []
|
|
for location in game_versions[game_id]['logsource']:
|
|
files_with_console += directory_list(location,'console')
|
|
#print files_with_console
|
|
|
|
if len(files_with_console) > 0:
|
|
files_with_console_and_log = []
|
|
for filename in files_with_console:
|
|
if filename.endswith('log'):
|
|
files_with_console_and_log.append(filename)
|
|
elif len(files_with_console) < 1:
|
|
files_with_console_and_log = []
|
|
|
|
|
|
if len(files_with_console_and_log) < 1:
|
|
username = raw_input('Enter your profile name: ')
|
|
else:
|
|
usernames =[]
|
|
for filename in files_with_console_and_log:
|
|
usernames += get_profile_name_from_log(filename)
|
|
final_usernames = []
|
|
for name in usernames:
|
|
if not name in final_usernames: final_usernames.append(name)
|
|
if len(final_usernames) > 1:
|
|
selection = menu_maker_2('Select a Profile Name',final_usernames,'Multiple profile names were found, please choose the correct profile name',NO_BACK=True)
|
|
username = final_usernames[selection]
|
|
else:
|
|
if len(final_usernames) == 0:
|
|
username = raw_input('Enter your profile name: ')
|
|
else:
|
|
username = final_usernames[0]
|
|
|
|
if multiplayer == True:
|
|
playertype = client_or_host(game_id)
|
|
if not playertype:
|
|
selection = menu_maker_2('Host or Client?',['Host','Client'],'Were you the host or a client in that game?',NO_BACK=True)
|
|
if selection == 0:
|
|
host_or_client = 'host'
|
|
if selection == 1:
|
|
host_or_client = 'client'
|
|
else:
|
|
host_or_client = playertype
|
|
else:
|
|
host_or_client = ''
|
|
|
|
print '\n\nProfile Name:',username
|
|
if host_or_client: print '\n Playertype:', host_or_client.upper()
|
|
print '\nWhat folder should the logs be sent to?\n'
|
|
|
|
print box_text('WARNING: Please only use valid bug numbers for folder names. '
|
|
'If you do not have a valid bug number yet, make sure you remember '
|
|
'to come back and rename the folder to match the bug number.\n\n'
|
|
'IF YOU FORGET, BRIAN WILL FIND YOU AND HE WILL TAKE YOUR EYES.', 1)
|
|
|
|
bugnumber = raw_input('Enter a bug number or destination folder: ')
|
|
log_folder = os.path.join(game_versions[game_id]['logdestination'],
|
|
bugnumber)
|
|
if host_or_client == '':
|
|
log_destination = os.path.join(log_folder,username)
|
|
else:
|
|
log_destination = os.path.join(log_folder,username+' ('+host_or_client+')')
|
|
|
|
while os.path.exists(log_destination) or bugnumber == '':
|
|
if bugnumber == '':
|
|
print '\nERROR: Blank destination folder entered. Please enter a destination folder'
|
|
else:
|
|
print '\nERROR: Log destination \''+log_destination+'\' already exists.'
|
|
bugnumber = raw_input('\nPlease enter a new bugnumber or foldername: ')
|
|
log_folder = os.path.join(game_versions[game_id]['logdestination'],
|
|
bugnumber)
|
|
if host_or_client == '':
|
|
log_destination = os.path.join(log_folder,username)
|
|
else:
|
|
log_destination = os.path.join(log_folder,username+' ('+host_or_client+')')
|
|
|
|
|
|
|
|
file_list = list_of_logfiles(game_versions[game_id]['logsource'],
|
|
game_versions[game_id]['logfiles'])
|
|
#copy_files(file_list,log_destination)
|
|
if not os.path.exists(log_destination):
|
|
os.makedirs(log_destination)
|
|
|
|
if 'homedir' in game_versions[game_id]:
|
|
rardest = os.path.join(log_destination, 'ps3core.rar')
|
|
dumps = FindPS3Dumps([game_versions[game_id]['homedir']])
|
|
if len(dumps) > 0:
|
|
print 'Compressing PS3 Dump...'
|
|
RARfile(dumps, rardest)
|
|
else:
|
|
print 'No PS3 Dump files found'
|
|
|
|
|
|
FancyCopyFiles(file_list, log_destination)
|
|
print '\nLogs Copied! Hooray!'
|
|
print 'All logs copied to', log_destination
|
|
|
|
friendly_file_list = 'Files uploaded to:\n"'+log_destination+'"\n\n'
|
|
|
|
for fname in file_list:
|
|
friendly_file_list += ' + '+os.path.basename(fname)+'\n'
|
|
|
|
|
|
|
|
items = []
|
|
|
|
items.append(['Continue to game page',
|
|
'final_event',
|
|
'continue',
|
|
friendly_file_list])
|
|
|
|
items.append(['Open logs folder',
|
|
'final_event',
|
|
'logs_folder',
|
|
'Open log destination folder'])
|
|
|
|
|
|
|
|
while True:
|
|
selection = MiniMenu('Logs copied!',items,NO_BACK=True)
|
|
if selection == 'logs_folder':
|
|
try:
|
|
os.startfile(log_destination)
|
|
except WindowsError:
|
|
## error_exit('Open Logs Folder tried to open a logs folder that '
|
|
## 'does not exist.')
|
|
print word_wrap('ERROR: Open logs folder tried to open a logs '
|
|
'folder that does not exist.\n\nPress any '
|
|
'key to continue.')
|
|
wait_for_key()
|
|
elif selection == 'continue':
|
|
break
|
|
|
|
|
|
|
|
return True
|
|
|
|
def directory_list(directory, extension=None):
|
|
""" Gives a list of files present in the directory provided. If "extension"
|
|
is supplied, lists only files with that string present in the filename. """
|
|
if not os.path.exists(directory):
|
|
return []
|
|
|
|
files = os.listdir(directory)
|
|
if extension == None:
|
|
return files
|
|
|
|
files_with_extension = []
|
|
for filename in files:
|
|
if extension.strip('*') in filename:
|
|
files_with_extension.append(os.path.join(directory,filename))
|
|
return files_with_extension
|
|
|
|
|
|
def copy_files(file_list,destination):
|
|
""" copies the list of files in file_list to 'destination'"""
|
|
if not os.path.exists(destination):
|
|
os.makedirs(destination)
|
|
|
|
for filename in file_list:
|
|
destinationpath = os.path.join(destination,os.path.basename(filename))
|
|
if not os.path.exists(destinationpath):
|
|
print 'Copying \''+os.path.basename(filename)+'\' to \''+destination+'\''
|
|
shutil.copy2(filename, destinationpath)
|
|
else:
|
|
## print 'Overwriting \''+destinationpath+'\''
|
|
## os.remove(destinationpath)
|
|
## shutil.copy2(filename, destinationpath)
|
|
add_number = 0
|
|
destinationpath_plus = destinationpath
|
|
while os.path.exists(destinationpath_plus):
|
|
add_number += 1
|
|
split_dest = os.path.splitext(destinationpath)
|
|
destinationpath_plus = split_dest[0]+' ('+str(add_number)+')'+split_dest[1]
|
|
logging('Copying "%s" to "%s"'% (filename,destinationpath_plus))
|
|
shutil.copy2(filename,destinationpath_plus)
|
|
|
|
|
|
|
|
|
|
def list_of_logfiles(logsources,files):
|
|
""" returns a list of files that match the filenames/wildcards present in
|
|
'files' """
|
|
#if not os.path.exists(source):
|
|
#return None
|
|
|
|
logfiles = []
|
|
for source in logsources:
|
|
for filename in files:
|
|
if not filename.startswith('*.'):
|
|
if os.path.exists(os.path.join(source,filename)):
|
|
logfiles.append(os.path.join(source,filename))
|
|
if filename.startswith('*.'):
|
|
for valid_file in directory_list(source,filename):
|
|
logfiles.append(os.path.join(source,valid_file))
|
|
|
|
|
|
return logfiles
|
|
|
|
|
|
def get_profile_name_from_log(location):
|
|
""" Searches for a console log in the location provided, then tries to
|
|
find a profile name inside the log. Returns a list of all profile names """
|
|
#if not os.path.exists(location):
|
|
#return None
|
|
|
|
console_log_file = open(location)
|
|
username_lines = []
|
|
for line in console_log_file:
|
|
if 'Gamer:' in line and 'SIGNED' in line:
|
|
#username_line = line
|
|
username_lines.append(line)
|
|
|
|
usernames = []
|
|
for line in username_lines:
|
|
usernames.append(clean_profile_line(line))
|
|
|
|
return usernames
|
|
|
|
|
|
def clean_profile_line(line):
|
|
""" Returns a cleaned up profile name from a line found in a console log """
|
|
## profile_start = line.find('"')
|
|
## profile_end = line.rfind('"')
|
|
##
|
|
## username = line[profile_start+1:profile_end]
|
|
|
|
regex = re.compile('(?<=Gamer:")[^"]+(?=")')
|
|
username = regex.findall(line)
|
|
|
|
|
|
|
|
return username[0]
|
|
|
|
|
|
#====General Stuff==============================================================
|
|
def get_keypress():
|
|
"""read the keybuffer, return a key if there's one in there"""
|
|
key = ''
|
|
special_key = False
|
|
|
|
if not msvcrt.kbhit():
|
|
return False
|
|
|
|
key = msvcrt.getch()
|
|
|
|
if key == '\000' or key == '\xe0':
|
|
special_key = True
|
|
key = msvcrt.getch()
|
|
|
|
#special case for non-special special buttons
|
|
if ord(key) == 27: key = 'ESCAPE'
|
|
elif ord(key) == 13: key = 'RETURN'
|
|
elif ord(key) == 9: key = 'TAB'
|
|
elif ord(key) == 8: key = 'BACKSPACE'
|
|
|
|
if special_key:
|
|
key = translate_special_key(key)
|
|
|
|
#logging(key,'TEST') #<-- that'll pipe everything typed straight into log
|
|
return key
|
|
|
|
|
|
def translate_special_key(key):
|
|
"""Translate letters into their special-key equivalent."""
|
|
ord_key = ord(key)
|
|
#cursor keys
|
|
if ord_key == 75: return_key = 'CURSOR_LEFT'
|
|
elif ord_key == 77: return_key = 'CURSOR_RIGHT'
|
|
elif ord_key == 80: return_key = 'CURSOR_DOWN'
|
|
elif ord_key == 72: return_key = 'CURSOR_UP'
|
|
|
|
#home bank
|
|
elif ord_key == 82: return_key = 'INSERT'
|
|
elif ord_key == 83: return_key = 'DELETE'
|
|
elif ord_key == 71: return_key = 'HOME'
|
|
elif ord_key == 79: return_key = 'END'
|
|
elif ord_key == 73: return_key = 'PAGE_UP'
|
|
elif ord_key == 81: return_key = 'PAGE_DOWN'
|
|
|
|
#function keys
|
|
elif ord_key == 59: return_key = 'F1'
|
|
elif ord_key == 60: return_key = 'F2'
|
|
elif ord_key == 61: return_key = 'F3'
|
|
elif ord_key == 62: return_key = 'F4'
|
|
elif ord_key == 63: return_key = 'F5'
|
|
elif ord_key == 64: return_key = 'F6'
|
|
elif ord_key == 65: return_key = 'F7'
|
|
elif ord_key == 66: return_key = 'F8'
|
|
elif ord_key == 67: return_key = 'F9'
|
|
elif ord_key == 68: return_key = 'F10'
|
|
elif ord_key == 133: return_key = 'F11'
|
|
elif ord_key == 134: return_key = 'F12'
|
|
|
|
else: return_key = key
|
|
|
|
return return_key
|
|
|
|
def wait_for_key():
|
|
while True:
|
|
key = get_keypress()
|
|
if key != False:
|
|
break
|
|
else:
|
|
time.sleep(0.5)
|
|
|
|
return key
|
|
|
|
def clear_key_buffer():
|
|
key = get_keypress()
|
|
while key != False:
|
|
key = get_keypress()
|
|
|
|
|
|
def error_exit(quitmessage=None, log=True):
|
|
""" quits with the supplied error message. if no message is supplied
|
|
prints with some basic debug information """
|
|
if quitmessage == None:
|
|
caller_name = sys._getframe(1).f_code.co_name
|
|
print 'An error occured in',caller_name
|
|
if log: logging('An error occured in'+caller_name,'ERROR')
|
|
else:
|
|
print word_wrap('ERROR: '+quitmessage)
|
|
if log: logging(quitmessage.replace('\n',' '),'ERROR')
|
|
print '\nPress any key to exit'
|
|
|
|
selection = wait_for_key()
|
|
if selection == '1':
|
|
caller_name = sys._getframe(1).f_code.co_name
|
|
print 'An error occured in',caller_name
|
|
|
|
traceback.print_stack()
|
|
#wait_for_key()
|
|
|
|
sys.exit()
|
|
|
|
def print_slow_dots(number=3,speed=0.3):
|
|
""" prints a sequence of dots one at a time with a gap of speed ms
|
|
between the dots. if you want it to follow a statement, do like this:
|
|
print 'waiting', #note the comma
|
|
print_slow_dots() """
|
|
dots_printed = 0
|
|
while dots_printed != number:
|
|
print '. ',
|
|
dots_printed += 1
|
|
time.sleep(speed)
|
|
|
|
def box_text(text='',boxtype=0):
|
|
""" Print text in a box made of ascii characters, length determined by
|
|
longest line in 'text', or word wrapped to 79 chars.
|
|
|
|
boxtypes:
|
|
0 = single line
|
|
1 = double line
|
|
2 = blocks """
|
|
|
|
single_line = [chr(218),
|
|
chr(196),
|
|
chr(191),
|
|
chr(179),
|
|
chr(179),
|
|
chr(192),
|
|
chr(196),
|
|
chr(217)]
|
|
|
|
double_line = [chr(201),
|
|
chr(205),
|
|
chr(187),
|
|
chr(186),
|
|
chr(186),
|
|
chr(200),
|
|
chr(205),
|
|
chr(188)]
|
|
|
|
blocks = [chr(219),
|
|
chr(223),
|
|
chr(219),
|
|
chr(219),
|
|
chr(219),
|
|
chr(219),
|
|
chr(220),
|
|
chr(219)]
|
|
|
|
splodge = [chr(219)]*8
|
|
|
|
if boxtype == 0:
|
|
elements = single_line
|
|
elif boxtype == 1:
|
|
elements = double_line
|
|
elif boxtype == 2:
|
|
elements = blocks
|
|
else:
|
|
elements = splodge
|
|
|
|
#print text
|
|
|
|
text = word_wrap(text,74)
|
|
|
|
#print text
|
|
text_l = text.splitlines()
|
|
|
|
box_height = len(text_l)
|
|
|
|
box_width = 0
|
|
for line in text_l:
|
|
if len(line) > box_width:
|
|
box_width = len(line)
|
|
|
|
box_width += 4
|
|
|
|
#print 'box:',box_width
|
|
|
|
top = elements[0]+elements[1]*(box_width-2)+elements[2]
|
|
bottom = elements[5]+elements[6]*(box_width-2)+elements[7]
|
|
|
|
middle = ''
|
|
for key,line in enumerate(text_l):
|
|
#middle += elements[3]+' '+line.ljust(box_width-4)+' '+elements[4]
|
|
middle += '%s %s %s' % (elements[3],
|
|
line.ljust(box_width-4),
|
|
elements[4])
|
|
if not key == len(text_l)-1:
|
|
middle += '\n'
|
|
|
|
#print top
|
|
#print middle
|
|
#print bottom
|
|
nl = '\n'
|
|
return top+nl+middle+nl+bottom
|
|
|
|
##def word_wrap(text, chars=79, ignore_codes=True):
|
|
## """Inserts linebreaks into a string in order to prevent it wrapping
|
|
## in the middle of a word. defaults to the width of the console """
|
|
##
|
|
## sentence = []
|
|
## word = ''
|
|
## position = 1
|
|
## for letter in text:
|
|
## if letter == ' ' or position == len(text):
|
|
## word += letter
|
|
## sentence.append(word)
|
|
## word = ''
|
|
## position += 1
|
|
## else:## word += letter
|
|
## position +=1
|
|
##
|
|
## #code_length = 0
|
|
## charsdisplayed = 0
|
|
## wrapped = ''
|
|
## for word in sentence:
|
|
## counting = False
|
|
## code_length = 0
|
|
##
|
|
## #See if there are any codes and remove them from the character count
|
|
## #so that word wrapping doesn't get fouled up by character codes.
|
|
## if ignore_codes and word.find('~') != -1 and word.find('|') != -1:
|
|
## for letter in word:
|
|
## if letter == '~':
|
|
## counting = True
|
|
## elif letter == '|':
|
|
## code_length += 1
|
|
## counting = False
|
|
##
|
|
## if counting:
|
|
## code_length += 1
|
|
## word_length = len(word)-code_length
|
|
## else:
|
|
## word_length = len(word)
|
|
##
|
|
#### print word
|
|
#### print word_length
|
|
#### print len(word)
|
|
#### print code_length
|
|
#### raw_input()
|
|
##
|
|
## if charsdisplayed+word_length > chars:
|
|
## if not word_length > chars:
|
|
## wrapped += '\n'+word
|
|
## charsdisplayed = word_length
|
|
## else:
|
|
## wrapped += word
|
|
## lengthdisplayed = word_length + charsdisplayed
|
|
## while lengthdisplayed > chars:
|
|
## lengthdisplayed -= chars
|
|
## charsdisplayed = lengthdisplayed
|
|
## else:
|
|
## wrapped += word
|
|
## if '\n' in word:
|
|
## charsdisplayed = word_length - word.find('\n')
|
|
## else:
|
|
## charsdisplayed += word_length
|
|
##
|
|
## return wrapped
|
|
|
|
def word_wrap(text,chars=79,ignore_codes=True):
|
|
if ignore_codes:
|
|
length = lambda x: len(remove_codes(x))
|
|
else:
|
|
length = lambda x: len(x)
|
|
|
|
lines = text.splitlines()
|
|
|
|
#print lines
|
|
|
|
wrapped=''
|
|
|
|
for line in lines:
|
|
if length(line) > chars:
|
|
split_line = line.split()
|
|
wrapped_line = ''
|
|
displayed = 0
|
|
for word in split_line:
|
|
if displayed + length(word) > chars:
|
|
#TODO:split words that are too long for one line here
|
|
wrapped_line = wrapped_line.strip()
|
|
wrapped_line += '\n'
|
|
displayed = 0
|
|
wrapped_line += word+' '
|
|
displayed += length(word+' ')
|
|
|
|
else:
|
|
wrapped_line = line
|
|
if line == ' ': wrapped_line = '\n'
|
|
wrapped += wrapped_line+'\n'
|
|
|
|
return wrapped
|
|
|
|
|
|
def remove_codes(text):
|
|
""" Remove any colour control codes from a string and return it """
|
|
first_code = find_code(text)
|
|
while first_code != -1:
|
|
text = text[:first_code] + text[text[first_code:].find('|')+1+first_code:]
|
|
first_code = find_code(text)
|
|
return text
|
|
|
|
def find_code(text):
|
|
code_found = -1
|
|
for index,letter in enumerate(text):
|
|
if letter == '~':
|
|
if text[index+1].lower() == 'b' or text[index+1].lower() == 'f':
|
|
if text[index:].find('|') != -1:
|
|
return index
|
|
|
|
return code_found
|
|
|
|
#Replace this with an implementation of get_tasklist() ?
|
|
def find_running_process(process):
|
|
error_exit('find_running_process deprecated, use ProcessRunning instead')
|
|
""" Uses 'tasklist | find' to find if a process is running.
|
|
NOTE: It will return True if it finds a process that contains a
|
|
segment of 'process' string. (i.e. searching for 'explore' will
|
|
return true because it finds 'explorer' running."""
|
|
#these will be 0 if the app IS running. DON'T FORGET.
|
|
process_running = os.system('tasklist | find /I "'+process+'"')
|
|
|
|
if process_running == 0:
|
|
process_running_final = True
|
|
elif process_running == 1:
|
|
process_running_final = False
|
|
else:
|
|
error_exit('find_running_process received an unexpected response from "tasklist | find".\n\nprocess: '+process)
|
|
|
|
return process_running_final
|
|
|
|
def logging(data, log_type=False):
|
|
""" Dump various Handy Messages and errors out to a logfile. """
|
|
if '-nolog' in command_line_switches():
|
|
return False
|
|
|
|
if log_type is 'EVENT':
|
|
if '-eventlog' not in command_line_switches():
|
|
return False
|
|
|
|
#sys.path[0] returns the folder of the running script
|
|
#filename = os.path.join(sys.path[0],'qa_menu.log')
|
|
#instead we grab filename from a global:
|
|
filename = log_path
|
|
|
|
## if os.path.exists(filename) and not os.access(filename,os.W_OK):
|
|
## return False
|
|
|
|
try:
|
|
logfile = codecs.open(filename, 'a', 'utf-8')#out = codecs.open(fname, 'w', 'utf-8')
|
|
except IOError:
|
|
print ('Error: Unable to access logfile, user may not have write '
|
|
'permission for "%s"' % filename)
|
|
print 'Press any key to exit'
|
|
#wait_for_key()
|
|
sys.exit()
|
|
|
|
|
|
|
|
formatted_time = time.strftime('%Y/%m/%d %H:%M:%S')
|
|
if not log_type:
|
|
log_type = 'MSG'
|
|
|
|
output_data = formatted_time+' '+str(log_type).upper()+': '+data+'\n'
|
|
|
|
try:
|
|
logfile.write(output_data)
|
|
logfile.close()
|
|
except IOError:
|
|
error_exit('Logging(): IOError while writing %s' % filename,log=False)
|
|
|
|
reduce_logfile_size(filename)
|
|
|
|
|
|
def reduce_logfile_size(logfn='x:\\qa menu\\qa_menu.log',target_size=102400):
|
|
"""delete the beginning of a log file until it is less than a target size"""
|
|
#if the file doesn't exist, or can't be written to give up
|
|
#sys.path[0] returns the folder of the running script
|
|
|
|
if not os.path.exists(logfn):
|
|
logging('Attempted to reduce size of "'+logfn+'", but failed because the file could not be found.')
|
|
return False
|
|
|
|
#if the file isn't bigger than the target size, give up
|
|
if not os.stat(logfn).st_size > target_size:
|
|
return False
|
|
|
|
#See if we don't have write access to the logfile, inform the user if we don't.
|
|
if not os.access(logfn,os.W_OK):
|
|
print word_wrap('WARNING: Unable to write to logfile.\n\nThis could mean you don\'t have '
|
|
'admin permission on this PC, or that the logfile is set to \'read only\'')
|
|
print ('\nPress any key to attempt to continue')
|
|
wait_for_key()
|
|
return False
|
|
|
|
#Put the last 'target_size' bytes of data in
|
|
log_file = open(logfn,'r')
|
|
try:
|
|
log_file.seek(-target_size,os.SEEK_END)
|
|
except IOError:
|
|
crash_info = (target_size,
|
|
os.SEEK_END,
|
|
os.fstat(log_file.fileno()))
|
|
|
|
raise Exception('Weird reduce_logfile_size crash. TELL THOMAS!\n'
|
|
'target_size is %s\n'
|
|
'os.SEEK_END is %s\n'
|
|
'log file fstat:\n%s' % crash_info)
|
|
log_data = log_file.read()
|
|
log_file.close()
|
|
|
|
#find the first newline (so that we aren't cutting a line in half)
|
|
first_newline = log_data.find('\n')
|
|
last_newline = log_data.rfind('\n')
|
|
|
|
#write the last 'target_size' bytes of data back to the logfile
|
|
log_file = open(logfn,'w')
|
|
log_file.write(log_data[first_newline+1:last_newline+1])
|
|
log_file.close()
|
|
|
|
#removed logging of this when i decided to reduce logsize every time the
|
|
#log was added to (because it would create a mad loop of doom)
|
|
#logging('Reduced "'+logfn+'" to '+str(target_size)+' bytes.')
|
|
|
|
return True
|
|
|
|
def delete_old_pstats():
|
|
""" deletes pstats backup folders that are older than a month """
|
|
backup_dir = 'x:\\pstats_backup'
|
|
|
|
if not os.path.exists(backup_dir):
|
|
return False
|
|
|
|
files_and_dirs = os.listdir(backup_dir)
|
|
one_month = 2629743 #roughly the number of seconds in one month
|
|
#one_month = 86400*2 #CHANGE THIS, THIS IS ONLY 2 DAYS.
|
|
one_month_old = time.time() - one_month
|
|
|
|
folders = []
|
|
for path in files_and_dirs:
|
|
full_path = os.path.join(backup_dir,path)
|
|
if os.path.isdir(full_path):
|
|
if os.stat(full_path).st_mtime < one_month_old:
|
|
folders.append(full_path)
|
|
|
|
if len(folders) > 0: logging('Cleaning up "'+backup_dir+'"')
|
|
|
|
for path in folders:
|
|
logging('Deleting "'+path+'"')
|
|
shutil.rmtree(path)
|
|
|
|
return True
|
|
|
|
def is_xbox_ready(delay=0.25):
|
|
""" use xbconsoletype to tell if the default xbox is responding (ie,
|
|
switched on) Returns False if the xbox doesn't respond within 250ms"""
|
|
xdk_vars()
|
|
xbox = subprocess.Popen('xbconsoletype',stdout=subprocess.PIPE, stderr=subprocess.PIPE,universal_newlines=True)
|
|
|
|
start_time = time.time()
|
|
xbox_responding = False
|
|
while time.time()-start_time < delay:
|
|
if xbox.poll() != None:
|
|
xbox_responding = True
|
|
break
|
|
#print time.time()-start_time
|
|
|
|
if xbox_responding:
|
|
if xbox.communicate()[1].startswith('xbconsoletype: error: cannot '
|
|
'connect to Xbox 360 development '
|
|
'kit'):
|
|
error_exit('Communications with Xbox failed, please check Xbox 360'
|
|
' Neighbourhood and confirm that the target Xbox is '
|
|
'present, and that it has been set as the \'default '
|
|
'xbox \'')
|
|
|
|
return xbox_responding
|
|
|
|
def wait_for_xbox():
|
|
#increased delay to 1s to hopefully prevent the 'xbox not responding'
|
|
#message from appearing too quickly.
|
|
delay = 1
|
|
while not is_xbox_ready(delay):
|
|
os.system('cls')
|
|
print box_text('Xbox not responding, please check power and network')
|
|
time.sleep(delay)
|
|
KillAllTaskName('xbconsoletype.exe',chatty=False)
|
|
#===============================================================================
|
|
|
|
|
|
|
|
#====Script Managment===========================================================
|
|
def compare_script_files(game_id, loop = True):
|
|
""" compares the scriptfiles described in the game_id and warns the user
|
|
if their script file doesn't match the one on the network."""
|
|
localfile = game_versions[game_id]['script_local']
|
|
remotefile = game_versions[game_id]['script_remote']
|
|
|
|
if not os.path.exists(localfile):
|
|
error_exit('Cannot find local scriptfile, please check game_versions '
|
|
'profile '+game_id+'\n\nSettings:\n'+localfile+'\n'+remotefile)
|
|
|
|
if not os.path.exists(remotefile):
|
|
logging('Cannot find remote scriptfile \''+remotefile+'\' for '
|
|
'comparison. GID: '+game_id)
|
|
do_files_match = False
|
|
remote_file_found = False
|
|
else:
|
|
do_files_match = filecmp.cmp(localfile, remotefile)
|
|
remote_file_found = True
|
|
|
|
|
|
return do_files_match
|
|
|
|
|
|
def check_script_warning(game_id):
|
|
""" Displays a warning if local scripts don't match remote scripts """
|
|
if 'script_remote' not in game_versions[game_id] or 'script_local' not in game_versions[game_id]:
|
|
return False
|
|
|
|
if compare_script_files(game_id) == True:
|
|
return True
|
|
else:
|
|
selection = menu_maker_2('Continue?', ['Yes','No'], 'WARNING: Local scriptfile does not match '
|
|
'reference scriptfile!\n\nIf you are not sure if you should continue, '
|
|
'please select \'No\' and check with someone who looks like they might '
|
|
'know.\n\nScript Checked:'
|
|
'\n'+os.path.basename(get_setting(game_id, 'script_remote','NO_EXIT')))
|
|
if selection == 0:
|
|
logging(game_id+'failed remote script check. User chose to ignore'
|
|
' the error and continue to load the game.')
|
|
return False
|
|
elif selection == 1:
|
|
error_exit('Script file does not match reference file.')
|
|
|
|
def script_manager(game_id):
|
|
"""replace active script with potential scripts from a list of them
|
|
supplied in game_versions.ini"""
|
|
active_script = get_setting(game_id,'active_script')
|
|
scripts = get_setting(game_id,'script_images')
|
|
script_location = os.path.dirname(active_script)
|
|
script_files = make_setting_dict(scripts)
|
|
|
|
menu_names = []
|
|
script_filenames = []
|
|
|
|
for clean, filename in script_files.iteritems():
|
|
if os.path.exists(os.path.join(script_location,filename)):
|
|
menu_names.append(clean)
|
|
script_filenames.append(os.path.join(script_location,filename))
|
|
|
|
if len(menu_names) == 0:
|
|
print '\n\nERROR: No non-active script files found in build. \n\nReturning to game page',
|
|
print_slow_dots(10)
|
|
return '_BACK'
|
|
|
|
#see if i can work out what the current active script is
|
|
matching_filepos = []
|
|
for position, filename in enumerate(script_filenames):
|
|
if filecmp.cmp(active_script, filename):
|
|
matching_filepos.append(position)
|
|
|
|
matching_scripts = ''
|
|
for position in matching_filepos:
|
|
matching_scripts += menu_names[position]
|
|
|
|
|
|
if matching_scripts != '':
|
|
comment = 'Current Active Script: '+matching_scripts
|
|
elif matching_scripts == '':
|
|
comment = 'Current Active Script: Unable to determine'
|
|
|
|
#comment += '\n\nPlease select a script image to use with '+get_setting(game_id,'name')+'.'
|
|
|
|
|
|
selection = menu_maker_2('Script Manager',menu_names,comment)
|
|
|
|
if selection == -1:
|
|
return '_BACK'
|
|
|
|
if os.path.exists(active_script):
|
|
os.system('attrib -r '+active_script)
|
|
os.remove(active_script)
|
|
|
|
shutil.copy2(script_filenames[selection],active_script)
|
|
|
|
logging('Script Manager: Copied "%s" to "%s"' %
|
|
(script_filenames[selection],active_script))
|
|
|
|
return menu_names[selection]
|
|
|
|
def make_setting_dict(settings):
|
|
""" converts a list containing 'filename: cleanname' items into a dictionary"""
|
|
setting_dict = {}
|
|
for full_setting in settings:
|
|
if not ':' in full_setting:
|
|
error_exit('Malformed setting supplied to make_setting_dict:\n\nFailed on line: '+full_setting)
|
|
else:
|
|
split_setting = full_setting.partition(':')
|
|
setting_dict[split_setting[0]] = split_setting[2].strip()
|
|
|
|
return setting_dict
|
|
|
|
#===============================================================================
|
|
|
|
#====Tasklist butchering========================================================
|
|
def get_tasklist():
|
|
error_exit('get_tasklist deprecated, use GetTasklist() instead')
|
|
|
|
|
|
def GetTasklist():
|
|
""" 'tasklist /v /fo csv' is a much better way of doing this! """
|
|
tasklist = subprocess.Popen('tasklist.exe /v /fo csv',
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE,
|
|
universal_newlines = True)
|
|
tasks_output = tasklist.communicate()[0].strip('\n').split('"\n')
|
|
|
|
|
|
# titles from headers
|
|
headers = [head.strip('"') for head in tasks_output[0].split(',')]
|
|
|
|
tasks = []
|
|
|
|
for task in tasks_output[1:]:
|
|
one_task = [item.strip('"') for item in task.split('",')]
|
|
|
|
one_task_dict = {}
|
|
for key, item in enumerate(one_task):
|
|
if len(one_task) != 9:
|
|
|
|
error_exit('GetTasklist adanoned ship on task because it '
|
|
'did not have 9 entries: %s' % str(one_task))
|
|
|
|
one_task_dict[headers[key]] = item
|
|
|
|
|
|
tasks.append(one_task_dict)
|
|
#print len(tasks),tasks[len(tasks)-1]['Image Name']
|
|
|
|
return tasks
|
|
|
|
|
|
def FindTask(proc_name):
|
|
valid_tasks = []
|
|
for task in GetTasklist():
|
|
if task['Image Name'].lower() == proc_name.lower():
|
|
valid_tasks.append(task)
|
|
|
|
return valid_tasks
|
|
|
|
def FindPID(pid):
|
|
if not IsPIDRunning(pid):
|
|
return False
|
|
|
|
valid_tasks = []
|
|
for task in GetTasklist():
|
|
if task['PID'] == pid:
|
|
return task
|
|
|
|
#failsafe, if the pid dissapears inbetween checking for it and getting list
|
|
return False
|
|
|
|
|
|
|
|
def KillAllTaskName(proc_name,chatty=True):
|
|
for task in FindTask(proc_name):
|
|
logging('Killing Task: "%s", PID: %s' %(task['Image Name'],
|
|
task['PID']))
|
|
kill_task(task['PID'],chatty=chatty)
|
|
|
|
def ProcessRunning(proc_name):
|
|
if len(FindTask(proc_name)) > 0:
|
|
return True
|
|
|
|
|
|
|
|
def format_tasklist(line):
|
|
error_exit('format_tasklist deprecated, use GetTaskList() instead')
|
|
""" turn a string output by tasklist into a formatted list"""
|
|
#This works in XP, gotta find out if it works in other windows
|
|
#versions.
|
|
|
|
if len(line) < 26:
|
|
return []
|
|
|
|
tasklist = [None]*6
|
|
|
|
#process name
|
|
tasklist[0] = line[0:25].strip()
|
|
|
|
#pid
|
|
tasklist[1] = line[26:32].strip()
|
|
if tasklist[1].isdigit(): tasklist[1] = int(tasklist[1])
|
|
|
|
#memory usage
|
|
tasklist[2] = line[59:69].strip().replace(',','')
|
|
if tasklist[2].isdigit(): tasklist[2] = int(tasklist[2])
|
|
|
|
#status running/not
|
|
tasklist[3] = line[72:87].strip()
|
|
|
|
#cpu time in seconds
|
|
time_strings = line[139:151].strip().split(':')
|
|
time_ints = []
|
|
for string in time_strings:
|
|
time_ints.append(int(string))
|
|
tasklist[4] = time_ints[2]+(time_ints[1]*60)+(time_ints[0]*60*60)
|
|
|
|
#window title
|
|
tasklist[5] = line[152:].strip()
|
|
|
|
|
|
return tasklist
|
|
|
|
|
|
def is_process_running(processname):
|
|
error_exit('is_process_running deprecated, use ProcessRunning() instead')
|
|
matches = 0
|
|
for task in get_tasklist():
|
|
if task[0].lower() == processname.lower():
|
|
matches += 1
|
|
|
|
if matches > 0:
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
def kill_watson():
|
|
error_exit('kill_watson deprecated, use KillAllTaskName instead')
|
|
running_tasks = get_tasklist()
|
|
|
|
watson_pids = []
|
|
for task in running_tasks:
|
|
if task[0] == 'xbWatson.exe':
|
|
watson_pids.append(task[1])
|
|
|
|
for pid in watson_pids:
|
|
logging('Attempting to kill watson.exe with PID: '+str(pid))
|
|
kill_task(pid)
|
|
|
|
def KillRag():
|
|
""" Special case for killing rag, to prevent people losing settings """
|
|
rag_proc = FindTask('rag.exe')
|
|
if len(rag_proc) == 0:
|
|
return True
|
|
|
|
#we should only ever have 1 rag running, so it's easier to do this
|
|
rag = rag_proc[0]
|
|
|
|
#If rag is only in the systray, we can force kill without problem
|
|
if rag['Window Title'] == 'N/A':
|
|
kill_task(rag['PID'])
|
|
return True
|
|
|
|
#however, if rag has a window title, we need a clean kill
|
|
kill_task(rag['PID'],force=False)
|
|
message = 'RAG is refusing to close, it may be asking you to save changes'
|
|
|
|
while IsPIDRunning(rag['PID']):
|
|
pid = FindPID(rag['PID'])
|
|
if pid != False:
|
|
if pid['Window Title'] == 'N/A':
|
|
break
|
|
else:
|
|
break
|
|
os.system('cls')
|
|
print box_text(message)
|
|
time.sleep(1)
|
|
|
|
#keep going until we have no more rags. Shouldn't loop more than twice!
|
|
if len(FindTask('rag.exe')) > 0:
|
|
KillRag()
|
|
|
|
|
|
|
|
def IsPIDRunning(pid):
|
|
for task in GetTasklist():
|
|
if task['PID'] == pid:
|
|
return True
|
|
|
|
return False
|
|
|
|
def kill_task(pid,force=True, chatty=True):
|
|
"""kills a task by process ID. uses TASKKILL.
|
|
Tested on: XP"""
|
|
logging('KILL_TASK: Attempting to gently kill task with PID: '+str(pid))
|
|
if chatty: print 'Politely asking %s to close...' % pid,
|
|
task_killer = subprocess.Popen('taskkill /PID '+str(pid),
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE)
|
|
|
|
if IsPIDRunning(pid):
|
|
time.sleep(0.8)
|
|
|
|
if IsPIDRunning(pid):
|
|
if force:
|
|
if chatty: print '%s has ignored request, forcing kill.' % pid
|
|
else:
|
|
if chatty: print '%s has ignored request.' % pid
|
|
return False
|
|
else:
|
|
if chatty: print '%s has closed politely.' % pid
|
|
|
|
while IsPIDRunning(pid):
|
|
task_killer = subprocess.Popen('taskkill /F /PID '+str(pid),
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE)
|
|
|
|
def kill_taskname(taskname):
|
|
""" Kills all tasks that match the process name. CAREFUL NOW """
|
|
error_exit('kill_taskname deprecated, use KillAllTaskName instead')
|
|
current_tasks = get_tasklist()
|
|
|
|
pids = []
|
|
for task in current_tasks:
|
|
if task[0].lower() == taskname.lower():
|
|
pids.append(task[1])
|
|
|
|
if len(pids) == 0:
|
|
logging('KILL_TASKNAME: No instances of '+taskname+' found.')
|
|
return False
|
|
|
|
for pid in pids:
|
|
logging('KILL_TASKNAME: Attempting to '
|
|
'kill '+taskname+' with PID: '+str(pid))
|
|
kill_task(pid)
|
|
|
|
|
|
#====Syncit!====================================================================
|
|
def syncit(game_id):
|
|
""" Run Synchronize It! using a project name located via game_id. Location
|
|
of synchronize it! is in settings.ini """
|
|
#project = 'update_episode1_jap'
|
|
project = get_setting(game_id,'sync')
|
|
path = get_setting('defaults','syncit')
|
|
proj_arg = '/P='+project
|
|
logfile = 'x:\\sync.html'
|
|
|
|
#kill any instances of rag and systray to prevent synchit failing
|
|
#KillAllTaskName('rag.exe')
|
|
KillRag()
|
|
KillAllTaskName('systrayrfs.exe')
|
|
|
|
|
|
#attempt to erase the logfile before creating a new one:
|
|
if os.path.exists(logfile):
|
|
os.remove(logfile)
|
|
|
|
sync=subprocess.Popen([path,proj_arg,'/auto','/show-','/log0',logfile])
|
|
|
|
#print sync.communicate()[0]
|
|
|
|
spinny_thing = ['|','/','-','\\']
|
|
spinny_progress = 0
|
|
|
|
while sync.poll() == None:
|
|
os.system('cls')
|
|
print 'Synchronize It! is updating '+project+', please wait'+('.'*spinny_progress)
|
|
if spinny_progress > len(spinny_thing)-1:
|
|
spinny_progress = 0
|
|
print spinny_thing[spinny_progress]
|
|
spinny_progress += 1
|
|
time.sleep(0.2)
|
|
|
|
sync_message = process_sync_log(logfile)
|
|
logging('SyncIt: '+project+': '+sync_message)
|
|
print
|
|
print 'Last message from Synchronize It! says:'
|
|
print box_text(sync_message)
|
|
print
|
|
print 'Press any key to continue'
|
|
wait_for_key()
|
|
print '\nOne moment please...'
|
|
|
|
|
|
|
|
def process_sync_log(filename='c:\\wndsync-log.html'):
|
|
""" Read in the Synchronize It! logfile and return the summary of the last
|
|
operation performed with the /log0 command line used """
|
|
if not os.path.exists(filename):
|
|
return 'Unable to read log'
|
|
|
|
logfile = open(filename,'r')
|
|
|
|
storing = False
|
|
section = []
|
|
for line in logfile:
|
|
if '<!-- $LOGCONTENT$ -->' in line:
|
|
storing = True
|
|
if storing:
|
|
section.append(line)
|
|
if '<!-- $LOGENTRY$ -->' in line:
|
|
break
|
|
|
|
if len(section) < 5:
|
|
return 'Unable to read log'
|
|
|
|
message_start = section[4].find('<td>')+4
|
|
message_end = section[4].find('</tr>')-5
|
|
message = section[4][message_start:message_end].strip().strip(':')
|
|
|
|
return message
|
|
|
|
|
|
#====Japery=====================================================================
|
|
def windows_username():
|
|
"""retrieve and format the windows username for japes. Return as a tuple
|
|
with [0] First [1] Last"""
|
|
name = os.environ.get('username')
|
|
if '.' not in name:
|
|
return [name.capitalize(),'Smith']
|
|
|
|
namesplit = name.partition('.')
|
|
|
|
firstname = namesplit[0].capitalize()
|
|
lastname = namesplit[2].capitalize()
|
|
|
|
if name.lower()=='rory.jepson':
|
|
firstname = 'Rory. You smell of wee'
|
|
|
|
elif name.lower() =='neil.corbett':
|
|
os.system('color 5d')
|
|
firstname = 'Lob Erect Tin'
|
|
|
|
elif name.lower() == 'scott.butchard':
|
|
firstname = 'Bat Crotch Dust'
|
|
|
|
elif name.lower() == 'tarek.hamad':
|
|
firstname = 'Death Karma'
|
|
|
|
elif name.lower() == 'andrew.scotland':
|
|
firstname = 'Cat\'s Wonderland'
|
|
|
|
elif name.lower() == 'brian.kelly':
|
|
firstname = 'Nearby Kill'
|
|
os.system('color 6e')
|
|
|
|
|
|
return [firstname, lastname]
|
|
|
|
def time_of_day():
|
|
""" Work out whether it is morning afternoon or night, return a string
|
|
with an appropriate word in it"""
|
|
current_hour = int(time.strftime('%H'))
|
|
#print current_hour
|
|
|
|
if current_hour >= 5 and current_hour <= 11:
|
|
time_name = 'morning'
|
|
elif current_hour >= 12 and current_hour <= 17:
|
|
time_name = 'afternoon'
|
|
elif current_hour >= 18 and current_hour <= 21:
|
|
time_name = 'evening'
|
|
elif current_hour >= 22 or current_hour <= 4:
|
|
time_name = 'night'
|
|
|
|
return time_name
|
|
|
|
def silly_intro():
|
|
""" this isn't a waste of time at all """
|
|
intro = ['BBC Computer 32k','Acorn 1770 DFS','BASIC']
|
|
command = '> LOAD "QA_MENU"\n\n'
|
|
|
|
for line in intro:
|
|
for char in line:
|
|
print char,
|
|
print
|
|
print
|
|
random_wait(0.2)
|
|
|
|
for char in command:
|
|
print char,
|
|
random_wait(0.1)
|
|
|
|
print
|
|
|
|
|
|
|
|
def random_wait(max_wait=1):
|
|
""" wait for a random time between 0s and max_wait) """
|
|
time.sleep(random.uniform(0,max_wait))
|
|
|
|
|
|
def OpenWikiPage(game_id):
|
|
wikiurl = get_setting('defaults','wikiurl')
|
|
wikipage = get_setting(game_id,'wikipage')
|
|
webbrowser.open(wikiurl+wikipage)
|
|
logging(('Opening wiki page for %s: %s' % (game_id, wikiurl+wikipage)))
|
|
|
|
def ARSEstar():
|
|
sequence = ['A.',
|
|
'R.',
|
|
'S.',
|
|
'E.',
|
|
'star\n',
|
|
'!delay=1',
|
|
'!cls',
|
|
'Advanced\nR.\nS.\nE.\nstar',
|
|
'!cls',
|
|
'Advanced\nRockstar\nS.\nE.\nstar',
|
|
'!cls',
|
|
'Advanced\nRockstar\nSelection\nE.\nstar',
|
|
'!cls',
|
|
'Advanced\nRockstar\nSelection\nEngine\nstar',]
|
|
|
|
os.system('cls')
|
|
for element in sequence:
|
|
if element == '!delay=1':
|
|
time.sleep(1)
|
|
elif element == '!cls':
|
|
os.system('cls')
|
|
else:
|
|
print element
|
|
time.sleep(0.5)
|
|
|
|
time.sleep(1)
|
|
|
|
def RandomIntro():
|
|
if random.randint(1,20) == random.randint(1,100):
|
|
selection = random.randint(1,2)
|
|
if selection is 1:
|
|
silly_intro()
|
|
elif selection is 2:
|
|
ARSEstar()
|
|
|
|
#======experiments=========================================================================
|
|
def move_something(something):
|
|
original_something = something
|
|
global object_size
|
|
object_size = len(something)
|
|
thing_x = 5
|
|
thing_y = 5
|
|
ms_screenupdate(something,thing_x,thing_y)
|
|
while True:
|
|
key = wait_for_key()
|
|
if key == 'CURSOR_LEFT': thing_y -= 1
|
|
elif key == 'CURSOR_RIGHT': thing_y += 1
|
|
elif key == 'CURSOR_UP': thing_x -= 1
|
|
elif key == 'CURSOR_DOWN': thing_x += 1
|
|
elif key == 'HOME':
|
|
thing_x =11
|
|
thing_y = 35
|
|
something = original_something
|
|
object_size = len(something)
|
|
elif key == 'INSERT':
|
|
something = 'tarek is a boner'
|
|
object_size = len(something)
|
|
elif key == 'PAGE_DOWN':
|
|
something = 'tarek is a stunted boner'
|
|
object_size = len(something)
|
|
else:
|
|
if something == original_something:
|
|
something = key
|
|
else:
|
|
something += key
|
|
object_size = len(something)
|
|
thing_x,thing_y = ms_force_constraints(thing_x,thing_y)
|
|
ms_screenupdate(something,thing_x,thing_y)
|
|
|
|
def ms_force_constraints(x,y):
|
|
if x > 23: x=23
|
|
elif x < 1: x=1
|
|
|
|
if y > 79-object_size: y=79-object_size+1
|
|
if y < 0: y=0
|
|
|
|
if x == 23 and y == 79-object_size+1: y=78-object_size+1
|
|
return x,y
|
|
|
|
|
|
def ms_screenupdate(thing,pos_x,pos_y):
|
|
os.system('cls')
|
|
debugstring = 'x: '+str(pos_x)+' y: '+str(pos_y)
|
|
string = debugstring+('\n'*pos_x)+(' '*pos_y)+thing
|
|
print string
|
|
|
|
|
|
|
|
def fun_with_stats(filename='thing.pstats'):
|
|
statsfile = open(filename,'r')
|
|
outfile = open('outfile.pstats','w')
|
|
|
|
whole_thing = ''
|
|
for line in statsfile:
|
|
whole_thing += line
|
|
|
|
all_items = split_pstats(whole_thing)
|
|
|
|
vehicles_used = pstats_vehicles_used(all_items,'VEHICLE_ENTERED')
|
|
vehicles_left = pstats_vehicles_used(all_items,'VEHICLE_LEFT')
|
|
|
|
print '------------------------------------------'
|
|
print '| Vehicle | Times in | Times out |'
|
|
print '|==============|============|============|'
|
|
for vehicle in sorted(vehicles_used.iterkeys()):
|
|
times_used = vehicles_used[vehicle]
|
|
if vehicle in vehicles_left:
|
|
times_left = vehicles_left[vehicle]
|
|
else:
|
|
times_left = 0
|
|
times_left_spaces = ' '*(12-len(str(times_left)))
|
|
vehicle_spaces = ' '*(14-len(vehicle))
|
|
times_used_spaces = ' '*(12-len(str(times_used)))
|
|
print '|' + vehicle + vehicle_spaces + '|' + str(times_used) + times_used_spaces + '|' + str(times_left) + times_left_spaces + '|'
|
|
print '------------------------------------------'
|
|
|
|
|
|
|
|
## for one_object in all_items:
|
|
## for key, value in one_object.iteritems():
|
|
## print key,'=',value
|
|
## print '---------------'
|
|
|
|
#for item in all_items:
|
|
# outfile.write(item+'\n')
|
|
|
|
#outfile.close()
|
|
|
|
def pstats_prettify(filename='thing.pstats'):
|
|
in_file = open(filename,'r')
|
|
#out_file = open('pretty.pstats','w')
|
|
out_name = os.path.join(os.path.dirname(filename),
|
|
'pretty_'+os.path.basename(filename))
|
|
out_file = open(out_name,'w')
|
|
|
|
stats_data = ''
|
|
for line in in_file:
|
|
stats_data += line
|
|
|
|
pstats = split_pstats(stats_data)
|
|
|
|
for event in pstats:
|
|
for statname, stat in event.iteritems():
|
|
out_file.write(str(statname)+' = '+str(stat)+'\n')
|
|
out_file.write('-----------------\n')
|
|
|
|
out_file.close()
|
|
|
|
|
|
def pstats_vehicles_used(pstats,psid):
|
|
start_time=time.time()
|
|
vehicles = {}
|
|
for event in pstats:
|
|
if 'psid' in event:
|
|
if event['psid'] == psid:
|
|
if event['name'] not in vehicles:
|
|
vehicles[event['name']] = 1
|
|
else:
|
|
vehicles[event['name']] += 1
|
|
|
|
end_time = time.time()
|
|
print 'vehicle processing completed in',
|
|
print end_time-start_time
|
|
|
|
return vehicles
|
|
|
|
def split_pstats(line):
|
|
start = time.clock()
|
|
one_id = re.compile('[gp]sid=.*?(?=$|[gp]sid)')
|
|
split_id = re.compile('[^|,].*?=.*?(?=$|,)')
|
|
|
|
final = []
|
|
|
|
for sid in one_id.findall(line):
|
|
full_split = {}
|
|
for data_split in split_id.findall(sid):
|
|
one_split = data_split.split('=')
|
|
full_split[one_split[0]] = one_split[1]
|
|
|
|
final.append(full_split)
|
|
|
|
end = time.clock()
|
|
print 'pstats processing completed in %ss' % (end-start)
|
|
|
|
return final
|
|
|
|
|
|
|
|
|
|
|
|
##old way, without using regex
|
|
##def split_pstats(line):
|
|
## all_split = []
|
|
## one_thing = ''
|
|
## total = len(line)
|
|
## starttime = time.time()
|
|
## for pos, char in enumerate(line):
|
|
## one_thing += char
|
|
## #print pos,'out of',total
|
|
## if pos == total-1:
|
|
## #print 'end hit'
|
|
## break
|
|
## if line[pos+1] == 'g' or line[pos+1] == 'p':
|
|
## if line[pos+2:pos+5] == 'sid':
|
|
## all_split.append(one_thing)
|
|
## one_thing = ''
|
|
##
|
|
## all_split.append(one_thing)
|
|
##
|
|
## pstats = []
|
|
## for one_object in all_split:
|
|
## one_object_split = one_object.split(',')
|
|
## one_object_dict = {}
|
|
## for key_value in one_object_split:
|
|
## key_value_split = key_value.split('=')
|
|
## one_object_dict[key_value_split[0]] = key_value_split[1].strip('"')
|
|
## pstats.append(one_object_dict)
|
|
##
|
|
##
|
|
##
|
|
## endtime = time.time()
|
|
## total_time = endtime-starttime
|
|
## print 'pstats processing completed in',
|
|
## print total_time
|
|
## return pstats
|
|
|
|
def pstats_track_path(filename='thing.pstats'):
|
|
stats_file = open(filename,'r')
|
|
stats_data_line = ''
|
|
for line in stats_file:
|
|
stats_data_line += line
|
|
|
|
pstats = split_pstats(stats_data_line)
|
|
|
|
player_path = []
|
|
for single_event in pstats:
|
|
if 'psid' in single_event:
|
|
#if single_event['psid'] == 'COORDS':
|
|
if single_event['psid'] == 'SPAWN':
|
|
player_path.append(['',''])
|
|
if 'x' in single_event and 'y' in single_event:
|
|
player_path.append([single_event['x'],single_event['y']])
|
|
|
|
path_file = open('path.csv','w')
|
|
|
|
for key,position in enumerate(player_path):
|
|
path_file.write(position[0]+', '+position[1]+'\n')
|
|
|
|
path_file.close()
|
|
|
|
|
|
def distance_between(x1,y1,x2,y2):
|
|
import math
|
|
|
|
final_dist = math.sqrt(pow(x2-x1,2)+pow(y2-y1,2))
|
|
|
|
return final_dist
|
|
|
|
##this is a terrible way of doing it, leave it here to remind you of AWFULNESS
|
|
##def split_oddly(line):
|
|
## sid_pos = 0
|
|
## end_pos = 5
|
|
## lines = []
|
|
## tempfile = open('temp.pstats','w')
|
|
## time1 = time.time()
|
|
##
|
|
## while sid_pos != -1:
|
|
## sid_pos = line.find('sid=',end_pos)
|
|
## #print sid_pos
|
|
##
|
|
## if line[sid_pos-2] != ',':
|
|
## new_line = '\n'+line[end_pos-5:line.find('sid=',sid_pos)-1]
|
|
## tempfile.write(new_line)
|
|
##
|
|
## end_pos = sid_pos+4
|
|
## print end_pos, len(line)
|
|
## print str(int((float(end_pos) / (len(line)))*100))+'%'
|
|
## #print percentage,'%'
|
|
## time2 = time.time()
|
|
## print 'time diff='+str(time2-time1)
|
|
## tempfile.close()
|
|
## return line
|
|
|
|
#===============================================================================
|
|
def sp_or_mp(game_id):
|
|
"""use 'logdestination' to work out if the user was playing multiplayer or
|
|
singleplayer, return false if it can't work it out"""
|
|
if 'singleplayer' in get_setting(game_id,'logdestination').lower():
|
|
gametype = 'singleplayer'
|
|
elif 'multiplayer' in get_setting(game_id,'logdestination').lower():
|
|
gametype = 'multiplayer'
|
|
else:
|
|
gametype = False
|
|
|
|
return gametype
|
|
|
|
|
|
|
|
def client_or_host(game_id):
|
|
"""use console logs to work out if the player was a client or host,
|
|
return false if it can't work it out."""
|
|
games_hosted = 0
|
|
games_joined = 0
|
|
host_text = 'Hosting STANDARD session'
|
|
client_text = 'Setting session id (from host)'
|
|
|
|
#print valid_console_logs(game_id)
|
|
for filename in valid_console_logs(game_id):
|
|
#print '[%s]' % filename
|
|
console_log = open(filename)
|
|
for line in console_log:
|
|
if host_text in line:
|
|
games_hosted += 1
|
|
#print line
|
|
elif client_text in line:
|
|
games_joined += 1
|
|
#print line
|
|
|
|
#if it looks like a game has been hosted AND joined, return false
|
|
if games_hosted > 0 and games_joined > 0:
|
|
playertype = False
|
|
|
|
#if it looks like a game has neither been hosted nor joined, return false
|
|
elif games_hosted == 0 and games_joined == 0:
|
|
playertype = False
|
|
|
|
#looks like a host
|
|
elif games_hosted > 0 and games_joined == 0:
|
|
playertype = 'host'
|
|
|
|
#looks like a client
|
|
elif games_hosted == 0 and games_joined > 0:
|
|
playertype = 'client'
|
|
|
|
return playertype
|
|
|
|
|
|
|
|
|
|
def upload_logs2(game_id):
|
|
"""stuff"""
|
|
gametype = sp_or_mp(game_id)
|
|
|
|
while True:
|
|
playertype = client_or_host
|
|
|
|
|
|
|
|
|
|
|
|
def valid_console_logs(game_id):
|
|
"""return the filenames of all valid console logs in the logging directories
|
|
described for the game_id in game_versions"""
|
|
|
|
files_with_console = []
|
|
for location in get_setting(game_id,'logsource'):
|
|
files_with_console += directory_list(location,'console')
|
|
#print files_with_console
|
|
|
|
if len(files_with_console) > 0:
|
|
files_with_console_and_log = []
|
|
for filename in files_with_console:
|
|
if filename.endswith('log'):
|
|
files_with_console_and_log.append(filename)
|
|
elif len(files_with_console) < 1:
|
|
files_with_console_and_log = []
|
|
|
|
return files_with_console_and_log
|
|
|
|
|
|
|
|
#====crap=======================================================================
|
|
|
|
def temp_mp_backup(multiplayer):
|
|
pass
|
|
|
|
##def copy_files(sources, destinations):
|
|
## """ copy files from sources to destinations """
|
|
##
|
|
## #if the number of sources don't match the number of destinations, abandon ship
|
|
## if len(sources) != len(destinations):
|
|
## error_exit('The number of sources supplied to copy_files() did not match the number of destinations')
|
|
##
|
|
## blocksize = 1024 #1k block size, copy a kilobyte at a time then refresh screen
|
|
##
|
|
## total_files = len(sources)
|
|
##
|
|
## overall_percentage = 0
|
|
## file_percentage = 0
|
|
##
|
|
## #get overall filesize
|
|
## total_size = 0
|
|
## for filename in sources:
|
|
## if not os.path.exists(filename):
|
|
## error_exit('Trying to copy a file that doesn\'t exist: "'+filename+'"')
|
|
## total_size += os.stat(filename).st_size
|
|
##
|
|
## for index, filename in enumerate(sources):
|
|
## if os.path.exists(destinations[index]):
|
|
## error_exit('Trying to copy a file to a location that already exists: "'+destinations[index]+'"')
|
|
##
|
|
## if not os.path.exists(os.path.dirname(destinations[index])):
|
|
## error_exit('Trying to copy a file to a folder that does not exist: "'+destinations[index]+'"')
|
|
##
|
|
## if not os.access(filename,os.R_OK):
|
|
## error_exit('"'+filename+'"'+' could not be read.')
|
|
##
|
|
## in_file = open(filename,'r')
|
|
## out_file = open(destinations[index],'a')
|
|
##
|
|
## total_bytes
|
|
|
|
|
|
|
|
|
|
|
|
def print_progress_bar(title, current_source, current_destination, file_percentage, overall_percentage):
|
|
""" display a progress bar with all those STATS """
|
|
os.system('cls')
|
|
|
|
if file_percentage > 100 or overall_percentage > 100:
|
|
error_exit('print_progress_bar(): percentages should not be greater than 100')
|
|
|
|
surround = '-'
|
|
end = '|'
|
|
block = '*'
|
|
spacer = ' '
|
|
|
|
bar_length = 50
|
|
|
|
file_blocks = int(round(bar_length*file_percentage/100))
|
|
|
|
print surround*(bar_length+2)
|
|
print end+block*file_blocks+spacer*(bar_length-file_blocks)+end
|
|
print surround*(bar_length+2)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#====WConio experiments=========================================================
|
|
def printc(text,xy=[-1,-1],max_xy=[79,24]):
|
|
#print xy
|
|
if xy == [-1,-1]:
|
|
xy = [WConio.wherex(),WConio.wherey()]
|
|
|
|
start_x = xy[0]
|
|
|
|
text = word_wrap(text, max_xy[0]-start_x)
|
|
|
|
display = True
|
|
for index, char in enumerate(text):
|
|
#See if the character is special text character (~)
|
|
if text[index-1] == '|' and not display:
|
|
display = True
|
|
if char == '~':
|
|
fore_or_back = ''
|
|
display = False
|
|
|
|
#look at the next character to see if it's F or B
|
|
if text[index+1].lower() == 'f':
|
|
fore_or_back = 'f'
|
|
display = False
|
|
elif text[index+1].lower() == 'b':
|
|
fore_or_back = 'b'
|
|
display = False
|
|
|
|
#if it's probably a colour code, get the colour from it
|
|
if fore_or_back:
|
|
colour_char = ''
|
|
colour_word = ''
|
|
colour_index = 2
|
|
|
|
while not text[index+colour_index] == '|':
|
|
colour_char = text[index+colour_index]
|
|
colour_word += colour_char
|
|
colour_index += 1
|
|
|
|
if fore_or_back == 'f':
|
|
if colour_word:
|
|
text_colour(colour_word)
|
|
else:
|
|
text_colour()
|
|
fore_or_back = ''
|
|
|
|
elif fore_or_back == 'b':
|
|
if colour_word:
|
|
bg_colour(colour_word)
|
|
else:
|
|
bg_colour()
|
|
fore_or_back = ''
|
|
|
|
if display:
|
|
if char == '\n':
|
|
xy[1] += 1
|
|
xy[0] = start_x
|
|
else:
|
|
gotoxy(xy)
|
|
WConio.putch(char)
|
|
if xy[0]+1 > max_xy[0]:
|
|
xy[1] += 1
|
|
if xy[1] > max_xy[1]:
|
|
return
|
|
xy[0] = start_x
|
|
else:
|
|
xy[0] += 1
|
|
|
|
|
|
def gotoxy(xy,max_xy=[79,300],min_xy=[0,0]):
|
|
""" Use WConio to position the cursor in x y, don't go out of constraints """
|
|
if xy[0] > max_xy[0] or xy[0] < min_xy[0]:
|
|
WConio.gotoxy(0,0)
|
|
text_colour('Black')
|
|
bg_colour('red')
|
|
print 'ERROR: Text cursor placed outside of constraints', xy
|
|
text_colour()
|
|
bg_colour()
|
|
xy = [0,1]
|
|
|
|
|
|
if xy[1] > max_xy[1] or xy[1] < min_xy[1]:
|
|
WConio.gotoxy(0,0)
|
|
text_colour('Black')
|
|
bg_colour('red')
|
|
print 'ERROR: Text cursor placed outside of constraints', xy
|
|
text_colour()
|
|
bg_colour()
|
|
xy = [0,1]
|
|
|
|
WConio.gotoxy(xy[0],xy[1])
|
|
|
|
def text_colour(colour='green'):
|
|
WConio.textcolor(colours(colour))
|
|
|
|
def bg_colour(colour='black'):
|
|
WConio.textbackground(colours(colour))
|
|
|
|
def colours(colour):
|
|
""" Convert numbers or words into WConio compatible colours """
|
|
if str(colour).isdigit():
|
|
if int(colour) >= 0 and int(colour) <= 15:
|
|
final_colour = int(colour)
|
|
else:
|
|
final_colour = WConio.LIGHTGRAY
|
|
else:
|
|
if colour.lower() == 'black': final_colour = WConio.BLACK
|
|
elif colour.lower() == 'blue': final_colour = WConio.LIGHTBLUE
|
|
elif colour.lower() == 'green': final_colour = WConio.LIGHTGREEN
|
|
elif colour.lower() == 'red': final_colour = WConio.LIGHTRED
|
|
elif colour.lower() == 'cyan': final_colour = WConio.LIGHTCYAN
|
|
elif colour.lower() == 'magenta': final_colour = WConio.LIGHTMAGENTA
|
|
elif colour.lower() == 'dblue': final_colour = WConio.BLUE
|
|
elif colour.lower() == 'dgreen': final_colour = WConio.GREEN
|
|
elif colour.lower() == 'dcyan': final_colour = WConio.CYAN
|
|
elif colour.lower() == 'dred': final_colour = WConio.RED
|
|
elif colour.lower() == 'dmagenta': final_colour = WConio.MAGENTA
|
|
elif colour.lower() == 'brown': final_colour = WConio.BROWN
|
|
elif colour.lower() == 'grey': final_colour = WConio.LIGHTGREY
|
|
elif colour.lower() == 'dgrey': final_colour = WConio.DARKGREY
|
|
elif colour.lower() == 'yellow': final_colour = WConio.YELLOW
|
|
elif colour.lower() == 'white': final_colour = WConio.WHITE
|
|
else: final_colour = WConio.LIGHTGREY
|
|
|
|
return final_colour
|
|
|
|
|
|
def cls(home=False):
|
|
cursor_position = [WConio.wherex(),WConio.wherey()]
|
|
WConio.clrscr()
|
|
if not home:
|
|
gotoxy(cursor_position)
|
|
|
|
def invert_colours():
|
|
colours = current_colours()
|
|
text_colour(colours[1])
|
|
bg_colour(colours[0])
|
|
|
|
def current_colours():
|
|
#I should probably learn how to 'do' bitwise stuff.
|
|
colours = WConio.gettextinfo()[4]
|
|
|
|
bg = colours >> 4
|
|
fg = colours - (bg << 4)
|
|
|
|
return [fg,bg]
|
|
|
|
#===============================================================================
|
|
|
|
|
|
#old rdr commandlines backup - new one should only modify the ones we need to,
|
|
#gather the rest from commandlines.txt
|
|
##def rdr_commandlines(game_id,path='x:\\rdr2\\rdr_cmd.txt'):
|
|
## """ create a text file containing all the commandlines found in an rdr
|
|
## game_id """
|
|
##
|
|
## if path[-4:].lower() != '.txt':
|
|
## error_exit('Panicky Pete Alert: The path supplied to rdr_commandlines '
|
|
## 'is not a *.txt file: '+path)
|
|
##
|
|
## if os.path.exists(path) and not os.access(path,os.W_OK):
|
|
## error_exit('WARNING: Unable to write to '+path+'\n\nThis could mean '
|
|
## 'you don\'t have admin permission on this PC, or that the '
|
|
## 'file is set to \'read only\'')
|
|
##
|
|
## switches = get_setting(game_id,'switches')
|
|
##
|
|
## cmdfile = open(path,'w')
|
|
##
|
|
## for switch in switches:
|
|
## cmdfile.write(switch+' ')
|
|
##
|
|
## cmdfile.close()
|
|
##
|
|
## print len(switches),'command line switches written to',path
|
|
|
|
|
|
def rdr_commandlines(game_id,
|
|
path,
|
|
commandline):
|
|
|
|
""" create a text file containing all the commandlines found in an rdr
|
|
game_id """
|
|
|
|
if path[-4:].lower() != '.txt':
|
|
error_exit('Panicky Pete Alert: The path supplied to rdr_commandlines '
|
|
'is not a *.txt file: '+path)
|
|
|
|
if os.path.exists(path) and not os.access(path,os.W_OK):
|
|
error_exit('WARNING: Unable to write to '+path+'\n\nThis could mean '
|
|
'you don\'t have admin permission on this PC, or that the '
|
|
'file is set to \'read only\'')
|
|
|
|
if not os.path.exists(commandline):
|
|
error_exit('WARNING: Unable to find RDR commandlines at '+commandline)
|
|
|
|
custom_switches = get_setting(game_id,'switches')
|
|
|
|
cmdline = open(commandline,'r')
|
|
switches = []
|
|
for line in cmdline:
|
|
switches.append(line)
|
|
cmdline.close()
|
|
|
|
|
|
#check for -megarpf, remove -path from switches+custom_switches
|
|
remove_path = False
|
|
for switch in switches:
|
|
if switch.lower().startswith('-megarpfs'):
|
|
remove_path = True
|
|
|
|
for switch in custom_switches:
|
|
if switch.lower().startswith('-megarpfs'):
|
|
remove_path = True
|
|
|
|
if remove_path:
|
|
print 'rdr_commandline: -megarpfs found, removing -path'
|
|
new_switches = []
|
|
for switch in switches:
|
|
if not switch.lower().startswith('-path'):
|
|
new_switches.append(switch)
|
|
switches = new_switches
|
|
|
|
new_custom_switches = []
|
|
for switch in custom_switches:
|
|
if not switch.lower().startswith('-path'):
|
|
new_custom_switches.append(switch)
|
|
custom_switches = new_custom_switches
|
|
|
|
#check for -rag, remove -localconsole
|
|
remove_lconsole = False
|
|
for switch in switches:
|
|
if switch.lower().startswith('-rag'):
|
|
remove_lconsole = True
|
|
|
|
for switch in custom_switches:
|
|
if switch.lower().startswith('-rag'):
|
|
remove_lconsole = True
|
|
|
|
if remove_lconsole:
|
|
print 'rdr_commandline: -rag found, removing -localconsole'
|
|
new_switches = []
|
|
for switch in switches:
|
|
if not switch.lower().startswith('-localconsole'):
|
|
new_switches.append(switch)
|
|
switches = new_switches
|
|
|
|
new_custom_switches = []
|
|
for switch in custom_switches:
|
|
if not switch.lower().startswith('-localconsole'):
|
|
new_custom_switches.append(switch)
|
|
custom_switches = new_custom_switches
|
|
|
|
|
|
#new switch or modified switch?
|
|
switches_applied = []
|
|
for key, switch in enumerate(switches):
|
|
for custom_key, custom_switch in enumerate(custom_switches):
|
|
if switch.startswith(custom_switch.split('=')[0]):
|
|
switches[key] = custom_switch+'\n'
|
|
switches_applied.append(custom_key)
|
|
if custom_switch in switch:
|
|
switches_applied.append(custom_key)
|
|
|
|
|
|
|
|
|
|
cmdfile = open(path,'w')
|
|
|
|
switches_out = 0
|
|
for switch in switches:
|
|
cmdfile.write(switch.strip('\n')+'\n')
|
|
switches_out += 1
|
|
|
|
|
|
for key, switch in enumerate(custom_switches):
|
|
if key not in switches_applied:
|
|
cmdfile.write(switch+'\n')
|
|
switches_out += 1
|
|
|
|
cmdfile.close()
|
|
|
|
print switches_out,'command line switches written to',path
|
|
|
|
|
|
|
|
|
|
def quicky(path='X:\\rdr2\\build\\commandline.txt'):
|
|
outfile = open('commands.txt','w')
|
|
|
|
infile = open(path,'r')
|
|
|
|
for line in infile:
|
|
outfile.write(line.strip('\n')+', ')
|
|
|
|
outfile.close()
|
|
infile.close()
|
|
|
|
def profile_latest_saves(drive=0,game='GTA4'):
|
|
""" Produce a list of paths to profiles that contain valid saves on the
|
|
selected drive. 0: HDD, 1: Memory Unit 1, 2: Memory Unit 2 """
|
|
xbox_drives = ['HDD','MU0','MU1']
|
|
gamehex = '545407f2'
|
|
savepath = os.path.join(gamehex,'00000001')
|
|
|
|
|
|
#cheat this until i can be bothered to make an interface
|
|
selected_drive = xbox_drives[drive]
|
|
|
|
start_folder = selected_drive+':\\'+'Content'#os.path.join(selected_drive,'Content')
|
|
|
|
|
|
#get a list of all the profiles inside the content folder of the selected
|
|
#drive
|
|
content_folder = []
|
|
for folder in xbdir_list(start_folder,True):
|
|
content_folder.append(os.path.join(start_folder,folder[0]))
|
|
|
|
#Get a list of profile locations with the game hex we're looking for
|
|
valid_profiles = []
|
|
for folder in content_folder:
|
|
folder_contents = xbdir_list(folder,True)
|
|
for subfolder in folder_contents:
|
|
if subfolder[0].lower() == gamehex.lower():
|
|
valid_profiles.append(folder)
|
|
|
|
#Get the newest modified on date from the save files. 0 means no saves!
|
|
profiles_and_dates = []
|
|
for index,profile in enumerate(valid_profiles):
|
|
newest_date=0
|
|
savename = ''
|
|
for saves in xbdir_list(os.path.join(profile,savepath)):
|
|
if saves[2] > newest_date:
|
|
newest_date = saves[2]
|
|
savename = saves[0]
|
|
profiles_and_dates.append([profile,newest_date,savename])
|
|
|
|
|
|
#remove any profiles with no saves
|
|
final_profiles = []
|
|
for profile in profiles_and_dates:
|
|
if profile[1] == 0:
|
|
pass
|
|
else:
|
|
final_profiles.append(profile)
|
|
|
|
return final_profiles
|
|
|
|
|
|
def archive_profile(xbpath):
|
|
temp = 'x:\\temp_profile'
|
|
|
|
if os.path.exists(temp):
|
|
os.remove(temp)
|
|
|
|
|
|
def pstats_timeline(statfile='thing.pstats'):
|
|
outfile = open('timeline.txt','w')
|
|
infile = open(statfile,'r')
|
|
stats = split_pstats(infile.read())
|
|
|
|
previous_time = 0
|
|
for full_stat in stats:
|
|
if full_stat['time'] != previous_time:
|
|
outfile.write('\n\n'+full_stat['time']+'\n====\n')
|
|
previous_time = full_stat['time']
|
|
for statname, stat in full_stat.iteritems():
|
|
outfile.write(str(statname)+' = '+str(stat)+'\n')
|
|
outfile.write('-----------------\n')
|
|
|
|
outfile.close()
|
|
infile.close()
|
|
|
|
#======object oriented what's that==============================================
|
|
class MenuMaker:
|
|
""" makes menus """
|
|
def __init__(self, heading, items):
|
|
self.heading = heading
|
|
self.items = list(items)
|
|
|
|
self.comments = False
|
|
self.default_comment = False
|
|
|
|
self.selection = 0
|
|
self.NO_BACK = False
|
|
self.NO_SORT = True
|
|
self.show_numbers = False
|
|
|
|
self.lselect = '-->'
|
|
self.rselect = '<--'
|
|
self.spacer = ' '*len(self.lselect)
|
|
self.back_text = 'Back'
|
|
|
|
self.max_item_length = 71
|
|
|
|
self.menu_position = 0
|
|
|
|
self.valid_keys = ['CURSOR_UP',
|
|
'CURSOR_DOWN',
|
|
'ESCAPE',
|
|
'RETURN',
|
|
'BACKSPACE',
|
|
'1',
|
|
'2',
|
|
'3',
|
|
'4',
|
|
'5',
|
|
'6',
|
|
'7',
|
|
'8',
|
|
'9',
|
|
'0',
|
|
'N',
|
|
'O',
|
|
'V',
|
|
'F10',
|
|
'G',]
|
|
|
|
self.menu_stack = []
|
|
|
|
self.AddEvent(['update_screen',self.menu_position])
|
|
self.RefreshActiveItems()
|
|
|
|
|
|
|
|
def AddEvent(self,event,delay=0):
|
|
event = Event(event[0],
|
|
event[1],
|
|
self.menu_position,
|
|
self.selection,
|
|
delay)
|
|
self.menu_stack.append(event)
|
|
|
|
|
|
def ListItems(self):
|
|
new_items = copy.deepcopy(self.items)
|
|
|
|
if self.NO_SORT:
|
|
pass
|
|
elif not self.NO_SORT:
|
|
new_items.sort()
|
|
|
|
if self.NO_BACK:
|
|
pass
|
|
elif not self.NO_BACK:
|
|
new_items.append([self.back_text,'go_back','back'])
|
|
|
|
if self.show_numbers:
|
|
for index,item in enumerate(new_items):
|
|
new_items[index][0] = str(index+1)+'. '+item[0]
|
|
|
|
for index,item in enumerate(new_items):
|
|
if len(item[0]) > self.max_item_length:
|
|
new_items[index][0] = item[0][0:self.max_item_length]
|
|
|
|
return self.FillComments(new_items)
|
|
|
|
|
|
def SortItems(self):
|
|
self.NO_SORT = False
|
|
self.RefreshActiveItems()
|
|
|
|
def RemoveBack(self):
|
|
self.NO_BACK = True
|
|
self.RefreshActiveItems()
|
|
|
|
def ChangeBackText(self,new_text):
|
|
self.back_text = new_text
|
|
self.RefreshActiveItems()
|
|
|
|
def RefreshActiveItems(self):
|
|
self.active_items = self.ListItems()
|
|
|
|
def DisableKey(self,key):
|
|
to_delete = []
|
|
for index, valid_key in enumerate(self.valid_keys):
|
|
if key == valid_key:
|
|
to_delete.append(index)
|
|
|
|
if len(to_delete) > 0:
|
|
for index in to_delete:
|
|
del self.valid_keys[index]
|
|
|
|
def ToggleDisplayNumbers(self):
|
|
if self.show_numbers:
|
|
self.show_numbers = False
|
|
elif not self.show_numbers:
|
|
self.show_numbers = True
|
|
|
|
self.RefreshActiveItems()
|
|
self.AddEvent(['update_screen',self.menu_position])
|
|
|
|
|
|
def UpdateScreen(self):
|
|
#do a print before the cls because if you don't, the last print could
|
|
#have ended with a comma, which adds a single space after doing a cls
|
|
print
|
|
os.system('cls')
|
|
print box_text(self.heading,1)
|
|
|
|
#print menu items (including selector)
|
|
for index,item in enumerate(self.active_items):
|
|
if self.selection == index:
|
|
lspacer = self.lselect
|
|
rspacer = self.rselect
|
|
else:
|
|
lspacer = self.spacer
|
|
rspacer = self.spacer
|
|
if not self.NO_BACK and index == len(self.active_items)-1:
|
|
print
|
|
print lspacer,item[0],rspacer
|
|
|
|
print
|
|
print box_text(self.active_items[self.selection][3])
|
|
|
|
|
|
def FillComments(self,items):
|
|
for index, item in enumerate(items):
|
|
if len(item) == 3:
|
|
if item[1] == 'go_back':
|
|
if item[0].lower() == 'quit':
|
|
comment = 'No, don\'t go!'
|
|
else:
|
|
comment = 'Previous menu'
|
|
else:
|
|
comment = 'Bustin makes me feel good'
|
|
|
|
items[index].append(comment)
|
|
|
|
return items
|
|
|
|
|
|
def CursorUp(self):
|
|
if self.selection == 0:
|
|
self.selection = self.HighestSelection()
|
|
else:
|
|
self.selection -= 1
|
|
self.AddEvent(['update_screen',self.menu_position])
|
|
|
|
def CursorDown(self):
|
|
if self.selection == self.HighestSelection():
|
|
self.selection = 0
|
|
else:
|
|
self.selection += 1
|
|
self.AddEvent(['on_select',[self.active_items[self.selection][1],
|
|
self.active_items[self.selection][2]]])
|
|
|
|
self.AddEvent(['update_screen',self.menu_position])
|
|
|
|
def HighestSelection(self):
|
|
return len(self.active_items)-1
|
|
|
|
def ReturnEvent(self):
|
|
self.AddEvent([self.active_items[self.selection][1],
|
|
self.active_items[self.selection][2]])
|
|
|
|
def NumkeySelect(self,key):
|
|
button_select = int(key)-1
|
|
|
|
if button_select+1 == 0:
|
|
return False
|
|
|
|
if button_select > len(self.active_items)-1:
|
|
return False
|
|
else:
|
|
self.selection = button_select
|
|
self.AddEvent(['update_screen',self.menu_position])
|
|
self.ReturnEvent()
|
|
return True
|
|
|
|
def ProcessKey(self,key):
|
|
numkeys = []
|
|
for num in range(0,10):
|
|
numkeys.append(str(num))
|
|
|
|
#print 'down:',key
|
|
|
|
if key.upper() not in self.valid_keys:
|
|
return
|
|
|
|
if key == 'CURSOR_UP':
|
|
self.CursorUp()
|
|
|
|
elif key == 'CURSOR_DOWN':
|
|
self.CursorDown()
|
|
|
|
elif key == 'RETURN':
|
|
self.ReturnEvent()
|
|
|
|
elif key.upper() == 'N':
|
|
self.ToggleDisplayNumbers()
|
|
|
|
elif key.upper() == 'G':
|
|
print '['+self.active_items[self.selection][1]+']',
|
|
print '['+self.active_items[self.selection][2]+']'
|
|
|
|
elif key.upper() == 'ESCAPE':
|
|
self.AddEvent(['exit_query',self.menu_position])
|
|
|
|
elif key.upper() == 'O':
|
|
self.AddEvent(['choose_date','place_order'])
|
|
|
|
|
|
elif key.upper() == 'V':
|
|
self.AddEvent(['choose_date', 'view_orders'])
|
|
|
|
|
|
|
|
elif key in numkeys:
|
|
self.NumkeySelect(key)
|
|
|
|
|
|
|
|
class MenuHandler:
|
|
def __init__(self):
|
|
self.events = []
|
|
self.menu = []
|
|
self.currentmenu = 0
|
|
self.last_process = time.clock()
|
|
self.last_key_read = time.clock()
|
|
self.key_history = []
|
|
time.clock()
|
|
|
|
self.final_event = None
|
|
|
|
def GatherMenuEvents(self):
|
|
for index,menu in enumerate(reversed(self.menu)):
|
|
while len(menu.menu_stack) is not 0:
|
|
event = menu.menu_stack.pop(0)
|
|
self.events.append(event)
|
|
|
|
|
|
def CatMenuItems(self):
|
|
categories = get_categories()
|
|
cat_items = []
|
|
for cat in categories:
|
|
cat_items.append([cat,
|
|
'select_category',
|
|
cat,
|
|
('Good %s, %s. Would you like to play %s today?'
|
|
% (time_of_day(),windows_username()[0],cat))])
|
|
return cat_items
|
|
|
|
def CategoryContents(self,category):
|
|
categories = []
|
|
for game_id in game_versions.ListGIDs():
|
|
if get_setting(game_id, 'category').lower() == category.lower():
|
|
categories.append([get_setting(game_id,'name'),
|
|
'select_game_page',
|
|
game_id,
|
|
'Let\'s play %s' % get_setting(game_id,'name')])
|
|
return categories
|
|
|
|
def CategoryMenu(self,category):
|
|
self.AddMenu(MenuMaker(category,self.CategoryContents(category)))
|
|
self.menu[self.currentmenu].SortItems()
|
|
|
|
|
|
def TopMenu(self):
|
|
""" Erase all current menus and snap back to the top category menu.
|
|
Essentially resets the menu system to a safe point. """
|
|
self.menu = []
|
|
self.AddMenu(GamesMenu())
|
|
self.menu[self.currentmenu].ChangeBackText('Quit')
|
|
|
|
def UpdateScreen(self):
|
|
""" Print the contents of the currently selected menu to the screen """
|
|
self.menu[self.currentmenu].UpdateScreen()
|
|
|
|
|
|
def AddKeyEvents(self):
|
|
self.last_key_read = time.clock()
|
|
key = get_keypress()
|
|
if key is not False:
|
|
self.AddEvent(['key_pressed',key])
|
|
self.UpdateKeyHistory(key)
|
|
self.CheckCodes()
|
|
#logging('added key event','DEBUG')
|
|
key = get_keypress()
|
|
|
|
|
|
def ProcessKeyEvent(self,event):
|
|
if event.EventType() is not 'key_pressed':
|
|
error_exit('Somehow a non-key_pressed event got sent to the '
|
|
'key event processor. Shouldn\'t happen!')
|
|
|
|
|
|
if event.Data() == 'BACKSPACE':
|
|
self.AddEvent(['go_back',self.currentmenu])
|
|
|
|
elif event.Data() == 'F10':
|
|
self.AddEvent(['top_menu',self.currentmenu])
|
|
|
|
elif event.Data() == 'F12':
|
|
#self.AddEvent(['final_event','test-me-do'])
|
|
print 'Opening QA drive...'
|
|
subprocess.Popen('explorer \\\\rsgedisan1n1\\qa$')
|
|
|
|
elif event.Data() == 'F11':
|
|
print __name__
|
|
|
|
else:
|
|
#todo: send key events to potentially not the active menu?
|
|
self.menu[self.currentmenu].ProcessKey(event.Data())
|
|
|
|
|
|
def AddMenu(self,menu):
|
|
self.menu.append(menu)
|
|
self.currentmenu = len(self.menu)-1
|
|
self.menu[self.currentmenu].menu_position = self.currentmenu
|
|
|
|
def RemoveTopMenu(self):
|
|
self.menu.pop(self.currentmenu)
|
|
self.currentmenu = len(self.menu)-1
|
|
|
|
|
|
def AddEvent(self,event,delay=0):
|
|
""" Add an event to the stack """
|
|
event = Event(event[0],
|
|
event[1],
|
|
self.currentmenu,
|
|
self.menu[self.currentmenu].selection,
|
|
delay)
|
|
|
|
self.events.append(event)
|
|
|
|
|
|
def ProcessEvent(self):
|
|
"""Read from the event stack. If there's nothing in the event
|
|
stack, check the key buffer """
|
|
clear_buffer_timeout = 5
|
|
|
|
#clear the key buffer if we haven't had access to it for 5 seconds
|
|
if time.clock() - self.last_process > clear_buffer_timeout:
|
|
clear_key_buffer()
|
|
|
|
self.last_process = time.clock()
|
|
|
|
undelayed_events = 0
|
|
for event_delays in list(self.events):
|
|
if event_delays.Delay() is 0:
|
|
undelayed_events += 1
|
|
|
|
if undelayed_events == 0:
|
|
self.GatherMenuEvents()
|
|
|
|
if undelayed_events == 0:
|
|
self.AddKeyEvents()
|
|
|
|
if undelayed_events == 0:
|
|
time.sleep(0.001)
|
|
return
|
|
|
|
event = self.events.pop(0)
|
|
|
|
if event.Delay() is not 0:
|
|
self.events.append(event)
|
|
#logging(str(EventLogging(event))+' delayed for '+str(event.Delay()),'DELAY')
|
|
return
|
|
|
|
logging(str(EventLogging(event)),'EVENT')
|
|
|
|
#event:key_pressed
|
|
if event.EventType() == 'key_pressed':
|
|
self.ProcessKeyEvent(event)
|
|
|
|
#event:on_select
|
|
elif event.EventType() == 'on_select':
|
|
self.OnSelectEvents(event)
|
|
|
|
#event:select_category
|
|
elif event.EventType() == 'select_category':
|
|
self.CategoryMenu(event.Data())
|
|
|
|
#event:select_game
|
|
elif event.EventType() == 'select_game':
|
|
self.AddMenu(OneGameMenu(event.Data()))
|
|
|
|
#event:select_game_page
|
|
elif event.EventType() == 'select_game_page':
|
|
self.GamePage(event.Data())
|
|
|
|
#event:update_screen
|
|
elif event.EventType() == 'update_screen':
|
|
self.UpdateScreen()
|
|
|
|
#event:lan_toggle_dlc
|
|
elif event.EventType() == 'lan_toggle_dlc':
|
|
toggle_id = event.Data()[0]
|
|
toggle_dlc = event.Data()[1]
|
|
|
|
if toggle_dlc in game_versions[toggle_id]['lan_dlc_disabled']:
|
|
game_versions[toggle_id]['lan_dlc_disabled'].pop(
|
|
game_versions[toggle_id]['lan_dlc_disabled'].index(
|
|
toggle_dlc))
|
|
else:
|
|
game_versions[toggle_id]['lan_dlc_disabled'].append(toggle_dlc)
|
|
|
|
self.RemoveTopMenu()
|
|
self.AddMenu(LANDLCMenu(toggle_id))
|
|
|
|
#event:disable_switches_menu
|
|
elif event.EventType() == 'disable_switches_menu':
|
|
self.AddMenu(GetSwitchesMenu(game_versions[event.Data()], False))
|
|
|
|
#event:enable_switches_menu
|
|
elif event.EventType() == 'enable_switches_menu':
|
|
self.AddMenu(GetSwitchesMenu(game_versions[event.Data()], True))
|
|
|
|
#event:manage_switches
|
|
elif event.EventType() == 'manage_switches':
|
|
items = []
|
|
if len(game_versions[event.Data()]['switches']) != 0:
|
|
items.append(['Disable Switch',
|
|
'disable_switches_menu',
|
|
event.Data(),
|
|
'Temporarily disable a switch'])
|
|
|
|
if 'disabled_switches' in game_versions[event.Data()]:
|
|
if len(game_versions[event.Data()]['disabled_switches']) != 0:
|
|
items.append(['Enable Switch',
|
|
'enable_switches_menu',
|
|
event.Data(),
|
|
'Enable a previously disabled switch'])
|
|
|
|
self.AddMenu(MenuMaker('Manage Switches', items))
|
|
|
|
|
|
#event:disable_switch
|
|
elif event.EventType() == 'disable_switch':
|
|
gid = game_versions[event.Data()[0]]
|
|
if 'disabled_switches' not in gid:
|
|
gid['disabled_switches'] = []
|
|
|
|
new_switches = []
|
|
for idx, switch in enumerate(gid['switches']):
|
|
if idx == event.Data()[1]:
|
|
gid['disabled_switches'].append(switch)
|
|
continue
|
|
else:
|
|
new_switches.append(switch)
|
|
|
|
gid['switches'] = ', '.join(new_switches)
|
|
|
|
print ', '.join(new_switches)
|
|
|
|
|
|
self.AddEvent(['go_back',0])
|
|
self.AddEvent(['go_back',0])
|
|
|
|
elif event.EventType() == 'enable_switch':
|
|
gid = game_versions[event.Data()[0]]
|
|
add_switch = gid['disabled_switches'].pop(event.Data()[1])
|
|
switches = copy.copy(gid['switches'])
|
|
switches.append(add_switch)
|
|
|
|
gid['switches'] = ', '.join(switches)
|
|
self.AddEvent(['go_back', 0])
|
|
self.AddEvent(['go_back',0])
|
|
|
|
|
|
|
|
|
|
#event:go_back
|
|
elif event.EventType() == 'go_back':
|
|
if self.menu[self.currentmenu].NO_BACK == True:
|
|
pass
|
|
else:
|
|
if self.currentmenu == 0:
|
|
self.final_event = '_BACK'
|
|
self.AddEvent(['exit_query',0])
|
|
else:
|
|
del self.menu[self.currentmenu]
|
|
self.currentmenu -= 1
|
|
self.AddEvent(['update_screen',self.currentmenu])
|
|
|
|
#event:exit
|
|
elif event.EventType() == 'exit':
|
|
print 'exiting',
|
|
print_slow_dots()
|
|
sys.exit()
|
|
|
|
#event:exit_query
|
|
elif event.EventType() == 'exit_query':
|
|
items = [['Yes',
|
|
'exit',
|
|
self.currentmenu,
|
|
'Exit the QA Menu']]
|
|
|
|
self.AddMenu(MenuMaker('Quit?',items))
|
|
self.menu[self.currentmenu].ChangeBackText('No')
|
|
self.menu[self.currentmenu].DisableKey('ESCAPE')
|
|
|
|
#event:update_loop
|
|
elif event.EventType() == 'update_loop':
|
|
self.AddEvent(['check_for_update',0])
|
|
self.AddEvent(['update_loop',event.Data()],delay=event.Data())
|
|
|
|
#event:xex
|
|
elif event.EventType() == 'xex':
|
|
launch_gta_iv(event.Data())
|
|
self.AddEvent(['update_screen',self.currentmenu])
|
|
|
|
|
|
#event:self
|
|
elif event.EventType() == 'self':
|
|
launch_ps3(event.Data())
|
|
self.AddEvent(['update_screen',self.currentmenu])
|
|
|
|
#event:set_home
|
|
elif event.EventType() == 'set_home':
|
|
SetPS3Home(event.Data())
|
|
self.AddEvent(['update_screen',self.currentmenu])
|
|
|
|
#event:lan_select_dlc
|
|
elif event.EventType() == 'lan_select_dlc':
|
|
self.AddMenu(LANDLCMenu(event.Data()))
|
|
|
|
|
|
#event:jimmy_level
|
|
elif event.EventType() == 'jimmy_level':
|
|
self.AddMenu(JimmyLevelSelect(event.Data()))
|
|
|
|
#event:identify_version
|
|
elif event.EventType() == 'identify_version':
|
|
IdentifyVersionScreen(game_versions[event.Data()])
|
|
self.AddEvent(['update_screen',self.currentmenu])
|
|
|
|
#event:self_custom_switch
|
|
elif event.EventType() == 'self_custom_switch':
|
|
launch_ps3(event.Data()[0],event.Data()[1])
|
|
self.AddEvent(['update_screen',self.currentmenu])
|
|
|
|
#event:xex_custom_switch
|
|
elif event.EventType() == 'xex_custom_switch':
|
|
launch_gta_iv(event.Data()[0],event.Data()[1])
|
|
self.AddEvent(['update_screen',self.currentmenu])
|
|
|
|
#event:upload_logs
|
|
elif event.EventType() == 'upload_logs':
|
|
upload_logs(event.Data())
|
|
self.AddEvent(['update_screen',self.currentmenu])
|
|
|
|
#event:open_log_folder
|
|
elif event.EventType() == 'open_log_folder':
|
|
os.startfile(get_setting(event.Data(),'logdestination'))
|
|
|
|
#event:script_manager
|
|
elif event.EventType() == 'script_manager':
|
|
active_script = script_manager(event.Data())
|
|
if active_script != '_BACK':
|
|
active_script = 'Active script changed to \'%s\'.' % active_script
|
|
self.ChangeComment(event,active_script)
|
|
self.AddEvent(['update_screen',self.currentmenu])
|
|
|
|
#event:cab_transfer
|
|
elif event.EventType() == 'cab_transfer':
|
|
cab_transfer(event.Data())
|
|
self.menu[event.SourceMenu()].RefreshActiveItems()
|
|
self.AddEvent(['update_screen',self.currentmenu])
|
|
|
|
|
|
#event:check_cab_file
|
|
elif event.EventType() == 'check_cab_file':
|
|
cabs_match = using_latest_cab_file(event.Data())
|
|
if cabs_match:
|
|
self.ChangeComment(event,
|
|
('Check passed! Check performed at %s. '
|
|
'The CAB file on your 360 matches the cab '
|
|
'file on your PC.' %
|
|
time.strftime('%H:%M:%S')))
|
|
elif not cabs_match:
|
|
self.ChangeComment(event,
|
|
('Check Failed! Check performed at %s. '
|
|
'The cab file on your 360 does not match the '
|
|
'cabfile on your PC. Try using Transfer CAB '
|
|
'again.' %
|
|
time.strftime('%H:%M:%S')))
|
|
|
|
#event:pkg
|
|
elif event.EventType() == 'pkg':
|
|
os.system('cls')
|
|
PS3InstallPKG(game_versions[event.Data()])
|
|
self.AddEvent(['update_screen',self.currentmenu])
|
|
|
|
#event:script_report
|
|
elif event.EventType() == 'script_report':
|
|
self.ChangeComment(event,
|
|
ScriptReport(game_versions[event.Data()]
|
|
['script_report']))
|
|
|
|
#event:prepare_for_emulation
|
|
elif event.EventType() == 'prepare_for_emulation':
|
|
prepare_for_emulation(event.Data())
|
|
self.AddEvent(['update_screen',event.Data()])
|
|
|
|
#event:syncit
|
|
elif event.EventType() == 'syncit':
|
|
syncit(event.Data())
|
|
self.AddEvent(['go_back',self.currentmenu])
|
|
self.AddEvent(['select_game_page',event.Data()])
|
|
self.AddEvent(['update_screen',event.Data()])
|
|
|
|
#event:sleep
|
|
elif event.EventType() == 'sleep':
|
|
time.sleep(event.Data())
|
|
|
|
#event:toggle_numbers
|
|
elif event.EventType() == 'toggle_numbers':
|
|
self.menu[event[1]].ToggleDisplayNumbers()
|
|
self.AddEvent(['update_screen',event.Data()])
|
|
|
|
#event:check_for_update
|
|
elif event.EventType() == 'check_for_update':
|
|
print 'Checking menu version...',
|
|
check_menu_update()
|
|
print 'no update required.'
|
|
|
|
print 'Checking for Syncit profile update...',
|
|
if update_sync_ini():
|
|
print 'Sync ini updated'
|
|
else:
|
|
print 'no update required'
|
|
self.AddEvent(['update_screen',event.Data()],10)
|
|
|
|
#event:top_menu
|
|
elif event.EventType() == 'top_menu':
|
|
self.TopMenu()
|
|
|
|
#event:wiki_page
|
|
elif event.EventType() == 'wiki_page':
|
|
OpenWikiPage(event.Data())
|
|
|
|
#event:set_socket_port
|
|
elif event.EventType() == 'set_socket_port':
|
|
SetSocketPort(event.Data())
|
|
comment = ('Temporarily change the socketport. Socketport '
|
|
'currently set to: %s' % CurrentSocketPort(event.Data()))
|
|
self.ChangeComment(event,comment)
|
|
|
|
#event:begin_emu
|
|
elif event.EventType() == 'begin_emu':
|
|
print 'erasing logs...'
|
|
delete_logs(event.Data())
|
|
|
|
print 'loading emulator...'
|
|
emu = XBDiscEmulator()
|
|
emu.StartEmulator()
|
|
|
|
print 'setting log file...'
|
|
emu.SetLogFile(os.path.join(os.environ.get('RAGE_CRASHDUMP_DIR'),
|
|
LogFilename()))
|
|
|
|
print 'loading layout...'
|
|
emu.LoadMedia(game_versions[event.Data()]['xgd'])
|
|
|
|
print 'starting emulation...'
|
|
emu.StartEmulation()
|
|
self.AddEvent(['update_screen',self.currentmenu])
|
|
|
|
#event:begin_debug_emu
|
|
elif event.EventType() == 'begin_debug_emu':
|
|
print 'erasing logs for %s...' % event.Data()
|
|
delete_logs(event.Data())
|
|
|
|
#TODO: add a thing here to run systrayrfs without running rag
|
|
run_watson(event.Data())
|
|
|
|
DoLaunchPreparations(event.Data())
|
|
|
|
print 'loading emulator...'
|
|
emu = XBDiscEmulator()
|
|
emu.StartEmulator()
|
|
|
|
print 'setting log file...'
|
|
|
|
crashdump_dir = os.environ.get('RAGE_CRASHDUMP_DIR')
|
|
if crashdump_dir == None:
|
|
crashdump_dir = os.path.dirname(game_versions[event.Data()]['debug_xgd'])
|
|
emu.SetLogFile(os.path.join(crashdump_dir,
|
|
LogFilename()))
|
|
|
|
print 'loading layout...'
|
|
emu.LoadMedia(game_versions[event.Data()]['debug_xgd'])
|
|
|
|
print 'starting emulation...'
|
|
emu.StartEmulation()
|
|
self.AddEvent(['update_screen',self.currentmenu])
|
|
|
|
#event: place_order
|
|
elif event.EventType() == 'place_order':
|
|
TakeOrder(event.Data())
|
|
self.AddEvent(['update_screen',self.currentmenu])
|
|
|
|
#event:subst
|
|
elif event.EventType() == 'subst':
|
|
os.system('cls')
|
|
print box_text('SubstDrive')
|
|
for subst_cmd in event.Data():
|
|
SubstDrive(subst_cmd)
|
|
os.system('pause')
|
|
self.AddEvent(['update_screen', self.currentmenu])
|
|
|
|
elif event.EventType() == 'view_orders':
|
|
ViewOrders(event.Data())
|
|
self.AddEvent(['update_screen', self.currentmenu])
|
|
|
|
elif event.EventType() == 'choose_date':
|
|
self.AddMenu(OneWeekChoice(event.Data()))
|
|
|
|
#event:run_batfile
|
|
elif event.EventType() == 'run_batfile':
|
|
run_batfile(event.Data())
|
|
self.AddEvent(['update_screen',self.currentmenu])
|
|
|
|
#event:konami_code
|
|
elif event.EventType() == 'konami_code':
|
|
print 'Press any key to play Gradius'
|
|
wait_for_key()
|
|
print 'You get TURTLETEST instead',
|
|
print_slow_dots(5)
|
|
self.AddMenu(TurtleTest())
|
|
self.AddEvent(['update_screen',self.currentmenu])
|
|
|
|
elif event.EventType() == 'final_event':
|
|
self.final_event = event.Data()
|
|
|
|
else:
|
|
error_exit('Unhandled Event: '+'['+str(event.EventType())+'] ['
|
|
+str(event.Data())+']')
|
|
|
|
|
|
|
|
|
|
def OnSelectEvents(self,event):
|
|
if event.EventType() is not 'on_select':
|
|
error_exit('Somehow a non-on_select event got sent to '
|
|
'OnSelectEvents(). Shouldn\'t happen!')
|
|
|
|
|
|
def UpdateKeyHistory(self,key):
|
|
if len(self.key_history) > 50:
|
|
self.key_history.pop(0)
|
|
self.key_history.append(key)
|
|
|
|
def CheckCodes(self):
|
|
code1 = ['CURSOR_UP',
|
|
'CURSOR_UP',
|
|
'CURSOR_DOWN',
|
|
'CURSOR_DOWN',
|
|
'CURSOR_LEFT',
|
|
'CURSOR_RIGHT',
|
|
'CURSOR_LEFT',
|
|
'CURSOR_RIGHT',
|
|
'b',
|
|
'a']
|
|
|
|
## code2 = ['CURSOR_UP',
|
|
## 'CURSOR_DOWN',
|
|
## 'CURSOR_LEFT',
|
|
## 'CURSOR_RIGHT',
|
|
## 'a',
|
|
## 'b',
|
|
## 'x',
|
|
## 'y']
|
|
|
|
codes = [code1]
|
|
|
|
for code in codes:
|
|
if code == self.key_history[-len(code):]:
|
|
self.AddEvent(['konami_code',''])
|
|
|
|
def GamePage(self,game_id):
|
|
items = []
|
|
|
|
if get_setting(game_id,'jimmylevels','NO_EXIT') != False:
|
|
items.append(['Level select',
|
|
'jimmy_level',
|
|
game_id,
|
|
('Choose a Jimmy level, then run %s, Version: %s' %
|
|
(get_setting(game_id,'name'),
|
|
GetVersionNumber(game_id)))])
|
|
|
|
if get_setting(game_id,'xex','NO_EXIT') != False:
|
|
items.append(['Run %s' % get_setting(game_id,'name'),
|
|
'xex',
|
|
game_id,
|
|
('Let\'s play %s, Version: %s!'
|
|
% (get_setting(game_id,'name'),
|
|
GetVersionNumber(game_id)))])
|
|
|
|
if get_setting(game_id,'self','NO_EXIT') != False:
|
|
items.append(['Run %s' % get_setting(game_id,'name'),
|
|
'self',
|
|
game_id,
|
|
('Let\'s play %s, Version: %s!'
|
|
% (get_setting(game_id,'name'),
|
|
GetVersionNumber(game_id)))])
|
|
|
|
if get_setting(game_id, 'switches', 'NO_EXIT') != False:
|
|
items.append(['Manage Switches',
|
|
'manage_switches',
|
|
game_id,
|
|
('Select switch to temporarily disable')])
|
|
|
|
|
|
if get_setting(game_id, 'set_home', 'NO_EXIT') != False:
|
|
items.append(['Set PS3 Home Directory',
|
|
'set_home',
|
|
game_versions[game_id],
|
|
('Set the PS3 home directory, then you can use '
|
|
'game -> app_home in the xmb to load %s' % (
|
|
get_setting(game_id,'name')))])
|
|
|
|
if get_setting(game_id, 'cab_hash', 'NO_EXIT') != False:
|
|
items.append(['Identify Version',
|
|
'identify_version',
|
|
game_id,
|
|
'Check CAB or PKG version'])
|
|
|
|
if get_setting(game_id,'xgd','NO_EXIT') != False:
|
|
items.append(['Start Emulator',
|
|
'begin_emu',
|
|
game_id,
|
|
('Start Xbox Disc Emulator with layout file %s'
|
|
% game_versions[game_id]['xgd'])])
|
|
|
|
if get_setting(game_id, 'debug_xgd', 'NO_EXIT') != False:
|
|
items.append(['Start Emulator (Debug)',
|
|
'begin_debug_emu',
|
|
game_id,
|
|
('Start Xbox Disc Emulator with layout file %s'
|
|
% game_versions[game_id]['debug_xgd'])])
|
|
|
|
if get_setting(game_id, 'lan_dlcnames', 'NO_EXIT') != False:
|
|
items.append(['Select DLC',
|
|
'lan_select_dlc',
|
|
game_id,
|
|
'Selectively disable DLC'])
|
|
|
|
if get_setting(game_id,'logdestination','NO_EXIT') != False:
|
|
items.append(['Copy logs to network',
|
|
'upload_logs',
|
|
game_id,
|
|
('Copy %s logs to the network (%s)'
|
|
% (get_setting(game_id,'name'),
|
|
get_setting(game_id,'logdestination')))])
|
|
|
|
items.append(['Open network logs folder',
|
|
'open_log_folder',
|
|
game_id,
|
|
('Open network logs folder (%s)' %
|
|
get_setting(game_id,'logdestination'))])
|
|
|
|
## GONE. Nobody used it. Thomson didn't keep the reference scripts up to date
|
|
## if get_setting(game_id,'script_local','NO_EXIT') != False:
|
|
## items.append(['Compare local script to network script',
|
|
## 'compare_scripts',
|
|
## game_id,
|
|
## ('Compare local scripts to reference scripts at %s'
|
|
## % get_setting(game_id,'script_remote'))])
|
|
|
|
if FindSocketPortPosition(game_id) is not False:
|
|
items.append(['Modify Socketport',
|
|
'set_socket_port',
|
|
game_id,
|
|
('Temporarily change the socketport. Socketport '
|
|
'currently set to: %s' % CurrentSocketPort(game_id))])
|
|
|
|
if get_setting(game_id,'active_script','NO_EXIT') != False:
|
|
items.append(['Script Manager',
|
|
'script_manager',
|
|
game_id,
|
|
'Open the script manager that sometimes works! Yay!'])
|
|
|
|
if get_setting(game_id,'cab_folder_source','NO_EXIT') != False and \
|
|
get_setting(game_id,'cab_folder_destination','NO_EXIT') != False:
|
|
items.append(['Transfer CAB',
|
|
'cab_transfer',
|
|
game_id,
|
|
('Transfer %s CAB file to Xbox 360' %
|
|
get_setting(game_id,'name'))])
|
|
|
|
if 'no_cab_check' not in game_versions[game_id]:
|
|
items.append(['Check 360 CAB version',
|
|
'check_cab_file',
|
|
game_id,
|
|
('Check if the CAB file currently on your 360 matches'
|
|
' the cab file currently on your PC.')])
|
|
|
|
|
|
if get_setting(game_id, 'ps3_pkg', 'NO_EXIT') != False or \
|
|
get_setting(game_id, 'ps3_patch', 'NO_EXIT') != False:
|
|
items.append(['Install PS3 Packages',
|
|
'pkg',
|
|
game_id,
|
|
('Transfer %s PKG file to PS3' %
|
|
get_setting(game_id, 'name'))])
|
|
|
|
if get_setting(game_id,'script_report','NO_EXIT') != False:
|
|
items.append(['Script Report',
|
|
'script_report',
|
|
game_id,
|
|
('EXPERIMENTAL: Attempt to identify the scripts '
|
|
'currently in use')])
|
|
|
|
|
|
|
|
if get_setting(game_id,'title_update','NO_EXIT') != False:
|
|
items.append(['Prepare For Emulation',
|
|
'prepare_for_emulation',
|
|
game_id,
|
|
('Copy CAB file and TitleUpdate from local PC '
|
|
'to 360.')])
|
|
|
|
if get_setting(game_id,'sync','NO_EXIT') != False:
|
|
items.append(['Synchronize It!',
|
|
'syncit',
|
|
game_id,
|
|
('Run Synchronize It! on project %s.\n\n Local Version: %s\nRemote Version: %s\n\nNote: To refresh version numbers, select "Back" then reselect "%s".' %
|
|
(get_setting(game_id,'sync'),
|
|
GetVersionNumber(game_id),
|
|
GetVersionNumber(game_id, local=False),
|
|
get_setting(game_id, 'name')))])
|
|
|
|
if get_setting(game_id,'wikipage','NO_EXIT') != False:
|
|
items.append(['Open wiki page',
|
|
'wiki_page',
|
|
game_id,
|
|
('Open "%s" on the QA Wiki' %
|
|
get_setting(game_id,'wikipage'))])
|
|
|
|
if get_setting(game_id, 'run_batfile', 'NO_EXIT') != False:
|
|
items.append([game_versions[game_id]['run_batfile'][0],
|
|
'run_batfile',
|
|
game_versions[game_id]['run_batfile'][1],
|
|
'Run the file "%s"' % game_versions[game_id]['run_batfile'][1]])
|
|
|
|
|
|
if get_setting(game_id, 'subst', 'NO_EXIT') != False:
|
|
items.append(['SubstDrive',
|
|
'subst',
|
|
game_versions[game_id]['subst'],
|
|
'Run Subst with the paramaters: %s' % game_versions[game_id]['subst']])
|
|
|
|
if get_setting(game_id, 'shortcuts', 'NO_EXIT') != False:
|
|
for sc_id in game_versions[game_id]['shortcuts']:
|
|
items.append(['Shortcut: %s' % game_versions[sc_id]['name'],
|
|
'select_game_page',
|
|
sc_id,
|
|
'Follow shortcut to %s' % game_versions[sc_id]['name']])
|
|
|
|
|
|
if get_setting(game_id, 'cat_shortcuts', 'NO_EXIT') != False:
|
|
for sc_id in game_versions[game_id]['cat_shortcuts']:
|
|
items.append(['Shortcut: %s' % sc_id,
|
|
'select_category',
|
|
sc_id,
|
|
'Follow shortcut to category %s' % sc_id])
|
|
|
|
|
|
self.AddMenu(MenuMaker(get_setting(game_id,'name'),items))
|
|
|
|
def ChangeComment(self,event,comment):
|
|
self.menu[event.SourceMenu()].active_items[event.MenuSelection()][3] = comment
|
|
#self.menu[finger[0]].RefreshActiveItems()
|
|
self.AddEvent(['update_screen',event.SourceMenu()])
|
|
|
|
|
|
|
|
class Event:
|
|
def __init__(self, event_type, data, source, source_selection, delay):
|
|
self.event_type = event_type
|
|
self.data = data
|
|
|
|
self.menu_source = source
|
|
self.menu_selection = source_selection
|
|
|
|
self.time_generated = time.clock()
|
|
self.delay = delay
|
|
|
|
def EventType(self):
|
|
return self.event_type
|
|
|
|
def Data(self):
|
|
return self.data
|
|
|
|
def TimeGenerated(self):
|
|
return self.time_generated
|
|
|
|
def Delay(self):
|
|
if self.delay == 0:
|
|
return 0
|
|
|
|
time_until_run = (self.TimeGenerated() + self.delay) - time.clock()
|
|
|
|
if time_until_run <= 0:
|
|
return 0
|
|
else:
|
|
return time_until_run
|
|
|
|
def SourceMenu(self):
|
|
return self.menu_source
|
|
|
|
def MenuSelection(self):
|
|
return self.menu_selection
|
|
|
|
|
|
def EventLogging(event):
|
|
event_list = [event.EventType(),
|
|
event.Data()]
|
|
|
|
fingerprint = ['%.2f' % event.TimeGenerated(),
|
|
'%.2f' % event.Delay(),
|
|
event.SourceMenu()]
|
|
|
|
return str(event_list)+str(fingerprint)
|
|
|
|
|
|
def MiniMenu(heading,items,NO_BACK=False):
|
|
minihandler = MenuHandler()
|
|
|
|
minihandler.AddMenu(MenuMaker(heading, items))
|
|
|
|
if NO_BACK:
|
|
minihandler.menu[0].RemoveBack()
|
|
|
|
while minihandler.final_event == None:
|
|
minihandler.ProcessEvent()
|
|
|
|
return minihandler.final_event
|
|
|
|
|
|
class TurtleTest:
|
|
def __init__(self):
|
|
self.menu_stack = []
|
|
self.x = 0
|
|
self.y = 0
|
|
|
|
#work out how to remove reliance on these
|
|
self.selection = 0
|
|
self.NO_BACK = False
|
|
|
|
self.turtle = 't'
|
|
|
|
def ProcessKey(self,key):
|
|
if key == 'CURSOR_UP': self.y -= 1
|
|
elif key == 'CURSOR_DOWN': self.y += 1
|
|
|
|
elif key == 'CURSOR_LEFT': self.x -= 1
|
|
elif key == 'CURSOR_RIGHT': self.x += 1
|
|
|
|
elif key.upper() == 'ESCAPE':
|
|
self.AddEvent(['exit_query',self.menu_position])
|
|
|
|
else: return
|
|
self.AddEvent(['update_screen',0])
|
|
|
|
def UpdateScreen(self):
|
|
print
|
|
os.system('cls')
|
|
|
|
print '\n'*self.y
|
|
print ' '*(self.x),
|
|
print self.turtle
|
|
|
|
def AddEvent(self,event,delay=0):
|
|
event = Event(event[0],
|
|
event[1],
|
|
self.menu_position,
|
|
self.selection,
|
|
delay)
|
|
self.menu_stack.append(event)
|
|
|
|
|
|
def ListGames():
|
|
games = []
|
|
for game_id in game_versions.ListGIDs():
|
|
game = get_setting(game_id,'game')
|
|
if game not in games:
|
|
games.append(game)
|
|
|
|
return games
|
|
|
|
|
|
def ListGameIDs(game,category=None):
|
|
game_ids = []
|
|
for game_id in game_versions.ListGIDs():
|
|
if get_setting(game_id,'game') == game:
|
|
|
|
game_ids.append(game_id)
|
|
|
|
return game_ids
|
|
|
|
|
|
def GamesMenu():
|
|
games = ListGames()
|
|
games.sort()
|
|
|
|
items = []
|
|
|
|
for game in games:
|
|
items.append([game,
|
|
'select_game',
|
|
game,
|
|
('Good %s, %s. Would you like to play %s today?'
|
|
%(time_of_day(),windows_username()[0],game))])
|
|
|
|
return MenuMaker('QA Menu',items)
|
|
|
|
|
|
def OneGameMenu(game):
|
|
game_ids = ListGameIDs(game)
|
|
|
|
categories = []
|
|
for game_id in game_ids:
|
|
category = get_setting(game_id,'category')
|
|
if category not in categories:
|
|
categories.append(category)
|
|
|
|
categories.sort()
|
|
|
|
items = []
|
|
for category in categories:
|
|
items.append([category,
|
|
'select_category',
|
|
category,
|
|
('Which type of %s would you like to play?'
|
|
% game)])
|
|
|
|
return MenuMaker(game,items)
|
|
|
|
|
|
def JimmyLevelSelect(game_id):
|
|
if not get_setting(game_id,'jimmylevels','NO_EXIT'):
|
|
error_exit('jimmylevels missing from game_id: %s' % game_id)
|
|
|
|
levels = get_setting(game_id,'jimmylevels')
|
|
list_switches = get_setting(game_id,'switches')
|
|
|
|
switches = ' '
|
|
for switch in list_switches:
|
|
switches += switch+' '
|
|
|
|
if get_setting(game_id,'self','NO_EXIT'):
|
|
event_command = 'self_custom_switch'
|
|
elif get_setting(game_id,'xex','NO_EXIT'):
|
|
event_command = 'xex_custom_switch'
|
|
|
|
items = []
|
|
for level in levels:
|
|
custom_switches = switches+' -level='+level
|
|
items.append([level,
|
|
event_command,
|
|
[game_id,custom_switches],
|
|
'Play %s in %s' % (level, get_setting(game_id,'name'))])
|
|
|
|
return MenuMaker('Level Select',items)
|
|
|
|
def LANDLCMenu(game_id):
|
|
if 'lan_dlcnames' not in game_versions[game_id]:
|
|
error_exit('LANDLCMenu(): lan_dlcnames missing from game_id: %s' % game_id)
|
|
|
|
if 'lan_dlcids' not in game_versions[game_id]:
|
|
error_exit('LANDLCMenu(): lan_dlcids missing from game_id: %s' % game_id)
|
|
|
|
if 'lan_dlc_disabled' not in game_versions[game_id]:
|
|
game_versions[game_id]['lan_dlc_disabled'] = []
|
|
|
|
|
|
dlcstats = {}
|
|
for dlcname in game_versions[game_id]['lan_dlcids']:
|
|
if dlcname in game_versions[game_id]['lan_dlc_disabled']:
|
|
dlcstats[dlcname] = False
|
|
else:
|
|
dlcstats[dlcname] = True
|
|
|
|
longest_name = 0
|
|
for dlcname in game_versions[game_id]['lan_dlcnames']:
|
|
if len(dlcname) > longest_name:
|
|
longest_name = len(dlcname)
|
|
|
|
longest_name += 2
|
|
|
|
|
|
items = []
|
|
for dlc, status in dlcstats.iteritems():
|
|
dlcname = game_versions[game_id]['lan_dlcnames'][
|
|
game_versions[game_id]['lan_dlcids'].index(dlc)]
|
|
one_item = []
|
|
if status:
|
|
one_item.append('%s (enabled)' % dlcname.ljust(longest_name))
|
|
else:
|
|
one_item.append('%s (disabled)'% dlcname.ljust(longest_name))
|
|
|
|
one_item.append('lan_toggle_dlc')
|
|
one_item.append([game_id, dlc])
|
|
|
|
if status:
|
|
one_item.append('Press ENTER to disable %s (%s)' % (dlcname, dlc))
|
|
else:
|
|
one_item.append('Press ENTER to enable %s (%s)' % (dlcname, dlc))
|
|
|
|
items.append(one_item)
|
|
|
|
items.sort()
|
|
|
|
return MenuMaker('DLC Select', items)
|
|
|
|
def GetLANSwitches(game_id):
|
|
if 'lan_dlc_disabled' not in game_versions[game_id]:
|
|
if 'switches' in game_versions[game_id]:
|
|
return game_versions[game_id]['switches']
|
|
else:
|
|
return []
|
|
|
|
if len(game_versions[game_id]['lan_dlc_disabled']) == 0:
|
|
if 'switches' in game_versions[game_id]:
|
|
return game_versions[game_id]['switches']
|
|
else:
|
|
return []
|
|
|
|
nodlc = '--nodlc %s'
|
|
dlc_str = ''
|
|
|
|
for dlc in game_versions[game_id]['lan_dlc_disabled']:
|
|
dlc_str += '%s ' % dlc
|
|
|
|
if 'switches' in game_versions[game_id]:
|
|
switches = copy.deepcopy(game_versions[game_id]['switches'])
|
|
else:
|
|
switches = []
|
|
|
|
switches.append(nodlc % dlc_str)
|
|
return switches
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def FindSocketPortPosition(game_id):
|
|
if 'switches' not in game_versions[game_id]:
|
|
return False
|
|
|
|
switches = game_versions[game_id].GetRawSetting('switches')
|
|
|
|
switchstart = switches.find('-socketport')
|
|
|
|
if switchstart == -1:
|
|
return False
|
|
|
|
eq_pos = switches[switchstart:].find('=') + switchstart
|
|
|
|
#remove whitespace, then find the end of the number
|
|
number_end = switches[eq_pos+1:].strip().find(',') + eq_pos+1
|
|
|
|
return [switchstart,number_end]
|
|
|
|
|
|
def ModifySocketPort(game_id, newport):
|
|
old_switches = game_versions[game_id].GetRawSetting('switches')
|
|
|
|
if FindSocketPortPosition(game_id) is False:
|
|
new_switches = old_switches + ', -socketport='+str(newport)
|
|
|
|
else:
|
|
pos = FindSocketPortPosition(game_id)
|
|
new_switches = (old_switches[:pos[0]] + ' -socketport='+str(newport)+
|
|
old_switches[pos[1]:])
|
|
|
|
game_versions[game_id]['switches'] = new_switches
|
|
|
|
|
|
def CurrentSocketPort(game_id):
|
|
if 'switches' not in game_versions[game_id]:
|
|
return 'Socketport Not Set'
|
|
|
|
if FindSocketPortPosition(game_id) is False:
|
|
return 'Socketport Not Set'
|
|
|
|
switches = game_versions[game_id].GetRawSetting('switches')
|
|
|
|
pos = FindSocketPortPosition(game_id)
|
|
pos[0] += len('-socketport')
|
|
|
|
return switches[pos[0]:pos[1]].strip(' = ')
|
|
|
|
|
|
def SetSocketPort(game_id):
|
|
current_tx = 'Current SocketPort:'
|
|
new_tx = 'Enter New SocketPort:'
|
|
|
|
print current_tx.rjust(len(new_tx)), CurrentSocketPort(game_id)
|
|
new_port = raw_input(new_tx+' ')
|
|
|
|
while not new_port.isdigit():
|
|
print 'Please only use numbers, not "%s"' %new_port
|
|
new_port = raw_input(new_tx+' ')
|
|
|
|
ModifySocketPort(game_id,new_port)
|
|
|
|
|
|
|
|
|
|
def GetSwitchesMenu(game_id, enable):
|
|
if '__static_switches__' not in game_id:
|
|
game_id['__static_switches__'] = game_id['switches']
|
|
|
|
if enable:
|
|
event = 'enable_switch'
|
|
switches_source = 'disabled_switches'
|
|
title = 'Enable Switch'
|
|
elif not enable:
|
|
event = 'disable_switch'
|
|
switches_source = 'switches'
|
|
title = 'Disable Switch'
|
|
|
|
|
|
items = []
|
|
for idx, switch in enumerate(game_id[switches_source]):
|
|
items.append([switch,
|
|
event,
|
|
[game_id.gid, idx],
|
|
'Temporarily remove the "%s" switch' % switch])
|
|
|
|
return MenuMaker(title, items)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def GetVersionNumber(game_id, local=True):
|
|
if 'cab_hash' in game_versions[game_id]:
|
|
#return HashVersion(game_id)
|
|
return 'CAB \\ PKG'
|
|
|
|
if local:
|
|
if 'version_file' not in game_versions[game_id]:
|
|
return 'Unknown'
|
|
else:
|
|
if 'sync_version_file' not in game_versions[game_id]:
|
|
return 'Unknown'
|
|
|
|
if local:
|
|
version_path = game_versions[game_id]['version_file']
|
|
else:
|
|
version_path = game_versions[game_id]['sync_version_file']
|
|
|
|
if not os.path.exists(version_path):
|
|
return 'Unknown'
|
|
|
|
|
|
v_file = open(version_path,'r')
|
|
|
|
store_next = False
|
|
version = 'Unknown'
|
|
for line in v_file:
|
|
|
|
if store_next:
|
|
version = line
|
|
break
|
|
|
|
#GTA IV condition:
|
|
if line.startswith('[VERSION_NUMBER]'):
|
|
store_next = True
|
|
|
|
|
|
#e1 / e2 condition
|
|
if line.startswith('[EPISODE_VERSION_NUMBER]'):
|
|
store_next = True
|
|
|
|
|
|
#rdr2 condition
|
|
if 'gametype' in game_versions[game_id]:
|
|
if game_versions[game_id]['gametype'] == 'rdr2':
|
|
return line.strip('\n')
|
|
|
|
#lan condition
|
|
if 'gametype' in game_versions[game_id]:
|
|
if game_versions[game_id]['gametype'] == 'LAN':
|
|
return line.strip('\r\n')
|
|
|
|
return version.strip('\n')
|
|
|
|
|
|
def VersionText(text,game_id):
|
|
version = GetVersionNumber(game_id)
|
|
|
|
if version == 'Unknown':
|
|
return ''
|
|
|
|
def HashVersion(game_id):
|
|
|
|
if 'cab_folder_source' not in game_versions[game_id]:
|
|
return 'Unknown'
|
|
|
|
UpdateHashes()
|
|
|
|
if game_versions[game_id]['cab_hash'] not in settings:
|
|
return 'Unknown'
|
|
|
|
local_hash = FileHash(game_versions[game_id]['cab_folder_source'])
|
|
|
|
if local_hash in settings[game_versions[game_id]['cab_hash']]:
|
|
return settings[game_versions[game_id]['cab_hash']][local_hash]
|
|
else:
|
|
return 'Unknown'
|
|
|
|
|
|
def IdentifyVersionHash(gid):
|
|
if 'cab_folder_source' in gid:
|
|
filename = gid['cab_folder_source']
|
|
elif 'ps3_pkg' in gid:
|
|
filename = gid['ps3_pkg']
|
|
else:
|
|
return False
|
|
|
|
UpdateHashes()
|
|
|
|
if 'cab_hash' not in gid:
|
|
return False
|
|
|
|
if gid['cab_hash'] not in settings:
|
|
return False
|
|
|
|
hasher = HashScreen(filename)
|
|
|
|
if hasher.hash in settings[gid['cab_hash']]:
|
|
return settings[gid['cab_hash']][hasher.hash]
|
|
else:
|
|
return False
|
|
|
|
|
|
|
|
|
|
class CommentBox:
|
|
def __init__(self):
|
|
self.comments = []
|
|
self.display_lines = 5
|
|
self.display_width = 75
|
|
self.display_position = 0
|
|
|
|
|
|
def __call__(self):
|
|
## cstring = ''
|
|
## for comment in self.comments:
|
|
## cstring += str(comment)+'\n'
|
|
## return cstring
|
|
return self.GetDisplayComments()
|
|
|
|
def AddComment(self,comment):
|
|
#self.comments.append(word_wrap(comment,self.display_width))
|
|
|
|
split = word_wrap(comment, self.display_width).strip('\n').split('\n')
|
|
|
|
self.comments.extend(split)
|
|
|
|
if len(self.comments) - 1 > self.display_lines:
|
|
self.display_position = (len(self.comments)) - self.display_lines
|
|
|
|
|
|
def GetDisplayComments(self):
|
|
if len(self.comments) < self.display_lines:
|
|
return self.FormatDisplayComments(self.comments)
|
|
|
|
start_line = self.display_position#len(self.comments) - self.display_lines
|
|
end_line = self.display_position + self.display_lines
|
|
return box_text(self.FormatDisplayComments(self.comments[start_line:end_line]))
|
|
|
|
|
|
|
|
def FormatDisplayComments(self, comments):
|
|
cstring = ''
|
|
for comment in comments:
|
|
cstring += str(comment)+'\n'
|
|
|
|
return cstring.strip('\n')
|
|
|
|
|
|
def CommentsBelow(self):
|
|
if len(self.comments) > self.display_position + self.display_lines:
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
def CommentsAbove(self):
|
|
if self.display_position is 0:
|
|
return False
|
|
else:
|
|
return True
|
|
|
|
def MoveUp(self):
|
|
if self.CommentsAbove():
|
|
self.display_position -= 1
|
|
|
|
def MoveDown(self):
|
|
if self.CommentsBelow():
|
|
self.display_position += 1
|
|
|
|
|
|
|
|
|
|
def CheckDumpEnv():
|
|
""" check for the rage_crashdump_dir environment variable, warn if it
|
|
isn't found. Then check for the directory it refers to and create it
|
|
if it isn't found """
|
|
|
|
rage_env = 'RAGE_CRASHDUMP_DIR'
|
|
|
|
dump_dir = os.getenv(rage_env)
|
|
|
|
if dump_dir == None:
|
|
print box_text('WARNING: Environment variable %s has not been set. '
|
|
'Dumps may not be created correctly. Please check '
|
|
'with your lead.' % rage_env)
|
|
print box_text('Press any key to continue')
|
|
wait_for_key()
|
|
return False
|
|
|
|
if not os.path.exists(dump_dir):
|
|
print 'Dump directory not found, attempting to create "%s"' % dump_dir
|
|
drive = os.path.splitdrive(dump_dir)[0]
|
|
if not drive == '':
|
|
if not os.path.exists(drive):
|
|
error_exit('Attempting to create %s, drive not found: "%s"'
|
|
% (rage_env,drive))
|
|
os.makedirs(dump_dir)
|
|
|
|
elif not os.path.isdir(dump_dir):
|
|
error_exit('The dump directory specified in %s appears to be a file.' %
|
|
rage_env)
|
|
|
|
|
|
|
|
|
|
def GIDtoString(gid):
|
|
|
|
final_string = '['+gid.gid+']\n'
|
|
|
|
for settingname in gid.ListSettings():
|
|
setting = gid.GetRawSetting(settingname)
|
|
|
|
if settingname in gid.lists:
|
|
line = 'LIST: '
|
|
else:
|
|
line = ''
|
|
|
|
#line += settingname + ' = ' + str(setting) + '\n'
|
|
line += '%s = %s\n' % (settingname, setting)
|
|
|
|
final_string += line
|
|
|
|
return final_string
|
|
|
|
|
|
def WriteAllGIDs(gid_holder,fname='allgids.ini'):
|
|
|
|
outfile = open(fname,'w')
|
|
|
|
for gid in gid_holder:
|
|
outfile.write(GIDtoString(gid))
|
|
outfile.write('\n\n\n')
|
|
|
|
outfile.close()
|
|
|
|
|
|
|
|
def ActiveProfiles():
|
|
target = 'hdd:\\Content\\'
|
|
|
|
folders = xbdir_list(target, directories = True)
|
|
|
|
profile_folders = []
|
|
|
|
for index, folder in enumerate(folders):
|
|
if folder[0] != '0000000000000000':
|
|
profile_folders.append(folder)
|
|
|
|
return profile_folders
|
|
|
|
|
|
def CountGTASaves(profile):
|
|
target = 'hdd:\\Content\\'
|
|
game = '545407f2'
|
|
|
|
profile = os.path.join(target, profile)
|
|
|
|
profile_folders = xbdir_list(profile, directories=True)
|
|
|
|
game_folders = []
|
|
for folder in profile_folders:
|
|
game_folders.append(folder[0])
|
|
|
|
if game.upper() not in game_folders:
|
|
return 0
|
|
|
|
save_folder = os.path.join(profile,game)
|
|
save_folder = os.path.join(save_folder,'00000001')
|
|
|
|
saves_found = 0
|
|
for fname in xbdir_list(save_folder):
|
|
if fname[0].startswith('SGTA'):
|
|
saves_found += 1
|
|
|
|
return saves_found
|
|
|
|
|
|
def SavesInProfiles():
|
|
""" return pretty string saying how many saves in how many profiles """
|
|
|
|
savesfound = 0
|
|
profilesfound = 0
|
|
|
|
for profile in ActiveProfiles():
|
|
profilesfound += 1
|
|
savesfound += CountGTASaves(profile[0])
|
|
|
|
if savesfound == 1:
|
|
saveword = 'save'
|
|
else:
|
|
saveword = 'saves'
|
|
|
|
if profilesfound == 1:
|
|
profileword = 'profile'
|
|
else:
|
|
profileword = 'profiles'
|
|
|
|
return '%s %s found in %s %s' % (savesfound,
|
|
saveword,
|
|
profilesfound,
|
|
profileword)
|
|
|
|
|
|
def XBCopyTree(source,
|
|
destination,
|
|
newer_only=False,
|
|
verbose=True):
|
|
"""copy all files in a directory and all it's subdirectories to the xbox"""
|
|
debug=False
|
|
|
|
logging('XBCopyTree called on "%s"' % source)
|
|
|
|
#see if we're looking for a wildcard
|
|
if os.path.basename(source).startswith('*.'):
|
|
if debug: print 'wildcard found'
|
|
wild_extension = os.path.basename(source).lstrip('*.')
|
|
wildcard = True
|
|
source = os.path.dirname(source)
|
|
if debug: print 'wildcard:',wild_extension
|
|
else:
|
|
wildcard = False
|
|
|
|
#exit if we can't find the source
|
|
if not os.path.exists(source):
|
|
error_exit('XBCopyTree called on a source that doesn\'t exist: "%s"'
|
|
% source)
|
|
|
|
#short-circuit to XBCopy if we're only copying a single file
|
|
if os.path.isfile(source):
|
|
if debug: print 'Copying a single file'
|
|
XBCopy(source, destination , newer_only=newer_only, verbose=verbose)
|
|
return True
|
|
|
|
#make sure our destination is a directory
|
|
if not destination.endswith('\\'):
|
|
destination += '\\'
|
|
if debug: print 'Appending \\ to destination'
|
|
|
|
#generate a list of source/destination pairs
|
|
transfer_list = []
|
|
for root, dirs, files in os.walk(source):
|
|
for filename in files:
|
|
if wildcard:
|
|
if not filename.endswith(wild_extension):
|
|
continue
|
|
transfer_item = [None,None]
|
|
transfer_item[0] = os.path.join(root,filename)
|
|
|
|
dest_path = root.replace(source,'').lstrip('\\')
|
|
if not dest_path == '':
|
|
dest_path += '\\'
|
|
|
|
if debug: print 'dest_path',dest_path
|
|
transfer_item[1] = destination + dest_path
|
|
|
|
if debug: print transfer_item
|
|
|
|
transfer_list.append(transfer_item)
|
|
|
|
#iterate through the list of files to transfer
|
|
for item in transfer_list:
|
|
XBCopy(item[0], item[1], newer_only=newer_only, verbose=verbose)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def XBCopy(source,
|
|
destination,
|
|
recursive=True,
|
|
newer_only=False,
|
|
verbose=True):
|
|
"""Copy a single file to the default xbox"""
|
|
debug=False
|
|
debug_log=False
|
|
|
|
if not destination.endswith('\\'):
|
|
destination += '\\'
|
|
|
|
logging('XBCopy: Copying "%s" to "%s"' % (source, destination))
|
|
|
|
if not os.path.exists(source):
|
|
error_exit('XBCopy called on a source that doesn\'t exist: "%s"'
|
|
% source)
|
|
|
|
if not os.path.isfile(source):
|
|
error_exit('XBCopy called on a source that isn\'t a file: "%s"' %source)
|
|
|
|
xdk_vars() #prepare to use xbox sdk
|
|
|
|
switches = ['/H','/T','/Y']
|
|
|
|
if recursive:
|
|
switches.append('/R')
|
|
|
|
if newer_only:
|
|
switches.append('/D')
|
|
|
|
#add the files to the end of the argument, source then dest
|
|
switches.append(source)
|
|
switches.append(destination)
|
|
|
|
|
|
full_args = ['xbcp.exe']
|
|
full_args.extend(switches)
|
|
|
|
if debug: print 'full_args:',full_args
|
|
|
|
if newer_only:
|
|
copy_message = 'Copying "%s" to "%s" only if source is newer'
|
|
else:
|
|
copy_message = 'Copying "%s" to "%s"'
|
|
|
|
if verbose: print word_wrap(copy_message %
|
|
(source, destination)).rstrip('\n '),
|
|
|
|
copier = subprocess.Popen(full_args,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE)
|
|
|
|
out = copier.communicate()
|
|
|
|
if debug_log: logging('err: '+out[1]+', stdout: '+out[0])
|
|
|
|
if len(out[1]) == 0:
|
|
if verbose: print '... success!'
|
|
logging('XBCopy: File copy appears successful')
|
|
else:
|
|
if verbose: print '... failure!'
|
|
err_string = out[1]
|
|
error_exit('XBCopy failed with this message: %s' % err_string)
|
|
|
|
if debug: print 'err:',out[1],'stdout:', out[0]
|
|
|
|
|
|
|
|
|
|
def XBDel(target):
|
|
""" Delete the target from the xbox """
|
|
|
|
args = ['xbdel.exe','/F','/R','/H',target]
|
|
|
|
print 'Deleting "%s" from Xbox' % target,
|
|
logging('XBDel: Deleting "%s"' % target)
|
|
|
|
deleter = subprocess.Popen(args,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE)
|
|
|
|
out = deleter.communicate()
|
|
|
|
if len(out[1]) == 0:
|
|
print '... success!'
|
|
return True
|
|
|
|
else:
|
|
if out[1].startswith('xbdel: No such file exists'):
|
|
print '... success!'
|
|
return True
|
|
|
|
elif 'does not exist' in out[1]:
|
|
print '... success!'
|
|
return True
|
|
|
|
|
|
print '... failure!'
|
|
error_exit('XBDel: Failed to erase "%s" from Xbox, because: %s'
|
|
% (target,out[1]))
|
|
|
|
|
|
|
|
def missiontime(statsfile='teeoff.pstats'):
|
|
try:
|
|
pstats = split_pstats(open(statsfile,'r').readline())
|
|
except IOError:
|
|
print 'ERROR: File not found'
|
|
wait_for_key()
|
|
return False
|
|
|
|
mission_name = ''
|
|
mission_end_time = -1
|
|
mission_start_time = -1
|
|
on_mission = False
|
|
|
|
time_in_cutscenes = 0
|
|
cutscene_start = 0
|
|
cutscene_end = 0
|
|
current_cut = ''
|
|
|
|
pause_start = 0
|
|
pause_end = 0
|
|
pause_time = 0
|
|
|
|
for position,stat in enumerate(reversed(pstats)):
|
|
if 'psid' in stat:
|
|
if stat['psid'] == 'MISSION_PASSED':
|
|
mission_name = stat['name']
|
|
mission_end_time = stat['time']
|
|
print '%s ended at %s' % (stat['name'],stat['time'])
|
|
mission_end_pos = position
|
|
on_mission = True
|
|
|
|
if mission_start_time == -1:
|
|
if stat['psid'] == 'MISSION_STARTED':
|
|
if stat['name'] == mission_name:
|
|
print '%s started at %s' % (stat['name'],stat['time'])
|
|
mission_start_time = stat['time']
|
|
mission_start_pos = position
|
|
on_mission = False
|
|
break
|
|
else:
|
|
print 'problem tracking mission, mission in progress?'
|
|
return False
|
|
|
|
if stat['psid'] == 'CUTSCENE_SKIPPED' or \
|
|
stat['psid'] == 'CUTSCENE_ENDED':
|
|
if cutscene_end == 0:
|
|
cutscene_end = int(stat['time'])
|
|
current_cut = stat['name']
|
|
|
|
if stat['psid'] == 'CUTSCENE_STARTED':
|
|
cutscene_start = int(stat['time'])
|
|
if cutscene_end < cutscene_start:
|
|
cutscene_end = 0
|
|
cutscene_start = 0
|
|
print 'problem tracking cutscene, end smaller than start'
|
|
elif current_cut != stat['name']:
|
|
print 'problem tracking cutscene, name change'
|
|
print ('thought i was tracking %s, now on %s' %
|
|
(current_cut, stat['name']))
|
|
else:
|
|
time_in_cutscenes += cutscene_end - cutscene_start
|
|
cutscene_end = 0
|
|
cutscene_start = 0
|
|
|
|
if on_mission:
|
|
if stat['psid'] == 'GAME_UNPAUSED':
|
|
#print 'game unpaused at %s' % stat['time']
|
|
pause_end = int(stat['time'])
|
|
|
|
if stat['psid'] == 'GAME_PAUSED':
|
|
#print 'game paused at %s' % stat['time']
|
|
pause_start = int(stat['time'])
|
|
|
|
if pause_end == 0:
|
|
print ('problem tracking pause time, pause without '
|
|
'unpause')
|
|
else:
|
|
pause_time += pause_end - pause_start
|
|
#print 'pause time now %s' % pause_time
|
|
|
|
|
|
|
|
if mission_end_time != -1 and mission_start_time != -1:
|
|
mission_time = int(mission_end_time) - int(mission_start_time)
|
|
print 'Mission Name:',mission_name
|
|
print 'Mission Time:',DisplayTime(mission_time)
|
|
print ' Cut Time:',DisplayTime(time_in_cutscenes)
|
|
print ' Pause Time:',DisplayTime(pause_time)
|
|
print '========================='
|
|
print 'Overall Time:',DisplayTime(mission_time-time_in_cutscenes-pause_time)
|
|
else:
|
|
print 'last mission not found'
|
|
wait_for_key()
|
|
return False
|
|
|
|
return pstats[mission_start_pos:mission_end_pos]
|
|
|
|
|
|
|
|
|
|
def ConvertTime(seconds):
|
|
hours = seconds / (60*60)
|
|
minutes = (seconds / 60) - (hours*60)
|
|
seconds = seconds - (hours*60*60) - (minutes*60)
|
|
|
|
return hours, minutes, seconds
|
|
|
|
|
|
|
|
|
|
def DisplayTime(seconds):
|
|
return '%02dh %02dm %02ds' % ConvertTime(seconds)
|
|
|
|
|
|
def FileHash(filename, blocksize=1024*8, verbose=False):
|
|
""" Generate a hash from the supplied file """
|
|
hasher = hashlib.md5()
|
|
|
|
logging('FileHash started, file: "%s", blocksize: "%s"' % (filename, blocksize))
|
|
|
|
if verbose: print 'Opening "%s" for hashing' % filename
|
|
|
|
|
|
|
|
#this is probably really really bad, accidentally left it on ascii instead
|
|
#of binary. seems to still be working at least for internal consistency
|
|
#but that is worrisome. binary hashing was slower by at least tenfold.
|
|
|
|
try:
|
|
hashfile = open(filename,'r')
|
|
except IOError:
|
|
logging('FileHash: Unable to open "%s" for hashing' % filename)
|
|
return False
|
|
|
|
while True:
|
|
data = hashfile.read(blocksize)
|
|
if not data:
|
|
break
|
|
hasher.update(data)
|
|
#print hasher.hexdigest()
|
|
|
|
## hasher.update(hashfile.read())
|
|
|
|
digest = hasher.hexdigest()
|
|
|
|
logging('FileHash complete: %s' % digest)
|
|
if verbose: print 'hash complete: %s' % digest
|
|
|
|
|
|
return digest
|
|
|
|
|
|
def UpdateHashes():
|
|
""" Update settings with cab hashes grabbed off the network """
|
|
|
|
if '-focus' in command_line_switches():
|
|
return False
|
|
|
|
for gid in settings.ListGIDs():
|
|
if gid.startswith('h_'):
|
|
del settings[gid]
|
|
|
|
if not os.path.exists(settings['defaults']['cab_hashes']):
|
|
return False
|
|
|
|
try:
|
|
settings.LoadFile(settings['defaults']['cab_hashes'])
|
|
except IOError:
|
|
logging('UpdateHashes: Error attempting to read %s' %
|
|
settings['defaults']['cab_hashes'])
|
|
return False
|
|
|
|
settings.DoStringReplacement(settings['variables'])
|
|
|
|
|
|
class XBDiscEmulator:
|
|
def __init__(self):
|
|
self.PID = ''
|
|
self.PIDmarker = 'ProcessId = '
|
|
self.exe = 'xbEmulate.exe'
|
|
|
|
|
|
def StartEmulator(self):
|
|
xdk_vars()
|
|
|
|
try:
|
|
emu = subprocess.Popen(self.exe,
|
|
stdin=subprocess.PIPE,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE)
|
|
except WindowsError:
|
|
error_exit('XBDiscEmulator: Error trying to load xbEmulate.exe. '
|
|
'Your Xbox SDK may require updating.')
|
|
|
|
emu_response = emu.communicate()
|
|
|
|
pid_pos = emu_response[0].find(self.PIDmarker)
|
|
|
|
if pid_pos == -1:
|
|
error_exit('XBDiscEmulator: Unable to determine process ID of disc '
|
|
'emulator')
|
|
|
|
self.PID = emu_response[0][pid_pos+len(self.PIDmarker):].strip('\n\r')
|
|
|
|
logging('XBDiscEmulator: Emulator started with PID %s' % self.PID)
|
|
|
|
|
|
def SendCommand(self,command):
|
|
args = [self.exe,'/nologo','/process',self.PID]
|
|
args.extend(command)
|
|
|
|
if self.PID == 0:
|
|
error_exit('XBDiscEmulator: Emulator has not been started. Emulator'
|
|
' must be started before commands can be sent to it.')
|
|
|
|
if not IsPIDRunning(self.PID):
|
|
error_exit('XBDiscEmulator: SendCommand tried to send a command '
|
|
'to a dead emulator')
|
|
|
|
try:
|
|
emu = subprocess.Popen(args,
|
|
stdin=subprocess.PIPE,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE)
|
|
except WindowsError:
|
|
error_exit('XBDiscEmulator: Error trying to load xbEmulate.exe. '
|
|
'Your Xbox SDK may require updating.')
|
|
|
|
#TODO: parse emu.communicate() to get errors and check em
|
|
#strange: errors don't seem to appear on stderr. thuddingly bad option:
|
|
emu_response = emu.communicate()
|
|
if 'ERROR' in emu_response[0]:
|
|
error_exit('XBDiscEmulator: MYSTERY ERROR: "%s"' % emu_response[0])
|
|
|
|
return emu_response
|
|
|
|
|
|
def LoadMedia(self,filename):
|
|
#todo: check for file before trying to use it
|
|
logging('XBDiscEmulator: Loading media from "%s"' % filename)
|
|
self.SendCommand(['/media',filename])
|
|
|
|
def GUIminimize(self):
|
|
self.SendCommand(['/minimize'])
|
|
|
|
def GUIrestore(self):
|
|
self.SendCommand(['/restore'])
|
|
|
|
def QueryEmulator(self):
|
|
result = self.SendCommand(['/query'])
|
|
split = result[0].split('\r\n')
|
|
|
|
results_dict = {}
|
|
for item in split:
|
|
if ':' in item:
|
|
item_split = item.split(':')
|
|
results_dict[item_split[0]] = item_split[1].strip()
|
|
|
|
return results_dict
|
|
|
|
def SetLogFile(self, filepath):
|
|
#todo: check for and erase logfile, etc
|
|
logging('XBDiscEmulator: Setting log file to "%s"' % filepath)
|
|
self.SendCommand(['/log',filepath])
|
|
|
|
def StartEmulation(self):
|
|
self.SendCommand(['/emulate','start'])
|
|
|
|
|
|
def LogFilename():
|
|
time_string = time.strftime('%Y%m%d_%H_%M_%S')
|
|
return 'emu_%s_%s.log' % (os.environ.get('username'), time_string)
|
|
|
|
|
|
def ScriptReport(report_file):
|
|
logging('ScriptReport: Generating report from %s' % report_file)
|
|
report = SettingsLoader()
|
|
report.LoadFile(report_file)
|
|
|
|
final_report = []
|
|
for script in report['default']['scripts']:
|
|
current_hash = FileHash(report[script]['location'])
|
|
script_name = 'Unknown'
|
|
for script_hash in report[script]['scripts']:
|
|
if current_hash == report[script][script_hash][1]:
|
|
script_name = report[script][script_hash][0]
|
|
|
|
|
|
final_report.append('%s: %s' % (report[script]['name'],script_name))
|
|
|
|
final_string = ''
|
|
for detail in final_report:
|
|
final_string += detail
|
|
final_string += '\n'
|
|
|
|
return final_string
|
|
|
|
|
|
|
|
# WHY: average time calculations seem wrong, dunno wwwwwhhhyyyyyyy
|
|
class CopyFile:
|
|
def __init__(self, source, destination):
|
|
#TODO: check source exists, destination writable
|
|
self.blocksize = 1024*1024
|
|
self.current_block = 0
|
|
self.time_copying = 0
|
|
|
|
self.destination = destination
|
|
|
|
self.filesize = int(os.stat(source).st_size)
|
|
self.bytes_copied = 0
|
|
|
|
self.src_stat = os.stat(source)
|
|
|
|
self.src_data = open(source, 'rb')
|
|
self.dst_data = open(destination, 'wb')
|
|
|
|
self.file_copied = False
|
|
|
|
self.speed = 0
|
|
self.all_speeds = 0
|
|
self.speed_samples = 0
|
|
self.average_speed = 0
|
|
|
|
def CopyNextBlock(self):
|
|
block_start = time.clock()
|
|
copy_buffer = self.src_data.read(self.blocksize)
|
|
if copy_buffer:
|
|
self.dst_data.write(copy_buffer)
|
|
self.current_block += 1
|
|
current_block_size = len(copy_buffer)
|
|
self.bytes_copied += current_block_size
|
|
block_stop = time.clock()
|
|
self.time_copying += (block_stop - block_start)
|
|
self.UpdateSpeed()
|
|
else:
|
|
if not self.file_copied:
|
|
self.CopyComplete()
|
|
|
|
def CopyComplete(self):
|
|
self.file_copied = True
|
|
self.src_data.close()
|
|
self.dst_data.close()
|
|
|
|
#set destination atime and mtime to sources
|
|
amtime = (self.src_stat.st_atime, self.src_stat.st_mtime)
|
|
os.utime(self.destination, amtime)
|
|
|
|
def OnAbort(self):
|
|
self.src_data.close()
|
|
self.dst_data.close()
|
|
|
|
|
|
def PercentageCopied(self):
|
|
try:
|
|
return (1.0 * self.bytes_copied / self.filesize) * 100
|
|
except ZeroDivisionError:
|
|
return 100
|
|
|
|
def UpdateSpeed(self):
|
|
## copy_time = stop - start
|
|
## print size
|
|
## print copy_time
|
|
## self.speed = 1.0*size / copy_time
|
|
## print self.speed
|
|
## self.all_speeds += self.speed
|
|
## self.speed_samples += 1
|
|
## raw_input()
|
|
#print self.bytes_copied, '/', self.time_copying
|
|
self.average_speed = 1.0*self.bytes_copied / self.time_copying
|
|
#print self.average_speed
|
|
#raw_input()
|
|
|
|
def AverageSpeed(self):
|
|
pass
|
|
|
|
|
|
|
|
class CopyScreen:
|
|
def __init__(self, source, destination):
|
|
self.copier = CopyFile(source, destination)
|
|
self.fname = os.path.basename(source)
|
|
self.start = time.clock()
|
|
self.elapsed = 0
|
|
|
|
while not self.copier.file_copied:
|
|
self.elapsed = time.clock() - self.start
|
|
self.UpdateScreen()
|
|
self.copier.CopyNextBlock()
|
|
|
|
def UpdateScreen(self):
|
|
percentage = self.copier.PercentageCopied()
|
|
#speed = self.copier.AverageSpeed() / (1024*1024)
|
|
|
|
os.system('cls')
|
|
|
|
print ' File: %s' % self.fname
|
|
print ' Progress: %d%%' % percentage
|
|
print ' Speed: %d MB\\s' % (self.copier.average_speed / (1024*1024))
|
|
print 'Time Elapsed: %d' % self.elapsed
|
|
print ' Block Speed: %d' % self.copier.speed
|
|
|
|
|
|
def PS3SetFSDir(directory):
|
|
""" use ps3run to set the fileserve directory on the default ps3 """
|
|
ps3run = subprocess.Popen(['ps3run','-f'+directory], stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE)
|
|
|
|
print ps3run.communicate()[0]
|
|
|
|
|
|
def PS3InstallPKG(gid):
|
|
""" remember: pass me a gid object, not a gid string """
|
|
|
|
to_install = []
|
|
|
|
if 'ps3_patch' in gid:
|
|
to_install.append(gid['ps3_patch'])
|
|
|
|
if 'ps3_pkg' in gid:
|
|
to_install.append(gid['ps3_pkg'])
|
|
|
|
pkg_installed = 0
|
|
for pkg in to_install:
|
|
if pkg.endswith('pkg'):
|
|
pkg_name = os.path.basename(pkg)
|
|
pkg_path = os.path.dirname(pkg)
|
|
else:
|
|
pkg_name = 'Unknown'
|
|
pkg_path = pkg
|
|
|
|
print word_wrap('Preparing to install package: %s' % pkg_name)
|
|
print word_wrap('Press any key to continue.')
|
|
|
|
wait_for_key()
|
|
|
|
PS3SetFSDir(pkg_path)
|
|
|
|
print word_wrap('From the XMB, please select "Game -> * Install Packag'
|
|
'e Files -> %s" and press any key to continue.' %
|
|
pkg_name)
|
|
|
|
wait_for_key()
|
|
|
|
print word_wrap('Please wait for installation to complete, then press '
|
|
'any key to continue.')
|
|
|
|
wait_for_key()
|
|
|
|
pkg_installed += 1
|
|
|
|
print word_wrap('%s of %s package(s) installed, press any key to '
|
|
'continue' % (pkg_installed, len(to_install)))
|
|
|
|
wait_for_key()
|
|
|
|
os.system('cls')
|
|
|
|
|
|
|
|
def ProgressBar(percent, width=74):
|
|
"""Print a progress bar!"""
|
|
|
|
#can't have more than 100%!
|
|
if percent > 100:
|
|
percent = 100
|
|
|
|
#or less than zero
|
|
if percent < 0:
|
|
percent = 0
|
|
|
|
#if the width is greater than 74, it's too big for the screen and messes up
|
|
if width > 74:
|
|
width = 74
|
|
|
|
one_box = 100.0 / width
|
|
boxes = percent / one_box
|
|
boxchar = chr(219)
|
|
|
|
#spacers = ' '*int(width-boxes)
|
|
bar = boxchar*int(boxes)
|
|
spacers = ' '*(width-len(bar))
|
|
|
|
return box_text(bar+spacers)
|
|
|
|
def RemoveDirectory(target):
|
|
if not os.path.exists(target):
|
|
return False
|
|
|
|
target_walk = os.walk(target, topdown=False)
|
|
|
|
for dirpath, dirnames, filenames in target_walk:
|
|
for fname in filenames:
|
|
os.remove(os.path.join(dirpath, fname))
|
|
|
|
os.rmdir(dirpath)
|
|
|
|
def FancyCopyFolder(source, destination):
|
|
if not os.path.exists(source):
|
|
return False
|
|
|
|
if not os.path.exists(destination):
|
|
os.makedirs(destination)
|
|
else:
|
|
RemoveDirectory(destination)
|
|
os.makedirs(destination)
|
|
|
|
file_list = []
|
|
destinations = []
|
|
for dirpath, dirnames, filenames in os.walk(source):
|
|
destination_folder = os.path.join(destination, dirpath.replace(source,'').strip('\\'))
|
|
if not os.path.exists(destination_folder):
|
|
os.mkdir(destination_folder)
|
|
for fname in filenames:
|
|
path_pair = [os.path.join(dirpath, fname),
|
|
os.path.join(destination_folder, fname)]
|
|
file_list.append(path_pair)
|
|
|
|
|
|
FancyCopyFiles2(file_list)
|
|
|
|
class FancyCopyFiles2:
|
|
def __init__(self, file_list, title='Copying Files'):
|
|
self.title = title
|
|
|
|
self.destination = ''
|
|
|
|
self.last_update = time.clock()
|
|
|
|
self.overall_percentage = 0
|
|
self.total_size = 0
|
|
self.total_bytes_copied = 0
|
|
self.total_files = len(file_list)
|
|
|
|
self.current_filename = ''
|
|
self.current_filenumber = 0
|
|
self.current_file_percentage = 0
|
|
|
|
for fname in file_list:
|
|
self.total_size += os.stat(fname[0]).st_size
|
|
|
|
for index, fname in enumerate(file_list):
|
|
self.destination = os.path.dirname(fname[0])
|
|
self.UpdateScreen(force=True)
|
|
self.current_filenumber = index+1
|
|
self.current_filename = os.path.basename(fname[0])
|
|
self.CopyFile(fname[0], fname[1])
|
|
try:
|
|
self.overall_percentage = (1.0 * self.total_bytes_copied /
|
|
self.total_size) * 100
|
|
except ZeroDivisionError:
|
|
self.overall_percentage = 100
|
|
self.UpdateScreen(force=True)
|
|
|
|
|
|
|
|
|
|
def CopyFile(self, fname, destination, overwrite=True):
|
|
self.UpdateScreen()
|
|
#destination = os.path.join(self.destination, os.path.basename(fname))
|
|
|
|
if os.path.exists(destination):
|
|
if overwrite:
|
|
os.remove(destination)
|
|
elif not overwrite:
|
|
destination = self.IncrementFilename(destination)
|
|
|
|
|
|
copier = CopyFile(fname, destination)
|
|
|
|
while not copier.file_copied:
|
|
copier.CopyNextBlock()
|
|
self.current_file_percentage = copier.PercentageCopied()
|
|
self.overall_percentage = (1.0 * (self.total_bytes_copied +
|
|
copier.bytes_copied) /
|
|
self.total_size) * 100
|
|
self.UpdateScreen()
|
|
|
|
self.total_bytes_copied += copier.bytes_copied
|
|
|
|
|
|
def IncrementFilename(self, dest):
|
|
final_dest = dest
|
|
increment = 0
|
|
while os.path.exists(final_dest):
|
|
increment += 1
|
|
fname = os.path.splitext(os.path.basename(dest))
|
|
new_fname = '%s (%s)%s' % (fname[0], increment, fname[1])
|
|
final_dest = os.path.join(os.path.dirname(dest), new_fname)
|
|
|
|
return final_dest
|
|
|
|
|
|
|
|
def UpdateScreen(self, force=False):
|
|
if (1.0* time.clock()) - self.last_update < 0.5:
|
|
if not force:
|
|
return False
|
|
|
|
os.system('cls')
|
|
print box_text(self.title,1)
|
|
|
|
print '\n'
|
|
|
|
print 'Copying File %s of %s to "%s"' % (self.current_filenumber,
|
|
self.total_files,
|
|
self.destination)
|
|
print ProgressBar(self.overall_percentage)
|
|
print '\n'
|
|
|
|
print 'Copying "%s"' % self.current_filename
|
|
print ProgressBar(self.current_file_percentage)
|
|
|
|
self.last_update = time.clock()
|
|
|
|
|
|
|
|
|
|
|
|
class FancyCopyFiles:
|
|
def __init__(self, file_list, destination, overwrite=False,
|
|
title='Upload Logs'):
|
|
self.title = title
|
|
self.destination = destination
|
|
|
|
self.last_update = time.clock()
|
|
|
|
self.overall_percentage = 0
|
|
self.total_size = 0
|
|
self.total_bytes_copied = 0
|
|
self.total_files = len(file_list)
|
|
|
|
self.current_filename = ''
|
|
self.current_filenumber = 0
|
|
self.current_file_percentage = 0
|
|
|
|
for fname in file_list:
|
|
self.total_size += os.stat(fname).st_size
|
|
|
|
for index, fname in enumerate(file_list):
|
|
self.UpdateScreen(force=True)
|
|
self.current_filenumber = index+1
|
|
self.current_filename = os.path.basename(fname)
|
|
self.CopyFile(fname, overwrite)
|
|
try:
|
|
self.overall_percentage = (1.0 * self.total_bytes_copied /
|
|
self.total_size) * 100
|
|
except ZeroDivisionError:
|
|
self.overall_percentage = 100
|
|
self.UpdateScreen(force=True)
|
|
|
|
|
|
|
|
|
|
def CopyFile(self, fname, overwrite=False):
|
|
self.UpdateScreen()
|
|
destination = os.path.join(self.destination, os.path.basename(fname))
|
|
|
|
if os.path.exists(destination):
|
|
if overwrite:
|
|
os.remove(destination)
|
|
elif not overwrite:
|
|
destination = self.IncrementFilename(destination)
|
|
|
|
|
|
copier = CopyFile(fname, destination)
|
|
|
|
while not copier.file_copied:
|
|
copier.CopyNextBlock()
|
|
self.current_file_percentage = copier.PercentageCopied()
|
|
self.overall_percentage = (1.0 * (self.total_bytes_copied +
|
|
copier.bytes_copied) /
|
|
self.total_size) * 100
|
|
self.UpdateScreen()
|
|
|
|
self.total_bytes_copied += copier.bytes_copied
|
|
|
|
|
|
def IncrementFilename(self, dest):
|
|
final_dest = dest
|
|
increment = 0
|
|
while os.path.exists(final_dest):
|
|
increment += 1
|
|
fname = os.path.splitext(os.path.basename(dest))
|
|
new_fname = '%s (%s)%s' % (fname[0], increment, fname[1])
|
|
final_dest = os.path.join(os.path.dirname(dest), new_fname)
|
|
|
|
return final_dest
|
|
|
|
|
|
|
|
def UpdateScreen(self, force=False):
|
|
if (1.0* time.clock()) - self.last_update < 0.5:
|
|
if not force:
|
|
return False
|
|
|
|
os.system('cls')
|
|
print box_text(self.title,1)
|
|
|
|
print '\n'
|
|
|
|
print 'Copying File %s of %s to "%s"' % (self.current_filenumber,
|
|
self.total_files,
|
|
self.destination)
|
|
print ProgressBar(self.overall_percentage)
|
|
print '\n'
|
|
|
|
print 'Copying "%s"' % self.current_filename
|
|
print ProgressBar(self.current_file_percentage)
|
|
|
|
self.last_update = time.clock()
|
|
|
|
|
|
def DynamicProgramFiles(settings):
|
|
""" replace %programfiles% with the env value """
|
|
programfiles = os.environ.get('programfiles')
|
|
programfiles86 = os.environ.get('PROGRAMFILES(X86)')
|
|
|
|
for setting in settings['defaults']:
|
|
if settings['defaults'].GetRawSetting(setting).startswith('%programfiles%'):
|
|
raw_setting = settings['defaults'].GetRawSetting(setting)
|
|
new_setting = '%s\\%s' % (programfiles,
|
|
raw_setting[15:])
|
|
logging('DynamicProgramFiles: Setting %s to %s' % (
|
|
setting, new_setting))
|
|
|
|
if programfiles86 != None:
|
|
if not os.path.exists(new_setting):
|
|
new_setting86 = '%s\\%s' % (programfiles86,
|
|
raw_setting[15:])
|
|
logging('DynamicProgramFiles: %s not found, setting to %s' % (
|
|
new_setting, new_setting86))
|
|
new_setting = new_setting86
|
|
|
|
settings['defaults'][setting] = new_setting
|
|
|
|
|
|
|
|
class MD5Hash:
|
|
def __init__(self, filename):
|
|
self.filename = filename
|
|
self.blocksize = 8192
|
|
self.bytes_hashed = 0
|
|
|
|
self.hasher = hashlib.md5()
|
|
try:
|
|
self.data = open(filename, 'rb')
|
|
except IOError, WindowsError:
|
|
error_exit('MD5Hash: IOError when trying to open file for '
|
|
'hashing: "%s"' % filename)
|
|
self.file_hashed = False
|
|
self.total_bytes = int(os.stat(filename).st_size)
|
|
|
|
self.start_time = time.clock()
|
|
self.stop_time = -1
|
|
|
|
|
|
def NextBlock(self):
|
|
if self.file_hashed:
|
|
return False
|
|
|
|
hash_buffer = self.data.read(self.blocksize)
|
|
if hash_buffer:
|
|
self.hasher.update(hash_buffer)
|
|
self.bytes_hashed += len(hash_buffer)
|
|
else:
|
|
if not self.file_hashed:
|
|
self.HashComplete()
|
|
|
|
|
|
def HashComplete(self):
|
|
self.file_hashed = True
|
|
self.data.close()
|
|
self.stop_time = time.clock()
|
|
logging('MD5Hash: Hashing performed on "%s". Result: %s' % (
|
|
self.filename,
|
|
self.FileHash()))
|
|
|
|
def PercentageHashed(self):
|
|
return (1.0 * self.bytes_hashed / self.total_bytes) * 100
|
|
|
|
def FileHash(self):
|
|
return self.hasher.hexdigest()
|
|
|
|
|
|
class HashScreen:
|
|
def __init__(self, filename):
|
|
self.filename = filename
|
|
|
|
self.hasher = MD5Hash(filename)
|
|
self.last_update = time.clock()
|
|
|
|
self.hash = None
|
|
|
|
self.HashFile()
|
|
self.UpdateScreen(force=True)
|
|
self.hash = self.hasher.FileHash()
|
|
|
|
|
|
def HashFile(self):
|
|
self.UpdateScreen()
|
|
|
|
while not self.hasher.file_hashed:
|
|
self.hasher.NextBlock()
|
|
self.UpdateScreen()
|
|
|
|
|
|
def UpdateScreen(self, force=False):
|
|
if (1.0 * time.clock()) - self.last_update < 0.5:
|
|
if not force:
|
|
return False
|
|
|
|
os.system('cls')
|
|
print box_text('Generate MD5 Hash',1)
|
|
|
|
print '\n'
|
|
|
|
print 'Generating MD5 Hash of "%s"' % os.path.basename(self.filename)
|
|
|
|
print ProgressBar(self.hasher.PercentageHashed())
|
|
print 'Time Elapsed: %sh %sm %ss' % (
|
|
ConvertTime(int(time.clock() - self.hasher.start_time)))
|
|
|
|
self.last_update = time.clock()
|
|
|
|
|
|
def IdentifyVersionScreen(gid):
|
|
start_time = time.clock()
|
|
version = IdentifyVersionHash(gid)
|
|
stop_time = time.clock()
|
|
|
|
if version:
|
|
print '\nVersion is:\n %s\n' % version
|
|
else:
|
|
print '\nUnable to identify version'
|
|
|
|
print '\nPress any key to return'
|
|
|
|
wait_for_key()
|
|
|
|
|
|
#=======================================================
|
|
class OrderGen:
|
|
def __init__(self, missions, use_flow):
|
|
self.missions = missions
|
|
self.use_flow = use_flow
|
|
print 'ordergen started, use flow: %s' % use_flow
|
|
|
|
self.played = []
|
|
self.available = []
|
|
self.remaining = []
|
|
|
|
self.needs = {}
|
|
|
|
self.order_digits = []
|
|
|
|
self.valid_path = True
|
|
|
|
self.walking_path = False
|
|
self.path_to_walk = []
|
|
|
|
#initialisation
|
|
to_ignore = ['info']
|
|
if 'info' in self.missions:
|
|
if 'to_ignore' in self.missions['info']:
|
|
for ignore_id in self.missions['info']['to_ignore']:
|
|
to_ignore.append(ignore_id)
|
|
|
|
print 'ignore list:', str(to_ignore)
|
|
|
|
for gid in self.missions:
|
|
if gid.gid in to_ignore:
|
|
continue
|
|
if 'category' not in gid:
|
|
gid['category'] = 'uncategorised'
|
|
|
|
if 'available' in gid:
|
|
if gid['available'] == 'yes':
|
|
self.available.append(gid.gid)
|
|
else:
|
|
self.remaining.append(gid.gid)
|
|
else:
|
|
self.remaining.append(gid.gid)
|
|
|
|
if use_flow:
|
|
print 'using flow'
|
|
self.GenerateNeeds()
|
|
|
|
else:
|
|
print 'ignoring flow'
|
|
self.available.extend(self.remaining)
|
|
self.remaining = []
|
|
|
|
for gid in self.available:
|
|
self.needs[gid] = []
|
|
|
|
|
|
self.PopulateAvailableMissions()
|
|
|
|
|
|
def PopulateAvailableMissions(self):
|
|
self.available_missions = []
|
|
for mission_id in self.available:
|
|
self.available_missions.append(self.missions[mission_id])
|
|
|
|
def GetAvailableMissions(self):
|
|
## start = time.clock()
|
|
## missions = []
|
|
## for mission_id in self.available:
|
|
## missions.append(self.missions[mission_id])
|
|
##
|
|
## end = time.clock()
|
|
## print 'GetAvailableMissions() completed in %s' % (end-start)
|
|
return self.available_missions
|
|
|
|
def PopMission(self, mission_id):
|
|
for key, mission in enumerate(self.available_missions):
|
|
if mission.gid == mission_id:
|
|
self.available_missions.pop(key)
|
|
return True
|
|
print 'PopMission couldn\'t find mission'
|
|
|
|
|
|
|
|
def CountAvailableMissions(self):
|
|
return len(self.available)
|
|
|
|
def CountMissionsLeft(self):
|
|
return len(self.remaining)
|
|
|
|
def GenerateNeeds(self):
|
|
for gid in self.missions:
|
|
if 'unlocks' in gid:
|
|
for mission_id in gid['unlocks']:
|
|
#print '%s needs %s' % (mission_id, gid.gid)
|
|
if mission_id not in self.needs:
|
|
self.needs[mission_id] = []
|
|
self.needs[mission_id].append(gid.gid)
|
|
|
|
def ChooseNextMission(self, choice=-1):
|
|
time_start = time.clock()
|
|
if self.CountAvailableMissions() == 0:
|
|
if self.CountMissionsLeft() == 0:
|
|
error_exit('ChooseNextMission() called with no missions left')
|
|
else:
|
|
self.DumpUnusedMissions()
|
|
error_exit('Missions are left, but none are available. This may'
|
|
'be caused by bad mission data.')
|
|
|
|
if choice == -1:
|
|
next_mission_id = random.randint(0, self.CountAvailableMissions()-1)
|
|
else:
|
|
next_mission_id = choice
|
|
|
|
|
|
try:
|
|
mission_selected = self.available.pop(next_mission_id)
|
|
except IndexError:
|
|
self.valid_path = False
|
|
self.available = []
|
|
self.remaining = []
|
|
print ('tried to choose mission %s but only %s missions are '
|
|
'available' % (
|
|
choice,
|
|
self.CountAvailableMissions()))
|
|
return False
|
|
|
|
print 'mission selected:', next_mission_id, mission_selected
|
|
self.PopMission(mission_selected)
|
|
self.order_digits.append(next_mission_id)
|
|
self.played.append(mission_selected)
|
|
time_end = time.clock()
|
|
# print 'ChooseNextMission() completed in %s' % (time_end-time_start)
|
|
|
|
start = time.clock()
|
|
self.UnlockMissions(mission_selected)
|
|
end = time.clock()
|
|
# print 'UnlockMissions() completed in %s' % (end-start)
|
|
|
|
def UnlockMissions(self, mission_selected):
|
|
selected_gid = self.missions[mission_selected]
|
|
if 'unlocks' not in selected_gid:
|
|
return False
|
|
|
|
if not self.use_flow:
|
|
return False
|
|
|
|
for unlock_id in selected_gid['unlocks']:
|
|
#check to see if all the requirements for a mission have been
|
|
#fulfilled
|
|
needs_met = 0
|
|
for need in self.needs[unlock_id]:
|
|
if need in self.played:
|
|
needs_met += 1
|
|
|
|
if needs_met == len(self.needs[unlock_id]):
|
|
print 'All requirements for %s met, unlocking' % unlock_id
|
|
self.available.append(unlock_id)
|
|
self.remaining.remove(unlock_id)
|
|
self.available_missions.append(self.missions[unlock_id])
|
|
|
|
|
|
def DumpUnusedMissions(self):
|
|
out = open('gen_dump.txt', 'w')
|
|
|
|
out.write('Missions Available (%s)\n' % len(self.available))
|
|
for m in self.available:
|
|
out.write('%s\n' % m)
|
|
|
|
out.write('\nRemaining Missions (%s)\n' % len(self.remaining))
|
|
for m in self.remaining:
|
|
out.write('%s\n' % m)
|
|
|
|
out.write('\nDumping Needs:\n')
|
|
for key, value in self.needs.iteritems():
|
|
out.write('[%s]\n'
|
|
'%s\n\n' % (key, value))
|
|
|
|
out.close()
|
|
|
|
|
|
|
|
|
|
|
|
def GetSignature(self):
|
|
sig_list = []
|
|
for num in self.order_digits:
|
|
sig_list.append(self.NumToAlpha(num))
|
|
|
|
sig = ''
|
|
last_element = ''
|
|
recur = 0
|
|
for element in sig_list:
|
|
if element == last_element:
|
|
recur += 1
|
|
elif element != last_element:
|
|
if recur != 0:
|
|
sig += '%s' % recur
|
|
recur = 0
|
|
last_element = element
|
|
sig += element
|
|
if recur != 0:
|
|
sig += '%s' % recur
|
|
|
|
#selfcheck
|
|
sig_len = len(sig)
|
|
sig = '%sl%s' % (sig_len, sig)
|
|
|
|
return sig
|
|
|
|
|
|
def NumToAlpha(self, num):
|
|
sep = '|'
|
|
alpha = string.ascii_letters
|
|
max_num = len(string.ascii_letters) - 1
|
|
repeats = 0
|
|
|
|
if num > max_num:
|
|
repeats = int(num / max_num)
|
|
num = num - (repeats*max_num)
|
|
#print repeats, num
|
|
|
|
|
|
if repeats == 0:
|
|
final_alpha = '%s' % alpha[num]
|
|
else:
|
|
final_alpha = '%s%s%s' % (sep,
|
|
repeats,
|
|
alpha[num])
|
|
|
|
return final_alpha
|
|
|
|
|
|
def AlphaToNum(self, let):
|
|
sep = '|'
|
|
alpha = string.ascii_letters
|
|
max_num = len(string.ascii_letters) - 1
|
|
|
|
if '|' not in let:
|
|
if len(let) == 1:
|
|
return alpha.find(let)
|
|
else:
|
|
return False
|
|
|
|
if not let.startswith('|'):
|
|
return False
|
|
|
|
#letter = let[-1]
|
|
#number = let[1:-1]
|
|
|
|
if not let[-1].isalpha():
|
|
return False
|
|
|
|
if not let[1:-1].isdigit():
|
|
return False
|
|
|
|
#print 'max_num', int(let[1:-1])*max_num
|
|
#print 'let',let[1:-1], alpha.find(let[1:-1])
|
|
|
|
return (int(let[1:-1])*max_num) + (alpha.find(let[-1]))
|
|
|
|
|
|
|
|
def IsSignatureValid(self, sig):
|
|
l_pos = sig.find('l')
|
|
if l_pos == -1:
|
|
return False
|
|
|
|
sig_len = sig[:l_pos]
|
|
|
|
if not sig_len.isdigit():
|
|
return False
|
|
else:
|
|
sig_len = int(sig_len)
|
|
|
|
sig_core = sig[l_pos+1:]
|
|
|
|
if len(sig_core) != sig_len:
|
|
return False
|
|
else:
|
|
return True
|
|
|
|
def GetCurrentOrderString(self):
|
|
final_string = ''
|
|
for mission in self.played:
|
|
final_string += '%s\n' % self.missions[mission]['name']
|
|
|
|
return final_string
|
|
|
|
def ContainsAlpha(self, current):
|
|
for char in current:
|
|
if char.isalpha():
|
|
return True
|
|
|
|
return False
|
|
|
|
def ConvertSignature(self, sig):
|
|
if not self.IsSignatureValid(sig):
|
|
return False
|
|
|
|
sig_core = sig[sig.find('l')+1:]
|
|
|
|
split_sig = []
|
|
|
|
current = ''
|
|
|
|
for char in sig_core:
|
|
if char.isalpha() and current != '':
|
|
if current[-1].isalpha() or current[0].isalpha():
|
|
split_sig.append(current)
|
|
current = ''
|
|
|
|
if self.ContainsAlpha(current):
|
|
split_sig.append(current)
|
|
current = ''
|
|
|
|
|
|
if char == '|':
|
|
if current != '':
|
|
split_sig.append(current)
|
|
current = ''
|
|
|
|
current += char
|
|
|
|
split_sig.append(current)
|
|
|
|
#return split_sig
|
|
|
|
order_digits = []
|
|
|
|
for num in split_sig:
|
|
print num
|
|
if num[-1].isdigit():
|
|
char = num[-1]
|
|
i = -1
|
|
while char.isdigit():
|
|
i -= 1
|
|
char = num[i]
|
|
i+=1
|
|
|
|
#print num[i:] <--repeats
|
|
#print num[:i] <--value
|
|
|
|
for rep in range(0, int(num[i:])+1):
|
|
order_digits.append(self.AlphaToNum(num[:i]))
|
|
|
|
else:
|
|
order_digits.append(self.AlphaToNum(num))
|
|
|
|
|
|
return order_digits
|
|
|
|
|
|
def RunSignature(self, sig):
|
|
if sig == '0l':
|
|
return False
|
|
order = self.ConvertSignature(sig)
|
|
for order_num in order:
|
|
self.ChooseNextMission(order_num)
|
|
|
|
def LoadPath(self, sig):
|
|
if sig == '0l':
|
|
return False
|
|
self.path_to_walk = self.ConvertSignature(sig)
|
|
self.walk_length = len(self.path_to_walk)
|
|
self.walking_path = True
|
|
|
|
def TakeStep(self):
|
|
if not self.walking_path:
|
|
error_exit('OrderGen: TakeStep called when not walking path')
|
|
|
|
if len(self.path_to_walk) == 0:
|
|
error_exit('OrderGen: TakeStep called when no steps left to take')
|
|
|
|
self.ChooseNextMission(self.path_to_walk.pop(0))
|
|
|
|
if len(self.path_to_walk) == 0:
|
|
self.walking_path = False
|
|
|
|
return True
|
|
|
|
|
|
def PathProgress(self):
|
|
## print len(self.path_to_walk), self.walk_length
|
|
## print (1.0 * len(self.path_to_walk) / self.walk_length)
|
|
return 100 - (1.0*len(self.path_to_walk) / self.walk_length) * 100
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def GetCategories(self):
|
|
cats = []
|
|
for item in self.missions:
|
|
if 'category' in item:
|
|
if item['category'] not in cats:
|
|
cats.append(item['category'])
|
|
|
|
return cats
|
|
|
|
|
|
def CountCategory(self, category):
|
|
done = 0
|
|
todo = 0
|
|
total = 0 #for a little bit of error checking
|
|
|
|
for item in self.missions:
|
|
if 'category' in item:
|
|
if item['category'] == category:
|
|
total += 1
|
|
if item.gid in self.played:
|
|
done += 1
|
|
elif item.gid in self.remaining:
|
|
todo += 1
|
|
## print 'done',done
|
|
## print 'todo',todo
|
|
## print 'calc',done+todo
|
|
## print 'total',total
|
|
|
|
return [done, total]
|
|
|
|
|
|
def CountCategories(self):
|
|
counts = {}
|
|
|
|
for item in self.missions:
|
|
if 'category' in item:
|
|
if item['category'] not in counts:
|
|
counts[item['category']] = [0,0,0]
|
|
counts[item['category']][1] += 1
|
|
if item.gid in self.played:
|
|
counts[item['category']][0] += 1
|
|
elif item.gid in self.remaining:
|
|
counts[item['category']][2] += 1
|
|
|
|
return counts
|
|
|
|
def GetOrderRange(self, object_id):
|
|
if not self.use_flow:
|
|
return [0, len(self.played)]
|
|
|
|
earliest = 0
|
|
if object_id in self.needs:
|
|
needs_ids = self.needs[object_id]
|
|
for need in needs_ids:
|
|
need_pos = self.played.index(need)
|
|
if need_pos+1> earliest:
|
|
earliest = need_pos + 1
|
|
|
|
|
|
latest = len(self.played) - 1
|
|
if 'unlocks' in self.missions[object_id]:
|
|
for unlock in self.missions[object_id]['unlocks']:
|
|
if unlock not in self.played:
|
|
continue
|
|
|
|
if self.played.index(unlock)- 1 < latest:
|
|
latest = self.played.index(unlock) - 1
|
|
|
|
return [earliest, latest]
|
|
|
|
|
|
|
|
|
|
|
|
def GetIVxex(gid):
|
|
if 'xex' not in gid:
|
|
error_exit('GetIVxex() called on non-xbox game id')
|
|
|
|
if 'skip_get_xex' in gid:
|
|
logging('%s requested skip GetIVxex()' % gid.gid)
|
|
return True
|
|
|
|
logging('GetIVxex called on %s' % gid.gid)
|
|
|
|
build_dir = os.path.dirname(gid['xex'])
|
|
xex_dir = settings['defaults']['iv_xex']
|
|
|
|
files = directory_list(xex_dir)
|
|
|
|
full_paths = []
|
|
for fname in files:
|
|
full_paths.append(os.path.join(xex_dir, fname))
|
|
|
|
FancyCopyFiles(full_paths, build_dir, overwrite=True,
|
|
title='Copy XEX files')
|
|
|
|
|
|
def RARfile(files, rardest):
|
|
rarpath = settings['defaults']['rar']
|
|
|
|
if not os.path.exists(rarpath):
|
|
error_exit('RARfile: path to rar.exe is not valid, please check the'
|
|
' value of "rar" in settings.ini, and that you have winrar '
|
|
'installed.')
|
|
|
|
#create file list textfile in temp
|
|
flist_path = os.path.join(os.getenv('temp'),'flist.txt')
|
|
flist = open(flist_path, 'w')
|
|
for path in files:
|
|
if os.path.exists(path):
|
|
flist.write('%s\n' % path)
|
|
else:
|
|
logging('RARfile: File not found: "%s", skipping.' % path)
|
|
flist.close()
|
|
|
|
#command is 'rar a rardest @flist'
|
|
args = ' a "%s" @"%s"' % (rardest, flist_path)
|
|
|
|
|
|
rarproc = subprocess.Popen(rarpath+args)
|
|
rarproc.wait()
|
|
|
|
|
|
|
|
|
|
def FindPS3Dumps(sources):
|
|
dump_paths = []
|
|
for source in sources:
|
|
for fname in os.listdir(source):
|
|
if fname.startswith('ps3core'):
|
|
dump_paths.append(os.path.join(source, fname))
|
|
|
|
return dump_paths
|
|
|
|
|
|
def SetPS3Home(gid):
|
|
if 'set_home' in gid:
|
|
homedir = gid['set_home']
|
|
else:
|
|
error_exit('SetPS3Home called on a gid with no "set_home" setting')
|
|
|
|
ps3run = os.path.join(os.path.join(os.getenv('SN_PS3_PATH'),'bin'),
|
|
'ps3run.exe')
|
|
|
|
ps3run = '"'+ps3run+'"'
|
|
|
|
os.system(ps3run+' -f '+homedir)
|
|
os.system(ps3run+' -h '+homedir)
|
|
|
|
|
|
def OneWeekChoice(evt_type):
|
|
items = []
|
|
|
|
if evt_type == 'place_order':
|
|
help_text = 'Place order for %s%s'
|
|
elif evt_type == 'view_orders':
|
|
help_text = 'View orders for %s%s'
|
|
|
|
for i in range(0, 6):
|
|
o_time_s = time.time() + i * (24*60*60)
|
|
o_time = time.localtime(o_time_s)
|
|
date_str = time.strftime('%A %d', o_time)
|
|
|
|
items.append(['%s%s' % (date_str, GetNumberSuffix(int(date_str[-1]))),
|
|
evt_type,
|
|
o_time_s,
|
|
help_text % (date_str,
|
|
GetNumberSuffix(int(date_str[-1])))])
|
|
|
|
return MenuMaker('Select Order Date', items)
|
|
|
|
def GetNumberSuffix(num):
|
|
if num == 0:
|
|
return 'th'
|
|
elif num == 1:
|
|
return 'st'
|
|
elif num == 2:
|
|
return 'nd'
|
|
elif num == 3:
|
|
return 'rd'
|
|
elif num > 3 and num < 10:
|
|
return 'th'
|
|
else:
|
|
return ''
|
|
|
|
def CheckOrderLock(o_time_s):
|
|
o_time = time.localtime(o_time_s)
|
|
orderlock_path = os.path.join(settings['defaults']['food_folder'],
|
|
time.strftime('%Y-%m-%d', o_time))
|
|
if os.path.exists(os.path.join(orderlock_path, 'order_lock.txt')):
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
def WriteOrder(order, o_time_s=None):
|
|
dest = settings['defaults']['food_folder']
|
|
|
|
fname = '%s.txt' % os.environ.get('username')
|
|
|
|
if o_time_s == None:
|
|
o_time = time.localtime()
|
|
else:
|
|
o_time = time.localtime(o_time_s)
|
|
|
|
final_dest = os.path.join(dest, time.strftime('%Y-%m-%d', o_time))
|
|
|
|
if not os.path.exists(final_dest):
|
|
os.makedirs(final_dest)
|
|
|
|
out_file = open(os.path.join(final_dest, fname), 'w')
|
|
out_file.write(order)
|
|
out_file.close()
|
|
|
|
in_file = open(os.path.join(final_dest, fname), 'r')
|
|
|
|
print '\nOrder written to network:'
|
|
for line in in_file:
|
|
print line
|
|
|
|
in_file.close()
|
|
|
|
|
|
|
|
def TakeOrder(o_time_s=None):
|
|
os.system('cls')
|
|
|
|
if o_time_s == None:
|
|
o_time = time.localtime()
|
|
o_time_s = time.time()
|
|
else:
|
|
o_time = time.localtime(o_time_s)
|
|
|
|
dest = settings['defaults']['food_folder']
|
|
dest_folder = os.path.join(dest, time.strftime('%Y-%m-%d', o_time))
|
|
final_dest = os.path.join(dest_folder, '%s.txt' % os.environ.get('username'))
|
|
|
|
if os.path.exists(os.path.join(dest_folder, 'order_lock.txt')):
|
|
print box_text('Ordering for this date has been locked, '
|
|
'please contact a lead to submit your order.\n\n'
|
|
'Press any key to continue')
|
|
wait_for_key()
|
|
return False
|
|
|
|
print 'Food-O-Tron 9000'
|
|
print '================\n'
|
|
|
|
if os.path.exists(final_dest):
|
|
print 'You have already made an order for %s! It was:\n' % (
|
|
time.strftime('%Y-%m-%d', o_time))
|
|
|
|
infile = open(final_dest, 'r')
|
|
for line in infile:
|
|
print line
|
|
infile.close()
|
|
|
|
print
|
|
|
|
print 'Enter your new order, or press enter to leave your order unchanged.'
|
|
|
|
else:
|
|
print 'Enter your order for %s%s.' % (
|
|
time.strftime('%A %d', o_time),
|
|
GetNumberSuffix(int(time.strftime('%A %d', o_time)[-1])))
|
|
|
|
print
|
|
order = raw_input('ORDER: ')
|
|
|
|
if os.path.exists(final_dest) and order == '':
|
|
print '\nOrder update cancelled, press any key to return.'
|
|
wait_for_key()
|
|
return True
|
|
|
|
elif order == '':
|
|
print '\nNothing entered, press any key to return.'
|
|
wait_for_key()
|
|
return True
|
|
|
|
else:
|
|
WriteOrder(order.replace('\n',''), o_time_s)
|
|
print '\nPress any key to return.'
|
|
wait_for_key()
|
|
return True
|
|
|
|
|
|
def ViewOrders(o_time_s = None):
|
|
if o_time_s == None:
|
|
o_time = time.localtime()
|
|
o_time_s = time.time()
|
|
else:
|
|
o_time = time.localtime(o_time_s)
|
|
|
|
folder = os.path.join(
|
|
settings['defaults']['food_folder'],
|
|
time.strftime('%Y-%m-%d', o_time))
|
|
|
|
items = []
|
|
items.append(['No',
|
|
'final_event',
|
|
'no',
|
|
'Do not lock ordering'])
|
|
|
|
items.append(['Yes',
|
|
'final_event',
|
|
'yes',
|
|
'Lock ordering! No more orders will be allowed'])
|
|
|
|
lock_sel = MiniMenu('Lock Ordering?', items, True)
|
|
if lock_sel == 'yes':
|
|
try:
|
|
lock_file = open(os.path.join(folder, 'order_lock.txt'), 'w')
|
|
lock_file.write(os.environ.get('username'))
|
|
lock_file.close()
|
|
except IOError:
|
|
error_exit('Lock file could not be written')
|
|
|
|
print 'Compiling order list for %s' % time.strftime('%Y-%m-%d', o_time)
|
|
|
|
order_files = directory_list(folder,'*.txt')
|
|
|
|
orders = {}
|
|
|
|
for fname in order_files:
|
|
name = os.path.basename(fname).replace('.txt','').lower()
|
|
if name == 'order_lock':
|
|
continue
|
|
infile = open(fname, 'r')
|
|
order_string = ''
|
|
for line in infile:
|
|
order_string += line.replace('\n','')
|
|
infile.close()
|
|
|
|
orders[name] = order_string
|
|
|
|
|
|
print orders
|
|
|
|
print 'Order list compiled, %s orders taken' % len(orders)
|
|
|
|
print 'Generating orders.csv'
|
|
|
|
outfname = os.path.join(os.environ.get('temp'),'orders.csv')
|
|
outfile = open(outfname, 'w')
|
|
outfile.write('Orders for:,%s\n' % time.strftime('%Y-%m-%d', o_time))
|
|
outfile.write('Number of orders:,%s\n' % len(orders))
|
|
## for name, order in orders.iteritems():
|
|
## outfile.write('"%s","%s"\n' % (name, order))
|
|
|
|
names = orders.keys()
|
|
names.sort()
|
|
for name in names:
|
|
outfile.write('"%s","%s"\n' % (name, orders[name]))
|
|
outfile.close()
|
|
|
|
time.sleep(0.5)
|
|
|
|
os.startfile(outfname)
|
|
|
|
|
|
|
|
|
|
def SubstDrive(command):
|
|
os.system('subst %s' % command)
|
|
|
|
|
|
def WriteSwitches(fname, switches, separator):
|
|
out = open(fname, 'w')
|
|
|
|
for switch in switches:
|
|
out.write('%s%s' % (switch, separator))
|
|
|
|
def GetCommandLineSeparator(gametype):
|
|
seps = {'lan': ' ',
|
|
'rdr': '\n',
|
|
'gta_v': '\n'}
|
|
|
|
try:
|
|
return seps[gametype.lower()]
|
|
except KeyError:
|
|
logging('GetCommandLineSeparator(): KeyError: %s' % gametype.lower())
|
|
return ''
|
|
|
|
|
|
|
|
def run_batfile(path):
|
|
os.system('"%s"' % path)
|
|
|
|
|
|
|
|
def ColdRebootXbox():
|
|
xdk_vars()
|
|
os.system('xbReboot.exe /C')
|
|
|
|
|
|
|
|
#===============================================================================
|
|
def TestLoad():
|
|
game_versions.LoadFile('game_versions.ini')
|
|
settings.LoadFile('settings.ini')
|
|
settings.LoadFile('variables.ini')
|
|
settings.LoadFile(settings['defaults']['cab_hashes'])
|
|
game_versions.DoStringReplacement(settings['variables'])
|
|
DynamicProgramFiles(settings)
|
|
|
|
log_path = os.path.join(sys.path[0],'qa_menu.log')
|
|
game_versions = SettingsLoader()
|
|
settings = SettingsLoader()
|
|
|
|
|
|
|
|
|