Files
2025-09-29 00:52:08 +02:00

435 lines
17 KiB
Python
Executable File

#this file requires python 2.5 or above, NOT version 3 or above, see http://www.python.org/download/ for details
import os,sys,time,random,hashlib
#note, these requires the Python Image Libary, see http://www.pythonware.com/products/pil/
import Image, ImageDraw, ImageFont, ImageColor
#this has to match the values at the top of core/watermark.h
watermarkDummy = "255,255,255 040,040 0ce168e9add4baa830e2249afca6a63af33b192e1636d4efadb85bbd2aeb4f32c4b083a5d83c3222b052be5bf403ea4a3d34a133c598b36ac14ed50c5d04ea41"
imageName = "bokeh_pent"
imageSize = (512, 256)
edgeName = "bokeh_deca"
pathForFinalDDS = "X:/gta5/art/ng/textures/graphics_pc/"
def GetCurrentTimeString():
return time.strftime("%A, %d %B %Y %H:%M:%S", time.gmtime())
def CreateDirectory(path):
if not os.path.exists(path):
os.makedirs(path)
def ConvertToDDS(pathToFile):
print("Generating DDS for " + pathToFile)
os.system(r"nvidia_dds_converter\nvcompress.exe -bc3 " + pathToFile)
#delete the intermediary
os.remove(pathToFile)
class Watermark:
name = ""
colour = ()
pos = ()
def IsValid(this):
return this.name and len(this.colour) is 3 and len(this.pos) is 2
class WatermarkInfo:
imageWatermarkString = ""
imageFont = ""
imageFontSize = -1
imageBorder = -1
watermarks = []
def ParseFromFile(this, filePath):
#open and read the content of the config file
watermarkConfigFile = open(filePath, mode="r")
#work out whats on the current line
currentLine = watermarkConfigFile.readline()
currentWatermark = Watermark()
while currentLine:
#ignore lines that don't contain equals
if currentLine.find("=") != - 1:
#ignore any comments
if currentLine.find("#"):
currentLine = currentLine.split("#")[0]
#get the token and value pair, the strip any whitespace or quotes we arn't interested in
token, value = currentLine.split("=", 2)
token = token.strip()
value = value.strip()
value = value.strip("\"")
#if these match any of the token we process them
if token == "image_watermark_string":
this.imageWatermarkString = value
elif token == "image_font":
this.imageFont = value
elif token == "image_font_size":
this.imageFontSize = int(value)
elif token == "image_border":
this.imageBorder = int(value)
elif token == "watermark":
currentWatermark.name = value
elif token == "colour":
#parse colour
colourSplit = value.split(" ")
if len(colourSplit) is not 3:
print(value)
raise Exception("Error, the config file did not contain a valid colour entry")
currentWatermark.colour = (int(colourSplit[0]), int(colourSplit[1]), int(colourSplit[2]))
elif token == "pos":
#parse position
posSplit = value.split(" ")
if len(posSplit) is not 2:
raise Exception("Error, the config file did not contain a valid pos entry")
currentWatermark.pos = (int(posSplit[0]), int(posSplit[1]))
#if we have a valid entry , then add it and clear the current
if currentWatermark.IsValid():
this.watermarks.append(currentWatermark)
currentWatermark = Watermark()
currentLine = watermarkConfigFile.readline()
watermarkConfigFile.close()
if not this.imageWatermarkString:
raise Exception("Error, the config file did not contain a valid image_watermark_string entry")
if not this.imageFont:
raise Exception("Error, the config file did not contain a valid image_font entry")
if this.imageFontSize < 0:
raise Exception("Error, the config file did not contain a valid image_font_size entry")
if this.imageBorder < 0:
raise Exception("Error, the config file did not contain a valid image_border entry")
if len(this.watermarks) == 0:
raise Exception("Error, the config file did not contain any valid watermark entry")
def GetImageWatermakString(this, watermark):
finalString = this.imageWatermarkString.replace(r"\n", "\n")
finalString = finalString.replace("<name>", watermark)
finalString = finalString.replace("<date>", GetCurrentTimeString())
return finalString
def GenerateWatermark(watermarkString, isDummy = False):
#create a new hasher using the sha512 algorithm
hash = hashlib.new('sha512')
hash.update(watermarkString);
if isDummy:
#if isDummy is set get an easily pasteable dummy value we can use to make sure the buffer is the right size
#the hex digest is twice the useal size, but the proper version pads itself to be the same size
random.seed("bertmcbertson")
secureHash = hash.hexdigest()
else:
#create a hash from the supplied string
sha512Hash = hash.hexdigest()
#seed the random number generator with the hash, plus a magic word
random.seed(sha512Hash + "malefactor")
xorResult = ""
#got through the hash converting the value from a char to long and then xor it against a long with random bits
for i in range(len(sha512Hash)):
xorResult += str(long(ord(sha512Hash[i])) ^ random.getrandbits(32))
#update the has with this value and generate a byte digest
hash.update(xorResult)
sha512Hash = hash.digest()
#reverse the hash
sha512Hash = sha512Hash[::-1]
#create some padding to go around the hash
hash.update(str(random.getrandbits(32)))
padding = hash.digest()
#create the final string, using the padding
padPoint = random.randrange(16, 42)
secureHash = padding[0:padPoint] + sha512Hash + padding[padPoint:64]
return secureHash
def EmbedWatermark(name, data, watermark, date, outputPath):
#apply watermark
finalWatermark = GenerateWatermark(watermark.name + date, False)
#add on the colour and pos info
red = str(watermark.colour[0]).zfill(3)
green = str(watermark.colour[1]).zfill(3)
blue = str(watermark.colour[2]).zfill(3)
xpos = str(watermark.pos[0]).zfill(3)
ypos = str(watermark.pos[1]).zfill(3)
finalWatermark = red + "," + green + "," + blue + " " + xpos + "," + ypos + " " + finalWatermark;
if len(watermarkDummy) != len(finalWatermark):
print("\"" + watermarkDummy + "\" l:" + str(len(watermarkDummy)))
print("\"" + finalWatermark + "\" l:" + str(len(finalWatermark)))
raise Exception("Error, watermark produced is a different length, this won't work!")
outputData = data.replace(watermarkDummy, finalWatermark)
#write out exec and info
outputDirectory = outputPath + "/" + watermark.name + "/"
CreateDirectory(outputDirectory)
outputExec = open(outputDirectory + name, mode="wb")
outputExec.write(outputData)
outputExec.close()
#ouput the info file which contains the watermark indside the exec, this can then be compaired later
outputInfo = open(outputDirectory + watermark.name + ".wmi", mode="w")
outputInfo.write("Watermark \"" + watermark.name + "\" was embedded on " + date + ", the watermark is:\n" + finalWatermark)
outputInfo.close()
def GenerateWatermarkedExecs(pathToConfigFile, pathToExec, outputDirectory):
#read the watermark info file
print("Reading watermark config file...")
info = WatermarkInfo()
info.ParseFromFile(pathToConfigFile)
print("Source exec = " + pathToExec + ", output path = " + outputDirectory + ", number of exec to generate = " + str(len(info.watermarks)))
#load the specified exec
execFile = open(pathToExec, mode="rb")
execData = execFile.read()
execFile.close()
#check the dummy watermark is present
if execData.find(watermarkDummy) == -1:
raise Exception("Error, the specified watermark was not found in this exec, check the start value at the top of watermarker.py matches the one in device_win32.cpp")
#create new execs based on the watermarks supplied
for watermark in info.watermarks:
currentTime = GetCurrentTimeString()
print("Embeding watermark \"" + watermark.name + "\" on " + currentTime)
execNameOnly = pathToExec;
#make sure we have only the exec name and not the path as well
if execNameOnly.find("/"):
execNameOnly = execNameOnly[execNameOnly.rfind("/") + 1:]
if execNameOnly.find("\\"):
execNameOnly = execNameOnly[execNameOnly.rfind("\\") + 1:]
EmbedWatermark(execNameOnly, execData, watermark, GetCurrentTimeString(), outputDirectory)
print("Completed!")
def DrawText(draw, string, colour, font, startYPos, border):
currentDrawPosY = startYPos
textLeftToDraw = string
stringSplitPoint = len(textLeftToDraw)
fontHeight = draw.textsize("Font", font = font)[1]
longestline = 0
while textLeftToDraw != "":
while draw.textsize(textLeftToDraw[:stringSplitPoint], font = font)[0] > imageSize[0] - border * 2:
stringSplitPoint -= 1
spaceSplit = textLeftToDraw[:stringSplitPoint].rfind(" ")
if spaceSplit > 0 and len(textLeftToDraw[:stringSplitPoint]) != len(textLeftToDraw):
stringSplitPoint = spaceSplit
lineLength = draw.textsize(textLeftToDraw[:stringSplitPoint].strip(), font = font)[0]
if lineLength > longestline:
longestline = lineLength
#invert the colour so we get something that stands out
strokeColour = (255 - colour[0], 255 - colour[1], 255 - colour[2])
strokeSize = 1
#draw stroke outline
draw.text((border - strokeSize, border + currentDrawPosY - strokeSize), textLeftToDraw[:stringSplitPoint].strip(), font = font, fill = strokeColour)
draw.text((border + strokeSize, border + currentDrawPosY - strokeSize), textLeftToDraw[:stringSplitPoint].strip(), font = font, fill = strokeColour)
draw.text((border - strokeSize, border + currentDrawPosY + strokeSize), textLeftToDraw[:stringSplitPoint].strip(), font = font, fill = strokeColour)
draw.text((border + strokeSize, border + currentDrawPosY + strokeSize), textLeftToDraw[:stringSplitPoint].strip(), font = font, fill = strokeColour)
#draw the main part of the text
draw.text((border, border + currentDrawPosY), textLeftToDraw[:stringSplitPoint].strip(), font = font, fill = colour)
textLeftToDraw = textLeftToDraw[stringSplitPoint:].strip()
stringSplitPoint = len(textLeftToDraw)
currentDrawPosY += fontHeight
return currentDrawPosY, longestline
def GenerateWatermarkImages(pathToConfigFile, outputDirectory):
#read the watermark info file
print("Reading watermark config file...")
info = WatermarkInfo()
info.ParseFromFile(pathToConfigFile)
print("output path = " + outputDirectory + ", number of images to generate = " + str(len(info.watermarks)) * 2)
#assumes windows is on c:
font = ImageFont.truetype("c:/windows/fonts/" + info.imageFont + ".ttf", info.imageFontSize)
#loop through the watermarks and draw an image for each one
for watermark in info.watermarks:
print("Creating image watermark \"" + watermark.name + "\" on " + GetCurrentTimeString())
outputDirectory = outputDirectory + "/" + watermark.name + "/"
CreateDirectory(outputDirectory)
#create a new image
image = Image.new("RGBA", imageSize, (0,0,0,0))
draw = ImageDraw.Draw(image)
yPos = 0
width = 0
#render the text
stringToDraw = info.GetImageWatermakString(watermark.name)
lines = stringToDraw.split('\n')
for line in lines:
yPos, longestline = DrawText(draw, line, watermark.colour, font, yPos, info.imageBorder)
if longestline > width:
width = longestline
#clear out any area of the texture that isn't border or string
draw.rectangle([0, info.imageBorder * 2 + yPos, imageSize[0], imageSize[1]], fill = (0,0,0,0))
draw.rectangle([width + info.imageBorder * 2, 0, imageSize[0], imageSize[1]], fill = (0,0,0,0))
image.save(pathForFinalDDS + imageName + ".tga","TGA")
#use the nVidia dds convertor to spit out a DDS file
ConvertToDDS(pathForFinalDDS + imageName + ".tga")
#genereate the secret watermark
GenerateEdgeWatermark(watermark.name, outputDirectory + "secret_ref.tga", False)
GenerateEdgeWatermark(watermark.name, pathForFinalDDS + edgeName + ".tga", True)
print("Complete!")
#sigh, python 2.5 doesn't have a in build inbinary convertor, don't want to upgrade in case the build machines don't have it :/
def ToBinaryString(number):
out = []
if number == 0: out.append('0')
while number > 0:
out.append('01'[number & 1])
number >>= 1
return ''.join(reversed(out))
def GenerateEdgeWatermark(string, outputPath, final):
#if final make the iimage more tranparent so it blends into the scene
if final:
image = Image.new("RGBA", (128, 4), (0, 0, 0, 0))
else:
image = Image.new("RGBA", (128, 4), (255, 255, 255, 255))
draw = ImageDraw.Draw(image)
xPos = 0;
if final:
color = (128,128,128,70)
else:
color = (0,0,0,255)
#draw a double height line for the start
draw.line([0, 0, 0, 1], fill = color)
xPos += 1
#output the string as binary pixel sequence
for letter in string:
letterAsBinary = ToBinaryString(ord(letter))
for bin in letterAsBinary:
if bin == "0":
draw.line([xPos, 0, xPos, 0], fill = color)
xPos += 1
#draw a double line for the end
draw.line([xPos, 0, xPos, 1], fill = color)
image.save(outputPath,"TGA")
#use the nVidia dds convertor to spit out a DDS file
if final:
ConvertToDDS(outputPath)
def SearchForWatermarkInfo(execData, dirname, filenames):
foundWatermark = False
print("Seaching " + dirname +"..")
for file in filenames:
#load the watermark info file in
if file.find(".wmi") is not -1:
watermarkInfo = open(dirname + "/" + file, mode="r")
watermarkInfo.readline() #the first line is a text discription of the watermark
watermark = watermarkInfo.read()
watermarkInfo.close()
#check to see if it contains the watermark we're looking for
if execData.find(watermark) != -1:
print("\tWatermark file, " + file + " is a MATCH for the watermark found in the supplied exec")
foundWatermark = True
if foundWatermark == False:
print("\tCouldn't find any watermarks which matched the supplied exec")
def VerifyWatermark(pathToSourceExec, pathToSearch):
print("Exec to check = " + pathToSourceExec)
#load the specified exec
execFile = open(pathToSourceExec, mode="rb")
execData = execFile.read()
execFile.close()
#look through the watermark info directory to find a match
os.path.walk(pathToSearch, SearchForWatermarkInfo, execData)
def GenerateDummy():
#genrate a dummy value that is then replaced by the real watermark
print("Dummy Value:")
print("\n" + GenerateWatermark("Watermark" + GetCurrentTimeString(), True) + "\n")
def Watermarker():
params = sys.argv[1:]
if len(params) > 0:
if params[0] == "-embed":
GenerateWatermarkedExecs(params[1], params[2], params[3])
sys.exit()
elif params[0] == "-image":
GenerateWatermarkImages(params[1], params[2])
sys.exit()
elif params[0] == "-verify":
VerifyWatermark(params[1], params[2])
sys.exit()
elif params[0] == "-codedummy":
GenerateDummy()
sys.exit()
print("Help:")
print("-embed <config file> <source exec> <output path>")
print("Embeds the watermark, using the specified config file\n")
print("-image, <watermarks config file> <output path>")
print("Generates image based watermark, using the specified config file\n")
print("-verify <source exec> <search path>")
print("verify the watermark against the generate watermark info files in the search path\n")
print("-codedummy")
print("Generates a dummy string that can be use to locate the string in the exec and to make sure the array is the same size as the actual watermark.")
Watermarker()