Compare commits
503 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c2d47a9e52 | ||
|
|
9bee63241e | ||
|
|
91ff8060ca | ||
|
|
a96771685c | ||
|
|
e61c0ebf34 | ||
|
|
56843f07d2 | ||
|
|
e681aceb29 | ||
|
|
7891ed19e1 | ||
|
|
95c2d8efc4 | ||
|
|
3155054531 | ||
|
|
04e8498edb | ||
|
|
69ca8dbf97 | ||
|
|
b479f50005 | ||
|
|
2cb21bea17 | ||
|
|
63c421ae10 | ||
|
|
9fadf3a627 | ||
|
|
ba7c17513a | ||
|
|
8d227944cf | ||
|
|
a13d689b8c | ||
|
|
5f0b9781c6 | ||
|
|
02a36e997e | ||
|
|
9374b9d382 | ||
|
|
6ed3e71536 | ||
|
|
bbdf0955db | ||
|
|
a8cd68fce6 | ||
|
|
55a65a54dc | ||
|
|
90c1c10253 | ||
|
|
377a0fcb98 | ||
|
|
3f70e4ef91 | ||
|
|
17434ecaad | ||
|
|
a2daed0739 | ||
|
|
2406e0ddc8 | ||
|
|
5c6094c2f1 | ||
|
|
d3bddc0e54 | ||
|
|
8578063b0a | ||
|
|
27be876501 | ||
|
|
ab2721716a | ||
|
|
f26c591572 | ||
|
|
4d70fa086d | ||
|
|
f75a8efd8d | ||
|
|
10487caa30 | ||
|
|
6bf96a5f3a | ||
|
|
1a8b19ce43 | ||
|
|
6e2f0a0872 | ||
|
|
6d8b66cf52 | ||
|
|
8e2385448b | ||
|
|
d6de8b34a1 | ||
|
|
ad6a3b70aa | ||
|
|
782492857f | ||
|
|
f7c293084c | ||
|
|
90fdca1d1e | ||
|
|
b9b09778ad | ||
|
|
7f2833c4ac | ||
|
|
984f5a66d3 | ||
|
|
85af1a555f | ||
|
|
3ece7f30b4 | ||
|
|
5fffc3d48a | ||
|
|
133191685f | ||
|
|
b6013321ba | ||
|
|
fe46619a57 | ||
|
|
e7f4d6c0b3 | ||
|
|
3df1b699c5 | ||
|
|
ec409a5c93 | ||
|
|
c5bde3cde6 | ||
|
|
cb3a220254 | ||
|
|
e8e741a908 | ||
|
|
0f06a45b9a | ||
|
|
dbf573c8ec | ||
|
|
d1ff298cfa | ||
|
|
7fb7c57367 | ||
|
|
b6566e15fa | ||
|
|
6583904bd9 | ||
|
|
a34d2e4ad9 | ||
|
|
c65f47f1d7 | ||
|
|
262864fcd4 | ||
|
|
930576e28c | ||
|
|
352f604b81 | ||
|
|
a668731b46 | ||
|
|
2e2fe928b3 | ||
|
|
589381ef6c | ||
|
|
d1f0fe7116 | ||
|
|
0a91fcfccf | ||
|
|
a55f16e50d | ||
|
|
6a747f8ac1 | ||
|
|
f557af562f | ||
|
|
e84bae7d22 | ||
|
|
7ba581a2cd | ||
|
|
dbefebc7d8 | ||
|
|
010e1b89c5 | ||
|
|
4f6014a2b4 | ||
|
|
9d4197b237 | ||
|
|
8e481132f3 | ||
|
|
8347005fc4 | ||
|
|
8bc3c5aef2 | ||
|
|
5d41b1e94a | ||
|
|
32153bb2b1 | ||
|
|
3813678469 | ||
|
|
da8b528856 | ||
|
|
209847bf97 | ||
|
|
ddb110764e | ||
|
|
827247f4f7 | ||
|
|
ccd5803ec1 | ||
|
|
3ebf2f920e | ||
|
|
ab96a2c78a | ||
|
|
f0f7766005 | ||
|
|
12f0e8f37b | ||
|
|
58e87b0b6b | ||
|
|
4ada0a3838 | ||
|
|
c8ac9985bb | ||
|
|
07c65e816c | ||
|
|
e60fa3b9ce | ||
|
|
903bdb7a8f | ||
|
|
bde5c534d4 | ||
|
|
434baee9c0 | ||
|
|
1a0ba73dc1 | ||
|
|
13930669c5 | ||
|
|
0c9d18eeb1 | ||
|
|
c43e92211e | ||
|
|
0b2d69b118 | ||
|
|
983c4c7604 | ||
|
|
9b95a6df53 | ||
|
|
c4bb1907bf | ||
|
|
79067b7092 | ||
|
|
f4cd650f65 | ||
|
|
9b2a7d0860 | ||
|
|
d47bfacad7 | ||
|
|
a576c22543 | ||
|
|
a8aa53f6b4 | ||
|
|
ac0e4b9a7e | ||
|
|
6065338b49 | ||
|
|
a70d8148a0 | ||
|
|
7f5775c616 | ||
|
|
5c05075d36 | ||
|
|
01a68e3163 | ||
|
|
753ccd77be | ||
|
|
917d72cbe6 | ||
|
|
b7f3f70bce | ||
|
|
d0c00ff79d | ||
|
|
da494356f2 | ||
|
|
c8719a7421 | ||
|
|
1ad022a1fb | ||
|
|
e9b01c1658 | ||
|
|
3674fcf2e9 | ||
|
|
35994122b9 | ||
|
|
2323bee305 | ||
|
|
d48f99eb31 | ||
|
|
1576c680ca | ||
|
|
0b46d66ad0 | ||
|
|
f5605aa6a5 | ||
|
|
369bd805c9 | ||
|
|
61f1852db1 | ||
|
|
1a2b91b78a | ||
|
|
5b27e4a2c9 | ||
|
|
882f4aa252 | ||
|
|
e7a4964400 | ||
|
|
62e487abfe | ||
|
|
1f604460a0 | ||
|
|
8a53d80502 | ||
|
|
efcf98d0f8 | ||
|
|
b5ecd66d8e | ||
|
|
e1bacd5b77 | ||
|
|
179d1f7f05 | ||
|
|
e57f90a700 | ||
|
|
8a75924652 | ||
|
|
cbbcce6997 | ||
|
|
cbad0edc81 | ||
|
|
67513abc01 | ||
|
|
f93d53f574 | ||
|
|
32b014a283 | ||
|
|
50de5b676a | ||
|
|
799dea18be | ||
|
|
45e14398f3 | ||
|
|
120dbb257c | ||
|
|
98a8e5f853 | ||
|
|
912ec9927c | ||
|
|
00d80a603c | ||
|
|
6c0fc19381 | ||
|
|
8147b3611a | ||
|
|
8f7f231000 | ||
|
|
6360e4f553 | ||
|
|
dba37f3563 | ||
|
|
42ff7dcae3 | ||
|
|
d8878896e7 | ||
|
|
1723bc6af0 | ||
|
|
274ece410a | ||
|
|
fce3c6cff3 | ||
|
|
a613d96f79 | ||
|
|
8686d3145a | ||
|
|
c2ca657450 | ||
|
|
8c6ba4dc76 | ||
|
|
0bb41b8f90 | ||
|
|
eeacf5885d | ||
|
|
0bb67ad0fb | ||
|
|
e87bc27904 | ||
|
|
4de82acadf | ||
|
|
67921755d3 | ||
|
|
4600cb0715 | ||
|
|
63647166b0 | ||
|
|
24b4789f16 | ||
|
|
8e60860365 | ||
|
|
efe997e73d | ||
|
|
2f93efc1a5 | ||
|
|
cdcc7ff250 | ||
|
|
bd0b4c0857 | ||
|
|
1ae267e610 | ||
|
|
d2bf485db5 | ||
|
|
9b436a81af | ||
|
|
48ed904863 | ||
|
|
11a63612e4 | ||
|
|
e94bafe100 | ||
|
|
ff55b24bbb | ||
|
|
de11c7bfda | ||
|
|
e33e2b85f1 | ||
|
|
ff89270976 | ||
|
|
c63cb94e28 | ||
|
|
fad16516f0 | ||
|
|
d20f94c449 | ||
|
|
1d7fd54c6a | ||
|
|
06dd83f99e | ||
|
|
0914a832d7 | ||
|
|
2de0ca8130 | ||
|
|
db75c7f623 | ||
|
|
219803f01b | ||
|
|
c8a9468e3a | ||
|
|
27da4d7534 | ||
|
|
c8717de5f7 | ||
|
|
0508be5171 | ||
|
|
199472e4af | ||
|
|
37b86711a7 | ||
|
|
92c3ca0f9d | ||
|
|
42d2ca108a | ||
|
|
481f9165e4 | ||
|
|
e75e10ba5e | ||
|
|
0c8cf2d15b | ||
|
|
dbc5db4e98 | ||
|
|
c7eb98263a | ||
|
|
dc5630d3af | ||
|
|
e76c0b7e0f | ||
|
|
cc7b4b9578 | ||
|
|
f7f3a6b254 | ||
|
|
326fbd1d73 | ||
|
|
12344805f3 | ||
|
|
0eee1f8c16 | ||
|
|
64dc7a79b8 | ||
|
|
f9e5bf4953 | ||
|
|
c8d215bf37 | ||
|
|
c945f90f36 | ||
|
|
2943fe62fe | ||
|
|
17e31901a2 | ||
|
|
b26e95a992 | ||
|
|
fcbf340a95 | ||
|
|
7aa60d53d9 | ||
|
|
01b9f1eda6 | ||
|
|
b2dcd5279f | ||
|
|
c655207c04 | ||
|
|
84dde67a4d | ||
|
|
6b603a004d | ||
|
|
1d1f66bb7c | ||
|
|
b8fc38b4f9 | ||
|
|
8b7253d9bf | ||
|
|
702a4530ed | ||
|
|
f0e378fde2 | ||
|
|
f64655eee2 | ||
|
|
ab8c23c6db | ||
|
|
36c42910a2 | ||
|
|
0f0442cf61 | ||
|
|
15eecac4e3 | ||
|
|
72febb5c8b | ||
|
|
f8ad3b3566 | ||
|
|
8891e550c5 | ||
|
|
47888668ae | ||
|
|
dc6a7d966c | ||
|
|
4f2b7d6cb5 | ||
|
|
c4085fe6ea | ||
|
|
0073974d25 | ||
|
|
a9cf7aa338 | ||
|
|
b66abf916a | ||
|
|
e4b287d2b2 | ||
|
|
5e39208999 | ||
|
|
b47d1ac1d9 | ||
|
|
3564bf7202 | ||
|
|
1a974f40d9 | ||
|
|
e7e5223132 | ||
|
|
d8e20aac62 | ||
|
|
18915bf7a4 | ||
|
|
9ccdf3e0cb | ||
|
|
a08402fb0e | ||
|
|
95d58b1bfa | ||
|
|
f17386d7c8 | ||
|
|
74cbc0fee9 | ||
|
|
866163b81c | ||
|
|
1682539aba | ||
|
|
4215b3b233 | ||
|
|
3e4faa5492 | ||
|
|
cacd77c246 | ||
|
|
9d5d09c252 | ||
|
|
72efb71ca4 | ||
|
|
be34cc746e | ||
|
|
7f669a44d4 | ||
|
|
e2020d7aa3 | ||
|
|
3f185153e9 | ||
|
|
28ea79276e | ||
|
|
b1bcac83f5 | ||
|
|
96fec37650 | ||
|
|
a976d807c1 | ||
|
|
0e56ec477f | ||
|
|
6eed37278d | ||
|
|
11e0316d50 | ||
|
|
f59b350acf | ||
|
|
e82d943cbd | ||
|
|
66a1b6f01a | ||
|
|
9a66fc9921 | ||
|
|
627630aec2 | ||
|
|
afb722f730 | ||
|
|
328e0527ab | ||
|
|
423a93afae | ||
|
|
7bedaadcdd | ||
|
|
e41f6b7958 | ||
|
|
d79f34f6d7 | ||
|
|
f1609d29aa | ||
|
|
f710ed3cce | ||
|
|
b3f3ea5ef7 | ||
|
|
66b1777be7 | ||
|
|
d0b3889a83 | ||
|
|
038de51557 | ||
|
|
76c1d3c4c5 | ||
|
|
0b8b6cb9ee | ||
|
|
98235b41c9 | ||
|
|
beeec6d3bf | ||
|
|
05b315e30d | ||
|
|
1906722c02 | ||
|
|
523418d882 | ||
|
|
93e2d4dd3e | ||
|
|
a4b08e7470 | ||
|
|
4e44cef80c | ||
|
|
cd817a8b8f | ||
|
|
11b122d5b7 | ||
|
|
a73053278a | ||
|
|
0ef69fa1e9 | ||
|
|
c84e86ca54 | ||
|
|
69a1cc9edc | ||
|
|
deb9f0b53a | ||
|
|
e711c7b6c6 | ||
|
|
2531f88fad | ||
|
|
3c932e69ab | ||
|
|
325d2a2772 | ||
|
|
f3155fb174 | ||
|
|
52b8aad68b | ||
|
|
4cbaa0b01a | ||
|
|
d310dfd238 | ||
|
|
4558e4bc25 | ||
|
|
37c074780e | ||
|
|
82ea62b6a9 | ||
|
|
7abe8aeab6 | ||
|
|
c1c63152fd | ||
|
|
c1715b10f4 | ||
|
|
07c22285ec | ||
|
|
e224c69931 | ||
|
|
33b83f6841 | ||
|
|
7cf1c9ad8c | ||
|
|
6ec2964267 | ||
|
|
b578bb16a6 | ||
|
|
d60ba5695d | ||
|
|
47f0f43fcc | ||
|
|
f85f65b40c | ||
|
|
a56803c4d4 | ||
|
|
6ad1d3652a | ||
|
|
445177d211 | ||
|
|
d1ced1cc53 | ||
|
|
dab989b79f | ||
|
|
6325f509b7 | ||
|
|
7b5897f357 | ||
|
|
812457033c | ||
|
|
5036309a84 | ||
|
|
53281fa643 | ||
|
|
89733df9b5 | ||
|
|
99f4b5a32b | ||
|
|
54fb0fedf5 | ||
|
|
8daacd3148 | ||
|
|
fa9427ea97 | ||
|
|
dedbb14a6a | ||
|
|
687bb2400b | ||
|
|
7d2a35738d | ||
|
|
d885150dee | ||
|
|
697acac76d | ||
|
|
4ab86662bb | ||
|
|
ff919b29b0 | ||
|
|
e552562861 | ||
|
|
490e9c81c3 | ||
|
|
b0139b499e | ||
|
|
2d409f98f2 | ||
|
|
f62c5e03bb | ||
|
|
f96581e87a | ||
|
|
4fb43dac1a | ||
|
|
f3b33eb5af | ||
|
|
35b8c476db | ||
|
|
d7260d8976 | ||
|
|
a7ce59fcd4 | ||
|
|
cc781b33d2 | ||
|
|
54a5027f8a | ||
|
|
1053c7b114 | ||
|
|
510d80c58a | ||
|
|
894197e75e | ||
|
|
eca48e2495 | ||
|
|
2bd8dc7c41 | ||
|
|
b8eb915788 | ||
|
|
5c0fe6fc9a | ||
|
|
14ecff0d12 | ||
|
|
9135dbd788 | ||
|
|
39511b2cbe | ||
|
|
55db579d88 | ||
|
|
8d31196e4d | ||
|
|
49d0378530 | ||
|
|
67f78a5242 | ||
|
|
ed71768d36 | ||
|
|
baf017efbd | ||
|
|
567d143cf5 | ||
|
|
496ad5724b | ||
|
|
76997c1c88 | ||
|
|
6d42c8c886 | ||
|
|
4c687b3b33 | ||
|
|
53c7eb9a35 | ||
|
|
35ceb8ad48 | ||
|
|
d9067650b4 | ||
|
|
1a3e6d7f04 | ||
|
|
33a88f172c | ||
|
|
087e40a705 | ||
|
|
36cadc541a | ||
|
|
b6852432dd | ||
|
|
7f6060dc19 | ||
|
|
c4f0177b4a | ||
|
|
157a2ecce9 | ||
|
|
e0f99bd1af | ||
|
|
638fef954c | ||
|
|
cd45a53aee | ||
|
|
1413a34c69 | ||
|
|
0c6a4353a5 | ||
|
|
b811201363 | ||
|
|
11ab85dbf8 | ||
|
|
4759a418c6 | ||
|
|
f77477b52e | ||
|
|
c4df5da15e | ||
|
|
ca066bcb3c | ||
|
|
7ed72f79ac | ||
|
|
7228e4bfbf | ||
|
|
99c97dbfb1 | ||
|
|
2803eeee08 | ||
|
|
c336639baf | ||
|
|
2dee6b66dc | ||
|
|
7d925ef800 | ||
|
|
f91247c0f0 | ||
|
|
ad52750cdf | ||
|
|
882f0cbf21 | ||
|
|
6813a268fd | ||
|
|
f379eb2e46 | ||
|
|
2cec3f077c | ||
|
|
a385a82773 | ||
|
|
7a6458ce76 | ||
|
|
4eb07780f2 | ||
|
|
acba5bf774 | ||
|
|
a6115ef016 | ||
|
|
878f98bfef | ||
|
|
f5b6adbdc2 | ||
|
|
465111fbdc | ||
|
|
3cdb8fe138 | ||
|
|
bcde2397cd | ||
|
|
fab7360488 | ||
|
|
c6eba0b51b | ||
|
|
80bdef06c9 | ||
|
|
7bb99845a5 | ||
|
|
cfc599f753 | ||
|
|
5c5e6d5b7e | ||
|
|
5347341414 | ||
|
|
ae83e0eea9 | ||
|
|
0262e82f3b | ||
|
|
4c3e0273d4 | ||
|
|
d5d8e4112e | ||
|
|
d0904e711c | ||
|
|
dd5dc86522 | ||
|
|
70c949db02 | ||
|
|
1a6117c674 | ||
|
|
cbab3919b8 | ||
|
|
72520a9da0 | ||
|
|
39026d1562 | ||
|
|
b6e70d5157 | ||
|
|
bfdf917c6f | ||
|
|
a2144e4126 | ||
|
|
1ab8b6220b | ||
|
|
8de8c7f9f8 | ||
|
|
33d575b334 | ||
|
|
13bb1d57b9 | ||
|
|
6c980628f1 | ||
|
|
ff3c7d2cff | ||
|
|
dc5f38d39e | ||
|
|
65793aa6dc | ||
|
|
1cb7a4f667 | ||
|
|
7d143bbff7 | ||
|
|
f1bec83c03 | ||
|
|
3b187dc717 | ||
|
|
54def97c38 | ||
|
|
6296d62089 | ||
|
|
36f7432330 | ||
|
|
4d4d17df75 |
1
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
custom: https://paypal.me/onderkin
|
||||
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,38 +1,20 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
about: Report a bug in this project
|
||||
title: ''
|
||||
labels: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
**Describe the bug**
|
||||
A clear and concise description of the bug.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
**Is your bug report related to another bug report? Please describe.**
|
||||
If yes, the issue number and an explanation of why the bug is related to this one.
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Smartphone (please complete the following information):**
|
||||
- Device: [e.g. iPhone6]
|
||||
- OS: [e.g. iOS8.1]
|
||||
- Browser [e.g. stock browser, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the bug report here.
|
||||
|
||||
23
.github/ISSUE_TEMPLATE/download_problem.md
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
---
|
||||
name: Download problem
|
||||
about: Report a difficulty in downloading a game
|
||||
title: ''
|
||||
labels: question
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the game you are trying to download**
|
||||
A clear and concise description of the bug.
|
||||
**App ID:** <app id>
|
||||
**Depot ID:** <depot id>
|
||||
**Manifest ID:** <manifest id>
|
||||
|
||||
**Show the error that DepotDownloader produces**
|
||||
A screenshot or a copy-paste wrapped in a code-block (` ``` `) of the DepotDownloader terminal output
|
||||
|
||||
**Do you own the game?**
|
||||
If no, you probably can't download the game. You must own the game on Steam to be able to download it.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the issue here.
|
||||
10
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -2,19 +2,19 @@
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
labels: feature
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
|
||||
25
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
ignore:
|
||||
- dependency-name: "*"
|
||||
update-types: ["version-update:semver-major"]
|
||||
groups:
|
||||
npm-deps:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
- package-ecosystem: "cargo"
|
||||
directory: "src-tauri/"
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
ignore:
|
||||
- dependency-name: "*"
|
||||
update-types: ["version-update:semver-major"]
|
||||
groups:
|
||||
cargo-deps:
|
||||
patterns:
|
||||
- "*"
|
||||
99
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
name: 'build'
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build-tauri:
|
||||
permissions:
|
||||
contents: write
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- platform: 'macos-latest'
|
||||
arch: 'aarch64'
|
||||
args: '--target aarch64-apple-darwin --bundles dmg'
|
||||
- platform: 'macos-latest'
|
||||
arch: 'x86_64'
|
||||
args: '--target x86_64-apple-darwin --bundles dmg'
|
||||
- platform: 'ubuntu-22.04'
|
||||
args: '--bundles appimage'
|
||||
- platform: 'windows-latest'
|
||||
args: ''
|
||||
|
||||
runs-on: ${{ matrix.platform }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: install dependencies (ubuntu only)
|
||||
if: matrix.platform == 'ubuntu-22.04'
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf
|
||||
|
||||
- name: setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
|
||||
- name: setup node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: lts/*
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: install Rust stable
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: ${{ matrix.platform == 'macos-latest' && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }}
|
||||
|
||||
- name: Rust cache
|
||||
uses: swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: './src-tauri -> target'
|
||||
|
||||
- name: install frontend dependencies
|
||||
# If you don't have `beforeBuildCommand` configured you may want to build your frontend here too.
|
||||
run: pnpm install # change this to npm or pnpm depending on which one you use.
|
||||
|
||||
- uses: tauri-apps/tauri-action@v0
|
||||
id: build
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
args: ${{ matrix.args }}
|
||||
includeUpdaterJson: false
|
||||
|
||||
- name: fix JSON
|
||||
if: matrix.platform != 'windows-latest'
|
||||
id: truncate_paths
|
||||
run: echo "paths=$(echo '${{ steps.build.outputs.artifactPaths }}' | sed 's/^..//' | sed 's/..$//')" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: upload macos artifacts (M1)
|
||||
if: matrix.platform == 'macos-latest' && matrix.arch == 'aarch64'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: macos-m1-artifacts
|
||||
path: ${{ steps.truncate_paths.outputs.paths }}
|
||||
|
||||
- name: upload macos artifacts (Intel)
|
||||
if: matrix.platform == 'macos-latest' && matrix.arch == 'x86_64'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: macos-intel-artifacts
|
||||
path: ${{ steps.truncate_paths.outputs.paths }}
|
||||
|
||||
- name: upload linux artifacts
|
||||
if: matrix.platform == 'ubuntu-22.04'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: linux-artifacts
|
||||
path: ${{ steps.truncate_paths.outputs.paths }}
|
||||
|
||||
- name: upload windows artifacts
|
||||
if: matrix.platform == 'windows-latest'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: windows-artifacts
|
||||
path: "./src-tauri/target/release/bundle/*" # fck windows
|
||||
|
||||
|
||||
67
.github/workflows/codeql-analysis.yml
vendored
@@ -1,67 +0,0 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ master ]
|
||||
schedule:
|
||||
- cron: '22 23 * * 5'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'csharp' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
|
||||
# Learn more:
|
||||
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
386
.gitignore
vendored
@@ -1,362 +1,32 @@
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||
|
||||
# User-specific files
|
||||
*.rsuser
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Mono auto generated files
|
||||
mono_crash.*
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
[Ww][Ii][Nn]32/
|
||||
[Aa][Rr][Mm]/
|
||||
[Aa][Rr][Mm]64/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
[Ll]ogs/
|
||||
|
||||
# Visual Studio 2015/2017 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# Visual Studio 2017 auto generated files
|
||||
Generated\ Files/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUnit
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
nunit-*.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# Benchmark Results
|
||||
BenchmarkDotNet.Artifacts/
|
||||
|
||||
# .NET Core
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
|
||||
# ASP.NET Scaffolding
|
||||
ScaffoldingReadMe.txt
|
||||
|
||||
# StyleCop
|
||||
StyleCopReport.xml
|
||||
|
||||
# Files built by Visual Studio
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_h.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.iobj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.ipdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*_wpftmp.csproj
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
*.iml
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# Visual Studio Trace Files
|
||||
*.e2e
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# AxoCover is a Code Coverage Tool
|
||||
.axoCover/*
|
||||
!.axoCover/settings.json
|
||||
|
||||
# Coverlet is a free, cross platform Code Coverage Tool
|
||||
coverage*.json
|
||||
coverage*.xml
|
||||
coverage*.info
|
||||
|
||||
# Visual Studio code coverage results
|
||||
*.coverage
|
||||
*.coveragexml
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# NuGet Symbol Packages
|
||||
*.snupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/[Pp]ackages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/[Pp]ackages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/[Pp]ackages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignorable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
*.appx
|
||||
*.appxbundle
|
||||
*.appxupload
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!?*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
orleans.codegen.cs
|
||||
|
||||
# Including strong name files can present a security risk
|
||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||
#*.snk
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
ServiceFabricBackup/
|
||||
*.rptproj.bak
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
*.ndf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
*.rptproj.rsuser
|
||||
*- [Bb]ackup.rdl
|
||||
*- [Bb]ackup ([0-9]).rdl
|
||||
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||
*.vbw
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# CodeRush personal settings
|
||||
.cr/personal
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Cake - Uncomment if you are using it
|
||||
# tools/**
|
||||
# !tools/packages.config
|
||||
|
||||
# Tabs Studio
|
||||
*.tss
|
||||
|
||||
# Telerik's JustMock configuration file
|
||||
*.jmconfig
|
||||
|
||||
# BizTalk build output
|
||||
*.btp.cs
|
||||
*.btm.cs
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
|
||||
# OpenCover UI analysis results
|
||||
OpenCover/
|
||||
|
||||
# Azure Stream Analytics local run output
|
||||
ASALocalRun/
|
||||
|
||||
# MSBuild Binary and Structured Log
|
||||
*.binlog
|
||||
|
||||
# NVidia Nsight GPU debugger configuration file
|
||||
*.nvuser
|
||||
|
||||
# MFractors (Xamarin productivity tool) working folder
|
||||
.mfractor/
|
||||
|
||||
# Local History for Visual Studio
|
||||
.localhistory/
|
||||
|
||||
# BeatPulse healthcheck temp database
|
||||
healthchecksdb
|
||||
|
||||
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||
MigrationBackup/
|
||||
|
||||
# Ionide (cross platform F# VS Code tools) working folder
|
||||
.ionide/
|
||||
|
||||
# Fody - auto-generated XML schema
|
||||
FodyWeavers.xsd
|
||||
# SteamDepotDownloaderGUI files
|
||||
src-tauri/depotdownloader
|
||||
src-tauri/*.zip
|
||||
src-tauri/*.exe
|
||||
**/DepotDownloader
|
||||
**/DepotDownloader.xml
|
||||
3
.gitmodules
vendored
@@ -1,3 +0,0 @@
|
||||
[submodule "DepotDownloader"]
|
||||
path = DepotDownloader
|
||||
url = https://github.com/SteamRE/DepotDownloader
|
||||
6
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"tauri-apps.tauri-vscode",
|
||||
"rust-lang.rust-analyzer"
|
||||
]
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
|
||||
</startup>
|
||||
</configuration>
|
||||
@@ -3,126 +3,192 @@
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
|
||||
and learning from the experience
|
||||
|
||||
* Focusing on what is best not just for us as individuals, but for the
|
||||
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or
|
||||
|
||||
advances of any kind
|
||||
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
|
||||
* Public or private harassment
|
||||
|
||||
* Publishing others' private information, such as a physical or email
|
||||
|
||||
address, without their explicit permission
|
||||
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
|
||||
an individual is officially representing the community in public spaces.
|
||||
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
|
||||
posting via an official social media account, or acting as an appointed
|
||||
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
|
||||
reported to the community leaders responsible for enforcement at
|
||||
the repository's webpage.
|
||||
|
||||
.
|
||||
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series
|
||||
|
||||
of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
|
||||
permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
|
||||
communication with the community for a specified period of time. No public or
|
||||
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
|
||||
the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
version 2.1, available at
|
||||
|
||||
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
|
||||
|
||||
Community Impact Guidelines were inspired by
|
||||
|
||||
[Mozilla's code of conduct enforcement ladder][mozilla coc].
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
|
||||
[https://www.contributor-covenant.org/faq][faq]. Translations are available at
|
||||
|
||||
[https://www.contributor-covenant.org/translations][translations].
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
||||
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
||||
|
||||
[mozilla coc]: https://github.com/mozilla/diversity
|
||||
|
||||
[faq]: https://www.contributor-covenant.org/faq
|
||||
|
||||
[translations]: https://www.contributor-covenant.org/translations
|
||||
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{9FE2AF44-2D88-43DB-9B8F-EA410552D8CE}</ProjectGuid>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<RootNamespace>DepotDownloaderGUI</RootNamespace>
|
||||
<AssemblyName>DepotDownloaderGUI</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<Deterministic>true</Deterministic>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.DirectoryServices" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Deployment" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Form1.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Form1.Designer.cs">
|
||||
<DependentUpon>Form1.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<EmbeddedResource Include="Form1.resx">
|
||||
<DependentUpon>Form1.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
<SubType>Designer</SubType>
|
||||
</EmbeddedResource>
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
<DesignTime>True</DesignTime>
|
||||
</Compile>
|
||||
<None Include="Properties\Settings.settings">
|
||||
<Generator>SettingsSingleFileGenerator</Generator>
|
||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||
</None>
|
||||
<Compile Include="Properties\Settings.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
</Compile>
|
||||
<EmbeddedResource Include="Resources\Poppins-Medium.ttf" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
||||
@@ -1,25 +0,0 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.30907.101
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DepotDownloaderGUI", "DepotDownloaderGUI.csproj", "{9FE2AF44-2D88-43DB-9B8F-EA410552D8CE}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{9FE2AF44-2D88-43DB-9B8F-EA410552D8CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9FE2AF44-2D88-43DB-9B8F-EA410552D8CE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9FE2AF44-2D88-43DB-9B8F-EA410552D8CE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9FE2AF44-2D88-43DB-9B8F-EA410552D8CE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {B49312C0-32E2-42A5-9DC8-892A707C0EF8}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
302
Form1.Designer.cs
generated
@@ -1,302 +0,0 @@
|
||||
|
||||
namespace DepotDownloaderGUI
|
||||
{
|
||||
partial class Form1
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.components = new System.ComponentModel.Container();
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1));
|
||||
this.label1 = new System.Windows.Forms.Label();
|
||||
this.label2 = new System.Windows.Forms.Label();
|
||||
this.label3 = new System.Windows.Forms.Label();
|
||||
this.label4 = new System.Windows.Forms.Label();
|
||||
this.label5 = new System.Windows.Forms.Label();
|
||||
this.label6 = new System.Windows.Forms.Label();
|
||||
this.label7 = new System.Windows.Forms.Label();
|
||||
this.label8 = new System.Windows.Forms.Label();
|
||||
this.textBox1 = new System.Windows.Forms.TextBox();
|
||||
this.textBox2 = new System.Windows.Forms.TextBox();
|
||||
this.textBox3 = new System.Windows.Forms.TextBox();
|
||||
this.textBox4 = new System.Windows.Forms.TextBox();
|
||||
this.textBox5 = new System.Windows.Forms.TextBox();
|
||||
this.textBox8 = new System.Windows.Forms.TextBox();
|
||||
this.button1 = new System.Windows.Forms.Button();
|
||||
this.numericUpDown1 = new System.Windows.Forms.NumericUpDown();
|
||||
this.numericUpDown2 = new System.Windows.Forms.NumericUpDown();
|
||||
this.button2 = new System.Windows.Forms.Button();
|
||||
this.button3 = new System.Windows.Forms.Button();
|
||||
this.button4 = new System.Windows.Forms.Button();
|
||||
this.label9 = new System.Windows.Forms.Label();
|
||||
this.label10 = new System.Windows.Forms.Label();
|
||||
this.toolTip1 = new System.Windows.Forms.ToolTip(this.components);
|
||||
((System.ComponentModel.ISupportInitialize)(this.numericUpDown1)).BeginInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.numericUpDown2)).BeginInit();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// label1
|
||||
//
|
||||
resources.ApplyResources(this.label1, "label1");
|
||||
this.label1.Name = "label1";
|
||||
this.toolTip1.SetToolTip(this.label1, resources.GetString("label1.ToolTip"));
|
||||
//
|
||||
// label2
|
||||
//
|
||||
resources.ApplyResources(this.label2, "label2");
|
||||
this.label2.Name = "label2";
|
||||
this.toolTip1.SetToolTip(this.label2, resources.GetString("label2.ToolTip"));
|
||||
//
|
||||
// label3
|
||||
//
|
||||
resources.ApplyResources(this.label3, "label3");
|
||||
this.label3.Name = "label3";
|
||||
this.toolTip1.SetToolTip(this.label3, resources.GetString("label3.ToolTip"));
|
||||
//
|
||||
// label4
|
||||
//
|
||||
resources.ApplyResources(this.label4, "label4");
|
||||
this.label4.Name = "label4";
|
||||
this.toolTip1.SetToolTip(this.label4, resources.GetString("label4.ToolTip"));
|
||||
//
|
||||
// label5
|
||||
//
|
||||
resources.ApplyResources(this.label5, "label5");
|
||||
this.label5.Name = "label5";
|
||||
this.toolTip1.SetToolTip(this.label5, resources.GetString("label5.ToolTip"));
|
||||
//
|
||||
// label6
|
||||
//
|
||||
resources.ApplyResources(this.label6, "label6");
|
||||
this.label6.Name = "label6";
|
||||
this.toolTip1.SetToolTip(this.label6, resources.GetString("label6.ToolTip"));
|
||||
//
|
||||
// label7
|
||||
//
|
||||
resources.ApplyResources(this.label7, "label7");
|
||||
this.label7.Name = "label7";
|
||||
this.toolTip1.SetToolTip(this.label7, resources.GetString("label7.ToolTip"));
|
||||
//
|
||||
// label8
|
||||
//
|
||||
resources.ApplyResources(this.label8, "label8");
|
||||
this.label8.Name = "label8";
|
||||
this.toolTip1.SetToolTip(this.label8, resources.GetString("label8.ToolTip"));
|
||||
//
|
||||
// textBox1
|
||||
//
|
||||
resources.ApplyResources(this.textBox1, "textBox1");
|
||||
this.textBox1.Name = "textBox1";
|
||||
//
|
||||
// textBox2
|
||||
//
|
||||
resources.ApplyResources(this.textBox2, "textBox2");
|
||||
this.textBox2.Name = "textBox2";
|
||||
//
|
||||
// textBox3
|
||||
//
|
||||
resources.ApplyResources(this.textBox3, "textBox3");
|
||||
this.textBox3.Name = "textBox3";
|
||||
//
|
||||
// textBox4
|
||||
//
|
||||
resources.ApplyResources(this.textBox4, "textBox4");
|
||||
this.textBox4.Name = "textBox4";
|
||||
//
|
||||
// textBox5
|
||||
//
|
||||
resources.ApplyResources(this.textBox5, "textBox5");
|
||||
this.textBox5.Name = "textBox5";
|
||||
//
|
||||
// textBox8
|
||||
//
|
||||
resources.ApplyResources(this.textBox8, "textBox8");
|
||||
this.textBox8.Name = "textBox8";
|
||||
//
|
||||
// button1
|
||||
//
|
||||
resources.ApplyResources(this.button1, "button1");
|
||||
this.button1.Name = "button1";
|
||||
this.toolTip1.SetToolTip(this.button1, resources.GetString("button1.ToolTip"));
|
||||
this.button1.UseVisualStyleBackColor = true;
|
||||
this.button1.Click += new System.EventHandler(this.button1_Click);
|
||||
//
|
||||
// numericUpDown1
|
||||
//
|
||||
resources.ApplyResources(this.numericUpDown1, "numericUpDown1");
|
||||
this.numericUpDown1.Increment = new decimal(new int[] {
|
||||
5,
|
||||
0,
|
||||
0,
|
||||
0});
|
||||
this.numericUpDown1.Maximum = new decimal(new int[] {
|
||||
1000,
|
||||
0,
|
||||
0,
|
||||
0});
|
||||
this.numericUpDown1.Minimum = new decimal(new int[] {
|
||||
10,
|
||||
0,
|
||||
0,
|
||||
0});
|
||||
this.numericUpDown1.Name = "numericUpDown1";
|
||||
this.numericUpDown1.Value = new decimal(new int[] {
|
||||
10,
|
||||
0,
|
||||
0,
|
||||
0});
|
||||
//
|
||||
// numericUpDown2
|
||||
//
|
||||
resources.ApplyResources(this.numericUpDown2, "numericUpDown2");
|
||||
this.numericUpDown2.Increment = new decimal(new int[] {
|
||||
5,
|
||||
0,
|
||||
0,
|
||||
0});
|
||||
this.numericUpDown2.Maximum = new decimal(new int[] {
|
||||
1000,
|
||||
0,
|
||||
0,
|
||||
0});
|
||||
this.numericUpDown2.Minimum = new decimal(new int[] {
|
||||
10,
|
||||
0,
|
||||
0,
|
||||
0});
|
||||
this.numericUpDown2.Name = "numericUpDown2";
|
||||
this.numericUpDown2.Value = new decimal(new int[] {
|
||||
10,
|
||||
0,
|
||||
0,
|
||||
0});
|
||||
//
|
||||
// button2
|
||||
//
|
||||
resources.ApplyResources(this.button2, "button2");
|
||||
this.button2.Name = "button2";
|
||||
this.button2.UseVisualStyleBackColor = true;
|
||||
this.button2.Click += new System.EventHandler(this.button2_Click);
|
||||
//
|
||||
// button3
|
||||
//
|
||||
resources.ApplyResources(this.button3, "button3");
|
||||
this.button3.Name = "button3";
|
||||
this.toolTip1.SetToolTip(this.button3, resources.GetString("button3.ToolTip"));
|
||||
this.button3.UseVisualStyleBackColor = true;
|
||||
this.button3.Click += new System.EventHandler(this.button3_Click);
|
||||
//
|
||||
// button4
|
||||
//
|
||||
resources.ApplyResources(this.button4, "button4");
|
||||
this.button4.Name = "button4";
|
||||
this.toolTip1.SetToolTip(this.button4, resources.GetString("button4.ToolTip"));
|
||||
this.button4.UseVisualStyleBackColor = true;
|
||||
this.button4.Click += new System.EventHandler(this.button4_Click);
|
||||
//
|
||||
// label9
|
||||
//
|
||||
resources.ApplyResources(this.label9, "label9");
|
||||
this.label9.Name = "label9";
|
||||
//
|
||||
// label10
|
||||
//
|
||||
resources.ApplyResources(this.label10, "label10");
|
||||
this.label10.Name = "label10";
|
||||
this.toolTip1.SetToolTip(this.label10, resources.GetString("label10.ToolTip"));
|
||||
//
|
||||
// toolTip1
|
||||
//
|
||||
this.toolTip1.AutomaticDelay = 0;
|
||||
this.toolTip1.AutoPopDelay = 0;
|
||||
this.toolTip1.InitialDelay = 0;
|
||||
this.toolTip1.IsBalloon = true;
|
||||
this.toolTip1.ReshowDelay = 600;
|
||||
this.toolTip1.ToolTipIcon = System.Windows.Forms.ToolTipIcon.Info;
|
||||
this.toolTip1.ToolTipTitle = "Info";
|
||||
//
|
||||
// Form1
|
||||
//
|
||||
resources.ApplyResources(this, "$this");
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.Controls.Add(this.label10);
|
||||
this.Controls.Add(this.label9);
|
||||
this.Controls.Add(this.button4);
|
||||
this.Controls.Add(this.button3);
|
||||
this.Controls.Add(this.button2);
|
||||
this.Controls.Add(this.numericUpDown2);
|
||||
this.Controls.Add(this.numericUpDown1);
|
||||
this.Controls.Add(this.button1);
|
||||
this.Controls.Add(this.textBox8);
|
||||
this.Controls.Add(this.textBox5);
|
||||
this.Controls.Add(this.textBox4);
|
||||
this.Controls.Add(this.textBox3);
|
||||
this.Controls.Add(this.textBox2);
|
||||
this.Controls.Add(this.textBox1);
|
||||
this.Controls.Add(this.label8);
|
||||
this.Controls.Add(this.label7);
|
||||
this.Controls.Add(this.label6);
|
||||
this.Controls.Add(this.label5);
|
||||
this.Controls.Add(this.label4);
|
||||
this.Controls.Add(this.label3);
|
||||
this.Controls.Add(this.label2);
|
||||
this.Controls.Add(this.label1);
|
||||
this.Name = "Form1";
|
||||
((System.ComponentModel.ISupportInitialize)(this.numericUpDown1)).EndInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.numericUpDown2)).EndInit();
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.Label label1;
|
||||
private System.Windows.Forms.Label label2;
|
||||
private System.Windows.Forms.Label label3;
|
||||
private System.Windows.Forms.Label label4;
|
||||
private System.Windows.Forms.Label label5;
|
||||
private System.Windows.Forms.Label label6;
|
||||
private System.Windows.Forms.Label label7;
|
||||
private System.Windows.Forms.Label label8;
|
||||
private System.Windows.Forms.TextBox textBox1;
|
||||
private System.Windows.Forms.TextBox textBox2;
|
||||
private System.Windows.Forms.TextBox textBox3;
|
||||
private System.Windows.Forms.TextBox textBox4;
|
||||
private System.Windows.Forms.TextBox textBox5;
|
||||
private System.Windows.Forms.TextBox textBox8;
|
||||
private System.Windows.Forms.Button button1;
|
||||
private System.Windows.Forms.NumericUpDown numericUpDown1;
|
||||
private System.Windows.Forms.NumericUpDown numericUpDown2;
|
||||
private System.Windows.Forms.Button button2;
|
||||
private System.Windows.Forms.Button button3;
|
||||
private System.Windows.Forms.Button button4;
|
||||
private System.Windows.Forms.Label label9;
|
||||
private System.Windows.Forms.Label label10;
|
||||
private System.Windows.Forms.ToolTip toolTip1;
|
||||
}
|
||||
}
|
||||
|
||||
71
Form1.cs
@@ -1,71 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using System.IO;
|
||||
using System.Drawing.Text;
|
||||
|
||||
namespace DepotDownloaderGUI
|
||||
{
|
||||
public partial class Form1 : Form
|
||||
{
|
||||
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
|
||||
private static extern IntPtr AddFontMemResourceEx(IntPtr pbFont, uint cbFont,
|
||||
IntPtr pdv, [System.Runtime.InteropServices.In] ref uint pcFonts);
|
||||
|
||||
private PrivateFontCollection fonts = new PrivateFontCollection();
|
||||
|
||||
Font Poppins;
|
||||
|
||||
string Command;
|
||||
public Form1()
|
||||
{
|
||||
InitializeComponent();
|
||||
byte[] fontData = Properties.Resources.Poppins_Medium;
|
||||
IntPtr fontPtr = System.Runtime.InteropServices.Marshal.AllocCoTaskMem(fontData.Length);
|
||||
System.Runtime.InteropServices.Marshal.Copy(fontData, 0, fontPtr, fontData.Length);
|
||||
uint dummy = 0;
|
||||
fonts.AddMemoryFont(fontPtr, Properties.Resources.Poppins_Medium.Length);
|
||||
AddFontMemResourceEx(fontPtr, (uint)Properties.Resources.Poppins_Medium.Length, IntPtr.Zero, ref dummy);
|
||||
System.Runtime.InteropServices.Marshal.FreeCoTaskMem(fontPtr);
|
||||
Poppins = new Font(fonts.Families[0], 18.0F);
|
||||
Directory.SetCurrentDirectory("./depotdownloader/");
|
||||
textBox2.PasswordChar = '*';
|
||||
label9.Font = Poppins;
|
||||
FormBorderStyle = FormBorderStyle.FixedSingle;
|
||||
MaximizeBox = false;
|
||||
}
|
||||
|
||||
|
||||
private void button1_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (textBox2.Text == "")
|
||||
Command = $"/k dotnet DepotDownloader.dll -app {textBox3.Text} -depot {textBox4.Text} -manifest {textBox5.Text} -max-servers {numericUpDown1.Value} -max-downloads {numericUpDown2.Value} -dir ../YourGame {textBox8.Text}";
|
||||
else
|
||||
Command = $"/k dotnet DepotDownloader.dll -app {textBox3.Text} -depot {textBox4.Text} -manifest {textBox5.Text} -username {textBox1.Text} -password {textBox2.Text} -max-servers {numericUpDown1.Value} -max-downloads {numericUpDown2.Value} -dir ../YourGame {textBox8.Text}";
|
||||
System.Diagnostics.Process.Start("cmd.exe", Command);
|
||||
}
|
||||
|
||||
|
||||
private void button2_Click(object sender, EventArgs e)
|
||||
{
|
||||
System.Diagnostics.Process.Start("https://github.com/mmvanheusden/SteamDepotDownloaderGUI/discussions/5");
|
||||
}
|
||||
|
||||
private void button3_Click(object sender, EventArgs e)
|
||||
{
|
||||
System.Diagnostics.Process.Start("https://steamdb.info/instantsearch/");
|
||||
}
|
||||
|
||||
private void button4_Click(object sender, EventArgs e)
|
||||
{
|
||||
System.Diagnostics.Process.Start("https://github.com/mmvanheusden/SteamDepotDownloaderGUI");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
787
Form1.resx
@@ -1,787 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<assembly alias="mscorlib" name="mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||
<data name="label1.AutoSize" type="System.Boolean, mscorlib">
|
||||
<value>True</value>
|
||||
</data>
|
||||
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<data name="label1.Font" type="System.Drawing.Font, System.Drawing">
|
||||
<value>Segoe UI, 12.75pt</value>
|
||||
</data>
|
||||
<data name="label1.Location" type="System.Drawing.Point, System.Drawing">
|
||||
<value>12, 44</value>
|
||||
</data>
|
||||
<data name="label1.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>87, 23</value>
|
||||
</data>
|
||||
<data name="label1.TabIndex" type="System.Int32, mscorlib">
|
||||
<value>0</value>
|
||||
</data>
|
||||
<data name="label1.Text" xml:space="preserve">
|
||||
<value>Username</value>
|
||||
</data>
|
||||
<metadata name="toolTip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>17, 17</value>
|
||||
</metadata>
|
||||
<data name="label1.ToolTip" xml:space="preserve">
|
||||
<value>Your Steam Username</value>
|
||||
</data>
|
||||
<data name=">>label1.Name" xml:space="preserve">
|
||||
<value>label1</value>
|
||||
</data>
|
||||
<data name=">>label1.Type" xml:space="preserve">
|
||||
<value>System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name=">>label1.Parent" xml:space="preserve">
|
||||
<value>$this</value>
|
||||
</data>
|
||||
<data name=">>label1.ZOrder" xml:space="preserve">
|
||||
<value>21</value>
|
||||
</data>
|
||||
<data name="label2.AutoSize" type="System.Boolean, mscorlib">
|
||||
<value>True</value>
|
||||
</data>
|
||||
<data name="label2.Font" type="System.Drawing.Font, System.Drawing">
|
||||
<value>Segoe UI, 12.75pt</value>
|
||||
</data>
|
||||
<data name="label2.Location" type="System.Drawing.Point, System.Drawing">
|
||||
<value>12, 72</value>
|
||||
</data>
|
||||
<data name="label2.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>80, 23</value>
|
||||
</data>
|
||||
<data name="label2.TabIndex" type="System.Int32, mscorlib">
|
||||
<value>1</value>
|
||||
</data>
|
||||
<data name="label2.Text" xml:space="preserve">
|
||||
<value>Password</value>
|
||||
</data>
|
||||
<data name="label2.ToolTip" xml:space="preserve">
|
||||
<value>Your Steam Password</value>
|
||||
</data>
|
||||
<data name=">>label2.Name" xml:space="preserve">
|
||||
<value>label2</value>
|
||||
</data>
|
||||
<data name=">>label2.Type" xml:space="preserve">
|
||||
<value>System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name=">>label2.Parent" xml:space="preserve">
|
||||
<value>$this</value>
|
||||
</data>
|
||||
<data name=">>label2.ZOrder" xml:space="preserve">
|
||||
<value>20</value>
|
||||
</data>
|
||||
<data name="label3.AutoSize" type="System.Boolean, mscorlib">
|
||||
<value>True</value>
|
||||
</data>
|
||||
<data name="label3.Font" type="System.Drawing.Font, System.Drawing">
|
||||
<value>Segoe UI, 12.75pt</value>
|
||||
</data>
|
||||
<data name="label3.Location" type="System.Drawing.Point, System.Drawing">
|
||||
<value>12, 100</value>
|
||||
</data>
|
||||
<data name="label3.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>63, 23</value>
|
||||
</data>
|
||||
<data name="label3.TabIndex" type="System.Int32, mscorlib">
|
||||
<value>2</value>
|
||||
</data>
|
||||
<data name="label3.Text" xml:space="preserve">
|
||||
<value>App ID</value>
|
||||
</data>
|
||||
<data name="label3.ToolTip" xml:space="preserve">
|
||||
<value>The app's unique ID, which can be found on the SteamDB website.</value>
|
||||
</data>
|
||||
<data name=">>label3.Name" xml:space="preserve">
|
||||
<value>label3</value>
|
||||
</data>
|
||||
<data name=">>label3.Type" xml:space="preserve">
|
||||
<value>System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name=">>label3.Parent" xml:space="preserve">
|
||||
<value>$this</value>
|
||||
</data>
|
||||
<data name=">>label3.ZOrder" xml:space="preserve">
|
||||
<value>19</value>
|
||||
</data>
|
||||
<data name="label4.AutoSize" type="System.Boolean, mscorlib">
|
||||
<value>True</value>
|
||||
</data>
|
||||
<data name="label4.Font" type="System.Drawing.Font, System.Drawing">
|
||||
<value>Segoe UI, 12.75pt</value>
|
||||
</data>
|
||||
<data name="label4.Location" type="System.Drawing.Point, System.Drawing">
|
||||
<value>11, 127</value>
|
||||
</data>
|
||||
<data name="label4.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>79, 23</value>
|
||||
</data>
|
||||
<data name="label4.TabIndex" type="System.Int32, mscorlib">
|
||||
<value>3</value>
|
||||
</data>
|
||||
<data name="label4.Text" xml:space="preserve">
|
||||
<value>Depot ID</value>
|
||||
</data>
|
||||
<data name="label4.ToolTip" xml:space="preserve">
|
||||
<value>The app's unique Depot ID, which can be found on the
|
||||
SteamDB website at 'Depots' in the sidebar.</value>
|
||||
</data>
|
||||
<data name=">>label4.Name" xml:space="preserve">
|
||||
<value>label4</value>
|
||||
</data>
|
||||
<data name=">>label4.Type" xml:space="preserve">
|
||||
<value>System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name=">>label4.Parent" xml:space="preserve">
|
||||
<value>$this</value>
|
||||
</data>
|
||||
<data name=">>label4.ZOrder" xml:space="preserve">
|
||||
<value>18</value>
|
||||
</data>
|
||||
<data name="label5.AutoSize" type="System.Boolean, mscorlib">
|
||||
<value>True</value>
|
||||
</data>
|
||||
<data name="label5.Font" type="System.Drawing.Font, System.Drawing">
|
||||
<value>Segoe UI, 12.75pt</value>
|
||||
</data>
|
||||
<data name="label5.Location" type="System.Drawing.Point, System.Drawing">
|
||||
<value>11, 158</value>
|
||||
</data>
|
||||
<data name="label5.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>97, 23</value>
|
||||
</data>
|
||||
<data name="label5.TabIndex" type="System.Int32, mscorlib">
|
||||
<value>4</value>
|
||||
</data>
|
||||
<data name="label5.Text" xml:space="preserve">
|
||||
<value>Manifest ID</value>
|
||||
</data>
|
||||
<data name="label5.ToolTip" xml:space="preserve">
|
||||
<value>The app's unique Manifest ID, which can be found on the
|
||||
SteamDB website at 'Manifests' in the sidebar.</value>
|
||||
</data>
|
||||
<data name=">>label5.Name" xml:space="preserve">
|
||||
<value>label5</value>
|
||||
</data>
|
||||
<data name=">>label5.Type" xml:space="preserve">
|
||||
<value>System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name=">>label5.Parent" xml:space="preserve">
|
||||
<value>$this</value>
|
||||
</data>
|
||||
<data name=">>label5.ZOrder" xml:space="preserve">
|
||||
<value>17</value>
|
||||
</data>
|
||||
<data name="label6.AutoSize" type="System.Boolean, mscorlib">
|
||||
<value>True</value>
|
||||
</data>
|
||||
<data name="label6.Font" type="System.Drawing.Font, System.Drawing">
|
||||
<value>Segoe UI, 12.75pt</value>
|
||||
</data>
|
||||
<data name="label6.Location" type="System.Drawing.Point, System.Drawing">
|
||||
<value>11, 192</value>
|
||||
</data>
|
||||
<data name="label6.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>101, 23</value>
|
||||
</data>
|
||||
<data name="label6.TabIndex" type="System.Int32, mscorlib">
|
||||
<value>5</value>
|
||||
</data>
|
||||
<data name="label6.Text" xml:space="preserve">
|
||||
<value>Max Servers</value>
|
||||
</data>
|
||||
<data name="label6.ToolTip" xml:space="preserve">
|
||||
<value>Maximum number of content servers to use.
|
||||
Speeds up download drastically</value>
|
||||
</data>
|
||||
<data name=">>label6.Name" xml:space="preserve">
|
||||
<value>label6</value>
|
||||
</data>
|
||||
<data name=">>label6.Type" xml:space="preserve">
|
||||
<value>System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name=">>label6.Parent" xml:space="preserve">
|
||||
<value>$this</value>
|
||||
</data>
|
||||
<data name=">>label6.ZOrder" xml:space="preserve">
|
||||
<value>16</value>
|
||||
</data>
|
||||
<data name="label7.AutoSize" type="System.Boolean, mscorlib">
|
||||
<value>True</value>
|
||||
</data>
|
||||
<data name="label7.Font" type="System.Drawing.Font, System.Drawing">
|
||||
<value>Segoe UI, 12.75pt</value>
|
||||
</data>
|
||||
<data name="label7.Location" type="System.Drawing.Point, System.Drawing">
|
||||
<value>12, 223</value>
|
||||
</data>
|
||||
<data name="label7.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>103, 23</value>
|
||||
</data>
|
||||
<data name="label7.TabIndex" type="System.Int32, mscorlib">
|
||||
<value>6</value>
|
||||
</data>
|
||||
<data name="label7.Text" xml:space="preserve">
|
||||
<value>Max Chunks</value>
|
||||
</data>
|
||||
<data name="label7.ToolTip" xml:space="preserve">
|
||||
<value>Maximum number of chunks to download concurrently.
|
||||
Speeds up download drastically</value>
|
||||
</data>
|
||||
<data name=">>label7.Name" xml:space="preserve">
|
||||
<value>label7</value>
|
||||
</data>
|
||||
<data name=">>label7.Type" xml:space="preserve">
|
||||
<value>System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name=">>label7.Parent" xml:space="preserve">
|
||||
<value>$this</value>
|
||||
</data>
|
||||
<data name=">>label7.ZOrder" xml:space="preserve">
|
||||
<value>15</value>
|
||||
</data>
|
||||
<data name="label8.AutoSize" type="System.Boolean, mscorlib">
|
||||
<value>True</value>
|
||||
</data>
|
||||
<data name="label8.Font" type="System.Drawing.Font, System.Drawing">
|
||||
<value>Segoe UI, 12.75pt</value>
|
||||
</data>
|
||||
<data name="label8.Location" type="System.Drawing.Point, System.Drawing">
|
||||
<value>11, 249</value>
|
||||
</data>
|
||||
<data name="label8.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>165, 23</value>
|
||||
</data>
|
||||
<data name="label8.TabIndex" type="System.Int32, mscorlib">
|
||||
<value>7</value>
|
||||
</data>
|
||||
<data name="label8.Text" xml:space="preserve">
|
||||
<value>Optional Arguments</value>
|
||||
</data>
|
||||
<data name="label8.ToolTip" xml:space="preserve">
|
||||
<value>Optional Parameters.
|
||||
For advanced users</value>
|
||||
</data>
|
||||
<data name=">>label8.Name" xml:space="preserve">
|
||||
<value>label8</value>
|
||||
</data>
|
||||
<data name=">>label8.Type" xml:space="preserve">
|
||||
<value>System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name=">>label8.Parent" xml:space="preserve">
|
||||
<value>$this</value>
|
||||
</data>
|
||||
<data name=">>label8.ZOrder" xml:space="preserve">
|
||||
<value>14</value>
|
||||
</data>
|
||||
<data name="textBox1.Font" type="System.Drawing.Font, System.Drawing">
|
||||
<value>Segoe UI, 12.75pt</value>
|
||||
</data>
|
||||
<data name="textBox1.Location" type="System.Drawing.Point, System.Drawing">
|
||||
<value>117, 44</value>
|
||||
</data>
|
||||
<data name="textBox1.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>209, 30</value>
|
||||
</data>
|
||||
<data name="textBox1.TabIndex" type="System.Int32, mscorlib">
|
||||
<value>1</value>
|
||||
</data>
|
||||
<data name=">>textBox1.Name" xml:space="preserve">
|
||||
<value>textBox1</value>
|
||||
</data>
|
||||
<data name=">>textBox1.Type" xml:space="preserve">
|
||||
<value>System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name=">>textBox1.Parent" xml:space="preserve">
|
||||
<value>$this</value>
|
||||
</data>
|
||||
<data name=">>textBox1.ZOrder" xml:space="preserve">
|
||||
<value>13</value>
|
||||
</data>
|
||||
<data name="textBox2.Font" type="System.Drawing.Font, System.Drawing">
|
||||
<value>Segoe UI, 12.75pt</value>
|
||||
</data>
|
||||
<data name="textBox2.Location" type="System.Drawing.Point, System.Drawing">
|
||||
<value>117, 73</value>
|
||||
</data>
|
||||
<data name="textBox2.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>209, 30</value>
|
||||
</data>
|
||||
<data name="textBox2.TabIndex" type="System.Int32, mscorlib">
|
||||
<value>2</value>
|
||||
</data>
|
||||
<data name=">>textBox2.Name" xml:space="preserve">
|
||||
<value>textBox2</value>
|
||||
</data>
|
||||
<data name=">>textBox2.Type" xml:space="preserve">
|
||||
<value>System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name=">>textBox2.Parent" xml:space="preserve">
|
||||
<value>$this</value>
|
||||
</data>
|
||||
<data name=">>textBox2.ZOrder" xml:space="preserve">
|
||||
<value>12</value>
|
||||
</data>
|
||||
<data name="textBox3.Font" type="System.Drawing.Font, System.Drawing">
|
||||
<value>Segoe UI, 12.75pt</value>
|
||||
</data>
|
||||
<data name="textBox3.Location" type="System.Drawing.Point, System.Drawing">
|
||||
<value>117, 101</value>
|
||||
</data>
|
||||
<data name="textBox3.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>209, 30</value>
|
||||
</data>
|
||||
<data name="textBox3.TabIndex" type="System.Int32, mscorlib">
|
||||
<value>3</value>
|
||||
</data>
|
||||
<data name=">>textBox3.Name" xml:space="preserve">
|
||||
<value>textBox3</value>
|
||||
</data>
|
||||
<data name=">>textBox3.Type" xml:space="preserve">
|
||||
<value>System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name=">>textBox3.Parent" xml:space="preserve">
|
||||
<value>$this</value>
|
||||
</data>
|
||||
<data name=">>textBox3.ZOrder" xml:space="preserve">
|
||||
<value>11</value>
|
||||
</data>
|
||||
<data name="textBox4.Font" type="System.Drawing.Font, System.Drawing">
|
||||
<value>Segoe UI, 12.75pt</value>
|
||||
</data>
|
||||
<data name="textBox4.Location" type="System.Drawing.Point, System.Drawing">
|
||||
<value>117, 130</value>
|
||||
</data>
|
||||
<data name="textBox4.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>209, 30</value>
|
||||
</data>
|
||||
<data name="textBox4.TabIndex" type="System.Int32, mscorlib">
|
||||
<value>4</value>
|
||||
</data>
|
||||
<data name=">>textBox4.Name" xml:space="preserve">
|
||||
<value>textBox4</value>
|
||||
</data>
|
||||
<data name=">>textBox4.Type" xml:space="preserve">
|
||||
<value>System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name=">>textBox4.Parent" xml:space="preserve">
|
||||
<value>$this</value>
|
||||
</data>
|
||||
<data name=">>textBox4.ZOrder" xml:space="preserve">
|
||||
<value>10</value>
|
||||
</data>
|
||||
<data name="textBox5.Font" type="System.Drawing.Font, System.Drawing">
|
||||
<value>Segoe UI, 12.75pt</value>
|
||||
</data>
|
||||
<data name="textBox5.Location" type="System.Drawing.Point, System.Drawing">
|
||||
<value>117, 159</value>
|
||||
</data>
|
||||
<data name="textBox5.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>209, 30</value>
|
||||
</data>
|
||||
<data name="textBox5.TabIndex" type="System.Int32, mscorlib">
|
||||
<value>5</value>
|
||||
</data>
|
||||
<data name=">>textBox5.Name" xml:space="preserve">
|
||||
<value>textBox5</value>
|
||||
</data>
|
||||
<data name=">>textBox5.Type" xml:space="preserve">
|
||||
<value>System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name=">>textBox5.Parent" xml:space="preserve">
|
||||
<value>$this</value>
|
||||
</data>
|
||||
<data name=">>textBox5.ZOrder" xml:space="preserve">
|
||||
<value>9</value>
|
||||
</data>
|
||||
<data name="textBox8.Font" type="System.Drawing.Font, System.Drawing">
|
||||
<value>Segoe UI, 12.75pt</value>
|
||||
</data>
|
||||
<data name="textBox8.Location" type="System.Drawing.Point, System.Drawing">
|
||||
<value>182, 249</value>
|
||||
</data>
|
||||
<data name="textBox8.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>144, 30</value>
|
||||
</data>
|
||||
<data name="textBox8.TabIndex" type="System.Int32, mscorlib">
|
||||
<value>8</value>
|
||||
</data>
|
||||
<data name=">>textBox8.Name" xml:space="preserve">
|
||||
<value>textBox8</value>
|
||||
</data>
|
||||
<data name=">>textBox8.Type" xml:space="preserve">
|
||||
<value>System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name=">>textBox8.Parent" xml:space="preserve">
|
||||
<value>$this</value>
|
||||
</data>
|
||||
<data name=">>textBox8.ZOrder" xml:space="preserve">
|
||||
<value>8</value>
|
||||
</data>
|
||||
<data name="button1.Font" type="System.Drawing.Font, System.Drawing">
|
||||
<value>Segoe UI Semibold, 9.75pt, style=Bold</value>
|
||||
</data>
|
||||
<data name="button1.Location" type="System.Drawing.Point, System.Drawing">
|
||||
<value>209, 307</value>
|
||||
</data>
|
||||
<data name="button1.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>117, 25</value>
|
||||
</data>
|
||||
<data name="button1.TabIndex" type="System.Int32, mscorlib">
|
||||
<value>11</value>
|
||||
</data>
|
||||
<data name="button1.Text" xml:space="preserve">
|
||||
<value>Start Download</value>
|
||||
</data>
|
||||
<data name="button1.ToolTip" xml:space="preserve">
|
||||
<value>Starts the download</value>
|
||||
</data>
|
||||
<data name=">>button1.Name" xml:space="preserve">
|
||||
<value>button1</value>
|
||||
</data>
|
||||
<data name=">>button1.Type" xml:space="preserve">
|
||||
<value>System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name=">>button1.Parent" xml:space="preserve">
|
||||
<value>$this</value>
|
||||
</data>
|
||||
<data name=">>button1.ZOrder" xml:space="preserve">
|
||||
<value>7</value>
|
||||
</data>
|
||||
<data name="numericUpDown1.Font" type="System.Drawing.Font, System.Drawing">
|
||||
<value>Segoe UI, 12.75pt</value>
|
||||
</data>
|
||||
<data name="numericUpDown1.Location" type="System.Drawing.Point, System.Drawing">
|
||||
<value>117, 188</value>
|
||||
</data>
|
||||
<data name="numericUpDown1.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>209, 30</value>
|
||||
</data>
|
||||
<data name="numericUpDown1.TabIndex" type="System.Int32, mscorlib">
|
||||
<value>6</value>
|
||||
</data>
|
||||
<data name=">>numericUpDown1.Name" xml:space="preserve">
|
||||
<value>numericUpDown1</value>
|
||||
</data>
|
||||
<data name=">>numericUpDown1.Type" xml:space="preserve">
|
||||
<value>System.Windows.Forms.NumericUpDown, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name=">>numericUpDown1.Parent" xml:space="preserve">
|
||||
<value>$this</value>
|
||||
</data>
|
||||
<data name=">>numericUpDown1.ZOrder" xml:space="preserve">
|
||||
<value>6</value>
|
||||
</data>
|
||||
<data name="numericUpDown2.Font" type="System.Drawing.Font, System.Drawing">
|
||||
<value>Segoe UI, 12.75pt</value>
|
||||
</data>
|
||||
<data name="numericUpDown2.Location" type="System.Drawing.Point, System.Drawing">
|
||||
<value>117, 216</value>
|
||||
</data>
|
||||
<data name="numericUpDown2.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>209, 30</value>
|
||||
</data>
|
||||
<data name="numericUpDown2.TabIndex" type="System.Int32, mscorlib">
|
||||
<value>7</value>
|
||||
</data>
|
||||
<data name=">>numericUpDown2.Name" xml:space="preserve">
|
||||
<value>numericUpDown2</value>
|
||||
</data>
|
||||
<data name=">>numericUpDown2.Type" xml:space="preserve">
|
||||
<value>System.Windows.Forms.NumericUpDown, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name=">>numericUpDown2.Parent" xml:space="preserve">
|
||||
<value>$this</value>
|
||||
</data>
|
||||
<data name=">>numericUpDown2.ZOrder" xml:space="preserve">
|
||||
<value>5</value>
|
||||
</data>
|
||||
<data name="button2.Font" type="System.Drawing.Font, System.Drawing">
|
||||
<value>Segoe UI Semibold, 9.75pt, style=Bold</value>
|
||||
</data>
|
||||
<data name="button2.Location" type="System.Drawing.Point, System.Drawing">
|
||||
<value>209, 280</value>
|
||||
</data>
|
||||
<data name="button2.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>117, 23</value>
|
||||
</data>
|
||||
<data name="button2.TabIndex" type="System.Int32, mscorlib">
|
||||
<value>9</value>
|
||||
</data>
|
||||
<data name="button2.Text" xml:space="preserve">
|
||||
<value>Need Help?</value>
|
||||
</data>
|
||||
<data name=">>button2.Name" xml:space="preserve">
|
||||
<value>button2</value>
|
||||
</data>
|
||||
<data name=">>button2.Type" xml:space="preserve">
|
||||
<value>System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name=">>button2.Parent" xml:space="preserve">
|
||||
<value>$this</value>
|
||||
</data>
|
||||
<data name=">>button2.ZOrder" xml:space="preserve">
|
||||
<value>4</value>
|
||||
</data>
|
||||
<data name="button3.Font" type="System.Drawing.Font, System.Drawing">
|
||||
<value>Segoe UI Semibold, 9.75pt, style=Bold</value>
|
||||
</data>
|
||||
<data name="button3.Location" type="System.Drawing.Point, System.Drawing">
|
||||
<value>12, 280</value>
|
||||
</data>
|
||||
<data name="button3.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>164, 26</value>
|
||||
</data>
|
||||
<data name="button3.TabIndex" type="System.Int32, mscorlib">
|
||||
<value>8</value>
|
||||
</data>
|
||||
<data name="button3.Text" xml:space="preserve">
|
||||
<value>SteamDB Instant Search</value>
|
||||
</data>
|
||||
<data name="button3.ToolTip" xml:space="preserve">
|
||||
<value>Brings you to the SteamDB Instant game search page.</value>
|
||||
</data>
|
||||
<data name=">>button3.Name" xml:space="preserve">
|
||||
<value>button3</value>
|
||||
</data>
|
||||
<data name=">>button3.Type" xml:space="preserve">
|
||||
<value>System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name=">>button3.Parent" xml:space="preserve">
|
||||
<value>$this</value>
|
||||
</data>
|
||||
<data name=">>button3.ZOrder" xml:space="preserve">
|
||||
<value>3</value>
|
||||
</data>
|
||||
<data name="button4.Font" type="System.Drawing.Font, System.Drawing">
|
||||
<value>Segoe UI Semibold, 9.75pt, style=Bold</value>
|
||||
</data>
|
||||
<data name="button4.Location" type="System.Drawing.Point, System.Drawing">
|
||||
<value>12, 309</value>
|
||||
</data>
|
||||
<data name="button4.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>164, 23</value>
|
||||
</data>
|
||||
<data name="button4.TabIndex" type="System.Int32, mscorlib">
|
||||
<value>10</value>
|
||||
</data>
|
||||
<data name="button4.Text" xml:space="preserve">
|
||||
<value>GitHub Repo</value>
|
||||
</data>
|
||||
<data name="button4.ToolTip" xml:space="preserve">
|
||||
<value>Brings you to the GitHub Repository page.</value>
|
||||
</data>
|
||||
<data name=">>button4.Name" xml:space="preserve">
|
||||
<value>button4</value>
|
||||
</data>
|
||||
<data name=">>button4.Type" xml:space="preserve">
|
||||
<value>System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name=">>button4.Parent" xml:space="preserve">
|
||||
<value>$this</value>
|
||||
</data>
|
||||
<data name=">>button4.ZOrder" xml:space="preserve">
|
||||
<value>2</value>
|
||||
</data>
|
||||
<data name="label9.AutoSize" type="System.Boolean, mscorlib">
|
||||
<value>True</value>
|
||||
</data>
|
||||
<data name="label9.Font" type="System.Drawing.Font, System.Drawing">
|
||||
<value>Segoe UI Semibold, 18pt, style=Bold</value>
|
||||
</data>
|
||||
<data name="label9.Location" type="System.Drawing.Point, System.Drawing">
|
||||
<value>7, -1</value>
|
||||
</data>
|
||||
<data name="label9.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>296, 32</value>
|
||||
</data>
|
||||
<data name="label9.TabIndex" type="System.Int32, mscorlib">
|
||||
<value>13</value>
|
||||
</data>
|
||||
<data name="label9.Text" xml:space="preserve">
|
||||
<value>Steam Depot Downloader</value>
|
||||
</data>
|
||||
<data name=">>label9.Name" xml:space="preserve">
|
||||
<value>label9</value>
|
||||
</data>
|
||||
<data name=">>label9.Type" xml:space="preserve">
|
||||
<value>System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name=">>label9.Parent" xml:space="preserve">
|
||||
<value>$this</value>
|
||||
</data>
|
||||
<data name=">>label9.ZOrder" xml:space="preserve">
|
||||
<value>1</value>
|
||||
</data>
|
||||
<data name="label10.AutoSize" type="System.Boolean, mscorlib">
|
||||
<value>True</value>
|
||||
</data>
|
||||
<data name="label10.Location" type="System.Drawing.Point, System.Drawing">
|
||||
<value>200, 335</value>
|
||||
</data>
|
||||
<data name="label10.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>134, 13</value>
|
||||
</data>
|
||||
<data name="label10.TabIndex" type="System.Int32, mscorlib">
|
||||
<value>14</value>
|
||||
</data>
|
||||
<data name="label10.Text" xml:space="preserve">
|
||||
<value>Star this project on GitHub!</value>
|
||||
</data>
|
||||
<data name="label10.ToolTip" xml:space="preserve">
|
||||
<value>Don't forget!!</value>
|
||||
</data>
|
||||
<data name=">>label10.Name" xml:space="preserve">
|
||||
<value>label10</value>
|
||||
</data>
|
||||
<data name=">>label10.Type" xml:space="preserve">
|
||||
<value>System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name=">>label10.Parent" xml:space="preserve">
|
||||
<value>$this</value>
|
||||
</data>
|
||||
<data name=">>label10.ZOrder" xml:space="preserve">
|
||||
<value>0</value>
|
||||
</data>
|
||||
<metadata name="$this.Localizable" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>True</value>
|
||||
</metadata>
|
||||
<data name="$this.AutoScaleDimensions" type="System.Drawing.SizeF, System.Drawing">
|
||||
<value>6, 13</value>
|
||||
</data>
|
||||
<data name="$this.ClientSize" type="System.Drawing.Size, System.Drawing">
|
||||
<value>334, 351</value>
|
||||
</data>
|
||||
<data name="$this.Text" xml:space="preserve">
|
||||
<value>Steam Depot Downloader</value>
|
||||
</data>
|
||||
<data name=">>toolTip1.Name" xml:space="preserve">
|
||||
<value>toolTip1</value>
|
||||
</data>
|
||||
<data name=">>toolTip1.Type" xml:space="preserve">
|
||||
<value>System.Windows.Forms.ToolTip, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name=">>$this.Name" xml:space="preserve">
|
||||
<value>Form1</value>
|
||||
</data>
|
||||
<data name=">>$this.Type" xml:space="preserve">
|
||||
<value>System.Windows.Forms.Form, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -1,16 +1,16 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
@@ -19,35 +19,35 @@ GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
@@ -58,49 +58,49 @@ products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
@@ -109,18 +109,18 @@ work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
@@ -131,7 +131,7 @@ implementation is available to the public in source code form. A
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
@@ -144,16 +144,16 @@ linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
@@ -161,7 +161,7 @@ covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
@@ -172,19 +172,19 @@ for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
@@ -192,9 +192,9 @@ modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
@@ -202,12 +202,12 @@ non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
@@ -232,7 +232,7 @@ terms of section 4, provided that you also meet all of these conditions:
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
@@ -242,9 +242,9 @@ beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
@@ -290,11 +290,11 @@ in one of these ways:
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
@@ -307,7 +307,7 @@ is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
@@ -315,7 +315,7 @@ suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
@@ -326,7 +326,7 @@ if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
@@ -334,15 +334,15 @@ network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
@@ -351,14 +351,14 @@ apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
@@ -385,7 +385,7 @@ that material) supplement the terms of this License with terms:
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
@@ -395,46 +395,46 @@ License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
@@ -443,14 +443,14 @@ modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
@@ -460,7 +460,7 @@ give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
@@ -468,13 +468,13 @@ rights granted under this License, and you may not initiate litigation
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
@@ -484,19 +484,19 @@ purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
@@ -510,7 +510,7 @@ covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
@@ -518,7 +518,7 @@ or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
@@ -533,13 +533,13 @@ for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
@@ -549,9 +549,9 @@ to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
@@ -560,14 +560,14 @@ but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
@@ -576,19 +576,19 @@ Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
@@ -597,9 +597,9 @@ PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
@@ -609,9 +609,9 @@ PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
@@ -622,11 +622,11 @@ copy of the Program in return for a fee.
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
@@ -649,7 +649,7 @@ the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
@@ -661,12 +661,12 @@ The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
22
Program.cs
@@ -1,22 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace DepotDownloaderGUI
|
||||
{
|
||||
static class Program
|
||||
{
|
||||
/// <summary>
|
||||
/// The main entry point for the application.
|
||||
/// </summary>
|
||||
[STAThread]
|
||||
static void Main()
|
||||
{
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
Application.Run(new Form1());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("DepotDownloaderGUI")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("DepotDownloaderGUI")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2021")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("9fe2af44-2d88-43db-9b8f-ea410552d8ce")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
73
Properties/Resources.Designer.cs
generated
@@ -1,73 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace DepotDownloaderGUI.Properties {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resources {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resources() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("DepotDownloaderGUI.Properties.Resources", typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Byte[].
|
||||
/// </summary>
|
||||
internal static byte[] Poppins_Medium {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("Poppins_Medium", resourceCulture);
|
||||
return ((byte[])(obj));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||
<data name="Poppins_Medium" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\Poppins-Medium.ttf;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
</root>
|
||||
29
Properties/Settings.Designer.cs
generated
@@ -1,29 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
namespace DepotDownloaderGUI.Properties
|
||||
{
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
|
||||
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
|
||||
{
|
||||
|
||||
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||
|
||||
public static Settings Default
|
||||
{
|
||||
get
|
||||
{
|
||||
return defaultInstance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
|
||||
<Profiles>
|
||||
<Profile Name="(Default)" />
|
||||
</Profiles>
|
||||
<Settings />
|
||||
</SettingsFile>
|
||||
161
README.md
@@ -1,78 +1,99 @@
|
||||
<div align="center">
|
||||
<h1>SteamDepotDownloaderGUI</h1>
|
||||
<h4>A graphical wrapper for DepotDownloader, designed to make downloading older versions of Steam games easy.</h4>
|
||||
|
||||
→ <a href="https://www.youtube.com/watch?v=H2COwT5OUOo" target="_blank"><b>Tutorial</b></a> ~
|
||||
<a href="https://depotdownloader.aphex.cc/" target="_blank"><b>Website</b></a> ~
|
||||
<a href="https://www.youtube.com/watch?v=ogiDAuH3VdY" target="_blank"><b>Example usage</b></a> ←
|
||||
</h4>
|
||||
|
||||
|
||||
<img src="https://socialify.git.ci/mmvanheusden/SteamDepotDownloaderGUI/image?description=1&font=Inter&forks=1&issues=1&language=1&owner=1&pattern=Floating%20Cogs&pulls=1&stargazers=1&theme=Light" alt="Banner" width="768"/>
|
||||
<a href="https://img.shields.io/github/last-commit/mmvanheusden/SteamDepotDownloaderGUI?color=crimson"><img src="https://img.shields.io/github/last-commit/mmvanheusden/SteamDepotDownloaderGUI?color=crimson" alt="Last contribution badge"></a>
|
||||
<a href="https://github.com/mmvanheusden/SteamDepotDownloaderGUI/releases/latest"><img src="https://img.shields.io/badge/Download -ffbd03?style=for-the-badge&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAABmJLR0QA/wD/AP+gvaeTAAABd0lEQVRoge2XwUrDQBCGPz3Ee++2UBSvdsGbeLHeFX0lPQhei+8g2AfwSVREK+LJ1oqHUGjRQ7Z0laRJNtldKPPBwoTM7D9/sks2IAiCsKrsAK/AC7DtSmTd1cTAKbAJtIAzVyIuDURGvOFKxKUBL4iB0IiB0IiB0IiB0IiB0IiB0IiB0IgBoA08Ak+AKpCvdO6AGn416zBwDGyRGLljuQmlc9pAEzipKl6HgVtgpOMGSYOdlLyOvtfQ1yPgpgb9WlDAEPjRYwj0jet+yv0iy80ru8AHiyazxiewF6jHXPJMeG3+CoiBy5J1/5dTlWVzAXwD5yXrAJho4YlFbdqesFnzsa6PLWr/PD0bFPAA3GO/YXN7WMspLpLnktweVvooMTPiKDPLHabmLCtpmYF3I96v3E55Doz4zWaCaxYb6Bno4udNRMCR1pzr92wmagFfZH+UfI0xycHPim5gE2Pg0Lb5OU2SVzgAph6anmqtHhWevCAIgh9+AdLMtu/CZhHJAAAAAElFTkSuQmCC" alt="Download latest release badge"></a>
|
||||
<a href="https://github.com/mmvanheusden/SteamDepotDownloaderGUI/releases/latest"><img src="https://img.shields.io/github/downloads/mmvanheusden/SteamDepotDownloaderGUI/total?color=orange&label=downloads" alt="Download count badge"></a>
|
||||
|
||||
<img src="https://github.com/user-attachments/assets/2e1b1b8e-9560-4dde-86c0-b70384a54fbb" alt="Steam downgrader interface" style="max-width: 40%;"/>
|
||||
</div>
|
||||
|
||||
|
||||
## Features
|
||||
- **Cross-platform support**
|
||||
| OS | Supported |
|
||||
|---------|-----------|
|
||||
| Windows | ✅ |
|
||||
| Linux | ✅ |
|
||||
| macOS | ✅ |
|
||||
|
||||
- **Support for every major Linux terminal emulator**
|
||||
<details><summary>List of supported terminals</summary>
|
||||
|
||||
* GNOME Terminal
|
||||
* GNOME Console
|
||||
* Konsole
|
||||
* Xfce-terminal
|
||||
* Alacritty
|
||||
* XTerm
|
||||
* Terminator
|
||||
* cool-retro-term
|
||||
* Kitty
|
||||
* LXTerminal
|
||||
* Deepin Terminal
|
||||
* Terminology
|
||||
* Tilix
|
||||
</details>
|
||||
|
||||
- **Automatic download and extraction of DepotDownloader**
|
||||
|
||||
|
||||
## How to download
|
||||
> [!CAUTION]
|
||||
> This GitHub repository is the only official place to download this software.
|
||||
> If you have paid for this software, or downloaded this from an untrusted place, **you are at risk** <sub><sup><sub><sup>and an idiot.<sub><sup><sub><sup>
|
||||
|
||||
|
||||
### Windows:
|
||||
Download the [latest Windows release](https://github.com/mmvanheusden/SteamDepotDownloaderGUI/releases/latest). There are multiple variants to choose from, but you are probably looking for the file that ends with **`.exe`**.
|
||||
|
||||
|
||||
### Linux:
|
||||
You'll need at least one of the supported terminal emulators. You most likely already have one of these.
|
||||
|
||||
Download the [latest Linux release](https://github.com/mmvanheusden/SteamDepotDownloaderGUI/releases/latest). There are multiple options to choose from.
|
||||
|
||||
|
||||
## Tutorials
|
||||
* https://www.youtube.com/watch?v=H2COwT5OUOo How to download older versions of Steam games tutorial
|
||||
|
||||
* https://www.youtube.com/watch?v=ogiDAuH3VdY How to download older versions of Subnautica tutorial
|
||||
|
||||
|
||||
## Credits
|
||||
This software makes use of the following projects:
|
||||
- [**DepotDownloader**](https://github.com/SteamRE/DepotDownloader/)
|
||||
- [Tauri](https://tauri.app)
|
||||
- [Primer CSS](https://primer.style/css/)
|
||||
- [async-process](https://github.com/smol-rs/async-process)
|
||||
- [Hubut Sans](https://github.com/github/hubot-sans) under [license](https://github.com/github/hubot-sans/blob/05d5ea150c20e6434485db8ffd2277ed18a9e911/LICENSE)
|
||||
|
||||
|
||||
## Donate
|
||||
You can donate [here](paypal.me/onderkin) or through the **donate** button in the interface.
|
||||
|
||||
|
||||
## Contribute
|
||||
Every pull request is welcome! ;)
|
||||
Please cleanup the code using:
|
||||
```console
|
||||
$ pnpm eslint --fix src/
|
||||
```
|
||||
|
||||
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/status-Beta-blue" />
|
||||
<img alt="GitHub all releases" src="https://img.shields.io/github/downloads/mmvanheusden/SteamDepotDownloaderGUI/total?color=orange&label=downloads">
|
||||
<img alt="GitHub release (latest by date including pre-releases)" src="https://img.shields.io/github/v/release/mmvanheusden/SteamDepotDownloaderGUI?color=seagreen&include_prereleases">
|
||||
<img alt="GitHub last commit" src="https://img.shields.io/github/last-commit/mmvanheusden/SteamDepotDownloaderGUI?color=crimson">
|
||||
<a href="https://wakatime.com/badge/github/mmvanheusden/SteamDepotDownloaderGUI"><img src="https://wakatime.com/badge/github/mmvanheusden/SteamDepotDownloaderGUI.svg"></a>
|
||||
<img alt="Visitor Count" src="https://visitor-badge.glitch.me/badge?page_id=mmvanheusden.SteamDepotDownloaderGUI">
|
||||
<a href="https://www.gnu.org/licenses/gpl-3.0" target="_blank">
|
||||
<img src="https://github.com/mmvanheusden/SteamDepotDownloaderGUI/assets/50550545/b5649b7f-ea49-45c4-b0cd-5f3788dcd6ca" height="40px">
|
||||
</a>
|
||||
<a href="https://aphex.cc" target="_blank">
|
||||
<img src="https://github.com/mmvanheusden/SteamDepotDownloaderGUI/assets/50550545/83f5f3b2-2bf9-41aa-ab87-880466f785fe" height="40px">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
# SteamDepotDownloaderGUI
|
||||
|
||||
A simple GUI tool based on [**DepotDownloader**][depotdownloader] for downloading older versions of Steam games.
|
||||
|
||||

|
||||
|
||||
## What can you do with the program?
|
||||
You can download older versions of Steam games and software :sunglasses:
|
||||
|
||||
## Want an example?
|
||||
Take a look at [**This**][subnauticawiki] example.
|
||||
|
||||
## YouTube Tutorial
|
||||
<a href="https://www.youtube.com/watch?v=X-tzW5ywCgU">
|
||||
<img border="0" alt="YouTube Tutorial" src="/src/readme.md/youtube.png" width="768">
|
||||
</a>
|
||||
|
||||
## How use the program?
|
||||
|
||||
### step 1:
|
||||
Download [**.NET Core 2.0**][dotnet] It is required for the program to work properly.
|
||||
### step 2:
|
||||
Download and unzip the program. (You can download it [**Here**][latest])
|
||||
### step 3:
|
||||
Run DepotDownloaderGUI.exe.
|
||||
### step 4:
|
||||
First enter your Steam credentials at "Username" and "Password" (Don't worry, it's safe!)
|
||||
### step 5:
|
||||
Click on "SteamDB Instant Search".
|
||||
### step 6:
|
||||
Enter your game of choice at the search bar and select the game.
|
||||
### step 7:
|
||||
Copy the App ID and paste it in the program at "App ID"
|
||||
### step 8:
|
||||
Click on "Depots" in the sidebar.
|
||||
### step 9:
|
||||
Select the Windows depot (usually it is Win32)
|
||||
### step 10:
|
||||
Copy the Depot ID and paste it in the program at "Depot ID"
|
||||
### step 11:
|
||||
Click on "Manifests" in the sidebar
|
||||
### step 12:
|
||||
Choose a Manifest(game version) of choice and copy its ID and paste it in the program at "Manifest ID"
|
||||
### step 13:
|
||||
If you want a faster download speed, increase the max servers and max chunks.
|
||||
### step 14:
|
||||
Click on "Start Download", a terminal will pop up and will show the download progress.
|
||||
### step 15:
|
||||
Once the download is done, close the program.
|
||||
The downloaded game is stored in the folder "YourGame"
|
||||
> :warning: **Once the download progress is complete, Move the downloaded files to a different folder, Or else your next game download will mix with your old download!**
|
||||
### Enjoy your game!
|
||||
|
||||
## Need help?
|
||||
Just place your cursor on a text label or button and a help balloon will appear!
|
||||
### If you have any questions, use the shiny new ✨GitHub Discussions✨ page!
|
||||
|
||||
|
||||
[latest]: https://github.com/mmvanheusden/SteamDepotDownloaderGUI/releases/latest
|
||||
[steamdb]: https://steamdb.info/
|
||||
[depotdownloader]: https://github.com/SteamRE/DepotDownloader
|
||||
[subnauticawiki]: https://github.com/mmvanheusden/SteamDepotDownloaderGUI/wiki/How-to-Download-older-versions-of-Subnautica
|
||||
[dotnet]: https://dotnet.microsoft.com/download/dotnet-core/thank-you/runtime-2.0.9-windows-x64-installer
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
; EditorConfig: http://EditorConfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
37
depotdownloader/.github/workflows/build.yml
vendored
@@ -1,37 +0,0 @@
|
||||
name: .NET Core CI
|
||||
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- '.github/*'
|
||||
- '.github/*_TEMPLATE/**'
|
||||
- '*.md'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '.github/*'
|
||||
- '.github/*_TEMPLATE/**'
|
||||
- '*.md'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: .NET on ${{ matrix.os }} (${{ matrix.configuration }})
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [macos-latest, ubuntu-latest, windows-latest]
|
||||
configuration: [Release, Debug]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v1
|
||||
- name: Build
|
||||
run: dotnet publish -c ${{ matrix.configuration }} -o artifacts
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
if: matrix.configuration == 'Release'
|
||||
with:
|
||||
name: DepotDownloader-${{ runner.os }}
|
||||
path: artifacts
|
||||
if-no-files-found: error
|
||||
120
depotdownloader/.gitignore
vendored
@@ -1,120 +0,0 @@
|
||||
# Build Folders (you can keep bin if you'd like, to store dlls and pdbs)
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
|
||||
# mstest test results
|
||||
TestResults
|
||||
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.sln.docstates
|
||||
.vs
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Rr]elease/
|
||||
x64/
|
||||
*_i.c
|
||||
*_p.c
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*
|
||||
|
||||
# NCrunch
|
||||
*.ncrunch*
|
||||
.*crunch*.local.xml
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish
|
||||
|
||||
# Publish Web Output
|
||||
*.Publish.xml
|
||||
|
||||
# NuGet Packages Directory
|
||||
packages
|
||||
*.nupkg
|
||||
|
||||
# Windows Azure Build Output
|
||||
csx
|
||||
*.build.csdef
|
||||
|
||||
# Windows Store app package directory
|
||||
AppPackages/
|
||||
|
||||
# Others
|
||||
[Bb]in
|
||||
[Oo]bj
|
||||
sql
|
||||
TestResults
|
||||
[Tt]est[Rr]esult*
|
||||
*.Cache
|
||||
ClientBin
|
||||
[Ss]tyle[Cc]op.*
|
||||
~$*
|
||||
*.dbmdl
|
||||
Generated_Code #added for RIA/Silverlight projects
|
||||
|
||||
# Backup & report files from converting an old project file to a newer
|
||||
# Visual Studio version. Backup files are not needed, because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
|
||||
# third party libs
|
||||
boost/
|
||||
google/
|
||||
zlib/
|
||||
protobuf/
|
||||
cryptopp/
|
||||
|
||||
# misc
|
||||
Thumbs.db
|
||||
@@ -1,22 +0,0 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.26228.4
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DepotDownloader", "DepotDownloader\DepotDownloader.csproj", "{39159C47-ACD3-449F-96CA-4F30C8ED147A}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{39159C47-ACD3-449F-96CA-4F30C8ED147A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{39159C47-ACD3-449F-96CA-4F30C8ED147A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{39159C47-ACD3-449F-96CA-4F30C8ED147A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{39159C47-ACD3-449F-96CA-4F30C8ED147A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -1,90 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ProtoBuf;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.IO.IsolatedStorage;
|
||||
using System.Linq;
|
||||
using SteamKit2;
|
||||
using SteamKit2.Discovery;
|
||||
|
||||
namespace DepotDownloader
|
||||
{
|
||||
[ProtoContract]
|
||||
class AccountSettingsStore
|
||||
{
|
||||
[ProtoMember(1, IsRequired=false)]
|
||||
public Dictionary<string, byte[]> SentryData { get; private set; }
|
||||
|
||||
[ProtoMember(2, IsRequired = false)]
|
||||
public System.Collections.Concurrent.ConcurrentDictionary<string, int> ContentServerPenalty { get; private set; }
|
||||
|
||||
[ProtoMember(3, IsRequired = false)]
|
||||
public Dictionary<string, string> LoginKeys { get; private set; }
|
||||
|
||||
string FileName = null;
|
||||
|
||||
AccountSettingsStore()
|
||||
{
|
||||
SentryData = new Dictionary<string, byte[]>();
|
||||
ContentServerPenalty = new System.Collections.Concurrent.ConcurrentDictionary<string, int>();
|
||||
LoginKeys = new Dictionary<string, string>();
|
||||
}
|
||||
|
||||
static bool Loaded
|
||||
{
|
||||
get { return Instance != null; }
|
||||
}
|
||||
|
||||
public static AccountSettingsStore Instance = null;
|
||||
static readonly IsolatedStorageFile IsolatedStorage = IsolatedStorageFile.GetUserStoreForAssembly();
|
||||
|
||||
public static void LoadFromFile(string filename)
|
||||
{
|
||||
if (Loaded)
|
||||
throw new Exception("Config already loaded");
|
||||
|
||||
if (IsolatedStorage.FileExists(filename))
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var fs = IsolatedStorage.OpenFile(filename, FileMode.Open, FileAccess.Read))
|
||||
using (DeflateStream ds = new DeflateStream(fs, CompressionMode.Decompress))
|
||||
{
|
||||
Instance = ProtoBuf.Serializer.Deserialize<AccountSettingsStore>(ds);
|
||||
}
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
Console.WriteLine("Failed to load account settings: {0}", ex.Message);
|
||||
Instance = new AccountSettingsStore();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Instance = new AccountSettingsStore();
|
||||
}
|
||||
|
||||
Instance.FileName = filename;
|
||||
}
|
||||
|
||||
public static void Save()
|
||||
{
|
||||
if (!Loaded)
|
||||
throw new Exception("Saved config before loading");
|
||||
|
||||
try
|
||||
{
|
||||
using (var fs = IsolatedStorage.OpenFile(Instance.FileName, FileMode.Create, FileAccess.Write))
|
||||
using (DeflateStream ds = new DeflateStream(fs, CompressionMode.Compress))
|
||||
{
|
||||
ProtoBuf.Serializer.Serialize<AccountSettingsStore>(ds, Instance);
|
||||
}
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
Console.WriteLine("Failed to save account settings: {0}", ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,196 +0,0 @@
|
||||
using SteamKit2;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DepotDownloader
|
||||
{
|
||||
/// <summary>
|
||||
/// CDNClientPool provides a pool of connections to CDN endpoints, requesting CDN tokens as needed
|
||||
/// </summary>
|
||||
class CDNClientPool
|
||||
{
|
||||
private const int ServerEndpointMinimumSize = 8;
|
||||
|
||||
private readonly Steam3Session steamSession;
|
||||
private readonly uint appId;
|
||||
public CDNClient CDNClient { get; }
|
||||
#if STEAMKIT_UNRELEASED
|
||||
public CDNClient.Server ProxyServer { get; private set; }
|
||||
#endif
|
||||
|
||||
private readonly ConcurrentStack<CDNClient.Server> activeConnectionPool;
|
||||
private readonly BlockingCollection<CDNClient.Server> availableServerEndpoints;
|
||||
|
||||
private readonly AutoResetEvent populatePoolEvent;
|
||||
private readonly Task monitorTask;
|
||||
private readonly CancellationTokenSource shutdownToken;
|
||||
public CancellationTokenSource ExhaustedToken { get; set; }
|
||||
|
||||
public CDNClientPool(Steam3Session steamSession, uint appId)
|
||||
{
|
||||
this.steamSession = steamSession;
|
||||
this.appId = appId;
|
||||
CDNClient = new CDNClient(steamSession.steamClient);
|
||||
|
||||
activeConnectionPool = new ConcurrentStack<CDNClient.Server>();
|
||||
availableServerEndpoints = new BlockingCollection<CDNClient.Server>();
|
||||
|
||||
populatePoolEvent = new AutoResetEvent(true);
|
||||
shutdownToken = new CancellationTokenSource();
|
||||
|
||||
monitorTask = Task.Factory.StartNew(ConnectionPoolMonitorAsync).Unwrap();
|
||||
}
|
||||
|
||||
public void Shutdown()
|
||||
{
|
||||
shutdownToken.Cancel();
|
||||
monitorTask.Wait();
|
||||
}
|
||||
|
||||
private async Task<IReadOnlyCollection<CDNClient.Server>> FetchBootstrapServerListAsync()
|
||||
{
|
||||
var backoffDelay = 0;
|
||||
|
||||
while (!shutdownToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
var cdnServers = await ContentServerDirectoryService.LoadAsync(this.steamSession.steamClient.Configuration, ContentDownloader.Config.CellID, shutdownToken.Token);
|
||||
if (cdnServers != null)
|
||||
{
|
||||
return cdnServers;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("Failed to retrieve content server list: {0}", ex.Message);
|
||||
|
||||
if (ex is SteamKitWebRequestException e && e.StatusCode == (HttpStatusCode)429)
|
||||
{
|
||||
// If we're being throttled, add a delay to the next request
|
||||
backoffDelay = Math.Min(5, ++backoffDelay);
|
||||
await Task.Delay(TimeSpan.FromSeconds(backoffDelay));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task ConnectionPoolMonitorAsync()
|
||||
{
|
||||
bool didPopulate = false;
|
||||
|
||||
while (!shutdownToken.IsCancellationRequested)
|
||||
{
|
||||
populatePoolEvent.WaitOne(TimeSpan.FromSeconds(1));
|
||||
|
||||
// We want the Steam session so we can take the CellID from the session and pass it through to the ContentServer Directory Service
|
||||
if (availableServerEndpoints.Count < ServerEndpointMinimumSize && steamSession.steamClient.IsConnected)
|
||||
{
|
||||
var servers = await FetchBootstrapServerListAsync().ConfigureAwait(false);
|
||||
|
||||
if (servers == null || servers.Count == 0)
|
||||
{
|
||||
ExhaustedToken?.Cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
#if STEAMKIT_UNRELEASED
|
||||
ProxyServer = servers.Where(x => x.UseAsProxy).FirstOrDefault();
|
||||
#endif
|
||||
|
||||
var weightedCdnServers = servers
|
||||
.Where(server =>
|
||||
{
|
||||
#if STEAMKIT_UNRELEASED
|
||||
var isEligibleForApp = server.AllowedAppIds == null || server.AllowedAppIds.Contains(appId);
|
||||
return isEligibleForApp && (server.Type == "SteamCache" || server.Type == "CDN");
|
||||
#else
|
||||
return server.Type == "SteamCache" || server.Type == "CDN";
|
||||
#endif
|
||||
})
|
||||
.Select(server =>
|
||||
{
|
||||
AccountSettingsStore.Instance.ContentServerPenalty.TryGetValue(server.Host, out var penalty);
|
||||
|
||||
return (server, penalty);
|
||||
})
|
||||
.OrderBy(pair => pair.penalty).ThenBy(pair => pair.server.WeightedLoad);
|
||||
|
||||
foreach (var (server, weight) in weightedCdnServers)
|
||||
{
|
||||
for (var i = 0; i < server.NumEntries; i++)
|
||||
{
|
||||
availableServerEndpoints.Add(server);
|
||||
}
|
||||
}
|
||||
|
||||
didPopulate = true;
|
||||
}
|
||||
else if (availableServerEndpoints.Count == 0 && !steamSession.steamClient.IsConnected && didPopulate)
|
||||
{
|
||||
ExhaustedToken?.Cancel();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private CDNClient.Server BuildConnection(CancellationToken token)
|
||||
{
|
||||
if (availableServerEndpoints.Count < ServerEndpointMinimumSize)
|
||||
{
|
||||
populatePoolEvent.Set();
|
||||
}
|
||||
|
||||
return availableServerEndpoints.Take(token);
|
||||
}
|
||||
|
||||
public CDNClient.Server GetConnection(CancellationToken token)
|
||||
{
|
||||
if (!activeConnectionPool.TryPop(out var connection))
|
||||
{
|
||||
connection = BuildConnection(token);
|
||||
}
|
||||
|
||||
return connection;
|
||||
}
|
||||
|
||||
public async Task<string> AuthenticateConnection(uint appId, uint depotId, CDNClient.Server server)
|
||||
{
|
||||
var host = steamSession.ResolveCDNTopLevelHost(server.Host);
|
||||
var cdnKey = $"{depotId:D}:{host}";
|
||||
|
||||
steamSession.RequestCDNAuthToken(appId, depotId, host, cdnKey);
|
||||
|
||||
if (steamSession.CDNAuthTokens.TryGetValue(cdnKey, out var authTokenCallbackPromise))
|
||||
{
|
||||
var result = await authTokenCallbackPromise.Task;
|
||||
return result.Token;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"Failed to retrieve CDN token for server {server.Host} depot {depotId}");
|
||||
}
|
||||
}
|
||||
|
||||
public void ReturnConnection(CDNClient.Server server)
|
||||
{
|
||||
if (server == null) return;
|
||||
|
||||
activeConnectionPool.Push(server);
|
||||
}
|
||||
|
||||
public void ReturnBrokenConnection(CDNClient.Server server)
|
||||
{
|
||||
if (server == null) return;
|
||||
|
||||
// Broken connections are not returned to the pool
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ProtoBuf;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
|
||||
namespace DepotDownloader
|
||||
{
|
||||
[ProtoContract]
|
||||
class DepotConfigStore
|
||||
{
|
||||
[ProtoMember(1)]
|
||||
public Dictionary<uint, ulong> InstalledManifestIDs { get; private set; }
|
||||
|
||||
string FileName = null;
|
||||
|
||||
DepotConfigStore()
|
||||
{
|
||||
InstalledManifestIDs = new Dictionary<uint, ulong>();
|
||||
}
|
||||
|
||||
static bool Loaded
|
||||
{
|
||||
get { return Instance != null; }
|
||||
}
|
||||
|
||||
public static DepotConfigStore Instance = null;
|
||||
|
||||
public static void LoadFromFile(string filename)
|
||||
{
|
||||
if (Loaded)
|
||||
throw new Exception("Config already loaded");
|
||||
|
||||
if (File.Exists(filename))
|
||||
{
|
||||
using (FileStream fs = File.Open(filename, FileMode.Open))
|
||||
using (DeflateStream ds = new DeflateStream(fs, CompressionMode.Decompress))
|
||||
Instance = ProtoBuf.Serializer.Deserialize<DepotConfigStore>(ds);
|
||||
}
|
||||
else
|
||||
{
|
||||
Instance = new DepotConfigStore();
|
||||
}
|
||||
|
||||
Instance.FileName = filename;
|
||||
}
|
||||
|
||||
public static void Save()
|
||||
{
|
||||
if (!Loaded)
|
||||
throw new Exception("Saved config before loading");
|
||||
|
||||
using (FileStream fs = File.Open(Instance.FileName, FileMode.Create))
|
||||
using (DeflateStream ds = new DeflateStream(fs, CompressionMode.Compress))
|
||||
ProtoBuf.Serializer.Serialize<DepotConfigStore>(ds, Instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="protobuf-net" Version="3.0.73" />
|
||||
<PackageReference Include="SteamKit2" Version="2.3.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,33 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace DepotDownloader
|
||||
{
|
||||
class DownloadConfig
|
||||
{
|
||||
public int CellID { get; set; }
|
||||
public bool DownloadAllPlatforms { get; set; }
|
||||
public bool DownloadAllLanguages { get; set; }
|
||||
public bool DownloadManifestOnly { get; set; }
|
||||
public string InstallDirectory { get; set; }
|
||||
|
||||
public bool UsingFileList { get; set; }
|
||||
public List<string> FilesToDownload { get; set; }
|
||||
public List<Regex> FilesToDownloadRegex { get; set; }
|
||||
|
||||
public bool UsingExclusionList { get; set; }
|
||||
|
||||
public string BetaPassword { get; set; }
|
||||
|
||||
public bool VerifyAll { get; set; }
|
||||
|
||||
public int MaxServers { get; set; }
|
||||
public int MaxDownloads { get; set; }
|
||||
|
||||
public string SuppliedPassword { get; set; }
|
||||
public bool RememberPassword { get; set; }
|
||||
|
||||
// A Steam LoginID to allow multiple concurrent connections
|
||||
public uint? LoginID {get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,416 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using SteamKit2;
|
||||
using System.ComponentModel;
|
||||
using System.Threading.Tasks;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Linq;
|
||||
|
||||
namespace DepotDownloader
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static int Main( string[] args )
|
||||
=> MainAsync( args ).GetAwaiter().GetResult();
|
||||
|
||||
static async Task<int> MainAsync( string[] args )
|
||||
{
|
||||
if ( args.Length == 0 )
|
||||
{
|
||||
PrintUsage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
DebugLog.Enabled = false;
|
||||
|
||||
AccountSettingsStore.LoadFromFile( "account.config" );
|
||||
|
||||
#region Common Options
|
||||
|
||||
if ( HasParameter( args, "-debug" ) )
|
||||
{
|
||||
DebugLog.Enabled = true;
|
||||
DebugLog.AddListener( ( category, message ) =>
|
||||
{
|
||||
Console.WriteLine( "[{0}] {1}", category, message );
|
||||
});
|
||||
}
|
||||
|
||||
string username = GetParameter<string>( args, "-username" ) ?? GetParameter<string>( args, "-user" );
|
||||
string password = GetParameter<string>( args, "-password" ) ?? GetParameter<string>( args, "-pass" );
|
||||
ContentDownloader.Config.RememberPassword = HasParameter( args, "-remember-password" );
|
||||
|
||||
ContentDownloader.Config.DownloadManifestOnly = HasParameter( args, "-manifest-only" );
|
||||
|
||||
int cellId = GetParameter<int>( args, "-cellid", -1 );
|
||||
if ( cellId == -1 )
|
||||
{
|
||||
cellId = 0;
|
||||
}
|
||||
|
||||
ContentDownloader.Config.CellID = cellId;
|
||||
|
||||
string fileList = GetParameter<string>( args, "-filelist" );
|
||||
string[] files = null;
|
||||
|
||||
if ( fileList != null )
|
||||
{
|
||||
try
|
||||
{
|
||||
string fileListData = File.ReadAllText(fileList);
|
||||
files = fileListData.Split( new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries );
|
||||
|
||||
ContentDownloader.Config.UsingFileList = true;
|
||||
ContentDownloader.Config.FilesToDownload = new List<string>();
|
||||
ContentDownloader.Config.FilesToDownloadRegex = new List<Regex>();
|
||||
|
||||
var isWindows = RuntimeInformation.IsOSPlatform( OSPlatform.Windows );
|
||||
foreach ( var fileEntry in files )
|
||||
{
|
||||
try
|
||||
{
|
||||
string fileEntryProcessed;
|
||||
if ( isWindows )
|
||||
{
|
||||
// On Windows, ensure that forward slashes can match either forward or backslashes in depot paths
|
||||
fileEntryProcessed = fileEntry.Replace( "/", "[\\\\|/]" );
|
||||
}
|
||||
else
|
||||
{
|
||||
// On other systems, treat / normally
|
||||
fileEntryProcessed = fileEntry;
|
||||
}
|
||||
Regex rgx = new Regex( fileEntryProcessed, RegexOptions.Compiled | RegexOptions.IgnoreCase );
|
||||
ContentDownloader.Config.FilesToDownloadRegex.Add( rgx );
|
||||
}
|
||||
catch
|
||||
{
|
||||
// For anything that can't be processed as a Regex, allow both forward and backward slashes to match
|
||||
// on Windows
|
||||
if( isWindows )
|
||||
{
|
||||
ContentDownloader.Config.FilesToDownload.Add( fileEntry.Replace( "/", "\\" ) );
|
||||
}
|
||||
ContentDownloader.Config.FilesToDownload.Add( fileEntry );
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine( "Using filelist: '{0}'.", fileList );
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine( "Warning: Unable to load filelist: {0}", ex.ToString() );
|
||||
}
|
||||
}
|
||||
|
||||
ContentDownloader.Config.InstallDirectory = GetParameter<string>( args, "-dir" );
|
||||
|
||||
ContentDownloader.Config.VerifyAll = HasParameter( args, "-verify-all" ) || HasParameter( args, "-verify_all" ) || HasParameter( args, "-validate" );
|
||||
ContentDownloader.Config.MaxServers = GetParameter<int>( args, "-max-servers", 20 );
|
||||
ContentDownloader.Config.MaxDownloads = GetParameter<int>( args, "-max-downloads", 8 );
|
||||
ContentDownloader.Config.MaxServers = Math.Max( ContentDownloader.Config.MaxServers, ContentDownloader.Config.MaxDownloads );
|
||||
ContentDownloader.Config.LoginID = HasParameter( args, "-loginid" ) ? (uint?)GetParameter<uint>( args, "-loginid" ) : null;
|
||||
|
||||
#endregion
|
||||
|
||||
uint appId = GetParameter<uint>( args, "-app", ContentDownloader.INVALID_APP_ID );
|
||||
if ( appId == ContentDownloader.INVALID_APP_ID )
|
||||
{
|
||||
Console.WriteLine( "Error: -app not specified!" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
ulong pubFile = GetParameter<ulong>( args, "-pubfile", ContentDownloader.INVALID_MANIFEST_ID );
|
||||
ulong ugcId = GetParameter<ulong>( args, "-ugc", ContentDownloader.INVALID_MANIFEST_ID );
|
||||
if ( pubFile != ContentDownloader.INVALID_MANIFEST_ID )
|
||||
{
|
||||
#region Pubfile Downloading
|
||||
|
||||
if ( InitializeSteam( username, password ) )
|
||||
{
|
||||
try
|
||||
{
|
||||
await ContentDownloader.DownloadPubfileAsync( appId, pubFile ).ConfigureAwait( false );
|
||||
}
|
||||
catch ( Exception ex ) when (
|
||||
ex is ContentDownloaderException
|
||||
|| ex is OperationCanceledException )
|
||||
{
|
||||
Console.WriteLine( ex.Message );
|
||||
return 1;
|
||||
}
|
||||
catch ( Exception e )
|
||||
{
|
||||
Console.WriteLine( "Download failed to due to an unhandled exception: {0}", e.Message );
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
ContentDownloader.ShutdownSteam3();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine( "Error: InitializeSteam failed" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
else if ( ugcId != ContentDownloader.INVALID_MANIFEST_ID )
|
||||
{
|
||||
#region UGC Downloading
|
||||
|
||||
if ( InitializeSteam( username, password ) )
|
||||
{
|
||||
try
|
||||
{
|
||||
await ContentDownloader.DownloadUGCAsync( appId, ugcId ).ConfigureAwait( false );
|
||||
}
|
||||
catch ( Exception ex ) when (
|
||||
ex is ContentDownloaderException
|
||||
|| ex is OperationCanceledException )
|
||||
{
|
||||
Console.WriteLine( ex.Message );
|
||||
return 1;
|
||||
}
|
||||
catch ( Exception e )
|
||||
{
|
||||
Console.WriteLine( "Download failed to due to an unhandled exception: {0}", e.Message );
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
ContentDownloader.ShutdownSteam3();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine( "Error: InitializeSteam failed" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
else
|
||||
{
|
||||
#region App downloading
|
||||
|
||||
string branch = GetParameter<string>( args, "-branch" ) ?? GetParameter<string>( args, "-beta" ) ?? ContentDownloader.DEFAULT_BRANCH;
|
||||
ContentDownloader.Config.BetaPassword = GetParameter<string>( args, "-betapassword" );
|
||||
|
||||
ContentDownloader.Config.DownloadAllPlatforms = HasParameter( args, "-all-platforms" );
|
||||
string os = GetParameter<string>( args, "-os", null );
|
||||
|
||||
if ( ContentDownloader.Config.DownloadAllPlatforms && !String.IsNullOrEmpty( os ) )
|
||||
{
|
||||
Console.WriteLine("Error: Cannot specify -os when -all-platforms is specified.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
string arch = GetParameter<string>( args, "-osarch", null );
|
||||
|
||||
ContentDownloader.Config.DownloadAllLanguages = HasParameter( args, "-all-languages" );
|
||||
string language = GetParameter<string>( args, "-language", null );
|
||||
|
||||
if ( ContentDownloader.Config.DownloadAllLanguages && !String.IsNullOrEmpty( language ) )
|
||||
{
|
||||
Console.WriteLine( "Error: Cannot specify -language when -all-languages is specified." );
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool lv = HasParameter( args, "-lowviolence" );
|
||||
|
||||
List<(uint, ulong)> depotManifestIds = new List<(uint, ulong)>();
|
||||
bool isUGC = false;
|
||||
|
||||
List<uint> depotIdList = GetParameterList<uint>( args, "-depot" );
|
||||
List<ulong> manifestIdList = GetParameterList<ulong>( args, "-manifest" );
|
||||
if ( manifestIdList.Count > 0 )
|
||||
{
|
||||
if ( depotIdList.Count != manifestIdList.Count )
|
||||
{
|
||||
Console.WriteLine( "Error: -manifest requires one id for every -depot specified" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
var zippedDepotManifest = depotIdList.Zip( manifestIdList, ( depotId, manifestId ) => ( depotId, manifestId ) );
|
||||
depotManifestIds.AddRange( zippedDepotManifest );
|
||||
}
|
||||
else
|
||||
{
|
||||
depotManifestIds.AddRange( depotIdList.Select( depotId => ( depotId, ContentDownloader.INVALID_MANIFEST_ID ) ) );
|
||||
}
|
||||
|
||||
if ( InitializeSteam( username, password ) )
|
||||
{
|
||||
try
|
||||
{
|
||||
await ContentDownloader.DownloadAppAsync( appId, depotManifestIds, branch, os, arch, language, lv, isUGC ).ConfigureAwait( false );
|
||||
}
|
||||
catch ( Exception ex ) when (
|
||||
ex is ContentDownloaderException
|
||||
|| ex is OperationCanceledException )
|
||||
{
|
||||
Console.WriteLine( ex.Message );
|
||||
return 1;
|
||||
}
|
||||
catch ( Exception e )
|
||||
{
|
||||
Console.WriteLine( "Download failed to due to an unhandled exception: {0}", e.Message );
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
ContentDownloader.ShutdownSteam3();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine( "Error: InitializeSteam failed" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool InitializeSteam( string username, string password )
|
||||
{
|
||||
if ( username != null && password == null && ( !ContentDownloader.Config.RememberPassword || !AccountSettingsStore.Instance.LoginKeys.ContainsKey( username ) ) )
|
||||
{
|
||||
do
|
||||
{
|
||||
Console.Write( "Enter account password for \"{0}\": ", username );
|
||||
if ( Console.IsInputRedirected )
|
||||
{
|
||||
password = Console.ReadLine();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Avoid console echoing of password
|
||||
password = Util.ReadPassword();
|
||||
}
|
||||
Console.WriteLine();
|
||||
} while ( String.Empty == password );
|
||||
}
|
||||
else if ( username == null )
|
||||
{
|
||||
Console.WriteLine( "No username given. Using anonymous account with dedicated server subscription." );
|
||||
}
|
||||
|
||||
// capture the supplied password in case we need to re-use it after checking the login key
|
||||
ContentDownloader.Config.SuppliedPassword = password;
|
||||
|
||||
return ContentDownloader.InitializeSteam3( username, password );
|
||||
}
|
||||
|
||||
static int IndexOfParam( string[] args, string param )
|
||||
{
|
||||
for ( int x = 0; x < args.Length; ++x )
|
||||
{
|
||||
if ( args[ x ].Equals( param, StringComparison.OrdinalIgnoreCase ) )
|
||||
return x;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
static bool HasParameter( string[] args, string param )
|
||||
{
|
||||
return IndexOfParam( args, param ) > -1;
|
||||
}
|
||||
|
||||
static T GetParameter<T>( string[] args, string param, T defaultValue = default( T ) )
|
||||
{
|
||||
int index = IndexOfParam( args, param );
|
||||
|
||||
if ( index == -1 || index == ( args.Length - 1 ) )
|
||||
return defaultValue;
|
||||
|
||||
string strParam = args[ index + 1 ];
|
||||
|
||||
var converter = TypeDescriptor.GetConverter( typeof( T ) );
|
||||
if ( converter != null )
|
||||
{
|
||||
return ( T )converter.ConvertFromString( strParam );
|
||||
}
|
||||
|
||||
return default( T );
|
||||
}
|
||||
|
||||
static List<T> GetParameterList<T>(string[] args, string param)
|
||||
{
|
||||
List<T> list = new List<T>();
|
||||
int index = IndexOfParam(args, param);
|
||||
|
||||
if (index == -1 || index == (args.Length - 1))
|
||||
return list;
|
||||
|
||||
index++;
|
||||
|
||||
while (index < args.Length)
|
||||
{
|
||||
string strParam = args[index];
|
||||
|
||||
if (strParam[0] == '-') break;
|
||||
|
||||
var converter = TypeDescriptor.GetConverter(typeof(T));
|
||||
if (converter != null)
|
||||
{
|
||||
list.Add((T)converter.ConvertFromString(strParam));
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
static void PrintUsage()
|
||||
{
|
||||
Console.WriteLine();
|
||||
Console.WriteLine( "Usage - downloading one or all depots for an app:" );
|
||||
Console.WriteLine( "\tdepotdownloader -app <id> [-depot <id> [-manifest <id>]]" );
|
||||
Console.WriteLine( "\t\t[-username <username> [-password <password>]] [other options]" );
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Usage - downloading a workshop item using pubfile id");
|
||||
Console.WriteLine( "\tdepotdownloader -app <id> -pubfile <id> [-username <username> [-password <password>]]" );
|
||||
Console.WriteLine("Usage - downloading a workshop item using ugc id");
|
||||
Console.WriteLine("\tdepotdownloader -app <id> -ugc <id> [-username <username> [-password <password>]]");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine( "Parameters:" );
|
||||
Console.WriteLine( "\t-app <#>\t\t\t\t- the AppID to download." );
|
||||
Console.WriteLine( "\t-depot <#>\t\t\t\t- the DepotID to download." );
|
||||
Console.WriteLine( "\t-manifest <id>\t\t\t- manifest id of content to download (requires -depot, default: current for branch)." );
|
||||
Console.WriteLine( "\t-beta <branchname>\t\t\t- download from specified branch if available (default: Public)." );
|
||||
Console.WriteLine( "\t-betapassword <pass>\t\t- branch password if applicable." );
|
||||
Console.WriteLine( "\t-all-platforms\t\t\t- downloads all platform-specific depots when -app is used." );
|
||||
Console.WriteLine( "\t-os <os>\t\t\t\t- the operating system for which to download the game (windows, macos or linux, default: OS the program is currently running on)" );
|
||||
Console.WriteLine( "\t-osarch <arch>\t\t\t\t- the architecture for which to download the game (32 or 64, default: the host's architecture)" );
|
||||
Console.WriteLine( "\t-all-languages\t\t\t\t- download all language-specific depots when -app is used." );
|
||||
Console.WriteLine( "\t-language <lang>\t\t\t\t- the language for which to download the game (default: english)" );
|
||||
Console.WriteLine( "\t-lowviolence\t\t\t\t- download low violence depots when -app is used." );
|
||||
Console.WriteLine();
|
||||
Console.WriteLine( "\t-ugc <#>\t\t\t\t- the UGC ID to download." );
|
||||
Console.WriteLine( "\t-pubfile <#>\t\t\t- the PublishedFileId to download. (Will automatically resolve to UGC id)" );
|
||||
Console.WriteLine();
|
||||
Console.WriteLine( "\t-username <user>\t\t- the username of the account to login to for restricted content.");
|
||||
Console.WriteLine( "\t-password <pass>\t\t- the password of the account to login to for restricted content." );
|
||||
Console.WriteLine( "\t-remember-password\t\t- if set, remember the password for subsequent logins of this user." );
|
||||
Console.WriteLine();
|
||||
Console.WriteLine( "\t-dir <installdir>\t\t- the directory in which to place downloaded files." );
|
||||
Console.WriteLine( "\t-filelist <file.txt>\t- a list of files to download (from the manifest). Can optionally use regex to download only certain files." );
|
||||
Console.WriteLine( "\t-validate\t\t\t\t- Include checksum verification of files already downloaded" );
|
||||
Console.WriteLine();
|
||||
Console.WriteLine( "\t-manifest-only\t\t\t- downloads a human readable manifest for any depots that would be downloaded." );
|
||||
Console.WriteLine( "\t-cellid <#>\t\t\t\t- the overridden CellID of the content server to download from." );
|
||||
Console.WriteLine( "\t-max-servers <#>\t\t- maximum number of content servers to use. (default: 20)." );
|
||||
Console.WriteLine( "\t-max-downloads <#>\t\t- maximum number of chunks to download concurrently. (default: 8)." );
|
||||
Console.WriteLine( "\t-loginid <#>\t\t- a unique 32-bit integer Steam LogonID in decimal, required if running multiple instances of DepotDownloader concurrently." );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle( "DepotDownloader" )]
|
||||
[assembly: AssemblyDescription("Steam Downloading Utility")]
|
||||
[assembly: AssemblyConfiguration( "" )]
|
||||
[assembly: AssemblyCompany("SteamRE Team")]
|
||||
[assembly: AssemblyProduct( "DepotDownloader" )]
|
||||
[assembly: AssemblyCopyright("Copyright © SteamRE Team 2017")]
|
||||
[assembly: AssemblyTrademark( "" )]
|
||||
[assembly: AssemblyCulture( "" )]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible( false )]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid( "df2ab32a-923c-46e3-a1b4-c901ee92ec94" )]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("2.3.0.0")]
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"profiles": {
|
||||
"DepotDownloader": {
|
||||
"commandName": "Project"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
|
||||
using ProtoBuf;
|
||||
using SteamKit2;
|
||||
|
||||
namespace DepotDownloader
|
||||
{
|
||||
[ProtoContract()]
|
||||
class ProtoManifest
|
||||
{
|
||||
// Proto ctor
|
||||
private ProtoManifest()
|
||||
{
|
||||
Files = new List<FileData>();
|
||||
}
|
||||
|
||||
public ProtoManifest(DepotManifest sourceManifest, ulong id) : this()
|
||||
{
|
||||
sourceManifest.Files.ForEach(f => Files.Add(new FileData(f)));
|
||||
ID = id;
|
||||
CreationTime = sourceManifest.CreationTime;
|
||||
}
|
||||
|
||||
[ProtoContract()]
|
||||
public class FileData
|
||||
{
|
||||
// Proto ctor
|
||||
private FileData()
|
||||
{
|
||||
Chunks = new List<ChunkData>();
|
||||
}
|
||||
|
||||
public FileData(DepotManifest.FileData sourceData) : this()
|
||||
{
|
||||
FileName = sourceData.FileName;
|
||||
sourceData.Chunks.ForEach(c => Chunks.Add(new ChunkData(c)));
|
||||
Flags = sourceData.Flags;
|
||||
TotalSize = sourceData.TotalSize;
|
||||
FileHash = sourceData.FileHash;
|
||||
}
|
||||
|
||||
[ProtoMember(1)]
|
||||
public string FileName { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the chunks that this file is composed of.
|
||||
/// </summary>
|
||||
[ProtoMember(2)]
|
||||
public List<ChunkData> Chunks { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the file flags
|
||||
/// </summary>
|
||||
[ProtoMember(3)]
|
||||
public EDepotFileFlag Flags { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total size of this file.
|
||||
/// </summary>
|
||||
[ProtoMember(4)]
|
||||
public ulong TotalSize { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the hash of this file.
|
||||
/// </summary>
|
||||
[ProtoMember(5)]
|
||||
public byte[] FileHash { get; private set; }
|
||||
}
|
||||
|
||||
[ProtoContract(SkipConstructor = true)]
|
||||
public class ChunkData
|
||||
{
|
||||
public ChunkData(DepotManifest.ChunkData sourceChunk)
|
||||
{
|
||||
ChunkID = sourceChunk.ChunkID;
|
||||
Checksum = sourceChunk.Checksum;
|
||||
Offset = sourceChunk.Offset;
|
||||
CompressedLength = sourceChunk.CompressedLength;
|
||||
UncompressedLength = sourceChunk.UncompressedLength;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the SHA-1 hash chunk id.
|
||||
/// </summary>
|
||||
[ProtoMember(1)]
|
||||
public byte[] ChunkID { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the expected Adler32 checksum of this chunk.
|
||||
/// </summary>
|
||||
[ProtoMember(2)]
|
||||
public byte[] Checksum { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the chunk offset.
|
||||
/// </summary>
|
||||
[ProtoMember(3)]
|
||||
public ulong Offset { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the compressed length of this chunk.
|
||||
/// </summary>
|
||||
[ProtoMember(4)]
|
||||
public uint CompressedLength { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the decompressed length of this chunk.
|
||||
/// </summary>
|
||||
[ProtoMember(5)]
|
||||
public uint UncompressedLength { get; private set; }
|
||||
}
|
||||
|
||||
[ProtoMember(1)]
|
||||
public List<FileData> Files { get; private set; }
|
||||
|
||||
[ProtoMember(2)]
|
||||
public ulong ID { get; private set; }
|
||||
|
||||
[ProtoMember(3)]
|
||||
public DateTime CreationTime { get; private set; }
|
||||
|
||||
public static ProtoManifest LoadFromFile(string filename, out byte[] checksum)
|
||||
{
|
||||
if (!File.Exists(filename))
|
||||
{
|
||||
checksum = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
using (MemoryStream ms = new MemoryStream())
|
||||
{
|
||||
using (FileStream fs = File.Open(filename, FileMode.Open))
|
||||
using (DeflateStream ds = new DeflateStream(fs, CompressionMode.Decompress))
|
||||
ds.CopyTo(ms);
|
||||
|
||||
checksum = Util.SHAHash(ms.ToArray());
|
||||
|
||||
ms.Seek(0, SeekOrigin.Begin);
|
||||
return ProtoBuf.Serializer.Deserialize<ProtoManifest>(ms);
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveToFile(string filename, out byte[] checksum)
|
||||
{
|
||||
|
||||
using (MemoryStream ms = new MemoryStream())
|
||||
{
|
||||
ProtoBuf.Serializer.Serialize<ProtoManifest>(ms, this);
|
||||
|
||||
checksum = Util.SHAHash(ms.ToArray());
|
||||
|
||||
ms.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
using (FileStream fs = File.Open(filename, FileMode.Create))
|
||||
using (DeflateStream ds = new DeflateStream(fs, CompressionMode.Compress))
|
||||
ms.CopyTo(ds);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,771 +0,0 @@
|
||||
using SteamKit2;
|
||||
using SteamKit2.Internal;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DepotDownloader
|
||||
{
|
||||
|
||||
class Steam3Session
|
||||
{
|
||||
public class Credentials
|
||||
{
|
||||
public bool LoggedOn { get; set; }
|
||||
public ulong SessionToken { get; set; }
|
||||
|
||||
public bool IsValid
|
||||
{
|
||||
get { return LoggedOn; }
|
||||
}
|
||||
}
|
||||
|
||||
public ReadOnlyCollection<SteamApps.LicenseListCallback.License> Licenses
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public Dictionary<uint, byte[]> AppTickets { get; private set; }
|
||||
public Dictionary<uint, ulong> AppTokens { get; private set; }
|
||||
public Dictionary<uint, ulong> PackageTokens { get; private set; }
|
||||
public Dictionary<uint, byte[]> DepotKeys { get; private set; }
|
||||
public ConcurrentDictionary<string, TaskCompletionSource<SteamApps.CDNAuthTokenCallback>> CDNAuthTokens { get; private set; }
|
||||
public Dictionary<uint, SteamApps.PICSProductInfoCallback.PICSProductInfo> AppInfo { get; private set; }
|
||||
public Dictionary<uint, SteamApps.PICSProductInfoCallback.PICSProductInfo> PackageInfo { get; private set; }
|
||||
public Dictionary<string, byte[]> AppBetaPasswords { get; private set; }
|
||||
|
||||
public SteamClient steamClient;
|
||||
public SteamUser steamUser;
|
||||
SteamApps steamApps;
|
||||
SteamCloud steamCloud;
|
||||
SteamUnifiedMessages.UnifiedService<IPublishedFile> steamPublishedFile;
|
||||
|
||||
CallbackManager callbacks;
|
||||
|
||||
bool authenticatedUser;
|
||||
bool bConnected;
|
||||
bool bConnecting;
|
||||
bool bAborted;
|
||||
bool bExpectingDisconnectRemote;
|
||||
bool bDidDisconnect;
|
||||
bool bDidReceiveLoginKey;
|
||||
int connectionBackoff;
|
||||
int seq; // more hack fixes
|
||||
DateTime connectTime;
|
||||
|
||||
// input
|
||||
SteamUser.LogOnDetails logonDetails;
|
||||
|
||||
// output
|
||||
Credentials credentials;
|
||||
|
||||
static readonly TimeSpan STEAM3_TIMEOUT = TimeSpan.FromSeconds( 30 );
|
||||
|
||||
|
||||
public Steam3Session( SteamUser.LogOnDetails details )
|
||||
{
|
||||
this.logonDetails = details;
|
||||
|
||||
this.authenticatedUser = details.Username != null;
|
||||
this.credentials = new Credentials();
|
||||
this.bConnected = false;
|
||||
this.bConnecting = false;
|
||||
this.bAborted = false;
|
||||
this.bExpectingDisconnectRemote = false;
|
||||
this.bDidDisconnect = false;
|
||||
this.bDidReceiveLoginKey = false;
|
||||
this.seq = 0;
|
||||
|
||||
this.AppTickets = new Dictionary<uint, byte[]>();
|
||||
this.AppTokens = new Dictionary<uint, ulong>();
|
||||
this.PackageTokens = new Dictionary<uint, ulong>();
|
||||
this.DepotKeys = new Dictionary<uint, byte[]>();
|
||||
this.CDNAuthTokens = new ConcurrentDictionary<string, TaskCompletionSource<SteamApps.CDNAuthTokenCallback>>();
|
||||
this.AppInfo = new Dictionary<uint, SteamApps.PICSProductInfoCallback.PICSProductInfo>();
|
||||
this.PackageInfo = new Dictionary<uint, SteamApps.PICSProductInfoCallback.PICSProductInfo>();
|
||||
this.AppBetaPasswords = new Dictionary<string, byte[]>();
|
||||
|
||||
this.steamClient = new SteamClient();
|
||||
|
||||
this.steamUser = this.steamClient.GetHandler<SteamUser>();
|
||||
this.steamApps = this.steamClient.GetHandler<SteamApps>();
|
||||
this.steamCloud = this.steamClient.GetHandler<SteamCloud>();
|
||||
var steamUnifiedMessages = this.steamClient.GetHandler<SteamUnifiedMessages>();
|
||||
this.steamPublishedFile = steamUnifiedMessages.CreateService<IPublishedFile>();
|
||||
|
||||
this.callbacks = new CallbackManager( this.steamClient );
|
||||
|
||||
this.callbacks.Subscribe<SteamClient.ConnectedCallback>( ConnectedCallback );
|
||||
this.callbacks.Subscribe<SteamClient.DisconnectedCallback>( DisconnectedCallback );
|
||||
this.callbacks.Subscribe<SteamUser.LoggedOnCallback>( LogOnCallback );
|
||||
this.callbacks.Subscribe<SteamUser.SessionTokenCallback>( SessionTokenCallback );
|
||||
this.callbacks.Subscribe<SteamApps.LicenseListCallback>( LicenseListCallback );
|
||||
this.callbacks.Subscribe<SteamUser.UpdateMachineAuthCallback>( UpdateMachineAuthCallback );
|
||||
this.callbacks.Subscribe<SteamUser.LoginKeyCallback>( LoginKeyCallback );
|
||||
|
||||
Console.Write( "Connecting to Steam3..." );
|
||||
|
||||
if ( authenticatedUser )
|
||||
{
|
||||
FileInfo fi = new FileInfo( String.Format( "{0}.sentryFile", logonDetails.Username ) );
|
||||
if ( AccountSettingsStore.Instance.SentryData != null && AccountSettingsStore.Instance.SentryData.ContainsKey( logonDetails.Username ) )
|
||||
{
|
||||
logonDetails.SentryFileHash = Util.SHAHash( AccountSettingsStore.Instance.SentryData[ logonDetails.Username ] );
|
||||
}
|
||||
else if ( fi.Exists && fi.Length > 0 )
|
||||
{
|
||||
var sentryData = File.ReadAllBytes( fi.FullName );
|
||||
logonDetails.SentryFileHash = Util.SHAHash( sentryData );
|
||||
AccountSettingsStore.Instance.SentryData[ logonDetails.Username ] = sentryData;
|
||||
AccountSettingsStore.Save();
|
||||
}
|
||||
}
|
||||
|
||||
Connect();
|
||||
}
|
||||
|
||||
public delegate bool WaitCondition();
|
||||
private object steamLock = new object();
|
||||
|
||||
public bool WaitUntilCallback( Action submitter, WaitCondition waiter )
|
||||
{
|
||||
while ( !bAborted && !waiter() )
|
||||
{
|
||||
lock (steamLock)
|
||||
{
|
||||
submitter();
|
||||
}
|
||||
|
||||
int seq = this.seq;
|
||||
do
|
||||
{
|
||||
lock (steamLock)
|
||||
{
|
||||
WaitForCallbacks();
|
||||
}
|
||||
}
|
||||
while ( !bAborted && this.seq == seq && !waiter() );
|
||||
}
|
||||
|
||||
return bAborted;
|
||||
}
|
||||
|
||||
public Credentials WaitForCredentials()
|
||||
{
|
||||
if ( credentials.IsValid || bAborted )
|
||||
return credentials;
|
||||
|
||||
WaitUntilCallback( () => { }, () => { return credentials.IsValid; } );
|
||||
|
||||
return credentials;
|
||||
}
|
||||
|
||||
public void RequestAppInfo( uint appId, bool bForce = false )
|
||||
{
|
||||
if ( ( AppInfo.ContainsKey( appId ) && !bForce ) || bAborted )
|
||||
return;
|
||||
|
||||
bool completed = false;
|
||||
Action<SteamApps.PICSTokensCallback> cbMethodTokens = ( appTokens ) =>
|
||||
{
|
||||
completed = true;
|
||||
if ( appTokens.AppTokensDenied.Contains( appId ) )
|
||||
{
|
||||
Console.WriteLine( "Insufficient privileges to get access token for app {0}", appId );
|
||||
}
|
||||
|
||||
foreach ( var token_dict in appTokens.AppTokens )
|
||||
{
|
||||
this.AppTokens[ token_dict.Key ] = token_dict.Value;
|
||||
}
|
||||
};
|
||||
|
||||
WaitUntilCallback( () =>
|
||||
{
|
||||
callbacks.Subscribe( steamApps.PICSGetAccessTokens( new List<uint>() { appId }, new List<uint>() { } ), cbMethodTokens );
|
||||
}, () => { return completed; } );
|
||||
|
||||
completed = false;
|
||||
Action<SteamApps.PICSProductInfoCallback> cbMethod = ( appInfo ) =>
|
||||
{
|
||||
completed = !appInfo.ResponsePending;
|
||||
|
||||
foreach ( var app_value in appInfo.Apps )
|
||||
{
|
||||
var app = app_value.Value;
|
||||
|
||||
Console.WriteLine( "Got AppInfo for {0}", app.ID );
|
||||
AppInfo[ app.ID ] = app;
|
||||
}
|
||||
|
||||
foreach ( var app in appInfo.UnknownApps )
|
||||
{
|
||||
AppInfo[ app ] = null;
|
||||
}
|
||||
};
|
||||
|
||||
SteamApps.PICSRequest request = new SteamApps.PICSRequest( appId );
|
||||
if ( AppTokens.ContainsKey( appId ) )
|
||||
{
|
||||
request.AccessToken = AppTokens[ appId ];
|
||||
request.Public = false;
|
||||
}
|
||||
|
||||
WaitUntilCallback( () =>
|
||||
{
|
||||
callbacks.Subscribe( steamApps.PICSGetProductInfo( new List<SteamApps.PICSRequest>() { request }, new List<SteamApps.PICSRequest>() { } ), cbMethod );
|
||||
}, () => { return completed; } );
|
||||
}
|
||||
|
||||
public void RequestPackageInfo( IEnumerable<uint> packageIds )
|
||||
{
|
||||
List<uint> packages = packageIds.ToList();
|
||||
packages.RemoveAll( pid => PackageInfo.ContainsKey( pid ) );
|
||||
|
||||
if ( packages.Count == 0 || bAborted )
|
||||
return;
|
||||
|
||||
bool completed = false;
|
||||
Action<SteamApps.PICSProductInfoCallback> cbMethod = ( packageInfo ) =>
|
||||
{
|
||||
completed = !packageInfo.ResponsePending;
|
||||
|
||||
foreach ( var package_value in packageInfo.Packages )
|
||||
{
|
||||
var package = package_value.Value;
|
||||
PackageInfo[ package.ID ] = package;
|
||||
}
|
||||
|
||||
foreach ( var package in packageInfo.UnknownPackages )
|
||||
{
|
||||
PackageInfo[package] = null;
|
||||
}
|
||||
};
|
||||
|
||||
var packageRequests = new List<SteamApps.PICSRequest>();
|
||||
|
||||
foreach ( var package in packages )
|
||||
{
|
||||
var request = new SteamApps.PICSRequest( package );
|
||||
|
||||
if ( PackageTokens.TryGetValue( package, out var token ) )
|
||||
{
|
||||
request.AccessToken = token;
|
||||
request.Public = false;
|
||||
}
|
||||
|
||||
packageRequests.Add( request );
|
||||
}
|
||||
|
||||
WaitUntilCallback( () =>
|
||||
{
|
||||
callbacks.Subscribe( steamApps.PICSGetProductInfo( new List<SteamApps.PICSRequest>(), packageRequests ), cbMethod );
|
||||
}, () => { return completed; } );
|
||||
}
|
||||
|
||||
public bool RequestFreeAppLicense( uint appId )
|
||||
{
|
||||
bool success = false;
|
||||
bool completed = false;
|
||||
Action<SteamApps.FreeLicenseCallback> cbMethod = ( resultInfo ) =>
|
||||
{
|
||||
completed = true;
|
||||
success = resultInfo.GrantedApps.Contains( appId );
|
||||
};
|
||||
|
||||
WaitUntilCallback( () =>
|
||||
{
|
||||
callbacks.Subscribe( steamApps.RequestFreeLicense( appId ), cbMethod );
|
||||
}, () => { return completed; } );
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public void RequestAppTicket( uint appId )
|
||||
{
|
||||
if ( AppTickets.ContainsKey( appId ) || bAborted )
|
||||
return;
|
||||
|
||||
|
||||
if ( !authenticatedUser )
|
||||
{
|
||||
AppTickets[ appId ] = null;
|
||||
return;
|
||||
}
|
||||
|
||||
bool completed = false;
|
||||
Action<SteamApps.AppOwnershipTicketCallback> cbMethod = ( appTicket ) =>
|
||||
{
|
||||
completed = true;
|
||||
|
||||
if ( appTicket.Result != EResult.OK )
|
||||
{
|
||||
Console.WriteLine( "Unable to get appticket for {0}: {1}", appTicket.AppID, appTicket.Result );
|
||||
Abort();
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine( "Got appticket for {0}!", appTicket.AppID );
|
||||
AppTickets[ appTicket.AppID ] = appTicket.Ticket;
|
||||
}
|
||||
};
|
||||
|
||||
WaitUntilCallback( () =>
|
||||
{
|
||||
callbacks.Subscribe( steamApps.GetAppOwnershipTicket( appId ), cbMethod );
|
||||
}, () => { return completed; } );
|
||||
}
|
||||
|
||||
public void RequestDepotKey( uint depotId, uint appid = 0 )
|
||||
{
|
||||
if ( DepotKeys.ContainsKey( depotId ) || bAborted )
|
||||
return;
|
||||
|
||||
bool completed = false;
|
||||
|
||||
Action<SteamApps.DepotKeyCallback> cbMethod = ( depotKey ) =>
|
||||
{
|
||||
completed = true;
|
||||
Console.WriteLine( "Got depot key for {0} result: {1}", depotKey.DepotID, depotKey.Result );
|
||||
|
||||
if ( depotKey.Result != EResult.OK )
|
||||
{
|
||||
Abort();
|
||||
return;
|
||||
}
|
||||
|
||||
DepotKeys[ depotKey.DepotID ] = depotKey.DepotKey;
|
||||
};
|
||||
|
||||
WaitUntilCallback( () =>
|
||||
{
|
||||
callbacks.Subscribe( steamApps.GetDepotDecryptionKey( depotId, appid ), cbMethod );
|
||||
}, () => { return completed; } );
|
||||
}
|
||||
|
||||
public string ResolveCDNTopLevelHost(string host)
|
||||
{
|
||||
// SteamPipe CDN shares tokens with all hosts
|
||||
if (host.EndsWith( ".steampipe.steamcontent.com" ) )
|
||||
{
|
||||
return "steampipe.steamcontent.com";
|
||||
}
|
||||
else if (host.EndsWith(".steamcontent.com"))
|
||||
{
|
||||
return "steamcontent.com";
|
||||
}
|
||||
|
||||
return host;
|
||||
}
|
||||
|
||||
public void RequestCDNAuthToken( uint appid, uint depotid, string host, string cdnKey )
|
||||
{
|
||||
if ( CDNAuthTokens.ContainsKey( cdnKey ) || bAborted )
|
||||
return;
|
||||
|
||||
if ( !CDNAuthTokens.TryAdd( cdnKey, new TaskCompletionSource<SteamApps.CDNAuthTokenCallback>() ) )
|
||||
return;
|
||||
|
||||
bool completed = false;
|
||||
var timeoutDate = DateTime.Now.AddSeconds( 10 );
|
||||
Action<SteamApps.CDNAuthTokenCallback> cbMethod = ( cdnAuth ) =>
|
||||
{
|
||||
completed = true;
|
||||
Console.WriteLine( "Got CDN auth token for {0} result: {1} (expires {2})", host, cdnAuth.Result, cdnAuth.Expiration );
|
||||
|
||||
if ( cdnAuth.Result != EResult.OK )
|
||||
{
|
||||
Abort();
|
||||
return;
|
||||
}
|
||||
|
||||
CDNAuthTokens[cdnKey].TrySetResult( cdnAuth );
|
||||
};
|
||||
|
||||
WaitUntilCallback( () =>
|
||||
{
|
||||
callbacks.Subscribe( steamApps.GetCDNAuthToken( appid, depotid, host ), cbMethod );
|
||||
}, () => { return completed || DateTime.Now >= timeoutDate; } );
|
||||
}
|
||||
|
||||
public void CheckAppBetaPassword( uint appid, string password )
|
||||
{
|
||||
bool completed = false;
|
||||
Action<SteamApps.CheckAppBetaPasswordCallback> cbMethod = ( appPassword ) =>
|
||||
{
|
||||
completed = true;
|
||||
|
||||
Console.WriteLine( "Retrieved {0} beta keys with result: {1}", appPassword.BetaPasswords.Count, appPassword.Result );
|
||||
|
||||
foreach ( var entry in appPassword.BetaPasswords )
|
||||
{
|
||||
AppBetaPasswords[ entry.Key ] = entry.Value;
|
||||
}
|
||||
};
|
||||
|
||||
WaitUntilCallback( () =>
|
||||
{
|
||||
callbacks.Subscribe( steamApps.CheckAppBetaPassword( appid, password ), cbMethod );
|
||||
}, () => { return completed; } );
|
||||
}
|
||||
|
||||
public CPublishedFile_GetItemInfo_Response.WorkshopItemInfo GetPubfileItemInfo( uint appId, PublishedFileID pubFile )
|
||||
{
|
||||
var pubFileRequest = new CPublishedFile_GetItemInfo_Request() { app_id = appId };
|
||||
pubFileRequest.workshop_items.Add( new CPublishedFile_GetItemInfo_Request.WorkshopItem() { published_file_id = pubFile } );
|
||||
|
||||
bool completed = false;
|
||||
CPublishedFile_GetItemInfo_Response.WorkshopItemInfo details = null;
|
||||
|
||||
Action<SteamUnifiedMessages.ServiceMethodResponse> cbMethod = callback =>
|
||||
{
|
||||
completed = true;
|
||||
if ( callback.Result == EResult.OK )
|
||||
{
|
||||
var response = callback.GetDeserializedResponse<CPublishedFile_GetItemInfo_Response>();
|
||||
details = response.workshop_items.FirstOrDefault();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception( $"EResult {(int)callback.Result} ({callback.Result}) while retrieving UGC id for pubfile {pubFile}.");
|
||||
}
|
||||
};
|
||||
|
||||
WaitUntilCallback(() =>
|
||||
{
|
||||
callbacks.Subscribe( steamPublishedFile.SendMessage( api => api.GetItemInfo( pubFileRequest ) ), cbMethod );
|
||||
}, () => { return completed; });
|
||||
|
||||
return details;
|
||||
}
|
||||
|
||||
public PublishedFileDetails GetPublishedFileDetails(uint appId, PublishedFileID pubFile)
|
||||
{
|
||||
var pubFileRequest = new CPublishedFile_GetDetails_Request() { appid = appId };
|
||||
pubFileRequest.publishedfileids.Add( pubFile );
|
||||
|
||||
bool completed = false;
|
||||
PublishedFileDetails details = null;
|
||||
|
||||
Action<SteamUnifiedMessages.ServiceMethodResponse> cbMethod = callback =>
|
||||
{
|
||||
completed = true;
|
||||
if (callback.Result == EResult.OK)
|
||||
{
|
||||
var response = callback.GetDeserializedResponse<CPublishedFile_GetDetails_Response>();
|
||||
details = response.publishedfiledetails.FirstOrDefault();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"EResult {(int)callback.Result} ({callback.Result}) while retrieving file details for pubfile {pubFile}.");
|
||||
}
|
||||
};
|
||||
|
||||
WaitUntilCallback(() =>
|
||||
{
|
||||
callbacks.Subscribe(steamPublishedFile.SendMessage(api => api.GetDetails(pubFileRequest)), cbMethod);
|
||||
}, () => { return completed; });
|
||||
|
||||
return details;
|
||||
}
|
||||
|
||||
|
||||
public SteamCloud.UGCDetailsCallback GetUGCDetails(UGCHandle ugcHandle)
|
||||
{
|
||||
bool completed = false;
|
||||
SteamCloud.UGCDetailsCallback details = null;
|
||||
|
||||
Action<SteamCloud.UGCDetailsCallback> cbMethod = callback =>
|
||||
{
|
||||
completed = true;
|
||||
if (callback.Result == EResult.OK)
|
||||
{
|
||||
details = callback;
|
||||
}
|
||||
else if (callback.Result == EResult.FileNotFound)
|
||||
{
|
||||
details = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"EResult {(int)callback.Result} ({callback.Result}) while retrieving UGC details for {ugcHandle}.");
|
||||
}
|
||||
};
|
||||
|
||||
WaitUntilCallback(() =>
|
||||
{
|
||||
callbacks.Subscribe(steamCloud.RequestUGCDetails(ugcHandle), cbMethod);
|
||||
}, () => { return completed; });
|
||||
|
||||
return details;
|
||||
}
|
||||
|
||||
void Connect()
|
||||
{
|
||||
bAborted = false;
|
||||
bConnected = false;
|
||||
bConnecting = true;
|
||||
connectionBackoff = 0;
|
||||
bExpectingDisconnectRemote = false;
|
||||
bDidDisconnect = false;
|
||||
bDidReceiveLoginKey = false;
|
||||
this.connectTime = DateTime.Now;
|
||||
this.steamClient.Connect();
|
||||
}
|
||||
|
||||
private void Abort( bool sendLogOff = true )
|
||||
{
|
||||
Disconnect( sendLogOff );
|
||||
}
|
||||
public void Disconnect( bool sendLogOff = true )
|
||||
{
|
||||
if ( sendLogOff )
|
||||
{
|
||||
steamUser.LogOff();
|
||||
}
|
||||
|
||||
steamClient.Disconnect();
|
||||
bConnected = false;
|
||||
bConnecting = false;
|
||||
bAborted = true;
|
||||
|
||||
// flush callbacks until our disconnected event
|
||||
while ( !bDidDisconnect )
|
||||
{
|
||||
callbacks.RunWaitAllCallbacks( TimeSpan.FromMilliseconds( 100 ) );
|
||||
}
|
||||
}
|
||||
|
||||
public void TryWaitForLoginKey()
|
||||
{
|
||||
if ( logonDetails.Username == null || !credentials.LoggedOn || !ContentDownloader.Config.RememberPassword ) return;
|
||||
|
||||
var totalWaitPeriod = DateTime.Now.AddSeconds( 3 );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
DateTime now = DateTime.Now;
|
||||
if ( now >= totalWaitPeriod ) break;
|
||||
|
||||
if ( bDidReceiveLoginKey ) break;
|
||||
|
||||
callbacks.RunWaitAllCallbacks( TimeSpan.FromMilliseconds( 100 ) );
|
||||
}
|
||||
}
|
||||
|
||||
private void WaitForCallbacks()
|
||||
{
|
||||
callbacks.RunWaitCallbacks( TimeSpan.FromSeconds( 1 ) );
|
||||
|
||||
TimeSpan diff = DateTime.Now - connectTime;
|
||||
|
||||
if ( diff > STEAM3_TIMEOUT && !bConnected )
|
||||
{
|
||||
Console.WriteLine( "Timeout connecting to Steam3." );
|
||||
Abort();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void ConnectedCallback( SteamClient.ConnectedCallback connected )
|
||||
{
|
||||
Console.WriteLine( " Done!" );
|
||||
bConnecting = false;
|
||||
bConnected = true;
|
||||
if ( !authenticatedUser )
|
||||
{
|
||||
Console.Write( "Logging anonymously into Steam3..." );
|
||||
steamUser.LogOnAnonymous();
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Write( "Logging '{0}' into Steam3...", logonDetails.Username );
|
||||
steamUser.LogOn( logonDetails );
|
||||
}
|
||||
}
|
||||
|
||||
private void DisconnectedCallback( SteamClient.DisconnectedCallback disconnected )
|
||||
{
|
||||
bDidDisconnect = true;
|
||||
|
||||
if ( disconnected.UserInitiated || bExpectingDisconnectRemote )
|
||||
{
|
||||
Console.WriteLine( "Disconnected from Steam" );
|
||||
}
|
||||
else if ( connectionBackoff >= 10 )
|
||||
{
|
||||
Console.WriteLine( "Could not connect to Steam after 10 tries" );
|
||||
Abort( false );
|
||||
}
|
||||
else if ( !bAborted )
|
||||
{
|
||||
if ( bConnecting )
|
||||
{
|
||||
Console.WriteLine( "Connection to Steam failed. Trying again" );
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine( "Lost connection to Steam. Reconnecting" );
|
||||
}
|
||||
|
||||
Thread.Sleep( 1000 * ++connectionBackoff );
|
||||
steamClient.Connect();
|
||||
}
|
||||
}
|
||||
|
||||
private void LogOnCallback( SteamUser.LoggedOnCallback loggedOn )
|
||||
{
|
||||
bool isSteamGuard = loggedOn.Result == EResult.AccountLogonDenied;
|
||||
bool is2FA = loggedOn.Result == EResult.AccountLoginDeniedNeedTwoFactor;
|
||||
bool isLoginKey = ContentDownloader.Config.RememberPassword && logonDetails.LoginKey != null && loggedOn.Result == EResult.InvalidPassword;
|
||||
|
||||
if ( isSteamGuard || is2FA || isLoginKey )
|
||||
{
|
||||
bExpectingDisconnectRemote = true;
|
||||
Abort( false );
|
||||
|
||||
if ( !isLoginKey )
|
||||
{
|
||||
Console.WriteLine( "This account is protected by Steam Guard." );
|
||||
}
|
||||
|
||||
if ( is2FA )
|
||||
{
|
||||
Console.Write( "Please enter your 2 factor auth code from your authenticator app: " );
|
||||
logonDetails.TwoFactorCode = Console.ReadLine();
|
||||
}
|
||||
else if ( isLoginKey )
|
||||
{
|
||||
AccountSettingsStore.Instance.LoginKeys.Remove( logonDetails.Username );
|
||||
AccountSettingsStore.Save();
|
||||
|
||||
logonDetails.LoginKey = null;
|
||||
|
||||
if ( ContentDownloader.Config.SuppliedPassword != null )
|
||||
{
|
||||
Console.WriteLine( "Login key was expired. Connecting with supplied password." );
|
||||
logonDetails.Password = ContentDownloader.Config.SuppliedPassword;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Write( "Login key was expired. Please enter your password: " );
|
||||
logonDetails.Password = Util.ReadPassword();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Write( "Please enter the authentication code sent to your email address: " );
|
||||
logonDetails.AuthCode = Console.ReadLine();
|
||||
}
|
||||
|
||||
Console.Write( "Retrying Steam3 connection..." );
|
||||
Connect();
|
||||
|
||||
return;
|
||||
}
|
||||
else if ( loggedOn.Result == EResult.ServiceUnavailable )
|
||||
{
|
||||
Console.WriteLine( "Unable to login to Steam3: {0}", loggedOn.Result );
|
||||
Abort( false );
|
||||
|
||||
return;
|
||||
}
|
||||
else if ( loggedOn.Result != EResult.OK )
|
||||
{
|
||||
Console.WriteLine( "Unable to login to Steam3: {0}", loggedOn.Result );
|
||||
Abort();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Console.WriteLine( " Done!" );
|
||||
|
||||
this.seq++;
|
||||
credentials.LoggedOn = true;
|
||||
|
||||
if ( ContentDownloader.Config.CellID == 0 )
|
||||
{
|
||||
Console.WriteLine( "Using Steam3 suggested CellID: " + loggedOn.CellID );
|
||||
ContentDownloader.Config.CellID = ( int )loggedOn.CellID;
|
||||
}
|
||||
}
|
||||
|
||||
private void SessionTokenCallback( SteamUser.SessionTokenCallback sessionToken )
|
||||
{
|
||||
Console.WriteLine( "Got session token!" );
|
||||
credentials.SessionToken = sessionToken.SessionToken;
|
||||
}
|
||||
|
||||
private void LicenseListCallback( SteamApps.LicenseListCallback licenseList )
|
||||
{
|
||||
if ( licenseList.Result != EResult.OK )
|
||||
{
|
||||
Console.WriteLine( "Unable to get license list: {0} ", licenseList.Result );
|
||||
Abort();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Console.WriteLine( "Got {0} licenses for account!", licenseList.LicenseList.Count );
|
||||
Licenses = licenseList.LicenseList;
|
||||
|
||||
foreach ( var license in licenseList.LicenseList )
|
||||
{
|
||||
if ( license.AccessToken > 0 )
|
||||
{
|
||||
PackageTokens.TryAdd( license.PackageID, license.AccessToken );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateMachineAuthCallback( SteamUser.UpdateMachineAuthCallback machineAuth )
|
||||
{
|
||||
byte[] hash = Util.SHAHash( machineAuth.Data );
|
||||
Console.WriteLine( "Got Machine Auth: {0} {1} {2} {3}", machineAuth.FileName, machineAuth.Offset, machineAuth.BytesToWrite, machineAuth.Data.Length, hash );
|
||||
|
||||
AccountSettingsStore.Instance.SentryData[ logonDetails.Username ] = machineAuth.Data;
|
||||
AccountSettingsStore.Save();
|
||||
|
||||
var authResponse = new SteamUser.MachineAuthDetails
|
||||
{
|
||||
BytesWritten = machineAuth.BytesToWrite,
|
||||
FileName = machineAuth.FileName,
|
||||
FileSize = machineAuth.BytesToWrite,
|
||||
Offset = machineAuth.Offset,
|
||||
|
||||
SentryFileHash = hash, // should be the sha1 hash of the sentry file we just wrote
|
||||
|
||||
OneTimePassword = machineAuth.OneTimePassword, // not sure on this one yet, since we've had no examples of steam using OTPs
|
||||
|
||||
LastError = 0, // result from win32 GetLastError
|
||||
Result = EResult.OK, // if everything went okay, otherwise ~who knows~
|
||||
|
||||
JobID = machineAuth.JobID, // so we respond to the correct server job
|
||||
};
|
||||
|
||||
// send off our response
|
||||
steamUser.SendMachineAuthResponse( authResponse );
|
||||
}
|
||||
|
||||
private void LoginKeyCallback( SteamUser.LoginKeyCallback loginKey )
|
||||
{
|
||||
Console.WriteLine( "Accepted new login key for account {0}", logonDetails.Username );
|
||||
|
||||
AccountSettingsStore.Instance.LoginKeys[ logonDetails.Username ] = loginKey.LoginKey;
|
||||
AccountSettingsStore.Save();
|
||||
|
||||
steamUser.AcceptNewLoginKey( loginKey );
|
||||
|
||||
bDidReceiveLoginKey = true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,170 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DepotDownloader
|
||||
{
|
||||
static class Util
|
||||
{
|
||||
public static string GetSteamOS()
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
return "windows";
|
||||
}
|
||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||
{
|
||||
return "macos";
|
||||
}
|
||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||
{
|
||||
return "linux";
|
||||
}
|
||||
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
public static string GetSteamArch()
|
||||
{
|
||||
return Environment.Is64BitOperatingSystem ? "64" : "32";
|
||||
}
|
||||
|
||||
public static string ReadPassword()
|
||||
{
|
||||
ConsoleKeyInfo keyInfo;
|
||||
StringBuilder password = new StringBuilder();
|
||||
|
||||
do
|
||||
{
|
||||
keyInfo = Console.ReadKey( true );
|
||||
|
||||
if ( keyInfo.Key == ConsoleKey.Backspace )
|
||||
{
|
||||
if ( password.Length > 0 )
|
||||
password.Remove( password.Length - 1, 1 );
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Printable ASCII characters only */
|
||||
char c = keyInfo.KeyChar;
|
||||
if ( c >= ' ' && c <= '~' )
|
||||
password.Append( c );
|
||||
} while ( keyInfo.Key != ConsoleKey.Enter );
|
||||
|
||||
return password.ToString();
|
||||
}
|
||||
|
||||
// Validate a file against Steam3 Chunk data
|
||||
public static List<ProtoManifest.ChunkData> ValidateSteam3FileChecksums(FileStream fs, ProtoManifest.ChunkData[] chunkdata)
|
||||
{
|
||||
var neededChunks = new List<ProtoManifest.ChunkData>();
|
||||
int read;
|
||||
|
||||
foreach (var data in chunkdata)
|
||||
{
|
||||
byte[] chunk = new byte[data.UncompressedLength];
|
||||
fs.Seek((long)data.Offset, SeekOrigin.Begin);
|
||||
read = fs.Read(chunk, 0, (int)data.UncompressedLength);
|
||||
|
||||
byte[] tempchunk;
|
||||
if (read < data.UncompressedLength)
|
||||
{
|
||||
tempchunk = new byte[read];
|
||||
Array.Copy(chunk, 0, tempchunk, 0, read);
|
||||
}
|
||||
else
|
||||
{
|
||||
tempchunk = chunk;
|
||||
}
|
||||
|
||||
byte[] adler = AdlerHash(tempchunk);
|
||||
if (!adler.SequenceEqual(data.Checksum))
|
||||
{
|
||||
neededChunks.Add(data);
|
||||
}
|
||||
}
|
||||
|
||||
return neededChunks;
|
||||
}
|
||||
|
||||
public static byte[] AdlerHash(byte[] input)
|
||||
{
|
||||
uint a = 0, b = 0;
|
||||
for (int i = 0; i < input.Length; i++)
|
||||
{
|
||||
a = (a + input[i]) % 65521;
|
||||
b = (b + a) % 65521;
|
||||
}
|
||||
return BitConverter.GetBytes(a | (b << 16));
|
||||
}
|
||||
|
||||
public static byte[] SHAHash( byte[] input )
|
||||
{
|
||||
using (var sha = SHA1.Create())
|
||||
{
|
||||
var output = sha.ComputeHash( input );
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] DecodeHexString( string hex )
|
||||
{
|
||||
if ( hex == null )
|
||||
return null;
|
||||
|
||||
int chars = hex.Length;
|
||||
byte[] bytes = new byte[ chars / 2 ];
|
||||
|
||||
for ( int i = 0 ; i < chars ; i += 2 )
|
||||
bytes[ i / 2 ] = Convert.ToByte( hex.Substring( i, 2 ), 16 );
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public static string EncodeHexString( byte[] input )
|
||||
{
|
||||
return input.Aggregate( new StringBuilder(),
|
||||
( sb, v ) => sb.Append( v.ToString( "x2" ) )
|
||||
).ToString();
|
||||
}
|
||||
|
||||
public static async Task InvokeAsync(IEnumerable<Func<Task>> taskFactories, int maxDegreeOfParallelism)
|
||||
{
|
||||
if (taskFactories == null) throw new ArgumentNullException(nameof(taskFactories));
|
||||
if (maxDegreeOfParallelism <= 0) throw new ArgumentException(nameof(maxDegreeOfParallelism));
|
||||
|
||||
Func<Task>[] queue = taskFactories.ToArray();
|
||||
|
||||
if (queue.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
List<Task> tasksInFlight = new List<Task>(maxDegreeOfParallelism);
|
||||
int index = 0;
|
||||
|
||||
do
|
||||
{
|
||||
while (tasksInFlight.Count < maxDegreeOfParallelism && index < queue.Length)
|
||||
{
|
||||
Func<Task> taskFactory = queue[index++];
|
||||
|
||||
tasksInFlight.Add(taskFactory());
|
||||
}
|
||||
|
||||
Task completedTask = await Task.WhenAny(tasksInFlight).ConfigureAwait(false);
|
||||
|
||||
await completedTask.ConfigureAwait(false);
|
||||
|
||||
tasksInFlight.Remove(completedTask);
|
||||
}
|
||||
while (index < queue.Length || tasksInFlight.Count != 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/>
|
||||
</startup>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="protobuf-net" publicKeyToken="257b51d87d2e4d67" culture="neutral"/>
|
||||
<bindingRedirect oldVersion="0.0.0.0-2.0.0.640" newVersion="2.0.0.640"/>
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"rollForwardOnNoCandidateFx": 2
|
||||
}
|
||||
@@ -1,339 +0,0 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
@@ -1,55 +0,0 @@
|
||||
DepotDownloader
|
||||
===============
|
||||
|
||||
Steam depot downloader utilizing the SteamKit2 library. Supports .NET Core 2.0
|
||||
|
||||
### Downloading one or all depots for an app
|
||||
```
|
||||
dotnet DepotDownloader.dll -app <id> [-depot <id> [-manifest <id>]]
|
||||
[-username <username> [-password <password>]] [other options]
|
||||
```
|
||||
|
||||
For example: `dotnet DepotDownloader.dll -app 730 -depot 731 -manifest 7617088375292372759`
|
||||
|
||||
### Downloading a workshop item using pubfile id
|
||||
```
|
||||
dotnet DepotDownloader.dll -app <id> -pubfile <id> [-username <username> [-password <password>]]
|
||||
```
|
||||
|
||||
For example: `dotnet DepotDownloader.dll -app 730 -pubfile 1885082371`
|
||||
|
||||
### Downloading a workshop item using ugc id
|
||||
```
|
||||
dotnet DepotDownloader.dll -app <id> -ugc <id> [-username <username> [-password <password>]]
|
||||
```
|
||||
|
||||
For example: `dotnet DepotDownloader.dll -app 730 -ugc 770604181014286929`
|
||||
|
||||
## Parameters
|
||||
|
||||
Parameter | Description
|
||||
--------- | -----------
|
||||
-app \<#> | the AppID to download.
|
||||
-depot \<#> | the DepotID to download.
|
||||
-manifest \<id> | manifest id of content to download (requires -depot, default: current for branch).
|
||||
-ugc \<#> | the UGC ID to download.
|
||||
-beta \<branchname> | download from specified branch if available (default: Public).
|
||||
-betapassword \<pass> | branch password if applicable.
|
||||
-all-platforms | downloads all platform-specific depots when -app is used.
|
||||
-os \<os> | the operating system for which to download the game (windows, macos or linux, default: OS the program is currently running on)
|
||||
-osarch \<arch> | the architecture for which to download the game (32 or 64, default: the host's architecture)
|
||||
-all-languages | download all language-specific depots when -app is used.
|
||||
-language \<lang> | the language for which to download the game (default: english)
|
||||
-lowviolence | download low violence depots when -app is used.
|
||||
-pubfile \<#> | the PublishedFileId to download. (Will automatically resolve to UGC id)
|
||||
-username \<user> | the username of the account to login to for restricted content.
|
||||
-password \<pass> | the password of the account to login to for restricted content.
|
||||
-remember-password | if set, remember the password for subsequent logins of this user.
|
||||
-dir \<installdir> | the directory in which to place downloaded files.
|
||||
-filelist \<file.txt> | a list of files to download (from the manifest). Can optionally use regex to download only certain files.
|
||||
-validate | Include checksum verification of files already downloaded
|
||||
-manifest-only | downloads a human readable manifest for any depots that would be downloaded.
|
||||
-cellid \<#> | the overridden CellID of the content server to download from.
|
||||
-max-servers \<#> | maximum number of content servers to use. (default: 20).
|
||||
-max-downloads \<#> | maximum number of chunks to download concurrently. (default: 8).
|
||||
-loginid \<#> | a unique 32-bit integer Steam LogonID in decimal, required if running multiple instances of DepotDownloader concurrently.
|
||||
18
eslint.config.js
Normal file
@@ -0,0 +1,18 @@
|
||||
// @ts-check
|
||||
|
||||
import eslint from '@eslint/js';
|
||||
import tseslint from 'typescript-eslint';
|
||||
|
||||
export default tseslint.config(
|
||||
{
|
||||
files: ["src/**"],
|
||||
rules: {
|
||||
"semi": ["error", "always"], // semicolons
|
||||
"indent": ["error", "tab"], // tabs indents
|
||||
"linebreak-style": ["error", "unix"],
|
||||
"quotes": ["error", "double"]
|
||||
}
|
||||
},
|
||||
eslint.configs.recommended,
|
||||
...tseslint.configs.stylistic,
|
||||
);
|
||||
31
package.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "vectum",
|
||||
"private": true,
|
||||
"version": "3.0.1",
|
||||
"type": "module",
|
||||
"license": "GPL-3.0-only",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"preview": "vite preview",
|
||||
"tauri": "tauri"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "2.2.0",
|
||||
"@tauri-apps/plugin-dialog": "2.2.0",
|
||||
"@tauri-apps/plugin-shell": "2.2.0",
|
||||
"jquery": "^3.7.1",
|
||||
"tauri-plugin-shellx-api": "^2.0.14"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.20.0",
|
||||
"@tauri-apps/cli": "2.2.7",
|
||||
"@types/eslint__js": "^8.42.3",
|
||||
"@types/jquery": "^3.5.32",
|
||||
"eslint": "^9.20.1",
|
||||
"typescript": "^5.7.3",
|
||||
"typescript-eslint": "^8.24.1",
|
||||
"vite": "^6.1.1"
|
||||
},
|
||||
"packageManager": "pnpm@10.4.1"
|
||||
}
|
||||
1709
pnpm-lock.yaml
generated
Normal file
16
src-tauri/.gitignore
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
/target/
|
||||
|
||||
# Generated by Tauri
|
||||
# will have schema files for capabilities auto-completion
|
||||
/gen/schemas
|
||||
|
||||
|
||||
# DepotDownloader
|
||||
depot/
|
||||
downloads/
|
||||
.DepotDownloader/
|
||||
Games/
|
||||
Depots/
|
||||
**/*.sh
|
||||
6016
src-tauri/Cargo.lock
generated
Normal file
43
src-tauri/Cargo.toml
Normal file
@@ -0,0 +1,43 @@
|
||||
[package]
|
||||
name = "vectum"
|
||||
version = "3.0.1"
|
||||
description = "Download older versions of Steam games with DepotDownloader"
|
||||
authors = ["mmvanheusden"]
|
||||
edition = "2021"
|
||||
license = "GPL-3.0-only"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "2.0.5", features = [] }
|
||||
|
||||
[dependencies]
|
||||
fix-path-env = { git = "https://github.com/tauri-apps/fix-path-env-rs" }
|
||||
tauri = { version = "2.2.5", features = [] }
|
||||
tauri-plugin-dialog = "2.2.0"
|
||||
serde = { version = "1.0.218", features = ["derive"] }
|
||||
serde_json = "1.0.139"
|
||||
derive-getters = "0.5.0"
|
||||
reqwest = { version = "0.12.12",features = ["blocking"] }
|
||||
zip = "2.2.2"
|
||||
tauri-plugin-shellx = "2.0.12"
|
||||
|
||||
|
||||
|
||||
# Bacon - https://dystroy.org/bacon/
|
||||
# Mold - https://github.com/rui314/mold#how-to-use
|
||||
# https://discord.com/channels/616186924390023171/731495028677148753/1254902668376150149
|
||||
[profile.dev]
|
||||
incremental = true
|
||||
opt-level = 1
|
||||
debug = 0
|
||||
|
||||
[profile.dev.package."*"]
|
||||
opt-level = 2
|
||||
|
||||
[profile.release]
|
||||
codegen-units = 1 # Allows LLVM to perform better optimization.
|
||||
lto = true # Enables link-time-optimizations.
|
||||
opt-level = 3 # Prioritizes small binary size. Use `3` if you prefer speed.
|
||||
panic = "abort" # Higher performance by disabling panic handlers.
|
||||
strip = true # Ensures debug symbols are removed.
|
||||
3
src-tauri/build.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
fn main() {
|
||||
tauri_build::build()
|
||||
}
|
||||
15
src-tauri/capabilities/default.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "../gen/schemas/desktop-schema.json",
|
||||
"identifier": "default",
|
||||
"description": "Capability for the main window",
|
||||
"windows": [
|
||||
"main"
|
||||
],
|
||||
"permissions": [
|
||||
"core:default",
|
||||
"dialog:default",
|
||||
"shellx:allow-open",
|
||||
"shellx:allow-execute",
|
||||
"shellx:allow-spawn"
|
||||
]
|
||||
}
|
||||
BIN
src-tauri/icons/128x128.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
src-tauri/icons/128x128@2x.png
Normal file
|
After Width: | Height: | Size: 6.8 KiB |
BIN
src-tauri/icons/32x32.png
Normal file
|
After Width: | Height: | Size: 974 B |
BIN
src-tauri/icons/Square107x107Logo.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
src-tauri/icons/Square142x142Logo.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
src-tauri/icons/Square150x150Logo.png
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
BIN
src-tauri/icons/Square284x284Logo.png
Normal file
|
After Width: | Height: | Size: 7.6 KiB |
BIN
src-tauri/icons/Square30x30Logo.png
Normal file
|
After Width: | Height: | Size: 903 B |
BIN
src-tauri/icons/Square310x310Logo.png
Normal file
|
After Width: | Height: | Size: 8.4 KiB |
BIN
src-tauri/icons/Square44x44Logo.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
src-tauri/icons/Square71x71Logo.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
src-tauri/icons/Square89x89Logo.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
src-tauri/icons/StoreLogo.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
src-tauri/icons/icon.icns
Normal file
BIN
src-tauri/icons/icon.ico
Normal file
|
After Width: | Height: | Size: 85 KiB |
BIN
src-tauri/icons/icon.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
112
src-tauri/src/depotdownloader.rs
Normal file
@@ -0,0 +1,112 @@
|
||||
use crate::get_os;
|
||||
use reqwest;
|
||||
use std::fs::File;
|
||||
use std::io::ErrorKind::AlreadyExists;
|
||||
use std::path::PathBuf;
|
||||
use std::{fs, io};
|
||||
use std::{io::Write, path::Path};
|
||||
|
||||
pub static DEPOTDOWNLOADER_VERSION: &str = "3.0.0";
|
||||
|
||||
|
||||
/**
|
||||
See: [`test_get_depotdownloader_url()`]
|
||||
*/
|
||||
pub fn get_depotdownloader_url() -> String {
|
||||
let arch = match std::env::consts::ARCH {
|
||||
"x86_64" => "x64",
|
||||
"aarch64" => "arm64",
|
||||
"arm" => "arm",
|
||||
_ => "x86_64",
|
||||
};
|
||||
|
||||
format!("https://github.com/SteamRE/DepotDownloader/releases/download/DepotDownloader_{}/DepotDownloader-{}-{}.zip", DEPOTDOWNLOADER_VERSION, get_os(), arch)
|
||||
}
|
||||
|
||||
/// Downloads a file. The file will be saved to the [`filename`] provided.
|
||||
pub async fn download_file(url: &str, filename: &Path) -> io::Result<()> {
|
||||
if filename.exists() {
|
||||
println!("DEBUG: Not downloading. File already exists.");
|
||||
return Err(io::Error::from(AlreadyExists));
|
||||
}
|
||||
|
||||
// Create any missing directories.
|
||||
if let Some(p) = filename.parent() {
|
||||
if !p.exists() {
|
||||
fs::create_dir_all(p)?;
|
||||
}
|
||||
}
|
||||
|
||||
let mut file = File::create(filename)?;
|
||||
let response = reqwest::get(url)
|
||||
.await
|
||||
.expect("Failed to contact internet.");
|
||||
|
||||
let content = response.bytes().await.unwrap();
|
||||
|
||||
file.write_all(&content)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Unzips DepotDownloader zips
|
||||
pub fn unzip(zip_file: &Path, working_dir: &PathBuf) -> io::Result<()> {
|
||||
let file = File::open(zip_file)?;
|
||||
let mut archive = zip::ZipArchive::new(file)?;
|
||||
|
||||
for i in 0..archive.len() {
|
||||
let mut file = archive.by_index(i)?;
|
||||
let outpath = match file.enclosed_name() {
|
||||
Some(path) => working_dir.join(path),
|
||||
None => continue,
|
||||
};
|
||||
|
||||
println!("Extracted {} from archive.", outpath.display());
|
||||
|
||||
if let Some(p) = outpath.parent() {
|
||||
if !p.exists() {
|
||||
fs::create_dir_all(p)?;
|
||||
}
|
||||
}
|
||||
let mut outfile = File::create(&outpath)?;
|
||||
io::copy(&mut file, &mut outfile)?;
|
||||
|
||||
// Copy over permissions from enclosed file to extracted file on UNIX systems.
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
// If the mode `file.unix_mode()` is something (not None), copy it over to the extracted file.
|
||||
if let Some(mode) = file.unix_mode() {
|
||||
fs::set_permissions(&outpath, fs::Permissions::from_mode(mode))?;
|
||||
}
|
||||
|
||||
// Set executable permission.
|
||||
if outpath.file_name().unwrap() == "DepotDownloader" {
|
||||
fs::set_permissions(&outpath, fs::Permissions::from_mode(0o755))?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use reqwest::blocking;
|
||||
|
||||
#[test]
|
||||
/// checks if all possible DepotDownloader URLs exist.
|
||||
fn test_get_depotdownloader_url() {
|
||||
for os in ["windows", "linux", "macos"].iter() {
|
||||
for arch in ["x64", "arm64", "arm"].iter() {
|
||||
if arch.eq(&"arm") && !os.eq(&"linux") {
|
||||
continue;
|
||||
}
|
||||
let url = format!("https://github.com/SteamRE/DepotDownloader/releases/download/DepotDownloader_{}/DepotDownloader-{}-{}.zip", DEPOTDOWNLOADER_VERSION, os, arch);
|
||||
println!("Testing DepotDownloader URL: {}", url);
|
||||
|
||||
assert!(blocking::get(url).unwrap().status().is_success());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
144
src-tauri/src/main.rs
Normal file
@@ -0,0 +1,144 @@
|
||||
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
|
||||
mod depotdownloader;
|
||||
mod steam;
|
||||
mod terminal;
|
||||
|
||||
use crate::depotdownloader::{get_depotdownloader_url, DEPOTDOWNLOADER_VERSION};
|
||||
use crate::terminal::Terminal;
|
||||
use std::env;
|
||||
use std::io::ErrorKind::AlreadyExists;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::OnceLock;
|
||||
use std::time::Duration;
|
||||
use tauri::{AppHandle, Emitter, Manager};
|
||||
use tauri_plugin_shellx::ShellExt;
|
||||
|
||||
|
||||
/// The first terminal found. Used as default terminal.
|
||||
static TERMINAL: OnceLock<Vec<Terminal>> = OnceLock::new(); // We create this variable now, and quickly populate it in preload_vectum(). we then later access the data in start_download()
|
||||
static WORKING_DIR: OnceLock<PathBuf> = OnceLock::new();
|
||||
|
||||
/// This function is called every time the app is reloaded/started. It quickly populates the [`TERMINAL`] variable with a working terminal.
|
||||
#[tauri::command]
|
||||
async fn preload_vectum(app: AppHandle) {
|
||||
// Only fill these variables once.
|
||||
if TERMINAL.get().is_none() {
|
||||
TERMINAL.set(terminal::get_installed_terminals(true, app.shell()).await).expect("Failed to set available terminals")
|
||||
}
|
||||
|
||||
if WORKING_DIR.get().is_none() {
|
||||
WORKING_DIR.set(Path::join(&app.path().local_data_dir().unwrap(), "SteamDepotDownloaderGUI")).expect("Failed to configure working directory")
|
||||
}
|
||||
|
||||
// Send the default terminal name to the frontend.
|
||||
app.emit(
|
||||
"default-terminal",
|
||||
Terminal::pretty_name(&TERMINAL.get().unwrap()[0]),
|
||||
).unwrap();
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn start_download(steam_download: steam::SteamDownload, app: AppHandle) {
|
||||
let default_terminal = TERMINAL.get().unwrap();
|
||||
let shell = app.shell();
|
||||
let terminal_to_use = if steam_download.options().terminal().is_none() { default_terminal.first().unwrap() } else { &Terminal::from_index(&steam_download.options().terminal().unwrap()).unwrap() };
|
||||
// Also change working directory
|
||||
std::env::set_current_dir(&WORKING_DIR.get().unwrap()).unwrap();
|
||||
|
||||
println!("\n-------------------------DEBUG INFO------------------------");
|
||||
println!("received these values from frontend:");
|
||||
println!("\t- Username: {}", steam_download.username().as_ref().unwrap_or(&String::from("Not provided")));
|
||||
// println!("\t- Password: {}", steam_download.password().as_ref().unwrap_or(&String::from("Not provided"))); Don't log in prod lol
|
||||
println!("\t- App ID: {}", steam_download.app_id());
|
||||
println!("\t- Depot ID: {}", steam_download.depot_id());
|
||||
println!("\t- Manifest ID: {}", steam_download.manifest_id());
|
||||
println!("\t- Output Path: {}", steam_download.output_path());
|
||||
println!("\t- Default terminal: {}", Terminal::pretty_name(&default_terminal[0]));
|
||||
println!("\t- Working directory: {}", &WORKING_DIR.get().unwrap().display());
|
||||
println!("\t- Terminal command: \n\t {:?}", terminal_to_use.create_command(&steam_download, shell, &WORKING_DIR.get().unwrap()));
|
||||
println!("----------------------------------------------------------\n");
|
||||
|
||||
terminal_to_use.create_command(&steam_download, shell, &WORKING_DIR.get().unwrap()).spawn().ok();
|
||||
}
|
||||
|
||||
/// Downloads the DepotDownloader zip file from the internet based on the OS.
|
||||
#[tauri::command]
|
||||
async fn download_depotdownloader() {
|
||||
let url = get_depotdownloader_url();
|
||||
|
||||
// Where we store the DepotDownloader zip.
|
||||
let zip_filename = format!("DepotDownloader-v{}-{}.zip", DEPOTDOWNLOADER_VERSION, env::consts::OS);
|
||||
let depotdownloader_zip = Path::join(&WORKING_DIR.get().unwrap(), Path::new(&zip_filename));
|
||||
|
||||
|
||||
if let Err(e) = depotdownloader::download_file(url.as_str(), depotdownloader_zip.as_path()).await {
|
||||
if e.kind() == AlreadyExists {
|
||||
println!("DepotDownloader already exists. Skipping download.");
|
||||
} else {
|
||||
println!("Failed to download DepotDownloader: {}", e);
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
println!("Downloaded DepotDownloader for {} to {}", env::consts::OS, depotdownloader_zip.display());
|
||||
}
|
||||
|
||||
depotdownloader::unzip(depotdownloader_zip.as_path(), &WORKING_DIR.get().unwrap()).unwrap();
|
||||
println!("Succesfully extracted DepotDownloader zip.");
|
||||
}
|
||||
|
||||
/// Checks internet connectivity using Google
|
||||
#[tauri::command]
|
||||
async fn internet_connection() -> bool {
|
||||
let client = reqwest::Client::builder().timeout(Duration::from_secs(5)).build().unwrap();
|
||||
|
||||
client.get("https://connectivitycheck.android.com/generate_204").send().await.is_ok()
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn get_all_terminals(app: AppHandle) {
|
||||
let terminals = terminal::get_installed_terminals(false, app.shell()).await;
|
||||
|
||||
terminals.iter().for_each(|terminal| {
|
||||
println!("Terminal #{} ({}) is installed!", terminal.index().unwrap(), terminal.pretty_name());
|
||||
|
||||
// Sends: (terminal index aligned with dropdown; total terminals)
|
||||
app.emit("working-terminal", (terminal.index(), Terminal::total())).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
pub fn get_os() -> &'static str {
|
||||
match env::consts::OS {
|
||||
"linux" => "linux",
|
||||
"macos" => "macos",
|
||||
"windows" => "windows",
|
||||
_ => "unknown",
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// macOS: change dir to documents because upon opening, our current dir by default is "/".
|
||||
if get_os() == "macos" {
|
||||
let _ = fix_path_env::fix(); // todo: does this actually do something useful
|
||||
// let documents_dir = format!(
|
||||
// "{}/Documents/SteamDepotDownloaderGUI",
|
||||
// std::env::var_os("HOME").unwrap().to_str().unwrap()
|
||||
// );
|
||||
// let documents_dir = Path::new(&documents_dir);
|
||||
// // println!("{}", documents_dir.display());
|
||||
|
||||
// std::fs::create_dir_all(documents_dir).unwrap();
|
||||
// env::set_current_dir(documents_dir).unwrap();
|
||||
}
|
||||
|
||||
println!();
|
||||
|
||||
tauri::Builder::default().plugin(tauri_plugin_dialog::init()).plugin(tauri_plugin_shellx::init(true)).invoke_handler(tauri::generate_handler![
|
||||
start_download,
|
||||
download_depotdownloader,
|
||||
internet_connection,
|
||||
preload_vectum,
|
||||
get_all_terminals
|
||||
]).run(tauri::generate_context!()).expect("error while running tauri application");
|
||||
}
|
||||
41
src-tauri/src/steam.rs
Normal file
@@ -0,0 +1,41 @@
|
||||
use derive_getters::Getters;
|
||||
use serde::Deserialize;
|
||||
use std::path::PathBuf;
|
||||
|
||||
|
||||
/// Represents the data required to download a Steam depot.
|
||||
#[derive(Deserialize, Debug, Getters)]
|
||||
pub struct SteamDownload {
|
||||
username: Option<String>,
|
||||
password: Option<String>,
|
||||
app_id: String,
|
||||
depot_id: String,
|
||||
manifest_id: String,
|
||||
options: VectumOptions
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Getters)]
|
||||
pub struct VectumOptions {
|
||||
terminal: Option<u8>,
|
||||
output_directory: Option<PathBuf>,
|
||||
directory_name: Option<String>
|
||||
}
|
||||
|
||||
|
||||
impl SteamDownload {
|
||||
/// If a username or password are not provided, the download is considered anonymous
|
||||
pub fn is_anonymous(&self) -> bool {
|
||||
self.username.is_none() || self.password.is_none()
|
||||
}
|
||||
|
||||
/// The directory where the download should happen
|
||||
pub fn output_path(&self) -> String {
|
||||
let sep = std::path::MAIN_SEPARATOR.to_string();
|
||||
match (&self.options.output_directory, &self.options.directory_name) {
|
||||
(Some(output_dir), Some(dir_name)) => format!("{}{}{}", output_dir.display(), sep, dir_name),
|
||||
(Some(output_dir), None) => format!("{}{}{}", output_dir.display(), sep, &self.manifest_id),
|
||||
(None, Some(dir_name)) => format!(".{}{}", sep, dir_name),
|
||||
(None, None) => format!(".{}{}", sep, &self.manifest_id)
|
||||
}
|
||||
}
|
||||
}
|
||||
309
src-tauri/src/terminal.rs
Normal file
@@ -0,0 +1,309 @@
|
||||
use crate::get_os;
|
||||
use crate::steam::SteamDownload;
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use tauri::Wry;
|
||||
use tauri_plugin_shellx::process::Command;
|
||||
use tauri_plugin_shellx::Shell;
|
||||
|
||||
/// Represents a terminal that can be used to run commands.
|
||||
/// **Should be in sync with the terminal dropdown in the frontend.**
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Terminal {
|
||||
GNOMETerminal,
|
||||
Alacritty,
|
||||
Konsole,
|
||||
GNOMEConsole,
|
||||
Xfce4Terminal,
|
||||
DeepinTerminal,
|
||||
Terminator,
|
||||
Kitty,
|
||||
LXTerminal,
|
||||
Tilix,
|
||||
XTerm,
|
||||
CMD,
|
||||
Terminal
|
||||
}
|
||||
|
||||
|
||||
impl Terminal {
|
||||
/// Iterates through each terminal
|
||||
pub fn iter() -> impl Iterator<Item=Terminal> {
|
||||
use self::Terminal::*;
|
||||
|
||||
vec![
|
||||
GNOMETerminal, Alacritty, Konsole, GNOMEConsole, Xfce4Terminal, DeepinTerminal, Terminator, Kitty, LXTerminal, Tilix, XTerm, CMD, Terminal
|
||||
].into_iter()
|
||||
}
|
||||
|
||||
/// Get terminal from index in order of the [`Terminal`] enum
|
||||
pub fn from_index(index: &u8) -> Option<Terminal> {
|
||||
Terminal::iter().nth(*index as usize)
|
||||
}
|
||||
|
||||
/// Get the index of a terminal in the order of the [`Terminal`] enum
|
||||
/// Returns `None` if the terminal is not found.
|
||||
pub fn index(&self) -> Option<u8> {
|
||||
Terminal::iter().position(|x| x == *self).map(|x| x as u8)
|
||||
}
|
||||
|
||||
|
||||
/// Get total number of terminals **possible** depending on the OS
|
||||
pub fn total() -> u8 {
|
||||
if get_os() == "windows" || get_os() == "macos" {
|
||||
return 1;
|
||||
}
|
||||
|
||||
Terminal::iter().count() as u8 - 1 // -1 because cmd is not available on linux
|
||||
}
|
||||
|
||||
/// Get the pretty name of a terminal
|
||||
pub fn pretty_name(&self) -> &str {
|
||||
match self {
|
||||
Terminal::GNOMETerminal => "GNOME Terminal",
|
||||
Terminal::GNOMEConsole => "GNOME Console",
|
||||
Terminal::Konsole => "Konsole",
|
||||
Terminal::Xfce4Terminal => "Xfce Terminal",
|
||||
Terminal::Terminator => "Terminator",
|
||||
Terminal::XTerm => "XTerm",
|
||||
Terminal::Kitty => "Kitty",
|
||||
Terminal::LXTerminal => "LXTerminal",
|
||||
Terminal::Tilix => "Tilix",
|
||||
Terminal::DeepinTerminal => "Deepin Terminal",
|
||||
Terminal::Alacritty => "Alacritty",
|
||||
Terminal::CMD => "cmd",
|
||||
Terminal::Terminal => "Terminal"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//region Probing a terminal
|
||||
/// Checks if a [`Terminal`] is installed.
|
||||
/// **See:** [`get_installed_terminals`]
|
||||
pub async fn installed(&self, shell: &Shell<Wry>) -> bool {
|
||||
match self {
|
||||
Terminal::CMD => get_os() == "windows",
|
||||
Terminal::GNOMETerminal => shell.command("gnome-terminal").arg("--version").status().await.is_ok(),
|
||||
Terminal::GNOMEConsole => shell.command("kgx").arg("--version").status().await.is_ok(),
|
||||
Terminal::Konsole => shell.command("konsole").arg("--version").status().await.is_ok(),
|
||||
Terminal::Xfce4Terminal => shell.command("xfce4-terminal").arg("--version").status().await.is_ok(),
|
||||
Terminal::Terminator => shell.command("terminator").arg("--version").status().await.is_ok(),
|
||||
Terminal::XTerm => shell.command("xterm").arg("-v").status().await.is_ok(),
|
||||
Terminal::Kitty => shell.command("kitty").arg("--version").status().await.is_ok(),
|
||||
Terminal::LXTerminal => shell.command("lxterminal").arg("--version").status().await.is_ok(),
|
||||
Terminal::Tilix => shell.command("tilix").arg("--version").status().await.is_ok(),
|
||||
Terminal::DeepinTerminal => shell.command("deepin-terminal").arg("--version").status().await.is_ok(),
|
||||
Terminal::Alacritty => shell.command("alacritty").arg("--version").status().await.is_ok(),
|
||||
Terminal::Terminal => get_os() == "macos",
|
||||
}
|
||||
}
|
||||
//endregion
|
||||
|
||||
|
||||
//region Running a command in the terminal
|
||||
/**
|
||||
Returns a [`Command`] that, when executed should open the terminal and run the command.
|
||||
|
||||
|
||||
## Commands
|
||||
`{command}` = `{command};echo Command finished with code $?;sleep infinity`
|
||||
|
||||
| Terminal | Command to open terminal |
|
||||
|------------------|--------------------------------------------------------------------------|
|
||||
| cmd | `start cmd.exe /k {command}` |
|
||||
| GNOMETerminal | `gnome-terminal -- /usr/bin/env sh -c {command}` |
|
||||
| GNOMEConsole | `kgx -e /usr/bin/env sh -c {command}` |
|
||||
| Konsole | `konsole -e /usr/bin/env sh -c {command}` |
|
||||
| Xfce4Terminal | `xfce4-terminal -x /usr/bin/env sh -c {command}` |
|
||||
| Terminator | `terminator -T "Downloading depot..." -e {command}` |
|
||||
| XTerm | `xterm -hold -T "Downloading depot..." -e /usr/bin/env sh -c {command}` |
|
||||
| Kitty | `kitty /usr/bin/env sh -c {command}` |
|
||||
| LXTerminal | `lxterminal -e /usr/bin/env sh -c {command}` |
|
||||
| Tilix | `tilix -e /usr/bin/env sh -c {command}` |
|
||||
| DeepinTerminal | `deepin-terminal -e /usr/bin/env sh -c {command}` |
|
||||
| Alacritty | `alacritty -e /usr/bin/env sh -c {command}` |
|
||||
| Terminal (macOS) | We create a bash script and run that using `open`. |
|
||||
|
||||
*/
|
||||
pub fn create_command(&self, steam_download: &SteamDownload, shell: &Shell<Wry>, working_dir: &PathBuf) -> Command {
|
||||
let command = create_depotdownloader_command(steam_download);
|
||||
|
||||
match self {
|
||||
Terminal::CMD => {
|
||||
return shell.command("cmd.exe").args(&["/c", "start", "PowerShell.exe", "-NoExit", "-Command"]).args(command);
|
||||
|
||||
/* let mut cmd = std::process::Command::new("cmd.exe");
|
||||
cmd.args(&["/c", "start", "PowerShell.exe", "-NoExit", "-Command"]).args(command);
|
||||
|
||||
return cmd*/
|
||||
}
|
||||
Terminal::GNOMETerminal => {
|
||||
shell.command("gnome-terminal")
|
||||
.args(&["--", "/usr/bin/env", "sh", "-c"])
|
||||
.args(command)
|
||||
.current_dir(working_dir.as_path())
|
||||
}
|
||||
Terminal::GNOMEConsole => {
|
||||
shell.command("kgx")
|
||||
.args(&["-e", "/usr/bin/env", "sh", "-c"])
|
||||
.args(command)
|
||||
.current_dir(working_dir.as_path())
|
||||
}
|
||||
Terminal::Konsole => {
|
||||
shell.command("konsole")
|
||||
.args(&["-e", "/usr/bin/env", "sh", "-c"])
|
||||
.args(command)
|
||||
.current_dir(working_dir.as_path())
|
||||
}
|
||||
Terminal::Xfce4Terminal => {
|
||||
shell.command("xfce4-terminal")
|
||||
.args(&["-x", "/usr/bin/env", "sh", "-c"])
|
||||
.args(command)
|
||||
.current_dir(working_dir.as_path())
|
||||
}
|
||||
Terminal::Terminator => {
|
||||
shell.command("terminator")
|
||||
.args(&["-T", "Downloading depot...", "-e"])
|
||||
.args(command)
|
||||
.current_dir(working_dir.as_path())
|
||||
}
|
||||
Terminal::XTerm => {
|
||||
shell.command("xterm")
|
||||
.args(&["-hold", "-T", "Downloading depot...", "-e", "/usr/bin/env", "sh", "-c"])
|
||||
.args(command)
|
||||
.current_dir(working_dir.as_path())
|
||||
}
|
||||
Terminal::Kitty => {
|
||||
shell.command("kitty")
|
||||
.args(&["/usr/bin/env", "sh", "-c"])
|
||||
.args(command)
|
||||
.current_dir(working_dir.as_path())
|
||||
}
|
||||
Terminal::LXTerminal => {
|
||||
shell.command("lxterminal")
|
||||
.args(&["-e", "/usr/bin/env", "sh", "-c"])
|
||||
.args(command)
|
||||
.current_dir(working_dir.as_path())
|
||||
}
|
||||
Terminal::Tilix => {
|
||||
shell.command("tilix")
|
||||
.args(&["-e", "/usr/bin/env", "sh", "-c"])
|
||||
.args(command)
|
||||
.current_dir(working_dir.as_path())
|
||||
}
|
||||
Terminal::DeepinTerminal => {
|
||||
shell.command("deepin-terminal")
|
||||
.args(&["-e", "/usr/bin/env", "sh", "-c"])
|
||||
.args(command)
|
||||
.current_dir(working_dir.as_path())
|
||||
}
|
||||
|
||||
Terminal::Alacritty => {
|
||||
shell.command("alacritty")
|
||||
.args(&["-e", "/usr/bin/env", "sh", "-c"])
|
||||
.args(command)
|
||||
.current_dir(working_dir.as_path())
|
||||
}
|
||||
Terminal::Terminal => {
|
||||
// Create a bash script and run that. Not very secure but it makes this easier.
|
||||
let download_script = format!("#!/bin/bash\ncd {}\n{}",working_dir.to_str().unwrap().replace(" ", "\\ "), command[0]);
|
||||
|
||||
fs::write("./script.sh", download_script).unwrap();
|
||||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
fs::set_permissions("./script.sh", fs::Permissions::from_mode(0o755)).unwrap(); // Won't run without executable permission
|
||||
}
|
||||
|
||||
shell.command("/usr/bin/open")
|
||||
.args(&["-a", "Terminal", "./script.sh"])
|
||||
.current_dir(working_dir.as_path())
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
//endregion
|
||||
}
|
||||
|
||||
/**
|
||||
Checks if terminals are installed by checking if they respond to commands.
|
||||
|
||||
## How it works
|
||||
Probes a list of popular terminals and checks if they return an error when calling their `--version` or similar command line flag.
|
||||
|
||||
## Options
|
||||
* `return_immediately`: [`bool`]: Return as soon as one terminal is found.
|
||||
|
||||
## Returns
|
||||
A vector containing a list of terminals that should work.
|
||||
|
||||
## Commands
|
||||
| Terminal | Command to check if installed |
|
||||
|----------------|-------------------------------|
|
||||
| cmd | `cmd /?` |
|
||||
| GNOMETerminal | `gnome-terminal --version` |
|
||||
| GNOMEConsole | `kgx --version` |
|
||||
| Konsole | `konsole --version` |
|
||||
| Xfce4Terminal | `xfce4-terminal --version` |
|
||||
| Terminator | `terminator --version` |
|
||||
| XTerm | `xterm -v` |
|
||||
| Kitty | `kitty --version` |
|
||||
| LXTerminal | `lxterminal --version` |
|
||||
| Tilix | `tilix --version` |
|
||||
| DeepinTerminal | `deepin-terminal --version` |
|
||||
| Alacritty | `alacritty --version` |
|
||||
|
||||
*/
|
||||
pub async fn get_installed_terminals(return_immediately: bool, shell: &Shell<Wry>) -> Vec<Terminal> {
|
||||
match get_os() {
|
||||
"windows" => { return vec!(Terminal::CMD); }
|
||||
"macos" => { return vec!(Terminal::Terminal); }
|
||||
_ => {}
|
||||
}
|
||||
|
||||
|
||||
let mut available_terminals: Vec<Terminal> = Vec::new();
|
||||
|
||||
for terminal in Terminal::iter() {
|
||||
// Probe terminal. If it doesn't raise an error, it is probably installed.
|
||||
if terminal.installed(shell).await {
|
||||
if return_immediately {
|
||||
return vec![terminal];
|
||||
}
|
||||
available_terminals.push(terminal);
|
||||
}
|
||||
}
|
||||
|
||||
if available_terminals.is_empty() {
|
||||
eprintln!("No terminals were detected. Try installing one.");
|
||||
}
|
||||
|
||||
available_terminals
|
||||
}
|
||||
|
||||
/// Creates the DepotDownloader command necessary to download the requested manifest.
|
||||
fn create_depotdownloader_command(steam_download: &SteamDownload) -> Vec<String> {
|
||||
let output_dir = if get_os() == "windows" {
|
||||
// In PowerShell, spaces can be escaped with a backtick.
|
||||
steam_download.output_path().replace(" ", "` ")
|
||||
} else {
|
||||
// In bash, spaces can be escaped with a backslash.
|
||||
steam_download.output_path().replace(" ", "\\ ")
|
||||
};
|
||||
|
||||
|
||||
if cfg!(not(windows)) {
|
||||
if steam_download.is_anonymous() {
|
||||
vec![format!(r#"./DepotDownloader -app {} -depot {} -manifest {} -dir {};echo Done!;sleep infinity"#, steam_download.app_id(), steam_download.depot_id(), steam_download.manifest_id(), output_dir)]
|
||||
} else {
|
||||
vec![format!(r#"./DepotDownloader -username {} -password {} -app {} -depot {} -manifest {} -dir {};echo Done!;sleep infinity"#, steam_download.username().clone().unwrap(), steam_download.password().clone().unwrap(), steam_download.app_id(), steam_download.depot_id(), steam_download.manifest_id(), output_dir)]
|
||||
}
|
||||
} else {
|
||||
if steam_download.is_anonymous() {
|
||||
vec![format!(r#".\DepotDownloader.exe -app {} -depot {} -manifest {} -dir {}"#, steam_download.app_id(), steam_download.depot_id(), steam_download.manifest_id(), output_dir)]
|
||||
} else {
|
||||
vec![format!(r#".\DepotDownloader.exe -username {} -password {} -app {} -depot {} -manifest {} -dir {}"#, steam_download.username().clone().unwrap(), steam_download.password().clone().unwrap(), steam_download.app_id(), steam_download.depot_id(), steam_download.manifest_id(), output_dir)]
|
||||
}
|
||||
}
|
||||
}
|
||||
36
src-tauri/tauri.conf.json
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"productName": "SteamDepotDownloaderGUI",
|
||||
"version": "3.0.1",
|
||||
"identifier": "net.oopium.depotdownloader",
|
||||
"build": {
|
||||
"beforeDevCommand": "pnpm dev",
|
||||
"devUrl": "http://localhost:1420",
|
||||
"beforeBuildCommand": "pnpm build",
|
||||
"frontendDist": "../dist"
|
||||
},
|
||||
"app": {
|
||||
"withGlobalTauri": true,
|
||||
"windows": [
|
||||
{
|
||||
"title": "SteamDepotDownloaderGUI",
|
||||
"width": 445,
|
||||
"height": 650,
|
||||
"resizable": false
|
||||
}
|
||||
],
|
||||
"security": {
|
||||
"csp": null
|
||||
}
|
||||
},
|
||||
"bundle": {
|
||||
"active": true,
|
||||
"targets": "all",
|
||||
"icon": [
|
||||
"icons/32x32.png",
|
||||
"icons/128x128.png",
|
||||
"icons/128x128@2x.png",
|
||||
"icons/icon.icns",
|
||||
"icons/icon.ico"
|
||||
]
|
||||
}
|
||||
}
|
||||
BIN
src/assets/Hubot-Sans.woff2
Normal file
BIN
src/assets/Windows.woff
Normal file
135
src/css/style.css
Normal file
@@ -0,0 +1,135 @@
|
||||
@font-face {
|
||||
font-family: 'Hubot Sans';
|
||||
src: url('../assets/Hubot-Sans.woff2') format('woff2 supports variations'),
|
||||
url('../assets/Hubot-Sans.woff2') format('woff2-variations');
|
||||
font-weight: 700;
|
||||
font-stretch: expanded;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Windows';
|
||||
src: url('../assets/Windows.woff') format('woff2 supports variations'),
|
||||
url('../assets/Windows.woff') format('woff2-variations');
|
||||
font-weight: 700;
|
||||
font-stretch: expanded;
|
||||
}
|
||||
|
||||
.f1-light {
|
||||
font-family: 'Hubot Sans', sans-serif;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* The grey part */
|
||||
.settings-surrounding {
|
||||
display: none;
|
||||
position: fixed;
|
||||
z-index: 1;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
background-color: rgba(0, 0, 0, 0.33);
|
||||
}
|
||||
|
||||
.settings-content {
|
||||
position: relative;
|
||||
border-radius: 10px;
|
||||
overflow: auto;
|
||||
/*noinspection CssUnresolvedCustomProperty*/
|
||||
background-color: var(--bgColor-default, var(--color-canvas-default));
|
||||
margin: 5%;
|
||||
padding: 25px;
|
||||
border: 1.5px solid white;
|
||||
width: 90vw; /* 90vw -> 90% */
|
||||
height: 90vh; /* 90vh -> 90% */
|
||||
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1), 0 6px 20px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
[data-color-mode="light"] .settings-content {
|
||||
border: 1.5px solid black;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
[data-color-mode="auto"] .settings-content {
|
||||
border: 1.5px solid black;
|
||||
}
|
||||
}
|
||||
|
||||
.hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 0;
|
||||
height: 1px;
|
||||
background: black linear-gradient(to right, #0c1016, #ccc, #0c1016);
|
||||
}
|
||||
|
||||
[data-color-mode="light"] hr {
|
||||
filter: invert(1);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
[data-color-mode="auto"] hr {
|
||||
filter: invert(1);
|
||||
}
|
||||
}
|
||||
|
||||
.version-info {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
font-size: 0.9em;
|
||||
padding: 5px 10px;
|
||||
font-family: monospace;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.AnimatedEllipsis {
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
vertical-align: bottom
|
||||
}
|
||||
|
||||
.AnimatedEllipsis::after {
|
||||
display: inline-block;
|
||||
content: "...";
|
||||
animation: AnimatedEllipsis-keyframes 1s steps(4, jump-none) infinite
|
||||
}
|
||||
|
||||
@keyframes AnimatedEllipsis-keyframes {
|
||||
0% {
|
||||
transform: translateX(-100%)
|
||||
}
|
||||
}
|
||||
|
||||
.opium-button {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
cursor: pointer;
|
||||
margin-left: 5px;
|
||||
margin-bottom: 4px;
|
||||
|
||||
border: 1px solid #000;
|
||||
background: linear-gradient(180deg, #8C8C8C 25%, #434343 75%);
|
||||
display: inline-block;
|
||||
font: 16px "Windows", monospace;
|
||||
padding: 2px 5px;
|
||||
color: darkred;
|
||||
text-decoration: none;
|
||||
|
||||
}
|
||||
|
||||
.opium-button:hover {
|
||||
cursor: zoom-in;
|
||||
background: linear-gradient(180deg, #b0b0b0 25%, #504f4f 75%);
|
||||
}
|
||||
|
||||
.opium-button:active {
|
||||
cursor: crosshair;
|
||||
border: 1px inset black;
|
||||
background: linear-gradient(180deg, #333232 25%, #504f4f 75%);
|
||||
}
|
||||
286
src/index.html
Normal file
@@ -0,0 +1,286 @@
|
||||
<!DOCTYPE html>
|
||||
<html data-color-mode="auto" data-dark-theme="dark" data-light-theme="light" id="theme" lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta content="width=device-width, initial-scale=1" name="viewport"/>
|
||||
<title>SteamDepotDownloaderGUI</title>
|
||||
<link href="https://unpkg.com/@primer/css@21.3.6/dist/primer.css" rel="stylesheet"/>
|
||||
<link href="css/style.css" rel="stylesheet">
|
||||
<script defer src="./ts/preload.ts" type="module"></script>
|
||||
<script src="./ts/main.ts" type="module"></script>
|
||||
<script src="./ts/settings.ts" type="module"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="mx-auto">
|
||||
<div>
|
||||
<div class="f1-light text-center">Steam Depot Downloader</div>
|
||||
<form id="theform">
|
||||
<div class="form-group mx-3 mt-1">
|
||||
<div class="form-group-header">
|
||||
<label for="username">Username</label>
|
||||
</div>
|
||||
<input spellcheck="false" class="form-control input-block" id="username" placeholder="Leave empty for anonymous download"
|
||||
type="text"/>
|
||||
</div>
|
||||
|
||||
<div class="form-group mx-3 mt-1">
|
||||
<div class="form-group-header">
|
||||
<label for="password">Password</label>
|
||||
</div>
|
||||
<input class="form-control input-block" id="password" placeholder="Leave empty for anonymous download"
|
||||
type="password"/>
|
||||
</div>
|
||||
|
||||
<div class="form-group mx-3 mt-1 required">
|
||||
<div class="form-group-header">
|
||||
<label for="appid">App ID</label>
|
||||
</div>
|
||||
<input class="form-control input-block" id="appid" type="number"/>
|
||||
</div>
|
||||
|
||||
<div class="form-group mx-3 mt-1 required">
|
||||
<div class="form-group-header">
|
||||
<label for="depotid">Depot ID</label>
|
||||
</div>
|
||||
<input class="form-control input-block" id="depotid" type="number"/>
|
||||
</div>
|
||||
|
||||
<div class="form-group mx-3 mt-1 required">
|
||||
<div class="form-group-header">
|
||||
<label for="manifestid">Manifest ID</label>
|
||||
</div>
|
||||
<input class="form-control input-block" id="manifestid" type="number"/>
|
||||
</div>
|
||||
|
||||
<div class="mx-3 mt-1 required">
|
||||
<div class="form-group-header">
|
||||
<label>Download Location</label>
|
||||
</div>
|
||||
<div aria-label="Pick the path/location where the game will be downloaded to."
|
||||
class="form-control btn btn-sm tooltipped tooltipped-ne" id="pickpath">
|
||||
Set location
|
||||
</div>
|
||||
|
||||
<div aria-disabled="true" aria-label="Check the location that has been selected."
|
||||
class="form-control btn btn-sm ml-2 tooltipped tooltipped-ne" id="checkpath">
|
||||
Open location
|
||||
</div>
|
||||
|
||||
<span class="Label mt-1 ml-3 Label--warning" id="busy">
|
||||
<span aria-label="Application is executing a task. Please be patient."
|
||||
class="tooltipped tooltipped-n">Busy<span class="AnimatedEllipsis"></span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div id="internet-btns">
|
||||
<div class="form-group mt-3 ml-3 mr-3">
|
||||
<div class="BtnGroup d-flex">
|
||||
<button class="BtnGroup-item btn btn-block btn-primary flex-1" id="downloadbtn">
|
||||
<svg class="octicon filter-red" height="16"
|
||||
style="display: inline-block; user-select: none; vertical-align: text-bottom;"
|
||||
viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M7.47 10.78a.75.75 0 001.06 0l3.75-3.75a.75.75 0 00-1.06-1.06L8.75 8.44V1.75a.75.75 0 00-1.5 0v6.69L4.78 5.97a.75.75 0 00-1.06 1.06l3.75 3.75zM3.75 13a.75.75 0 000 1.5h8.5a.75.75 0 000-1.5h-8.5z"
|
||||
fill-rule="evenodd"></path>
|
||||
</svg>
|
||||
Download
|
||||
</button>
|
||||
<button aria-disabled="true" class="BtnGroup-item btn flex-0" id="settings-button">
|
||||
<svg fill="#8B949E" height="16"
|
||||
style="display: inline-block; user-select: none; vertical-align: text-bottom;"
|
||||
viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M8 0a8.2 8.2 0 0 1 .701.031C9.444.095 9.99.645 10.16 1.29l.288 1.107c.018.066.079.158.212.224.231.114.454.243.668.386.123.082.233.09.299.071l1.103-.303c.644-.176 1.392.021 1.82.63.27.385.506.792.704 1.218.315.675.111 1.422-.364 1.891l-.814.806c-.049.048-.098.147-.088.294.016.257.016.515 0 .772-.01.147.038.246.088.294l.814.806c.475.469.679 1.216.364 1.891a7.977 7.977 0 0 1-.704 1.217c-.428.61-1.176.807-1.82.63l-1.102-.302c-.067-.019-.177-.011-.3.071a5.909 5.909 0 0 1-.668.386c-.133.066-.194.158-.211.224l-.29 1.106c-.168.646-.715 1.196-1.458 1.26a8.006 8.006 0 0 1-1.402 0c-.743-.064-1.289-.614-1.458-1.26l-.289-1.106c-.018-.066-.079-.158-.212-.224a5.738 5.738 0 0 1-.668-.386c-.123-.082-.233-.09-.299-.071l-1.103.303c-.644.176-1.392-.021-1.82-.63a8.12 8.12 0 0 1-.704-1.218c-.315-.675-.111-1.422.363-1.891l.815-.806c.05-.048.098-.147.088-.294a6.214 6.214 0 0 1 0-.772c.01-.147-.038-.246-.088-.294l-.815-.806C.635 6.045.431 5.298.746 4.623a7.92 7.92 0 0 1 .704-1.217c.428-.61 1.176-.807 1.82-.63l1.102.302c.067.019.177.011.3-.071.214-.143.437-.272.668-.386.133-.066.194-.158.211-.224l.29-1.106C6.009.645 6.556.095 7.299.03 7.53.01 7.764 0 8 0Zm-.571 1.525c-.036.003-.108.036-.137.146l-.289 1.105c-.147.561-.549.967-.998 1.189-.173.086-.34.183-.5.29-.417.278-.97.423-1.529.27l-1.103-.303c-.109-.03-.175.016-.195.045-.22.312-.412.644-.573.99-.014.031-.021.11.059.19l.815.806c.411.406.562.957.53 1.456a4.709 4.709 0 0 0 0 .582c.032.499-.119 1.05-.53 1.456l-.815.806c-.081.08-.073.159-.059.19.162.346.353.677.573.989.02.03.085.076.195.046l1.102-.303c.56-.153 1.113-.008 1.53.27.161.107.328.204.501.29.447.222.85.629.997 1.189l.289 1.105c.029.109.101.143.137.146a6.6 6.6 0 0 0 1.142 0c.036-.003.108-.036.137-.146l.289-1.105c.147-.561.549-.967.998-1.189.173-.086.34-.183.5-.29.417-.278.97-.423 1.529-.27l1.103.303c.109.029.175-.016.195-.045.22-.313.411-.644.573-.99.014-.031.021-.11-.059-.19l-.815-.806c-.411-.406-.562-.957-.53-1.456a4.709 4.709 0 0 0 0-.582c-.032-.499.119-1.05.53-1.456l.815-.806c.081-.08.073-.159.059-.19a6.464 6.464 0 0 0-.573-.989c-.02-.03-.085-.076-.195-.046l-1.102.303c-.56.153-1.113.008-1.53-.27a4.44 4.44 0 0 0-.501-.29c-.447-.222-.85-.629-.997-1.189l-.289-1.105c-.029-.11-.101-.143-.137-.146a6.6 6.6 0 0 0-1.142 0ZM11 8a3 3 0 1 1-6 0 3 3 0 0 1 6 0ZM9.5 8a1.5 1.5 0 1 0-3.001.001A1.5 1.5 0 0 0 9.5 8Z">
|
||||
</path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div aria-label="Join the Discord server for rapid support." class="btn btn-sm ml-3 tooltipped tooltipped-ne mb-1"
|
||||
id="smbtn1">
|
||||
|
||||
<svg fill="#8B949E" height="16" style="display: inline-block; vertical-align: text-bottom;" viewBox="0 0 16 16">
|
||||
<path d="M13.545 2.907a13.2 13.2 0 0 0-3.257-1.011.05.05 0 0 0-.052.025c-.141.25-.297.577-.406.833a12.2 12.2 0 0 0-3.658 0 8 8 0 0 0-.412-.833.05.05 0 0 0-.052-.025c-1.125.194-2.22.534-3.257 1.011a.04.04 0 0 0-.021.018C.356 6.024-.213 9.047.066 12.032q.003.022.021.037a13.3 13.3 0 0 0 3.995 2.02.05.05 0 0 0 .056-.019q.463-.63.818-1.329a.05.05 0 0 0-.01-.059l-.018-.011a9 9 0 0 1-1.248-.595.05.05 0 0 1-.02-.066l.015-.019q.127-.095.248-.195a.05.05 0 0 1 .051-.007c2.619 1.196 5.454 1.196 8.041 0a.05.05 0 0 1 .053.007q.121.1.248.195a.05.05 0 0 1-.004.085 8 8 0 0 1-1.249.594.05.05 0 0 0-.03.03.05.05 0 0 0 .003.041c.24.465.515.909.817 1.329a.05.05 0 0 0 .056.019 13.2 13.2 0 0 0 4.001-2.02.05.05 0 0 0 .021-.037c.334-3.451-.559-6.449-2.366-9.106a.03.03 0 0 0-.02-.019m-8.198 7.307c-.789 0-1.438-.724-1.438-1.612s.637-1.613 1.438-1.613c.807 0 1.45.73 1.438 1.613 0 .888-.637 1.612-1.438 1.612m5.316 0c-.788 0-1.438-.724-1.438-1.612s.637-1.613 1.438-1.613c.807 0 1.451.73 1.438 1.613 0 .888-.631 1.612-1.438 1.612"/>
|
||||
</svg>
|
||||
Discord
|
||||
</div>
|
||||
|
||||
<div aria-label="Visit the SteamDB instant search website."
|
||||
class="btn btn-sm ml-2 tooltipped tooltipped-n mb-1" id="smbtn2">
|
||||
<svg aria-hidden="true" class="octicon" fill="#8B949E" height="16" viewBox="0 0 128 128" width="16"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M63.9 0C30.5 0 3.1 11.9.1 27.1l35.6 6.7c2.9-.9 6.2-1.3 9.6-1.3l16.7-10c-.2-2.5 1.3-5.1 4.7-7.2 4.8-3.1 12.3-4.8 19.9-4.8 5.2-.1 10.5.7 15 2.2 11.2 3.8 13.7 11.1 5.7 16.3-5.1 3.3-13.3 5-21.4 4.8l-22 7.9c-.2 1.6-1.3 3.1-3.4 4.5-5.9 3.8-17.4 4.7-25.6 1.9-3.6-1.2-6-3-7-4.8L2.5 38.4c2.3 3.6 6 6.9 10.8 9.8C5 53 0 59 0 65.5c0 6.4 4.8 12.3 12.9 17.1C4.8 87.3 0 93.2 0 99.6 0 115.3 28.6 128 64 128c35.3 0 64-12.7 64-28.4 0-6.4-4.8-12.3-12.9-17 8.1-4.8 12.9-10.7 12.9-17.1 0-6.5-5-12.6-13.4-17.4 8.3-5.1 13.3-11.4 13.3-18.2 0-16.5-28.7-29.9-64-29.9zm22.8 14.2c-5.2.1-10.2 1.2-13.4 3.3-5.5 3.6-3.8 8.5 3.8 11.1 7.6 2.6 18.1 1.8 23.6-1.8s3.8-8.5-3.8-11c-3.1-1-6.7-1.5-10.2-1.5zm.3 1.7c7.4 0 13.3 2.8 13.3 6.2 0 3.4-5.9 6.2-13.3 6.2s-13.3-2.8-13.3-6.2c0-3.4 5.9-6.2 13.3-6.2zM45.3 34.4c-1.6.1-3.1.2-4.6.4l9.1 1.7a10.8 5 0 1 1-8.1 9.3l-8.9-1.7c1 .9 2.4 1.7 4.3 2.4 6.4 2.2 15.4 1.5 20-1.5s3.2-7.2-3.2-9.3c-2.6-.9-5.7-1.3-8.6-1.3zM109 51v9.3c0 11-20.2 19.9-45 19.9-24.9 0-45-8.9-45-19.9v-9.2c11.5 5.3 27.4 8.6 44.9 8.6 17.6 0 33.6-3.3 45.2-8.7zm0 34.6v8.8c0 11-20.2 19.9-45 19.9-24.9 0-45-8.9-45-19.9v-8.8c11.6 5.1 27.4 8.2 45 8.2s33.5-3.1 45-8.2z"
|
||||
fill-rule="evenodd"></path>
|
||||
</svg>
|
||||
SteamDB
|
||||
</div>
|
||||
|
||||
<div aria-label="Donate to the author of SteamDepotDownloaderGUI."
|
||||
class="btn btn-sm ml-2 tooltipped tooltipped-n mb-1" id="smbtn3">
|
||||
<svg fill="#8B949E" height="16" style="display: inline-block; vertical-align: text-bottom;"
|
||||
viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M2 2.75A2.75 2.75 0 0 1 4.75 0c.983 0 1.873.42 2.57 1.232.268.318.497.668.68 1.042.183-.375.411-.725.68-1.044C9.376.42 10.266 0 11.25 0a2.75 2.75 0 0 1 2.45 4h.55c.966 0 1.75.784 1.75 1.75v2c0 .698-.409 1.301-1 1.582v4.918A1.75 1.75 0 0 1 13.25 16H2.75A1.75 1.75 0 0 1 1 14.25V9.332C.409 9.05 0 8.448 0 7.75v-2C0 4.784.784 4 1.75 4h.55c-.192-.375-.3-.8-.3-1.25ZM7.25 9.5H2.5v4.75c0 .138.112.25.25.25h4.5Zm1.5 0v5h4.5a.25.25 0 0 0 .25-.25V9.5Zm0-4V8h5.5a.25.25 0 0 0 .25-.25v-2a.25.25 0 0 0-.25-.25Zm-7 0a.25.25 0 0 0-.25.25v2c0 .138.112.25.25.25h5.5V5.5h-5.5Zm3-4a1.25 1.25 0 0 0 0 2.5h2.309c-.233-.818-.542-1.401-.878-1.793-.43-.502-.915-.707-1.431-.707ZM8.941 4h2.309a1.25 1.25 0 0 0 0-2.5c-.516 0-1 .205-1.43.707-.337.392-.646.975-.879 1.793Z"
|
||||
fill-rule="evenodd"></path>
|
||||
</svg>
|
||||
Donate
|
||||
</div>
|
||||
|
||||
<div aria-label="View the official SteamDepotDownloaderGUI tutorials."
|
||||
class="btn btn-sm ml-2 tooltipped tooltipped-nw mb-1" id="smbtn4">
|
||||
<svg fill="#8B949E" style="display: inline-block; vertical-align: text-bottom;" height="16" width="16">
|
||||
<path d="M0 3.75C0 2.784.784 2 1.75 2h12.5c.966 0 1.75.784 1.75 1.75v8.5A1.75 1.75 0 0 1 14.25 14H1.75A1.75 1.75 0 0 1 0 12.25Zm1.75-.25a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h12.5a.25.25 0 0 0 .25-.25v-8.5a.25.25 0 0 0-.25-.25Z"></path>
|
||||
<path d="M6 10.559V5.442a.25.25 0 0 1 .379-.215l4.264 2.559a.25.25 0 0 1 0 .428l-4.264 2.559A.25.25 0 0 1 6 10.559Z"></path>
|
||||
</svg>
|
||||
Tutorial
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-2" id="warning-banners">
|
||||
<div hidden id="dotnetwarning">
|
||||
<div class="flash flash-error mx-2 mt-2 color-shadow-medium" id="dotnetalert">
|
||||
<svg class="octicon" height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zM0 8a8 8 0 1116 0A8 8 0 010 8zm6.5-.25A.75.75 0 017.25 7h1a.75.75 0 01.75.75v2.75h.25a.75.75 0 010 1.5h-2a.75.75 0 010-1.5h.25v-2h-.25a.75.75 0 01-.75-.75zM8 6a1 1 0 100-2 1 1 0 000 2z"
|
||||
fill-rule="evenodd"></path>
|
||||
</svg>
|
||||
<code><span class="text-italic">dotnet</span></code> was not found.
|
||||
<button class="btn btn-sm flash-action" id="dotnetalertbtn">
|
||||
<svg class="octicon" height="16" viewBox="0 0 16 16" width="16"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.47 10.78a.75.75 0 001.06 0l3.75-3.75a.75.75 0 00-1.06-1.06L8.75 8.44V1.75a.75.75 0 00-1.5
|
||||
0v6.69L4.78 5.97a.75.75 0 00-1.06 1.06l3.75 3.75zM3.75 13a.75.75 0 000 1.5h8.5a.75.75 0 000-1.5h-8.5z"
|
||||
fill-rule="evenodd"></path>
|
||||
</svg>
|
||||
<span class="text-bold">Download</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div hidden id="emptywarning">
|
||||
<div class="flash flash-warn mx-2 mt-2 color-shadow-medium" id="emptyalert">
|
||||
<svg class="octicon" height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zM0 8a8 8 0 1116 0A8 8 0 010 8zm6.5-.25A.75.75 0 017.25 7h1a.75.75 0 01.75.75v2.75h.25a.75.75 0 010 1.5h-2a.75.75 0 010-1.5h.25v-2h-.25a.75.75 0 01-.75-.75zM8 6a1 1 0 100-2 1 1 0 000 2z"
|
||||
fill-rule="evenodd"></path>
|
||||
</svg>
|
||||
Please fill in all required fields.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div hidden id="nopathwarning">
|
||||
<div class="flash flash-warn mx-2 mt-2 color-shadow-medium" id="emptyalert">
|
||||
<svg class="octicon" height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zM0 8a8 8 0 1116 0A8 8 0 010 8zm6.5-.25A.75.75 0 017.25 7h1a.75.75 0 01.75.75v2.75h.25a.75.75 0 010 1.5h-2a.75.75 0 010-1.5h.25v-2h-.25a.75.75 0 01-.75-.75zM8 6a1 1 0 100-2 1 1 0 000 2z"
|
||||
fill-rule="evenodd"></path>
|
||||
</svg>
|
||||
Please choose a download location.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flash mx-2 mt-2 color-shadow-medium" hidden id="downloadingnotice">
|
||||
<svg class="octicon" height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zM0 8a8 8 0 1116 0A8 8 0 010 8zm6.5-.25A.75.75 0 017.25 7h1a.75.75 0 01.75.75v2.75h.25a.75.75 0 010 1.5h-2a.75.75 0 010-1.5h.25v-2h-.25a.75.75 0 01-.75-.75zM8 6a1 1 0 100-2 1 1 0 000 2z"
|
||||
fill-rule="evenodd"></path>
|
||||
</svg>
|
||||
Downloading and extracting DepotDownloader<span class="AnimatedEllipsis"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="settings-surrounding" id="settings-surrounding">
|
||||
<div class="settings-content mx-auto" id="settings-content">
|
||||
<label class="version-info" id="version-info">UNKNOWN</label>
|
||||
<button class="opium-button" id="opium-btn">aphex</button>
|
||||
<h2><b>Settings</b></h2>
|
||||
<hr>
|
||||
<h4><b>Appearance</b></h4>
|
||||
<div class="form-group">
|
||||
<div class="form-group-header">
|
||||
<label>Theme</label>
|
||||
</div>
|
||||
<div class="form-group-body">
|
||||
<div class="BtnGroup">
|
||||
<button aria-selected="true" class="BtnGroup-item btn btn-sm" id="theme-auto" type="button">
|
||||
Auto
|
||||
</button>
|
||||
<button class="BtnGroup-item btn btn-sm" id="theme-light" type="button">
|
||||
Light
|
||||
</button>
|
||||
<button class="BtnGroup-item btn btn-sm" id="theme-dark" type="button">
|
||||
Dark
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<h4><b>Output</b></h4>
|
||||
<form>
|
||||
<div class="form-group">
|
||||
<div class="form-group-header">
|
||||
<label for="folder-name-custom-input">Game directory name</label>
|
||||
</div>
|
||||
<div class="form-group-body">
|
||||
<div class="BtnGroup">
|
||||
<button aria-selected="true" class="BtnGroup-item btn btn-sm" id="folder-name-appid"
|
||||
type="button">
|
||||
Manifest ID
|
||||
</button>
|
||||
<button class="BtnGroup-item btn btn-sm" id="folder-name-custom" type="button">
|
||||
Custom
|
||||
</button>
|
||||
</div>
|
||||
<br>
|
||||
<input class="form-control input-block mt-2" disabled
|
||||
id="folder-name-custom-input" placeholder="DepotDownloader output directory name"
|
||||
type="text">
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<h4><b>Debugging</b></h4>
|
||||
<div class="form-group">
|
||||
<div class="form-group-header">
|
||||
<label for="terminal-dropdown">Linux only: Force a terminal</label>
|
||||
</div>
|
||||
<div class="form-group-body">
|
||||
<div class="mb-2">
|
||||
<select class="form-select" id="terminal-dropdown">
|
||||
<!-- "(not installed)" part is sliced later. -->
|
||||
<option disabled>GNOME Terminal (not installed)</option>
|
||||
<option disabled>Alacritty (not installed)</option>
|
||||
<option disabled>Konsole (not installed)</option>
|
||||
<option disabled>GNOME Console (not installed)</option>
|
||||
<option disabled>Xfce Terminal (not installed)</option>
|
||||
<option disabled>Deepin Terminal (not installed)</option>
|
||||
<option disabled>Terminator (not installed)</option>
|
||||
<option disabled>Kitty (not installed)</option>
|
||||
<option disabled>LXTerminal (not installed)</option>
|
||||
<option disabled>Tilix (not installed)</option>
|
||||
<option disabled>XTerm (not installed)</option>
|
||||
<option disabled>CMD (not installed)</option>
|
||||
<option disabled>macOS Terminal (not installed)</option>
|
||||
<option selected="selected">Auto</option>
|
||||
</select>
|
||||
<br>
|
||||
found: <span class="Counter"><code id="terminals-found">none</code></span>
|
||||
<br>default: <span class="Counter"><code id="default-terminal">none</code></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 76 KiB |
223
src/ts/main.ts
Normal file
@@ -0,0 +1,223 @@
|
||||
import $ from "jquery";
|
||||
import {invoke} from "@tauri-apps/api/core";
|
||||
import {open as openDialog} from "@tauri-apps/plugin-dialog";
|
||||
import {open as openShell} from "tauri-plugin-shellx-api";
|
||||
import {listen} from "@tauri-apps/api/event";
|
||||
|
||||
function setLoader(state: boolean) {
|
||||
$("#busy").prop("hidden", !state);
|
||||
}
|
||||
|
||||
|
||||
function setLoadingState(state: boolean) {
|
||||
$("#busy").prop("hidden", !state);
|
||||
|
||||
// loop through all buttons and input fields and disable them
|
||||
for (const element of document.querySelectorAll("button, input")) {
|
||||
if (element.closest("#settings-content")) continue;
|
||||
(element as any).disabled = state;
|
||||
}
|
||||
|
||||
// These elements need additional properties to be properly disabled
|
||||
$("#pickpath").prop("ariaDisabled", state);
|
||||
$("#downloadbtn").prop("ariaDisabled", state);
|
||||
|
||||
// disable internet buttons
|
||||
for (const element of document.querySelectorAll("#internet-btns div")) {
|
||||
element.ariaDisabled = String(state);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Returns list of IDs of invalid form fields
|
||||
const invalidFields = () => {
|
||||
const form = document.forms[0];
|
||||
|
||||
const invalidFields: string[] = [];
|
||||
for (const input of form) {
|
||||
const inputElement = input as HTMLInputElement;
|
||||
const valid = !(inputElement.value === "" && inputElement?.parentElement?.classList.contains("required"));
|
||||
if (!valid) {
|
||||
invalidFields.push(inputElement.id);
|
||||
}
|
||||
}
|
||||
// console.debug(`[${invalidFields.join(", ")}] fields invalid/empty`);
|
||||
|
||||
return invalidFields;
|
||||
};
|
||||
|
||||
|
||||
$(async () => {
|
||||
let terminalsCollected = false;
|
||||
let downloadDirectory: string | null;
|
||||
|
||||
// Startup logic
|
||||
setLoadingState(true);
|
||||
|
||||
await invoke("preload_vectum");
|
||||
|
||||
setLoadingState(false);
|
||||
|
||||
|
||||
// Collect the rest of the terminals in the background.
|
||||
if (!terminalsCollected) {
|
||||
setLoader(true);
|
||||
// @ts-ignore
|
||||
const terminals = await invoke("get_all_terminals") as string[];
|
||||
for (const terminal in terminals) {
|
||||
console.log(terminal);
|
||||
}
|
||||
|
||||
// Allow opening settings now that it is ready to be shown.
|
||||
$("#settings-button").prop("ariaDisabled", false);
|
||||
terminalsCollected = true;
|
||||
setLoader(false);
|
||||
}
|
||||
|
||||
$("#pickpath").on("click", async () => {
|
||||
// Open a dialog
|
||||
downloadDirectory = await openDialog({
|
||||
title: "Choose where to save the game download.",
|
||||
multiple: false,
|
||||
directory: true,
|
||||
canCreateDirectories: true
|
||||
});
|
||||
|
||||
if (downloadDirectory == null) {
|
||||
// user cancelled
|
||||
$("#checkpath").prop("ariaDisabled", true);
|
||||
$("#checkpath").prop("disabled", true);
|
||||
return;
|
||||
}
|
||||
|
||||
$("#checkpath").prop("ariaDisabled", false);
|
||||
$("#checkpath").prop("disabled", false);
|
||||
$("#downloadbtn").prop("ariaDisabled", false);
|
||||
$("#nopathwarning").prop("hidden", true);
|
||||
|
||||
|
||||
console.log(downloadDirectory);
|
||||
});
|
||||
|
||||
$("#checkpath").on("click", async () => {
|
||||
console.log(`Checking path: ${downloadDirectory}`);
|
||||
|
||||
if (downloadDirectory != null) {
|
||||
await openShell(downloadDirectory);
|
||||
} else {
|
||||
$("#checkpath").prop("ariaDisabled", true);
|
||||
}
|
||||
});
|
||||
|
||||
$("#downloadbtn").on("click", async () => {
|
||||
console.log("download button clicked");
|
||||
|
||||
if (invalidFields().length > 0) {
|
||||
// Loop through invalid fields. If there are any, make those "errored" and block the download button.
|
||||
for (const id of invalidFields()) {
|
||||
document.getElementById(id)?.parentElement?.classList.toggle("errored", true);
|
||||
}
|
||||
$("#emptywarning").prop("hidden", false);
|
||||
$("#downloadbtn").prop("ariaDisabled", true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (downloadDirectory == null) {
|
||||
$("#nopathwarning").prop("hidden", false);
|
||||
$("#downloadbtn").prop("ariaDisabled", true);
|
||||
return;
|
||||
}
|
||||
|
||||
setLoadingState(true);
|
||||
$("#downloadingnotice").prop("hidden", false);
|
||||
$("#busy").prop("hidden", true); // Don't show the loader this time.
|
||||
|
||||
const terminalChoice = (document.getElementById("terminal-dropdown") as HTMLSelectElement).selectedIndex;
|
||||
const directoryNameChoice = $("#folder-name-custom-input").val();
|
||||
|
||||
// Output path w/ directories chosen is: {downloadDirectory}/{directoryNameChoice}
|
||||
const vectumOptions = {
|
||||
terminal: terminalChoice == 13 ? null : terminalChoice,
|
||||
output_directory: downloadDirectory || null, // if not specified let backend choose a path.
|
||||
directory_name: directoryNameChoice || null,
|
||||
};
|
||||
|
||||
const steamDownload = {
|
||||
// String || null translate to Some(String) || None
|
||||
username: String($("#username").val()).trim() || null,
|
||||
password: String($("#password").val()).trim() || null,
|
||||
app_id: $("#appid").val(),
|
||||
depot_id: $("#depotid").val(),
|
||||
manifest_id: $("#manifestid").val(),
|
||||
options: vectumOptions
|
||||
};
|
||||
|
||||
// console.debug(steamDownload);
|
||||
await invoke("download_depotdownloader");
|
||||
|
||||
$("#downloadingnotice").prop("hidden", true);
|
||||
setLoadingState(false);
|
||||
|
||||
console.debug("DepotDownloader download process completed. Starting game download...");
|
||||
|
||||
await invoke("start_download", {steamDownload: steamDownload});
|
||||
console.log("Send frontend data over to backend. Ready for next download.");
|
||||
});
|
||||
|
||||
$("#settings-button").on("click", async () => {
|
||||
if (terminalsCollected) $("#settings-surrounding").css("display", "block");
|
||||
});
|
||||
|
||||
$("#settings-surrounding").on("click", (event) => {
|
||||
if (event.target === document.getElementById("settings-surrounding")) {
|
||||
$("#settings-surrounding").css("display", "none");
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
$("#opium-btn").on("click", () => {
|
||||
openShell("https://aphex.cc");
|
||||
});
|
||||
|
||||
|
||||
document.forms[0].addEventListener("input", (event) => {
|
||||
// Remove errored class. This is a bad way to do it, but it works for now.
|
||||
const target = event.target as HTMLElement;
|
||||
target?.parentElement?.classList.toggle("errored", false);
|
||||
|
||||
// If there are no more invalid fields, hide the warning and enable the download button again
|
||||
if (invalidFields().length === 0) {
|
||||
$("#emptywarning").prop("hidden", true);
|
||||
$("#downloadbtn").prop("ariaDisabled", false);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
let a = 0;
|
||||
// Each terminal that is installed gets received from rust with this event.
|
||||
listen<[number, number]>("working-terminal", (event) => {
|
||||
a++;
|
||||
console.log(
|
||||
`Terminal #${event.payload[0]} is installed. a = ${a}`
|
||||
);
|
||||
const terminalSelection = (document.getElementById("terminal-dropdown") as HTMLSelectElement);
|
||||
|
||||
// Enable the <option> of the terminal because we know it is available. Ignore null check because we know it is valid.
|
||||
// @ts-ignore
|
||||
terminalSelection.options.item(event.payload[0]).disabled = false;
|
||||
// @ts-ignore 16
|
||||
|
||||
terminalSelection.options.item(event.payload[0]).text = terminalSelection.options.item(event.payload[0]).text.slice(0,-16);
|
||||
|
||||
$("#terminals-found").text(`${a}/${event.payload[1]}`);
|
||||
});
|
||||
|
||||
|
||||
listen<string>("default-terminal", (event) => {
|
||||
console.log(
|
||||
`Default terminal is ${event.payload}.`
|
||||
);
|
||||
|
||||
$("#default-terminal").text(event.payload);
|
||||
});
|
||||
37
src/ts/preload.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import {message} from "@tauri-apps/plugin-dialog";
|
||||
import {invoke} from "@tauri-apps/api/core";
|
||||
import {fixPathEnv, open} from "tauri-plugin-shellx-api";
|
||||
import $ from "jquery";
|
||||
|
||||
|
||||
$(async () => {
|
||||
/* Fix stupid PATH shit */
|
||||
fixPathEnv();
|
||||
|
||||
/* eslint-disable indent */
|
||||
switch (await invoke("internet_connection")) {
|
||||
case false: {
|
||||
await message("No internet connection! Can't proceed.", {
|
||||
title: "SteamDepotDownloaderGUI", kind: "error", okLabel: "Close"
|
||||
});
|
||||
}
|
||||
}
|
||||
/* eslint-enable indent */
|
||||
|
||||
//discord
|
||||
$("#smbtn1").on("click", () => {
|
||||
open("https://discord.com/invite/3qCt4DT5qe");
|
||||
});
|
||||
// steamdb
|
||||
$("#smbtn2").on("click", () => {
|
||||
open("https://steamdb.info/instantsearch");
|
||||
});
|
||||
// donate
|
||||
$("#smbtn3").on("click", () => {
|
||||
open("https://paypal.me/onderkin");
|
||||
});
|
||||
// tutorial
|
||||
$("#smbtn4").on("click", () => {
|
||||
open("https://youtube.com/playlist?list=PLRAjc5plLScj967hnsYX-I3Vjw9C1v7Ca");
|
||||
});
|
||||
});
|
||||
46
src/ts/settings.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import {getVersion} from "@tauri-apps/api/app";
|
||||
import {open} from "tauri-plugin-shellx-api";
|
||||
import $ from "jquery";
|
||||
|
||||
|
||||
$(async () => {
|
||||
$("#version-info").text(`v${await getVersion()}`);
|
||||
|
||||
$("#theme-auto").on("click", () => {
|
||||
setTheme("auto");
|
||||
});
|
||||
$("#theme-light").on("click", () => {
|
||||
setTheme("light");
|
||||
});
|
||||
$("#theme-dark").on("click", () => {
|
||||
setTheme("dark");
|
||||
});
|
||||
|
||||
$("#folder-name-appid").on("click", () => {
|
||||
$("#folder-name-custom").attr("aria-selected", "false");
|
||||
$("#folder-name-appid").attr("aria-selected", "true");
|
||||
$("#folder-name-custom-input").prop("disabled", true);
|
||||
$("#folder-name-custom-input").val("");
|
||||
});
|
||||
|
||||
// todo: fix folder-name-custom-input not disabled on untouched app state
|
||||
|
||||
$("#folder-name-custom").on("click", () => {
|
||||
$("#folder-name-appid").attr("aria-selected", "false");
|
||||
$("#folder-name-custom").attr("aria-selected", "true");
|
||||
$("#folder-name-custom-input").prop("disabled", false);
|
||||
});
|
||||
|
||||
console.log(await getVersion());
|
||||
|
||||
$("#version-info").on("click", async () => {
|
||||
await open(`https://github.com/mmvanheusden/SteamDepotDownloaderGUI/releases/v${await getVersion()}`);
|
||||
});
|
||||
});
|
||||
|
||||
function setTheme(theme: string) {
|
||||
$("#theme-auto").attr("aria-selected", String(theme === "auto"));
|
||||
$("#theme-light").attr("aria-selected", String(theme === "light"));
|
||||
$("#theme-dark").attr("aria-selected", String(theme === "dark"));
|
||||
$("#theme").attr("data-color-mode", theme);
|
||||
}
|
||||
27
tsconfig.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"module": "ESNext",
|
||||
"lib": [
|
||||
"ES2020",
|
||||
"DOM",
|
||||
"DOM.Iterable"
|
||||
],
|
||||
"skipLibCheck": true,
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
]
|
||||
}
|
||||
35
vite.config.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import {defineConfig} from "vite";
|
||||
|
||||
// @ts-expect-error process is a nodejs global
|
||||
const host = process.env.TAURI_DEV_HOST;
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig(async () => ({
|
||||
|
||||
// Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
|
||||
//
|
||||
// 1. prevent vite from obscuring rust errors
|
||||
clearScreen: false,
|
||||
// 2. tauri expects a fixed port, fail if that port is not available
|
||||
server: {
|
||||
port: 1420,
|
||||
strictPort: true,
|
||||
host: host || false,
|
||||
hmr: host
|
||||
? {
|
||||
protocol: "ws",
|
||||
host,
|
||||
port: 1421,
|
||||
}
|
||||
: undefined,
|
||||
watch: {
|
||||
// 3. tell vite to ignore watching `src-tauri`
|
||||
ignored: ["**/src-tauri/**"],
|
||||
}
|
||||
},
|
||||
root: "src",
|
||||
build: {
|
||||
outDir: '../dist',
|
||||
emptyOutDir: true,
|
||||
}
|
||||
}));
|
||||