1546 lines
55 KiB
Ruby
Executable File
1546 lines
55 KiB
Ruby
Executable File
require 'getoptlong'
|
|
require 'Logger'
|
|
require 'singleton'
|
|
require 'fileutils'
|
|
require 'net/ftp'
|
|
require 'net/smtp'
|
|
require 'iconv'
|
|
require 'erb'
|
|
|
|
include ERB::Util
|
|
|
|
# Setting up
|
|
RAGE_DIR = ENV['RAGE_DIR'].gsub(/\\/, "/")
|
|
RS_CODEBRANCH = ENV['RS_CODEBRANCH'].gsub(/\\/, "/")
|
|
RS_BUILDBRANCH = ENV['RS_BUILDBRANCH'].gsub(/\\/, "/")
|
|
RS_TOOLSROOT = ENV['RS_TOOLSROOT'].gsub(/\\/, "/")
|
|
RS_TITLE = ENV['RS_TITLE']
|
|
USER_PROFILE = ENV['USERPROFILE']
|
|
USER_DESKTOP = USER_PROFILE + "/Desktop"
|
|
USERNAME = ENV['USERNAME']
|
|
USER_LOG_PATH = USER_PROFILE + "/Documents/Rockstar Games/GTA V"
|
|
GAME = "GTAV"
|
|
PROTECTION_PATH = RS_TOOLSROOT + "/script/coding/protection"
|
|
TOOL_PATH = PROTECTION_PATH + "/launcherTester"
|
|
GENERATED_PATH = PROTECTION_PATH + "/generated"
|
|
CURR_TIME = Time.now.strftime("%Y%m%d.%H.%M.%S")
|
|
LOG_SEVERITY_LEVEL = Logger::INFO
|
|
DEFAULT_RELEASE = ""
|
|
DEFAULT_SKIP_BUILDS = false
|
|
DEFAULT_USERNAME = ""
|
|
DEFAULT_PASSWORD = ""
|
|
PATCHBUILDER_DIR = RS_CODEBRANCH + "/extra/patchbuilder"
|
|
PATCHBUILDER_DATA_DIR = PATCHBUILDER_DIR + "/Data/"
|
|
PATCHBUILDER_OUTPUT_DIR = PATCHBUILDER_DIR + "/Output/"
|
|
GAME_DIR = "C:/Program Files/Rockstar Games/Grand Theft Auto V/"
|
|
OLD_PATCH = "1.0.0.0"
|
|
OLD_PATCH_REGEX = "1([._,])0([._,])0([._,])0"
|
|
NEW_PATCH_UNDERSCORE = "1_0_0_1"
|
|
NEW_PATCH_REGEX = "1\\10\\10\\11"
|
|
OLD_XML_FILE = "launcher_version.xml"
|
|
NEW_XML_FILE = "launcher_version.testing.#{USERNAME}.xml"
|
|
PROGFILESX86 = ENV['ProgramFiles(x86)'].gsub(/\\/, "/")
|
|
SIGNTOOL_DIR = PROGFILESX86 + "/Windows Kits/8.0/bin/x86/"
|
|
$LOG_DIR = "C:/"
|
|
$SHOW_ERROR_DELAY = 500
|
|
|
|
# Clearing out our data directory, as we don't need it any more. We do this ahead of the creation, so the directory will be there for when we need to write to it.
|
|
FileUtils.rm_rf("#{PATCHBUILDER_DATA_DIR}")
|
|
# Then our generated paths
|
|
|
|
def safecreatepath(path)
|
|
begin
|
|
FileUtils.mkdir_p(path) unless File.exists?(path)
|
|
rescue Exception =>e
|
|
puts e.message
|
|
end
|
|
end
|
|
|
|
|
|
safecreatepath(GENERATED_PATH)
|
|
safecreatepath(PATCHBUILDER_OUTPUT_DIR)
|
|
safecreatepath(PATCHBUILDER_DATA_DIR)
|
|
safecreatepath(PROTECTION_PATH)
|
|
|
|
|
|
class Parameters
|
|
|
|
|
|
attr_reader :release
|
|
attr_reader :skipbuilds
|
|
attr_reader :skipupload
|
|
attr_reader :username
|
|
attr_reader :password
|
|
attr_reader :emailfrom
|
|
attr_reader :emailto
|
|
attr_reader :yes
|
|
attr_reader :showerror
|
|
attr_reader :errorsonly
|
|
attr_reader :noerrors
|
|
attr_reader :preloader
|
|
def initialize
|
|
@release = DEFAULT_RELEASE #nil
|
|
@skipbuilds = DEFAULT_SKIP_BUILDS
|
|
@skipupload = DEFAULT_SKIP_BUILDS
|
|
@username = DEFAULT_USERNAME
|
|
@password = DEFAULT_PASSWORD
|
|
@emailfrom = ""
|
|
@emailto = ""
|
|
@yes = false
|
|
@showerror = false
|
|
@errorsonly = false
|
|
@noerrors = false
|
|
@preloader = false
|
|
@opts = GetoptLong.new(
|
|
[ '--release', '-r', GetoptLong::REQUIRED_ARGUMENT ],
|
|
[ '--username', '-u', GetoptLong::REQUIRED_ARGUMENT ],
|
|
[ '--password', '-p', GetoptLong::REQUIRED_ARGUMENT ],
|
|
[ '--skipbuild', '-s', GetoptLong::NO_ARGUMENT ],
|
|
[ '--skipupload', '-k', GetoptLong::NO_ARGUMENT ],
|
|
[ '--emailfrom', '-e', GetoptLong::REQUIRED_ARGUMENT ],
|
|
[ '--emailto', '-t', GetoptLong::REQUIRED_ARGUMENT ],
|
|
[ '--always-yes', '-y', GetoptLong::NO_ARGUMENT ],
|
|
[ '--showerror', '-o', GetoptLong::NO_ARGUMENT ],
|
|
[ '--errorsonly', '-z', GetoptLong::NO_ARGUMENT ],
|
|
[ '--noerrors', '-x', GetoptLong::NO_ARGUMENT ],
|
|
[ '--logdir', '-l', GetoptLong::REQUIRED_ARGUMENT ],
|
|
[ '--errordelay', '-d', GetoptLong::REQUIRED_ARGUMENT ],
|
|
[ '--preloader', '-h', GetoptLong::NO_ARGUMENT ]
|
|
|
|
)
|
|
end
|
|
|
|
def PrintHelp()
|
|
RsgLog.instance().info("DNE Yet");
|
|
end
|
|
def ParseArguments()
|
|
@opts.each do |opt, arg|
|
|
case opt
|
|
when '--help'
|
|
PrintHelp()
|
|
exit
|
|
when '--skipbuild'
|
|
@skipbuilds = true
|
|
when '--skipupload'
|
|
@skipupload = true
|
|
when '--always-yes'
|
|
@yes = true;
|
|
when '--username'
|
|
@username = arg
|
|
when '--password'
|
|
@password = arg
|
|
when '--emailfrom'
|
|
@emailfrom = arg
|
|
when '--emailto'
|
|
@emailto = arg
|
|
when '--showerror'
|
|
@showerror = true
|
|
when '--errorsonly'
|
|
@errorsonly = true
|
|
when '--noerrors'
|
|
@noerrors = true
|
|
when '--preloader'
|
|
@preloader = true
|
|
when '--logdir'
|
|
$LOG_DIR = arg
|
|
when '--errordelay'
|
|
$SHOW_ERROR_DELAY = arg
|
|
when '--release'
|
|
case arg
|
|
when /release/i,/bankrelease/i,/debug/i,/final/i,/beta/i, /master/i
|
|
@release = arg
|
|
else
|
|
RsgLog.instance().error("Invalid release [#{arg}] specified")
|
|
exit
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
class MultiDelegator
|
|
def initialize(*targets)
|
|
@targets = targets
|
|
end
|
|
|
|
def self.delegate(*methods)
|
|
methods.each do |m|
|
|
define_method(m) do |*args|
|
|
@targets.map { |t| t.send(m, *args) }
|
|
end
|
|
end
|
|
self
|
|
end
|
|
|
|
class <<self
|
|
alias to new
|
|
end
|
|
end
|
|
|
|
class RsgLog
|
|
include Singleton
|
|
attr_accessor :log
|
|
def initialize()
|
|
log_file = File.open(GENERATED_PATH + "/" + CURR_TIME + ".log", "w")
|
|
@log = Logger.new MultiDelegator.delegate(:write, :close).to(STDOUT, log_file)
|
|
|
|
@log.datetime_format = "%Y%m%d-%H:%M:%S"
|
|
@log.level = LOG_SEVERITY_LEVEL
|
|
@log.formatter = proc do |serverity, time, progname, msg|
|
|
"[#{time.strftime(@log.datetime_format)}]\t[#{serverity}]\t#{msg}"
|
|
end
|
|
end
|
|
def info(msg, newline=true)
|
|
if newline == true
|
|
msg = msg + "\n"
|
|
end
|
|
@log.info(msg)
|
|
end
|
|
def debug(msg, newline=true)
|
|
if newline == true
|
|
msg = msg + "\n"
|
|
end
|
|
@log.debug(msg)
|
|
end
|
|
def warn(msg, newline=true)
|
|
if newline == true
|
|
msg = msg + "\n"
|
|
end
|
|
@log.warn(msg)
|
|
end
|
|
def error(msg, newline=true)
|
|
if newline == true
|
|
msg = msg + "\n"
|
|
end
|
|
@log.error(msg)
|
|
end
|
|
|
|
def SetLevel(level)
|
|
@log.level = level
|
|
end
|
|
|
|
|
|
end
|
|
|
|
class LauncherTestPerforceHandler
|
|
include Singleton
|
|
attr_reader :verbose
|
|
|
|
def initialize()
|
|
@depotArray = Array.new
|
|
@depotArray.push("//depot/gta5/src/dev_ng/extra/launcher/Bootstrap/Bootstrap.rc")
|
|
@depotArray.push("//depot/gta5/src/dev_ng/extra/launcher/Launcher/Launcher.rc")
|
|
@depotArray.push("//depot/gta5/src/dev_ng/extra/launcher/Launcher/res/launcher_config.xml")
|
|
FileUtils.cp("#{RS_CODEBRANCH}/extra/launcher/Launcher/res/launcher_config.xml", "#{GENERATED_PATH}/#{CURR_TIME}.launcher_config.xml")
|
|
end
|
|
|
|
def Revert()
|
|
@depotArray.each { |x|
|
|
cmd = "p4 revert #{x}"
|
|
output = `#{cmd}`
|
|
RsgLog.instance().debug(output)
|
|
}
|
|
end
|
|
|
|
def Edit()
|
|
@depotArray.each { |x|
|
|
cmd = "p4 edit #{x}"
|
|
output = `#{cmd}`
|
|
RsgLog.instance().debug(output)
|
|
}
|
|
end
|
|
end
|
|
|
|
def ReplaceTokenInFile(filename, oldString, newString)
|
|
text = File.read(filename)
|
|
newContents = text.gsub(/#{oldString}/, newString)
|
|
# To write changes to the file, use:
|
|
File.open(filename, "w") {|file| file.puts newContents }
|
|
end
|
|
|
|
def CleanSolution(slnPath, release)
|
|
system("\"C:\\Program Files (x86)\\Xoreax\\IncrediBuild\\BuildConsole.exe\" #{slnPath} /clean /Silent /cfg=\"#{release}|x64\"")
|
|
if $?.exitstatus !=0
|
|
log.error("Danger Will Robinson! Error found. Exiting!")
|
|
exit
|
|
end
|
|
end
|
|
|
|
def BuildSolution(slnPath, release)
|
|
system("\"C:\\Program Files (x86)\\Xoreax\\IncrediBuild\\BuildConsole.exe\" #{slnPath} /build /Silent /cfg=\"#{release}|x64\"")
|
|
if $?.exitstatus !=0
|
|
log.error("Danger Will Robinson! Error found. Exiting!")
|
|
exit
|
|
end
|
|
end
|
|
|
|
def ProcessConfiguration(slnPath, release)
|
|
# Call all the functionality needed to do one clean / build
|
|
CleanSolution(slnPath,release)
|
|
BuildSolution(slnPath,release)
|
|
end
|
|
|
|
def ProtectIT(lorp, release, preloader)
|
|
cmd = "ruby #{RS_TOOLSROOT}\\script\\coding\\protection\\ProtectIT.rb -r #{release} -f -v -g "
|
|
if preloader == true
|
|
RsgLog.instance().info("Preloader Iteration Detected!")
|
|
if lorp == "launcher"
|
|
cmd+="#{RS_TOOLSROOT}\\script\\coding\\protection\\preloaderlauncher.gsml"
|
|
elsif lorp == "bootstrap"
|
|
cmd+="#{RS_TOOLSROOT}\\script\\coding\\protection\\preloaderbootstrap.gsml"
|
|
end
|
|
else
|
|
RsgLog.instance().info("Regular Iteration Detected!")
|
|
if lorp == "launcher"
|
|
cmd+="#{RS_TOOLSROOT}\\script\\coding\\protection\\launcher.gsml"
|
|
elsif lorp == "bootstrap"
|
|
cmd+="#{RS_TOOLSROOT}\\script\\coding\\protection\\prelauncher.gsml"
|
|
end
|
|
end
|
|
system(cmd)
|
|
if $?.exitstatus !=0
|
|
RsgLog.instance().error("Danger Will Robinson! Error found. Exiting!")
|
|
exit
|
|
end
|
|
end
|
|
|
|
def GetMostRecentFile()
|
|
returnFile = Dir.glob("#{RS_TOOLSROOT}/script/coding/protection/generated/*.exe").max_by {|f| File.mtime(f)}
|
|
if returnFile.empty?
|
|
RsgLog.instance().error("Most recent file came back empty. Exiting.")
|
|
exit
|
|
end
|
|
|
|
# Now lets sign the protected executable, whatever we're fetching
|
|
SignFile(returnFile)
|
|
# Return the signed protected file
|
|
return returnFile
|
|
end
|
|
|
|
def SignFile(path)
|
|
pfxPath = "#{RS_CODEBRANCH}/rage/suite/src/rgsc/rockstar.pfx"
|
|
cmd = "#{PROGFILESX86}/signtool.exe sign /f #{pfxPath} /p uci5eNJRxv! /t http://timestamp.verisign.com/scripts/timstamp.dll /v #{path}"
|
|
system(cmd)
|
|
end
|
|
|
|
def ShoveIntoDataDirectory(pathToPatch)
|
|
end
|
|
|
|
def DeleteSomeFiles()
|
|
# Deleting some local RPFs to test the downloading of pipes
|
|
RsgLog.instance().info("Deleting some local RPF files...")
|
|
begin
|
|
FileUtils.rm("#{GAME_DIR}/x64a.rpf")
|
|
FileUtils.rm("#{GAME_DIR}/x64b.rpf")
|
|
FileUtils.rm("#{GAME_DIR}/x64c.rpf")
|
|
FileUtils.rm("#{GAME_DIR}/x64d.rpf")
|
|
rescue
|
|
end
|
|
|
|
# Deleting game executable to test the downloading of patches
|
|
RsgLog.instance().info("Deleting the local game exe...")
|
|
begin
|
|
FileUtils.rm("#{GAME_DIR}/gta5.exe")
|
|
rescue
|
|
end
|
|
end
|
|
|
|
# </loggingSetup>
|
|
|
|
|
|
log = RsgLog.instance()
|
|
|
|
|
|
# Banner
|
|
log.info("=======================")
|
|
log.info("Launcher Testing Script")
|
|
log.info("=======================")
|
|
|
|
log.info("Parsing arguments...")
|
|
parameters = Parameters.new
|
|
parameters.ParseArguments()
|
|
|
|
if !parameters.skipupload && !parameters.showerror && !parameters.errorsonly
|
|
|
|
if !parameters.yes
|
|
log.warn("THIS SCRIPT REVERTS AND MODIFIES YOUR LAUNCHER_CONFIG.XML FILE. PRESS ANY KEY TO CONTINUE.")
|
|
system("pause")
|
|
end
|
|
|
|
|
|
if parameters.release == ""
|
|
parameters.PrintHelp()
|
|
log.error("Invalid release [#{parameters.release}] specified")
|
|
exit
|
|
end
|
|
|
|
if parameters.username == ""
|
|
parameters.PrintHelp()
|
|
log.error("Invalid username [#{parameters.username}] specified")
|
|
exit
|
|
end
|
|
|
|
if parameters.password == ""
|
|
parameters.PrintHelp()
|
|
log.error("Invalid password [#{parameters.password}] specified")
|
|
exit
|
|
end
|
|
|
|
|
|
# Revert and edit the Bootstrap
|
|
log.info("Checking out files in Perforce...")
|
|
LauncherTestPerforceHandler.instance().Revert()
|
|
LauncherTestPerforceHandler.instance().Edit()
|
|
# Good, now the files we care about are in a pristine state (or should be)
|
|
if parameters.skipbuilds == false
|
|
# Now lets swap out the tokens in our XML file so we point to the right patch file on the server
|
|
ReplaceTokenInFile("#{RS_CODEBRANCH}\\extra\\launcher\\Launcher\\res\\launcher_config.xml", OLD_XML_FILE, NEW_XML_FILE)
|
|
|
|
# Lets build the first version of the Bootstrap
|
|
log.info("Building first version of the Bootstrap...");
|
|
ProcessConfiguration("#{RS_CODEBRANCH}\\extra\\launcher\\Bootstrap.sln", parameters.release)
|
|
log.info("Protecting first version of the Bootstrap...")
|
|
ProtectIT("bootstrap", parameters.release, parameters.preloader)
|
|
firstProtectedBootstrap = GetMostRecentFile()
|
|
|
|
log.info("First Protected Bootstrap #{firstProtectedBootstrap}")
|
|
|
|
|
|
# Increment the version number
|
|
# Since Ruby 1.8.7 has absolute shit support for UTF-16 search/replacing, I'm just copying over a new file
|
|
# Yes it's a hack. I've spent 2 hours trying to do it the proper way. I give up.
|
|
FileUtils.cp("#{TOOL_PATH}/patched/Bootstrap.rc", "#{RS_CODEBRANCH}/extra/launcher/Bootstrap/Bootstrap.rc")
|
|
|
|
# Build the second version of the bootstrap (the one we're patching down
|
|
log.info("Building second version of the Bootstrap...");
|
|
ProcessConfiguration("#{RS_CODEBRANCH}\\extra\\launcher\\Bootstrap.sln", parameters.release)
|
|
log.info("Protecting second version of the Bootstrap...")
|
|
ProtectIT("bootstrap", parameters.release,parameters.preloader)
|
|
secondProtectedBootstrap = GetMostRecentFile()
|
|
log.info("Second Protected Bootstrap #{secondProtectedBootstrap}")
|
|
|
|
# Do the same thing with the Launcher
|
|
log.info("Building first version of the Launcher...");
|
|
ProcessConfiguration("#{RS_CODEBRANCH}\\extra\\launcher\\Launcher.sln", parameters.release)
|
|
log.info("Protecting first version of the Launcher...")
|
|
ProtectIT("launcher", parameters.release,parameters.preloader)
|
|
firstProtectedLauncher = GetMostRecentFile()
|
|
log.info("First Protected Launcher #{firstProtectedLauncher}")
|
|
|
|
# Increment the Version #
|
|
FileUtils.cp("#{TOOL_PATH}/patched/Launcher.rc", "#{RS_CODEBRANCH}/extra/launcher/Launcher/Launcher.rc")
|
|
# Second version of the launcher
|
|
log.info("Building second version of the Launcher...");
|
|
ProcessConfiguration("#{RS_CODEBRANCH}\\extra\\launcher\\Launcher.sln", parameters.release)
|
|
log.info("Protecting second version of the Launcher...")
|
|
ProtectIT("launcher", parameters.release,parameters.preloader)
|
|
secondProtectedLauncher = GetMostRecentFile()
|
|
log.info("Second Protected Launcher #{secondProtectedLauncher}")
|
|
|
|
# Now we have to do the same with RGSC. #stellar.
|
|
# The worst part is, it requires interaction with the user
|
|
# The good part is, all I need to do is call the collect_ script.
|
|
# Lets create the first protected version of RGSC
|
|
# Create the second protected version of RGSC
|
|
|
|
end
|
|
|
|
# Create the patches
|
|
# First lets create the bootstrap patch
|
|
# We do this by copying the second bootstrap into the data directory of the patchbuilder
|
|
log.info("Building the installable patch executables...")
|
|
# First clear out the directory
|
|
FileUtils.rm_rf("#{PATCHBUILDER_DATA_DIR}/*")
|
|
# Copy the file over
|
|
FileUtils.cp(secondProtectedBootstrap, PATCHBUILDER_DATA_DIR + "/PlayGTAV.exe")
|
|
|
|
# Now we run the CreatePatchTool to build the patch
|
|
system("#{RS_CODEBRANCH}/extra/patchbuilder/CreatePatchTest.bat Prelauncher #{NEW_PATCH_UNDERSCORE} > #{GENERATED_PATH}/#{CURR_TIME}.bootstrap.patchbuilder.log")
|
|
|
|
# First clear out the directory
|
|
FileUtils.rm_rf("#{PATCHBUILDER_DATA_DIR}/*")
|
|
# Copy the file over
|
|
FileUtils.cp(secondProtectedLauncher, PATCHBUILDER_DATA_DIR + "/GTAVLauncher.exe")
|
|
# Now we run the CreatePatchTool to build the patch
|
|
system("#{RS_CODEBRANCH}/extra/patchbuilder/CreatePatchTest.bat Launcher #{NEW_PATCH_UNDERSCORE} > #{GENERATED_PATH}/#{CURR_TIME}.launcher.patchbuilder.log")
|
|
|
|
sleep(3)
|
|
|
|
# Create our FTP pipe
|
|
log.info("Creating our FTP connection...")
|
|
ftp = Net::FTP.new("rsgames.upload.llnw.net")
|
|
ftp.login(parameters.username, parameters.password)
|
|
|
|
# Upload the patches to the FTP
|
|
log.info("Uploading the patches...")
|
|
ftp.chdir('/patches/dev/gtav/Launcher_EFIGS')
|
|
|
|
Dir.glob("#{PATCHBUILDER_OUTPUT_DIR}/*.exe").each do |patch|
|
|
# Sign the file before we do anything with it
|
|
log.info("Signing #{patch}...")
|
|
SignFile(patch)
|
|
# Now upload it to the server
|
|
log.info("Uploading #{patch}...")
|
|
ftp.putbinaryfile(patch)
|
|
end
|
|
|
|
# Upload the XML files to the FTP
|
|
log.info("Uploading new XML files...")
|
|
ftp.chdir('/patches/dev/gtav')
|
|
|
|
# Remove old files - hitting errors where they are read-only
|
|
begin
|
|
FileUtils.rm_f("#{GENERATED_PATH}/launcher_version.testing.#{USERNAME}.xml")
|
|
rescue
|
|
end
|
|
begin
|
|
FileUtils.rm_f("#{GENERATED_PATH}/prelauncher_version.testing.#{USERNAME}.xml")
|
|
rescue
|
|
end
|
|
|
|
FileUtils.cp("#{TOOL_PATH}/patched/launcher_version.testing.xml", "#{GENERATED_PATH}/launcher_version.testing.#{USERNAME}.xml")
|
|
FileUtils.cp("#{TOOL_PATH}/patched/prelauncher_version.testing.xml", "#{GENERATED_PATH}/prelauncher_version.testing.#{USERNAME}.xml")
|
|
ftp.putbinaryfile("#{GENERATED_PATH}/launcher_version.testing.#{USERNAME}.xml")
|
|
ftp.putbinaryfile("#{GENERATED_PATH}/prelauncher_version.testing.#{USERNAME}.xml")
|
|
|
|
# Copy over the first iterations to our build directory
|
|
log.info("Copying executables over to the build directory...")
|
|
if parameters.skipbuilds == false
|
|
FileUtils.cp(firstProtectedBootstrap, GAME_DIR + "/PlayGTAV.exe")
|
|
FileUtils.cp(firstProtectedLauncher, GAME_DIR + "/GTAVLauncher.exe")
|
|
FileUtils.cp(firstProtectedBootstrap, RS_BUILDBRANCH + "/PlayGTAV.exe")
|
|
FileUtils.cp(firstProtectedLauncher, RS_BUILDBRANCH + "/GTAVLauncher.exe")
|
|
|
|
end
|
|
|
|
# Deleting a local file to test the downloading of pipes
|
|
log.info("Deleting a local RPF file...")
|
|
begin
|
|
FileUtils.rm("#{GAME_DIR}/x64a.rpf")
|
|
rescue
|
|
end
|
|
|
|
# Deleting game executable to test the downloading of patches
|
|
log.info("Deleting a local RPF file...")
|
|
begin
|
|
FileUtils.rm("#{GAME_DIR}/gta5.exe")
|
|
rescue
|
|
end
|
|
|
|
# Revert back out the files we opened up
|
|
log.info("Reverting files that we've opened up...");
|
|
LauncherTestPerforceHandler.instance().Revert()
|
|
|
|
# Restore the files that we've previously clobbered
|
|
|
|
# Close FTP connection
|
|
log.info("Closing FTP connection...")
|
|
ftp.close
|
|
|
|
else
|
|
|
|
log.info("Skipping build / upload.")
|
|
|
|
DeleteSomeFiles()
|
|
|
|
end
|
|
|
|
|
|
|
|
# Defining the testing functions down here, to not get mixed up with the
|
|
# patch-building / -uploading code.
|
|
|
|
PASS_COLOUR = "#004400"
|
|
FAIL_COLOUR = "#880000"
|
|
INFO_COLOUR = "#202020"
|
|
|
|
$currentLogfilePath = ""
|
|
$currentLogfileDestPath = ""
|
|
$currentLogFile = nil
|
|
$reportHtml = ""
|
|
|
|
|
|
$sectionTitle = ""
|
|
$sectionBody = ""
|
|
$sectionPassed = true
|
|
$sections = []
|
|
|
|
def StartReport()
|
|
$reportHtml = '<head><meta charset="UTF-8"></head>'
|
|
$reportHtml += "\n<div style=\"font-family: Sans-serif;\">\n<h1>Launcher Automated Test Results</h1>\n"
|
|
$reportHtml += "<div><div style=\"font-size:150%;font-weight:bold;\">Summary</div>\n<ul>"
|
|
|
|
$reportHtml += "<li>User: " + USERNAME + "</li>\n"
|
|
$reportHtml += "<li>Userprofile: " + USER_PROFILE + "</li>\n"
|
|
$reportHtml += "<li>Log dir: " + USER_LOG_PATH + "</li>\n"
|
|
|
|
p4ver = "(unknown)"
|
|
begin
|
|
p4ver = `p4 changes -m 1 X:\\gta5\\src\\dev_ng\\extra\\launcher...#have`
|
|
p4ver = p4ver.split(" ")[1]
|
|
rescue
|
|
end
|
|
|
|
$reportHtml += "<li>CL: " + p4ver.to_s + "</li>\n"
|
|
|
|
end
|
|
|
|
def StartSection(title)
|
|
RsgLog.instance().info("Starting section \"#{title}\"...")
|
|
$sectionTitle = title
|
|
$sectionBody = ""
|
|
$sectionPassed = true
|
|
|
|
$sectionBody += "<table width=\"100%\" border=\"1\" style=\"font-family:Sans-serif;\">\n"
|
|
end
|
|
|
|
def OpenLogFile(filename)
|
|
RsgLog.instance().info("Opening log file \"#{filename}\"...")
|
|
$currentLogfilePath = "#{USER_LOG_PATH}/#{filename}"
|
|
begin
|
|
$currentLogFile = File.open($currentLogfilePath)
|
|
rescue
|
|
$currentLogFile = nil
|
|
end
|
|
end
|
|
|
|
def InsertLogFilePath()
|
|
$sectionBody += "<tr><td style=\"font-size:75%;\">Logfile: #{$currentLogfileDestPath}</td><td>[<a href=\"#{$currentLogfileDestPath}\">open</a>]</td></tr>\n"
|
|
end
|
|
|
|
def CopyAndOpenLogFile(filename)
|
|
|
|
$currentLogfilePath = "#{USER_LOG_PATH}/#{filename}"
|
|
destPath = "#{$LOG_DIR}/" + Time.now.strftime("%Y%m%d.%H.%M.%S") + "." + filename
|
|
#destPath = "#{$LOG_DIR}/#{CURR_TIME}.#{filename}"
|
|
|
|
RsgLog.instance().info("Source log: "+$currentLogfilePath)
|
|
RsgLog.instance().info("Dest log: "+destPath)
|
|
|
|
FileUtils.cp($currentLogfilePath, destPath)
|
|
|
|
$currentLogFile = File.open($currentLogfilePath)
|
|
$currentLogfileDestPath = destPath
|
|
|
|
InsertLogFilePath()
|
|
end
|
|
|
|
def ContinueLogFileSearch(string)
|
|
line = nil
|
|
pos = $currentLogFile.pos
|
|
while (line = $currentLogFile.gets) != nil
|
|
if (line.include?(string))
|
|
return true
|
|
end
|
|
end
|
|
|
|
# Rewind on failure
|
|
$currentLogFile.close
|
|
$currentLogFile = File.open($currentLogfilePath)
|
|
$currentLogFile.pos = pos
|
|
|
|
return false
|
|
end
|
|
|
|
def ContinueLogFileSearchPassFail(string0, string1)
|
|
line = nil
|
|
pos = $currentLogFile.pos
|
|
while (line = $currentLogFile.gets) != nil
|
|
if (line.include?(string0))
|
|
return 0
|
|
end
|
|
if (line.include?(string1))
|
|
# Rewind on failure
|
|
$currentLogFile.pos = pos
|
|
return 1
|
|
end
|
|
end
|
|
|
|
# Rewind on failure
|
|
$currentLogFile.close
|
|
$currentLogFile = File.open($currentLogfilePath)
|
|
$currentLogFile.pos = pos
|
|
|
|
return -1
|
|
end
|
|
|
|
def ContinueLogFileSearchReturnLine(string0)
|
|
line = nil
|
|
pos = $currentLogFile.pos
|
|
while (line = $currentLogFile.gets) != nil
|
|
if (line.include?(string0))
|
|
return line
|
|
end
|
|
end
|
|
|
|
# Rewind on failure
|
|
$currentLogFile.close
|
|
$currentLogFile = File.open($currentLogfilePath)
|
|
$currentLogFile.pos = pos
|
|
|
|
return nil
|
|
end
|
|
|
|
def ReadLineFromFile(file, linenumber)
|
|
line = nil
|
|
begin
|
|
File.open(file) do |f|
|
|
linenumber.times { line = f.gets }
|
|
end
|
|
rescue
|
|
end
|
|
|
|
return line
|
|
end
|
|
|
|
def AddResultLine(text, passed)
|
|
if passed
|
|
RsgLog.instance().info("#{text} - PASSED")
|
|
$sectionBody += "<tr style=\"color:#{PASS_COLOUR}\"><td>#{text}</td><td>PASSED</td></tr>\n"
|
|
else
|
|
RsgLog.instance().info("#{text} - FAILED")
|
|
$sectionBody += "<tr style=\"color:#{FAIL_COLOUR}\"><td>#{text}</td><td>FAILED</td></tr>\n"
|
|
$sectionPassed = false
|
|
end
|
|
end
|
|
|
|
def AddInfoLine(text)
|
|
RsgLog.instance().info("#{text} - INFO")
|
|
$sectionBody += "<tr style=\"color:#{INFO_COLOUR}\"><td>#{text}</td><td>INFO</td></tr>\n"
|
|
end
|
|
|
|
def AddSmallInfoLine(text)
|
|
RsgLog.instance().info("#{text} - INFO")
|
|
$sectionBody += "<tr style=\"color:#{INFO_COLOUR};\"><td style=\"font-size:75%;\">#{text}</td><td>INFO</td></tr>\n"
|
|
end
|
|
|
|
def AddCodeLine(text)
|
|
if text == nil
|
|
return
|
|
end
|
|
|
|
text = html_escape(text)
|
|
|
|
RsgLog.instance().info("#{text} - INFO")
|
|
$sectionBody += '<tr style="color:#{INFO_COLOUR};"><td style="font-size:75%;font-family:monospace;">' + text + '</td><td>INFO</td></tr>'
|
|
$sectionBody += "\n"
|
|
end
|
|
|
|
def TestForLogLine(search)
|
|
AddResultLine("Check for \"#{search}\"", ContinueLogFileSearch(search))
|
|
end
|
|
|
|
def TestForLogPassFail(pass, fail)
|
|
AddResultLine("Check for \"#{pass}\"", ContinueLogFileSearchPassFail(pass, fail) == 0)
|
|
end
|
|
|
|
def CloseLogFile()
|
|
$currentLogFile.close
|
|
end
|
|
|
|
def Countdown(var)
|
|
while var > 0
|
|
RsgLog.instance().info(var.to_s + "... ")
|
|
sleep(1)
|
|
var = var - 1
|
|
end
|
|
end
|
|
|
|
def EndSection()
|
|
RsgLog.instance().info("Ending section...")
|
|
|
|
section = "<div><div style=\"font-size:150%;font-weight:bold;\">" + $sectionTitle
|
|
if $sectionPassed
|
|
section += " - <span style=\"color:#{PASS_COLOUR}\">PASSED</span></div>\n"
|
|
else
|
|
section += " - <span style=\"color:#{FAIL_COLOUR}\">FAILED</span></div>\n"
|
|
end
|
|
section += $sectionBody
|
|
section += "\n</table></div>\n<br>\n\n"
|
|
|
|
$sections.push(section)
|
|
|
|
|
|
$reportHtml += "<li style=\"font-weight:normal;\">" + $sectionTitle
|
|
if $sectionPassed
|
|
$reportHtml += " - <span style=\"color:#{PASS_COLOUR}\">PASSED</span></li>\n"
|
|
else
|
|
$reportHtml += " - <span style=\"color:#{FAIL_COLOUR}\">FAILED</span></li>\n"
|
|
end
|
|
|
|
sleep(0.2)
|
|
end
|
|
|
|
def EndReport()
|
|
$reportHtml += "</ul></div>\n<br>\n"
|
|
|
|
$sections.each {|i| $reportHtml += i }
|
|
|
|
$reportHtml += "</div>"
|
|
end
|
|
|
|
def BootstrapSelfPatchTest()
|
|
|
|
random_arg = "-autotest_random_arg_" + rand(100).to_s
|
|
RsgLog.instance().info("Launching Bootstrap only...")
|
|
result = system(GAME_DIR + "/PlayGTAV.exe", "-autotest_bootstrap_nolauncher", random_arg)
|
|
|
|
StartSection("Bootstrap Patch Check")
|
|
|
|
AddResultLine("Execution success", result)
|
|
AddResultLine("Process return code == 0", $? == 0)
|
|
|
|
generated_log = false
|
|
OpenLogFile("bootstrap.log")
|
|
generated_log = ContinueLogFileSearch(random_arg)
|
|
CloseLogFile()
|
|
|
|
AddResultLine("Created log file", generated_log)
|
|
AddResultLine("Generated a log file", generated_log)
|
|
|
|
CopyAndOpenLogFile("bootstrap.log")
|
|
|
|
if generated_log
|
|
|
|
|
|
TestForLogLine("Bootstrap starting!")
|
|
TestForLogLine("Bmp size: 573 x 601")
|
|
TestForLogLine("There is/are 2 check(s) to do")
|
|
TestForLogLine("Update check 1/2 starting")
|
|
TestForLogPassFail("RockstarDownloader doing download.", "Setting error condition")
|
|
TestForLogPassFail("RockstarDownloader checking URL validity...", "Setting error condition")
|
|
TestForLogPassFail("RockstarDownloader checking disk...", "Setting error condition")
|
|
TestForLogPassFail("prelauncher_version.testing.#{USERNAME}.xml to determine length...", "Setting error condition")
|
|
TestForLogPassFail("Update check 1/2 done", "Update check 1/2 failed")
|
|
TestForLogPassFail("Check 1 patch 1 is not installed; queueing.", "Check 1 patch 1 is installed.")
|
|
TestForLogLine("Update check 2/2 starting")
|
|
TestForLogPassFail("launcher_version.testing.#{USERNAME}.xml to determine length...", "Setting error condition")
|
|
TestForLogPassFail("Update check 2/2 done", "Update check 2/2 failed")
|
|
TestForLogPassFail("Check 2 patch 1 is not installed; queueing.", "Check 2 patch 1 is installed.")
|
|
TestForLogLine("Found 2 patches.")
|
|
TestForLogLine("Installing patch")
|
|
TestForLogLine("Install needs restart")
|
|
TestForLogLine("Installer requested restart")
|
|
TestForLogLine("Restart required; exiting")
|
|
|
|
end
|
|
|
|
CloseLogFile()
|
|
|
|
EndSection()
|
|
end
|
|
|
|
def BootstrapLauncherPatchTest()
|
|
|
|
random_arg = "-autotest_random_arg_" + rand(100).to_s
|
|
RsgLog.instance().info("Launching Bootstrap only...")
|
|
result = system(GAME_DIR + "/PlayGTAV.exe", "-autotest_bootstrap_nolauncher", random_arg)
|
|
|
|
StartSection("Launcher Patch Check")
|
|
|
|
AddResultLine("Execution success", result)
|
|
AddResultLine("Process return code == 0", $? == 0)
|
|
|
|
generated_log = false
|
|
OpenLogFile("bootstrap.log")
|
|
generated_log = ContinueLogFileSearch(random_arg)
|
|
CloseLogFile()
|
|
|
|
AddResultLine("Generated a log file", generated_log)
|
|
|
|
CopyAndOpenLogFile("bootstrap.log")
|
|
|
|
if generated_log
|
|
|
|
TestForLogLine("Bootstrap starting!")
|
|
TestForLogLine("Bmp size: 573 x 601")
|
|
TestForLogLine("There is/are 2 check(s) to do")
|
|
TestForLogLine("Update check 1/2 starting")
|
|
TestForLogPassFail("RockstarDownloader doing download.", "Setting error condition")
|
|
TestForLogPassFail("RockstarDownloader checking URL validity...", "Setting error condition")
|
|
TestForLogPassFail("RockstarDownloader checking disk...", "Setting error condition")
|
|
TestForLogPassFail("prelauncher_version.testing.#{USERNAME}.xml to determine length...", "Setting error condition")
|
|
TestForLogPassFail("Update check 1/2 done", "Update check 1/2 failed")
|
|
TestForLogPassFail("Check 1 was up-to-date.", "Check 1 patch 1 is not installed; queueing.")
|
|
TestForLogLine("Update check 2/2 starting")
|
|
TestForLogPassFail("launcher_version.testing.#{USERNAME}.xml to determine length...", "Setting error condition")
|
|
TestForLogPassFail("Update check 2/2 done", "Update check 2/2 failed")
|
|
TestForLogPassFail("Check 2 patch 1 is not installed; queueing.", "Check 2 patch 1 is installed.")
|
|
TestForLogLine("Found 1 patches.")
|
|
TestForLogLine("Installing patch")
|
|
TestForLogLine("About to launch process")
|
|
TestForLogPassFail("Pid:", "Error launching process")
|
|
TestForLogLine("Waiting for handle")
|
|
TestForLogLine("Finished waiting")
|
|
TestForLogPassFail("Successfully installed patch", "Failed to install patch")
|
|
TestForLogLine("No restart required; chaining Launcher")
|
|
|
|
end
|
|
|
|
CloseLogFile()
|
|
|
|
EndSection()
|
|
end
|
|
|
|
def BootstrapLauncherRunTest()
|
|
|
|
random_arg = "-autotest_random_arg_" + rand(100).to_s
|
|
RsgLog.instance().info("Launching Bootstrap (expecting Launcher as well)...")
|
|
result = system(GAME_DIR + "/PlayGTAV.exe", "-autotest_launcher_exitimmediately", random_arg)
|
|
|
|
StartSection("Bootstrap Starting Launcher")
|
|
|
|
AddResultLine("Execution success", result)
|
|
AddResultLine("Process return code == 0", $? == 0)
|
|
|
|
OpenLogFile("bootstrap.log")
|
|
generated_log = ContinueLogFileSearch(random_arg)
|
|
CloseLogFile()
|
|
|
|
AddResultLine("Bootstrap generated a log file", generated_log)
|
|
|
|
CopyAndOpenLogFile("bootstrap.log")
|
|
|
|
if generated_log
|
|
|
|
TestForLogLine("Bootstrap starting!")
|
|
TestForLogLine("Bmp size: 573 x 601")
|
|
TestForLogLine("There is/are 2 check(s) to do")
|
|
TestForLogLine("Update check 1/2 starting")
|
|
TestForLogPassFail("RockstarDownloader doing download.", "Setting error condition")
|
|
TestForLogPassFail("RockstarDownloader checking URL validity...", "Setting error condition")
|
|
TestForLogPassFail("RockstarDownloader checking disk...", "Setting error condition")
|
|
TestForLogPassFail("prelauncher_version.testing.#{USERNAME}.xml to determine length...", "Setting error condition")
|
|
TestForLogPassFail("Update check 1/2 done", "Update check 1/2 failed")
|
|
TestForLogPassFail("Check 1 was up-to-date.", "Check 1 patch 1 is not installed; queueing.")
|
|
TestForLogLine("Update check 2/2 starting")
|
|
TestForLogPassFail("launcher_version.testing.#{USERNAME}.xml to determine length...", "Setting error condition")
|
|
TestForLogPassFail("Update check 2/2 done", "Update check 2/2 failed")
|
|
TestForLogPassFail("Check 2 was up-to-date.", "Check 2 patch 1 is not installed; queueing.")
|
|
TestForLogLine("Found 0 patches.")
|
|
TestForLogLine("No restart required; chaining Launcher")
|
|
|
|
end
|
|
|
|
CloseLogFile()
|
|
|
|
# Give it a moment
|
|
sleep(3)
|
|
|
|
OpenLogFile("launcher.log")
|
|
generated_log = ContinueLogFileSearch(random_arg)
|
|
CloseLogFile()
|
|
|
|
AddResultLine("Launcher generated a log file", generated_log)
|
|
|
|
CopyAndOpenLogFile("launcher.log")
|
|
|
|
CloseLogFile()
|
|
|
|
EndSection()
|
|
end
|
|
|
|
def LauncherRunTest()
|
|
|
|
random_arg = "-autotest_random_arg_" + rand(100).to_s
|
|
RsgLog.instance().info("Launching Launcher...")
|
|
result = system(GAME_DIR + "/GTAVLauncher.exe", random_arg, "-autotest_launcher_selfsignin", "-autotest_launcher_digitalupdate")
|
|
|
|
StartSection("Launcher Startup")
|
|
|
|
AddResultLine("Execution success", result)
|
|
AddResultLine("Process return code == 0", $? == 0)
|
|
|
|
OpenLogFile("launcher.log")
|
|
generated_log = ContinueLogFileSearch(random_arg)
|
|
CloseLogFile()
|
|
|
|
AddResultLine("Launcher generated a log file", generated_log)
|
|
|
|
if !generated_log
|
|
EndSection()
|
|
return
|
|
end
|
|
|
|
CopyAndOpenLogFile("launcher.log")
|
|
|
|
TestForLogLine("Running as admin")
|
|
TestForLogLine("P4 Version")
|
|
TestForLogLine("Waiting for connections on named pipe instance")
|
|
TestForLogLine("Social Club constructed")
|
|
TestForLogLine("Now on background")
|
|
TestForLogLine("Hiding all controls")
|
|
TestForLogLine("Found 0 in-file(s)")
|
|
TestForLogLine("OS version")
|
|
TestForLogLine("Controller monitor thread launched")
|
|
|
|
EndSection()
|
|
|
|
StartSection("Launcher Online Config")
|
|
InsertLogFilePath()
|
|
|
|
TestForLogLine("eDownloadOnlineConfig")
|
|
TestForLogLine("JOB_DOWNLOAD_ONLINE_CONFIG")
|
|
TestForLogLine("launcher_online_config.xml to determine length...")
|
|
TestForLogPassFail("Content length", "error")
|
|
TestForLogPassFail("Download succeeded", "nothing to download")
|
|
|
|
EndSection()
|
|
|
|
|
|
StartSection("Launcher RGSC Update")
|
|
InsertLogFilePath()
|
|
|
|
TestForLogPassFail("Entering state 2 (eCheckForRGSCUpdates)", "eCheckForInPlaceDownload")
|
|
TestForLogLine("Dev server looks accessible")
|
|
|
|
TestForLogPassFail("There is/are 1 check(s) to do.", "error")
|
|
TestForLogPassFail("Update check 1/1 starting...", "error")
|
|
TestForLogPassFail("Getting temp path...", "error")
|
|
TestForLogPassFail("sc_installer_version.xml", "error")
|
|
TestForLogPassFail("XML file: sc_installer_version.xml", "error")
|
|
TestForLogPassFail("Starting downloader synchronously...", "error")
|
|
TestForLogPassFail("Downloading", "error")
|
|
TestForLogPassFail("Selecting regular downloader.", "error")
|
|
TestForLogPassFail("RockstarDownloader starting.", "error")
|
|
TestForLogPassFail("RockstarDownloader doing download.", "error")
|
|
TestForLogPassFail("RockstarDownloader checking URL validity...", "error")
|
|
TestForLogPassFail("RockstarDownloader checking disk...", "error")
|
|
TestForLogPassFail("sc_installer_version.xml to determine length...", "error")
|
|
TestForLogPassFail("Content length", "error")
|
|
TestForLogPassFail("bytes available; we only need", "error")
|
|
TestForLogPassFail("RockstarDownloader receiving data...", "error")
|
|
TestForLogPassFail("GET to patches.rockstargames.com", "error")
|
|
TestForLogPassFail("Download succeeded.", "error")
|
|
|
|
TestForLogPassFail("Parsing XML...", "error")
|
|
TestForLogPassFail("Adding RGSC patch (removing any previous).", "error")
|
|
TestForLogPassFail("Cleaning up XML...", "error")
|
|
TestForLogPassFail("Checking EXE...", "error")
|
|
TestForLogPassFail("File version", "error")
|
|
TestForLogPassFail("Executable version", "error")
|
|
TestForLogPassFail("Checking 1 patches...", "error")
|
|
TestForLogPassFail("Patch 1 is already installed.", "error")
|
|
|
|
TestForLogLine("Finished job: JOB_CHECK_FOR_SC_UPDATES")
|
|
TestForLogPassFail("No patches to install; we are up-to-date.", "Entering state 3 (eSocialClub)")
|
|
|
|
EndSection()
|
|
|
|
|
|
StartSection("Launcher SC Init")
|
|
InsertLogFilePath()
|
|
|
|
TestForLogLine("Entering state 3 (eSocialClub)")
|
|
TestForLogPassFail("Social Club initializing...", "error")
|
|
TestForLogPassFail("Starting Social Club thread...", "error")
|
|
TestForLogPassFail("Loading DLL...", "error")
|
|
TestForLogPassFail("Calling LoadLibraryExA: C:\\Program Files (x86)\\Rockstar Games\\Social Club\\socialclub64.dll", "error")
|
|
TestForLogPassFail("Initialising DLL...", "error")
|
|
TestForLogPassFail("Getting proc address...", "error")
|
|
TestForLogPassFail("Obtaining interfaces...", "error")
|
|
TestForLogPassFail("Setting up ROS interface...", "error")
|
|
TestForLogPassFail("Beginning decryption of ROS Secrets", "error")
|
|
TestForLogPassFail("Beginning Decryption", "error")
|
|
TestForLogPassFail("Finished Decryption", "error")
|
|
TestForLogPassFail("Finished decryption of ROS Secrets", "error")
|
|
TestForLogPassFail("Initialising RGSC...", "error")
|
|
TestForLogPassFail("Setting up SCUI...", "error")
|
|
TestForLogPassFail("Getting activation system...", "error")
|
|
TestForLogPassFail("Notifying SCUI of OnCreateDevice...", "error")
|
|
TestForLogPassFail("Attempting to create device...", "error")
|
|
TestForLogPassFail("Attempting to initialise DX9 COM object...", "error")
|
|
TestForLogPassFail("Created Direct3D.", "error")
|
|
TestForLogPassFail("Window size: 800 x 600", "error")
|
|
TestForLogPassFail("Attempting D3DFMT_UNKNOWN, D3DCREATE_HARDWARE_VERTEXPROCESSING, D3DCREATE_MULTITHREADED...", "error")
|
|
TestForLogPassFail("Successfully created DX Device!", "error")
|
|
TestForLogPassFail("Social Club seems to have loaded correctly.", "error")
|
|
TestForLogPassFail("Entitlement seems to have initialized correctly.", "error")
|
|
TestForLogPassFail("SCUI is ready to accept commands. Showing SCUI...", "error")
|
|
TestForLogPassFail("Pre-rendering...", "error")
|
|
TestForLogPassFail("SCUI is visible.", "error")
|
|
TestForLogPassFail("Showing DxSurface9.", "error")
|
|
TestForLogPassFail("SCUI is ready for commands.", "error")
|
|
TestForLogPassFail("SCUI is visible.", "error")
|
|
|
|
EndSection()
|
|
|
|
|
|
StartSection("Launcher Simulate Logging In")
|
|
InsertLogFilePath()
|
|
|
|
TestForLogLine("SetTextBoxHasFocusV2(true, Nickname/Email")
|
|
TestForLogLine("SetTextBoxHasFocusV2(false")
|
|
TestForLogLine("SetTextBoxHasFocusV2(true, Password")
|
|
TestForLogLine("SetTextBoxHasFocusV2(false")
|
|
TestForLogLine("Signed-in; continuing.")
|
|
|
|
EndSection()
|
|
|
|
|
|
StartSection("Launcher Checking Entitlement")
|
|
InsertLogFilePath()
|
|
|
|
TestForLogPassFail("Entering state 4 (eCheckForEntitlement)", "error")
|
|
TestForLogLine("Erasing completed state (eCheckForEntitlement)")
|
|
|
|
EndSection()
|
|
|
|
|
|
StartSection("Launcher In-place Download")
|
|
InsertLogFilePath()
|
|
|
|
TestForLogLine("Entering state 5 (eCheckForInPlaceDownload)")
|
|
TestForLogPassFail("manifest to determine length", "error")
|
|
TestForLogPassFail("we only need", "error")
|
|
TestForLogPassFail("GET to patches.rockstargames.com", "error")
|
|
TestForLogPassFail("Download succeeded", "error")
|
|
TestForLogPassFail("x64a.rpf missing", "x64a.rpf seems complete")
|
|
TestForLogLine("In-place download is not up-to-date; download required")
|
|
TestForLogLine("Finished job: JOB_CHECK_FOR_IN_PLACE_DOWNLOAD")
|
|
TestForLogLine("Entering state 12 (eGameInstallAvailable)")
|
|
TestForLogLine("Posting SCUI message")
|
|
TestForLogPassFail("AutotestDigitalUpdate pressing update button...", "error")
|
|
TestForLogPassFail("Inserting new flow state at the front: eDownloadInPlaceData", "error")
|
|
TestForLogPassFail("Entering state 13 (eDownloadInPlaceData)", "error")
|
|
|
|
TestForLogLine("Beginning job: JOB_DOWNLOAD_IN_PLACE")
|
|
TestForLogLine("Initialising downloader.")
|
|
TestForLogLine("Starting downloader synchronously...")
|
|
TestForLogLine("Selecting chunked downloader.")
|
|
TestForLogLine("RockstarDownloader starting.")
|
|
TestForLogLine("RockstarDownloader doing download.")
|
|
TestForLogLine("RockstarDownloader checking URL validity...")
|
|
TestForLogLine("RockstarDownloader checking disk...")
|
|
TestForLogLine("to determine length...")
|
|
TestForLogLine("Content length")
|
|
TestForLogLine("available; we only need")
|
|
TestForLogLine("RockstarDownloader receiving data...")
|
|
TestForLogLine("RockstarDownloader starting.")
|
|
TestForLogLine("RockstarDownloader doing download.")
|
|
TestForLogLine("RockstarDownloader checking URL validity...")
|
|
TestForLogLine("RockstarDownloader checking disk...")
|
|
TestForLogLine("hash to determine length...")
|
|
TestForLogLine("Content length")
|
|
TestForLogLine("available; we only need")
|
|
TestForLogLine("RockstarDownloader receiving data...")
|
|
TestForLogLine("GET to patches.rockstargames.com")
|
|
TestForLogLine("Download succeeded.")
|
|
TestForLogLine("[1] Using range header: Range: bytes=")
|
|
TestForLogLine("[1] Sending request.")
|
|
TestForLogLine("[1] 16KB @ ")
|
|
|
|
TestForLogLine("Checking hash")
|
|
TestForLogLine("Hashes match:")
|
|
TestForLogLine("Hash check succeeded.")
|
|
TestForLogLine("Starting another chunk.")
|
|
|
|
TestForLogLine("AutotestDigitalUpdate terminating launcher...")
|
|
|
|
EndSection()
|
|
|
|
|
|
StartSection("Launcher Clean Shutdown")
|
|
InsertLogFilePath()
|
|
|
|
TestForLogPassFail("Entering state 30 (eShuttingDown)", "error")
|
|
TestForLogPassFail("Shutting down.", "error")
|
|
TestForLogPassFail("Joining WorkerThread.", "error")
|
|
TestForLogPassFail("Signaling WorkerThread to stop.", "error")
|
|
TestForLogPassFail("Ending Social Club thread...", "error")
|
|
TestForLogPassFail("Thread no longer running.", "error")
|
|
TestForLogPassFail("Shutting down Entitlement...", "error")
|
|
TestForLogPassFail("Shutting down SCUI...", "error")
|
|
TestForLogPassFail("Shutting down RGSC...", "error")
|
|
TestForLogPassFail("Freeing library...", "error")
|
|
TestForLogPassFail("Uninitializing.", "error")
|
|
TestForLogPassFail("Social Club destructed.", "error")
|
|
|
|
EndSection()
|
|
|
|
CloseLogFile()
|
|
end
|
|
|
|
def LauncherPatchTest()
|
|
|
|
random_arg = "-autotest_random_arg_" + rand(100).to_s
|
|
RsgLog.instance().info("Launching Launcher...")
|
|
result = system(GAME_DIR + "/GTAVLauncher.exe", random_arg, "-autotest_launcher_selfsignin", "-autotest_launcher_gamepatch")
|
|
|
|
StartSection("Launcher Check For Game Patch")
|
|
|
|
AddResultLine("Execution success", result)
|
|
AddResultLine("Process return code == 0", $? == 0)
|
|
|
|
OpenLogFile("launcher.log")
|
|
generated_log = ContinueLogFileSearch(random_arg)
|
|
CloseLogFile()
|
|
|
|
AddResultLine("Launcher generated a log file", generated_log)
|
|
|
|
if !generated_log
|
|
EndSection()
|
|
return
|
|
end
|
|
|
|
CopyAndOpenLogFile("launcher.log")
|
|
|
|
TestForLogLine("Entering state 6 (eCheckForUpdate)")
|
|
TestForLogLine("Beginning job: JOB_CHECK_FOR_GAME_UPDATES")
|
|
TestForLogLine("Dev server has already been checked.")
|
|
TestForLogLine("Initialising downloader.")
|
|
TestForLogLine("Checking for updates...")
|
|
TestForLogLine("There is/are 2 check(s) to do.")
|
|
TestForLogLine("Update check 1/2 starting...")
|
|
TestForLogLine("Clearing temporary patch list...")
|
|
TestForLogLine("Getting temp path...")
|
|
TestForLogLine("gtav_version.xml")
|
|
TestForLogLine("Queueing file...")
|
|
TestForLogLine("Starting file...")
|
|
TestForLogLine("Starting downloader synchronously...")
|
|
TestForLogLine("Downloading")
|
|
TestForLogLine("Selecting regular downloader.")
|
|
TestForLogLine("RockstarDownloader starting.")
|
|
TestForLogLine("RockstarDownloader doing download.")
|
|
TestForLogLine("RockstarDownloader checking URL validity...")
|
|
TestForLogLine("RockstarDownloader checking disk...")
|
|
TestForLogLine("gtav_version.xml to determine length...")
|
|
|
|
TestForLogLine("Content length")
|
|
TestForLogLine("bytes available; we only need")
|
|
TestForLogLine("RockstarDownloader receiving data...")
|
|
TestForLogLine("GET to patches.rockstargames.com")
|
|
|
|
TestForLogLine("Download succeeded.")
|
|
TestForLogLine("Download complete.")
|
|
TestForLogLine("Synchronous downloading ended.")
|
|
TestForLogLine("File finished.")
|
|
TestForLogLine("Parsing XML...")
|
|
TestForLogLine("Adding game patch.")
|
|
TestForLogLine("Cleaning up XML...")
|
|
TestForLogLine("Checking EXE...")
|
|
TestForLogLine("Unable to get file version info size")
|
|
TestForLogLine("The system cannot find the file specified.")
|
|
TestForLogLine("Executable version 0.0.0.0")
|
|
TestForLogLine("Checking 1 patches...")
|
|
TestForLogLine("Patch 1 needs installing.")
|
|
TestForLogLine("Update check 1/2 done.")
|
|
TestForLogLine("Check 1 was not up-to-date; adding patches.")
|
|
TestForLogLine("Check 1 patch 1 is not installed; queueing.")
|
|
TestForLogLine("Update check 2/2 starting...")
|
|
TestForLogLine("Clearing temporary patch list...")
|
|
TestForLogLine("Getting temp path...")
|
|
TestForLogLine("dlc_version.xml")
|
|
TestForLogLine("Selecting regular downloader.")
|
|
TestForLogLine("RockstarDownloader starting.")
|
|
TestForLogLine("RockstarDownloader doing download.")
|
|
TestForLogLine("RockstarDownloader checking URL validity...")
|
|
TestForLogLine("RockstarDownloader checking disk...")
|
|
TestForLogLine("dlc_version.xml to determine length...")
|
|
TestForLogLine("Content length")
|
|
TestForLogLine("bytes available; we only need")
|
|
TestForLogLine("RockstarDownloader receiving data...")
|
|
TestForLogLine("GET to patches.rockstargames.com")
|
|
TestForLogLine("Download succeeded.")
|
|
TestForLogLine("Download complete.")
|
|
TestForLogLine("Synchronous downloading ended.")
|
|
TestForLogLine("File finished.")
|
|
TestForLogLine("Parsing XML...")
|
|
TestForLogLine("Cleaning up XML...")
|
|
TestForLogLine("Checking EXE...")
|
|
TestForLogLine("Executable version 0.0.0.0")
|
|
TestForLogLine("No patches to check.")
|
|
TestForLogLine("Update check 2/2 done.")
|
|
TestForLogLine("Check 2 was up-to-date.")
|
|
TestForLogLine("No more checks.")
|
|
TestForLogLine("We have become mandatory.")
|
|
TestForLogLine("Shutting down downloader.")
|
|
TestForLogLine("Finished job: JOB_CHECK_FOR_GAME_UPDATES")
|
|
TestForLogLine("Erasing completed state (eCheckForUpdate)")
|
|
TestForLogLine("1 patch(es) to install; we are not up-to-date.")
|
|
TestForLogLine("Flow disrupted due to update available.")
|
|
|
|
EndSection()
|
|
|
|
|
|
StartSection("Launcher Download patch")
|
|
InsertLogFilePath()
|
|
|
|
TestForLogLine("Entering state 9 (eRequiredUpdate)")
|
|
TestForLogLine("Hiding all controls.")
|
|
TestForLogLine("Posting SCUI message: ")
|
|
|
|
TestForLogLine("AutotestGamePatch pressing update button...")
|
|
TestForLogLine("Inserting new flow state at the front: eDownloadUpdate")
|
|
TestForLogLine("Entering state 15 (eDownloadUpdate)")
|
|
|
|
TestForLogLine("Beginning job: JOB_DOWNLOAD_UPDATE")
|
|
TestForLogLine("Running patch process.")
|
|
TestForLogLine("We have (or can acquire) admin rights.")
|
|
TestForLogLine("1 patch to process.")
|
|
|
|
TestForLogLine("Attempting to download patch:")
|
|
TestForLogLine("Initialising downloader.")
|
|
TestForLogLine("Starting downloader synchronously...")
|
|
TestForLogLine("Downloading http://patches.rockstargames.com/")
|
|
TestForLogLine("Selecting chunked downloader.")
|
|
TestForLogLine("RockstarDownloader starting.")
|
|
TestForLogLine("RockstarDownloader doing download.")
|
|
TestForLogLine("RockstarDownloader checking URL validity...")
|
|
TestForLogLine("RockstarDownloader checking disk...")
|
|
TestForLogLine(" to determine length...")
|
|
TestForLogLine("Content length")
|
|
TestForLogLine("bytes available; we only need")
|
|
|
|
TestForLogLine("RockstarDownloader starting.")
|
|
TestForLogLine("RockstarDownloader doing download.")
|
|
TestForLogLine("RockstarDownloader checking URL validity...")
|
|
TestForLogLine("RockstarDownloader checking disk...")
|
|
TestForLogLine(".hash to determine length...")
|
|
TestForLogLine("Query failed.")
|
|
|
|
TestForLogLine("[1] Using range header: Range:")
|
|
TestForLogLine("[1] Sending request.")
|
|
TestForLogLine("[1] 16KB @")
|
|
|
|
TestForLogLine("No more incomplete ranges.")
|
|
TestForLogLine("Deleting partfile.")
|
|
TestForLogLine("Download complete.")
|
|
TestForLogLine("Synchronous downloading ended.")
|
|
TestForLogLine("Attempting to install patch.")
|
|
TestForLogLine("Installing patch")
|
|
|
|
|
|
TestForLogLine("About to launch process:")
|
|
TestForLogLine("Pid:")
|
|
TestForLogLine("Waiting for handle")
|
|
TestForLogLine("Finished waiting.")
|
|
TestForLogLine("Exit code: 0")
|
|
TestForLogLine("Successfully installed patch ")
|
|
TestForLogLine("Finished job: JOB_DOWNLOAD_UPDATE")
|
|
TestForLogLine("Erasing completed state (eDownloadUpdate)")
|
|
|
|
EndSection()
|
|
|
|
|
|
StartSection("Launcher Launching Game")
|
|
InsertLogFilePath()
|
|
|
|
TestForLogLine("Entering state 8 (eLoading)")
|
|
TestForLogPassFail("Launching game...", "error")
|
|
TestForLogPassFail("(path: ", "error")
|
|
TestForLogLine("autotest_launcher_gamepatch killing process")
|
|
|
|
EndSection()
|
|
end
|
|
|
|
def LauncherErrorScreenTest(title, extra_args, start_error = 1, stop_error = 50)
|
|
|
|
DeleteSomeFiles()
|
|
|
|
errornumber = start_error
|
|
|
|
StartSection(title)
|
|
AddInfoLine("Adding parameters: " + extra_args.join(","))
|
|
|
|
while errornumber < stop_error
|
|
|
|
# There's no easy way to say this. It's a hack.
|
|
# These cases trigger B*2149495, which need SC DLL support.
|
|
if (errornumber >= 34 && errornumber <= 37)
|
|
AddInfoLine("[Skipping test "+errornumber.to_s+' due to <a href="url:bugstar:2149495">Bug 2149495</a>]');
|
|
errornumber += 1
|
|
next
|
|
end
|
|
|
|
random_arg = "-autotest_random_arg_" + rand(100).to_s
|
|
RsgLog.instance().info("AutotestFail "+errornumber.to_s+"...")
|
|
args = [random_arg, "-autotest_launcher_selfsignin", "-autotest_launcher_errors="+errornumber.to_s, "-autotest_launcher_errors_delay="+$SHOW_ERROR_DELAY.to_s, *extra_args]
|
|
result = system(GAME_DIR + "/GTAVLauncher.exe ", *args)
|
|
|
|
|
|
OpenLogFile("launcher.log")
|
|
generated_log = ContinueLogFileSearch(random_arg)
|
|
|
|
if (!generated_log)
|
|
AddResultLine("Test "+errornumber.to_s+": Generated log file", false)
|
|
CloseLogFile()
|
|
CopyAndOpenLogFile("launcher.log")
|
|
CloseLogFile()
|
|
break
|
|
end
|
|
|
|
errorTest = "Testing error message at "
|
|
test_line = ContinueLogFileSearchReturnLine(errorTest)
|
|
if (test_line == nil)
|
|
break;
|
|
end
|
|
|
|
indexof = test_line.index(errorTest)+errorTest.length
|
|
|
|
test_location = test_line[indexof,test_line.length-indexof-1]
|
|
|
|
test_filename = test_location.gsub(/(.:.*):.*/, "\\1")
|
|
test_linenumber = test_location.gsub(/.:.*:(.*)/, "\\1")
|
|
|
|
test_complete_line = ReadLineFromFile(test_filename, test_linenumber.to_i)
|
|
|
|
test_location.gsub!("\\","/")
|
|
|
|
test_file = test_location.gsub(/(.:.*):.*/, "file:///\\1")
|
|
|
|
test_location = errornumber.to_s + ": at <a href=\"#{test_file}\">#{test_location}</a>"
|
|
|
|
if ($? != 0)
|
|
AddResultLine(test_location, false)
|
|
AddCodeLine(test_complete_line)
|
|
AddInfoLine("Test "+errornumber.to_s+": Appears to have crashed. Exit code: "+$?.to_s)
|
|
CloseLogFile()
|
|
CopyAndOpenLogFile("launcher.log")
|
|
CloseLogFile()
|
|
break
|
|
end
|
|
|
|
saw_error_screen = ContinueLogFileSearch("AutotestFail: Notified about reaching error screen")
|
|
if saw_error_screen
|
|
|
|
AddResultLine(test_location, true)
|
|
AddCodeLine(test_complete_line)
|
|
|
|
line = ContinueLogFileSearchReturnLine("AutotestFail: Error state:")
|
|
if (line != nil)
|
|
line.gsub!(/.*Error state:(.*)/,'\\1')
|
|
AddSmallInfoLine("Reached error screen:" + line)
|
|
else
|
|
AddSmallInfoLine("Reached error screen.")
|
|
end
|
|
|
|
line = ContinueLogFileSearchReturnLine("AutotestFail: UI state:")
|
|
if (line != nil)
|
|
line.gsub!(/.*(UI state:.*)/,'\\1')
|
|
AddSmallInfoLine(line)
|
|
end
|
|
|
|
else
|
|
retrying = ContinueLogFileSearch("AutotestFail: Notified about retrying")
|
|
|
|
if (retrying)
|
|
AddResultLine(test_location, true)
|
|
AddCodeLine(test_complete_line)
|
|
AddInfoLine("Retried.")
|
|
else
|
|
if (ContinueLogFileSearch("AutotestFail: Notified that this is not an error"))
|
|
AddResultLine(test_location, true)
|
|
AddCodeLine(test_complete_line)
|
|
AddInfoLine("Not an error.")
|
|
else
|
|
AddResultLine(test_location, false)
|
|
AddCodeLine(test_complete_line)
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
CloseLogFile()
|
|
|
|
if (!saw_error_screen)
|
|
CopyAndOpenLogFile("launcher.log")
|
|
CloseLogFile()
|
|
end
|
|
|
|
errornumber = errornumber + 1
|
|
end
|
|
|
|
EndSection()
|
|
end
|
|
|
|
# Okay! We're done. Now lets actually launch the stuff....
|
|
|
|
# Handle showing a single error code
|
|
|
|
if parameters.showerror
|
|
puts
|
|
puts
|
|
puts "Which section is the error screen from? (1 or 2)"
|
|
section = gets.to_i
|
|
puts "Which error number would you like? (see automated email for numbers)"
|
|
errornumber = gets.to_i
|
|
|
|
args = ["-autotest_launcher_selfsignin", "-autotest_launcher_errors="+errornumber.to_s, "-noautokill"]
|
|
if section == 1
|
|
args.push("-autotest_launcher_digitalupdate")
|
|
else
|
|
args.push("-autotest_launcher_gamepatch")
|
|
end
|
|
|
|
system(GAME_DIR + "/GTAVLauncher.exe ", *args)
|
|
|
|
exit
|
|
end
|
|
|
|
StartReport()
|
|
if !parameters.errorsonly
|
|
if !parameters.skipupload
|
|
BootstrapSelfPatchTest()
|
|
Countdown(10)
|
|
BootstrapLauncherPatchTest()
|
|
Countdown(3)
|
|
end
|
|
BootstrapLauncherRunTest()
|
|
Countdown(5)
|
|
LauncherRunTest()
|
|
Countdown(3)
|
|
LauncherPatchTest()
|
|
Countdown(3)
|
|
end
|
|
if !parameters.noerrors
|
|
LauncherErrorScreenTest("Launcher Error Screens 1", ["-autotest_launcher_digitalupdate"], 1, 60)
|
|
LauncherErrorScreenTest("Launcher Error Screens 2", ["-autotest_launcher_gamepatch"], 27, 65)
|
|
|
|
#TODO: Test https://dev.ros.rockstargames.com/scui/v2/api/test/resetrecaptcha
|
|
|
|
end
|
|
EndReport()
|
|
|
|
#log.info("Report: "+$reportHtml)
|
|
|
|
File.open("report.html", "w") do |f|
|
|
f.write($reportHtml)
|
|
end
|
|
|
|
if (parameters.emailfrom != nil) && (parameters.emailto != nil)
|
|
log.info("Mailing out results...")
|
|
|
|
log.info("From: #{parameters.emailfrom}")
|
|
log.info("To: #{parameters.emailto}")
|
|
|
|
recipients = parameters.emailto.split(";")
|
|
log.info(recipients.size.to_s + " recipients")
|
|
|
|
message = "From: Launcher Automated Test Tool <#{parameters.emailfrom}>\n"
|
|
message += "To: "
|
|
|
|
first = true
|
|
recipients.each { |i|
|
|
if first
|
|
first = false
|
|
else
|
|
message += ", "
|
|
end
|
|
message += "<#{i}>"
|
|
log.info(" #{i}")
|
|
}
|
|
message += "MIME-Version: 1.0\n"
|
|
message += "Content-type: text/html\n"
|
|
message += "Subject: Launcher Automated Test Results\n\n"
|
|
|
|
message += $reportHtml
|
|
|
|
Net::SMTP.start('smtp.rockstar.t2.corp') do |smtp|
|
|
smtp.send_message message, parameters.emailfrom, recipients
|
|
end
|
|
end
|
|
|
|
# Exiting
|
|
log.info("All done! Exiting!")
|