Commit af2616a2 authored by Jai Luthra's avatar Jai Luthra Committed by GitHub

Merge pull request #682 from reloxx13/add_gh_action

Add github action for windows patches
parents 4ce52a4b 2fa2dc6b
# GitHub Actions usage
To create patches to a new driver version simply create a new release with the following name schema:
- For DCH: `win-dch-536.67`
- For Studio: `win-studio-536.67`
- If you need to rerun append `-{try}` e.g. `win-dch-536.67-2`
Tagname same as release name.
If the patch file exist for a version, they will get deleted and recreated.
The patches will be added as asset to the release.
> Currently only for windows10 patches
\ No newline at end of file
name: Generate Windows patches
on:
release:
types:
- created
permissions:
contents: write
jobs:
build:
runs-on: ubuntu-latest
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: Check release name and OS
id: check_release
run: |
release_name="${{ github.event.release.tag_name }}"
echo "Release Name: $release_name"
if [[ $release_name =~ (win)-(dch|studio)-([0-9]+\.[0-9]+(-[a-zA-Z]+)?)(-.+)? ]]; then
os="${BASH_REMATCH[1]}"
variant="${BASH_REMATCH[2]}"
version="${BASH_REMATCH[3]}"
echo "Operating System: $os"
echo "Variant: $variant"
echo "Version: $version"
if [ "$os" != "win" ]; then
echo "Not a Windows release. Stopping the CI workflow."
exit 0
fi
if [ "$variant" == "dch" ]; then
variant="DCH"
fi
echo "OS=$os" >> $GITHUB_ENV
echo "VARIANT=$variant" >> $GITHUB_ENV
echo "VERSION=$version" >> $GITHUB_ENV
else
echo "Invalid release name format. Must be in the format 'win-dch-123.45' or 'win-studio-123.45'"
exit 1
fi
- name: Checkout repository
uses: actions/checkout@v3
with:
ref: master
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Delete Existing Files
run: |
echo "Deleting existing files if they exist"
rm -f "${{ github.workspace }}/win/win10_x64/${{ env.VERSION }}/nvencodeapi64.1337"
rm -f "${{ github.workspace }}/win/win10_x64/${{ env.VERSION }}/nvencodeapi.1337"
echo "Existing files deleted successfully"
- name: Run autopatch.py
run: |
echo "Running autopatch.py with version ${{ env.VERSION }}"
cd "${{ github.workspace }}/win/tools/autopatch"
python autopatch.py ${{ env.VERSION }}
echo "autopatch.py executed successfully"
- name: Run add_driver.py
run: |
echo "Running add_driver.py with variant ${{ env.VARIANT }} and version ${{ env.VERSION }}"
cd "${{ github.workspace }}/tools/readme-autogen"
python add_driver.py -W -P GeForce --variant ${{ env.VARIANT }} -w win10 ${{ env.VERSION }}
echo "add_driver.py executed successfully"
- name: Run readme_autogen.py
run: |
echo "Running readme_autogen.py"
cd "${{ github.workspace }}/tools/readme-autogen"
python readme_autogen.py
echo "readme_autogen.py executed successfully"
- name: Commit and push changes
run: |
echo "Committing and pushing changes"
cd "${{ github.workspace }}"
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
git add -A
git diff --quiet --exit-code --cached || git commit -m "${{ env.OS }}: add support for ${{ env.VARIANT }} driver ${{ env.VERSION }}"
git push origin master
echo "Committed and pushed changes"
- name: Upload Patch Files
uses: softprops/action-gh-release@v1
with:
files: |
${{ github.workspace }}/win/win10_x64/${{ env.VERSION }}/nvencodeapi64.1337
${{ github.workspace }}/win/win10_x64/${{ env.VERSION }}/nvencodeapi.1337
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG_NAME: ${{ github.event.release.tag_name }}
...@@ -111,5 +111,4 @@ venv.bak/ ...@@ -111,5 +111,4 @@ venv.bak/
# dont commit 7zip files 7z.dll 7z.exe # dont commit 7zip files 7z.dll 7z.exe
7z.* 7z.*
temp/*
!.gitempty !.gitempty
\ No newline at end of file
#!/usr/bin/env python3 #!/usr/bin/env python3
import argparse import argparse
import sys import functools
import itertools
import os.path
import subprocess import subprocess
import sys
import tempfile import tempfile
import os.path
from binascii import unhexlify
import xml.etree.ElementTree as ET
import itertools
import functools
import urllib.request import urllib.request
import xml.etree.ElementTree as ET
from binascii import unhexlify
CRLF = b"\x0d\x0a" CRLF = b"\x0d\x0a"
HEADER_FORMAT = b">%s" HEADER_FORMAT = b">%s"
...@@ -19,7 +18,6 @@ OFFSET_ADJUSTMENT = 0xC00 # shift specific to x64dbg .1337 format ...@@ -19,7 +18,6 @@ OFFSET_ADJUSTMENT = 0xC00 # shift specific to x64dbg .1337 format
def parse_args(): def parse_args():
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description="Generates .1337 patch for Nvidia drivers for Windows", description="Generates .1337 patch for Nvidia drivers for Windows",
formatter_class=argparse.ArgumentDefaultsHelpFormatter) formatter_class=argparse.ArgumentDefaultsHelpFormatter)
...@@ -44,7 +42,7 @@ def parse_args(): ...@@ -44,7 +42,7 @@ def parse_args():
"nvencodeapi.dll", "nvencodeapi.dll",
], ],
help="name(s) of installed target file. Used for patch " help="name(s) of installed target file. Used for patch "
"header") "header")
parser.add_argument("-P", "--patch-name", parser.add_argument("-P", "--patch-name",
nargs="+", nargs="+",
default=[ default=[
...@@ -72,7 +70,7 @@ def parse_args(): ...@@ -72,7 +70,7 @@ def parse_args():
parser.add_argument("-D", "--direct", parser.add_argument("-D", "--direct",
action="store_true", action="store_true",
help="supply patched library directly instead of " help="supply patched library directly instead of "
"installer file") "installer file")
args = parser.parse_args() args = parser.parse_args()
return args return args
...@@ -84,9 +82,11 @@ class ExtractException(Exception): ...@@ -84,9 +82,11 @@ class ExtractException(Exception):
class PatternNotFoundException(Exception): class PatternNotFoundException(Exception):
pass pass
class MultipleOccurencesException(Exception): class MultipleOccurencesException(Exception):
pass pass
class UnknownPlatformException(Exception): class UnknownPlatformException(Exception):
pass pass
...@@ -145,26 +145,26 @@ def make_patch(archive, *, ...@@ -145,26 +145,26 @@ def make_patch(archive, *,
arch_tgt, arch_tgt,
search, search,
replacement, replacement,
tmpdir,
sevenzip="7z", sevenzip="7z",
direct=False): direct=False):
if direct: if direct:
with open(archive, 'rb') as fo: with open(archive, 'rb') as fo:
f = fo.read() f = fo.read()
else: else:
with tempfile.TemporaryDirectory() as tmpdir: with ExtractedTarget(archive,
with ExtractedTarget(archive, tmpdir,
tmpdir, arch_tgt,
arch_tgt, sevenzip=sevenzip) as tgt:
sevenzip=sevenzip) as tgt: if tgt.endswith(".dll"):
if tgt.endswith(".dll"): with open(tgt, 'rb') as fo:
with open(tgt, 'rb') as fo: f = fo.read()
f = fo.read() else:
else: f = expand(tgt, sevenzip=sevenzip)
f = expand(tgt, sevenzip=sevenzip)
offset = f.find(search) offset = f.find(search)
if offset == -1: if offset == -1:
raise PatternNotFoundException("Pattern not found.") raise PatternNotFoundException("Pattern not found.")
if f[offset+len(search):].find(search) != -1: if f[offset + len(search):].find(search) != -1:
raise MultipleOccurencesException("Multiple occurences of pattern found!") raise MultipleOccurencesException("Multiple occurences of pattern found!")
del f del f
print("Pattern found @ %016X" % (offset,), file=sys.stderr) print("Pattern found @ %016X" % (offset,), file=sys.stderr)
...@@ -181,7 +181,7 @@ def identify_driver(archive, *, sevenzip="7z"): ...@@ -181,7 +181,7 @@ def identify_driver(archive, *, sevenzip="7z"):
manifest = extract_single_file(archive, "setup.cfg", sevenzip=sevenzip) manifest = extract_single_file(archive, "setup.cfg", sevenzip=sevenzip)
root = ET.fromstring(manifest) root = ET.fromstring(manifest)
version = root.attrib['version'] version = root.attrib['version']
product_type = root.find('./properties/string[@name="ProductType"]')\ product_type = root.find('./properties/string[@name="ProductType"]') \
.attrib['value'] .attrib['value']
return version, product_type return version, product_type
...@@ -192,92 +192,109 @@ def format_patch(diff, filename): ...@@ -192,92 +192,109 @@ def format_patch(diff, filename):
res += LINE_FORMAT % (offset + OFFSET_ADJUSTMENT, left, right) res += LINE_FORMAT % (offset + OFFSET_ADJUSTMENT, left, right)
return res return res
def patch_flow(installer_file, search, replacement, target, target_name, patch_name, *, def patch_flow(installer_file, search, replacement, target, target_name, patch_name, *,
direct=False, stdout=False, sevenzip="7z"): tempdir, direct=False, stdout=False, sevenzip="7z"):
search = unhexlify(search) search = unhexlify(search)
replacement = unhexlify(replacement) replacement = unhexlify(replacement)
assert len(search) == len(replacement), "len() of search and replacement"\ assert len(search) == len(replacement), "len() of search and replacement is not equal"
" is not equal"
# Check if installer file exists or try to download
# check if installer file exists or try to download
if not os.path.isfile(installer_file): #installer file does not exists, get url for download print(f"Search for installer file `{installer_file}`...")
if not installer_file.startswith("http"): #installer_file is a version, parse to url if not os.path.isfile(installer_file):
filename = installer_file+"-desktop-win10-win11-64bit-international-dch-whql.exe" print("Installer file is not a file...")
installer_file = "https://international.download.nvidia.com/Windows/"+installer_file+"/"+filename if not installer_file.startswith("http"):
else: # installer_file is an url print("Installer file is not a URL...")
filename = os.path.basename(installer_file)
# download installer and save in .temp # Construct URL from version
print("Installer file is a version!")
if not os.path.isfile(os.path.join('temp', filename)): # check if file already downloaded filename = installer_file + "-desktop-win10-win11-64bit-international-dch-whql.exe"
print(f"Downloading... ( {installer_file} TO {os.path.join('temp', filename)} )") installer_url = f"https://international.download.nvidia.com/Windows/{installer_file}/{filename}"
print("This may take a while (~800MB)")
urllib.request.urlretrieve(installer_file, os.path.join('temp', filename))
installer_file = os.path.join('temp', filename)
else: else:
installer_file = os.path.join('temp', filename) print("Installer file is a URL!")
print(f"Use downloaded file in `{installer_file}`") installer_url = installer_file
if installer_url:
try:
patch = make_patch(installer_file, file_path = os.path.join(tempdir, os.path.basename(installer_url))
arch_tgt=target, if not os.path.isfile(file_path):
search=search, with urllib.request.urlopen(installer_url) as response, open(file_path, 'wb') as out_file:
replacement=replacement, print(f"Downloading... ({installer_url} TO {file_path})")
sevenzip=sevenzip, print("This may take a while (~800MB)")
direct=direct) out_file.write(response.read())
patch_content = format_patch(patch, target_name) print("Download completed successfully!")
if stdout: installer_file = file_path
with open(sys.stdout.fileno(), mode='wb', closefd=False) as out: else:
out.write(patch_content) print(f"Using downloaded file in '{file_path}'")
elif direct: installer_file = file_path
with open(patch_name, mode='wb') as out: except (urllib.error.URLError, Exception) as e:
out.write(patch_content) print(f"Failed to download the file: {e}")
else: return
version, product_type = identify_driver(installer_file, except Exception as e:
sevenzip=sevenzip) print(f"An error occurred during download: {str(e)}")
drv_prefix = { return
"100": "quadro_",
"103": "quadro_",
"300": "",
"301": "nsd_",
"303": "", # DCH
"304": "nsd_",
}
installer_name = os.path.basename(installer_file).lower()
if 'winserv2008' in installer_name:
os_prefix = 'ws2012_x64'
elif 'winserv-2012' in installer_name:
os_prefix = 'ws2012_x64'
elif 'winserv-2016' in installer_name:
os_prefix = 'ws2016_x64'
elif 'win10' in installer_name:
os_prefix = 'win10_x64'
elif 'win7' in installer_name:
os_prefix = 'win7_x64'
else: else:
raise UnknownPlatformException("Can't infer platform from filename %s" print(f"Invalid installer file or version: {installer_file}")
% (repr(installer_name),)) return
driver_name = drv_prefix[product_type] + version
out_dir = os.path.join( # Rest of the code remains the same...
os.path.dirname( patch = make_patch(installer_file,
os.path.abspath(__file__)), '..', '..', os_prefix, driver_name) arch_tgt=target,
os.makedirs(out_dir, 0o755, True) search=search,
out_filename = os.path.join(out_dir, replacement=replacement,
patch_name) tmpdir=tempdir,
with open(out_filename, 'xb') as out: sevenzip=sevenzip,
out.write(patch_content) direct=direct)
patch_content = format_patch(patch, target_name)
if stdout:
sys.stdout.buffer.write(patch_content)
elif direct:
with open(patch_name, mode='wb') as out:
out.write(patch_content)
else:
version, product_type = identify_driver(installer_file, sevenzip=sevenzip)
drv_prefix = {
"100": "quadro_",
"103": "quadro_",
"300": "",
"301": "nsd_",
"303": "", # DCH
"304": "nsd_",
}
installer_name = os.path.basename(installer_file).lower()
if 'winserv2008' in installer_name or 'winserv-2012' in installer_name:
os_prefix = 'ws2012_x64'
elif 'winserv-2016' in installer_name or 'win10' in installer_name:
os_prefix = 'win10_x64'
elif 'win7' in installer_name:
os_prefix = 'win7_x64'
else:
raise UnknownPlatformException(f"Can't infer platform from filename {installer_name}")
driver_name = drv_prefix.get(product_type, "") + version
out_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', '..', os_prefix, driver_name)
os.makedirs(out_dir, 0o755, exist_ok=True)
out_filename = os.path.join(out_dir, patch_name)
with open(out_filename, 'wb') as out:
out.write(patch_content)
def main(): def main():
args = parse_args() args = parse_args()
if args.direct: if args.direct:
combinations = zip(args.installer_file, args.search, args.replacement, combinations = zip(args.installer_file, args.search, args.replacement,
args.target, args.target_name, args.patch_name) args.target, args.target_name, args.patch_name)
else: else:
base_params = zip(args.search, args.replacement, args.target, args.target_name, args.patch_name) base_params = zip(args.search, args.replacement, args.target, args.target_name, args.patch_name)
combinations = ((l,) + r for l, r in itertools.product(args.installer_file, base_params)) combinations = ((l,) + r for l, r in itertools.product(args.installer_file, base_params))
for params in combinations:
patch_flow(*params, direct=args.direct, stdout=args.stdout) with tempfile.TemporaryDirectory() as tempdir:
print(f"Using tempdir `{tempdir}`")
for params in combinations:
patch_flow(*params, tempdir=tempdir, direct=args.direct, stdout=args.stdout)
if __name__ == '__main__': if __name__ == '__main__':
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment