diff --git a/.github/workflows/README.md b/.github/workflows/README.md new file mode 100644 index 0000000..58892a1 --- /dev/null +++ b/.github/workflows/README.md @@ -0,0 +1,15 @@ +# 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 diff --git a/.github/workflows/gen_win_patches.yml b/.github/workflows/gen_win_patches.yml new file mode 100644 index 0000000..ffb7d4c --- /dev/null +++ b/.github/workflows/gen_win_patches.yml @@ -0,0 +1,104 @@ +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 }} diff --git a/win/tools/autopatch/.gitignore b/win/tools/autopatch/.gitignore index df082ef..e00c98c 100644 --- a/win/tools/autopatch/.gitignore +++ b/win/tools/autopatch/.gitignore @@ -111,5 +111,4 @@ venv.bak/ # dont commit 7zip files 7z.dll 7z.exe 7z.* -temp/* !.gitempty \ No newline at end of file diff --git a/win/tools/autopatch/autopatch.py b/win/tools/autopatch/autopatch.py index 81c3209..b616004 100755 --- a/win/tools/autopatch/autopatch.py +++ b/win/tools/autopatch/autopatch.py @@ -1,16 +1,15 @@ #!/usr/bin/env python3 import argparse -import sys +import functools +import itertools +import os.path import subprocess +import sys import tempfile -import os.path -from binascii import unhexlify -import xml.etree.ElementTree as ET -import itertools -import functools import urllib.request - +import xml.etree.ElementTree as ET +from binascii import unhexlify CRLF = b"\x0d\x0a" HEADER_FORMAT = b">%s" @@ -19,7 +18,6 @@ def parse_args(): - parser = argparse.ArgumentParser( description="Generates .1337 patch for Nvidia drivers for Windows", formatter_class=argparse.ArgumentDefaultsHelpFormatter) @@ -44,7 +42,7 @@ def parse_args(): "nvencodeapi.dll", ], help="name(s) of installed target file. Used for patch " - "header") + "header") parser.add_argument("-P", "--patch-name", nargs="+", default=[ @@ -72,7 +70,7 @@ def parse_args(): parser.add_argument("-D", "--direct", action="store_true", help="supply patched library directly instead of " - "installer file") + "installer file") args = parser.parse_args() return args @@ -84,9 +82,11 @@ class ExtractException(Exception): class PatternNotFoundException(Exception): pass + class MultipleOccurencesException(Exception): pass + class UnknownPlatformException(Exception): pass @@ -145,26 +145,26 @@ def make_patch(archive, *, arch_tgt, search, replacement, + tmpdir, sevenzip="7z", direct=False): if direct: with open(archive, 'rb') as fo: f = fo.read() else: - with tempfile.TemporaryDirectory() as tmpdir: - with ExtractedTarget(archive, - tmpdir, - arch_tgt, - sevenzip=sevenzip) as tgt: - if tgt.endswith(".dll"): - with open(tgt, 'rb') as fo: - f = fo.read() - else: - f = expand(tgt, sevenzip=sevenzip) + with ExtractedTarget(archive, + tmpdir, + arch_tgt, + sevenzip=sevenzip) as tgt: + if tgt.endswith(".dll"): + with open(tgt, 'rb') as fo: + f = fo.read() + else: + f = expand(tgt, sevenzip=sevenzip) offset = f.find(search) if offset == -1: 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!") del f print("Pattern found @ %016X" % (offset,), file=sys.stderr) @@ -181,7 +181,7 @@ def identify_driver(archive, *, sevenzip="7z"): manifest = extract_single_file(archive, "setup.cfg", sevenzip=sevenzip) root = ET.fromstring(manifest) version = root.attrib['version'] - product_type = root.find('./properties/string[@name="ProductType"]')\ + product_type = root.find('./properties/string[@name="ProductType"]') \ .attrib['value'] return version, product_type @@ -192,92 +192,109 @@ def format_patch(diff, filename): res += LINE_FORMAT % (offset + OFFSET_ADJUSTMENT, left, right) return res + 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) replacement = unhexlify(replacement) - assert len(search) == len(replacement), "len() of search and replacement"\ - " is not equal" - - # 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 - if not installer_file.startswith("http"): #installer_file is a version, parse to url - filename = installer_file+"-desktop-win10-win11-64bit-international-dch-whql.exe" - installer_file = "https://international.download.nvidia.com/Windows/"+installer_file+"/"+filename - else: # installer_file is an url - filename = os.path.basename(installer_file) - # download installer and save in .temp - - if not os.path.isfile(os.path.join('temp', filename)): # check if file already downloaded - print(f"Downloading... ( {installer_file} TO {os.path.join('temp', 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) + assert len(search) == len(replacement), "len() of search and replacement is not equal" + + # Check if installer file exists or try to download + + print(f"Search for installer file `{installer_file}`...") + if not os.path.isfile(installer_file): + print("Installer file is not a file...") + if not installer_file.startswith("http"): + print("Installer file is not a URL...") + + # Construct URL from version + print("Installer file is a version!") + filename = installer_file + "-desktop-win10-win11-64bit-international-dch-whql.exe" + installer_url = f"https://international.download.nvidia.com/Windows/{installer_file}/{filename}" else: - installer_file = os.path.join('temp', filename) - print(f"Use downloaded file in `{installer_file}`") - - - - patch = make_patch(installer_file, - arch_tgt=target, - search=search, - replacement=replacement, - sevenzip=sevenzip, - direct=direct) - patch_content = format_patch(patch, target_name) - if stdout: - with open(sys.stdout.fileno(), mode='wb', closefd=False) as out: - out.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: - 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' + print("Installer file is a URL!") + installer_url = installer_file + + if installer_url: + try: + file_path = os.path.join(tempdir, os.path.basename(installer_url)) + if not os.path.isfile(file_path): + with urllib.request.urlopen(installer_url) as response, open(file_path, 'wb') as out_file: + print(f"Downloading... ({installer_url} TO {file_path})") + print("This may take a while (~800MB)") + out_file.write(response.read()) + print("Download completed successfully!") + installer_file = file_path + else: + print(f"Using downloaded file in '{file_path}'") + installer_file = file_path + except (urllib.error.URLError, Exception) as e: + print(f"Failed to download the file: {e}") + return + except Exception as e: + print(f"An error occurred during download: {str(e)}") + return else: - raise UnknownPlatformException("Can't infer platform from filename %s" - % (repr(installer_name),)) - driver_name = drv_prefix[product_type] + version - out_dir = os.path.join( - os.path.dirname( - os.path.abspath(__file__)), '..', '..', os_prefix, driver_name) - os.makedirs(out_dir, 0o755, True) - out_filename = os.path.join(out_dir, - patch_name) - with open(out_filename, 'xb') as out: - out.write(patch_content) + print(f"Invalid installer file or version: {installer_file}") + return + + # Rest of the code remains the same... + patch = make_patch(installer_file, + arch_tgt=target, + search=search, + replacement=replacement, + tmpdir=tempdir, + sevenzip=sevenzip, + 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(): args = parse_args() + if args.direct: combinations = zip(args.installer_file, args.search, args.replacement, args.target, args.target_name, args.patch_name) else: 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)) - 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__': diff --git a/win/tools/autopatch/temp/.gitempty b/win/tools/autopatch/temp/.gitempty deleted file mode 100644 index e69de29..0000000