Files
gtav-src/tools_ng/bin/mpLogGrabber/data/qa.py
T
2025-09-29 00:52:08 +02:00

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()