Downloads:
1,010
Downloads of v 2.7.1:
57
Last Update:
08 Apr 2025
Package Maintainer(s):
Software Author(s):
- Deco
Tags:
dlss nvidia gaming update-tool admin- Software Specific:
- Software Site
- Software Source
- Software License
- Software Docs
- Software Issues
- Package Specific:
- Package Source
- Package outdated?
- Package broken?
- Contact Maintainers
- Contact Site Admins
- Software Vendor?
- Report Abuse
- Download
DLSS Updater
This is not the latest version of DLSS Updater available.
- 1
- 2
- 3
2.7.1 | Updated: 08 Apr 2025
- Software Specific:
- Software Site
- Software Source
- Software License
- Software Docs
- Software Issues
- Package Specific:
- Package Source
- Package outdated?
- Package broken?
- Contact Maintainers
- Contact Site Admins
- Software Vendor?
- Report Abuse
- Download
Downloads:
1,010
Downloads of v 2.7.1:
57
Maintainer(s):
Software Author(s):
- Deco
DLSS Updater 2.7.1
This is not the latest version of DLSS Updater available.
Legal Disclaimer: Neither this package nor Chocolatey Software, Inc. are affiliated with or endorsed by Deco. The inclusion of Deco trademark(s), if any, upon this webpage is solely to identify Deco goods or services and not for commercial purposes.
- 1
- 2
- 3
All Checks are Passing
3 Passing Tests
Deployment Method: Individual Install, Upgrade, & Uninstall
To install DLSS Updater, run the following command from the command line or from PowerShell:
To upgrade DLSS Updater, run the following command from the command line or from PowerShell:
To uninstall DLSS Updater, run the following command from the command line or from PowerShell:
Deployment Method:
This applies to both open source and commercial editions of Chocolatey.
1. Enter Your Internal Repository Url
(this should look similar to https://community.chocolatey.org/api/v2/)
2. Setup Your Environment
1. Ensure you are set for organizational deployment
Please see the organizational deployment guide
2. Get the package into your environment
Option 1: Cached Package (Unreliable, Requires Internet - Same As Community)-
Open Source or Commercial:
- Proxy Repository - Create a proxy nuget repository on Nexus, Artifactory Pro, or a proxy Chocolatey repository on ProGet. Point your upstream to https://community.chocolatey.org/api/v2/. Packages cache on first access automatically. Make sure your choco clients are using your proxy repository as a source and NOT the default community repository. See source command for more information.
- You can also just download the package and push it to a repository Download
-
Open Source
-
Download the package:
Download - Follow manual internalization instructions
-
-
Package Internalizer (C4B)
-
Run: (additional options)
choco download dlss-updater --internalize --version=2.7.1 --source=https://community.chocolatey.org/api/v2/
-
For package and dependencies run:
choco push --source="'INTERNAL REPO URL'"
- Automate package internalization
-
Run: (additional options)
3. Copy Your Script
choco upgrade dlss-updater -y --source="'INTERNAL REPO URL'" --version="'2.7.1'" [other options]
See options you can pass to upgrade.
See best practices for scripting.
Add this to a PowerShell script or use a Batch script with tools and in places where you are calling directly to Chocolatey. If you are integrating, keep in mind enhanced exit codes.
If you do use a PowerShell script, use the following to ensure bad exit codes are shown as failures:
choco upgrade dlss-updater -y --source="'INTERNAL REPO URL'" --version="'2.7.1'"
$exitCode = $LASTEXITCODE
Write-Verbose "Exit code was $exitCode"
$validExitCodes = @(0, 1605, 1614, 1641, 3010)
if ($validExitCodes -contains $exitCode) {
Exit 0
}
Exit $exitCode
- name: Install dlss-updater
win_chocolatey:
name: dlss-updater
version: '2.7.1'
source: INTERNAL REPO URL
state: present
See docs at https://docs.ansible.com/ansible/latest/modules/win_chocolatey_module.html.
chocolatey_package 'dlss-updater' do
action :install
source 'INTERNAL REPO URL'
version '2.7.1'
end
See docs at https://docs.chef.io/resource_chocolatey_package.html.
cChocoPackageInstaller dlss-updater
{
Name = "dlss-updater"
Version = "2.7.1"
Source = "INTERNAL REPO URL"
}
Requires cChoco DSC Resource. See docs at https://github.com/chocolatey/cChoco.
package { 'dlss-updater':
ensure => '2.7.1',
provider => 'chocolatey',
source => 'INTERNAL REPO URL',
}
Requires Puppet Chocolatey Provider module. See docs at https://forge.puppet.com/puppetlabs/chocolatey.
4. If applicable - Chocolatey configuration/installation
See infrastructure management matrix for Chocolatey configuration elements and examples.
This package was approved as a trusted package on 09 Apr 2025.
DLSS Updater is a utility that automatically updates DLSS (Deep Learning Super Sampling) DLLs for games across multiple platforms including Steam, EA Play, Ubisoft, Epic Games, GOG, and Battle.net. It simplifies the process of keeping your games up-to-date with the latest DLSS improvements.
Note: This application requires administrative privileges to run.
This package does not contain malware, it is a limitation of Pyinstaller with the binary not being signed, please see https://github.com/pyinstaller/pyinstaller/issues/6754#issuecomment-1100821249 for more details.
md5: B4BA4A8C3B4FA60F89E4425E506403A2 | sha1: BC6C0033C8E85A453C6E1D51192183F0752689CC | sha256: 82667A0FD705FD39337417EEC6DBD5BE53E76F3D5E008084B6422741F1B9E3A5 | sha512: DC56EFA31FC8EA494840ED2B20DCE1274A19C3ED05E827C17F141D8D6CEB2507C630625DDF921FCCBD6465257CC8E9288A3082F324604E3B77914ADA88FDF9FF
VERIFICATION
Verification is intended to assist the Chocolatey moderators and community
in verifying that this package's contents are trustworthy.
This package is published by the DLSS Updater Project itself. The binaries are
identical to those published in the GitHub releases section.
Project URL: https://github.com/Recol/DLSS-Updater
Releases: https://github.com/Recol/DLSS-Updater/releases
To verify the binaries:
1. Download the official release from the GitHub releases page
2. Generate a checksum using Get-FileHash in PowerShell
Get-FileHash DLSS_Updater.exe -Algorithm SHA256
3. Compare the checksum with the one provided in the release notes or in this file
Alternatively, you can use the verification methods provided by GitHub to ensure
the release you're downloading matches the source code at the tagged commit.
Note: This file will be updated with each release to include the specific
checksum for the current version. Always refer to the VERIFICATION.txt
included in the package for the most up-to-date verification information.
Checksum (SHA256) for DLSS_Updater.exe: 82667A0FD705FD39337417EEC6DBD5BE53E76F3D5E008084B6422741F1B9E3A5
md5: 9F746F4F7D845F063FEA3C37DCEBC27C | sha1: 24D00523770127A5705FCC2A165731723DF36312 | sha256: 88ACE577A9C51061CB7D1A36BABBBEFA48212FADC838FFDE98FDFFF60DE18386 | sha512: 306952418B095E5CF139372A7E684062D05B2209E41D74798A20D7819EFEB41D9A53DC864CB62CC927A98DF45F7365F32B72EC9B17BA1AEE63E2BF4E1D61A6E4
md5: 8F8EB9CB9E78E3A611BC8ACAEC4399CB | sha1: 237EEE6E6E0705C4BE7B0EF716B6A4136BF4E8A8 | sha256: 1BD81DFD19204B44662510D9054852FB77C9F25C1088D647881C9B976CC16818 | sha512: 5B10404CDC29E9FC612A0111B0B22F41D78E9A694631F48F186BDDE940C477C88F202377E887B05D914108B9BE531E6790F8F56E6F03273AB964209D83A60596
md5: 226A5983AE2CBBF0C1BDA85D65948ABC | sha1: D0F131DCBA0F0717C5DEA4A9CA7F2E2ECF0AD1C3 | sha256: 591358EB4D1531E9563EE0813E4301C552CE364C912CE684D16576EABF195DC3 | sha512: A1E6671091BD5B2F83BFAA8FCF47093026E354563F84559BD2B57D6E9FA1671EEA27B4ED8493E9FDF4BDE814074DC669DE047B4272B2D14B4F928D25C4BE819D
md5: C2F8C03ECCE9941492BFBE4B82F7D2D5 | sha1: 909C66C6DFEA5E0C74D3892D980918251BB08632 | sha256: D56CE7B1CD76108AD6C137326EC694A14C99D48C3D7B0ACE8C3FF4D9BCEE3CE8 | sha512: 7C6C85E390BBE903265574E0E7A074DA2CE30D9376D7A91A121A3E0B1A8B0FFFD5579F404D91836525D4400D2760CB74C9CB448F8C5AE9713385329612B074CF
md5: B5E2760C5A46DBEB8AE18C75F335707E | sha1: E71DB44FC0E0C125DE90A9A87CCB1461E72A9030 | sha256: 91D249D7BC0E38EF6BCB17158B1FDC6DD8888DC086615C9B8B750B87E52A5FB3 | sha512: C3400772D501C5356F873D96B95DC33428A34B6FCAAD83234B6782B5F4BF087121E4FD84885B1ABAB202066DA98EB424F93DD2EED19A0E2A9F6FF4A5CFD1E4F3
md5: 050A30A687E7A2FA6F086A0DB89AA131 | sha1: 1484322CAAF0D71CBB873A2B87BDD8D456DA1A3B | sha256: FC9D86CEC621383EAB636EBC87DDD3F5C19A3CB2A33D97BE112C051D0B275429 | sha512: 07A15AA3B0830F857B9B9FFEB57B6593AE40847A146C5041D38BE9CE3410F58CAA091A7D5671CC1BC7285B51D4547E3004CF0E634AE51FE3DA0051E54D8759E1
md5: 9F45A47EBFD9D0629F4935764243DD5A | sha1: 86A4A0EA205E31FB73F3BFCCE24945BD6BEA06C7 | sha256: 1CA895ABA4E7435563A6B43E85EBA67A0F8C74AA6A6A94D0FC48FA35535E2585 | sha512: 8C1CDCAD557BFF1685A633D181FCF14EC512D322CAEAEB9C937DA8794C74694FE93528FC9578CB75098F50A2489ED4A5DEDF8C8C2AC93EEB9C8F50E3DD690D5F
md5: CC228FF8D86B608E73026B1E9960B2F8 | sha1: CEF0705AEE1E8702589524879A49E859505D6FE0 | sha256: 4CADBC0C39DA7C6722206FDCEBD670ABE5B8D261E7B041DD94F9397A89D1990D | sha512: 17ABD9E0EC20B7EB686E3C0F41B043D0742AB7F9501A423B2D2922D44AF660379792D1CC6221EFFBD7E856575D5BABF72657AE9127C87CC5CF678BD2CEB1228F
md5: E368A236F5676A3DA44E76870CD691C9 | sha1: E4F1D2C6F714A47F0DC29021855C632EF98B0A74 | sha256: 93C624B366BA16C643FC8933070A26F03B073AD0CF7F80173266D67536C61989 | sha512: F5126498A8B65AB20AFAAF6B0F179AB5286810384D44638C35F3779F37E288A51C28BED3C3F8125D51FEB2A0909329F3B21273CB33B3C30728B87318480A9EF8
md5: 416AA8314222DB6CBB3760856BE13D46 | sha1: 5F28FE2D565378C033EF8EEA874BC38F4B205327 | sha256: 39095F59C41D76EC81BB2723D646FDE4C148E7CC3402F4980D2ADE95CB9C84F9 | sha512: B16ED31DC3343CAEA47C771326810C040A082E0AB65D9AE69946498CEB6AE0DEE0A570DBCD88090668A100B952C1FF88BADE148811B913C90931AA0E657CD808
md5: 344A09B4BE069F86356A89482C156647 | sha1: 2506FFEB157CB531195DD04D11D07C16E4429530 | sha256: 8F105771B236DBCB859DE271F0A6822CE1CB79C36988DD42C9E3F6F55C5F7EB9 | sha512: 4C1E616443576DC83200A4F98D122065926F23212B6647B601470806151FF15EA44996364674821AFEC492B29BA868F188A9D6119B1E1D378A268F1584CA5B29
md5: 86023497FA48CA2C7705D3F90B76EBC5 | sha1: 835215D7954E57D33D9B34D8850E8DC82F6D09E8 | sha256: 53B25E753CA785BF8B695D89DDE5818A318890211DC992A89146F16658F0B606 | sha512: 8F8370F4C0B27779D18529164FA40CBFDDAFA81A4300D9273713B13428D0367D50583271EA388D43C1A96FED5893448CD14711D5312DA9DFA09B9893DF333186
md5: 0C1CC0A54D4B38885E1B250B40A34A84 | sha1: 24400F712BBE1DD260ED407D1EB24C35DCB2ECAC | sha256: A9B13A1CD1B8C19B0C6B4AFCD5BB0DD29C0E2288231AC9E6DB8510094CE68BA6 | sha512: 71674E7ED8650CAC26B6F11A05BFC12BD7332588D21CF81D827C1D22DF5730A13C1E6B3BA797573BB05B3138F8D46091402E63C059650C7E33208D50973DDE39
md5: 5FBCB20D99E463259B4F15429010B9CD | sha1: B16770F8BB53DC2BAFCB309824D6FA7B57044D8A | sha256: 7F39BA298B41E4963047341288CAB36B6A241835EE11BA4AD70F44DACD40906C | sha512: 7BA1AC34B3ECFBFB8252F5875BE381D8EF823B50DFE0E070222175EE51191F5EE6D541EEEDD1445ED603A23D200CE9CE15914C8ED3FAFE7E7F3591F51F896C58
md5: 5241DF2E95E31E73CCFD6357AD309DF0 | sha1: 2644CC5E86DFAD1AD2140181AB2CA79725F95411 | sha256: 6EE44DD0D8510DC024C9F7C79B1B9FA88C987B26B6BEB6653DDD11751C34E5DC | sha512: 52CCCD1DD237E764E34996C0C5F7A759A7F0EFF29B61BEFEAF96A16D80DF2BA9EE2C3615F875153198A145D68F275AEA6D02187E6EEE5A129E3E2AB81AACEB16
md5: 8D285430E8BDA6D5C9B683579ADCB180 | sha1: 619DBBCFF06C659E3FC48F03917A4DADBFC1C275 | sha256: 0512A35316EC9180437F86696A84C5C06A7E4E82E050055A656E5BF9FCA206F9 | sha512: 38405DD85DD62F843ABB55ACEA1B64D7D63BB601445BF1B32078CDE5BBEF4861DD99F26659281FE2AEA86F58CFB1725D8C63D91FB539DCBF5D98CDBE783337FC
md5: 4A28CA64F44B91F43945EE3971E0996A | sha1: 45B3D8584C58E8D6AE507FDBD772FEEB1886C8B0 | sha256: C05F1FFFE3B5A2738EA54CE9485CCA026FB9635F982626FBA1E1DCC531897273 | sha512: 862A0428F08D447CD1EE0431969E0FBCB182F4C46418C26D26FA33E586E686D9C093C1CA5781F544CE9276195CE973850719636E39E465F059607F455ECFDD93
md5: 7FD4A71085783CCFE9C289C07BCF9B04 | sha1: BB6FFDB5C069DBBA06998DC877D24F72DAD6298D | sha256: C4ECA98C3C67B6395D5B005B00AC1EB0318B86B23AA71035A44C2B1602BEFBA9 | sha512: A96C5B90B8384B239BE111D90CAA3B947651AD73382AB9E5DBE4A4B6AD30921876545331D37C8D5A8F669E39D71BF60983C4BA39C479E23015C2F7579C5E55CD
md5: C123F2C161884FBFF4F00EF1E1391266 | sha1: 7DB3055DA53916BEA2B85B159491A0772FB620CE | sha256: 5CCB89E93D67BC3288D4E84649C5346E66E15E3D7CD65D989DAF3F4CB584BE9A | sha512: DAC5616320B9052254B5687959E67126C4A938E79173D8245675A9651674384C36CC856F996EF88AE621EC67AFC6616626657585D92BB5D14602A7CC9FC0F669
md5: 385F562BDC391CCD4F81ACA3719F3236 | sha1: F6633E1DAC227BA3CD14D004748EF0C1C4135E67 | sha256: 4AD565A8BA3EF0EA8AB87221AD11F83EE0BC844CE236607958406663B407333E | sha512: B72ED1A02D4A02791CA5490B35F7E2CB6CB988E4899EDA78134A34FB28964EA573D3289B69D5DB1AAC2289D1F24FD0A432B8187F7AE8147656D38691AE923F27
md5: 7A629293EEB0BCA5F9BDEE8ADE477C54 | sha1: A25BF8BAC4FBFD9216EA827E71344BA07B1D463B | sha256: 7809160932F44E59B021699F5BC68799EB7293EE1FA926D6FCCA3C3445302E61 | sha512: 1C58C547D1FE9B54DDF07E5407EDAF3375C6425CA357AA81D09C76A001376C43487476A6F18C891065AB99680501B0F43A16A10ED8E0D5E87B9A9542098F45FE
md5: 3C5C7A3130B075B2DEF5C413C127173F | sha1: F3D2B8AD93F3DC99C8410D34C871AEC56C52E317 | sha256: 9DC1E91E71C7C054854BD1487CB4E6946D82C9F463430F1C4E8D1471005172B1 | sha512: 46A52631E3DD49B0AE10AFBDF50A08D6D6575F3093B3921B2FA744704E2D317F8B10A6D48AD7F922A7843731782521773032A6CC04833B00BD85E404C168FFE4
md5: 28005B20FBEF6E1DB10912D0FDD6471C | sha1: 47B83697677E08E4EBCFF6FC41ECA7ECE120CC17 | sha256: 60FC31D2A0C634412F529DBA76AF3B9BF991352877C6DAE528186D3935704CFD | sha512: 45D6F860D7F7AEFAA7A0A3B4B21B5C3234F442E39D6259E0A9E2083890533C275F07DDDA93FDDC7445928A55475B83C63253D3B08E41E5576F9029B205DFB36A
md5: 436EA0237ED040513EC887046418FAAA | sha1: 44BAFBBDB1B97D86505E16B8A5FCB42B2B771F91 | sha256: 3A72B4F29F39A265D32AD12F0CE15DBF60129C840E10D84D427829EDE45E78AD | sha512: 9F0DBFB538C05383AE9ABFE95E55740530ECC12C1890D8862DEACBC84212BE0740D82AFC9E81D529125221E00B2286CAE0D4B3CA8DD3A6C57774D59F37933692
md5: 8F107A7BC018227B181A0E7E76E9CA39 | sha1: EF57E24F29D2B1DEEACEFD82171873B971A3F606 | sha256: EFC1E4460984A73CF47A3DEF033AF1C8F3B1DBC1A56CD27781D3AACF3E3330CB | sha512: D8D8250AAF93FA99E9D1E4286B32579DE0029C83867A787C0A765505A0F8CBD2DD076BB324509D5C4867423BC7DC8F00C8B8458E08E8CBFA8DD731D03DD1AE3F
md5: B65BF5EF316880FD8D21E1B34EB5C8A9 | sha1: 3AB4674CB5C76E261FE042D6D0DA8A20BFCBCBAE | sha256: B203D862DDEF1DD62BF623FC866C7F7A9C317C1C2AE30D1F52CB41F955B5698E | sha512: 4AF3B0EF9A813CE1A93A35DD6869817910AE4B628F374477F60EA1831D2CC1AAE7908262672E11954A4953BDFF22BCC5FE23B4A736788E8E5EF4F8AC30EB24F8
md5: FC9FC5F308FFC2D2D71814DF8E2AE107 | sha1: 24D7477F2A7DC2610EB701ED683108CD57ECA966 | sha256: 2703635D835396AFD0F138D7C73751AFE7E33A24F4225D08C1690B0A371932C0 | sha512: 490FA6DC846E11C94CFE2F80A781C1BD1943CDDD861D8907DE8F05D9DC7A6364A777C6988C58059E435AC7E5D523218A597B2E9C69C9C34C50D82CAC4400FE01
md5: 43D8D2FB8801C5BD90D9482DDF3EA356 | sha1: D582B55CD58531E726141C63BA9910FF185D72E0 | sha256: 33F4FDDC181066FCE06B2227BDED813F95E94ED1F3D785E982C6B6B56C510C57 | sha512: 0E073381A340DB3F95165DBCCEB8DFBF1ED1B4343E860446032400A7B321B7922C42EE5D9A881E28E69A3F55D56D63663ADB9BB5ABB69C5306EFBF116CC5E456
md5: 3C58A804B90A0782E80BBBF6C6B6F167 | sha1: B333143E0F6E508B51D27ADF7872B586FA54C794 | sha256: 6EDA016742A6171205A387A14B3C0B331841567740376F56768F8C151724207D | sha512: 773F8DEDED48B34BABE24D955A501F4F357C20125AFFB6EADE36CE6A7ACD380906713C366318F79D627747E636D156875C216FFFAC26DBA25373BBC1C820DA76
md5: 5794B8E183EB547AADD5FAF30A8C4DD2 | sha1: 5B1ED8A9DA14D8ECC4209662809727931AA49307 | sha256: B762061B688AAE679AFE788904D2C9970F74A7DAC98F3B42463D08F25E483D3F | sha512: 3E896854E5DD957AB2B88C82FBAF2EAA03729BAB30FD8518BD999081F4DA9000D9B22894B324E5930DF161C7ADAEC3FC87FD00DE60DCDA34876007AEA4A2FD31
md5: 3560176D0CDBE2F5D33F543348E0A027 | sha1: 1E35A1F7793FC3899927835491F28FE5B903EDCD | sha256: EBB2AE5535A64F65DAEAB8235585114FC9DD2CF1A49F5852D446250B998B6AE4 | sha512: 8AB24C8C9FE8331F21BE96818C5FA69AE5578EB742C4504596310BB0DB7C4C087D350FA47A13ED9FF2E051BB62AC5581DE082D0177923D24FEE6B140AFECF50B
md5: E93C7F013493B12AD40229B19DB02CE6 | sha1: EF878BFBFD2F8328BBB8CFF1AA29A39E624A8503 | sha256: 17D63275D00BDD8670422B95BD264C532998E0A1B041079E54FCE4B6B7A55819 | sha512: 2F4A25EA4062840BEA10442CAD665A72ABBCE747307AD9CE7B3BB89EAF7DCC28F1E9396749576BE304FD793690DDC445653613440442695E72B761EACACB6020
md5: 47555752931CECF90E796499B62EC729 | sha1: 217B171764FBA5E91190D1F8A36FECCB3F6D4585 | sha256: 9A9E2A65A281644E368D0F272B95BA5F6B445D1C35910D06056C5EBEB77402DB | sha512: A68009F0306D4D8E70951978D2C184EB80FBEC98C6DB0997BD7B0B503DD63019363CFEF68A9ADBFB568C0A552B774FBDBEB1BCF45F211A6A3224B49E85A5619C
md5: 527BBBFDED529EA77EE798D94CE0F243 | sha1: 647F8C89EB4DB3CF3656292B3DE984B32C6E02A5 | sha256: BAB9AC3EC83E380AE51E4295EF3BF2C738627812D3A49D1E713661ABBC8DC57A | sha512: C1ED69E15AB19084390CF9D1CEAB791758AC4DDD688169F3B814B0E4CF1FC3B6BA17651E35B25DCDC601A8A64821D58933D52A5E939942FA134DFD04FCA04C8B
md5: 09796DAB12CBBD920F632AEB89820193 | sha1: 7D81C0E5537B6D8B79AF0C28CD102E064027C78D | sha256: BD14C67EA28E21D6257AD780A37122C9B5773F69E693F5DB6BFFAEE4D839526E | sha512: 09A6175DCCBBD18A62209E156089F1167DFB8040C97C8C2C14724CE2A8FBE6CE039D7FE04FB8BD60092427BEB7FDD8E7127D611F006FFF1CF2A1AD75E9E5EF3A
md5: AA9624CB27CC50A3FBBD3B223A617B1C | sha1: 797AEA1C5CEDD1125276BFC5DCD7A3FB8C6355AA | sha256: 606D66D82DB562EA7979179D06486A0F94D079941D26B80A1E2C49D29959DF6F | sha512: 024975E6787F7A6B0AB6E4B02AD33901F8473B97DC73D4F03B7A116B24AC74150C0C48990EA7A4FB750F9FE728DAFED172796743F802E70F2150EEFCF70FE96A
md5: 9D6925407136753E8EB8234D59FA3F1F | sha1: 62631B7007D394FB4D406EA686B291FFF9E486CD | sha256: F6156B1020380EC4F0E48577EBEDAAEF5FB1AB1F337D8B4E72E6A33A7567A9CC | sha512: AB04DE62524E465810CD0EE81E85018863E276D49861E67A920667AF802E94869B816B47A6E3C4738179A7A7D726D44BBBA6E47D9097363A63EAFF51CD56DE8A
md5: BBAA58E9E1ABDF7D8C4C69652D29D789 | sha1: 38AEF13ABC14502354E8C5C3C37B97A8E2E5FDCF | sha256: C5902934D026D7E15FBE9917D474F3322846A41A25E66F4B2B1F758801879F4B | sha512: 7882A8E1E1EA7E217F70FF9DF27D36709B4BE23588909EF002F3EB1B9A7D3EEA2591A8524AF2C83448DDFFF0911658517C6989683245C54678583F359A78B0AD
md5: EF37235FC43157A4C93241D5E49E304B | sha1: D4DE26B36812C2DDCCD1618B4D7AC02AD1B42273 | sha256: A9C5A153D8C0286F9B41A2B1C65854AD9E6471B8755B7DE87BAE4470E60BCAB6 | sha512: C0857760D5D069BEEB1EB1737F4160530910331BF6047022836CF58137BD28C2A966A8760A681859F57EBD810FD424CE231402EDDDE1316EAEF7B6F9F773AFBB
md5: 639B1FB35CB61BA633EB1791B750631F | sha1: 392A6925009F5FB02A4C122C9CE31D82B9059628 | sha256: 25B8F83A7767211B11132775A0E27A45AA4EC8AB4E6572599F9C172AE3606B40 | sha512: DEF547EF66673862CEA9BB13C433EDCE24A3075C328D9B3B9452F2F01F2F4243DAAB38C0F8571C52D601BC4AECAAA0682DBEBF6BE41CAE345787A719063EBF58
md5: FCCCE207A34C947F01D3F23A7DD09569 | sha1: 75F722801C77285DB98A08AF763252A0255E99E2 | sha256: 7C7F6393F06DE11750ADB09CC5698AE55CD9FB27B2E51E207286FEB1B5B2B156 | sha512: D3D923F133594EB4325F4A6E5ED46FCC348A7C0F310F14EAA38C6FAD070BA637BDB4A77200FEB231114E111D07A86595A6130291028CDE3A284D9F847EC38AD4
md5: 708A5BC205384633A7B6674EECC7F0F0 | sha1: 01603A7826029293236C67FCE02ACE8D392A0514 | sha256: D8BA5F17B9FFCBF3AEAF3FA1DA226832D2FA90F81ACCE0CD669464E76CE434AC | sha512: 8638845326AB6543338BAA7A644AF8BE33A123E1FC9DA2037158BE7C8D165691CCD06CB3FF73696A30B8801EAB030E81F93DB81216BB3B7E83A320A0DF5AF270
md5: C016F23C42A37A6330AE2D1AD7F44C78 | sha1: 91C19EF9C7F6C35DE7C17E7BF83E615752EDDB61 | sha256: F028D8098B6D7C9BA3A8316F97B2242DCB948C7381890F79880C06564A80D1A5 | sha512: 343B041F05CC61EC1888D524A4F42D5274013DC240B0E28472DE9D5E7DB1F33A3BDC59789AF5EEC46C3B546EAEE2D60F207BFDF91C048469BE2BB1922B00A900
import os
import sys
import json
import shutil
import zipfile
import subprocess
import time
from urllib import request
from urllib.error import URLError
from packaging import version
from dlss_updater.version import __version__
from dlss_updater.logger import setup_logger
logger = setup_logger()
GITHUB_API_URL = "https://api.github.com/repos/Recol/DLSS-Updater/releases/latest"
def check_for_updates():
"""
Check for available updates by comparing versions.
Returns (latest_version, download_url) tuple or (None, None) if no update available.
"""
try:
with request.urlopen(GITHUB_API_URL) as response:
latest_release = json.loads(response.read().decode())
latest_version = latest_release["tag_name"].lstrip("V")
if version.parse(latest_version) > version.parse(__version__):
return latest_version, latest_release["assets"][0]["browser_download_url"]
else:
return None, None
except URLError as e:
logger.error(f"Error checking for updates: {e}")
return None, None
def download_update(download_url):
"""
Download and extract the update package.
Returns path to new executable or None if download/extraction fails.
"""
try:
# Create a temporary update directory
base_dir = os.path.dirname(sys.executable)
update_dir = os.path.join(base_dir, "update")
# Remove old update directory if it exists
if os.path.exists(update_dir):
try:
shutil.rmtree(update_dir)
logger.info("Removed existing update directory")
except Exception as e:
logger.error(f"Failed to remove old update directory: {e}")
return None
# Create a fresh update directory
os.makedirs(update_dir, exist_ok=True)
update_zip = os.path.join(update_dir, "update.zip")
logger.info("Downloading update package...")
request.urlretrieve(download_url, update_zip)
logger.info("Extracting update package...")
with zipfile.ZipFile(update_zip, "r") as zip_ref:
zip_ref.extractall(update_dir)
os.remove(update_zip)
# Look for the new executable
new_exe = None
for root, dirs, files in os.walk(update_dir):
for file in files:
if file.lower() == "dlss_updater.exe":
new_exe = os.path.join(root, file)
break
if new_exe:
break
if new_exe:
logger.info(f"Found new executable: {new_exe}")
return new_exe
else:
logger.error("Error: New executable not found in the update package.")
return None
except Exception as e:
logger.error(f"Error downloading update: {e}")
if os.path.exists(update_dir):
shutil.rmtree(update_dir)
return None
def update_script(current_exe, new_exe):
"""
Perform the actual update by replacing the old executable with the new one.
"""
logger.info(f"Starting update process: from {current_exe} to {new_exe}")
# Wait for the original process to exit
time.sleep(2)
try:
# Get the command line arguments to pass to the new executable
args = sys.argv[1:] if len(sys.argv) > 1 else []
# Create a backup of the current executable path for cleanup
current_dir = os.path.dirname(current_exe)
backup_path = os.path.join(current_dir, "old_exe_backup.txt")
with open(backup_path, "w") as f:
f.write(current_exe)
# Replace the old executable with the new one
if os.path.exists(current_exe):
os.chmod(current_exe, 0o777) # Ensure we have write permission
os.remove(current_exe)
logger.info(f"Removed old executable: {current_exe}")
# Move the new executable to the location of the old one
shutil.move(new_exe, current_exe)
logger.info(f"Moved new executable to: {current_exe}")
# Wait briefly to ensure the file is fully written
time.sleep(1)
# Ensure the new executable has appropriate permissions
os.chmod(current_exe, 0o755)
# Clean up the update directory
update_dir = os.path.dirname(new_exe)
if os.path.exists(update_dir) and os.path.isdir(update_dir):
shutil.rmtree(update_dir)
logger.info(f"Cleaned up update directory: {update_dir}")
# Start the updated executable with original arguments
startup_args = [current_exe] + args
logger.info(f"Starting updated executable with args: {startup_args}")
subprocess.Popen(startup_args, creationflags=subprocess.CREATE_NEW_CONSOLE)
# Log the update completion
with open(
os.path.join(os.path.dirname(current_exe), "update_log.txt"), "w"
) as f:
f.write(
f"Update completed at {time.ctime()}. New executable started with args: {args}"
)
except Exception as e:
# Log the error
error_msg = f"Error during update process at {time.ctime()}: {str(e)}"
logger.error(error_msg)
with open(
os.path.join(os.path.dirname(current_exe), "update_error_log.txt"), "w"
) as f:
f.write(error_msg)
def perform_update(new_exe_path):
"""
Start the update process in a new process and exit the current one.
"""
current_exe = sys.executable
logger.info(f"Preparing to update from {current_exe} to {new_exe_path}")
# Get command line arguments to pass to the updater
args = sys.argv[1:] if len(sys.argv) > 1 else []
# Create the update command
update_cmd = [sys.executable, __file__, "update", current_exe, new_exe_path]
# Add a log for debugging
logger.info(f"Starting update process with command: {update_cmd}")
# Start the update process in a separate process
subprocess.Popen(
update_cmd,
creationflags=subprocess.CREATE_NO_WINDOW,
)
# Exit the current process
sys.exit(0)
def auto_update():
"""
Main update function that orchestrates the update process.
Returns True if an update was downloaded and ready to install,
False otherwise.
"""
logger.info("Checking for updates...")
latest_version, download_url = check_for_updates()
if latest_version:
logger.info(f"New version available: {latest_version}")
new_exe_path = download_update(download_url)
if new_exe_path:
logger.info("Update downloaded successfully.")
logger.info(
"The application will now close and update. It will restart automatically."
)
perform_update(new_exe_path)
return True
else:
logger.info("No updates available. You have the latest version.")
return False
def cleanup_old_update_files():
"""
Clean up any leftover files from previous updates.
Should be called at application startup.
"""
try:
base_dir = os.path.dirname(sys.executable)
# Check for old update directory
update_dir = os.path.join(base_dir, "update")
if os.path.exists(update_dir):
logger.info(f"Cleaning up old update directory: {update_dir}")
shutil.rmtree(update_dir)
# Check for backup file from previous update
backup_path = os.path.join(base_dir, "old_exe_backup.txt")
if os.path.exists(backup_path):
with open(backup_path, "r") as f:
old_exe = f.read().strip()
if os.path.exists(old_exe):
logger.info(f"Removing old executable from previous update: {old_exe}")
try:
os.remove(old_exe)
except:
logger.warning(f"Could not remove old executable: {old_exe}")
os.remove(backup_path)
return True
except Exception as e:
logger.error(f"Error cleaning up update files: {e}")
return False
if __name__ == "__main__":
if len(sys.argv) > 1 and sys.argv[1] == "update":
update_script(sys.argv[2], sys.argv[3])
import os
import sys
import configparser
from enum import StrEnum
import appdirs
from .logger import setup_logger
logger = setup_logger()
def get_config_path():
"""Get the path for storing configuration files"""
app_name = "DLSS-Updater"
app_author = "Recol"
config_dir = appdirs.user_config_dir(app_name, app_author)
os.makedirs(config_dir, exist_ok=True)
return os.path.join(config_dir, "config.ini")
def resource_path(relative_path):
"""Get absolute path to resource, works for dev and for PyInstaller"""
try:
base_path = sys._MEIPASS
except Exception:
base_path = os.path.abspath(".")
return os.path.join(base_path, relative_path)
LATEST_DLL_VERSIONS = {
"nvngx_dlss.dll": "310.2.1.0",
"nvngx_dlssg.dll": "310.2.1.0",
"nvngx_dlssd.dll": "310.2.1.0",
"libxess.dll": "2.0.1.41",
"libxess_dx11.dll": "2.0.1.41",
}
LATEST_DLL_PATHS = {
"nvngx_dlss.dll": resource_path(os.path.join("latest_dll", "nvngx_dlss.dll")),
"nvngx_dlssg.dll": resource_path(os.path.join("latest_dll", "nvngx_dlssg.dll")),
"nvngx_dlssd.dll": resource_path(os.path.join("latest_dll", "nvngx_dlssd.dll")),
"libxess.dll": resource_path(os.path.join("latest_dll", "libxess.dll")),
"libxess_dx11.dll": resource_path(os.path.join("latest_dll", "libxess_dx11.dll")),
}
class LauncherPathName(StrEnum):
STEAM = "SteamPath"
EA = "EAPath"
EPIC = "EpicPath"
GOG = "GOGPath"
UBISOFT = "UbisoftPath"
BATTLENET = "BattleDotNetPath"
XBOX = "XboxPath"
CUSTOM1 = "CustomPath1"
CUSTOM2 = "CustomPath2"
CUSTOM3 = "CustomPath3"
CUSTOM4 = "CustomPath4"
class ConfigManager(configparser.ConfigParser):
_instance = None
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super(ConfigManager, cls).__new__(cls)
return cls._instance
def __init__(self):
if not hasattr(self, "initialized"):
super().__init__()
self.logger = setup_logger()
self.config_path = get_config_path()
self.read(self.config_path)
# Initialize sections
sections = {
"LauncherPaths": {
LauncherPathName.STEAM: "",
LauncherPathName.EA: "",
LauncherPathName.EPIC: "",
LauncherPathName.GOG: "",
LauncherPathName.UBISOFT: "",
LauncherPathName.BATTLENET: "",
LauncherPathName.XBOX: "",
LauncherPathName.CUSTOM1: "",
LauncherPathName.CUSTOM2: "",
LauncherPathName.CUSTOM3: "",
LauncherPathName.CUSTOM4: "",
},
"Settings": {
"CheckForUpdatesOnStart": "True",
"AutoBackup": "True",
"MinimizeToTray": "False",
},
"Updates": {
"LastUpdateCheck": "",
"CurrentDLSSVersion": LATEST_DLL_VERSIONS["nvngx_dlss.dll"],
},
"BlacklistSkips": {}, # Empty dict to store games to skip in the blacklist
}
for section, values in sections.items():
if not self.has_section(section):
self.add_section(section)
for key, value in values.items():
if key not in self[section]:
self[section][key] = value
self.save()
self.initialized = True
def update_launcher_path(
self, path_to_update: LauncherPathName, new_launcher_path: str
):
"""Update launcher path in config"""
self.logger.debug(f"Attempting to update path for {path_to_update}.")
self["LauncherPaths"][path_to_update] = new_launcher_path
self.save()
self.logger.debug(f"Updated path for {path_to_update}.")
def check_path_value(self, path_to_check: LauncherPathName) -> str:
"""Get launcher path from config"""
return self["LauncherPaths"].get(path_to_check, "")
def reset_launcher_path(self, path_to_reset: LauncherPathName):
"""Reset launcher path to default empty value"""
self.logger.debug(f"Resetting path for {path_to_reset}")
self["LauncherPaths"][path_to_reset] = ""
self.save()
def get_setting(self, setting_name: str, default_value: str = "") -> str:
"""Get setting value with fallback"""
return self["Settings"].get(setting_name, default_value)
def update_setting(self, setting_name: str, value: str):
"""Update setting value"""
self["Settings"][setting_name] = value
self.save()
def update_last_check_time(self, timestamp: str):
"""Update last update check timestamp"""
self["Updates"]["LastUpdateCheck"] = timestamp
self.save()
def update_current_dlss_version(self, version: str):
"""Update current DLSS version"""
self["Updates"]["CurrentDLSSVersion"] = version
self.save()
def add_blacklist_skip(self, game_name: str):
"""Add a game name to skip in blacklist checks"""
if not self.has_section("BlacklistSkips"):
self.add_section("BlacklistSkips")
# Use a sanitized version of the game name as the key
safe_key = game_name.replace(" ", "_").replace(".", "_")
self["BlacklistSkips"][safe_key] = game_name
self.save()
def remove_blacklist_skip(self, game_name: str):
"""Remove a game name from blacklist skips"""
if self.has_section("BlacklistSkips"):
safe_key = game_name.replace(" ", "_").replace(".", "_")
if safe_key in self["BlacklistSkips"]:
self["BlacklistSkips"].pop(safe_key)
self.save()
def is_blacklist_skipped(self, game_name: str) -> bool:
"""Check if a game name should skip blacklist checks"""
if self.has_section("BlacklistSkips"):
safe_key = game_name.replace(" ", "_").replace(".", "_")
return safe_key in self["BlacklistSkips"]
return False
def get_all_blacklist_skips(self) -> list:
"""Get all game names to skip in blacklist checks"""
if self.has_section("BlacklistSkips"):
return list(self["BlacklistSkips"].values())
return []
def clear_all_blacklist_skips(self):
"""Clear all blacklist skips"""
if self.has_section("BlacklistSkips"):
self["BlacklistSkips"].clear()
self.save()
def save(self):
"""Save configuration to disk"""
with open(self.config_path, "w") as configfile:
self.write(configfile)
config_manager = ConfigManager()
DLL_TYPE_MAP = {
"nvngx_dlss.dll": "DLSS DLL",
"nvngx_dlssg.dll": "DLSS Frame Generation DLL",
"nvngx_dlssd.dll": "DLSS Ray Reconstruction DLL",
"libxess.dll": "XeSS DLL",
"libxess_dx11.dll": "XeSS DX11 DLL",
}
from PyQt6.QtCore import QThreadPool, QRunnable, pyqtSignal, QObject
class WorkerSignals(QObject):
"""Signals available from a running worker thread."""
finished = pyqtSignal()
result = pyqtSignal(object)
error = pyqtSignal(tuple)
progress = pyqtSignal(int)
class Worker(QRunnable):
"""
Worker thread for running background tasks.
Inherits from QRunnable for better thread pool management.
"""
def __init__(self, fn, *args, **kwargs):
super().__init__()
# Store constructor arguments (re-used for processing)
self.fn = fn
self.args = args
self.kwargs = kwargs
# Create signals for communication
self.signals = WorkerSignals()
# Add an option to stop the worker if needed
self.is_running = True
def run(self):
"""
Initialise the runner function with passed args, kwargs.
Automatic handling of different function signatures and return values.
"""
try:
# Retrieve args/kwargs here; and fire processing using them
result = self.fn(*self.args, **self.kwargs)
except Exception as e:
# If function raised an exception, capture it
import traceback
# Package the exception details
exctype = type(e)
value = str(e)
tb = traceback.format_exc()
# If still running, emit the error signal
if self.is_running:
self.signals.error.emit((exctype, value, tb))
else:
# If function successfully completed, emit result
if self.is_running:
if result is not None:
self.signals.result.emit(result)
finally:
# Always emit finished signal
if self.is_running:
self.signals.finished.emit()
def stop(self):
"""Set the running state to False to prevent further signal emissions."""
self.is_running = False
class ThreadManager:
"""
Manages thread pool and worker creation for background tasks.
Provides a simplified interface for running functions in a thread pool.
"""
def __init__(self, parent=None):
# Create a thread pool
self.thread_pool = QThreadPool()
# Set up maximum thread count (adjust as needed)
self.thread_pool.setMaxThreadCount(1)
# Store the current worker
self.current_worker = None
# Store the parent (optional)
self.parent = parent
# Signals to be connected from the current worker
self.signals = None
def assign_function(self, func, *args, **kwargs):
"""
Assign a function to be run in a background thread.
Stops any existing worker before creating a new one.
"""
# Stop any existing worker
if self.current_worker:
self.current_worker.stop()
# Create a new worker
worker = Worker(func, *args, **kwargs)
# Store the current worker
self.current_worker = worker
# Update signals reference
self.signals = worker.signals
def run(self):
"""
Run the currently assigned worker in the thread pool.
Adds the worker to the thread pool for execution.
"""
if self.current_worker:
# Add worker to thread pool
self.thread_pool.start(self.current_worker)
def waitForDone(self):
"""
Wait for all threads in the pool to complete.
Useful for clean shutdown of the application.
"""
# Stop current worker if exists
if self.current_worker:
self.current_worker.stop()
# Wait for thread pool to finish
self.thread_pool.waitForDone()
import logging
import sys
from pathlib import Path
from PyQt6.QtCore import QObject, pyqtSignal, Qt
from PyQt6.QtWidgets import QTextBrowser
class QLoggerLevelSignal(QObject):
"""Signals for the Logger QTextBrowser derived class."""
debug = pyqtSignal()
info = pyqtSignal()
warning = pyqtSignal()
error = pyqtSignal()
class LoggerWindow(QTextBrowser):
"""A QTextBrowser subclass that have signals and a dict for ease of access to said signals."""
def __init__(self, parent=None):
super().__init__(parent)
self.signals = QLoggerLevelSignal()
self.signals_to_emit = {
"DEBUG": self.signals.debug,
"INFO": self.signals.info,
"WARNING": self.signals.warning,
"ERROR": self.signals.error,
}
# Set document max size to prevent memory issues with very large logs
self.document().setMaximumBlockCount(5000) # Limit to last 5000 lines
# Set text browser properties
self.setReadOnly(True)
self.setOpenExternalLinks(False)
# Enable smooth scrolling
self.verticalScrollBar().setSingleStep(2)
class QLogger(logging.Handler, QObject):
"""Logger handler for the Qt GUI"""
# Define the signal as a class attribute
logMessage = pyqtSignal(str, str)
def __init__(self, text_browser):
logging.Handler.__init__(self)
QObject.__init__(self) # Initialize QObject
self.text_browser = text_browser
self.colors_dict = {
"DEBUG": "white",
"INFO": "green",
"WARNING": "yellow",
"ERROR": "red",
}
# Connect the signal to the slot method
self.logMessage.connect(self.write_log, Qt.ConnectionType.QueuedConnection)
def emit(self, record):
"""
Logs the record to the text browser object.
@param record: LogRecord object to log.
"""
msg = self.format(record)
# Emit the signal with levelname and formatted message
self.logMessage.emit(record.levelname, msg)
def write_log(self, levelname, msg):
"""Write the log message to the text browser in the main thread."""
color = self.colors_dict[levelname]
formatted_msg = f'<font color="{color}">{msg}</font>'
self.text_browser.signals_to_emit[levelname].emit()
self.text_browser.append(formatted_msg)
# Scroll to bottom
scrollbar = self.text_browser.verticalScrollBar()
scrollbar.setValue(scrollbar.maximum())
def setup_logger(log_file_name="dlss_updater.log"):
"""
Setups the initial logger.
param: log_file_name: filename to be used for the logfile.
return: logger instance created.
"""
logger = logging.getLogger("DLSSUpdater")
# Check if the logger has already been configured
if not logger.handlers:
logger.setLevel(logging.DEBUG)
log_file_path = (
Path(sys.executable).parent / log_file_name
if getattr(sys, "frozen", False)
else Path(__file__).parent / log_file_name
)
# Create handlers
console_handler = logging.StreamHandler(sys.stdout)
file_handler = logging.FileHandler(log_file_path, encoding="utf-8")
# Create formatter and add it to handlers
log_format = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
console_handler.setFormatter(log_format)
file_handler.setFormatter(log_format)
# Add handlers to the logger
logger.addHandler(console_handler)
logger.addHandler(file_handler)
# Prevent propagation to avoid duplicate logs
logger.propagate = False
return logger
def add_qt_handler(logger_to_extend, text_browser):
"""
Add a QTextBrowser handler to an existing logger instance.
@param: logger_to_extend: logger instance to be extended.
@param: text_browser: QTextBrowser instance to be added as a logger.
"""
# Remove any existing QLogger handlers
for handler in logger_to_extend.handlers[:]:
if isinstance(handler, QLogger):
logger_to_extend.removeHandler(handler)
# Create a new QLogger handler
text_browser_handler = QLogger(text_browser)
formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
text_browser_handler.setFormatter(formatter)
logger_to_extend.addHandler(text_browser_handler)
# Usage example
if __name__ == "__main__":
logger = setup_logger()
logger.info("This is a test log message")
logger.info("This is a test logger.info message with an argument: %s", "test arg")
from PyQt6.QtCore import (
Qt,
QSize,
QPoint,
QPointF,
QRectF,
QEasingCurve,
QPropertyAnimation,
QSequentialAnimationGroup,
pyqtSlot,
pyqtProperty,
)
from PyQt6.QtWidgets import QCheckBox
from PyQt6.QtGui import QColor, QBrush, QPaintEvent, QPen, QPainter
class AnimatedToggle(QCheckBox):
def __init__(
self,
parent=None,
track_color="#888888",
thumb_color="#FFFFFF",
track_active_color="#2D6E88",
thumb_active_color="#FFFFFF",
animation_duration=120,
):
super().__init__(parent)
# Colors
self._track_color = QColor(track_color)
self._thumb_color = QColor(thumb_color)
self._track_active_color = QColor(track_active_color)
self._thumb_active_color = QColor(thumb_active_color)
# Dimensions
self._track_radius = 11
self._thumb_radius = 8
# Animation
self._animation_duration = animation_duration
self._margin = max(0, self._thumb_radius - self._track_radius)
self._offset = 0
self._pulse_radius = 0
# Setup animations
self.animation = QPropertyAnimation(self, b"offset")
self.animation.setEasingCurve(QEasingCurve.Type.InOutCubic)
self.animation.setDuration(self._animation_duration)
self.pulse_animation = QPropertyAnimation(self, b"pulse_radius")
self.pulse_animation.setDuration(self._animation_duration)
self.pulse_animation.setEasingCurve(QEasingCurve.Type.InOutCubic)
self.animations_group = QSequentialAnimationGroup()
self.animations_group.addAnimation(self.animation)
self.animations_group.addAnimation(self.pulse_animation)
# Setup initial state
self.setFixedSize(
self._track_radius * 4 + self._margin * 2,
self._track_radius * 2 + self._margin * 2,
)
self.setCursor(Qt.CursorShape.PointingHandCursor)
def sizeHint(self):
return QSize(
4 * self._track_radius + 2 * self._margin,
2 * self._track_radius + 2 * self._margin,
)
def hitButton(self, pos: QPoint):
return self.contentsRect().contains(pos)
@pyqtSlot(int)
def setChecked(self, checked):
super().setChecked(checked)
self.offset = 1 if checked else 0
def paintEvent(self, e: QPaintEvent):
# Set up painter
p = QPainter(self)
p.setRenderHint(QPainter.RenderHint.Antialiasing)
# Get current state
checked = self.isChecked()
enabled = self.isEnabled()
# Calculate sizes
track_opacity = 0.6 if enabled else 0.3
margin = self._margin
thumb_radius = self._thumb_radius
track_radius = self._track_radius
# Get widget dimensions
width = self.width() - 2 * margin
height = self.height() - 2 * margin
# Draw track
track_brush = QBrush(self._track_active_color if checked else self._track_color)
track_pen = QPen(Qt.PenStyle.NoPen)
p.setBrush(track_brush)
p.setPen(track_pen)
p.setOpacity(track_opacity)
p.drawRoundedRect(margin, margin, width, height, track_radius, track_radius)
# Calculate thumb position
total_offset = width - 2 * thumb_radius
offset = total_offset * self.offset
# Draw thumb
p.setBrush(QBrush(self._thumb_active_color if checked else self._thumb_color))
p.setPen(QPen(Qt.PenStyle.NoPen))
p.setOpacity(1.0)
p.drawEllipse(
QPointF(margin + thumb_radius + offset, margin + height / 2),
thumb_radius,
thumb_radius,
)
# Draw pulse if animating
if self._pulse_radius > 0:
p.setBrush(QBrush(QColor(0, 0, 0, 0)))
p.setPen(QPen(QColor(0, 0, 0, 0)))
p.setOpacity(0.1)
p.drawEllipse(
QPointF(margin + thumb_radius + offset, margin + height / 2),
self._pulse_radius,
self._pulse_radius,
)
p.end()
# Property animations
@pyqtProperty(float)
def offset(self):
return self._offset
@offset.setter
def offset(self, value):
self._offset = value
self.update()
@pyqtProperty(float)
def pulse_radius(self):
return self._pulse_radius
@pulse_radius.setter
def pulse_radius(self, value):
self._pulse_radius = value
self.update()
# Override the change event to handle state changes
def mouseReleaseEvent(self, e):
super().mouseReleaseEvent(e)
if self.isEnabled():
if self.isChecked():
self.animation.setStartValue(0)
self.animation.setEndValue(1)
else:
self.animation.setStartValue(1)
self.animation.setEndValue(0)
self.animations_group.start()
from .. import __version__, resource_path
from ..utils import update_dlss_versions, extract_game_name, DLL_TYPE_MAP
import os
from PyQt6.QtCore import Qt, QUrl, QSize, QTimer, pyqtSignal
from PyQt6.QtGui import QDesktopServices, QIcon
from PyQt6.QtWidgets import (
QMainWindow,
QWidget,
QVBoxLayout,
QSplitter,
QPushButton,
QFileDialog,
QHBoxLayout,
QLabel,
QMenu,
QDialog,
QTextBrowser,
QScrollArea,
QListWidget,
QListWidgetItem,
QDialogButtonBox,
QFrame,
QProgressBar,
QSizePolicy,
)
from dlss_updater.lib.threading_lib import ThreadManager
from pathlib import Path
from dlss_updater.config import config_manager, LauncherPathName
from dlss_updater.logger import add_qt_handler, LoggerWindow, setup_logger
from dlss_updater.whitelist import get_all_blacklisted_games
from dlss_updater.main_ui.animated_toggle import AnimatedToggle
class LoadingOverlay(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
# Make this widget transparent
self.setAttribute(Qt.WidgetAttribute.WA_TransparentForMouseEvents)
self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
# Create a semi-transparent background
self.setStyleSheet("background-color: rgba(0, 0, 0, 120);")
# Add a progress bar
layout = QVBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
container = QWidget()
container_layout = QVBoxLayout(container)
self.progress_bar = QProgressBar()
self.progress_bar.setRange(0, 0) # Indeterminate progress
self.progress_bar.setFixedSize(250, 20)
self.progress_bar.setStyleSheet(
"""
QProgressBar {
border: 1px solid #5A5A5A;
border-radius: 5px;
background-color: #3C3C3C;
text-align: center;
}
QProgressBar::chunk {
background-color: #2D6E88;
width: 10px;
margin: 0.5px;
}
"""
)
self.label = QLabel("Processing...")
self.label.setStyleSheet(
"color: white; background-color: transparent; font-size: 14px; padding: 10px;"
)
container_layout.addWidget(self.label, 0, Qt.AlignmentFlag.AlignCenter)
container_layout.addWidget(self.progress_bar, 0, Qt.AlignmentFlag.AlignCenter)
# Center the container
container.setStyleSheet(
"background-color: #2E2E2E; border-radius: 10px; padding: 20px;"
)
container.setFixedSize(300, 100)
layout.addWidget(container, 0, Qt.AlignmentFlag.AlignCenter)
# Hide by default
self.hide()
def showEvent(self, event):
# Position the overlay to cover the entire parent widget
if self.parent():
self.setGeometry(self.parent().rect())
super().showEvent(event)
# Manual fade in using timers but with smoother transition
self.setWindowOpacity(0.0)
for i in range(11): # Keep original steps for stability
opacity = i / 10.0
QTimer.singleShot(i * 25, lambda op=opacity: self.setWindowOpacity(op))
def set_message(self, message):
self.label.setText(message)
def hideWithAnimation(self):
# Manual fade out using timers
for i in range(11): # Keep original steps for stability
opacity = 1.0 - (i / 10.0)
QTimer.singleShot(i * 25, lambda op=opacity: self.setWindowOpacity(op))
# Hide when fully transparent
QTimer.singleShot(300, self.hide)
class NotificationWidget(QWidget):
"""A notification widget with proper positioning and animations"""
def __init__(self, message, parent=None):
super().__init__(parent)
# Setup widget
self.setWindowFlags(Qt.WindowType.FramelessWindowHint | Qt.WindowType.Tool)
self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
self.setStyleSheet("background-color: transparent;")
# Main layout
layout = QVBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
# Notification label
self.label = QLabel(message)
self.label.setStyleSheet(
"""
background-color: #2D6E88;
color: white;
border-radius: 8px;
padding: 10px 20px;
font: bold 12px;
"""
)
self.label.setAlignment(Qt.AlignmentFlag.AlignCenter)
layout.addWidget(self.label)
# Size and position - now handled by the parent positioning method
self.adjustSize()
def showEvent(self, event):
super().showEvent(event)
# Keep original animation timing for stability
self.setWindowOpacity(0.0)
for i in range(11):
opacity = i / 10.0
QTimer.singleShot(i * 25, lambda op=opacity: self.setWindowOpacity(op))
# Schedule fade out
QTimer.singleShot(2000, self.start_fade_out)
def start_fade_out(self):
# Keep original animation timing for stability
for i in range(11):
opacity = 1.0 - (i / 10.0)
QTimer.singleShot(i * 25, lambda op=opacity: self.setWindowOpacity(op))
# Close when fully transparent
QTimer.singleShot(300, self.close)
class MainWindow(QMainWindow):
# Define signals at the class level
resized = pyqtSignal()
def __init__(self, logger=None):
super().__init__()
self.thread_manager = ThreadManager(self)
self.button_enum_dict = {}
self.setWindowTitle("DLSS-Updater")
self.setGeometry(100, 100, 700, 500) # Starting size
self.setMinimumSize(700, 500) # Minimum allowed size
# FIX: Limit maximum height more strictly to prevent excessive vertical space.
# In theory no longer needed, but kept here just in case.
# self.setMaximumSize(1200, 650) # Reduced maximum height from 800 to 650
# Control variables used to keep track of app state.
self.logger_expanded = False
self.original_width = None
self.current_button_style = None
# FIX: Keep track of active notifications for repositioning
self.active_notifications = []
# Load and set the window icon
logo_path = resource_path(os.path.join("icons", "dlss_updater.png"))
logo_icon = QIcon(logo_path)
self.setWindowIcon(logo_icon)
# Main container
self.main_container = QWidget()
main_layout = QVBoxLayout()
main_layout.setSpacing(10) # Consistent spacing
main_layout.setContentsMargins(10, 10, 10, 10) # Consistent margins
header_layout = QHBoxLayout()
main_layout.setAlignment(Qt.AlignmentFlag.AlignTop)
# Header section with welcome, logo, version, and other buttons
header_left = QHBoxLayout()
welcome_label = QLabel("Welcome to the GUI :) -Deco")
version_label = QLabel(f"v{__version__}")
welcome_label.setStyleSheet(
"color: white; font-size: 16px; background-color: transparent;"
)
version_label.setStyleSheet(
"color: #888888; font-size: 12px; margin-left: 8px; background-color: transparent;"
)
header_left.addWidget(welcome_label)
header_left.addStretch()
header_left.addWidget(version_label)
# Add the header layout to the main layout
header_layout.addLayout(header_left)
main_layout.addLayout(header_layout)
# Add the DLSS Updater logo as a separate element
self.logo_label = QLabel()
self.logo_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
main_layout.addWidget(self.logo_label)
# Custom folders info
info_label = QLabel(
"Note: You can now use the custom folder buttons below to add up to 4 additional game folders."
)
info_label.setWordWrap(True)
info_label.setStyleSheet(
"color: white; background-color: #3C3C3C; padding: 10px; border-radius: 4px; border: none;"
)
main_layout.addWidget(info_label)
# Donate, report a bug, contact, release notes, and view logs buttons
button_layout = QHBoxLayout()
button_layout.setSpacing(5) # Consistent spacing
donate_button = self.create_styled_button(
"☕ Support Development", "heart.png", "Support the development"
)
donate_button.clicked.connect(
lambda: QDesktopServices.openUrl(QUrl("https://buymeacoffee.com/decouk"))
)
report_bug_button = self.create_styled_button(
"🐛 Report a Bug", "bug.png", "Report a bug"
)
report_bug_button.clicked.connect(
lambda: QDesktopServices.openUrl(
QUrl("https://github.com/Recol/DLSS-Updater/issues")
)
)
contact_button = self.create_styled_button(
"📞 Contact", "contact.png", "Contact the developer"
)
contact_menu = QMenu()
twitter_action = contact_menu.addAction("Twitter")
discord_action = contact_menu.addAction("Discord")
twitter_action.triggered.connect(
lambda: QDesktopServices.openUrl(QUrl("https://x.com/iDeco_UK"))
)
discord_action.triggered.connect(
lambda: QDesktopServices.openUrl(
QUrl("https://discord.com/users/162568099839606784")
)
)
contact_button.setMenu(contact_menu)
release_notes_button = self.create_styled_button(
"📝 Release Notes", "notes.png", "View release notes"
)
release_notes_button.clicked.connect(self.show_release_notes)
view_logs_button = self.create_styled_button(
"📋 View Logs", "logs.png", "View application logs"
)
view_logs_button.clicked.connect(self.toggle_logger_window)
# Add Blacklist Manager button
blacklist_button = self.create_styled_button(
"⚙ Manage Blacklist", "settings.png", "Manage blacklisted games"
)
blacklist_button.clicked.connect(self.show_blacklist_manager)
# Add hover effect to buttons
for btn in [
donate_button,
report_bug_button,
contact_button,
release_notes_button,
view_logs_button,
blacklist_button,
]:
self.add_button_hover_effect(btn)
button_layout.addWidget(donate_button)
button_layout.addWidget(report_bug_button)
button_layout.addWidget(contact_button)
button_layout.addWidget(release_notes_button)
button_layout.addWidget(view_logs_button)
button_layout.addWidget(blacklist_button)
main_layout.addLayout(button_layout)
# Original logger splitter setup
self.logger_splitter = QSplitter(Qt.Orientation.Horizontal)
self.can_continue = False
self.button_list = []
self.path_list = []
# Launcher buttons setup
self.setup_launcher_buttons()
# Create QTextBrowser widget
self.logger_window = LoggerWindow(self)
# Set up splitter layout
self.logger_splitter.addWidget(self.browse_buttons_container_widget)
self.logger_splitter.addWidget(self.logger_window)
# We want the logger_window to be collapsed by default
self.logger_splitter.setSizes([1, 0])
main_layout.addWidget(self.logger_splitter)
self.main_container.setLayout(main_layout)
self.main_container.setSizePolicy(QSizePolicy.Policy.MinimumExpanding, QSizePolicy.Policy.MinimumExpanding)
self.logger_splitter.setSizePolicy(QSizePolicy.Policy.MinimumExpanding, QSizePolicy.Policy.MinimumExpanding)
self.setCentralWidget(self.main_container)
# Create the loading overlay
self.loading_overlay = LoadingOverlay(self.main_container)
self.loading_overlay.hide()
# Set up logging
self.logger = logger or setup_logger()
add_qt_handler(self.logger, self.logger_window)
self.logger_window.signals.error.connect(self.expand_logger_window)
# Connect the update button to the threaded update function
self.start_update_button.clicked.connect(self.call_threaded_update)
# Connect resize event to handle notification repositioning
self.resized.connect(self.reposition_notifications)
self.apply_dark_theme()
def resizeEvent(self, event):
"""Handle window resize events"""
super().resizeEvent(event)
# Emit the custom resize signal
self.resized.emit()
# Update overlay position if it's visible
if hasattr(self, "loading_overlay") and self.loading_overlay.isVisible():
self.loading_overlay.setGeometry(self.main_container.rect())
def reposition_notifications(self):
"""Reposition all active notifications when window size changes"""
for notification in self.active_notifications[:]:
if notification.isVisible():
self.position_notification(notification)
else:
# Be careful with list modification during iteration
try:
self.active_notifications.remove(notification)
except ValueError:
# Notification might have been removed already
pass
def position_notification(self, notification):
"""Position a notification properly within the window bounds"""
if notification and notification.isVisible() and notification.parent():
notification.adjustSize()
parent_rect = self.rect()
notification_width = notification.width()
notification_height = notification.height()
# Calculate position - centered horizontally, near bottom vertically
x = max(0, (parent_rect.width() - notification_width) // 2)
y = max(0, parent_rect.height() - notification_height - 30)
# Ensure notification stays within parent bounds
x = min(x, parent_rect.width() - notification_width)
y = min(y, parent_rect.height() - notification_height)
notification.move(x, y)
def add_button_hover_effect(self, button):
"""Add hover effect to button while preserving original styling"""
# Store the button's original style
original_style = button.styleSheet()
# Check if this is a custom folder button (blue background) or regular button (gray background)
is_custom = (
"background-color: #2D6E88" in original_style
or "background-color: #2D5A88" in original_style
)
# Create hover style by changing only the background color while preserving other styles
if is_custom:
original_bg = (
"#2D6E88"
if "background-color: #2D6E88" in original_style
else "#2D5A88"
)
hover_bg = "#367FA3" if original_bg == "#2D6E88" else "#366BA3"
hover_style = original_style.replace(
f"background-color: {original_bg}", f"background-color: {hover_bg}"
)
else:
hover_style = original_style.replace(
"background-color: #4D4D4D", "background-color: #5A5A5A"
)
# Store original event handlers
original_enter = button.enterEvent
original_leave = button.leaveEvent
# Define new event handlers
def new_enter_event(event):
self.current_button_style = button.styleSheet()
button.setStyleSheet(hover_style)
if original_enter:
original_enter(event)
def new_leave_event(event):
if self.current_button_style:
button.setStyleSheet(self.current_button_style)
if original_leave:
original_leave(event)
# Override event handlers
button.enterEvent = new_enter_event
button.leaveEvent = new_leave_event
def show_blacklist_manager(self):
"""Show dialog to manage blacklisted games"""
dialog = QDialog(self)
dialog.setWindowTitle("Manage Blacklisted Games")
dialog.setMinimumWidth(500)
dialog.setMinimumHeight(400)
layout = QVBoxLayout()
info_label = QLabel(
"Select games to ignore in the blacklist. Selected games will be updated even if they're in the blacklist."
)
info_label.setWordWrap(True)
info_label.setStyleSheet(
"margin-bottom: 10px; background-color: transparent; border: none;"
)
layout.addWidget(info_label)
# Create a list widget with animated toggles for blacklisted games
game_list = QListWidget()
game_list.setStyleSheet("background-color: #3C3C3C;")
# Get all blacklisted games
blacklisted_games = get_all_blacklisted_games()
blacklisted_games = sorted(blacklisted_games) # Sort alphabetically
# Get currently skipped games
skipped_games = config_manager.get_all_blacklist_skips()
# Store toggle widgets to access them later
toggle_widgets = []
for game in blacklisted_games:
item = QListWidgetItem()
item_widget = QWidget()
item_layout = QHBoxLayout(item_widget)
item_layout.setContentsMargins(5, 2, 5, 2)
game_label = QLabel(game)
game_label.setStyleSheet("color: white; background: transparent;")
# Create animated toggle
toggle = AnimatedToggle()
toggle.setChecked(game in skipped_games)
toggle.game_name = game # Store game name with the toggle
toggle_widgets.append(toggle)
item_layout.addWidget(game_label)
item_layout.addStretch()
item_layout.addWidget(toggle)
item.setSizeHint(item_widget.sizeHint())
game_list.addItem(item)
game_list.setItemWidget(item, item_widget)
layout.addWidget(game_list)
# Add Select All and Deselect All buttons
selection_layout = QHBoxLayout()
select_all_button = QPushButton("Select All")
select_all_button.clicked.connect(
lambda: self.toggle_all_toggles(toggle_widgets, True)
)
deselect_all_button = QPushButton("Deselect All")
deselect_all_button.clicked.connect(
lambda: self.toggle_all_toggles(toggle_widgets, False)
)
# Add hover effect to buttons
self.add_button_hover_effect(select_all_button)
self.add_button_hover_effect(deselect_all_button)
selection_layout.addWidget(select_all_button)
selection_layout.addWidget(deselect_all_button)
layout.addLayout(selection_layout)
# Add OK and Cancel buttons
button_box = QDialogButtonBox(
QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel
)
button_box.accepted.connect(dialog.accept)
button_box.rejected.connect(dialog.reject)
layout.addWidget(button_box)
dialog.setLayout(layout)
# Process the result if accepted
if dialog.exec() == QDialog.DialogCode.Accepted:
# Clear existing skips
config_manager.clear_all_blacklist_skips()
# Add new skips
for toggle in toggle_widgets:
if toggle.isChecked():
config_manager.add_blacklist_skip(toggle.game_name)
self.logger.info("Updated blacklist skip settings")
# Show notification
self.show_notification("Blacklist settings updated!")
def toggle_all_toggles(self, toggles, state):
"""Toggle all toggle switches to the given state"""
for toggle in toggles:
toggle.setChecked(state)
def safe_disconnect(self, signal, slot):
"""Safely disconnect a signal from a slot if connected"""
try:
if signal and slot:
signal.disconnect(slot)
except Exception:
# Ignore any disconnect errors
pass
def show_notification(self, message, duration=2000):
"""Show a floating notification message properly positioned"""
notification = NotificationWidget(message, self)
# Add to active notifications list
self.active_notifications.append(notification)
# Position notification
self.position_notification(notification)
# Show notification
notification.show()
# Remove from active notifications after it's closed
QTimer.singleShot(
duration + 300, lambda: self._remove_notification(notification)
)
def _remove_notification(self, notification):
"""Safely remove a notification from the active list"""
if notification in self.active_notifications:
try:
self.active_notifications.remove(notification)
except ValueError:
# Already removed
pass
def show_release_notes(self):
"""Display release notes in a dialog"""
release_notes_file = Path(resource_path("release_notes.txt"))
if release_notes_file.exists():
with open(release_notes_file, "r") as file:
notes = file.read()
dialog = QDialog(self)
dialog.setWindowTitle("Release Notes")
layout = QVBoxLayout()
text_browser = QTextBrowser()
text_browser.setPlainText(notes)
text_browser.setStyleSheet("background-color: #3C3C3C; color: white;")
layout.addWidget(text_browser)
dialog.setLayout(layout)
dialog.resize(500, 400)
# Simple fade-in effect with opacity
dialog.setWindowOpacity(0.0)
dialog.show()
# Gradually increase opacity - keep original timing for stability
for i in range(11):
opacity = i / 10.0
QTimer.singleShot(
i * 25, lambda op=opacity: dialog.setWindowOpacity(op)
)
dialog.exec()
def reset_path(self):
"""Reset the associated launcher path"""
reset_button = self.sender()
launcher_button = reset_button.property("reset_button")
if launcher_button:
launcher_enum = self.button_enum_dict.get(launcher_button.objectName())
if launcher_enum:
config_manager.reset_launcher_path(launcher_enum)
launcher_button.setText(launcher_button.objectName())
self.logger.info(f"Reset path for {launcher_button.objectName()}")
# Show notification
self.show_notification(f"Reset path for {launcher_button.objectName()}")
def expand_logger_window(self):
"""Increase app window size and expands the logger window. Used only for errors."""
if self.logger_expanded:
return
self.original_width = self.width()
target_width = min(int(self.width() * 1.4), self.maximumWidth())
# Set the splitter sizes directly
self.logger_splitter.setSizes([target_width // 2, target_width // 2])
self.logger_expanded = True
def toggle_logger_window(self):
"""Toggle logger window with proper sizing constraints"""
try:
if self.logger_expanded:
# Collapse logger
self.logger_splitter.setSizes([self.original_width, 0])
self.logger_expanded = False
return
# Store original width before expanding
self.original_width = self.width()
target_width = min(int(self.width() * 1.4), self.maximumWidth())
# Expand logger
self.logger_splitter.setSizes([target_width // 2, target_width // 2])
self.logger_expanded = True
except Exception as e:
self.logger.error(f"Error toggling logger window: {e}")
def create_styled_button(
self, text: str, icon_path: str, tooltip: str = ""
) -> QPushButton:
"""
Creates styled buttons with the specific icon and tooltip.
@param text: Text to be displayed.
@param icon_path: Path to the icon.
@param tooltip: Tooltip on hover. Optional.
@return: QPushButton Created button.
"""
button = QPushButton(f" {text}", self)
# Load and process icon
icon = QIcon(resource_path(os.path.join("icons", icon_path)))
button.setIcon(icon)
button.setIconSize(QSize(24, 24)) # Consistent icon size
# Set fixed height for uniformity
button.setMinimumHeight(40)
# Set alignment and size policy
button.setStyleSheet(button.styleSheet() + "text-align: left;")
button.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
if tooltip:
button.setToolTip(tooltip)
# Connect to browse functionality if not the update button
if "Update" not in text:
if "Custom Folder" in text or any(
launcher in text
for launcher in [
"Steam",
"EA",
"Ubisoft",
"Epic",
"GOG",
"Battle.net",
"Xbox",
]
):
button.clicked.connect(self.browse_folder)
return button
def setup_launcher_buttons(self):
"""Setups the launcher buttons."""
# Create launcher buttons
self.steam_text_browser = self.create_styled_button(
"Steam Games", "steam.png", "Select Steam game locations"
)
self.ea_text_browser = self.create_styled_button(
"EA Games", "ea.png", "Select EA game locations"
)
self.ubisoft_text_browser = self.create_styled_button(
"Ubisoft Games", "ubisoft.png", "Select Ubisoft game locations"
)
self.epic_text_browser = self.create_styled_button(
"Epic Games", "epic.png", "Select Epic game locations"
)
self.gog_text_browser = self.create_styled_button(
"GOG Games", "gog.png", "Select GOG game locations"
)
self.battlenet_text_browser = self.create_styled_button(
"Battle.net Games", "battlenet.png", "Select Battle.net game locations"
)
self.xbox_text_browser = self.create_styled_button(
"Xbox Games", "xbox.png", "Select Xbox game locations"
)
# Add custom folder buttons
self.custom1_text_browser = self.create_styled_button(
"Custom Folder 1", "folder.png", "Select custom game location 1"
)
self.custom2_text_browser = self.create_styled_button(
"Custom Folder 2", "folder.png", "Select custom game location 2"
)
self.custom3_text_browser = self.create_styled_button(
"Custom Folder 3", "folder.png", "Select custom game location 3"
)
self.custom4_text_browser = self.create_styled_button(
"Custom Folder 4", "folder.png", "Select custom game location 4"
)
# Update button with special styling
self.start_update_button = self.create_styled_button(
"Start Update", "update.png", "Start DLSS update process"
)
# Set object names for identification
self.steam_text_browser.setObjectName("Steam")
self.ea_text_browser.setObjectName("EA")
self.ubisoft_text_browser.setObjectName("UBISOFT")
self.epic_text_browser.setObjectName("EPIC")
self.gog_text_browser.setObjectName("GOG")
self.battlenet_text_browser.setObjectName("BATTLENET")
self.xbox_text_browser.setObjectName("XBOX")
self.custom1_text_browser.setObjectName("CUSTOM1")
self.custom2_text_browser.setObjectName("CUSTOM2")
self.custom3_text_browser.setObjectName("CUSTOM3")
self.custom4_text_browser.setObjectName("CUSTOM4")
# Store buttons in list
self.button_list = [
self.steam_text_browser,
self.ea_text_browser,
self.ubisoft_text_browser,
self.epic_text_browser,
self.gog_text_browser,
self.battlenet_text_browser,
self.xbox_text_browser,
self.custom1_text_browser,
self.custom2_text_browser,
self.custom3_text_browser,
self.custom4_text_browser,
]
# Update button dictionary
self.button_enum_dict.update(
{
"Steam": LauncherPathName.STEAM,
"EA": LauncherPathName.EA,
"UBISOFT": LauncherPathName.UBISOFT,
"EPIC": LauncherPathName.EPIC,
"GOG": LauncherPathName.GOG,
"BATTLENET": LauncherPathName.BATTLENET,
"XBOX": LauncherPathName.XBOX,
"CUSTOM1": LauncherPathName.CUSTOM1,
"CUSTOM2": LauncherPathName.CUSTOM2,
"CUSTOM3": LauncherPathName.CUSTOM3,
"CUSTOM4": LauncherPathName.CUSTOM4,
}
)
# Create layout for buttons with reset buttons
browse_buttons_layout = QVBoxLayout()
browse_buttons_layout.setSpacing(5) # Consistent spacing
browse_buttons_layout.setContentsMargins(5, 5, 5, 5) # Consistent margins
# Create a separator for original launchers and custom folders
def create_separator(text):
# Create a horizontal line with label - fixed styling
container = QWidget()
separator_layout = QHBoxLayout(container)
separator_layout.setContentsMargins(0, 10, 0, 5)
# Create label with better styling
label = QLabel(text)
label.setStyleSheet(
"color: white; background-color: transparent; border: none;"
)
label.setMaximumWidth(150) # Limit label width
# Create lines
left_line = QFrame()
left_line.setFrameShape(QFrame.Shape.HLine)
left_line.setFrameShadow(QFrame.Shadow.Sunken)
left_line.setStyleSheet("background-color: #5A5A5A; border: none;")
right_line = QFrame()
right_line.setFrameShape(QFrame.Shape.HLine)
right_line.setFrameShadow(QFrame.Shadow.Sunken)
right_line.setStyleSheet("background-color: #5A5A5A; border: none;")
# Add everything to layout
separator_layout.addWidget(left_line)
separator_layout.addWidget(label, 0, Qt.AlignmentFlag.AlignCenter)
separator_layout.addWidget(right_line)
return container
# Add built-in launchers label
browse_buttons_layout.addWidget(create_separator("Game Launchers"))
# Add standard launcher buttons
for button in self.button_list[:7]: # First 7 are standard launchers
button_row = QHBoxLayout()
button_row.addWidget(button, stretch=1)
# Create reset button
reset_button = QPushButton()
reset_button.setIcon(
QIcon(resource_path(os.path.join("icons", "reset.png")))
)
reset_button.setIconSize(QSize(16, 16))
reset_button.setFixedSize(24, 24)
reset_button.setToolTip("Reset path")
reset_button.setProperty("reset_button", button)
reset_button.clicked.connect(self.reset_path)
reset_button.setStyleSheet(
"""
QPushButton {
background-color: #4D4D4D;
border: 1px solid #7F7F7F;
border-radius: 4px;
padding: 2px;
margin: 2px;
}
QPushButton:hover {
background-color: #5A5A5A;
}
QPushButton:pressed {
background-color: #444444;
}
"""
)
# Add hover effect to reset button
self.add_button_hover_effect(reset_button)
button_row.addWidget(reset_button)
browse_buttons_layout.addLayout(button_row)
# Add custom folders label
browse_buttons_layout.addWidget(create_separator("Custom Folders"))
# Add custom folder buttons
for button in self.button_list[7:]: # Last 4 are custom folders
button_row = QHBoxLayout()
button_row.addWidget(button, stretch=1)
# Create reset button
reset_button = QPushButton()
reset_button.setIcon(
QIcon(resource_path(os.path.join("icons", "reset.png")))
)
reset_button.setIconSize(QSize(16, 16))
reset_button.setFixedSize(24, 24)
reset_button.setToolTip("Reset path")
reset_button.setProperty("reset_button", button)
reset_button.clicked.connect(self.reset_path)
reset_button.setStyleSheet(
"""
QPushButton {
background-color: #4D4D4D;
border: 1px solid #7F7F7F;
border-radius: 4px;
padding: 2px;
margin: 2px;
}
QPushButton:hover {
background-color: #5A5A5A;
}
QPushButton:pressed {
background-color: #444444;
}
"""
)
# Add hover effect to reset button
self.add_button_hover_effect(reset_button)
button_row.addWidget(reset_button)
browse_buttons_layout.addLayout(button_row)
# Add update button separator and button
browse_buttons_layout.addWidget(create_separator("Update"))
browse_buttons_layout.addWidget(self.start_update_button)
# Add hover effects to all buttons
for button in self.button_list:
self.add_button_hover_effect(button)
# Add special hover effect to update button
self.add_button_hover_effect(self.start_update_button)
# Create a scrollable area for the buttons
scroll_area = QScrollArea()
scroll_area.setWidgetResizable(True)
scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
scroll_area.setSizePolicy(QSizePolicy.Policy.MinimumExpanding, QSizePolicy.Policy.MinimumExpanding)
self.browse_buttons_container_widget = QWidget()
self.browse_buttons_container_widget.setLayout(browse_buttons_layout)
# Set size policy to ensure proper expansion
self.browse_buttons_container_widget.setSizePolicy(
QSizePolicy.Policy.MinimumExpanding, QSizePolicy.Policy.MinimumExpanding
)
scroll_area.setWidget(self.browse_buttons_container_widget)
self.browse_buttons_container_widget = scroll_area
def call_threaded_update(self):
"""Start the update process in a separate thread."""
try:
# Disable the button immediately to prevent multiple clicks
self.start_update_button.setEnabled(False)
self.logger.info("Starting update process in thread...")
# Show loading overlay
self.loading_overlay.set_message("Starting update process...")
self.loading_overlay.setGeometry(
self.main_container.rect()
) # Ensure correct position
self.loading_overlay.show()
# Clear any previous signal connections
if self.thread_manager.signals:
try:
# Disconnect previous connections if they exist
self.thread_manager.signals.finished.disconnect()
self.thread_manager.signals.result.disconnect()
self.thread_manager.signals.error.disconnect()
except TypeError:
# Ignore errors if signals were not connected
pass
# Assign the update function
self.thread_manager.assign_function(update_dlss_versions)
# Connect new signals
self.thread_manager.signals.finished.connect(self.handle_update_finished)
self.thread_manager.signals.result.connect(self.handle_update_result)
self.thread_manager.signals.error.connect(self.handle_update_error)
# Run the thread
self.thread_manager.run()
# Simulate progress updates with timer (since we don't have real progress data)
self.progress_messages = [
"Scanning for DLLs...",
"Looking for DLSS files...",
"Checking XeSS files...",
"Creating backups...",
"Updating DLL files...",
"Finalizing updates...",
]
self.message_index = 0
# Keep original timer interval for stability
self.progress_timer = QTimer()
self.progress_timer.timeout.connect(self.update_loading_message)
self.progress_timer.start(800) # Original timing: 800ms
except Exception as e:
self.logger.error(f"Error starting update thread: {e}")
import traceback
self.logger.error(traceback.format_exc())
# Ensure button is re-enabled in case of an error
self.start_update_button.setEnabled(True)
self.loading_overlay.hideWithAnimation()
if hasattr(self, "progress_timer") and self.progress_timer.isActive():
self.progress_timer.stop()
def update_loading_message(self):
"""Update the loading message with next progress text"""
if hasattr(self, "message_index") and self.message_index < len(
self.progress_messages
):
self.loading_overlay.set_message(self.progress_messages[self.message_index])
self.message_index += 1
else:
# Reset to first message if we've gone through all messages
self.message_index = 0
def handle_update_error(self, error):
"""
Handle errors from the update thread.
@param error: The error from the update thread.
"""
exctype, value, tb = error
self.logger.error(f"Error: {exctype}")
self.logger.error(f"Value: {value}")
self.logger.error(f"Traceback: {tb}")
self.start_update_button.setEnabled(True)
# Hide loading overlay
self.loading_overlay.hideWithAnimation()
# Stop timer for progress messages
if hasattr(self, "progress_timer") and self.progress_timer.isActive():
self.progress_timer.stop()
# Show error notification
self.show_notification(f"Error: {value}", 5000)
def handle_update_result(self, result):
"""
Handle results from the update thread.
@param result: Tuple containing (success, updated_games, skipped_games, successful_backups)
"""
try:
# Stop timer for progress messages
if hasattr(self, "progress_timer") and self.progress_timer.isActive():
self.progress_timer.stop()
# Hide loading overlay with animation
self.loading_overlay.hideWithAnimation()
if isinstance(result, tuple) and len(result) == 4:
success, updated_games, skipped_games, successful_backups = result
if success:
self.logger.info("Update process completed successfully")
# Show success notification
if updated_games:
self.show_notification(
f"Update completed: {len(updated_games)} games updated",
3000,
)
else:
self.show_notification(
"Update completed: No games needed updates", 3000
)
self.show_update_summary(
(updated_games, skipped_games, successful_backups)
)
else:
self.logger.error("Update process failed")
self.show_notification("Update process failed", 3000)
else:
self.logger.error(f"Unexpected result format: {result}")
self.show_notification(
"Update process returned unexpected result", 3000
)
except Exception as e:
self.logger.error(f"Error handling update result: {e}")
import traceback
self.logger.error(traceback.format_exc())
self.show_notification(f"Error: {str(e)}", 3000)
finally:
self.start_update_button.setEnabled(True)
def handle_update_finished(self):
"""Handle completion of the update thread."""
try:
self.logger.debug("Update thread finished")
self.start_update_button.setEnabled(True)
# Clean up worker reference
self._current_worker = None
except Exception as e:
self.logger.error(f"Error in update finished handler: {e}")
def closeEvent(self, event):
"""Handle application close event."""
try:
if self.thread_manager and self.thread_manager.current_worker:
self.thread_manager.waitForDone()
except Exception as e:
self.logger.error(f"Error during cleanup: {e}")
super().closeEvent(event)
def get_current_settings(self):
"""Get the current settings from the settings file."""
# Standard launchers
steam_path = config_manager.check_path_value(LauncherPathName.STEAM)
ea_path = config_manager.check_path_value(LauncherPathName.EA)
ubisoft_path = config_manager.check_path_value(LauncherPathName.UBISOFT)
epic_path = config_manager.check_path_value(LauncherPathName.EPIC)
gog_path = config_manager.check_path_value(LauncherPathName.GOG)
battlenet_path = config_manager.check_path_value(LauncherPathName.BATTLENET)
xbox_path = config_manager.check_path_value(LauncherPathName.XBOX)
# Custom paths
custom1_path = config_manager.check_path_value(LauncherPathName.CUSTOM1)
custom2_path = config_manager.check_path_value(LauncherPathName.CUSTOM2)
custom3_path = config_manager.check_path_value(LauncherPathName.CUSTOM3)
custom4_path = config_manager.check_path_value(LauncherPathName.CUSTOM4)
self.path_list = [
steam_path,
ea_path,
ubisoft_path,
epic_path,
gog_path,
battlenet_path,
xbox_path,
custom1_path,
custom2_path,
custom3_path,
custom4_path,
]
for i, button in enumerate(self.button_list):
if i < len(self.path_list) and self.path_list[i]:
button.setText(self.path_list[i])
def browse_folder(self):
"""Open a dialog to select a directory."""
directory = QFileDialog.getExistingDirectory(self, "Select Folder")
if directory:
directory = directory.replace("/", "\\")
self.sender().setText(directory)
config_manager.update_launcher_path(
self.button_enum_dict.get(self.sender().objectName()), directory
)
# Show notification
self.show_notification(f"Folder path updated!")
def show_update_summary(self, update_result):
"""Display the update summary in a message box."""
updated_games, skipped_games, successful_backups = update_result
# Create a custom dialog for better styling
dialog = QDialog(self)
dialog.setWindowTitle("DLSS Updater - Update Summary")
dialog.setMinimumWidth(500)
dialog.setMinimumHeight(300)
layout = QVBoxLayout(dialog)
text_browser = QTextBrowser()
text_browser.setStyleSheet(
"background-color: #3C3C3C; color: white; border: 1px solid #555;"
)
summary_text = ""
if updated_games:
summary_text += "Games updated successfully:\n"
for dll_path, launcher, dll_type in updated_games:
game_name = extract_game_name(dll_path, launcher)
summary_text += f" - {game_name} - {launcher} ({dll_type})\n"
else:
summary_text += "No games were updated.\n"
if successful_backups:
summary_text += "\nSuccessful backups:\n"
for dll_path, backup_path in successful_backups:
game_name = extract_game_name(dll_path, "Unknown")
dll_type = DLL_TYPE_MAP.get(
Path(dll_path).name.lower(), "Unknown DLL type"
)
summary_text += f" - {game_name}: {backup_path} ({dll_type})\n"
else:
summary_text += "\nNo backups were created.\n"
if skipped_games:
summary_text += "\nGames skipped:\n"
for dll_path, launcher, reason, dll_type in skipped_games:
game_name = extract_game_name(dll_path, launcher)
summary_text += (
f" - {game_name} - {launcher} ({dll_type}) (Reason: {reason})\n"
)
text_browser.setPlainText(summary_text)
layout.addWidget(text_browser)
# Add close button
button_box = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok)
button_box.accepted.connect(dialog.accept)
layout.addWidget(button_box)
# Simple fade-in effect with opacity
dialog.setWindowOpacity(0.0)
dialog.show()
# Keep original animation timing for stability
for i in range(11):
opacity = i / 10.0
QTimer.singleShot(i * 25, lambda op=opacity: dialog.setWindowOpacity(op))
dialog.exec()
def apply_dark_theme(self):
"""Apply a dark theme using stylesheets."""
dark_stylesheet = """
QMainWindow, QDialog {
background-color: #2E2E2E; /* Dark background */
color: #FFFFFF; /* White text */
}
QPushButton {
background-color: #4D4D4D; /* Button background */
color: #FFFFFF; /* Button text color */
border: 1px solid #7F7F7F; /* Button border */
padding: 5px;
border-radius: 4px;
}
QPushButton:hover {
background-color: #5A5A5A; /* Button hover effect */
}
QPushButton:pressed {
background-color: #444444; /* Button press effect */
}
QPushButton:disabled {
background-color: #3D3D3D; /* Disabled button */
color: #888888;
}
QTextBrowser {
background-color: #3C3C3C; /* Text browser background */
color: #FFFFFF; /* Text color */
border: 1px solid #7F7F7F; /* Text browser border */
}
QMenu {
background-color: #3C3C3C;
color: #FFFFFF;
border: 1px solid #7F7F7F;
}
QMenu::item:selected {
background-color: #5A5A5A;
}
QScrollArea {
background-color: #2E2E2E;
border: none;
}
QLabel {
color: #FFFFFF;
background-color: transparent;
border: none;
}
QCheckBox {
color: #FFFFFF;
background-color: transparent;
border: none;
}
QListWidget {
background-color: #3C3C3C;
color: #FFFFFF;
border: 1px solid #7F7F7F;
}
QListWidget::item {
background-color: #3C3C3C;
}
QListWidget::item:hover {
background-color: #444444;
}
QFrame {
background-color: transparent;
border: none;
}
QSplitter::handle {
background-color: #5A5A5A;
}
QMessageBox {
background-color: #2E2E2E;
color: #FFFFFF;
}
QMessageBox QLabel {
color: #FFFFFF;
background-color: transparent;
border: none;
}
QDialogButtonBox {
background-color: transparent;
border: none;
}
QScrollBar:vertical {
border: none;
background: #3C3C3C;
width: 10px;
margin: 0px;
}
QScrollBar::handle:vertical {
background: #5A5A5A;
min-height: 20px;
border-radius: 5px;
}
QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {
height: 0px;
}
QProgressBar {
border: 1px solid #5A5A5A;
border-radius: 5px;
background-color: #3C3C3C;
text-align: center;
color: white;
}
QProgressBar::chunk {
background-color: #2D6E88;
width: 10px;
margin: 0.5px;
}
"""
self.setStyleSheet(dark_stylesheet)
# Apply consistent styling to all launcher buttons
button_style = """
QPushButton {
background-color: #4D4D4D;
color: white;
border: 1px solid #7F7F7F;
border-radius: 4px;
padding: 8px 16px;
text-align: left;
margin: 2px 0px;
}
QPushButton:hover {
background-color: #5A5A5A;
border-color: #999999;
}
QPushButton:pressed {
background-color: #444444;
}
QPushButton:disabled {
background-color: #3D3D3D;
color: #888888;
}
"""
for button in self.button_list[:7]: # Apply to launcher buttons
button.setStyleSheet(button_style)
# Apply special styling to custom folder buttons
custom_button_style = """
QPushButton {
background-color: #2D6E88;
color: white;
border: 1px solid #7F7F7F;
border-radius: 4px;
padding: 8px 16px;
text-align: left;
margin: 2px 0px;
}
QPushButton:hover {
background-color: #367FA3;
border-color: #999999;
}
QPushButton:pressed {
background-color: #245D73;
}
QPushButton:disabled {
background-color: #1D3D5A;
color: #888888;
}
"""
for button in self.button_list[7:]: # Apply to custom folder buttons
button.setStyleSheet(custom_button_style)
self.start_update_button.setStyleSheet(
"""
QPushButton {
background-color: #2D5A88;
color: white;
border: 1px solid #7F7F7F;
border-radius: 4px;
padding: 8px 16px;
text-align: left;
font-weight: bold;
margin: 2px 0px;
}
QPushButton:hover {
background-color: #366BA3;
border-color: #999999;
}
QPushButton:pressed {
background-color: #244B73;
}
QPushButton:disabled {
background-color: #1D3D5A;
color: #888888;
}
"""
)
import os
from pathlib import Path
from .config import LauncherPathName, config_manager
from .whitelist import is_whitelisted
from dlss_updater.logger import setup_logger
import sys
logger = setup_logger()
def get_steam_install_path():
try:
if config_manager.check_path_value(LauncherPathName.STEAM):
path = config_manager.check_path_value(LauncherPathName.STEAM)
# Remove \steamapps\common if it exists
path = path.replace("\\steamapps\\common", "")
logger.debug(f"Using configured Steam path: {path}")
return path
import winreg
key = winreg.OpenKey(
winreg.HKEY_LOCAL_MACHINE, r"SOFTWARE\WOW6432Node\Valve\Steam"
)
value, _ = winreg.QueryValueEx(key, "InstallPath")
config_manager.update_launcher_path(LauncherPathName.STEAM, str(value))
return value
except (FileNotFoundError, ImportError) as e:
logger.debug(f"Could not find Steam install path: {e}")
return None
def get_steam_libraries(steam_path):
logger.debug(f"Looking for Steam libraries in: {steam_path}")
library_folders_path = Path(steam_path) / "steamapps" / "libraryfolders.vdf"
logger.debug(f"Checking libraryfolders.vdf at: {library_folders_path}")
if not library_folders_path.exists():
default_path = Path(steam_path) / "steamapps" / "common"
logger.debug(
f"libraryfolders.vdf not found, using default path: {default_path}"
)
return [default_path]
libraries = []
with library_folders_path.open("r", encoding="utf-8") as file:
lines = file.readlines()
for line in lines:
if "path" in line:
path = line.split('"')[3]
library_path = Path(path) / "steamapps" / "common"
logger.debug(f"Found Steam library: {library_path}")
libraries.append(library_path)
return libraries
def find_dlss_dlls(library_paths, launcher_name):
"""
Find DLSS and XeSS DLLs in the given list of library paths.
"""
dll_names = [
"nvngx_dlss.dll",
"nvngx_dlssg.dll",
"nvngx_dlssd.dll",
"libxess.dll",
"libxess_dx11.dll",
]
dll_paths = []
logger.debug(f"Searching for DLLs in {launcher_name}")
for library_path in library_paths:
logger.debug(f"Scanning directory: {library_path}")
try:
for root, _, files in os.walk(library_path):
for dll_name in dll_names:
if dll_name.lower() in [f.lower() for f in files]:
dll_path = os.path.join(root, dll_name)
logger.debug(f"Found DLL: {dll_path}")
if not is_whitelisted(dll_path):
logger.info(
f"Found non-whitelisted DLL in {launcher_name}: {dll_path}"
)
dll_paths.append(dll_path)
else:
logger.info(
f"Skipped whitelisted game in {launcher_name}: {dll_path}"
)
except Exception as e:
logger.error(f"Error scanning {library_path}: {e}")
return dll_paths
def get_ea_games():
"""
Find EA game directories and DLLs within the given path.
"""
ea_path = config_manager.check_path_value(LauncherPathName.EA)
ea_games_paths = []
ea_dll_paths = []
if ea_path and ea_path != "":
for root, dirs, files in os.walk(ea_path):
for file in files:
if file.lower() in [
"nvngx_dlss.dll",
"nvngx_dlssg.dll",
"nvngx_dlssd.dll",
"libxess.dll",
"libxess_dx11.dll",
]:
dll_path = os.path.join(root, file)
ea_dll_paths.append(dll_path)
logger.debug(f"Found EA DLL: {dll_path}")
for dir in dirs:
if dir.lower() == "ea games":
games_path = os.path.join(root, dir)
ea_games_paths.append(games_path)
logger.debug(f"Found EA games directory: {games_path}")
return ea_games_paths, ea_dll_paths
def get_ubisoft_install_path():
try:
if config_manager.check_path_value(LauncherPathName.UBISOFT):
return config_manager.check_path_value(LauncherPathName.UBISOFT)
import winreg
key = winreg.OpenKey(
winreg.HKEY_LOCAL_MACHINE, r"SOFTWARE\WOW6432Node\Ubisoft\Launcher"
)
value, _ = winreg.QueryValueEx(key, "InstallDir")
config_manager.update_launcher_path(LauncherPathName.UBISOFT, str(value))
logger.debug(f"Ubisoft install path: {value}")
return value
except (FileNotFoundError, ImportError):
logger.error("Could not find Ubisoft install path")
return None
def get_ubisoft_games(ubisoft_path):
"""
Find Ubisoft game directories and DLLs within the given path.
"""
ubisoft_games_paths = []
ubisoft_dll_paths = []
for root, dirs, files in os.walk(ubisoft_path):
for file in files:
if file.lower() in [
"nvngx_dlss.dll",
"nvngx_dlssg.dll",
"nvngx_dlssd.dll",
"libxess.dll",
"libxess_dx11.dll",
]:
dll_path = os.path.join(root, file)
ubisoft_dll_paths.append(dll_path)
logger.debug(f"Found Ubisoft DLL: {dll_path}")
for dir in dirs:
if dir.lower() == "games":
games_path = os.path.join(root, dir)
ubisoft_games_paths.append(games_path)
logger.debug(f"Found Ubisoft games directory: {games_path}")
return ubisoft_games_paths, ubisoft_dll_paths
def get_xbox_games():
"""
Find Xbox game directories and DLLs within the given path.
"""
xbox_path = config_manager.check_path_value(LauncherPathName.XBOX)
xbox_games_paths = []
xbox_dll_paths = []
if xbox_path and xbox_path != "":
for root, dirs, files in os.walk(xbox_path):
for file in files:
if file.lower() in [
"nvngx_dlss.dll",
"nvngx_dlssg.dll",
"nvngx_dlssd.dll",
"libxess.dll",
"libxess_dx11.dll",
]:
dll_path = os.path.join(root, file)
xbox_dll_paths.append(dll_path)
logger.debug(f"Found Xbox DLL: {dll_path}")
for dir in dirs:
if dir.lower() == "games":
games_path = os.path.join(root, dir)
xbox_games_paths.append(games_path)
logger.debug(f"Found Xbox games directory: {games_path}")
return xbox_games_paths, xbox_dll_paths
def get_epic_games():
"""
Find Epic Games directories and DLLs within the given path.
"""
epic_path = config_manager.check_path_value(LauncherPathName.EPIC)
epic_games_paths = []
epic_dll_paths = []
if epic_path and epic_path != "":
for root, dirs, files in os.walk(epic_path):
for file in files:
if file.lower() in [
"nvngx_dlss.dll",
"nvngx_dlssg.dll",
"nvngx_dlssd.dll",
"libxess.dll",
"libxess_dx11.dll",
]:
dll_path = os.path.join(root, file)
epic_dll_paths.append(dll_path)
logger.debug(f"Found Epic Games DLL: {dll_path}")
for dir in dirs:
if dir.lower() == "games":
games_path = os.path.join(root, dir)
epic_games_paths.append(games_path)
logger.debug(f"Found Epic Games directory: {games_path}")
return epic_games_paths, epic_dll_paths
def get_gog_games():
"""
Find GOG game directories and DLLs within the given path.
"""
gog_path = config_manager.check_path_value(LauncherPathName.GOG)
gog_games_paths = []
gog_dll_paths = []
if gog_path and gog_path != "":
for root, dirs, files in os.walk(gog_path):
for file in files:
if file.lower() in [
"nvngx_dlss.dll",
"nvngx_dlssg.dll",
"nvngx_dlssd.dll",
"libxess.dll",
"libxess_dx11.dll",
]:
dll_path = os.path.join(root, file)
gog_dll_paths.append(dll_path)
logger.debug(f"Found GOG DLL: {dll_path}")
for dir in dirs:
if dir.lower() == "games":
games_path = os.path.join(root, dir)
gog_games_paths.append(games_path)
logger.debug(f"Found GOG games directory: {games_path}")
return gog_games_paths, gog_dll_paths
def get_battlenet_games(battlenet_path):
"""
Find Battle.net game directories and DLLs within the given path.
"""
battlenet_games_paths = []
battlenet_dll_paths = []
for root, dirs, files in os.walk(battlenet_path):
for file in files:
if file.lower() in [
"nvngx_dlss.dll",
"nvngx_dlssg.dll",
"nvngx_dlssd.dll",
"libxess.dll",
"libxess_dx11.dll",
]:
dll_path = os.path.join(root, file)
battlenet_dll_paths.append(dll_path)
logger.debug(f"Found Battle.net DLL: {dll_path}")
for dir in dirs:
if dir.lower() == "games":
games_path = os.path.join(root, dir)
battlenet_games_paths.append(games_path)
logger.debug(f"Found Battle.net games directory: {games_path}")
return battlenet_games_paths, battlenet_dll_paths
def get_custom_games(path_enum):
"""
Find DLLs in custom game directories.
"""
custom_path = config_manager.check_path_value(path_enum)
custom_dll_paths = []
if custom_path and custom_path != "":
for root, _, files in os.walk(custom_path):
for file in [
f
for f in files
if f.lower()
in [
"nvngx_dlss.dll",
"nvngx_dlssg.dll",
"nvngx_dlssd.dll",
"libxess.dll",
"libxess_dx11.dll",
]
]:
dll_path = os.path.join(root, file)
custom_dll_paths.append(dll_path)
logger.debug(f"Found DLL in custom path: {dll_path}")
return custom_dll_paths
def find_all_dlss_dlls():
logger.info("Starting find_all_dlss_dlls function")
all_dll_paths = {
"Steam": [],
"EA Launcher": [],
"Ubisoft Launcher": [],
"Epic Games Launcher": [],
"GOG Launcher": [],
"Battle.net Launcher": [],
"Xbox Launcher": [],
"Custom Path 1": [],
"Custom Path 2": [],
"Custom Path 3": [],
"Custom Path 4": [],
}
# Steam
steam_path = get_steam_install_path()
if steam_path:
steam_libraries = get_steam_libraries(steam_path)
all_dll_paths["Steam"] = find_dlss_dlls(steam_libraries, "Steam")
# EA
ea_games, ea_dlls = get_ea_games()
if ea_games:
all_dll_paths["EA Launcher"].extend(ea_dlls)
# Ubisoft
ubisoft_path = get_ubisoft_install_path()
if ubisoft_path:
ubisoft_games, ubisoft_dlls = get_ubisoft_games(ubisoft_path)
all_dll_paths["Ubisoft Launcher"].extend(ubisoft_dlls)
# Epic Games
epic_games, epic_dlls = get_epic_games()
if epic_games:
all_dll_paths["Epic Games Launcher"].extend(epic_dlls)
# Xbox
xbox_games, xbox_dlls = get_xbox_games()
if xbox_games:
all_dll_paths["Xbox Launcher"].extend(xbox_dlls)
# GOG
gog_games, gog_dlls = get_gog_games()
if gog_games:
all_dll_paths["GOG Launcher"].extend(gog_dlls)
# Battle.net
battlenet_path = config_manager.check_path_value(LauncherPathName.BATTLENET)
if battlenet_path:
battlenet_games, battlenet_dlls = get_battlenet_games(battlenet_path)
all_dll_paths["Battle.net Launcher"].extend(battlenet_dlls)
# Custom paths
custom_paths = [
(LauncherPathName.CUSTOM1, "Custom Path 1"),
(LauncherPathName.CUSTOM2, "Custom Path 2"),
(LauncherPathName.CUSTOM3, "Custom Path 3"),
(LauncherPathName.CUSTOM4, "Custom Path 4"),
]
for path_enum, key in custom_paths:
custom_dlls = get_custom_games(path_enum)
if custom_dlls:
all_dll_paths[key].extend(custom_dlls)
# Remove duplicates
for launcher in all_dll_paths:
all_dll_paths[launcher] = list(set(all_dll_paths[launcher]))
return all_dll_paths
import os
import shutil
import pefile
from dlss_updater.config import LATEST_DLL_VERSIONS, LATEST_DLL_PATHS
from pathlib import Path
import stat
import time
import psutil
from packaging import version
from .logger import setup_logger
from .constants import DLL_TYPE_MAP
logger = setup_logger()
def parse_version(version_string):
# Replace commas with dots and remove any trailing zeros
cleaned_version = ".".join(version_string.replace(",", ".").split(".")[:3])
return version.parse(cleaned_version)
def get_dll_version(dll_path):
try:
with open(dll_path, "rb") as file:
pe = pefile.PE(data=file.read())
for fileinfo in pe.FileInfo:
for entry in fileinfo:
if hasattr(entry, "StringTable"):
for st in entry.StringTable:
for key, value in st.entries.items():
if key == b"FileVersion":
return value.decode("utf-8").strip()
except Exception as e:
logger.error(f"Error reading version from {dll_path}: {e}")
return None
def remove_read_only(file_path):
if not os.access(file_path, os.W_OK):
logger.info(f"Removing read-only attribute from {file_path}")
os.chmod(file_path, stat.S_IWRITE)
def restore_permissions(file_path, original_permissions):
os.chmod(file_path, original_permissions)
def is_file_in_use(file_path, timeout=5):
start_time = time.time()
while time.time() - start_time < timeout:
try:
with open(file_path, "rb"):
return False
except PermissionError:
for proc in psutil.process_iter(["pid", "name", "open_files"]):
try:
for file in proc.open_files():
if file.path == file_path:
logger.error(
f"File {file_path} is in use by process {proc.name()} (PID: {proc.pid})"
)
return True
except (psutil.NoSuchProcess, psutil.AccessDenied):
pass
time.sleep(0.1)
logger.info(f"Timeout reached while checking if file {file_path} is in use")
return True # Assume file is NOT in use if we can't determine otherwise to prevent hanging conditions
def normalize_path(path):
return os.path.normpath(path)
def create_backup(dll_path):
backup_path = dll_path.with_suffix(".dlsss")
try:
logger.info(f"Attempting to create backup at: {backup_path}")
if backup_path.exists():
logger.info("Previous backup exists, removing...")
try:
os.chmod(backup_path, stat.S_IWRITE)
os.remove(backup_path)
logger.info("Successfully removed old backup")
except Exception as e:
logger.error(f"Failed to remove old backup: {e}")
return None
dir_path = os.path.dirname(backup_path)
os.chmod(dir_path, stat.S_IWRITE | stat.S_IREAD | stat.S_IEXEC)
shutil.copy2(dll_path, backup_path)
if backup_path.exists():
os.chmod(backup_path, stat.S_IWRITE | stat.S_IREAD)
logger.info(f"Successfully created backup at: {backup_path}")
return backup_path
else:
logger.error("Backup file not created")
return None
except Exception as e:
logger.error(f"Failed to create backup for {dll_path}: {e}")
logger.error(f"Error type: {type(e)}")
return None
def update_dll(dll_path, latest_dll_path):
dll_path = Path(normalize_path(dll_path)).resolve()
latest_dll_path = Path(normalize_path(latest_dll_path)).resolve()
logger.info(f"Checking DLL at {dll_path}...")
dll_type = DLL_TYPE_MAP.get(dll_path.name.lower(), "Unknown DLL type")
original_permissions = os.stat(dll_path).st_mode
try:
existing_version = get_dll_version(dll_path)
latest_version = get_dll_version(latest_dll_path)
if existing_version and latest_version:
existing_parsed = parse_version(existing_version)
latest_parsed = parse_version(latest_version)
logger.info(
f"Existing version: {existing_version}, Latest version: {latest_version}"
)
# Do not include FG/RR DLLs in the update check
if dll_type == "nvngx_dlss.dll" and existing_parsed < parse_version("2.0.0"):
logger.info(
f"Skipping update for {dll_path}: Version {existing_version} is less than 2.0.0 and cannot be updated."
)
return False, None, dll_type
if existing_parsed >= latest_parsed:
logger.info(
f"{dll_path} is already up-to-date (version {existing_version})."
)
return False, None, dll_type
if not dll_path.exists():
logger.error(f"Error: Target DLL path does not exist: {dll_path}")
return False, None, dll_type
if not latest_dll_path.exists():
logger.error(f"Error: Latest DLL path does not exist: {latest_dll_path}")
return False, None, dll_type
if not os.access(dll_path.parent, os.W_OK):
logger.error(
f"Error: No write permission to the directory: {dll_path.parent}"
)
return False, None, dll_type
backup_path = create_backup(dll_path)
if not backup_path:
return False, None, dll_type
remove_read_only(dll_path)
retry_count = 3
while retry_count > 0:
if not is_file_in_use(str(dll_path)):
break
logger.info(
f"File is in use. Retrying in 2 seconds... (Attempts left: {retry_count})"
)
time.sleep(2)
retry_count -= 1
if retry_count == 0:
logger.info(
f"File {dll_path} is still in use after multiple attempts. Cannot update."
)
restore_permissions(dll_path, original_permissions)
return False, None, dll_type
try:
os.remove(dll_path)
shutil.copyfile(latest_dll_path, dll_path)
restore_permissions(dll_path, original_permissions)
# Verify update
new_version = get_dll_version(dll_path)
if new_version == latest_version:
logger.info(
f"Successfully updated {dll_path} from version {existing_version} to {latest_version}."
)
return True, backup_path, dll_type
else:
logger.error(
f"Version verification failed - Expected: {latest_version}, Got: {new_version}"
)
return False, backup_path, dll_type
except Exception as e:
logger.error(f"File update operation failed: {e}")
if backup_path and backup_path.exists():
try:
shutil.copyfile(backup_path, dll_path)
logger.info("Restored backup after failed update")
except Exception as restore_error:
logger.error(f"Failed to restore backup: {restore_error}")
return False, backup_path, dll_type
except Exception as e:
logger.error(f"Error updating {dll_path}: {e}")
restore_permissions(dll_path, original_permissions)
return False, None, dll_type
import os
import sys
from pathlib import Path
import ctypes
from dlss_updater.logger import setup_logger
logger = setup_logger()
try:
from dlss_updater import (
update_dll,
is_whitelisted,
__version__,
LATEST_DLL_PATHS,
DLL_TYPE_MAP,
find_all_dlss_dlls,
auto_update,
resource_path,
)
except ImportError as e:
logger.error(f"Error importing dlss_updater modules: {e}")
logger.error("Current sys.path:")
for path in sys.path:
logger.error(path)
logger.error("\nCurrent directory contents:")
for item in os.listdir():
logger.error(item)
logger.error("\ndlss_updater directory contents:")
try:
for item in os.listdir("dlss_updater"):
logger.error(item)
except FileNotFoundError:
logger.error("dlss_updater directory not found")
sys.exit(1)
def find_file_in_directory(directory, filename):
for root, _, files in os.walk(directory):
if filename in files:
return os.path.join(root, filename)
return None
def check_update_completion():
update_log_path = os.path.join(os.path.dirname(sys.executable), "update_log.txt")
if os.path.exists(update_log_path):
with open(update_log_path, "r") as f:
logger.info(f"Update completed: {f.read()}")
os.remove(update_log_path)
def check_update_error():
error_log_path = os.path.join(
os.path.dirname(sys.executable), "update_error_log.txt"
)
if os.path.exists(error_log_path):
with open(error_log_path, "r") as f:
logger.error(f"Update error occurred: {f.read()}")
os.remove(error_log_path)
def check_dependencies():
try:
from importlib.metadata import distributions
required = {"pefile", "psutil"}
installed = set()
for dist in distributions():
name = dist.metadata.get("Name")
if name:
installed.add(name.lower())
missing = required - installed
if missing:
logger.info(f"Missing dependencies: {', '.join(missing)}")
return False
return True
except ImportError:
logger.error("Unable to check dependencies. Proceeding anyway.")
return True
def run_as_admin():
script = Path(sys.argv[0]).resolve()
params = " ".join([str(script)] + sys.argv[1:])
logger.info("Re-running script with admin privileges...")
ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, params, None, 1)
def is_admin():
try:
return ctypes.windll.shell32.IsUserAnAdmin()
except:
return False
def extract_game_name(dll_path, launcher_name):
parts = Path(dll_path).parts
try:
if "steamapps" in parts:
return parts[parts.index("steamapps") + 2]
elif "EA Games" in parts:
return parts[parts.index("EA Games") + 1]
elif "Ubisoft Game Launcher" in parts:
return parts[parts.index("games") + 1]
elif "Epic Games" in parts:
return parts[parts.index("Epic Games") + 2]
elif "GOG Galaxy" in parts:
return parts[parts.index("Games") + 1]
elif "Battle.net" in parts:
return parts[parts.index("Battle.net") + 1]
elif "Custom Path" in launcher_name:
# For custom paths, try to get parent directory name
return parts[-2]
else:
# If we can't determine the game name, use the parent directory name
return parts[-2]
except (ValueError, IndexError) as e:
logger.error(
f"Error extracting game name for {dll_path} in {launcher_name}: {e}"
)
return "Unknown Game"
def update_dlss_versions():
logger.info(f"DLSS Updater version {__version__}")
logger.info("Starting DLL search...")
updated_games = []
skipped_games = []
successful_backups = []
try:
logger.info("Checking for updates...")
if auto_update is None:
logger.info("No updates were found.")
else:
try:
update_available = auto_update()
if update_available:
logger.info(
"The application will now close for the update. If the update does NOT automatically restart, please manually reboot it from the /update/ folder."
)
return True, [], [], [] # Early return for auto-update
except Exception as e:
logger.error(f"Error during update check: {e}")
import traceback
traceback.print_exc()
try:
all_dll_paths = find_all_dlss_dlls()
logger.info("DLL search completed.")
except Exception as e:
logger.error(f"Error finding DLLs: {e}")
return False, [], [], []
processed_dlls = set()
if any(all_dll_paths.values()):
logger.info("\nFound DLLs in the following launchers:")
# Process each launcher
for launcher, dll_paths in all_dll_paths.items():
if dll_paths:
logger.info(f"{launcher}:")
for dll_path in dll_paths:
try:
dll_path = (
Path(dll_path)
if isinstance(dll_path, str)
else dll_path
)
if str(dll_path) not in processed_dlls:
result = process_single_dll(dll_path, launcher)
if result:
success, backup_path, dll_type = result
if success:
logger.info(
f"Successfully processed: {dll_path}"
)
updated_games.append(
(str(dll_path), launcher, dll_type)
)
if backup_path:
successful_backups.append(
(str(dll_path), backup_path)
)
else:
if backup_path: # Attempted but failed
skipped_games.append(
(
str(dll_path),
launcher,
"Update failed",
dll_type,
)
)
else: # Skipped for other reasons
skipped_games.append(
(
str(dll_path),
launcher,
"Skipped",
dll_type,
)
)
processed_dlls.add(str(dll_path))
except Exception as e:
logger.error(f"Error processing DLL {dll_path}: {e}")
continue
# Display summary after processing
if updated_games:
logger.info("\nGames updated successfully:")
for dll_path, launcher, dll_type in updated_games:
game_name = extract_game_name(dll_path, launcher)
logger.info(f" - {game_name} - {launcher} ({dll_type})")
else:
logger.info("\nNo games were updated.")
if successful_backups:
logger.info("\nSuccessful backups:")
for dll_path, backup_path in successful_backups:
game_name = extract_game_name(dll_path, "Unknown")
dll_type = DLL_TYPE_MAP.get(
Path(dll_path).name.lower(), "Unknown DLL type"
)
logger.info(f" - {game_name}: {backup_path} ({dll_type})")
else:
logger.info("\nNo backups were created.")
if skipped_games:
logger.info("\nGames skipped:")
for dll_path, launcher, reason, dll_type in skipped_games:
game_name = extract_game_name(dll_path, launcher)
logger.info(
f" - {game_name} - {launcher} ({dll_type}) (Reason: {reason})"
)
else:
logger.info("No DLLs were found or processed.")
return True, updated_games, skipped_games, successful_backups
except Exception as e:
import traceback
trace = traceback.format_exc()
logger.error(f"Critical error in update process: {e}")
logger.error(f"Traceback:\n{trace}")
return False, [], [], []
def process_single_dll(dll_path, launcher):
"""Process a single DLL file"""
try:
dll_type = DLL_TYPE_MAP.get(dll_path.name.lower(), "Unknown DLL type")
logger.info(f" - {dll_type}: {dll_path}")
game_name = extract_game_name(str(dll_path), launcher)
if "warframe" in game_name.lower():
return None
if is_whitelisted(str(dll_path)):
logger.debug(
f"Game {game_name} is in whitelist, checking if it's on skip list..."
)
return False, None, dll_type
dll_name = dll_path.name.lower()
if dll_name in LATEST_DLL_PATHS:
latest_dll_path = LATEST_DLL_PATHS[dll_name]
return update_dll(str(dll_path), latest_dll_path)
return False, None, dll_type
except Exception as e:
logger.error(f"Error processing DLL {dll_path}: {e}")
return False, None, "Error"
def display_update_summary(updated_games, skipped_games, successful_backups):
"""Display a summary of the update process"""
logger.info("\nSummary:")
if not (updated_games or skipped_games or successful_backups):
logger.info("No DLLs were found or processed.")
return
if updated_games:
logger.info("\nGames updated successfully:")
for dll_path, launcher, dll_type in updated_games:
game_name = extract_game_name(dll_path, launcher)
logger.info(f" - {game_name} - {launcher} ({dll_type})")
else:
logger.info("\nNo games were updated.")
if successful_backups:
logger.info("\nSuccessful backups:")
for dll_path, backup_path in successful_backups:
game_name = extract_game_name(dll_path, "Unknown")
dll_type = DLL_TYPE_MAP.get(Path(dll_path).name.lower(), "Unknown DLL type")
logger.info(f" - {game_name}: {backup_path} ({dll_type})")
else:
logger.info("\nNo backups were created.")
if skipped_games:
logger.info("\nGames skipped:")
for dll_path, launcher, reason, dll_type in skipped_games:
game_name = extract_game_name(dll_path, launcher)
logger.info(f" - {game_name} - {launcher} ({dll_type}) (Reason: {reason})")
__version__ = "2.7.0"
import os
import csv
from io import StringIO
from urllib.request import urlopen
from urllib.error import URLError
from dlss_updater.logger import setup_logger
from dlss_updater.config import config_manager
logger = setup_logger()
WHITELIST_URL = (
"https://raw.githubusercontent.com/Recol/DLSS-Updater-Whitelist/main/whitelist.csv"
)
def fetch_whitelist():
try:
with urlopen(WHITELIST_URL) as response:
csv_data = StringIO(response.read().decode("utf-8"))
reader = csv.reader(csv_data)
return set(row[0].strip() for row in reader if row and row[0].strip())
except URLError as e:
logger.error(f"Failed to fetch whitelist: {e}")
return set()
except csv.Error as e:
logger.error(f"Failed to parse whitelist CSV: {e}")
return set()
WHITELISTED_GAMES = fetch_whitelist()
def is_whitelisted(game_path):
"""Check if a game path matches any whitelisted games"""
logger.debug(f"Checking game against whitelist: {game_path}")
path_parts = game_path.lower().split(os.path.sep)
game_name = (
path_parts[-2] if len(path_parts) > 2 else "Unknown"
) # Usually parent folder is game name
for game in WHITELISTED_GAMES:
game_words = game.lower().split()
if all(word in " ".join(path_parts).lower() for word in game_words):
# Check if this game is in the user's skip list
if config_manager.is_blacklist_skipped(game):
logger.info(
f"Whitelist match found but user has chosen to ignore: {game_name} matches {game}"
)
return False
logger.info(f"Whitelist match found: {game_name} matches {game}")
return True
logger.debug(f"No whitelist match found for: {game_name}")
return False
def get_all_blacklisted_games():
"""Return the list of all blacklisted games for UI display"""
return list(WHITELISTED_GAMES)
from .scanner import get_steam_install_path, get_steam_libraries, find_dlss_dlls, find_all_dlss_dlls
from .updater import update_dll
from .whitelist import is_whitelisted
from .version import __version__
from .config import LATEST_DLL_PATHS, resource_path
from .auto_updater import auto_update
from .logger import setup_logger
from .constants import DLL_TYPE_MAP
from .lib.threading_lib import ThreadManager, WorkerSignals
__all__ = [
"get_steam_install_path",
"get_steam_libraries",
"find_dlss_dlls",
"find_all_dlss_dlls",
"update_dll",
"is_whitelisted",
"__version__",
"LATEST_DLL_PATHS",
"resource_path",
"auto_update",
"setup_logger",
"DLL_TYPE_MAP",
"ThreadManager",
"WorkerSignals",
]
md5: F9E8BCAB719E249BA8BD50414D42143F | sha1: 849FFF0273080413648DC897B59C62B41812BDEA | sha256: 23B3EBC802185F537423965C2EE9C8AD8ADB5D137B004CE027A4088E629B5FD3 | sha512: 38B1C968A1FE73D38455442C7C7817FB25505055BC6D623194C13D831B324881C56A90D7402C7BA0F48408DAB5EBF50343165CDA5AB9A3AA152D0CF61F3CA92F
md5: 155CC423A077A4F8435D3919F0F1DA80 | sha1: 306844C496D8BE443BF0D3C2491D4107868D81F2 | sha256: 9E0BB17D627306D7DF07E16C0E08DEEF226332C6746631B7AD8095AC6F463D79 | sha512: C649059E843FEB96BB25A6E3084BD0199B18004DB88487EA3F56176775441400C17DF5A7431E3BF3B53BFACB2E5B7E607D96767907FA557462DC7A0B0CFB9780
md5: 3A875F45C315D09E5F4548CC9288F178 | sha1: 064709DA803D9A796C2E4AC17B5F904522C4F646 | sha256: 4E85CDBE0896AAB5F12C29F0B66AE7E5D9622CFADAC37AA9852ACBA8E4861F5A | sha512: 9628969F053F9DA54D84422BF8E0915EDC7129379AA49B4D12969628E7599BE256D1FE3EDD18AAE32C7A837C82DEE30E8C28EDD8C8246B240F76CF2103A12980
md5: E5311D6C46C2920E2D5347C029F60CB7 | sha1: 651657641ED5D419B382C191626B7EFDF2B7EC75 | sha256: 395DD725BDD34BC7BF9CCE7AE5A4D379A67D8EF436219BCA4C6F1355D033336A | sha512: C3E6778D4451084BAA11A87F678F1A729ADE8358876A3B27CABC20A8C7E8979C12955D588734F1845B0677FEFD8B7FAEEEE64DBAA96037D1F89384A5411C20CF
md5: 96CB06FF5A84E4E90591973BF99A7D45 | sha1: F770313A4C25328B06C3223E934B12C5CB323F68 | sha256: 3D17996B4C4F669F0FE27C9672E9FA71A39E997E09AC020C077EDEC682570CE8 | sha512: 0A86425A4C1BE59539BFD15101A0C197886C3E9EC94AA697A212405668802B7FFB23F2AB25D8E649251998698094BFFBCF392BF157FFD8485DBA00E9AE645295
md5: 123AD0908C76CCBA4789C084F7A6B8D0 | sha1: 86DE58289C8200ED8C1FC51D5F00E38E32C1AAD5 | sha256: 4E5D5D20D6D31E72AB341C81E97B89E514326C4C861B48638243BDF0918CFA43 | sha512: 80FAE0533BA9A2F5FA7806E86F0DB8B6AAB32620DDE33B70A3596938B529F3822856DE75BDDB1B06721F8556EC139D784BC0BB9C8DA0D391DF2C20A80D33CB04
md5: 0F8E4992CA92BAAF54CC0B43AACCCE21 | sha1: C7300975DF267B1D6ADCBAC0AC93FD7B1AB49BD2 | sha256: EFF52743773EB550FCC6CE3EFC37C85724502233B6B002A35496D828BD7B280A | sha512: 6E1B223462DC124279BFCA74FD2C66FE18B368FFBCA540C84E82E0F5BCBEA0E10CC243975574FA95ACE437B9D8B03A446ED5EE0C9B1B094147CEFAF704DFE978
md5: 4FF168AAA6A1D68E7957175C8513F3A2 | sha1: 782F886709FEBC8C7CEBCEC4D92C66C4D5DBCF57 | sha256: 2E4D35B681A172D3298CAF7DC670451BE7A8BA27C26446EFC67470742497A950 | sha512: C372B759B8C7817F2CBB78ECCC5A42FA80BDD8D549965BD925A97C3EEBDCE0335FBFEC3995430064DEAD0F4DB68EBB0134EB686A0BE195630C49F84B468113E3
ordlookup
pefile
peutils
psutil
md5: D30149D319EFCAECF0A5C5E71EF6CB39 | sha1: 99BEEB17BFC69E8370036F9457EDB4D6812B22E2 | sha256: 9C7FC855D9D1614E70705C7DCC6F4AC3CDCAB5ADFEB6A67D382F5ADE09EADC15 | sha512: B6FB265F0EFED56FDD3455ED620E1FB581D40D2B23B92544CCCBF331E30DC29592C4297E3FAAF437A9D1A33099E0B48D5B2344943FB7B581A448F6C5806ACEC6
md5: 0351DC34C06A7E74E977C142A8784DA8 | sha1: 1096BC9B3AE3A57DC7F684D53191DF5365889164 | sha256: B93E6083EB06137CC9191DAC0D9CF4483E47192113D3AC2228B4549F737BAC85 | sha512: 92CAEE00CC0588D30659D4B0BDE38BF229BEAB0FC07D9AAC362B84814B6EA541C39C03ABA936124CBFD5D60C219D01CB09EBA8005DD2236774503094CBDC609B
md5: 01B946A2EDC5CC166DE018DBB754B69C | sha1: DBE09B7B9AB2D1A61EF63395111D2EB9B04F0A46 | sha256: 88F55D86B50B0A7E55E71AD2D8F7552146BA26E927230DAF2E26AD3A971973C5 | sha512: 65DC3F32FAF30E62DFDECB72775DF870AF4C3A32A0BF576ED1AAAE4B16AC6897B62B19E01DC2BF46F46FBE3F475C061F79CBE987EDA583FEE1817070779860E5
md5: 0FE6D52EB94C848FE258DC0EC9FF4C11 | sha1: 95CC74C64AB80785F3893D61A73B8A958D24DA29 | sha256: 446C48C1224C289BD3080087FE15D6759416D64F4136ADDF30086ABD5415D83F | sha512: C39A134210E314627B0F2072F4FFC9B2CE060D44D3365D11D8C1FE908B3B9403EBDD6F33E67D556BD052338D0ED3D5F16B54D628E8290FD3A155F55D36019A86
md5: 9002E0BEE6455B2322E3E717FE25F9BE | sha1: BC8DF83CC657F0F46A0BFF20565870A435ED1563 | sha256: 24B47C966B6E4A65B3E4DF866D347D3427E9BD709BE550C38224427EB5E143D3 | sha512: 28DDD087B48D5AA96EC39CCC29A4020CF75AE3C5CB6AF9A9571694D73F7AAA4FECB15336C9C7A7D12C93D8BF12EFA4FE4D8D612CD93D72C72130CAE52317D0D9
md5: 83BBECF92FB68795A620B395998B131B | sha1: 026F9E87A5623FE9370C2EEDEF24C765F7312800 | sha256: B04DE4541863BC7D8879040A78889C4849C1B1DA2784C4630F734C146C2998CE | sha512: C63CA8863F63C8F415D685EFF991A1AA67E3457AC2B1F6524DB271C2986F7E79415F98F212E1F7CDD644F41BC48D558661E1136716D63F81675F664E53FDFC70
md5: C9523863236A0125880654AC4440E16C | sha1: 0D5032EB2B0BEAC130EEC57337629A7BA941D6B2 | sha256: B95236241CE0B825857064A386B9A22B0F28FA24F3E246AD63528E87C2688B94 | sha512: E926C35109E6FCCA001367F0880658996A9252C5456487E8ECF766EA21934219E8FF22F26A54FC1882EAB39875228BD254E877EE5189DEA56101E4100FE91577
md5: 1487F1A29360F602863D829C5302854A | sha1: 73488033A373BB145DDA52D200FB46312A344EC8 | sha256: 8E81C9FAFD4711667CE81824DD9D54E128C562557A1AD5B247EAFB85E64A14E0 | sha512: D4BC15288CE37C3F94D44B8730909FAE5D3CDF0963A630B7A1AF183273A4ADB2337F931D1EE68C24F43CD1353FA9E25561B180843AC5F29DABAB580A571461E7
md5: 0A637D5E4D04462F5851DB72DACAFAB1 | sha1: 5AE85F101F1D603FA00E16CB4F24FEC6F6969237 | sha256: F1A184C8FFB602A1550BE4CA0BA06CD97551920B5E802B4FFA74587BF3A550BB | sha512: D86D45F2F6BD40F3C927A2FFBD8F6744F176ED7507C84C81DF4939E0A458831B9EACE737B54D5A268416887E74C130F110689858950240555B9CDA3899720BD6
md5: 0AF3578D91EEFDEFEEBBC89E9456F3A4 | sha1: E45CA7646A123D5FBB890357A375DC89F2AA4D5B | sha256: 1CC89C072C34CD28F8612EFD7A3BF866900B74585405FE590539D5894C7C3401 | sha512: 9AC7B9B5D55E4F511EFCA34C70108E6BFD86099A52528ECC3CC83973EF0AC2DDF3C095D18D66B0EC46A6C3C9B554C9D083CC50B2DF573E36F90E287E4523062C
md5: 5642F7BAEF071E135D7D009A75D472C5 | sha1: 41FFEA0200298425F6120777B39A985276DCC668 | sha256: ACAAA68BF7DC518B4DAA17D92A4E7AD4FCE3B0A8EF7E6E4D2C587C56D44B1DA4 | sha512: 99DFE44F68C51E35465915CBCDD2692B5D5645FA6C39C0BFEECEB2F2C3CEED690BE0311E5DFAE3CC49C5C27D7618ED229B0EAB7302BA8BC190BC7C4421DCB219
md5: 971DBBE854FC6AB78C095607DFAD7B5C | sha1: 1731FB947CD85F9017A95FDA1DC5E3B0F6B42CA2 | sha256: 5E197A086B6A7711BAA09AFE4EA7C68F0E777B2FF33F1DF25A21F375B7D9693A | sha512: B966AAB9C0D9459FADA3E5E96998292D6874A7078924EA2C171F0A1A50B0784C24CC408D00852BEC48D6A01E67E41D017684631176D3E90151EC692161F1814D
md5: 6BC084255A5E9EB8DF2BCD75B4CD0777 | sha1: CF071AD4E512CD934028F005CABE06384A3954B6 | sha256: 1F0F5F2CE671E0F68CF96176721DF0E5E6F527C8CA9CFA98AA875B5A3816D460 | sha512: B822538494D13BDA947655AF791FED4DAA811F20C4B63A45246C8F3BEFA3EC37FF1AA79246C89174FE35D76FFB636FA228AFA4BDA0BD6D2C41D01228B151FD89
md5: FED07A9B7E62FCC655F510169F03F3CD | sha1: A917D6D573D0B8BE802F82C6091960DA983C87E9 | sha256: 34D08A36CC2F2AD6128DD19FDE16EC54166C5C44DF023A79879C5DC841894729 | sha512: F582370497893C9FAFB421B67B6865E3D5B1D280FB6934629E37386117A5FC1CD020CAD7573E31D5A50D2BBE566C3C9D13DFFEBD36EE396E9BF3923C67F194C9
md5: FB8ABEFE7FC7C583A142A4921A889D32 | sha1: 1C605D8BD84CD5FFD18B24E943476A254BFF0668 | sha256: BF0B233139ECF92C7BC1F2327F91E4C7936E873CD458994EB23E8B05DF69AAB4 | sha512: 80CCE310DAAC448EB6FE091FA990600B9523AA65DA027A4884A4A92887B578B1C6B2DFA34C45A90B1ADB465EB1374249831E71E9702151227DB009CA8EC0D63C
md5: 848F16245B325FE16214D5A1F1B62C74 | sha1: DD64CE996AE05EA55D3BD8EA01B76A56CA8A5844 | sha256: 68E8A89B3E717674A25C1FC0E6E36C5DA6CDD69D87E00996726F06B2BB90088C | sha512: 408CE093B85C8C4D56ED769903EDED058C1EB3A8282C96FBBC56BF500AF3BD0D949C2C709FB5526BAAFBB08594307C9F0CE3B9A4EE1BB4C3A20B0826252CDFC3
md5: AFB4933B85924615485FE8CD56C697F6 | sha1: 9F02603CC6A237925790D25F4986CCFCDEAD2C4E | sha256: 0DAC8FE7C7781EFF6414D0B9C6DCB951AB24DF25A35A667B8280FCBEEF9F3B82 | sha512: FAF4EECE905155D82BE1A63B57FAE36BCE5475C89DD73427C75139FA0986A3A317B954455B696F086926FD4982F802671E011E1AB5EA3589C9F91684ACF7F51F
md5: 45C73D3F132424AAFF08BAA431D957DF | sha1: 448B889B4FDD95701D19F87AFEA0BCCCDE217811 | sha256: E1F90F7E760083C40014B72F5E1731B044EC9F881EC240C5B96469F9468EE729 | sha512: F5CB4DC640F0810FECF1C36313DEF60519331B518D36FB5D1097E38A5F8B61222D44785DB7A9EA6B244935864796678995BE139D020AB6D3D7616104C47242ED
md5: 26A61892F91351BC42C8F23937B60006 | sha1: 07EA4037D6303C4FF1FDBD522C159D0317383DCA | sha256: D047B52D2EA32F46DCD45D782C3D63E0F192BB1B3E262718A0DE12DF943F3420 | sha512: AA680A09FD2389B939069A8105AABD102EEE306C683BAD4F9BF8EF4032AE8D4B51226CD325B8648752D1691F2BF4D7139159295077F6CCD469A972C4B4BE7D1E
md5: 0F03D80F018A3FF5212C3DBE86DE41D0 | sha1: DEB4A2618B249DEF9E7ECE9A4B4573A5BE09859C | sha256: 245516C0CFB12D28638C68E0E297E653BC69A3CABB94748DC3FA1AEA67593C7C | sha512: 524B3C01641E7D36B2C2D96A517D65C8C0E0C98D59E8301D0110500956526D27BB88C3677E1121EA80D942114C687B7D048FF28786AA230AFB11B626A182BEA5
md5: 8FF515FE132D355FCCB4693D163B596B | sha1: 21FD63913E633FE3685C30D74DFA37ADAC7ADDD1 | sha256: 0FC3F0BC1EE332197EF2FF8F37ADA1D78A3FB298A46A75344B54035CFA1058E8 | sha512: ED6049EB75116DDC880E9444F132511947055BBDFBBC3DAB17048B6E618529CA8BBE61F7B13B50E773898AA5508DF7225841B414903052BD244811F98A39EB55
md5: 8130AA72D7918FEC0454488637EEF942 | sha1: E0985D25443CE3C0D66D062EFABA659A67A6E4AA | sha256: A781C0FB9AED52BA8EB38D68687818EA0763277F510CFB68D0DCD5EA6BF4E507 | sha512: FDE0261ABFC46D99220AA4F794357A2595BA63978051FAF66C926DDCF0253782D8EC13EF4568C7C8D6E2E30C3671B664AC0D99A9C7431ABF5C380C4DE0F81C67
md5: 31BD978BE967359C1BEFA2171C5051AE | sha1: 9759D00F0D68D06FAF3E62264A6A364374CDE7AD | sha256: 830E97751D3D19FD7CBDCB9BA83F9FD72CED43421609D7B4EDF05207BDC294DC | sha512: F223360DFA28B9333FDE0E93D6DA3E715B674CB6C78813B33AF46B5AD2DDEAEE08747C2CF25F43C8C21B3F575C4ED4F0B194A37AF1EB0B8B00DB982842E9606B
md5: D45BFBD0653B86682C73714D85448414 | sha1: 01810751A1C5F21999EED06EA2D0662B3B0F9CE8 | sha256: 426FEC36A39DB0D2D3EA7DB8D0DF12513CD67A6D511ECFFA91F18572CF968A1B | sha512: 1BA22CE64B178609A30D0845BC41BF5E1135B0D384FE8BCBEDF145BA9BCD67E85B82B2AECB475B1EFBF339811A82FA54D776E5E9BB5E009040AF9536E34B59F1
md5: DF36480CA906A784F193D2768E18B75A | sha1: 05045744159AAE78526EAEE8C339BE69C2B8E1B6 | sha256: 5555FBAB56D72F68FAF84845ABB573BB481556BC99B5564EC85D1C63702A06B4 | sha512: D0D0287F138F5BABEC1C29B624A92482CE7C94287525E587EC1A6D920081C17FE7C8F661D07927DA1EF9D20602BFE5672C9173555AC837EDF162CC3A8F0C36C9
md5: 7DE6AB9104F0A12D03EE2F91EB756138 | sha1: 57AEFDC331957164E7D6C062938F6F4A7D8DD970 | sha256: 18FF77A19555B45EDECF265D9FCCC6513CDEEFCB2036C4B0E2FDFF1F33B2C0B1 | sha512: 4EF75EBC7BD57DE5966B1FE49766994FA73C910DFDF12F0FA0E761434E8160B0BE85BDCF95017E559FF1A2557ACAB4A7737021D4DAA265780AF2C1E807B4B8F4
md5: 4AF90A0D27DFA67791D97B49FAD90A0D | sha1: 2DA8981BE159DDA321548210329586E42012765F | sha256: D28842EE1C2B420CF85F49649B17F523197B4CECFCDD0893C20671863DC18F1C | sha512: 17B0107DA9FFF3804A2B39A28F36F377BA04DB9BC4DEBC72230226A7276003B5F83C6978C297EFA4CA0BF3C1B845EDC014DE6EE88FE11C92CBEA2C730860D341
md5: 0AE46094796365591C19772ED06F3677 | sha1: FF76CBF41786D05983C9F82CBF22D1E9680DB731 | sha256: 210D8FB2ED1FDBFB4105E2025C3EC649C30D30442E0071D6925E95A2335EEDD4 | sha512: 22A84C00B015C0DC57FE88117A3FFCE541ED419D7E9F6FD38396C3868AAD916293F6FF2F87D288EB35F8B4876F6992A5C0C7032F5CBFB6205C35311E69524C62
md5: 55AD1911C5B8F8D51DB9EF02CB00F342 | sha1: 23835492EE81DB8900D9F784E83B3C8277B8F73F | sha256: 061B45CE229496A7BE8B152200D45248CB6C2F47CB725A684FD718D7FA68A8FB | sha512: 11A04149554769BF5F9EEA50E84CB1F596DCC0C33515A756FDC7449B5407F9C4D0C155AC3E58CD464DA8AAEDD8D4CADD9BD37DCEE3A57F2C3A926505F0B44FE3
md5: 6F0167B4B5F5B63992D97EA939477E14 | sha1: C042FC4F672F1F0F3710F197203AF1B192485116 | sha256: 9DC389DCD7C0AC40879B723E1AA51A39B727B983B3BD5540C9D14FBA6756C9F5 | sha512: 0D63B540B81FBB38E006B5D8F49E12A1CCF21DFC3A4A02B0ABC94A2697D94AB861A6665EA9C62980C7CD5B594FF10069BE215D1DE55DF9FDBC5BFE49AF2680CD
md5: F892F5D1E2D1AFA44E61A3C4AB67979C | sha1: DDA553060122E93369B7012D1E870C558679CA58 | sha256: AEF7DBAFCF00C437EBBB9B03A0B67D27334BA98889854172A96C24AC545A28BE | sha512: C5DBA15198E1F82056EA60773A88DBBC723379F266C506564901969D82C3E7296BA384BD0C7E7F3D98FF3A0F750F36FE872A52309E821AB353BDD459BB49D6B7
md5: 19216D581E072FCD6E38B8D0F54F2564 | sha1: B39AC076F5F117192ACBC9848B18C22D353746FB | sha256: 9D217CBB2F2C059850852D466C77D3C641078F9CFBE2C544588B23C2E3561F26 | sha512: 410BB4762E82919AE25716E0773CC15A5873FCD53091484A2FFC4CDE0F1AE7DE51942647F9E579D6651400B693A4C97D6DB2A2FDB4C6BD31DF5043D2C1217273
md5: 98B008BE9834BFC362B4C2EEF4E8CDB9 | sha1: A4A50CED1329C3986E3C1576F089B25AFF5FFDF2 | sha256: 4F93342B59ADDEDBE45EBD973E6449AB85B11C0AAB6AD7962124E293C5D03638 | sha512: D594FFD7D44D4D862475711973DF87B08FB63A900DDFD87C7771AD27F0CC71E5FBDCE92DA4D4AD5856FE3CFB803257CE0B71CD8DC24CA5C421DDB1B9B44C7881
md5: 501080884BED38CB8801A307C9D7B7B4 | sha1: 881B250CC8F4FA4F75111AC557A4FDE8E1E217AF | sha256: BF68CF819A1E865170430C10E91C18B427AEF88DB1DA1742020443864AA2B749 | sha512: 63D74A4871D1C72C2A79AE8A5D380070F9D2128C16949C3AD36C9862FCC4DAB738137ED3D51CAF0BC46B36655F8BD8A2D425D68200123415EE8D4DE0E1CBEBC9
md5: 7B4BD20267C93E35C49C32AAD05B6B15 | sha1: 860A10D04C8764F540ED34CF08E06F32B7B37611 | sha256: 90BA935A0145EE9AE56267A365CC0088D34FA506B7AFEB2BD1BD78CD33359605 | sha512: 9E05566461D9BE1A234057E1AE9979B6D022189CB49B2C264C9AD253ABEC0F0235919F24159638ACCC45FA3E75AB324DB8EDF737E72DB1EFDA2CFA589531DDFB
Version 2.6.4:
Performance improvements for scanning directories.
Rewrite of scanning subdirectories.
There is now an output of the results in a box.
Removal of the full banner from the GUI.
Version 2.6.5:
Updates the DLSS files to DLSS 4.0.
Use at your own discretion :).
Version 2.6.6:
Bug fix for FG/RR DLL's being included in the skip for <2.0.0.
Version 2.6.7:
Updates to the DLL's to allow for profile K (refined version of profile J) to be the default for DLSS.
Version 2.6.8:
Updates to the RR DLL to version 310.2.1.0.
Updates the frame generation DLL to version 310.2.1.0.
Version 2.7.0:
Added support for XeSS DLLs (libxess.dll and libxess_dx11.dll) - version 2.0.1.41.
Added support for up to 4 custom game folders.
Improved auto-updater to better handle update process and cleanup.
Added ability to skip specific games in the blacklist.
Enhanced UI with separated sections for launcher types.
Fixed bug in game name extraction for better reporting.
Introduction of animations for certain components, should be better for viewability.
md5: 2663E22900AB5791C6687A264473AE1E | sha1: D8DB587B6C632200AE13BE880CC824CDC8390DF9 | sha256: BAEE284995B22D495FD12FA8378077E470978DB1522C61BFB9AF37FB827F33D1 | sha512: 5F29FF4288B9DB33976F5F79B9FD07C4900A560BB41FE98C93A33DA7A36C0981FFD71F460E81E13E4F6A2DEBAFA6D9284BC1A728734752BA5AD5FBD766659E80
importlib_metadata
[console_scripts]
wheel=wheel.cli:main
[distutils.commands]
bdist_wheel=wheel.bdist_wheel:bdist_wheel
MIT License
Copyright (c) 2012 Daniel Holth <[email protected]> and contributors
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
md5: 286B308DF8012A5DFC4276FB16DD9CCC | sha1: 8AE9DF813B281C2BD7A81DE1E4E9CEF8934A9120 | sha256: 2E5FB14B7BF8540278F3614A12F0226E56A7CC9E64B81CBD976C6FCF2F71CBFB | sha512: 24166CC1477CDE129A9AB5B71075A6D935EB6EEBCAE9B39C0A106C5394DED31AF3D93F6DEA147120243F7790D0A0C625A690FD76177DDDAB2D2685105C3EB7B2
md5: C0B4C55CE3711AF914B2015F707E4452 | sha1: F1C1E9F8A461CFEE1199D2100F5C0796733518B6 | sha256: A67EEC238162FDE20AC24CA7DF931792734AAD0611BE22D1B3A71BC15ACF72F3 | sha512: FA6BD9223898EF0C54CA9A67B10207BFCE152EADBAEC4C91D4E951D0790F455066F5095ED739FA2452AEA1420D154BEB00BFA9E6E10B46BED687C5D0D7484900
md5: 862F820C3251E4CA6FC0AC00E4092239 | sha1: EF96D84B253041B090C243594F90938E9A487A9A | sha256: 36585912E5EAF83BA9FEA0631534F690CCDC2D7BA91537166FE53E56C221E153 | sha512: 2F8A0F11BCCC3A8CB99637DEEDA0158240DF0885A230F38BB7F21257C659F05646C6B61E993F87E0877F6BA06B347DDD1FC45D5C44BC4E309EF75ED882B82E4E
md5: 68156F41AE9A04D89BB6625A5CD222D4 | sha1: 3BE29D5C53808186EBA3A024BE377EE6F267C983 | sha256: 82A2F9AE1E6146AE3CB0F4BC5A62B7227E0384209D9B1AEF86BBCC105912F7CD | sha512: F7BF8AD7CD8B450050310952C56F6A20B378A972C822CCC253EF3D7381B56FFB3CA6CE3323BEA9872674ED1C02017F78AB31E9EB9927FC6B3CBA957C247E5D57
md5: 747FC8B90E33F5E9048BCF26788B9169 | sha1: AC30AAE15BEA0514C7730B007B68DD841A7F3DDC | sha256: B1B1BB33AF9CC14749936B1F6BAC36B2FFC494EC1A5FB8B12FC9180A6454F545 | sha512: 51416CDA9A18F113D46C3CB06E7ED85717C64325156BE16C4FC78BDDC7A06F0E845B3FEDD2C1CA6C965517530D9CBB9B9497DD1B309BC16011D2E1499BB5D082
md5: 70DEC3CE00E5CAF45246736B53EA3AD0 | sha1: 3CD7037D211EBF9BD023C248EC6420F193AD7ED2 | sha256: 8CEF0CD8333F88A9F9E52FA0D151B5F661D452EFBCFC507DC28A46259B82596C | sha512: EDDBEB527C01167FB69D9C743495C868073B5CACAE3652D777B6A635C4FEB0344F085BDC2AEB6A775FFEF8056394DDB4DF5CD47E622CCBF974D11C30857FD536
md5: 057325E89B4DB46E6B18A52D1A691CAA | sha1: 8EAB0897D679E223AA0D753F6D3D2119F4D72230 | sha256: 5BA872CAA7FCEE0F4FB81C6E0201CEED9BD92A3624F16828DD316144D292A869 | sha512: 6BC7606869CA871B7EE5F2D43EC52ED295FA5C3A7DF31DBD7E955DDB98C0748AFF58D67F09D82EDCDE9D727E662D1550C6A9CF82F9CB7BE021159D4B410E7CBC
md5: 2185849BC0423F6641EE30804F475478 | sha1: D37CA3E68F4B2111FC0C0CEAD9695D598795C780 | sha256: 199CD8D7DB743C316771EF7BBF414BA9A9CDAE1F974E90DA6103563B2023538D | sha512: BA89DB9F265A546B331482D779AB30131814E42AD3711A837A3450F375D2910BD41B3B3258DB90B29CD5AFCCDC695318FC8AD8CD921A57CE25F69AEA539B26EE
md5: F465C15E7BACEAC920DC58A5FB922C1C | sha1: 3A5A0156F5288F14938494609D377EDE0B67D993 | sha256: F4A486A0CA6A53659159A404614C7E7EDCCB6BFBCDEB844F6CEE544436A826CB | sha512: 22902C1BCCA7F80ED064E1E822C253BC8242B4E15E34A878A623E0A562A11203B45D5FF43904268322A7EF5CEBB8E80E5FE1F1F1BCAA972E219348F84A1DAF5F
md5: EED5E3C565099640C146D512E3CEDD84 | sha1: E427D8AF6A5DC3691B61E815F034F40FD62A6053 | sha256: F7D884C475E5C98006BF7C2ABB6B5ACBD885157FB809ED2EE06D2347AB409BC0 | sha512: B93CC53A09E0B959C62BA35A804C6FCA0AFF821B77D6D72047721FA71E27D644EB98F0102DF4D33A96BF4BED447E3947EBCEDD0C798D50C46E3475D97F57127A
md5: CF4120BAD9A7F77993DD7A95568D83D7 | sha1: AC477C046D14C5306AA09BB65015330701EF0F89 | sha256: 14765E83996FE6D50AEDC11BB41D7C427A3E846A6A6293A4A46F7EA7E3F14148 | sha512: F905F9D203F86A7B1FC81BE3ABA51A82174411878C53FD7A62D17F8E26F5010D195F9371FA7400E2E2DC35FDA0DB0CBE68367FCAF834DD157542E9EE7A9742B6
md5: 3E73BC69EFB418E76D38BE5857A77027 | sha1: 7BEE01096669CAA7BEC81CDC77D6BB2F2346608C | sha256: 6F48E7EBA363CB67F3465A6C91B5872454B44FC30B82710DFA4A4489270CE95C | sha512: B6850E764C8849058488F7051DCABFF096709B002D2F427A49E83455838D62A9D3FC7B65285702DE2B995858ED433E35A0C4DA93C2D5AE34684BF624EB59FA6A
md5: 24AEE7D83525CB43AD02FD3116B28274 | sha1: 68A2870BD5496C959EE7E499F4472D0614FDFD87 | sha256: 3262EC7496D397C0B6BFB2F745516E9E225BD9246F78518852C61D559AA89485 | sha512: 6EF5082E83F9400E8FFDBB2F945B080085FD48C0E89E2283BCEDD193A4E6A9F533F8DA78C643DAD95DB138EC265099110A3A6DC8BC68563DBEF5CA08D5E0D029
md5: 51E4C701E4EFA92A56ADAF5BDC9CF49B | sha1: 1ADBC8B57E5EC0A90B9EC629323833DAEAD8C3B4 | sha256: 9EF177DB14CFA3AA66193078C431A96B6AE70858E9DD774B3D3E3CB6E39D10A3 | sha512: 35B2D4114AA12843CB767B7D7A2C82B00144FE8FEA04B41601B790D8B4026E271148B5186308F461F2ED70D75DF7C0AC56C4E023ED069F4F0F6F23F5EA11A2D1
md5: 59C05030E47BDE800AD937CCB98802D8 | sha1: F7B830029A9371B4E500C1548597BEB8FBC1864F | sha256: E4956834DF819C1758D17C1C42A152306F7C0EA7B457CA24CE2F6466A6CB1CAA | sha512: 4F5E7EF0948155DB6712E1BD7F4F31CB81602B325BA4E6E199F67693913B4BB70BB2C983393646C0AC0D86EF81071907D04BCEB8AB0D506B7C5AC7C389FE692D
md5: 69C4A9A654CF6D1684B73A431949B333 | sha1: 3C8886DAC45BB21A6B11D25893C83A273FF19E0B | sha256: 8DAEFAFF53E6956F5AEA5279A7C71F17D8C63E2B0D54031C3B9E82FCB0FB84DB | sha512: CADCEC9A6688B54B36DBD125210D1A742047167DAD308907A3C4E976B68483A8C6144E02D5CF26F887744DC41AF63B7731551287BB3EF8BD947C38C277783C16
md5: CE19076F6B62292ED66FD06E5BA67BBA | sha1: 231F6236BDBBE95C662E860D46E56E42C4E3FE28 | sha256: 21CA71B2C1766FC68734CB3D1E7C2C0439B86BCFB95E00B367C5FD48C59E617C | sha512: 7357598BC63195C2FD2DDDE0376B3ECF5BD0211A286F4A5C1E72E8C68B6E881E7E617F561E7A859C800FE67BEC8F4C376E7A6943CAB8DACFEDA0056B8E864143
md5: CCF609AE4416F13FCB80A122C4345348 | sha1: BE60263E7CBB2702733A37513D5FB717F6B30216 | sha256: 99E97E0AF615F43150778AAA44D82BC58B70BF595A8412CFAFCC5D38BE38BDFB | sha512: 9DFE0E4AA31E50E5B799CDC86A276C6576FFC44C919657E4230E17C9B739B8E69E0865EED38AB9EC0B07E77090A6F2C03C415E68FA431FDE108D2D92CB3E8987
md5: E3213CF44340D7B4CB65F7231A65E3A4 | sha1: 815E5809A01905ECAA463F6827F657C11B95D243 | sha256: AB87FE4B0CF5B2B17901905EA86367B9756C44845EB463E77435648F0F719354 | sha512: D32B6CB1C5A286B2CE9837051D099FEA98F9E5AD00C15B14CCCE02B4556D74C4B703B1C94A59670599BF6A9BFBF84C7C22DAC25653AF9B455999A5E42CF38B7A
Log in or click on link to see number of positives.
- VCRUNTIME140.dll (5e197a086b6a) - ## / 72
- MSVCP140.dll (88f55d86b50b) - ## / 72
- MSVCP140_1.dll (446c48c1224c) - ## / 72
- VCRUNTIME140_1.dll (1f0f5f2ce671) - ## / 71
- MSVCP140_2.dll (24b47c966b6e) - ## / 72
- libffi-8.dll (eff52743773e) - ## / 72
- opengl32sw.dll (b04de4541863) - ## / 72
- libcrypto-3.dll (4e5d5d20d6d3) - ## / 71
- libssl-3.dll (2e4d35b681a1) - ## / 71
- VCRUNTIME140.dll (36585912e5ea) - ## / 71
- VCRUNTIME140_1.dll (82a2f9ae1e61) - ## / 72
- api-ms-win-core-console-l1-1-0.dll (88ace577a9c5) - ## / 72
- api-ms-win-core-datetime-l1-1-0.dll (1bd81dfd1920) - ## / 72
- api-ms-win-core-debug-l1-1-0.dll (591358eb4d15) - ## / 70
- api-ms-win-core-errorhandling-l1-1-0.dll (d56ce7b1cd76) - ## / 71
- api-ms-win-core-fibers-l1-1-0.dll (91d249d7bc0e) - ## / 72
- api-ms-win-core-fibers-l1-1-1.dll (fc9d86cec621) - ## / 72
- api-ms-win-core-file-l1-1-0.dll (1ca895aba4e7) - ## / 72
- api-ms-win-core-file-l1-2-0.dll (4cadbc0c39da) - ## / 72
- api-ms-win-core-file-l2-1-0.dll (93c624b366ba) - ## / 72
- api-ms-win-core-handle-l1-1-0.dll (39095f59c41d) - ## / 72
- api-ms-win-core-heap-l1-1-0.dll (8f105771b236) - ## / 72
- api-ms-win-core-interlocked-l1-1-0.dll (53b25e753ca7) - ## / 67
- api-ms-win-core-kernel32-legacy-l1-1-1.dll (a9b13a1cd1b8) - ## / 70
- api-ms-win-core-libraryloader-l1-1-0.dll (7f39ba298b41) - ## / 72
- api-ms-win-core-localization-l1-2-0.dll (6ee44dd0d851) - ## / 71
- api-ms-win-core-memory-l1-1-0.dll (0512a35316ec) - ## / 67
- api-ms-win-core-namedpipe-l1-1-0.dll (c05f1fffe3b5) - ## / 72
- api-ms-win-core-processenvironment-l1-1-0.dll (c4eca98c3c67) - ## / 68
- api-ms-win-core-processthreads-l1-1-0.dll (5ccb89e93d67) - ## / 71
- api-ms-win-core-processthreads-l1-1-1.dll (4ad565a8ba3e) - ## / 71
- api-ms-win-core-profile-l1-1-0.dll (7809160932f4) - ## / 70
- api-ms-win-core-rtlsupport-l1-1-0.dll (9dc1e91e71c7) - ## / 70
- api-ms-win-core-string-l1-1-0.dll (60fc31d2a0c6) - ## / 73
- api-ms-win-core-synch-l1-1-0.dll (3a72b4f29f39) - ## / 72
- api-ms-win-core-synch-l1-2-0.dll (efc1e4460984) - ## / 67
- api-ms-win-core-sysinfo-l1-1-0.dll (b203d862ddef) - ## / 72
- api-ms-win-core-sysinfo-l1-2-0.dll (2703635d8353) - ## / 71
- api-ms-win-core-timezone-l1-1-0.dll (33f4fddc1810) - ## / 72
- api-ms-win-core-util-l1-1-0.dll (6eda016742a6) - ## / 72
- api-ms-win-crt-conio-l1-1-0.dll (b762061b688a) - ## / 72
- api-ms-win-crt-convert-l1-1-0.dll (ebb2ae5535a6) - ## / 71
- api-ms-win-crt-environment-l1-1-0.dll (17d63275d00b) - ## / 71
- api-ms-win-crt-filesystem-l1-1-0.dll (9a9e2a65a281) - ## / 72
- api-ms-win-crt-heap-l1-1-0.dll (bab9ac3ec83e) - ## / 72
- api-ms-win-crt-locale-l1-1-0.dll (bd14c67ea28e) - ## / 72
- api-ms-win-crt-math-l1-1-0.dll (606d66d82db5) - ## / 72
- api-ms-win-crt-process-l1-1-0.dll (f6156b102038) - ## / 71
- api-ms-win-crt-runtime-l1-1-0.dll (c5902934d026) - ## / 73
- api-ms-win-crt-stdio-l1-1-0.dll (a9c5a153d8c0) - ## / 72
- api-ms-win-crt-string-l1-1-0.dll (25b8f83a7767) - ## / 72
- api-ms-win-crt-time-l1-1-0.dll (7c7f6393f06d) - ## / 71
- api-ms-win-crt-utility-l1-1-0.dll (d8ba5f17b9ff) - ## / 70
- ucrtbase.dll (2e5fb14b7bf8) - ## / 72
- nvngx_dlss.dll (4e85cdbe0896) - ## / 72
- python3.dll (4f93342b59ad) - ## / 72
- python313.dll (bf68cf819a1e) - ## / 72
- pywintypes313.dll (90ba935a0145) - ## / 70
- nvngx_dlssd.dll (395dd725bdd3) - ## / 70
- nvngx_dlssg.dll (3d17996b4c4f) - ## / 72
- dlss-updater.2.7.1.nupkg (cbc486233739) - ## / 66
- DLSS_Updater.exe (82667a0fd705) - ## / 72
- base_library.zip (f028d8098b6d) - ## / 64
- libxess.dll (23b3ebc80218) - ## / 57
- libxess_dx11.dll (9e0bb17d6273) - ## / 72
- Qt6Core.dll (b95236241ce0) - ## / 72
- Qt6Gui.dll (8e81c9fafd47) - ## / 72
- Qt6Network.dll (f1a184c8ffb6) - ## / 72
- Qt6Pdf.dll (1cc89c072c34) - ## / 72
- Qt6Svg.dll (acaaa68bf7dc) - ## / 72
- Qt6Widgets.dll (e7ca7a709520) - ## / 72
- qtuiotouchplugin.dll (34d08a36cc2f) - ## / 72
- qsvgicon.dll (bf0b233139ec) - ## / 72
- qgif.dll (68e8a89b3e71) - ## / 72
- qicns.dll (0dac8fe7c778) - ## / 72
- qico.dll (e1f90f7e7600) - ## / 72
- qjpeg.dll (d047b52d2ea3) - ## / 72
- qpdf.dll (245516c0cfb1) - ## / 72
- qsvg.dll (0fc3f0bc1ee3) - ## / 72
- qtga.dll (a781c0fb9aed) - ## / 72
- qtiff.dll (830e97751d3d) - ## / 72
- qwbmp.dll (426fec36a39d) - ## / 72
- qwebp.dll (5555fbab56d7) - ## / 72
- qminimal.dll (18ff77a19555) - ## / 72
- qoffscreen.dll (d28842ee1c2b) - ## / 72
- qwindows.dll (210d8fb2ed1f) - ## / 72
- qmodernwindowsstyle.dll (061b45ce2294) - ## / 72
In cases where actual malware is found, the packages are subject to removal. Software sometimes has false positives. Moderators do not necessarily validate the safety of the underlying software, only that a package retrieves software from the official distribution point and/or validate embedded software against official distribution point (where distribution rights allow redistribution).
Chocolatey Pro provides runtime protection from possible malware.
Add to Builder | Version | Downloads | Last Updated | Status |
---|---|---|---|---|
DLSS Updater 2.7.3 | 16 | Thursday, May 8, 2025 | Approved | |
DLSS Updater 2.7.2 | 65 | Wednesday, April 23, 2025 | Approved | |
DLSS Updater 2.7.1 | 57 | Tuesday, April 8, 2025 | Approved | |
DLSS Updater 2.7.0 | 37 | Saturday, April 5, 2025 | Approved | |
DLSS Updater 2.6.8 | 52 | Thursday, March 27, 2025 | Approved | |
DLSS Updater 2.6.7 | 107 | Saturday, February 1, 2025 | Approved | |
DLSS Updater 2.6.6 | 13 | Thursday, January 30, 2025 | Approved | |
DLSS Updater 2.6.5 | 19 | Saturday, January 25, 2025 | Approved | |
DLSS Updater 2.6.4 | 151 | Monday, January 13, 2025 | Approved | |
DLSS Updater 2.6.3 | 19 | Thursday, January 9, 2025 | Approved | |
DLSS Updater 2.6.2 | 50 | Friday, January 3, 2025 | Approved | |
DLSS Updater 2.6.1 | 34 | Saturday, December 28, 2024 | Approved | |
DLSS Updater 2.5.2 | 57 | Thursday, December 19, 2024 | Approved | |
DLSS Updater 2.5.1 | 49 | Saturday, December 14, 2024 | Approved | |
DLSS Updater 2.2.6 | 49 | Thursday, November 14, 2024 | Approved | |
DLSS Updater 2.2.3 | 171 | Thursday, September 26, 2024 | Approved |
-
- chocolatey-core.extension (≥ 1.1.0)
Ground Rules:
- This discussion is only about DLSS Updater and the DLSS Updater package. If you have feedback for Chocolatey, please contact the Google Group.
- This discussion will carry over multiple versions. If you have a comment about a particular version, please note that in your comments.
- The maintainers of this Chocolatey Package will be notified about new comments that are posted to this Disqus thread, however, it is NOT a guarantee that you will get a response. If you do not hear back from the maintainers after posting a message below, please follow up by using the link on the left side of this page or follow this link to contact maintainers. If you still hear nothing back, please follow the package triage process.
- Tell us what you love about the package or DLSS Updater, or tell us what needs improvement.
- Share your experiences with the package, or extra configuration or gotchas that you've found.
- If you use a url, the comment will be flagged for moderation until you've been whitelisted. Disqus moderated comments are approved on a weekly schedule if not sooner. It could take between 1-5 days for your comment to show up.