Compare commits

...

10 Commits

95 changed files with 1868 additions and 1730 deletions

View File

@ -1,5 +1,4 @@
 [*]
[*]
charset = utf-8-bom charset = utf-8-bom
end_of_line = crlf end_of_line = crlf
trim_trailing_whitespace = false trim_trailing_whitespace = false

View File

@ -7,6 +7,9 @@
<entry key="SPTInstaller/Assets/Styles.axaml" value="SPTInstaller/SPTInstaller.csproj" /> <entry key="SPTInstaller/Assets/Styles.axaml" value="SPTInstaller/SPTInstaller.csproj" />
<entry key="SPTInstaller/CustomControls/CacheInfo.axaml" value="SPTInstaller/SPTInstaller.csproj" /> <entry key="SPTInstaller/CustomControls/CacheInfo.axaml" value="SPTInstaller/SPTInstaller.csproj" />
<entry key="SPTInstaller/CustomControls/DetailedPreCheckItem.axaml" value="SPTInstaller/SPTInstaller.csproj" /> <entry key="SPTInstaller/CustomControls/DetailedPreCheckItem.axaml" value="SPTInstaller/SPTInstaller.csproj" />
<entry key="SPTInstaller/CustomControls/Dialogs/ChangeLogDialog.axaml" value="SPTInstaller/SPTInstaller.csproj" />
<entry key="SPTInstaller/CustomControls/Dialogs/ConfirmationDialog.axaml" value="SPTInstaller/SPTInstaller.csproj" />
<entry key="SPTInstaller/CustomControls/Dialogs/MessageDialog.axaml" value="SPTInstaller/SPTInstaller.csproj" />
<entry key="SPTInstaller/CustomControls/Dialogs/WhyCacheThoughDialog.axaml" value="SPTInstaller/SPTInstaller.csproj" /> <entry key="SPTInstaller/CustomControls/Dialogs/WhyCacheThoughDialog.axaml" value="SPTInstaller/SPTInstaller.csproj" />
<entry key="SPTInstaller/CustomControls/MainInstallerButton.axaml" value="SPTInstaller/SPTInstaller.csproj" /> <entry key="SPTInstaller/CustomControls/MainInstallerButton.axaml" value="SPTInstaller/SPTInstaller.csproj" />
<entry key="SPTInstaller/CustomControls/PreCheckDetails.axaml" value="SPTInstaller/SPTInstaller.csproj" /> <entry key="SPTInstaller/CustomControls/PreCheckDetails.axaml" value="SPTInstaller/SPTInstaller.csproj" />

View File

@ -33,18 +33,20 @@
<SolidColorBrush x:Key="AKI_Brush_Lighter" Color="Gainsboro" /> <SolidColorBrush x:Key="AKI_Brush_Lighter" Color="Gainsboro" />
<!-- Path Geometry --> <!-- Path Geometry -->
<PathGeometry x:Key="CircledCheck" Figures="M 9.0646825 0.06313182 C 7.3648066 0.28806336 5.7978836 0.78839047 4.3639137 1.7461752 3.2921695 2.4620115 2.3631641 3.4084722 1.6479106 4.4762536 0.98737415 5.4623497 0.47819447 6.5896932 0.22806644 7.7524208 -1.2315929 14.537597 4.5254007 20.882361 11.493416 19.89881 c 1.391191 -0.196414 2.73334 -0.717402 3.917306 -1.463979 1.003459 -0.632768 1.91619 -1.463899 2.626322 -2.413989 C 22.172937 10.487163 19.448371 2.526326 12.903647 0.44688781 11.70918 0.06738309 10.312268 -0.10195753 9.0646825 0.06313182 M 14.235529 6.538212 c 0.719844 -0.1922804 1.369569 0.5544499 0.96037 1.2142088 -0.345429 0.5568703 -0.967577 1.0212266 -1.430447 1.4820746 L 10.94499 12.042639 C 10.500924 12.484766 9.9264114 13.323465 9.299721 13.490862 8.8023811 13.623702 8.452016 13.299829 8.1245295 12.978374 7.342478 12.210582 6.3754514 11.44552 5.7298007 10.560564 5.480503 10.218905 5.4699265 9.723192 5.7920077 9.4212362 6.6694846 8.5988409 7.8158456 10.253773 8.3595682 10.794576 c 0.1820751 0.181125 0.4825335 0.608587 0.7834627 0.52988 0.4212659 -0.110141 0.8750481 -0.777076 1.1751921 -1.075907 L 12.981992 7.5964118 C 13.331649 7.2482817 13.738346 6.6710512 14.235529 6.538212 Z" <PathGeometry x:Key="CircledCheck"
FillRule="NonZero" Figures="M 9.0646825 0.06313182 C 7.3648066 0.28806336 5.7978836 0.78839047 4.3639137 1.7461752 3.2921695 2.4620115 2.3631641 3.4084722 1.6479106 4.4762536 0.98737415 5.4623497 0.47819447 6.5896932 0.22806644 7.7524208 -1.2315929 14.537597 4.5254007 20.882361 11.493416 19.89881 c 1.391191 -0.196414 2.73334 -0.717402 3.917306 -1.463979 1.003459 -0.632768 1.91619 -1.463899 2.626322 -2.413989 C 22.172937 10.487163 19.448371 2.526326 12.903647 0.44688781 11.70918 0.06738309 10.312268 -0.10195753 9.0646825 0.06313182 M 14.235529 6.538212 c 0.719844 -0.1922804 1.369569 0.5544499 0.96037 1.2142088 -0.345429 0.5568703 -0.967577 1.0212266 -1.430447 1.4820746 L 10.94499 12.042639 C 10.500924 12.484766 9.9264114 13.323465 9.299721 13.490862 8.8023811 13.623702 8.452016 13.299829 8.1245295 12.978374 7.342478 12.210582 6.3754514 11.44552 5.7298007 10.560564 5.480503 10.218905 5.4699265 9.723192 5.7920077 9.4212362 6.6694846 8.5988409 7.8158456 10.253773 8.3595682 10.794576 c 0.1820751 0.181125 0.4825335 0.608587 0.7834627 0.52988 0.4212659 -0.110141 0.8750481 -0.777076 1.1751921 -1.075907 L 12.981992 7.5964118 C 13.331649 7.2482817 13.738346 6.6710512 14.235529 6.538212 Z"
/> FillRule="NonZero" />
<PathGeometry x:Key="CircledX" Figures="M 9.3972738 0.04245969 C 7.0827527 0.34574986 5.0318949 1.0076069 3.2592046 2.6077382 2.3324496 3.4442761 1.5788823 4.453119 1.0210803 5.566508 c -2.7620688 5.513177 0.320857 12.50987 6.3432023 14.090152 1.2916144 0.338914 2.6250608 0.43131 3.9486944 0.2576 4.747034 -0.622966 8.468465 -4.700542 8.677783 -9.470458 C 20.19922 5.6929665 16.858078 1.3284705 12.212185 0.27365016 11.325097 0.07224603 10.306294 -0.0766601 9.3972738 0.04245969 M 6.8951311 6.0962212 c 0.4071071 0.00285 0.6713562 0.2964224 0.938304 0.5628006 l 1.4856467 1.4826978 c 0.158768 0.1584535 0.4033136 0.5189835 0.6255352 0.5771984 0.126202 0.033088 0.234732 -0.1102653 0.312768 -0.1870931 0.249277 -0.2453478 0.495229 -0.4941674 0.742824 -0.7412705 l 1.094688 -1.0925142 c 0.20842 -0.2080069 0.414066 -0.4608068 0.703728 -0.554646 0.666547 -0.2158494 1.280275 0.3966607 1.063998 1.0618847 -0.141137 0.4339627 -0.633395 0.779821 -0.94671 1.092514 L 11.664841 9.54638 c -0.107944 0.1077293 -0.38877 0.3007922 -0.38877 0.46822 0 0.222015 0.475094 0.554373 0.623346 0.70233 0.595784 0.594602 1.420358 1.20083 1.871407 1.9119 0.241614 0.380976 0.158691 0.920717 -0.229532 1.171722 -0.308154 0.199227 -0.730468 0.156386 -1.016338 -0.06095 C 11.870017 13.24165 11.308677 12.546499 10.726537 11.965515 10.582664 11.821928 10.166604 11.252104 9.944617 11.31028 9.7223954 11.3685 9.4778498 11.729025 9.3190818 11.887479 l -1.4856467 1.4827 C 7.520315 13.682638 7.2187683 13.989088 6.7387474 13.917412 6.1917559 13.835786 5.9203119 13.23264 6.1340103 12.745886 6.2635359 12.450907 6.5544494 12.227488 6.7778435 12.004537 L 8.0289153 10.755949 C 8.183657 10.601515 8.691357 10.24512 8.691357 10.0146 c 0 -0.2305204 -0.5077 -0.5869139 -0.6624417 -0.7413489 L 6.7778435 8.0246649 C 6.5544494 7.8017132 6.2635359 7.5782948 6.1340103 7.2833157 5.8906767 6.7289817 6.2771407 6.091851 6.8951311 6.0962212 Z" FillRule="NonZero" <PathGeometry x:Key="CircledX"
/> Figures="M 9.3972738 0.04245969 C 7.0827527 0.34574986 5.0318949 1.0076069 3.2592046 2.6077382 2.3324496 3.4442761 1.5788823 4.453119 1.0210803 5.566508 c -2.7620688 5.513177 0.320857 12.50987 6.3432023 14.090152 1.2916144 0.338914 2.6250608 0.43131 3.9486944 0.2576 4.747034 -0.622966 8.468465 -4.700542 8.677783 -9.470458 C 20.19922 5.6929665 16.858078 1.3284705 12.212185 0.27365016 11.325097 0.07224603 10.306294 -0.0766601 9.3972738 0.04245969 M 6.8951311 6.0962212 c 0.4071071 0.00285 0.6713562 0.2964224 0.938304 0.5628006 l 1.4856467 1.4826978 c 0.158768 0.1584535 0.4033136 0.5189835 0.6255352 0.5771984 0.126202 0.033088 0.234732 -0.1102653 0.312768 -0.1870931 0.249277 -0.2453478 0.495229 -0.4941674 0.742824 -0.7412705 l 1.094688 -1.0925142 c 0.20842 -0.2080069 0.414066 -0.4608068 0.703728 -0.554646 0.666547 -0.2158494 1.280275 0.3966607 1.063998 1.0618847 -0.141137 0.4339627 -0.633395 0.779821 -0.94671 1.092514 L 11.664841 9.54638 c -0.107944 0.1077293 -0.38877 0.3007922 -0.38877 0.46822 0 0.222015 0.475094 0.554373 0.623346 0.70233 0.595784 0.594602 1.420358 1.20083 1.871407 1.9119 0.241614 0.380976 0.158691 0.920717 -0.229532 1.171722 -0.308154 0.199227 -0.730468 0.156386 -1.016338 -0.06095 C 11.870017 13.24165 11.308677 12.546499 10.726537 11.965515 10.582664 11.821928 10.166604 11.252104 9.944617 11.31028 9.7223954 11.3685 9.4778498 11.729025 9.3190818 11.887479 l -1.4856467 1.4827 C 7.520315 13.682638 7.2187683 13.989088 6.7387474 13.917412 6.1917559 13.835786 5.9203119 13.23264 6.1340103 12.745886 6.2635359 12.450907 6.5544494 12.227488 6.7778435 12.004537 L 8.0289153 10.755949 C 8.183657 10.601515 8.691357 10.24512 8.691357 10.0146 c 0 -0.2305204 -0.5077 -0.5869139 -0.6624417 -0.7413489 L 6.7778435 8.0246649 C 6.5544494 7.8017132 6.2635359 7.5782948 6.1340103 7.2833157 5.8906767 6.7289817 6.2771407 6.091851 6.8951311 6.0962212 Z"
<PathGeometry x:Key="CircledWarn" Figures="M 9.4328769 0.04019892 C 7.0982838 0.34605265 4.9864964 0.9947734 3.2144923 2.6416847 -0.51716071 6.1098902 -1.0931625 11.937378 1.9776592 16.023146 c 0.6666064 0.886919 1.4893703 1.657003 2.4101133 2.276223 0.9414784 0.633151 1.9874973 1.110834 3.0896371 1.387001 1.2547837 0.314488 2.5508664 0.396281 3.8327134 0.22809 4.776463 -0.626673 8.470809 -4.727503 8.680748 -9.510654 C 20.200809 5.6204614 16.766697 1.2560532 12.09231 0.24958776 11.252125 0.06868514 10.291794 -0.07233093 9.4328769 0.04019892 M 9.784861 4.2119583 c 0.934085 -0.1667851 1.016841 0.6682722 1.016841 1.352973 v 5.1120367 c 0 0.62605 0.190424 1.671637 -0.664858 1.821253 C 9.2424915 12.654703 9.1591106 11.781677 9.1591106 11.145248 V 6.0332096 c 0 -0.6032595 -0.201139 -1.6735876 0.6257504 -1.8212513 m 0 9.9899357 c 1.062442 -0.189692 1.447474 1.424659 0.391092 1.613298 -1.0624433 0.189692 -1.4474743 -1.424659 -0.391092 -1.613298 z" FillRule="NonZero" FillRule="NonZero" />
/> <PathGeometry x:Key="CircledWarn"
<PathGeometry x:Key="Cache" Figures="M 5.4515409 18.707476 C 2.8719183 18.411612 0.6356275 17.508046 0.12886774 16.556868 0.011421 16.336424 0 16.17685 0 14.75659 v -1.558391 l 0.20348761 0.163959 c 0.89061819 0.717606 2.90294209 1.394851 4.91765599 1.655024 1.0475327 0.135279 3.5115239 0.155131 4.4933927 0.03621 2.1668367 -0.262447 3.9985147 -0.826312 5.0629357 -1.558573 l 0.322527 -0.221877 v 1.524757 c 0 1.755973 -0.0041 1.772626 -0.56529 2.263709 -0.796666 0.697209 -2.144167 1.203544 -4.0933 1.53809 -0.5767809 0.099 -1.1020996 0.129287 -2.5440527 0.146688 -0.9994482 0.01206 -2.0550657 -0.0054 -2.3458154 -0.0387 z M 7.8601306 17.28745 c 0.0527 -0.04941 0.095816 -0.193581 0.095816 -0.320366 0 -0.405037 -0.46603 -0.61585 -0.7533032 -0.340767 -0.1321068 0.126504 -0.1782781 0.469731 -0.081588 0.606525 0.1545094 0.218594 0.5342719 0.246654 0.7390756 0.05461 z M 5.5458634 17.092351 c 0.075327 -0.0917 0.1369546 -0.21599 0.1369546 -0.276208 0 -0.203883 -0.2583299 -0.442927 -0.478668 -0.442927 -0.2881074 0 -0.4464405 0.159294 -0.4464405 0.449148 0 0.424533 0.5160004 0.601292 0.7881539 0.269987 z m 4.7625076 0.04017 c 0.07342 -0.0703 0.132157 -0.210917 0.132157 -0.316377 0 -0.105459 -0.05874 -0.24607 -0.132157 -0.316375 -0.07342 -0.0703 -0.220267 -0.126552 -0.3303979 -0.126552 -0.1101318 0 -0.2569762 0.05625 -0.3303975 0.126552 -0.073421 0.0703 -0.1321569 0.210916 -0.1321569 0.316375 0 0.10546 0.058738 0.246072 0.1321569 0.316377 0.073421 0.07031 0.2202657 0.126551 0.3303975 0.126551 0.1101309 0 0.2569759 -0.05624 0.3303979 -0.126551 z M 6.4427306 14.219282 C 3.438293 14.06843 0.69941831 13.09766 0.13645011 11.98407 0.00570426 11.725447 -0.00143606 11.622911 0.01527712 10.244516 L 0.03303424 8.7797875 0.36343157 9.0065262 C 2.6575042 10.580863 7.8536269 11.112097 11.747571 10.170399 12.962111 9.8766787 13.852599 9.520745 14.620039 9.0222575 l 0.379955 -0.2467994 v 1.4852079 c 0 0.822685 -0.02906 1.558392 -0.06514 1.649265 -0.108197 0.272498 -0.66301 0.788548 -1.121691 1.043315 -1.168844 0.64922 -3.065811 1.13473 -4.8263882 1.235267 -1.2569769 0.07178 -1.6359325 0.07636 -2.5440532 0.03076 z m 1.4159088 -1.492928 c 0.075324 -0.0917 0.1369543 -0.215989 0.1369543 -0.276207 0 -0.203883 -0.2583318 -0.442926 -0.4786675 -0.442926 -0.2881093 0 -0.4464425 0.159291 -0.4464425 0.449146 0 0.424534 0.5160019 0.601293 0.7881557 0.269987 z m -2.29146 -0.159368 c 0.1618895 -0.183199 0.1439776 -0.411835 -0.046556 -0.594283 -0.3090962 -0.29598 -0.7629143 -0.107341 -0.7629143 0.317126 0 0.426014 0.5204584 0.604217 0.8094703 0.277157 z m 4.6992386 0.06839 c 0.12628 -0.09512 0.17411 -0.18946 0.17411 -0.343424 0 -0.232297 -0.242217 -0.474564 -0.4744705 -0.474564 -0.1820319 0 -0.4506387 0.282868 -0.4506387 0.474564 0 0.191699 0.2686068 0.474567 0.4506387 0.474567 0.069437 0 0.2046015 -0.05901 0.3003605 -0.131143 z M 5.9140951 9.6877494 C 3.2886712 9.3885406 1.5607385 8.8497481 0.58264334 8.0253262 0.03362115 7.5625637 0 7.4352112 0 5.8183118 V 4.4150124 L 0.30935351 4.6411272 C 1.3355178 5.3911787 3.3497821 5.9987632 5.5506611 6.2221182 6.7548513 6.3443244 9.00242 6.3139162 10.077093 6.1608788 12.027869 5.8830787 13.636 5.3630162 14.620197 4.6916622 14.792913 4.5738469 14.949026 4.4774523 14.967113 4.4774523 14.985194 4.4774523 15 5.1050107 15 5.8720299 15 7.4791794 14.982469 7.5435731 14.411053 8.0355275 13.533871 8.7907237 11.69734 9.3913644 9.5158238 9.6365256 8.7752594 9.71975 6.4811675 9.7523775 5.9140951 9.6877506 Z M 7.7751038 8.4969125 C 8.1763294 8.227805 7.8985219 7.5555425 7.4323869 7.6675706 7.1999381 7.7234394 7.0703688 7.8802994 7.071495 8.104485 7.072745 8.3355694 7.11687 8.4143437 7.3017606 8.5147362 7.4816994 8.6124437 7.610055 8.6076106 7.7751038 8.4969175 Z M 5.5458634 8.2338012 C 5.6211901 8.1421037 5.682818 8.0178119 5.682818 7.9575956 c 0 -0.1899694 -0.2585097 -0.4429287 -0.4526467 -0.4429287 -0.2913367 0 -0.4724618 0.1632037 -0.4724618 0.425715 0 0.2931318 0.1578686 0.4601406 0.4349596 0.4601406 0.154904 0 0.2550881 -0.047287 0.3531943 -0.1667213 z m 4.7625076 0.040175 c 0.07342 -0.070305 0.132157 -0.2109162 0.132157 -0.316375 0 -0.1054612 -0.05874 -0.2460718 -0.132157 -0.3163775 -0.286005 -0.2738662 -0.7929522 -0.071601 -0.7929522 0.3163775 0 0.1054588 0.058737 0.2460694 0.1321568 0.316375 0.073421 0.070304 0.2202657 0.1265519 0.3303975 0.1265519 0.1101309 0 0.2569759 -0.056244 0.3303979 -0.1265519 z M 6.178414 5.3866444 C 3.8630228 5.2033161 2.0080132 4.6795372 0.82192238 3.8741859 0.26331487 3.4948944 0.03303924 3.1533125 0.03303924 2.7039893 c 0 -0.2939821 0.03374352 -0.3937029 0.21156697 -0.6252455 C 1.4428273 0.518552 5.8324898 -0.36375235 9.8724944 0.14357696 12.82408 0.51422624 14.999999 1.6015126 14.999999 2.7057413 c 0 1.1212199 -2.227472 2.2145851 -5.2863421 2.5948303 C 9.1252344 5.3737159 6.74658 5.4316311 6.178414 5.3866444 Z" FillRule="NonZero" Figures="M 9.4328769 0.04019892 C 7.0982838 0.34605265 4.9864964 0.9947734 3.2144923 2.6416847 -0.51716071 6.1098902 -1.0931625 11.937378 1.9776592 16.023146 c 0.6666064 0.886919 1.4893703 1.657003 2.4101133 2.276223 0.9414784 0.633151 1.9874973 1.110834 3.0896371 1.387001 1.2547837 0.314488 2.5508664 0.396281 3.8327134 0.22809 4.776463 -0.626673 8.470809 -4.727503 8.680748 -9.510654 C 20.200809 5.6204614 16.766697 1.2560532 12.09231 0.24958776 11.252125 0.06868514 10.291794 -0.07233093 9.4328769 0.04019892 M 9.784861 4.2119583 c 0.934085 -0.1667851 1.016841 0.6682722 1.016841 1.352973 v 5.1120367 c 0 0.62605 0.190424 1.671637 -0.664858 1.821253 C 9.2424915 12.654703 9.1591106 11.781677 9.1591106 11.145248 V 6.0332096 c 0 -0.6032595 -0.201139 -1.6735876 0.6257504 -1.8212513 m 0 9.9899357 c 1.062442 -0.189692 1.447474 1.424659 0.391092 1.613298 -1.0624433 0.189692 -1.4474743 -1.424659 -0.391092 -1.613298 z"
/> FillRule="NonZero" />
<PathGeometry x:Key="Cache"
Figures="M 5.4515409 18.707476 C 2.8719183 18.411612 0.6356275 17.508046 0.12886774 16.556868 0.011421 16.336424 0 16.17685 0 14.75659 v -1.558391 l 0.20348761 0.163959 c 0.89061819 0.717606 2.90294209 1.394851 4.91765599 1.655024 1.0475327 0.135279 3.5115239 0.155131 4.4933927 0.03621 2.1668367 -0.262447 3.9985147 -0.826312 5.0629357 -1.558573 l 0.322527 -0.221877 v 1.524757 c 0 1.755973 -0.0041 1.772626 -0.56529 2.263709 -0.796666 0.697209 -2.144167 1.203544 -4.0933 1.53809 -0.5767809 0.099 -1.1020996 0.129287 -2.5440527 0.146688 -0.9994482 0.01206 -2.0550657 -0.0054 -2.3458154 -0.0387 z M 7.8601306 17.28745 c 0.0527 -0.04941 0.095816 -0.193581 0.095816 -0.320366 0 -0.405037 -0.46603 -0.61585 -0.7533032 -0.340767 -0.1321068 0.126504 -0.1782781 0.469731 -0.081588 0.606525 0.1545094 0.218594 0.5342719 0.246654 0.7390756 0.05461 z M 5.5458634 17.092351 c 0.075327 -0.0917 0.1369546 -0.21599 0.1369546 -0.276208 0 -0.203883 -0.2583299 -0.442927 -0.478668 -0.442927 -0.2881074 0 -0.4464405 0.159294 -0.4464405 0.449148 0 0.424533 0.5160004 0.601292 0.7881539 0.269987 z m 4.7625076 0.04017 c 0.07342 -0.0703 0.132157 -0.210917 0.132157 -0.316377 0 -0.105459 -0.05874 -0.24607 -0.132157 -0.316375 -0.07342 -0.0703 -0.220267 -0.126552 -0.3303979 -0.126552 -0.1101318 0 -0.2569762 0.05625 -0.3303975 0.126552 -0.073421 0.0703 -0.1321569 0.210916 -0.1321569 0.316375 0 0.10546 0.058738 0.246072 0.1321569 0.316377 0.073421 0.07031 0.2202657 0.126551 0.3303975 0.126551 0.1101309 0 0.2569759 -0.05624 0.3303979 -0.126551 z M 6.4427306 14.219282 C 3.438293 14.06843 0.69941831 13.09766 0.13645011 11.98407 0.00570426 11.725447 -0.00143606 11.622911 0.01527712 10.244516 L 0.03303424 8.7797875 0.36343157 9.0065262 C 2.6575042 10.580863 7.8536269 11.112097 11.747571 10.170399 12.962111 9.8766787 13.852599 9.520745 14.620039 9.0222575 l 0.379955 -0.2467994 v 1.4852079 c 0 0.822685 -0.02906 1.558392 -0.06514 1.649265 -0.108197 0.272498 -0.66301 0.788548 -1.121691 1.043315 -1.168844 0.64922 -3.065811 1.13473 -4.8263882 1.235267 -1.2569769 0.07178 -1.6359325 0.07636 -2.5440532 0.03076 z m 1.4159088 -1.492928 c 0.075324 -0.0917 0.1369543 -0.215989 0.1369543 -0.276207 0 -0.203883 -0.2583318 -0.442926 -0.4786675 -0.442926 -0.2881093 0 -0.4464425 0.159291 -0.4464425 0.449146 0 0.424534 0.5160019 0.601293 0.7881557 0.269987 z m -2.29146 -0.159368 c 0.1618895 -0.183199 0.1439776 -0.411835 -0.046556 -0.594283 -0.3090962 -0.29598 -0.7629143 -0.107341 -0.7629143 0.317126 0 0.426014 0.5204584 0.604217 0.8094703 0.277157 z m 4.6992386 0.06839 c 0.12628 -0.09512 0.17411 -0.18946 0.17411 -0.343424 0 -0.232297 -0.242217 -0.474564 -0.4744705 -0.474564 -0.1820319 0 -0.4506387 0.282868 -0.4506387 0.474564 0 0.191699 0.2686068 0.474567 0.4506387 0.474567 0.069437 0 0.2046015 -0.05901 0.3003605 -0.131143 z M 5.9140951 9.6877494 C 3.2886712 9.3885406 1.5607385 8.8497481 0.58264334 8.0253262 0.03362115 7.5625637 0 7.4352112 0 5.8183118 V 4.4150124 L 0.30935351 4.6411272 C 1.3355178 5.3911787 3.3497821 5.9987632 5.5506611 6.2221182 6.7548513 6.3443244 9.00242 6.3139162 10.077093 6.1608788 12.027869 5.8830787 13.636 5.3630162 14.620197 4.6916622 14.792913 4.5738469 14.949026 4.4774523 14.967113 4.4774523 14.985194 4.4774523 15 5.1050107 15 5.8720299 15 7.4791794 14.982469 7.5435731 14.411053 8.0355275 13.533871 8.7907237 11.69734 9.3913644 9.5158238 9.6365256 8.7752594 9.71975 6.4811675 9.7523775 5.9140951 9.6877506 Z M 7.7751038 8.4969125 C 8.1763294 8.227805 7.8985219 7.5555425 7.4323869 7.6675706 7.1999381 7.7234394 7.0703688 7.8802994 7.071495 8.104485 7.072745 8.3355694 7.11687 8.4143437 7.3017606 8.5147362 7.4816994 8.6124437 7.610055 8.6076106 7.7751038 8.4969175 Z M 5.5458634 8.2338012 C 5.6211901 8.1421037 5.682818 8.0178119 5.682818 7.9575956 c 0 -0.1899694 -0.2585097 -0.4429287 -0.4526467 -0.4429287 -0.2913367 0 -0.4724618 0.1632037 -0.4724618 0.425715 0 0.2931318 0.1578686 0.4601406 0.4349596 0.4601406 0.154904 0 0.2550881 -0.047287 0.3531943 -0.1667213 z m 4.7625076 0.040175 c 0.07342 -0.070305 0.132157 -0.2109162 0.132157 -0.316375 0 -0.1054612 -0.05874 -0.2460718 -0.132157 -0.3163775 -0.286005 -0.2738662 -0.7929522 -0.071601 -0.7929522 0.3163775 0 0.1054588 0.058737 0.2460694 0.1321568 0.316375 0.073421 0.070304 0.2202657 0.1265519 0.3303975 0.1265519 0.1101309 0 0.2569759 -0.056244 0.3303979 -0.1265519 z M 6.178414 5.3866444 C 3.8630228 5.2033161 2.0080132 4.6795372 0.82192238 3.8741859 0.26331487 3.4948944 0.03303924 3.1533125 0.03303924 2.7039893 c 0 -0.2939821 0.03374352 -0.3937029 0.21156697 -0.6252455 C 1.4428273 0.518552 5.8324898 -0.36375235 9.8724944 0.14357696 12.82408 0.51422624 14.999999 1.6015126 14.999999 2.7057413 c 0 1.1212199 -2.227472 2.2145851 -5.2863421 2.5948303 C 9.1252344 5.3737159 6.74658 5.4316311 6.178414 5.3866444 Z"
FillRule="NonZero" />
<PathGeometry x:Key="Bug" <PathGeometry x:Key="Bug"
Figures="m 12.25 0 a 0.75 0.75 0 0 1 0.743 0.648 L 13 0.75 v 0.752 c 0 0.633 -0.196 1.22 -0.53 1.704 a 3.75 3.75 0 0 1 2.521 3.29 h 0.256 a 2.25 2.25 0 0 0 2.24 -2.259 L 17.481 2.752 a 0.750006 0.750006 0 0 1 1.5 -0.006 l 0.007 1.485 a 3.75 3.75 0 0 1 -3.536 3.76 L 15.238 7.997 L 15 7.996 v 1.502 h 4.253 a 0.75 0.75 0 0 1 0.743 0.649 l 0.007 0.102 a 0.75 0.75 0 0 1 -0.648 0.743 l -0.102 0.007 H 15 v 1.999 h 0.238 l 0.214 0.007 a 3.75 3.75 0 0 1 3.531 3.56 l 0.005 0.2 l -0.007 1.485 a 0.75 0.75 0 0 1 -1.493 0.095 l -0.007 -0.102 l 0.007 -1.485 a 2.25 2.25 0 0 0 -2.087 -2.253 l -0.154 -0.006 h -0.476 a 5.002 5.002 0 0 1 -9.542 0 H 4.74 A 2.25 2.25 0 0 0 2.5 16.758 l 0.005 1.485 a 0.750008 0.750008 0 1 1 -1.5 0.007 L 1 16.764 a 3.75 3.75 0 0 1 3.535 -3.76 L 4.75 12.999 L 5 12.998 v -2 H 0.75 A 0.75 0.75 0 0 1 0.007 10.35 L 0 10.249 A 0.75 0.75 0 0 1 0.648 9.506 L 0.75 9.499 L 5 9.498 V 7.996 H 4.75 L 4.535 7.991 A 3.75 3.75 0 0 1 1.005 4.431 L 1 4.23 L 1.006 2.745 A 0.75 0.75 0 0 1 2.5 2.649 L 2.506 2.751 L 2.5 4.237 A 2.25 2.25 0 0 0 4.587 6.491 L 4.741 6.497 H 5.009 A 3.753 3.753 0 0 1 7.53 3.205 A 2.968 2.968 0 0 1 7.006 1.711 L 7 1.502 V 0.75 A 0.75 0.75 0 0 1 8.493 0.648 L 8.5 0.75 v 0.752 a 1.5 1.5 0 0 0 2.993 0.145 L 11.5 1.502 V 0.75 A 0.75 0.75 0 0 1 12.25 0 Z" Figures="m 12.25 0 a 0.75 0.75 0 0 1 0.743 0.648 L 13 0.75 v 0.752 c 0 0.633 -0.196 1.22 -0.53 1.704 a 3.75 3.75 0 0 1 2.521 3.29 h 0.256 a 2.25 2.25 0 0 0 2.24 -2.259 L 17.481 2.752 a 0.750006 0.750006 0 0 1 1.5 -0.006 l 0.007 1.485 a 3.75 3.75 0 0 1 -3.536 3.76 L 15.238 7.997 L 15 7.996 v 1.502 h 4.253 a 0.75 0.75 0 0 1 0.743 0.649 l 0.007 0.102 a 0.75 0.75 0 0 1 -0.648 0.743 l -0.102 0.007 H 15 v 1.999 h 0.238 l 0.214 0.007 a 3.75 3.75 0 0 1 3.531 3.56 l 0.005 0.2 l -0.007 1.485 a 0.75 0.75 0 0 1 -1.493 0.095 l -0.007 -0.102 l 0.007 -1.485 a 2.25 2.25 0 0 0 -2.087 -2.253 l -0.154 -0.006 h -0.476 a 5.002 5.002 0 0 1 -9.542 0 H 4.74 A 2.25 2.25 0 0 0 2.5 16.758 l 0.005 1.485 a 0.750008 0.750008 0 1 1 -1.5 0.007 L 1 16.764 a 3.75 3.75 0 0 1 3.535 -3.76 L 4.75 12.999 L 5 12.998 v -2 H 0.75 A 0.75 0.75 0 0 1 0.007 10.35 L 0 10.249 A 0.75 0.75 0 0 1 0.648 9.506 L 0.75 9.499 L 5 9.498 V 7.996 H 4.75 L 4.535 7.991 A 3.75 3.75 0 0 1 1.005 4.431 L 1 4.23 L 1.006 2.745 A 0.75 0.75 0 0 1 2.5 2.649 L 2.506 2.751 L 2.5 4.237 A 2.25 2.25 0 0 0 4.587 6.491 L 4.741 6.497 H 5.009 A 3.753 3.753 0 0 1 7.53 3.205 A 2.968 2.968 0 0 1 7.006 1.711 L 7 1.502 V 0.75 A 0.75 0.75 0 0 1 8.493 0.648 L 8.5 0.75 v 0.752 a 1.5 1.5 0 0 0 2.993 0.145 L 11.5 1.502 V 0.75 A 0.75 0.75 0 0 1 12.25 0 Z"
FillRule="NonZero" FillRule="NonZero" />
/>
</Application.Resources> </Application.Resources>
</Application> </Application>

View File

@ -13,6 +13,7 @@ namespace SPTInstaller;
public partial class App : Application public partial class App : Application
{ {
private readonly string _logPath = Path.Join(Environment.CurrentDirectory, "spt-aki-installer_.log"); private readonly string _logPath = Path.Join(Environment.CurrentDirectory, "spt-aki-installer_.log");
public override void Initialize() public override void Initialize()
{ {
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);
@ -29,7 +30,6 @@ public partial class App : Application
{ {
Log.Error(exception, "An application exception occurred"); Log.Error(exception, "An application exception occurred");
}); });
} }
public override void OnFrameworkInitializationCompleted() public override void OnFrameworkInitializationCompleted()

View File

@ -1,13 +1,11 @@
<Styles xmlns="https://github.com/avaloniaui" <Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cc="using:SPTInstaller.CustomControls" xmlns:cc="using:SPTInstaller.CustomControls">
>
<Design.PreviewWith> <Design.PreviewWith>
<StackPanel Spacing="5" Background="{StaticResource AKI_Background_Dark}"> <StackPanel Spacing="5" Background="{StaticResource AKI_Background_Dark}">
<Button Classes="icon" x:Name="testBtn"> <Button Classes="icon" x:Name="testBtn">
<Path Data="{StaticResource Bug}" <Path Data="{StaticResource Bug}"
Fill="{Binding ElementName=testBtn, Path=Foreground}" Fill="{Binding ElementName=testBtn, Path=Foreground}" />
/>
</Button> </Button>
<TextBox Text="Some cool text here" Margin="5" /> <TextBox Text="Some cool text here" Margin="5" />
<TextBox Watermark="This is a watermark" Margin="5" /> <TextBox Watermark="This is a watermark" Margin="5" />
@ -49,15 +47,18 @@
<Setter Property="BorderBrush" Value="DimGray" /> <Setter Property="BorderBrush" Value="DimGray" />
</Style> </Style>
<Style Selector="TextBox:pointerover /template/ TextBlock#PART_Watermark, TextBox:focus /template/ TextBlock#PART_FloatingWatermark"> <Style
Selector="TextBox:pointerover /template/ TextBlock#PART_Watermark, TextBox:focus /template/ TextBlock#PART_FloatingWatermark">
<Setter Property="Foreground" Value="DimGray" /> <Setter Property="Foreground" Value="DimGray" />
</Style> </Style>
<Style Selector="TextBox:focus /template/ TextBlock#PART_Watermark, TextBox:focus /template/ TextBlock#PART_FloatingWatermark"> <Style
Selector="TextBox:focus /template/ TextBlock#PART_Watermark, TextBox:focus /template/ TextBlock#PART_FloatingWatermark">
<Setter Property="Foreground" Value="DimGray" /> <Setter Property="Foreground" Value="DimGray" />
</Style> </Style>
<Style Selector="TextBox /template/ TextBlock#PART_Watermark, TextBox:focus /template/ TextBlock#PART_FloatingWatermark"> <Style
Selector="TextBox /template/ TextBlock#PART_Watermark, TextBox:focus /template/ TextBlock#PART_FloatingWatermark">
<Setter Property="Foreground" Value="White" /> <Setter Property="Foreground" Value="White" />
</Style> </Style>

View File

@ -5,7 +5,8 @@ namespace SPTInstaller.Behaviors;
public class SpanBehavior : AvaloniaObject public class SpanBehavior : AvaloniaObject
{ {
public static readonly AttachedProperty<bool> SpanProperty = AvaloniaProperty.RegisterAttached<SpanBehavior, Interactive, bool>("Span"); public static readonly AttachedProperty<bool> SpanProperty =
AvaloniaProperty.RegisterAttached<SpanBehavior, Interactive, bool>("Span");
public static void SetSpan(AvaloniaObject element, bool value) public static void SetSpan(AvaloniaObject element, bool value)
{ {

View File

@ -58,7 +58,8 @@ public class InstallController
{ {
var result = await check.RunCheck(); var result = await check.RunCheck();
Log.Information($"PreCheck: {check.Name} ({(check.IsRequired ? "Required" : "Optional")}) -> {(result.Succeeded ? "Passed" : "Failed")}\nDetail: {check.PreCheckDetails.ReplaceLineEndings(" ")}"); Log.Information(
$"PreCheck: {check.Name} ({(check.IsRequired ? "Required" : "Optional")}) -> {(result.Succeeded ? "Passed" : "Failed")}\nDetail: {check.PreCheckDetails.ReplaceLineEndings(" ")}");
if (check.IsRequired) if (check.IsRequired)
{ {

View File

@ -3,6 +3,7 @@ using SPTInstaller.CustomControls;
using System.Globalization; using System.Globalization;
namespace SPTInstaller.Converters; namespace SPTInstaller.Converters;
public class StatusSpinnerIsProcessingConverter : IValueConverter public class StatusSpinnerIsProcessingConverter : IValueConverter
{ {
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)

View File

@ -3,6 +3,7 @@ using SPTInstaller.CustomControls;
using System.Globalization; using System.Globalization;
namespace SPTInstaller.Converters; namespace SPTInstaller.Converters;
public class StatusSpinnerIsStateConverter : IValueConverter public class StatusSpinnerIsStateConverter : IValueConverter
{ {
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)

View File

@ -11,21 +11,20 @@
</UserControl.Resources> </UserControl.Resources>
<Grid RowDefinitions="Auto,Auto" ColumnDefinitions="*,AUTO,10,AUTO,*"> <Grid RowDefinitions="Auto,Auto" ColumnDefinitions="*,AUTO,10,AUTO,*">
<cc:StatusSpinner Grid.Column="1" State="{Binding State, RelativeSource={RelativeSource AncestorType=UserControl}}" <cc:StatusSpinner Grid.Column="1"
State="{Binding State, RelativeSource={RelativeSource AncestorType=UserControl}}"
IsVisible="{Binding State, RelativeSource={RelativeSource AncestorType=UserControl}, IsVisible="{Binding State, RelativeSource={RelativeSource AncestorType=UserControl},
Converter={StaticResource IsInProcessingStateConverter}}" Converter={StaticResource IsInProcessingStateConverter}}" />
/>
<Path Grid.Column="1" Data="{StaticResource Cache}" Fill="DodgerBlue" Margin="0 6 0 0" <Path Grid.Column="1" Data="{StaticResource Cache}" Fill="DodgerBlue" Margin="0 6 0 0"
IsVisible="{Binding State, RelativeSource={RelativeSource AncestorType=UserControl}, IsVisible="{Binding State, RelativeSource={RelativeSource AncestorType=UserControl},
Converter={StaticResource IsInProcessingStateConverter}, Converter={StaticResource IsInProcessingStateConverter},
ConverterParameter=invert}" ConverterParameter=invert}" />
/>
<Label Grid.Column="3" Content="{Binding InfoText, RelativeSource={RelativeSource AncestorType=UserControl}}" <Label Grid.Column="3" Content="{Binding InfoText, RelativeSource={RelativeSource AncestorType=UserControl}}"
Margin="0 2 0 0" Margin="0 2 0 0" />
/> <Button Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="3" Content="What's this?" Classes="link"
<Button Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="3" Content="What's this?" Classes="link" HorizontalAlignment="Center" HorizontalAlignment="Center"
Command="{Binding ShowCacheDialogCommand, RelativeSource={RelativeSource AncestorType=UserControl}}" /> Command="{Binding ShowCacheDialogCommand, RelativeSource={RelativeSource AncestorType=UserControl}}" />
</Grid> </Grid>
</UserControl> </UserControl>

View File

@ -5,6 +5,7 @@ using SPTInstaller.CustomControls.Dialogs;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace SPTInstaller.CustomControls; namespace SPTInstaller.CustomControls;
public partial class CacheInfo : UserControl public partial class CacheInfo : UserControl
{ {
public CacheInfo() public CacheInfo()

View File

@ -0,0 +1,23 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:dialogHost="clr-namespace:DialogHostAvalonia;assembly=DialogHost.Avalonia"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="SPTInstaller.CustomControls.Dialogs.ChangeLogDialog"
MinWidth="400" MaxWidth="600">
<StackPanel>
<Label Content="{Binding Version, RelativeSource={RelativeSource AncestorType=UserControl}, StringFormat='{}Installer Change Log for {0}'}" FontSize="18" FontWeight="SemiBold"
/>
<Separator Margin="0 10" Padding="0" Background="{StaticResource AKI_Yellow}"/>
<ScrollViewer MaxHeight="250">
<TextBlock Text="{Binding Message, RelativeSource={RelativeSource AncestorType=UserControl}}"
TextWrapping="Wrap" MinHeight="100"
/>
</ScrollViewer>
<Button Content="Close" Classes="yellow"
HorizontalAlignment="Right"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=dialogHost:DialogHost}, Path=CloseDialogCommand}"
/>
</StackPanel>
</UserControl>

View File

@ -0,0 +1,27 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Tmds.DBus.Protocol;
namespace SPTInstaller.CustomControls.Dialogs;
public partial class ChangeLogDialog : UserControl
{
public string Message { get; set; }
public string Version { get; set; }
public ChangeLogDialog(string newVersion, string message)
{
InitializeComponent();
Message = message;
Version = newVersion;
}
// public static readonly StyledProperty<string> MessageProperty =
// AvaloniaProperty.Register<ChangeLogDialog, string>("Message");
//
// public string Message
// {
// get => GetValue(MessageProperty);
// set => SetValue(MessageProperty, value);
// }
}

View File

@ -11,18 +11,15 @@
Background="{StaticResource AKI_Background_Light}"> Background="{StaticResource AKI_Background_Light}">
<TextBlock Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="4" <TextBlock Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="4"
Text="{Binding Message, RelativeSource={RelativeSource AncestorType=UserControl}}" Text="{Binding Message, RelativeSource={RelativeSource AncestorType=UserControl}}"
TextWrapping="Wrap" TextWrapping="Wrap" />
/>
<Button Content="No" Grid.Row="3" Grid.Column="2" <Button Content="No" Grid.Row="3" Grid.Column="2"
Width="50" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Width="50" VerticalContentAlignment="Center" HorizontalContentAlignment="Center"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=dialogHost:DialogHost}, Path=CloseDialogCommand}" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=dialogHost:DialogHost}, Path=CloseDialogCommand}"
CommandParameter="False" CommandParameter="False"
Classes="yellow" Classes="yellow" />
/>
<Button Content="Yes" Grid.Row="3" Grid.Column="4" <Button Content="Yes" Grid.Row="3" Grid.Column="4"
Width="50" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Width="50" VerticalContentAlignment="Center" HorizontalContentAlignment="Center"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=dialogHost:DialogHost}, Path=CloseDialogCommand}" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=dialogHost:DialogHost}, Path=CloseDialogCommand}"
CommandParameter="True" CommandParameter="True" />
/>
</Grid> </Grid>
</UserControl> </UserControl>

View File

@ -2,6 +2,7 @@
using Avalonia.Controls; using Avalonia.Controls;
namespace SPTInstaller.CustomControls.Dialogs; namespace SPTInstaller.CustomControls.Dialogs;
public partial class ConfirmationDialog : UserControl public partial class ConfirmationDialog : UserControl
{ {
public ConfirmationDialog(string message) public ConfirmationDialog(string message)

View File

@ -9,8 +9,7 @@
<Grid RowDefinitions="AUTO,AUTO,AUTO,*,AUTO" ColumnDefinitions="*,AUTO, AUTO" <Grid RowDefinitions="AUTO,AUTO,AUTO,*,AUTO" ColumnDefinitions="*,AUTO, AUTO"
Background="{StaticResource AKI_Background_Light}"> Background="{StaticResource AKI_Background_Light}">
<Label Content="What is the installer cache for?" FontSize="20" <Label Content="What is the installer cache for?" FontSize="20"
Foreground="{StaticResource AKI_Brush_Yellow}" Foreground="{StaticResource AKI_Brush_Yellow}" />
/>
<TextBlock Grid.Row="1" Grid.ColumnSpan="2" TextWrapping="Wrap" xml:space="preserve"> <TextBlock Grid.Row="1" Grid.ColumnSpan="2" TextWrapping="Wrap" xml:space="preserve">
The installer cache is used to ensure you don't re-download large files that you've already downloaded before. The installer cache is used to ensure you don't re-download large files that you've already downloaded before.
<Span Foreground="red">You should only delete the cache folder if</Span> <Span Foreground="red">You should only delete the cache folder if</Span>
@ -19,31 +18,27 @@ The installer cache is used to ensure you don't re-download large files that you
- You are not planning on installing SPT again any time soon - You are not planning on installing SPT again any time soon
If possible, you should leave the cache in place to avoid uneccessary, lengthy downloads. If possible, you should leave the cache in place to avoid uneccessary, lengthy downloads.
It also helps us prevent extra traffic to our limited download mirrors. Every bit helps <Span Foreground="red" FontSize="25">♥️</Span> It also helps us prevent extra traffic to our limited download mirrors. Every bit helps <Span Foreground="red"
FontSize="25">♥️</Span>
</TextBlock> </TextBlock>
<Label Grid.Row="2" Content="You can find the cache folder here" <Label Grid.Row="2" Content="You can find the cache folder here" />
/> <Button Grid.Row="3" Grid.ColumnSpan="2"
<Button Grid.Row="3" Grid.ColumnSpan="2" Content="{Binding Source={x:Static helpers:DownloadCacheHelper.CachePath}}" Content="{Binding Source={x:Static helpers:DownloadCacheHelper.CachePath}}"
Classes="link" Classes="link"
Margin="0 10" Margin="0 10"
IsVisible="{Binding CacheExists, RelativeSource={RelativeSource AncestorType=UserControl}}" IsVisible="{Binding CacheExists, RelativeSource={RelativeSource AncestorType=UserControl}}"
Command="{Binding OpenCacheFolder, RelativeSource={RelativeSource AncestorType=UserControl}}" Command="{Binding OpenCacheFolder, RelativeSource={RelativeSource AncestorType=UserControl}}" />
/>
<Label Grid.Row="3" Content="No cache folder exists" <Label Grid.Row="3" Content="No cache folder exists"
IsVisible="{Binding !CacheExists, RelativeSource={RelativeSource AncestorType=UserControl}}" IsVisible="{Binding !CacheExists, RelativeSource={RelativeSource AncestorType=UserControl}}" />
/>
<Label Grid.Row="4" <Label Grid.Row="4"
Content="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=AdditionalInfo}" Content="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=AdditionalInfo}"
Foreground="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=AdditionalInfoColor}" Foreground="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=AdditionalInfoColor}" />
/>
<Button Grid.Row="4" Grid.Column="1" Content="Move Downloaded Patcher" Margin="0 0 10 0" <Button Grid.Row="4" Grid.Column="1" Content="Move Downloaded Patcher" Margin="0 0 10 0"
Command="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=MoveDownloadsPatcherToCache}" Command="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=MoveDownloadsPatcherToCache}" />
/>
<Button Grid.Row="4" Grid.Column="2" Content="Close" Classes="yellow" <Button Grid.Row="4" Grid.Column="2" Content="Close" Classes="yellow"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=dialogHost:DialogHost}, Path=CloseDialogCommand}" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=dialogHost:DialogHost}, Path=CloseDialogCommand}" />
/>
</Grid> </Grid>
</UserControl> </UserControl>

View File

@ -6,6 +6,7 @@ using Avalonia.Controls;
using Serilog; using Serilog;
namespace SPTInstaller.CustomControls.Dialogs; namespace SPTInstaller.CustomControls.Dialogs;
public partial class WhyCacheThoughDialog : UserControl public partial class WhyCacheThoughDialog : UserControl
{ {
private int _movePatcherState = 0; private int _movePatcherState = 0;
@ -45,7 +46,9 @@ public partial class WhyCacheThoughDialog : UserControl
Process.Start(new ProcessStartInfo() Process.Start(new ProcessStartInfo()
{ {
FileName = Path.EndsInDirectorySeparator(DownloadCacheHelper.CachePath) ? DownloadCacheHelper.CachePath : DownloadCacheHelper.CachePath + Path.DirectorySeparatorChar, FileName = Path.EndsInDirectorySeparator(DownloadCacheHelper.CachePath)
? DownloadCacheHelper.CachePath
: DownloadCacheHelper.CachePath + Path.DirectorySeparatorChar,
UseShellExecute = true, UseShellExecute = true,
Verb = "open" Verb = "open"
}); });
@ -71,7 +74,8 @@ public partial class WhyCacheThoughDialog : UserControl
return; return;
} }
_foundPatcher = downloadsFolder.GetFiles("Patcher_*").OrderByDescending(p => p.CreationTime).FirstOrDefault(); _foundPatcher = downloadsFolder.GetFiles("Patcher_*").OrderByDescending(p => p.CreationTime)
.FirstOrDefault();
if (_foundPatcher == null || !_foundPatcher.Exists) if (_foundPatcher == null || !_foundPatcher.Exists)
{ {
@ -84,7 +88,8 @@ public partial class WhyCacheThoughDialog : UserControl
} }
Log.Information($"[MV_0] Found patcher for move: {_foundPatcher.Name}"); Log.Information($"[MV_0] Found patcher for move: {_foundPatcher.Name}");
AdditionalInfo = $"Click again to move the below patcher file to the cache folder\n{_foundPatcher?.Name ?? "-SOMETHING WENT WRONG-"}"; AdditionalInfo =
$"Click again to move the below patcher file to the cache folder\n{_foundPatcher?.Name ?? "-SOMETHING WENT WRONG-"}";
AdditionalInfoColor = "#FFC107"; AdditionalInfoColor = "#FFC107";
_movePatcherState = 1; _movePatcherState = 1;
break; break;
@ -104,6 +109,7 @@ public partial class WhyCacheThoughDialog : UserControl
AdditionalInfoColor = "red"; AdditionalInfoColor = "red";
Log.Error(ex, "Failed to move downloaded patcher file into cache"); Log.Error(ex, "Failed to move downloaded patcher file into cache");
} }
break; break;
default: default:
Log.Error("[MV_ ] Move state is broken :("); Log.Error("[MV_ ] Move state is broken :(");

View File

@ -55,10 +55,11 @@ public class DistributedSpacePanel : Panel
.WithWidth(finalSize.Width); .WithWidth(finalSize.Width);
child.Arrange(rcChild); child.Arrange(rcChild);
continue; continue;
}; }
;
rcChild = rcChild.WithY(rcChild.Y + previousChildSize); rcChild = rcChild.WithY(rcChild.Y + previousChildSize);
previousChildSize = child.DesiredSize.Height; previousChildSize = child.DesiredSize.Height;

View File

@ -15,8 +15,7 @@
<!-- show when nothing is selected --> <!-- show when nothing is selected -->
<Label Content="Select a Pre-Check to see more info" FontSize="20" <Label Content="Select a Pre-Check to see more info" FontSize="20"
HorizontalAlignment="Center" VerticalAlignment="Center" HorizontalAlignment="Center" VerticalAlignment="Center"
IsVisible="{Binding HasSelection, RelativeSource={RelativeSource AncestorType=UserControl}, Converter={x:Static BoolConverters.Not}}" IsVisible="{Binding HasSelection, RelativeSource={RelativeSource AncestorType=UserControl}, Converter={x:Static BoolConverters.Not}}" />
/>
<ItemsControl ItemsSource="{Binding PreChecks}" VerticalAlignment="Stretch"> <ItemsControl ItemsSource="{Binding PreChecks}" VerticalAlignment="Stretch">
<ItemsControl.ItemsPanel> <ItemsControl.ItemsPanel>
@ -27,20 +26,19 @@
<ItemsControl.ItemTemplate> <ItemsControl.ItemTemplate>
<DataTemplate DataType="model:PreCheckBase"> <DataTemplate DataType="model:PreCheckBase">
<!-- selected precheck details --> <!-- selected precheck details -->
<Grid RowDefinitions="10, *, Auto, 10" ColumnDefinitions="10, 10, *, 10" VerticalAlignment="Stretch" <Grid RowDefinitions="10, *, Auto, 10" ColumnDefinitions="10, 10, *, 10"
IsVisible="{Binding IsSelected}" VerticalAlignment="Stretch"
> IsVisible="{Binding IsSelected}">
<Rectangle Grid.Row="1" Grid.Column="1" Width="3" Fill="{Binding State, Converter={StaticResource colorConverter}}" <Rectangle Grid.Row="1" Grid.Column="1" Width="3"
Fill="{Binding State, Converter={StaticResource colorConverter}}"
HorizontalAlignment="Left" /> HorizontalAlignment="Left" />
<StackPanel Grid.Row="1" Grid.Column="2" HorizontalAlignment="Left"> <StackPanel Grid.Row="1" Grid.Column="2" HorizontalAlignment="Left">
<Label Content="{Binding Name}" <Label Content="{Binding Name}"
FontSize="20" FontSize="20" />
/>
<Rectangle Height="1" Fill="Gray" Margin="0 10" /> <Rectangle Height="1" Fill="Gray" Margin="0 10" />
<TextBlock Text="{Binding PreCheckDetails}" <TextBlock Text="{Binding PreCheckDetails}"
TextWrapping="Wrap" TextWrapping="Wrap" />
/>
</StackPanel> </StackPanel>
<Button Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2" Classes="yellow" <Button Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2" Classes="yellow"
@ -50,8 +48,7 @@
Command="{Binding ActionButtonCommand}" Command="{Binding ActionButtonCommand}"
Content="{Binding ActionButtonText}" Content="{Binding ActionButtonText}"
HorizontalContentAlignment="Center" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" VerticalContentAlignment="Center"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch" />
/>
</Grid> </Grid>
</DataTemplate> </DataTemplate>

View File

@ -5,8 +5,7 @@
xmlns:cc="using:SPTInstaller.CustomControls" xmlns:cc="using:SPTInstaller.CustomControls"
xmlns:convt="using:SPTInstaller.Converters" xmlns:convt="using:SPTInstaller.Converters"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="SPTInstaller.CustomControls.PreCheckItem" x:Class="SPTInstaller.CustomControls.PreCheckItem">
>
<UserControl.Resources> <UserControl.Resources>
<convt:StatusSpinnerIsStateConverter x:Key="IsStateConverter" /> <convt:StatusSpinnerIsStateConverter x:Key="IsStateConverter" />
@ -47,8 +46,7 @@
Command="{Binding SelectCommand, RelativeSource={RelativeSource AncestorType=UserControl}}" Command="{Binding SelectCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
CommandParameter="{Binding }" CommandParameter="{Binding }"
Classes.selectable="{Binding !IsSelected, RelativeSource={RelativeSource AncestorType=UserControl}}" Classes.selectable="{Binding !IsSelected, RelativeSource={RelativeSource AncestorType=UserControl}}"
Classes.selected="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=UserControl}}" Classes.selected="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=UserControl}}">
>
<Grid ColumnDefinitions="AUTO, AUTO" Margin="3 0 0 3"> <Grid ColumnDefinitions="AUTO, AUTO" Margin="3 0 0 3">
<cc:StatusSpinner State="{Binding State, RelativeSource={RelativeSource AncestorType=UserControl}}" /> <cc:StatusSpinner State="{Binding State, RelativeSource={RelativeSource AncestorType=UserControl}}" />
@ -56,8 +54,7 @@
Content="{Binding PreCheckName, RelativeSource={RelativeSource AncestorType=UserControl}}" Content="{Binding PreCheckName, RelativeSource={RelativeSource AncestorType=UserControl}}"
Classes.bold="{Binding State, RelativeSource={RelativeSource AncestorType=UserControl}, Classes.bold="{Binding State, RelativeSource={RelativeSource AncestorType=UserControl},
Converter={StaticResource ResourceKey=IsStateConverter}, Converter={StaticResource ResourceKey=IsStateConverter},
ConverterParameter=Running}" ConverterParameter=Running}" />
/>
</Grid> </Grid>
</Button> </Button>
</UserControl> </UserControl>

View File

@ -9,14 +9,17 @@
<UserControl.Styles> <UserControl.Styles>
<!-- Ellipse Styles --> <!-- Ellipse Styles -->
<Style Selector="Ellipse"> <Style Selector="Ellipse">
<Setter Property="Stroke" Value="{Binding PendingColor, RelativeSource={RelativeSource AncestorType=UserControl}}"/> <Setter Property="Stroke"
Value="{Binding PendingColor, RelativeSource={RelativeSource AncestorType=UserControl}}" />
<Setter Property="Margin" Value="7 0" /> <Setter Property="Margin" Value="7 0" />
</Style> </Style>
<Style Selector="Ellipse.completed"> <Style Selector="Ellipse.completed">
<Setter Property="Stroke" Value="{Binding CompletedColor, RelativeSource={RelativeSource AncestorType=UserControl}}"/> <Setter Property="Stroke"
Value="{Binding CompletedColor, RelativeSource={RelativeSource AncestorType=UserControl}}" />
</Style> </Style>
<Style Selector="Ellipse.running"> <Style Selector="Ellipse.running">
<Setter Property="Stroke" Value="{Binding RunningColor, RelativeSource={RelativeSource AncestorType=UserControl}}"/> <Setter Property="Stroke"
Value="{Binding RunningColor, RelativeSource={RelativeSource AncestorType=UserControl}}" />
<Setter Property="Margin" Value="7 3" /> <Setter Property="Margin" Value="7 3" />
<Style.Animations> <Style.Animations>
<Animation Duration="0:0:1" PlaybackDirection="Alternate" IterationCount="Infinite"> <Animation Duration="0:0:1" PlaybackDirection="Alternate" IterationCount="Infinite">
@ -32,22 +35,27 @@
</Style.Animations> </Style.Animations>
</Style> </Style>
<Style Selector="Ellipse.centerRunning"> <Style Selector="Ellipse.centerRunning">
<Setter Property="Fill" Value="{Binding RunningColor, RelativeSource={RelativeSource AncestorType=UserControl}}"/> <Setter Property="Fill"
Value="{Binding RunningColor, RelativeSource={RelativeSource AncestorType=UserControl}}" />
</Style> </Style>
<Style Selector="Ellipse.centerCompleted"> <Style Selector="Ellipse.centerCompleted">
<Setter Property="Fill" Value="{Binding CompletedColor, RelativeSource={RelativeSource AncestorType=UserControl}}"/> <Setter Property="Fill"
Value="{Binding CompletedColor, RelativeSource={RelativeSource AncestorType=UserControl}}" />
</Style> </Style>
<!-- Label Styles --> <!-- Label Styles -->
<Style Selector="TextBlock"> <Style Selector="TextBlock">
<Setter Property="Foreground" Value="{Binding PendingColor, RelativeSource={RelativeSource <Setter Property="Foreground"
Value="{Binding PendingColor, RelativeSource={RelativeSource
AncestorType=UserControl}}" /> AncestorType=UserControl}}" />
</Style> </Style>
<Style Selector="TextBlock.completed"> <Style Selector="TextBlock.completed">
<Setter Property="Foreground" Value="{Binding CompletedColor, RelativeSource={RelativeSource AncestorType=UserControl}}"/> <Setter Property="Foreground"
Value="{Binding CompletedColor, RelativeSource={RelativeSource AncestorType=UserControl}}" />
</Style> </Style>
<Style Selector="TextBlock.running"> <Style Selector="TextBlock.running">
<Setter Property="Foreground" Value="{Binding RunningColor, RelativeSource={RelativeSource AncestorType=UserControl}}"/> <Setter Property="Foreground"
Value="{Binding RunningColor, RelativeSource={RelativeSource AncestorType=UserControl}}" />
</Style> </Style>
</UserControl.Styles> </UserControl.Styles>
<Grid ColumnDefinitions="AUTO, *"> <Grid ColumnDefinitions="AUTO, *">
@ -57,13 +65,11 @@
Fill="{StaticResource AKI_Background_Dark}" Fill="{StaticResource AKI_Background_Dark}"
HorizontalAlignment="Left" HorizontalAlignment="Left"
Classes.running="{Binding IsRunning, RelativeSource={RelativeSource AncestorType=UserControl}}" Classes.running="{Binding IsRunning, RelativeSource={RelativeSource AncestorType=UserControl}}"
Classes.completed="{Binding IsCompleted, RelativeSource={RelativeSource AncestorType=UserControl}}" Classes.completed="{Binding IsCompleted, RelativeSource={RelativeSource AncestorType=UserControl}}" />
/>
<Ellipse Height="20" Width="20" <Ellipse Height="20" Width="20"
Classes.centerRunning="{Binding IsRunning, RelativeSource={RelativeSource AncestorType=UserControl}}" Classes.centerRunning="{Binding IsRunning, RelativeSource={RelativeSource AncestorType=UserControl}}"
Classes.centerCompleted="{Binding IsCompleted, RelativeSource={RelativeSource AncestorType=UserControl}}" Classes.centerCompleted="{Binding IsCompleted, RelativeSource={RelativeSource AncestorType=UserControl}}" />
/>
<TextBlock Grid.Column="1" <TextBlock Grid.Column="1"
Text="{Binding TaskName, RelativeSource={RelativeSource AncestorType=UserControl}}" Text="{Binding TaskName, RelativeSource={RelativeSource AncestorType=UserControl}}"
@ -73,7 +79,6 @@
FontSize="15" FontSize="15"
TextWrapping="Wrap" TextWrapping="Wrap"
VerticalAlignment="Center" VerticalAlignment="Center"
HorizontalAlignment="Left" HorizontalAlignment="Left" />
/>
</Grid> </Grid>
</UserControl> </UserControl>

View File

@ -6,8 +6,7 @@
xmlns:bh="using:SPTInstaller.Behaviors" xmlns:bh="using:SPTInstaller.Behaviors"
xmlns:convt="using:SPTInstaller.Converters" xmlns:convt="using:SPTInstaller.Converters"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="SPTInstaller.CustomControls.ProgressableTaskList" x:Class="SPTInstaller.CustomControls.ProgressableTaskList">
>
<UserControl.Resources> <UserControl.Resources>
<convt:InvertedProgressConverter x:Key="invtProgressConvt" /> <convt:InvertedProgressConverter x:Key="invtProgressConvt" />
</UserControl.Resources> </UserControl.Resources>
@ -19,9 +18,9 @@
Foreground="{Binding PendingColor, RelativeSource={RelativeSource AncestorType=UserControl}}" Foreground="{Binding PendingColor, RelativeSource={RelativeSource AncestorType=UserControl}}"
HorizontalAlignment="Left" HorizontalAlignment="Left"
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
Margin="25 20" Margin="25 20" />
/> <ItemsControl Name="itemsControl"
<ItemsControl Name="itemsControl" ItemsSource="{Binding Tasks, RelativeSource={RelativeSource AncestorType=UserControl}}" ItemsSource="{Binding Tasks, RelativeSource={RelativeSource AncestorType=UserControl}}"
Padding="5"> Padding="5">
<ItemsControl.ItemsPanel> <ItemsControl.ItemsPanel>
<ItemsPanelTemplate> <ItemsPanelTemplate>
@ -36,8 +35,7 @@
IsCompleted="{Binding IsCompleted}" IsCompleted="{Binding IsCompleted}"
PendingColor="{Binding PendingColor, RelativeSource={RelativeSource AncestorType=UserControl}}" PendingColor="{Binding PendingColor, RelativeSource={RelativeSource AncestorType=UserControl}}"
RunningColor="{Binding RunningColor, RelativeSource={RelativeSource AncestorType=UserControl}}" RunningColor="{Binding RunningColor, RelativeSource={RelativeSource AncestorType=UserControl}}"
CompletedColor="{Binding CompletedColor, RelativeSource={RelativeSource AncestorType=UserControl}}" CompletedColor="{Binding CompletedColor, RelativeSource={RelativeSource AncestorType=UserControl}}" />
/>
</DataTemplate> </DataTemplate>
</ItemsControl.ItemTemplate> </ItemsControl.ItemTemplate>
</ItemsControl> </ItemsControl>

View File

@ -20,6 +20,7 @@ public partial class ProgressableTaskList : UserControl
} }
private int _taskProgress; private int _taskProgress;
public int TaskProgress public int TaskProgress
{ {
get => _taskProgress; get => _taskProgress;

View File

@ -4,8 +4,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:convt="using:SPTInstaller.Converters" xmlns:convt="using:SPTInstaller.Converters"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="SPTInstaller.CustomControls.StatusSpinner" x:Class="SPTInstaller.CustomControls.StatusSpinner">
>
<UserControl.Resources> <UserControl.Resources>
<convt:StatusSpinnerIsStateConverter x:Key="IsStateConverter" /> <convt:StatusSpinnerIsStateConverter x:Key="IsStateConverter" />
@ -33,7 +32,8 @@
<Style Selector="Arc"> <Style Selector="Arc">
<Setter Property="Stroke" Value="Gray" /> <Setter Property="Stroke" Value="Gray" />
<Setter Property="IsVisible" Value="{Binding State, RelativeSource={RelativeSource AncestorType=UserControl}, <Setter Property="IsVisible"
Value="{Binding State, RelativeSource={RelativeSource AncestorType=UserControl},
Converter={StaticResource ResourceKey=IsInProcessingStateConverter}}" /> Converter={StaticResource ResourceKey=IsInProcessingStateConverter}}" />
<Style.Animations> <Style.Animations>
<Animation Duration="0:0:1" IterationCount="Infinite"> <Animation Duration="0:0:1" IterationCount="Infinite">
@ -52,8 +52,7 @@
<Canvas Margin="0 3 0 0" Height="20" Width="20" <Canvas Margin="0 3 0 0" Height="20" Width="20"
IsVisible="{Binding State, RelativeSource={RelativeSource AncestorType=UserControl}, IsVisible="{Binding State, RelativeSource={RelativeSource AncestorType=UserControl},
Converter={StaticResource ResourceKey=IsInProcessingStateConverter}, ConverterParameter=invert}"> Converter={StaticResource ResourceKey=IsInProcessingStateConverter}, ConverterParameter=invert}">
<Ellipse Fill="White" Height="15" Width="15" Canvas.Top="3" Canvas.Left="3" <Ellipse Fill="White" Height="15" Width="15" Canvas.Top="3" Canvas.Left="3" />
/>
<Path StrokeThickness="2" <Path StrokeThickness="2"
Classes.ok="{Binding State, RelativeSource={RelativeSource AncestorType=UserControl}, Classes.ok="{Binding State, RelativeSource={RelativeSource AncestorType=UserControl},
Converter={StaticResource ResourceKey=IsStateConverter}, Converter={StaticResource ResourceKey=IsStateConverter},
@ -63,15 +62,13 @@
ConverterParameter=Warning}" ConverterParameter=Warning}"
Classes.error="{Binding State, RelativeSource={RelativeSource AncestorType=UserControl}, Classes.error="{Binding State, RelativeSource={RelativeSource AncestorType=UserControl},
Converter={StaticResource ResourceKey=IsStateConverter}, Converter={StaticResource ResourceKey=IsStateConverter},
ConverterParameter=Error}" ConverterParameter=Error}" />
/>
</Canvas> </Canvas>
<Arc StartAngle="280" SweepAngle="80" Margin="0 3 0 0" StrokeThickness="3" <Arc StartAngle="280" SweepAngle="80" Margin="0 3 0 0" StrokeThickness="3"
Width="20" Height="20" VerticalAlignment="Top" Width="20" Height="20" VerticalAlignment="Top"
Classes.running="{Binding State, RelativeSource={RelativeSource AncestorType=UserControl}, Classes.running="{Binding State, RelativeSource={RelativeSource AncestorType=UserControl},
Converter={StaticResource ResourceKey=IsStateConverter}, Converter={StaticResource ResourceKey=IsStateConverter},
ConverterParameter=Running}" ConverterParameter=Running}" />
/>
</Grid> </Grid>
</UserControl> </UserControl>

View File

@ -10,8 +10,7 @@
HorizontalAlignment="Center" HorizontalAlignment="Center"
FontSize="15" FontSize="15"
FontWeight="SemiBold" FontWeight="SemiBold"
Content="{Binding Message, RelativeSource={RelativeSource AncestorType=UserControl}}" Content="{Binding Message, RelativeSource={RelativeSource AncestorType=UserControl}}" />
/>
<TextBlock Grid.Column="1" Grid.Row="4" <TextBlock Grid.Column="1" Grid.Row="4"
Foreground="Gainsboro" Foreground="Gainsboro"
@ -20,21 +19,18 @@
Text="{Binding Details, RelativeSource={RelativeSource AncestorType=UserControl}}" Text="{Binding Details, RelativeSource={RelativeSource AncestorType=UserControl}}"
TextTrimming="CharacterEllipsis" TextTrimming="CharacterEllipsis"
TextWrapping="Wrap" TextWrapping="Wrap"
MaxLines="3" MaxLines="3" />
/>
<Grid Grid.Column="1" Grid.Row="6" ColumnDefinitions="*,AUTO"> <Grid Grid.Column="1" Grid.Row="6" ColumnDefinitions="*,AUTO">
<ProgressBar IsVisible="{Binding ShowProgress, RelativeSource={RelativeSource AncestorType=UserControl}}" <ProgressBar IsVisible="{Binding ShowProgress, RelativeSource={RelativeSource AncestorType=UserControl}}"
Value="{Binding Progress, RelativeSource={RelativeSource AncestorType=UserControl}}" Value="{Binding Progress, RelativeSource={RelativeSource AncestorType=UserControl}}"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
IsIndeterminate="{Binding IndeterminateProgress, RelativeSource={RelativeSource AncestorType=UserControl}}" IsIndeterminate="{Binding IndeterminateProgress, RelativeSource={RelativeSource AncestorType=UserControl}}" />
/>
<Label Grid.Column="1" <Label Grid.Column="1"
Content="{Binding Progress, RelativeSource={RelativeSource AncestorType=UserControl}, StringFormat='{}{0}%'}" Content="{Binding Progress, RelativeSource={RelativeSource AncestorType=UserControl}, StringFormat='{}{0}%'}"
IsVisible="{Binding !IndeterminateProgress, RelativeSource={RelativeSource AncestorType=UserControl}}" IsVisible="{Binding !IndeterminateProgress, RelativeSource={RelativeSource AncestorType=UserControl}}" />
/>
</Grid> </Grid>
</Grid> </Grid>
</UserControl> </UserControl>

View File

@ -9,8 +9,7 @@
<Rectangle Grid.ColumnSpan="6" IsHitTestVisible="False" <Rectangle Grid.ColumnSpan="6" IsHitTestVisible="False"
Fill="{Binding Background, RelativeSource={ Fill="{Binding Background, RelativeSource={
RelativeSource AncestorType=UserControl}}" RelativeSource AncestorType=UserControl}}" />
/>
<Label Content="{Binding Title, RelativeSource={ <Label Content="{Binding Title, RelativeSource={
RelativeSource AncestorType=UserControl}}" RelativeSource AncestorType=UserControl}}"
@ -18,8 +17,7 @@
Foreground="{Binding Foreground, RelativeSource={ Foreground="{Binding Foreground, RelativeSource={
RelativeSource AncestorType=UserControl}}" RelativeSource AncestorType=UserControl}}"
Background="Transparent" Background="Transparent"
VerticalContentAlignment="Center" VerticalContentAlignment="Center" />
/>
<!-- Minimize (-) Button --> <!-- Minimize (-) Button -->
<Button Content="&#xE949;" Grid.Column="2" <Button Content="&#xE949;" Grid.Column="2"
@ -33,8 +31,7 @@
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
FontFamily="Segoe MDL2 Assets" FontFamily="Segoe MDL2 Assets"
CornerRadius="0" CornerRadius="0"
Width="35" Width="35">
>
<Button.Styles> <Button.Styles>
<Style Selector="Button:pointerover /template/ ContentPresenter"> <Style Selector="Button:pointerover /template/ ContentPresenter">
<Setter Property="Background" Value="{StaticResource AKI_Brush_DarkGrayBlue}" /> <Setter Property="Background" Value="{StaticResource AKI_Brush_DarkGrayBlue}" />
@ -58,8 +55,7 @@
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
FontFamily="Segoe MDL2 Assets" FontFamily="Segoe MDL2 Assets"
CornerRadius="0" CornerRadius="0"
Width="35" Width="35">
>
<Button.Styles> <Button.Styles>
<Style Selector="Button:pointerover /template/ ContentPresenter"> <Style Selector="Button:pointerover /template/ ContentPresenter">
<Setter Property="Background" Value="IndianRed" /> <Setter Property="Background" Value="IndianRed" />

View File

@ -10,22 +10,24 @@
<Panel> <Panel>
<StackPanel Orientation="Horizontal"> <StackPanel HorizontalAlignment="Center">
<StackPanel.IsVisible> <StackPanel.IsVisible>
<MultiBinding Converter="{x:Static BoolConverters.And}"> <MultiBinding Converter="{x:Static BoolConverters.And}">
<Binding Path="!Updating" RelativeSource="{RelativeSource AncestorType=UserControl}" /> <Binding Path="!Updating" RelativeSource="{RelativeSource AncestorType=UserControl}" />
<Binding Path="UpdateAvailable" RelativeSource="{RelativeSource AncestorType=UserControl}" /> <Binding Path="UpdateAvailable" RelativeSource="{RelativeSource AncestorType=UserControl}" />
</MultiBinding> </MultiBinding>
</StackPanel.IsVisible> </StackPanel.IsVisible>
<StackPanel Orientation="Horizontal">
<Button Content="{Binding InfoText, RelativeSource={RelativeSource AncestorType=UserControl}}" <Button Content="{Binding InfoText, RelativeSource={RelativeSource AncestorType=UserControl}}"
CornerRadius="20 0 0 20" CornerRadius="20 0 0 20"
Classes="yellow" Classes="yellow"
Command="{Binding UpdateCommand, RelativeSource={RelativeSource AncestorType=UserControl}}" Command="{Binding UpdateCommand, RelativeSource={RelativeSource AncestorType=UserControl}}" />
/>
<Button Content="Not now" CornerRadius="0 20 20 0" <Button Content="Not now" CornerRadius="0 20 20 0"
Command="{Binding DismissCommand, RelativeSource={RelativeSource AncestorType=UserControl}}" Command="{Binding DismissCommand, RelativeSource={RelativeSource AncestorType=UserControl}}" />
/> </StackPanel>
<Button HorizontalAlignment="Center" Content="What's new?" Classes="link"
Command="{Binding WhatsNewCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
</StackPanel> </StackPanel>
<Panel Margin="0 10"> <Panel Margin="0 10">
@ -37,8 +39,7 @@
</Panel.IsVisible> </Panel.IsVisible>
<ProgressBar CornerRadius="20" VerticalAlignment="Stretch" <ProgressBar CornerRadius="20" VerticalAlignment="Stretch"
Value="{Binding DownloadProgress, RelativeSource={RelativeSource AncestorType=UserControl}}" Value="{Binding DownloadProgress, RelativeSource={RelativeSource AncestorType=UserControl}}"
IsIndeterminate="{Binding IsIndeterminate, RelativeSource={RelativeSource AncestorType=UserControl}}" IsIndeterminate="{Binding IsIndeterminate, RelativeSource={RelativeSource AncestorType=UserControl}}" />
/>
<Label Content="{Binding InfoText, RelativeSource={RelativeSource AncestorType=UserControl}}" <Label Content="{Binding InfoText, RelativeSource={RelativeSource AncestorType=UserControl}}"
VerticalAlignment="Center" HorizontalAlignment="Center" VerticalAlignment="Center" HorizontalAlignment="Center"
Foreground="Black" FontWeight="SemiBold" /> Foreground="Black" FontWeight="SemiBold" />

View File

@ -20,7 +20,8 @@ public partial class UpdateButton : UserControl
set => SetValue(InfoTextProperty, value); set => SetValue(InfoTextProperty, value);
} }
public static readonly StyledProperty<bool> CheckingForUpdateProperty = AvaloniaProperty.Register<UpdateButton, bool>( public static readonly StyledProperty<bool> CheckingForUpdateProperty =
AvaloniaProperty.Register<UpdateButton, bool>(
"CheckingForUpdate"); "CheckingForUpdate");
public bool CheckingForUpdate public bool CheckingForUpdate
@ -29,7 +30,8 @@ public partial class UpdateButton : UserControl
set => SetValue(CheckingForUpdateProperty, value); set => SetValue(CheckingForUpdateProperty, value);
} }
public static readonly StyledProperty<ICommand> DismissCommandProperty = AvaloniaProperty.Register<UpdateButton, ICommand>( public static readonly StyledProperty<ICommand> DismissCommandProperty =
AvaloniaProperty.Register<UpdateButton, ICommand>(
"DismissCommand"); "DismissCommand");
public ICommand DismissCommand public ICommand DismissCommand
@ -38,7 +40,8 @@ public partial class UpdateButton : UserControl
set => SetValue(DismissCommandProperty, value); set => SetValue(DismissCommandProperty, value);
} }
public static readonly StyledProperty<ICommand> UpdateCommandProperty = AvaloniaProperty.Register<UpdateButton, ICommand>( public static readonly StyledProperty<ICommand> UpdateCommandProperty =
AvaloniaProperty.Register<UpdateButton, ICommand>(
"UpdateCommand"); "UpdateCommand");
public ICommand UpdateCommand public ICommand UpdateCommand
@ -47,6 +50,15 @@ public partial class UpdateButton : UserControl
set => SetValue(UpdateCommandProperty, value); set => SetValue(UpdateCommandProperty, value);
} }
public static readonly StyledProperty<ICommand> WhatsNewCommandProperty =
AvaloniaProperty.Register<UpdateButton, ICommand>("WhatsNewCommand");
public ICommand WhatsNewCommand
{
get => GetValue(WhatsNewCommandProperty);
set => SetValue(WhatsNewCommandProperty, value);
}
public static readonly StyledProperty<bool> UpdatingProperty = AvaloniaProperty.Register<UpdateButton, bool>( public static readonly StyledProperty<bool> UpdatingProperty = AvaloniaProperty.Register<UpdateButton, bool>(
"Updating"); "Updating");

View File

@ -1,65 +0,0 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="SPTInstaller.CustomControls.UpdateInfoCard"
MinHeight="100" MinWidth="300">
<UserControl.Styles>
<Style Selector="Grid">
<Setter Property="Opacity" Value="0"/>
<Setter Property="Transitions">
<Setter.Value>
<Transitions>
<DoubleTransition Property="Opacity" Duration="0:0:0.2"/>
</Transitions>
</Setter.Value>
</Setter>
</Style>
<Style Selector="Grid.show">
<Setter Property="Opacity" Value="1"/>
</Style>
<Style Selector="ProgressBar">
<Setter Property="IsVisible" Value="False"/>
</Style>
<Style Selector="ProgressBar.checking">
<Setter Property="IsIndeterminate" Value="True"/>
<Setter Property="IsVisible" Value="True"/>
</Style>
<Style Selector="ProgressBar.updating">
<Setter Property="IsIndeterminate" Value="false"/>
<Setter Property="IsVisible" Value="True"/>
</Style>
</UserControl.Styles>
<Grid ColumnDefinitions="10,*,AUTO,AUTO,10" RowDefinitions="10,AUTO,AUTO,10"
Classes.show="{Binding ShowUpdateCard, RelativeSource={RelativeSource AncestorType=UserControl}}">
<Border Grid.ColumnSpan="5" Grid.RowSpan="4" Background="{StaticResource AKI_Background_Light}"
BoxShadow="2 2 10 .1 black" CornerRadius="8"
/>
<TextBlock Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="3" MaxWidth="400"
Text="{Binding InfoText, RelativeSource={RelativeSource AncestorType=UserControl}}"
TextWrapping="Wrap" Margin="0 10"
/>
<Button Grid.Column="2" Grid.Row="2" Content="Not now"
Classes="outlined"
IsVisible="{Binding UpdateAvailable, RelativeSource={RelativeSource AncestorType=UserControl}}"
Command="{Binding NotNowCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
/>
<Button Grid.Column="3" Grid.Row="2" Content="Update"
Classes="yellow" Margin="10 0 0 0"
IsVisible="{Binding UpdateAvailable, RelativeSource={RelativeSource AncestorType=UserControl}}"
Command="{Binding UpdateInstallerCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
/>
<ProgressBar Grid.Row="4" Grid.Column="1" Grid.ColumnSpan="3"
Value="{Binding DownloadProgress, RelativeSource={RelativeSource AncestorType=UserControl}}"
Classes.updating="{Binding Updating, RelativeSource={RelativeSource AncestorType=UserControl}}"
Classes.checking="{Binding IndeterminateProgress, RelativeSource={RelativeSource AncestorType=UserControl}}"
/>
</Grid>
</UserControl>

View File

@ -1,76 +0,0 @@
using Avalonia;
using Avalonia.Controls;
using System.Windows.Input;
namespace SPTInstaller.CustomControls;
public partial class UpdateInfoCard : UserControl
{
public UpdateInfoCard()
{
InitializeComponent();
}
public bool ShowUpdateCard
{
get => GetValue(ShowUpdateCardProperty);
set => SetValue(ShowUpdateCardProperty, value);
}
public static readonly StyledProperty<bool> ShowUpdateCardProperty =
AvaloniaProperty.Register<UpdateInfoCard, bool>(nameof(ShowUpdateCard));
public bool Updating
{
get => GetValue(UpdatingProperty);
set => SetValue(UpdatingProperty, value);
}
public static readonly StyledProperty<bool> UpdatingProperty =
AvaloniaProperty.Register<UpdateInfoCard, bool>(nameof(Updating));
public bool UpdateAvailable
{
get => GetValue(UpdateAvailableProperty);
set => SetValue(UpdateAvailableProperty, value);
}
public static readonly StyledProperty<bool> UpdateAvailableProperty =
AvaloniaProperty.Register<UpdateInfoCard, bool>(nameof(UpdateAvailable));
public bool IndeterminateProgress
{
get => GetValue(IndeterminateProgressProperty);
set => SetValue(IndeterminateProgressProperty, value);
}
public static readonly StyledProperty<bool> IndeterminateProgressProperty =
AvaloniaProperty.Register<UpdateInfoCard, bool>(nameof(IndeterminateProgress));
public string InfoText
{
get => GetValue(InfoTextProperty);
set => SetValue(InfoTextProperty, value);
}
public static readonly StyledProperty<string> InfoTextProperty =
AvaloniaProperty.Register<UpdateInfoCard, string>(nameof(InfoText));
public int DownloadProgress
{
get => GetValue(DownloadProgressProperty);
set => SetValue(DownloadProgressProperty, value);
}
public static readonly StyledProperty<int> DownloadProgressProperty =
AvaloniaProperty.Register<UpdateInfoCard, int>(nameof(DownloadProgress));
public ICommand NotNowCommand
{
get => GetValue(NotNowCommandProperty);
set => SetValue(NotNowCommandProperty, value);
}
public static readonly StyledProperty<ICommand> NotNowCommandProperty =
AvaloniaProperty.Register<UpdateInfoCard, ICommand>(nameof(NotNowCommand));
public ICommand UpdateInstallerCommand
{
get => GetValue(UpdateInstallerCommandProperty);
set => SetValue(UpdateInstallerCommandProperty, value);
}
public static readonly StyledProperty<ICommand> UpdateInstallerCommandProperty =
AvaloniaProperty.Register<UpdateInfoCard, ICommand>(nameof(UpdateInstallerCommand));
}

View File

@ -1,3 +1,4 @@
// Global using directives // Global using directives
global using System; global using System;
global using System.IO; global using System.IO;

View File

@ -7,11 +7,15 @@ namespace SPTInstaller.Helpers;
public static class DownloadCacheHelper public static class DownloadCacheHelper
{ {
private static HttpClient _httpClient = new() { Timeout = TimeSpan.FromHours(1) }; private static HttpClient _httpClient = new() { Timeout = TimeSpan.FromMinutes(15) };
public static string CachePath = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"spt-installer/cache");
public static string CachePath = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "spt-installer/cache");
public static string ReleaseMirrorUrl = "https://spt-releases.modd.in/release.json"; public static string ReleaseMirrorUrl = "https://spt-releases.modd.in/release.json";
public static string PatchMirrorUrl = "https://slugma.waffle-lord.net/mirrors.json"; public static string PatchMirrorUrl = "https://slugma.waffle-lord.net/mirrors.json";
public static string InstallerUrl = "https://ligma.waffle-lord.net/SPTInstaller.exe";
public static string InstallerInfoUrl = "https://ligma.waffle-lord.net/installer.json";
public static string GetCacheSizeText() public static string GetCacheSizeText()
{ {
@ -90,7 +94,8 @@ public static class DownloadCacheHelper
/// <param name="progress">A provider for progress updates</param> /// <param name="progress">A provider for progress updates</param>
/// <returns>A <see cref="FileInfo"/> object of the cached file</returns> /// <returns>A <see cref="FileInfo"/> object of the cached file</returns>
/// <remarks>If the file exists, it is deleted before downloading</remarks> /// <remarks>If the file exists, it is deleted before downloading</remarks>
public static async Task<FileInfo?> DownloadFileAsync(string outputFileName, string targetLink, IProgress<double> progress) public static async Task<FileInfo?> DownloadFileAsync(string outputFileName, string targetLink,
IProgress<double> progress)
{ {
Directory.CreateDirectory(CachePath); Directory.CreateDirectory(CachePath);
var outputFile = new FileInfo(Path.Join(CachePath, outputFileName)); var outputFile = new FileInfo(Path.Join(CachePath, outputFileName));
@ -171,7 +176,8 @@ public static class DownloadCacheHelper
/// <param name="expectedHash">The expected hash of the cached file</param> /// <param name="expectedHash">The expected hash of the cached file</param>
/// <returns>A <see cref="FileInfo"/> object of the cached file</returns> /// <returns>A <see cref="FileInfo"/> object of the cached file</returns>
/// <remarks>Use <see cref="DownloadFileAsync(string, string, IProgress{double})"/> if you don't have an expected cache file hash</remarks> /// <remarks>Use <see cref="DownloadFileAsync(string, string, IProgress{double})"/> if you don't have an expected cache file hash</remarks>
public static async Task<FileInfo?> GetOrDownloadFileAsync(string fileName, string targetLink, IProgress<double> progress, string expectedHash) public static async Task<FileInfo?> GetOrDownloadFileAsync(string fileName, string targetLink,
IProgress<double> progress, string expectedHash)
{ {
try try
{ {
@ -195,7 +201,8 @@ public static class DownloadCacheHelper
/// <param name="expectedHash">The expected hash of the cached file</param> /// <param name="expectedHash">The expected hash of the cached file</param>
/// <returns>A <see cref="FileInfo"/> object of the cached file</returns> /// <returns>A <see cref="FileInfo"/> object of the cached file</returns>
/// <remarks>Use <see cref="DownloadFileAsync(string, Stream)"/> if you don't have an expected cache file hash</remarks> /// <remarks>Use <see cref="DownloadFileAsync(string, Stream)"/> if you don't have an expected cache file hash</remarks>
public static async Task<FileInfo?> GetOrDownloadFileAsync(string fileName, Stream fileDownloadStream, string expectedHash) public static async Task<FileInfo?> GetOrDownloadFileAsync(string fileName, Stream fileDownloadStream,
string expectedHash)
{ {
try try
{ {

View File

@ -24,7 +24,8 @@ public static class FileHelper
if (currentDirRelativePath.StartsWith(exclusion) || currentDirRelativePath == exclusion) if (currentDirRelativePath.StartsWith(exclusion) || currentDirRelativePath == exclusion)
{ {
exclude = true; exclude = true;
Log.Debug($"EXCLUSION FOUND :: DIR\nExclusion: '{exclusion}'\nPath: '{currentDirRelativePath}'"); Log.Debug(
$"EXCLUSION FOUND :: DIR\nExclusion: '{exclusion}'\nPath: '{currentDirRelativePath}'");
break; break;
} }
} }
@ -34,6 +35,7 @@ public static class FileHelper
Directory.CreateDirectory(dir.FullName.Replace(sourceDir.FullName, targetDir.FullName)); Directory.CreateDirectory(dir.FullName.Replace(sourceDir.FullName, targetDir.FullName));
} }
return Result.FromSuccess(); return Result.FromSuccess();
} }
catch (Exception ex) catch (Exception ex)
@ -43,7 +45,8 @@ public static class FileHelper
} }
} }
private static Result IterateFiles(DirectoryInfo sourceDir, DirectoryInfo targetDir, string[] exclusions, Action<string, int> updateCallback = null) private static Result IterateFiles(DirectoryInfo sourceDir, DirectoryInfo targetDir, string[] exclusions,
Action<string, int> updateCallback = null)
{ {
try try
{ {
@ -63,7 +66,15 @@ public static class FileHelper
if (currentFileRelativePath.StartsWith(exclusion) || currentFileRelativePath == exclusion) if (currentFileRelativePath.StartsWith(exclusion) || currentFileRelativePath == exclusion)
{ {
exclude = true; exclude = true;
Log.Debug($"EXCLUSION FOUND :: FILE\nExclusion: '{exclusion}'\nPath: '{currentFileRelativePath}'"); Log.Debug(
$"EXCLUSION FOUND :: FILE\nExclusion: '{exclusion}'\nPath: '{currentFileRelativePath}'");
break;
}
if (currentFileRelativePath.EndsWith(".bak"))
{
exclude = true;
Log.Debug($"EXCLUDING BAK FILE :: {currentFileRelativePath}");
break; break;
} }
} }
@ -74,7 +85,8 @@ public static class FileHelper
var targetFile = file.FullName.Replace(sourceDir.FullName, targetDir.FullName); var targetFile = file.FullName.Replace(sourceDir.FullName, targetDir.FullName);
Log.Debug($"COPY\nSourceDir: '{sourceDir.FullName}'\nTargetDir: '{targetDir.FullName}'\nNewPath: '{targetFile}'"); Log.Debug(
$"COPY\nSourceDir: '{sourceDir.FullName}'\nTargetDir: '{targetDir.FullName}'\nNewPath: '{targetFile}'");
File.Copy(file.FullName, targetFile, true); File.Copy(file.FullName, targetFile, true);
processedFiles++; processedFiles++;
@ -102,10 +114,12 @@ public static class FileHelper
return path; return path;
} }
public static Result CopyDirectoryWithProgress(DirectoryInfo sourceDir, DirectoryInfo targetDir, IProgress<double> progress = null, string[] exclusions = null) => public static Result CopyDirectoryWithProgress(DirectoryInfo sourceDir, DirectoryInfo targetDir,
IProgress<double> progress = null, string[] exclusions = null) =>
CopyDirectoryWithProgress(sourceDir, targetDir, (msg, prog) => progress?.Report(prog), exclusions); CopyDirectoryWithProgress(sourceDir, targetDir, (msg, prog) => progress?.Report(prog), exclusions);
public static Result CopyDirectoryWithProgress(DirectoryInfo sourceDir, DirectoryInfo targetDir, Action<string, int> updateCallback = null, string[] exclusions = null) public static Result CopyDirectoryWithProgress(DirectoryInfo sourceDir, DirectoryInfo targetDir,
Action<string, int> updateCallback = null, string[] exclusions = null)
{ {
try try
{ {
@ -180,6 +194,7 @@ public static class FileHelper
new("Google", PathCheckType.Contains, PathCheckAction.Deny), new("Google", PathCheckType.Contains, PathCheckAction.Deny),
new("Program Files", PathCheckType.Contains, PathCheckAction.Deny), new("Program Files", PathCheckType.Contains, PathCheckAction.Deny),
new("Program Files (x86", PathCheckType.Contains, PathCheckAction.Deny), new("Program Files (x86", PathCheckType.Contains, PathCheckAction.Deny),
new(Path.Join("spt-installer", "cache"), PathCheckType.Contains, PathCheckAction.Deny),
new("Drive Root", PathCheckType.DriveRoot, PathCheckAction.Deny) new("Drive Root", PathCheckType.DriveRoot, PathCheckAction.Deny)
}; };
@ -193,6 +208,7 @@ public static class FileHelper
failedCheck = check; failedCheck = check;
return true; return true;
} }
break; break;
case PathCheckType.Contains: case PathCheckType.Contains:
if (path.ToLower().Contains(check.Target.ToLower())) if (path.ToLower().Contains(check.Target.ToLower()))
@ -200,6 +216,7 @@ public static class FileHelper
failedCheck = check; failedCheck = check;
return true; return true;
} }
break; break;
case PathCheckType.DriveRoot: case PathCheckType.DriveRoot:
if (Regex.Match(path.ToLower(), @"^\w:(\\|\/)$").Success) if (Regex.Match(path.ToLower(), @"^\w:(\\|\/)$").Success)
@ -207,11 +224,11 @@ public static class FileHelper
failedCheck = check; failedCheck = check;
return true; return true;
} }
break; break;
} }
} }
return false; return false;
} }
} }

View File

@ -6,7 +6,8 @@ namespace SPTInstaller.Helpers;
public static class HttpClientProgressExtensions public static class HttpClientProgressExtensions
{ {
public static async Task DownloadDataAsync(this HttpClient client, string requestUrl, Stream destination, IProgress<double> progress = null, CancellationToken cancellationToken = default(CancellationToken)) public static async Task DownloadDataAsync(this HttpClient client, string requestUrl, Stream destination,
IProgress<double> progress = null, CancellationToken cancellationToken = default(CancellationToken))
{ {
using (var response = await client.GetAsync(requestUrl, HttpCompletionOption.ResponseHeadersRead)) using (var response = await client.GetAsync(requestUrl, HttpCompletionOption.ResponseHeadersRead))
{ {
@ -19,8 +20,10 @@ public static class HttpClientProgressExtensions
await download.CopyToAsync(destination); await download.CopyToAsync(destination);
return; return;
} }
// Such progress and contentLength much reporting Wow! // Such progress and contentLength much reporting Wow!
var progressWrapper = new Progress<long>(totalBytes => progress.Report(GetProgressPercentage(totalBytes, contentLength.Value))); var progressWrapper = new Progress<long>(totalBytes =>
progress.Report(GetProgressPercentage(totalBytes, contentLength.Value)));
await download.CopyToAsync(destination, 81920, progressWrapper, cancellationToken); await download.CopyToAsync(destination, 81920, progressWrapper, cancellationToken);
} }
} }
@ -28,7 +31,8 @@ public static class HttpClientProgressExtensions
float GetProgressPercentage(float totalBytes, float currentBytes) => (totalBytes / currentBytes) * 100f; float GetProgressPercentage(float totalBytes, float currentBytes) => (totalBytes / currentBytes) * 100f;
} }
static async Task CopyToAsync(this Stream source, Stream destination, int bufferSize, IProgress<long> progress = null, CancellationToken cancellationToken = default(CancellationToken)) static async Task CopyToAsync(this Stream source, Stream destination, int bufferSize,
IProgress<long> progress = null, CancellationToken cancellationToken = default(CancellationToken))
{ {
if (bufferSize < 0) if (bufferSize < 0)
throw new ArgumentOutOfRangeException(nameof(bufferSize)); throw new ArgumentOutOfRangeException(nameof(bufferSize));
@ -44,7 +48,8 @@ public static class HttpClientProgressExtensions
var buffer = new byte[bufferSize]; var buffer = new byte[bufferSize];
long totalBytesRead = 0; long totalBytesRead = 0;
int bytesRead; int bytesRead;
while ((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0) while ((bytesRead =
await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0)
{ {
await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false); await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false);
totalBytesRead += bytesRead; totalBytesRead += bytesRead;

View File

@ -7,7 +7,8 @@ namespace SPTInstaller.Helpers;
public static class PreCheckHelper public static class PreCheckHelper
{ {
private const string registryInstall = @"Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\EscapeFromTarkov"; private const string registryInstall =
@"Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\EscapeFromTarkov";
public static string DetectOriginalGamePath() public static string DetectOriginalGamePath()
{ {
@ -26,7 +27,8 @@ public static class PreCheckHelper
{ {
try try
{ {
string version = FileVersionInfo.GetVersionInfo(Path.Join(gamePath, "/EscapeFromTarkov.exe")).ProductVersion.Replace('-', '.').Split('.')[^2]; string version = FileVersionInfo.GetVersionInfo(Path.Join(gamePath, "/EscapeFromTarkov.exe")).ProductVersion
.Replace('-', '.').Split('.')[^2];
return Result.FromSuccess(version); return Result.FromSuccess(version);
} }
catch (Exception ex) catch (Exception ex)

View File

@ -5,7 +5,8 @@ namespace SPTInstaller.Helpers;
public static class ZipHelper public static class ZipHelper
{ {
public static Result Decompress(FileInfo archiveFile, DirectoryInfo outputDirectory, IProgress<double> progress = null) public static Result Decompress(FileInfo archiveFile, DirectoryInfo outputDirectory,
IProgress<double> progress = null)
{ {
try try
{ {
@ -17,10 +18,7 @@ public static class ZipHelper
var extractor = new SevenZipExtractor(archiveStream); var extractor = new SevenZipExtractor(archiveStream);
extractor.Extracting += (_, args) => extractor.Extracting += (_, args) => { progress.Report(args.PercentDone); };
{
progress.Report(args.PercentDone);
};
extractor.ExtractArchive(outputDirectory.FullName); extractor.ExtractArchive(outputDirectory.FullName);

View File

@ -24,6 +24,7 @@ public class CopyClientTask : InstallerTaskBase
// relative path for exclusions // relative path for exclusions
var exclusions = new[] { "\\Logs" }; var exclusions = new[] { "\\Logs" };
return FileHelper.CopyDirectoryWithProgress(originalGameDirInfo, targetInstallDirInfo, (message, progress) => { SetStatus(null, message, progress, null, true); }, exclusions); return FileHelper.CopyDirectoryWithProgress(originalGameDirInfo, targetInstallDirInfo,
(message, progress) => { SetStatus(null, message, progress, null, true); }, exclusions);
} }
} }

View File

@ -48,7 +48,8 @@ public class DownloadTask : InstallerTaskBase
if (DownloadCacheHelper.CheckCache("patcher", _expectedPatcherHash, out var cacheFile)) if (DownloadCacheHelper.CheckCache("patcher", _expectedPatcherHash, out var cacheFile))
{ {
_data.PatcherZipInfo = cacheFile; _data.PatcherZipInfo = cacheFile;
Log.Information("Using cached file {fileName} - Hash: {hash}", _data.PatcherZipInfo.Name, _expectedPatcherHash); Log.Information("Using cached file {fileName} - Hash: {hash}", _data.PatcherZipInfo.Name,
_expectedPatcherHash);
return Result.FromSuccess(); return Result.FromSuccess();
} }
@ -74,7 +75,8 @@ public class DownloadTask : InstallerTaskBase
{ {
SetStatus("Downloading SPT-AKI", mirror.DownloadUrl, progressStyle: ProgressStyle.Indeterminate); SetStatus("Downloading SPT-AKI", mirror.DownloadUrl, progressStyle: ProgressStyle.Indeterminate);
_data.AkiZipInfo = await DownloadCacheHelper.GetOrDownloadFileAsync("sptaki", mirror.DownloadUrl, progress, mirror.Hash); _data.AkiZipInfo =
await DownloadCacheHelper.GetOrDownloadFileAsync("sptaki", mirror.DownloadUrl, progress, mirror.Hash);
if (_data.AkiZipInfo != null) if (_data.AkiZipInfo != null)
{ {

View File

@ -31,12 +31,14 @@ public class InitializationTask : InstallerTaskBase
if (_data.OriginalGamePath == null) if (_data.OriginalGamePath == null)
{ {
return Result.FromError("Unable to find original EFT directory, please make sure EFT is installed. Please also run EFT once"); return Result.FromError(
"Unable to find original EFT directory, please make sure EFT is installed. Please also run EFT once");
} }
if (File.Exists(Path.Join(_data.TargetInstallPath, "EscapeFromTarkov.exe"))) if (File.Exists(Path.Join(_data.TargetInstallPath, "EscapeFromTarkov.exe")))
{ {
return Result.FromError("Installer is located in a folder that has existing game files. Please make sure the installer is in an empty folder as per the guide"); return Result.FromError(
"Installer is located in a folder that has existing game files. Please make sure the installer is in an empty folder as per the guide");
} }
return Result.FromSuccess($"Current Game Version: {_data.OriginalGameVersion}"); return Result.FromSuccess($"Current Game Version: {_data.OriginalGameVersion}");

View File

@ -27,6 +27,8 @@ public class FreeSpacePreCheck : PreCheckBase
var eftSourceDirectoryInfo = new DirectoryInfo(_internalData.OriginalGamePath); var eftSourceDirectoryInfo = new DirectoryInfo(_internalData.OriginalGamePath);
var installTargetDirectoryInfo = new DirectoryInfo(_internalData.TargetInstallPath); var installTargetDirectoryInfo = new DirectoryInfo(_internalData.TargetInstallPath);
var cacheDirectory = new DirectoryInfo(DownloadCacheHelper.CachePath);
var eftSourceDirSize = DirectorySizeHelper.GetSizeOfDirectory(eftSourceDirectoryInfo); var eftSourceDirSize = DirectorySizeHelper.GetSizeOfDirectory(eftSourceDirectoryInfo);
if (eftSourceDirSize == -1) if (eftSourceDirSize == -1)
@ -34,20 +36,57 @@ public class FreeSpacePreCheck : PreCheckBase
return PreCheckResult.FromError("An error occurred while getting the EFT source directory size"); return PreCheckResult.FromError("An error occurred while getting the EFT source directory size");
} }
var availableSize = DriveInfo.GetDrives().FirstOrDefault(d => d.Name.ToLower() == installTargetDirectoryInfo.Root.Name.ToLower())?.AvailableFreeSpace ?? 0; var availableSize = DriveInfo.GetDrives()
.FirstOrDefault(d => d.Name.ToLower() == installTargetDirectoryInfo.Root.Name.ToLower())
?.AvailableFreeSpace ?? 0;
// add 10Gb overhead to game files for potential patches / release files // add 10Gb overhead to game files for potential patches / release files
eftSourceDirSize += 10000000000; eftSourceDirSize += 10000000000;
var availableSpaceMessage = $"Available Space: {DirectorySizeHelper.SizeSuffix(availableSize, 2)}"; var availableSpaceMessage = $"Available Space: {DirectorySizeHelper.SizeSuffix(availableSize, 2)}";
var requiredSpaceMessage = $"Space Required for EFT Client: {DirectorySizeHelper.SizeSuffix(eftSourceDirSize, 2)} including ~10Gb overhead"; var requiredSpaceMessage =
$"Space Required for EFT Client: {DirectorySizeHelper.SizeSuffix(eftSourceDirSize, 2)} including ~10Gb overhead";
var cacheDriveMessage = "";
var cacheDriveOK = true;
// if cache directory is on another drive, check that drive for around 5Gb of required space
if (cacheDirectory.Root.Name.ToLower() != installTargetDirectoryInfo.Root.Name.ToLower())
{
cacheDriveOK = false;
var availableCacheDriveSize = DriveInfo.GetDrives()
.FirstOrDefault(d =>
d.Name.ToLower() == cacheDirectory.Root.Name.ToLower())
?.AvailableFreeSpace ??
0;
// check if the drive where the cache is has at least 5Gb of free space. We should only need 2-3Gb
if (availableCacheDriveSize > 5000000000)
{
cacheDriveMessage = $"Drive for cache '{cacheDirectory.Root.Name}' has at least 5Gb of space. Available: {DirectorySizeHelper.SizeSuffix(availableCacheDriveSize, 2)}";
cacheDriveOK = true;
}
else
{
cacheDriveMessage = $"Drive for cache '{cacheDirectory.Root.Name}' does NOT have at least 5Gb of space. Available: {DirectorySizeHelper.SizeSuffix(availableCacheDriveSize, 2)}";
}
}
if (eftSourceDirSize > availableSize) if (eftSourceDirSize > availableSize)
{ {
return PreCheckResult.FromError($"Not enough free space on {installTargetDirectoryInfo.Root.Name} to install SPT\n\n{availableSpaceMessage}\n{requiredSpaceMessage}"); return PreCheckResult.FromError(
$"Not enough free space on {installTargetDirectoryInfo.Root.Name} to install SPT\n\n{availableSpaceMessage}\n{requiredSpaceMessage}\n\n{cacheDriveMessage}");
} }
return PreCheckResult.FromSuccess($"There is enough space available on {installTargetDirectoryInfo.Root.Name} to install SPT.\n\n{availableSpaceMessage}\n{requiredSpaceMessage}"); var okGameSpaceMessage =
$"There is enough space available on {installTargetDirectoryInfo.Root.Name} to install SPT.\n\n{availableSpaceMessage}\n{requiredSpaceMessage}\n\n{cacheDriveMessage}";
if (!cacheDriveOK)
{
return PreCheckResult.FromError(okGameSpaceMessage);
}
return PreCheckResult.FromSuccess(okGameSpaceMessage);
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@ -27,18 +27,24 @@ public class Net8PreCheck : PreCheckBase
FileName = "cmd.exe", FileName = "cmd.exe",
UseShellExecute = true, UseShellExecute = true,
WindowStyle = ProcessWindowStyle.Hidden, WindowStyle = ProcessWindowStyle.Hidden,
ArgumentList = { "/C", "start", "https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-desktop-8.0.2-windows-x64-installer" } ArgumentList =
{
"/C", "start",
"https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-desktop-8.0.2-windows-x64-installer"
}
}); });
}; };
try try
{ {
var programFiles = Environment.ExpandEnvironmentVariables("%ProgramW6432%"); var programFiles = Environment.ExpandEnvironmentVariables("%ProgramW6432%");
var result = ProcessHelper.RunAndReadProcessOutputs($@"{programFiles}\dotnet\dotnet.exe", "--list-runtimes"); var result =
ProcessHelper.RunAndReadProcessOutputs($@"{programFiles}\dotnet\dotnet.exe", "--list-runtimes");
if (!result.Succeeded) if (!result.Succeeded)
{ {
return PreCheckResult.FromError(result.Message + "\n\nYou most likely don't have .net 8 installed", failedButtonText, failedButtonAction); return PreCheckResult.FromError(result.Message + "\n\nYou most likely don't have .net 8 installed",
failedButtonText, failedButtonAction);
} }
output = result.StdOut.Split("\r\n"); output = result.StdOut.Split("\r\n");
@ -64,12 +70,15 @@ public class Net8PreCheck : PreCheckBase
if (foundVersion >= minRequiredVersion) if (foundVersion >= minRequiredVersion)
{ {
return PreCheckResult.FromSuccess($".Net {minRequiredVersion} Desktop Runtime or higher is installed.\n\nInstalled Version: {foundVersion}"); return PreCheckResult.FromSuccess(
$".Net {minRequiredVersion} Desktop Runtime or higher is installed.\n\nInstalled Version: {foundVersion}");
} }
highestFoundVersion = foundVersion > highestFoundVersion ? foundVersion : highestFoundVersion; highestFoundVersion = foundVersion > highestFoundVersion ? foundVersion : highestFoundVersion;
} }
return PreCheckResult.FromError($".Net Desktop Runtime version {minRequiredVersion} or higher is required.\n\nHighest Version Found: {(highestFoundVersion > new Version("0.0.0") ? highestFoundVersion : "Not Found")}\n\nThis is required to play SPT", failedButtonText, failedButtonAction); return PreCheckResult.FromError(
$".Net Desktop Runtime version {minRequiredVersion} or higher is required.\n\nHighest Version Found: {(highestFoundVersion > new Version("0.0.0") ? highestFoundVersion : "Not Found")}\n\nThis is required to play SPT",
failedButtonText, failedButtonAction);
} }
} }

View File

@ -7,7 +7,8 @@ using SPTInstaller.Helpers;
namespace SPTInstaller.Installer_Tasks.PreChecks; namespace SPTInstaller.Installer_Tasks.PreChecks;
[Obsolete("No longer needed, but keeping around for now just in case. Can be removed from code after 7/1/2024 if no issues are found")] [Obsolete(
"No longer needed, but keeping around for now just in case. Can be removed from code after 7/1/2024 if no issues are found")]
public class NetCore6PreCheck : PreCheckBase public class NetCore6PreCheck : PreCheckBase
{ {
public NetCore6PreCheck() : base(".Net Core 6 Desktop Runtime", true) public NetCore6PreCheck() : base(".Net Core 6 Desktop Runtime", true)
@ -28,18 +29,24 @@ public class NetCore6PreCheck : PreCheckBase
FileName = "cmd.exe", FileName = "cmd.exe",
UseShellExecute = true, UseShellExecute = true,
WindowStyle = ProcessWindowStyle.Hidden, WindowStyle = ProcessWindowStyle.Hidden,
ArgumentList = { "/C", "start", "https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-desktop-6.0.4-windows-x64-installer" } ArgumentList =
{
"/C", "start",
"https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-desktop-6.0.4-windows-x64-installer"
}
}); });
}; };
try try
{ {
var programFiles = Environment.ExpandEnvironmentVariables("%ProgramW6432%"); var programFiles = Environment.ExpandEnvironmentVariables("%ProgramW6432%");
var result = ProcessHelper.RunAndReadProcessOutputs($@"{programFiles}\dotnet\dotnet.exe", "--list-runtimes"); var result =
ProcessHelper.RunAndReadProcessOutputs($@"{programFiles}\dotnet\dotnet.exe", "--list-runtimes");
if (!result.Succeeded) if (!result.Succeeded)
{ {
return PreCheckResult.FromError(result.Message + "\n\nYou most likely don't have .net 6 installed", failedButtonText, failedButtonAction); return PreCheckResult.FromError(result.Message + "\n\nYou most likely don't have .net 6 installed",
failedButtonText, failedButtonAction);
} }
output = result.StdOut.Split("\r\n"); output = result.StdOut.Split("\r\n");
@ -65,12 +72,15 @@ public class NetCore6PreCheck : PreCheckBase
if (foundVersion >= minRequiredVersion) if (foundVersion >= minRequiredVersion)
{ {
return PreCheckResult.FromSuccess($".Net Core {minRequiredVersion} Desktop Runtime or higher is installed.\n\nInstalled Version: {foundVersion}"); return PreCheckResult.FromSuccess(
$".Net Core {minRequiredVersion} Desktop Runtime or higher is installed.\n\nInstalled Version: {foundVersion}");
} }
highestFoundVersion = foundVersion > highestFoundVersion ? foundVersion : highestFoundVersion; highestFoundVersion = foundVersion > highestFoundVersion ? foundVersion : highestFoundVersion;
} }
return PreCheckResult.FromError($".Net Core Desktop Runtime version {minRequiredVersion} or higher is required.\n\nHighest Version Found: {(highestFoundVersion > new Version("0.0.0") ? highestFoundVersion : "Not Found")}\n\nThis is required to play SPT, but you can install it later if and shouldn't affect the SPT install process.", failedButtonText, failedButtonAction); return PreCheckResult.FromError(
$".Net Core Desktop Runtime version {minRequiredVersion} or higher is required.\n\nHighest Version Found: {(highestFoundVersion > new Version("0.0.0") ? highestFoundVersion : "Not Found")}\n\nThis is required to play SPT, but you can install it later if and shouldn't affect the SPT install process.",
failedButtonText, failedButtonAction);
} }
} }

View File

@ -29,30 +29,40 @@ public class NetFramework472PreCheck : PreCheckBase
FileName = "cmd.exe", FileName = "cmd.exe",
UseShellExecute = true, UseShellExecute = true,
WindowStyle = ProcessWindowStyle.Hidden, WindowStyle = ProcessWindowStyle.Hidden,
ArgumentList = { "/C", "start", "https://dotnet.microsoft.com/download/dotnet-framework/thank-you/net472-developer-pack-offline-installer" } ArgumentList =
{
"/C", "start",
"https://dotnet.microsoft.com/download/dotnet-framework/thank-you/net472-developer-pack-offline-installer"
}
}); });
}; };
if (key == null) if (key == null)
{ {
return PreCheckResult.FromError("Could not find .Net Framework on system.\n\nThis is required to play SPT, but you can install it later and shouldn't affect the SPT install process.", failedButtonText, failedButtonAction); return PreCheckResult.FromError(
"Could not find .Net Framework on system.\n\nThis is required to play SPT, but you can install it later and shouldn't affect the SPT install process.",
failedButtonText, failedButtonAction);
} }
var value = key.GetValue("Version"); var value = key.GetValue("Version");
if (value == null || value is not string versionString) if (value == null || value is not string versionString)
{ {
return PreCheckResult.FromError("Something went wrong. This precheck failed for an unknown reason. :("); return PreCheckResult.FromError(
"Something went wrong. This precheck failed for an unknown reason. :(");
} }
var installedVersion = new Version(versionString); var installedVersion = new Version(versionString);
if (installedVersion < minRequiredVersion) if (installedVersion < minRequiredVersion)
{ {
return PreCheckResult.FromError($".Net Framework {versionString} is installed, but {minRequiredVersion} or higher is required.\n\nYou can install it later and shouldn't affect the SPT install process.", failedButtonText, failedButtonAction); return PreCheckResult.FromError(
$".Net Framework {versionString} is installed, but {minRequiredVersion} or higher is required.\n\nYou can install it later and shouldn't affect the SPT install process.",
failedButtonText, failedButtonAction);
} }
return PreCheckResult.FromSuccess($".Net Framework {minRequiredVersion} or higher is installed.\n\nInstalled Version: {installedVersion}"); return PreCheckResult.FromSuccess(
$".Net Framework {minRequiredVersion} or higher is installed.\n\nInstalled Version: {installedVersion}");
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@ -3,10 +3,13 @@ using SPTInstaller.Models;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace SPTInstaller.Installer_Tasks.PreChecks; namespace SPTInstaller.Installer_Tasks.PreChecks;
public class TestPreCheck : PreCheckBase public class TestPreCheck : PreCheckBase
{ {
private StatusSpinner.SpinnerState _endState; private StatusSpinner.SpinnerState _endState;
public static TestPreCheck FromRandomName(StatusSpinner.SpinnerState EndState) => new TestPreCheck($"{EndState} #{new Random().Next(0, 9999)}", EndState == StatusSpinner.SpinnerState.Error, EndState);
public static TestPreCheck FromRandomName(StatusSpinner.SpinnerState EndState) => new TestPreCheck(
$"{EndState} #{new Random().Next(0, 9999)}", EndState == StatusSpinner.SpinnerState.Error, EndState);
public TestPreCheck(string name, bool isRequired, StatusSpinner.SpinnerState endState) : base(name, isRequired) public TestPreCheck(string name, bool isRequired, StatusSpinner.SpinnerState endState) : base(name, isRequired)
{ {

View File

@ -24,13 +24,16 @@ public class ReleaseCheckTask : InstallerTaskBase
SetStatus("Checking SPT Releases", "", null, ProgressStyle.Indeterminate); SetStatus("Checking SPT Releases", "", null, ProgressStyle.Indeterminate);
var progress = new Progress<double>((d) => { SetStatus(null, null, (int)Math.Floor(d)); }); var progress = new Progress<double>((d) => { SetStatus(null, null, (int)Math.Floor(d)); });
var akiReleaseInfoFile = await DownloadCacheHelper.DownloadFileAsync("release.json", DownloadCacheHelper.ReleaseMirrorUrl, progress); var akiReleaseInfoFile =
await DownloadCacheHelper.DownloadFileAsync("release.json", DownloadCacheHelper.ReleaseMirrorUrl,
progress);
if (akiReleaseInfoFile == null) if (akiReleaseInfoFile == null)
{ {
return Result.FromError("Failed to download release metadata"); return Result.FromError("Failed to download release metadata");
} }
var akiReleaseInfo = JsonConvert.DeserializeObject<ReleaseInfo>(File.ReadAllText(akiReleaseInfoFile.FullName)); var akiReleaseInfo =
JsonConvert.DeserializeObject<ReleaseInfo>(File.ReadAllText(akiReleaseInfoFile.FullName));
SetStatus("Checking for Patches", "", null, ProgressStyle.Indeterminate); SetStatus("Checking for Patches", "", null, ProgressStyle.Indeterminate);
@ -43,7 +46,8 @@ public class ReleaseCheckTask : InstallerTaskBase
return Result.FromError("Failed to download patch mirror data"); return Result.FromError("Failed to download patch mirror data");
} }
var patchMirrorInfo = JsonConvert.DeserializeObject<PatchInfo>(File.ReadAllText(akiPatchMirrorsFile.FullName)); var patchMirrorInfo =
JsonConvert.DeserializeObject<PatchInfo>(File.ReadAllText(akiPatchMirrorsFile.FullName));
if (akiReleaseInfo == null || patchMirrorInfo == null) if (akiReleaseInfo == null || patchMirrorInfo == null)
{ {
@ -69,14 +73,17 @@ public class ReleaseCheckTask : InstallerTaskBase
patchNeedCheck = false; patchNeedCheck = false;
} }
if ((intGameVersion != patchMirrorInfo.SourceClientVersion || intAkiVersion != patchMirrorInfo.TargetClientVersion) && patchNeedCheck) if ((intGameVersion != patchMirrorInfo.SourceClientVersion ||
intAkiVersion != patchMirrorInfo.TargetClientVersion) && patchNeedCheck)
{ {
return Result.FromError("No patcher available for your version.\nA patcher is usually created within 24 hours of an EFT update."); return Result.FromError(
"No patcher available for your version.\nA patcher is usually created within 24 hours of an EFT update.");
} }
_data.PatchNeeded = patchNeedCheck; _data.PatchNeeded = patchNeedCheck;
string status = $"Current Release: {akiReleaseInfo.ClientVersion} - {(_data.PatchNeeded ? "Patch Available" : "No Patch Needed")}"; string status =
$"Current Release: {akiReleaseInfo.ClientVersion} - {(_data.PatchNeeded ? "Patch Available" : "No Patch Needed")}";
SetStatus(null, status); SetStatus(null, status);

View File

@ -49,7 +49,8 @@ public class SetupClientTask : InstallerTaskBase
var patcherDirInfo = patcherOutputDir.GetDirectories("Patcher*", SearchOption.TopDirectoryOnly).First(); var patcherDirInfo = patcherOutputDir.GetDirectories("Patcher*", SearchOption.TopDirectoryOnly).First();
var copyPatcherResult = FileHelper.CopyDirectoryWithProgress(patcherDirInfo, targetInstallDirInfo, progress); var copyPatcherResult =
FileHelper.CopyDirectoryWithProgress(patcherDirInfo, targetInstallDirInfo, progress);
if (!copyPatcherResult.Succeeded) if (!copyPatcherResult.Succeeded)
{ {

View File

@ -2,6 +2,7 @@
using System.Threading.Tasks; using System.Threading.Tasks;
namespace SPTInstaller.Interfaces; namespace SPTInstaller.Interfaces;
public interface IMirrorDownloader public interface IMirrorDownloader
{ {
public PatchInfoMirror MirrorInfo { get; } public PatchInfoMirror MirrorInfo { get; }

View File

@ -0,0 +1,9 @@
using System.Collections.Generic;
namespace SPTInstaller.Models;
public class InstallerInfo
{
public string LatestVersion { get; set; }
public List<string> Changes { get; set; }
}

View File

@ -8,6 +8,7 @@ namespace SPTInstaller.Models;
public abstract class InstallerTaskBase : ReactiveObject, IProgressableTask public abstract class InstallerTaskBase : ReactiveObject, IProgressableTask
{ {
private string _id; private string _id;
public string Id public string Id
{ {
get => _id; get => _id;
@ -15,6 +16,7 @@ public abstract class InstallerTaskBase : ReactiveObject, IProgressableTask
} }
private string _name; private string _name;
public string Name public string Name
{ {
get => _name; get => _name;
@ -22,6 +24,7 @@ public abstract class InstallerTaskBase : ReactiveObject, IProgressableTask
} }
private bool _isComleted; private bool _isComleted;
public bool IsCompleted public bool IsCompleted
{ {
get => _isComleted; get => _isComleted;
@ -29,6 +32,7 @@ public abstract class InstallerTaskBase : ReactiveObject, IProgressableTask
} }
private bool _hasErrors; private bool _hasErrors;
public bool HasErrors public bool HasErrors
{ {
get => _hasErrors; get => _hasErrors;
@ -36,6 +40,7 @@ public abstract class InstallerTaskBase : ReactiveObject, IProgressableTask
} }
private bool _isRunning; private bool _isRunning;
public bool IsRunning public bool IsRunning
{ {
get => _isRunning; get => _isRunning;
@ -43,6 +48,7 @@ public abstract class InstallerTaskBase : ReactiveObject, IProgressableTask
} }
private int _progress; private int _progress;
public int Progress public int Progress
{ {
get => _progress; get => _progress;
@ -50,6 +56,7 @@ public abstract class InstallerTaskBase : ReactiveObject, IProgressableTask
} }
private bool _showProgress; private bool _showProgress;
public bool ShowProgress public bool ShowProgress
{ {
get => _showProgress; get => _showProgress;
@ -57,6 +64,7 @@ public abstract class InstallerTaskBase : ReactiveObject, IProgressableTask
} }
private bool _indeterminateProgress; private bool _indeterminateProgress;
public bool IndeterminateProgress public bool IndeterminateProgress
{ {
get => _indeterminateProgress; get => _indeterminateProgress;
@ -64,6 +72,7 @@ public abstract class InstallerTaskBase : ReactiveObject, IProgressableTask
} }
private string _statusMessage; private string _statusMessage;
public string StatusMessage public string StatusMessage
{ {
get => _statusMessage; get => _statusMessage;
@ -71,6 +80,7 @@ public abstract class InstallerTaskBase : ReactiveObject, IProgressableTask
} }
private string _statusDetails; private string _statusDetails;
public string StatusDetails public string StatusDetails
{ {
get => _statusDetails; get => _statusDetails;
@ -91,7 +101,8 @@ public abstract class InstallerTaskBase : ReactiveObject, IProgressableTask
/// <param name="details">The details of the task. Not updated if null</param> /// <param name="details">The details of the task. Not updated if null</param>
/// <param name="progress">Progress of the task. Overrides progressStyle if a non-null value is supplied</param> /// <param name="progress">Progress of the task. Overrides progressStyle if a non-null value is supplied</param>
/// <param name="progressStyle">The style of the progress bar</param> /// <param name="progressStyle">The style of the progress bar</param>
public void SetStatus(string? message, string? details, int? progress = null, ProgressStyle? progressStyle = null, bool noLog = false) public void SetStatus(string? message, string? details, int? progress = null, ProgressStyle? progressStyle = null,
bool noLog = false)
{ {
if (message != null && message != StatusMessage) if (message != null && message != StatusMessage)
{ {

View File

@ -3,15 +3,18 @@ using Serilog;
using SPTInstaller.Helpers; using SPTInstaller.Helpers;
using System.Diagnostics; using System.Diagnostics;
using System.Threading.Tasks; using System.Threading.Tasks;
using Newtonsoft.Json;
namespace SPTInstaller.Models; namespace SPTInstaller.Models;
public class InstallerUpdateInfo : ReactiveObject public class InstallerUpdateInfo : ReactiveObject
{ {
private Version? _newVersion; public Version? NewVersion { get; private set; }
public string NewInstallerUrl = ""; public string ChangeLog = "";
private string _updateInfoText = ""; private string _updateInfoText = "";
public string UpdateInfoText public string UpdateInfoText
{ {
get => _updateInfoText; get => _updateInfoText;
@ -19,6 +22,7 @@ public class InstallerUpdateInfo : ReactiveObject
} }
private bool _show = false; private bool _show = false;
public bool Show public bool Show
{ {
get => _show; get => _show;
@ -26,6 +30,7 @@ public class InstallerUpdateInfo : ReactiveObject
} }
private bool _updating = false; private bool _updating = false;
public bool Updating public bool Updating
{ {
get => _updating; get => _updating;
@ -33,6 +38,7 @@ public class InstallerUpdateInfo : ReactiveObject
} }
private bool _updateAvailable = false; private bool _updateAvailable = false;
public bool UpdateAvailable public bool UpdateAvailable
{ {
get => _updateAvailable; get => _updateAvailable;
@ -40,6 +46,7 @@ public class InstallerUpdateInfo : ReactiveObject
} }
private bool _checkingForUpdates = false; private bool _checkingForUpdates = false;
public bool CheckingForUpdates public bool CheckingForUpdates
{ {
get => _checkingForUpdates; get => _checkingForUpdates;
@ -47,6 +54,7 @@ public class InstallerUpdateInfo : ReactiveObject
} }
private int _downloadProgress; private int _downloadProgress;
public int DownloadProgress public int DownloadProgress
{ {
get => _downloadProgress; get => _downloadProgress;
@ -81,17 +89,22 @@ public class InstallerUpdateInfo : ReactiveObject
Process.Start(new ProcessStartInfo Process.Start(new ProcessStartInfo
{ {
FileName = "powershell.exe", FileName = "powershell.exe",
ArgumentList = { "-ExecutionPolicy", "Bypass", "-File", $"{updater.FullName}", $"{newInstallerPath}", $"{Path.Join(Environment.CurrentDirectory, "SPTInstaller.exe")}" } ArgumentList =
{
"-ExecutionPolicy", "Bypass", "-File", $"{updater.FullName}", $"{newInstallerPath}",
$"{Path.Join(Environment.CurrentDirectory, "SPTInstaller.exe")}"
}
}); });
} }
private async Task<string> DownloadNewInstaller() private async Task<string> DownloadNewInstaller()
{ {
UpdateInfoText = $"Downloading installer v{_newVersion}"; UpdateInfoText = $"Downloading installer v{NewVersion}";
var progress = new Progress<double>(x => DownloadProgress = (int)x); var progress = new Progress<double>(x => DownloadProgress = (int)x);
var file = await DownloadCacheHelper.DownloadFileAsync("SPTInstller.exe", NewInstallerUrl, progress); var file = await DownloadCacheHelper.DownloadFileAsync("SPTInstaller.exe", DownloadCacheHelper.InstallerUrl,
progress);
if (file == null || !file.Exists) if (file == null || !file.Exists)
{ {
@ -115,57 +128,61 @@ public class InstallerUpdateInfo : ReactiveObject
UpdateAvailable = updateAvailable; UpdateAvailable = updateAvailable;
} }
// public async Task CheckForUpdates(Version? currentVersion) public async Task CheckForUpdates(Version? currentVersion)
// { {
// if (currentVersion == null) if (currentVersion == null)
// return; return;
//
// UpdateInfoText = "Checking for installer updates"; UpdateInfoText = "Checking for installer updates";
// Show = true; Show = true;
// CheckingForUpdates = true; CheckingForUpdates = true;
//
// try try
// { {
// var repo = new RepositoryApi(Configuration.Default); var installerInfoFile =
// await DownloadCacheHelper.DownloadFileAsync("installer.json", DownloadCacheHelper.InstallerInfoUrl,
// var releases = await repo.RepoListReleasesAsync("CWX", "SPT-AKI-Installer"); null);
//
// if (releases == null || releases.Count == 0) if (installerInfoFile == null)
// { {
// EndCheck("No releases available", false); EndCheck("Failed to download installer info", false);
// return; return;
// } }
//
// var latest = releases.FindAll(x => !x.Prerelease)[0]; var installerInfo =
// JsonConvert.DeserializeObject<InstallerInfo>(File.ReadAllText(installerInfoFile.FullName));
// if (latest == null)
// { if (installerInfo == null)
// EndCheck("could not get latest release", false); {
// return; EndCheck("Failed to parse installer info json", false);
// } return;
// }
// var latestVersion = new Version(latest.TagName);
// var latestVersion = new Version(installerInfo.LatestVersion);
// if (latestVersion == null || latestVersion <= currentVersion)
// { if (latestVersion <= currentVersion)
// EndCheck("No updates available", false); {
// return; EndCheck("No updates available", false);
// } return;
// }
// _newVersion = latestVersion;
// NewVersion = latestVersion;
// NewInstallerUrl = latest.Assets[0].BrowserDownloadUrl;
// foreach (var change in installerInfo.Changes)
// EndCheck($"Update available: v{latestVersion}", true); {
// ChangeLog += $"◉ {change}\n";
// return; }
// }
// catch (Exception ex) EndCheck($"Update Installer: v{latestVersion}", true);
// {
// EndCheck(ex.Message, false, false); return;
// Log.Error(ex, "Failed to check for updates"); }
// } catch (Exception ex)
// {
// return; EndCheck(ex.Message, false, false);
// } Log.Error(ex, "Failed to check for updates");
}
return;
}
} }

View File

@ -2,6 +2,7 @@
using System.Threading.Tasks; using System.Threading.Tasks;
namespace SPTInstaller.Models.Mirrors.Downloaders; namespace SPTInstaller.Models.Mirrors.Downloaders;
public class HttpMirrorDownloader : MirrorDownloaderBase public class HttpMirrorDownloader : MirrorDownloaderBase
{ {
public HttpMirrorDownloader(PatchInfoMirror mirror) : base(mirror) public HttpMirrorDownloader(PatchInfoMirror mirror) : base(mirror)

View File

@ -4,6 +4,7 @@ using System.Threading.Tasks;
using Serilog; using Serilog;
namespace SPTInstaller.Models.Mirrors.Downloaders; namespace SPTInstaller.Models.Mirrors.Downloaders;
public class MegaMirrorDownloader : MirrorDownloaderBase public class MegaMirrorDownloader : MirrorDownloaderBase
{ {
public MegaMirrorDownloader(PatchInfoMirror mirrorInfo) : base(mirrorInfo) public MegaMirrorDownloader(PatchInfoMirror mirrorInfo) : base(mirrorInfo)

View File

@ -2,10 +2,12 @@
using System.Threading.Tasks; using System.Threading.Tasks;
namespace SPTInstaller.Models.Mirrors.Downloaders; namespace SPTInstaller.Models.Mirrors.Downloaders;
public abstract class MirrorDownloaderBase : IMirrorDownloader public abstract class MirrorDownloaderBase : IMirrorDownloader
{ {
public PatchInfoMirror MirrorInfo { get; private set; } public PatchInfoMirror MirrorInfo { get; private set; }
public abstract Task<FileInfo?> Download(IProgress<double> progress); public abstract Task<FileInfo?> Download(IProgress<double> progress);
public MirrorDownloaderBase(PatchInfoMirror mirrorInfo) public MirrorDownloaderBase(PatchInfoMirror mirrorInfo)
{ {
MirrorInfo = mirrorInfo; MirrorInfo = mirrorInfo;

View File

@ -10,6 +10,7 @@ public abstract class PreCheckBase : ReactiveObject, IPreCheck
{ {
public event EventHandler ReeevaluationRequested = delegate { }; public event EventHandler ReeevaluationRequested = delegate { };
private string _id; private string _id;
public string Id public string Id
{ {
get => _id; get => _id;
@ -17,6 +18,7 @@ public abstract class PreCheckBase : ReactiveObject, IPreCheck
} }
private bool _isSelected; private bool _isSelected;
public bool IsSelected public bool IsSelected
{ {
get => _isSelected; get => _isSelected;
@ -24,6 +26,7 @@ public abstract class PreCheckBase : ReactiveObject, IPreCheck
} }
private string _name; private string _name;
public string Name public string Name
{ {
get => _name; get => _name;
@ -31,6 +34,7 @@ public abstract class PreCheckBase : ReactiveObject, IPreCheck
} }
private bool _required; private bool _required;
public bool IsRequired public bool IsRequired
{ {
get => _required; get => _required;
@ -38,6 +42,7 @@ public abstract class PreCheckBase : ReactiveObject, IPreCheck
} }
private StatusSpinner.SpinnerState _state; private StatusSpinner.SpinnerState _state;
public StatusSpinner.SpinnerState State public StatusSpinner.SpinnerState State
{ {
get => _state; get => _state;
@ -45,6 +50,7 @@ public abstract class PreCheckBase : ReactiveObject, IPreCheck
} }
private string _preCheckDetails; private string _preCheckDetails;
public string PreCheckDetails public string PreCheckDetails
{ {
get => _preCheckDetails; get => _preCheckDetails;
@ -52,6 +58,7 @@ public abstract class PreCheckBase : ReactiveObject, IPreCheck
} }
private bool _actionButtonIsVisible; private bool _actionButtonIsVisible;
public bool ActionButtonIsVisible public bool ActionButtonIsVisible
{ {
get => _actionButtonIsVisible; get => _actionButtonIsVisible;
@ -59,6 +66,7 @@ public abstract class PreCheckBase : ReactiveObject, IPreCheck
} }
private string _actionButtonText; private string _actionButtonText;
public string ActionButtonText public string ActionButtonText
{ {
get => _actionButtonText; get => _actionButtonText;
@ -66,6 +74,7 @@ public abstract class PreCheckBase : ReactiveObject, IPreCheck
} }
private ICommand _actionButtonCommand; private ICommand _actionButtonCommand;
public ICommand ActionButtonCommand public ICommand ActionButtonCommand
{ {
get => _actionButtonCommand; get => _actionButtonCommand;
@ -108,7 +117,9 @@ public abstract class PreCheckBase : ReactiveObject, IPreCheck
PreCheckDetails = !string.IsNullOrWhiteSpace(result.Message) PreCheckDetails = !string.IsNullOrWhiteSpace(result.Message)
? result.Message ? result.Message
: (result.Succeeded ? "Pre-Check succeeded, but no details were provided" : "Pre-Check failed, but no details were provided"); : (result.Succeeded
? "Pre-Check succeeded, but no details were provided"
: "Pre-Check failed, but no details were provided");
ActionButtonText = result.ActionButtonText; ActionButtonText = result.ActionButtonText;
ActionButtonCommand = result.ButtonPressedCommand; ActionButtonCommand = result.ButtonPressedCommand;
@ -116,7 +127,9 @@ public abstract class PreCheckBase : ReactiveObject, IPreCheck
State = ProcessResult(result); State = ProcessResult(result);
return State == StatusSpinner.SpinnerState.OK ? Result.FromSuccess() : Result.FromError($"PreCheck Failed: {Name}"); return State == StatusSpinner.SpinnerState.OK
? Result.FromSuccess()
: Result.FromError($"PreCheck Failed: {Name}");
} }
public abstract Task<PreCheckResult> CheckOperation(); public abstract Task<PreCheckResult> CheckOperation();

View File

@ -15,6 +15,7 @@ public class PreCheckDetailInfo : ReactiveObject
} }
private string _name; private string _name;
public string Name public string Name
{ {
get => _name; get => _name;
@ -22,6 +23,7 @@ public class PreCheckDetailInfo : ReactiveObject
} }
private string _details; private string _details;
public string Details public string Details
{ {
get => _details; get => _details;
@ -29,6 +31,7 @@ public class PreCheckDetailInfo : ReactiveObject
} }
private string _actionButtonText; private string _actionButtonText;
public string ActionButtonText public string ActionButtonText
{ {
get => _actionButtonText; get => _actionButtonText;
@ -36,6 +39,7 @@ public class PreCheckDetailInfo : ReactiveObject
} }
private ICommand _actionButtonCommand; private ICommand _actionButtonCommand;
public ICommand ActionButtonCommand public ICommand ActionButtonCommand
{ {
get => _actionButtonCommand; get => _actionButtonCommand;
@ -43,6 +47,7 @@ public class PreCheckDetailInfo : ReactiveObject
} }
private bool _showActionButton; private bool _showActionButton;
public bool ShowActionButton public bool ShowActionButton
{ {
get => _showActionButton; get => _showActionButton;

View File

@ -3,6 +3,7 @@ using SPTInstaller.Interfaces;
using System.Windows.Input; using System.Windows.Input;
namespace SPTInstaller.Models; namespace SPTInstaller.Models;
public class PreCheckResult : IResult public class PreCheckResult : IResult
{ {
public bool Succeeded { get; private set; } public bool Succeeded { get; private set; }
@ -31,7 +32,13 @@ public class PreCheckResult : IResult
public static PreCheckResult FromSuccess(string message = "") => new PreCheckResult(message, true, "", null); public static PreCheckResult FromSuccess(string message = "") => new PreCheckResult(message, true, "", null);
public static PreCheckResult FromError(string message, string actionButtonText = "", Action? actionButtonPressedAction = null) => new PreCheckResult(message, false, actionButtonText, actionButtonPressedAction); public static PreCheckResult FromError(string message, string actionButtonText = "",
Action? actionButtonPressedAction = null) =>
new PreCheckResult(message, false, actionButtonText, actionButtonPressedAction);
public static PreCheckResult FromException(Exception ex, string actionButtonText = "", Action? actionButtonPressedAction = null) => new PreCheckResult($"An exception was thrown during this precheck\n\nException:\n{ex.Message}\n\nStacktrace:\n{ex.StackTrace}", false, actionButtonText, actionButtonPressedAction); public static PreCheckResult
FromException(Exception ex, string actionButtonText = "", Action? actionButtonPressedAction = null) =>
new PreCheckResult(
$"An exception was thrown during this precheck\n\nException:\n{ex.Message}\n\nStacktrace:\n{ex.StackTrace}",
false, actionButtonText, actionButtonPressedAction);
} }

View File

@ -5,7 +5,8 @@ public class ReadProcessResult : Result
public string StdOut { get; } public string StdOut { get; }
public string StdErr { get; } public string StdErr { get; }
protected ReadProcessResult(string message, bool succeeded, string stdOut = "", string stdErr = "") : base(message, succeeded) protected ReadProcessResult(string message, bool succeeded, string stdOut = "", string stdErr = "") : base(message,
succeeded)
{ {
StdOut = stdOut; StdOut = stdOut;
StdErr = stdErr; StdErr = stdErr;

View File

@ -1,10 +1,10 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace SPTInstaller.Models.ReleaseInfo; namespace SPTInstaller.Models.ReleaseInfo;
public class ReleaseInfo public class ReleaseInfo
{ {
public string AkiVersion { get; set; } public string AkiVersion { get; set; }
public string ClientVersion { get; set; } public string ClientVersion { get; set; }
public List<ReleaseInfoMirror> Mirrors { get; set; } public List<ReleaseInfoMirror> Mirrors { get; set; }
} }

View File

@ -1,4 +1,5 @@
namespace SPTInstaller.Models.ReleaseInfo; namespace SPTInstaller.Models.ReleaseInfo;
public class ReleaseInfoMirror public class ReleaseInfoMirror
{ {
public string DownloadUrl { get; set; } public string DownloadUrl { get; set; }

Binary file not shown.

View File

@ -9,7 +9,8 @@ Write-Host "Stopping installer ..."
$installer = Stop-Process -Name "SPTInstaller" -ErrorAction SilentlyContinue $installer = Stop-Process -Name "SPTInstaller" -ErrorAction SilentlyContinue
if ($installer -ne $null) { if ($installer -ne $null)
{
Write-Host "Something went wrong, couldn't stop installer process'" Write-Host "Something went wrong, couldn't stop installer process'"
return; return;
} }

View File

@ -9,8 +9,8 @@
<PackageIcon>icon.ico</PackageIcon> <PackageIcon>icon.ico</PackageIcon>
<ApplicationIcon>Assets\icon.ico</ApplicationIcon> <ApplicationIcon>Assets\icon.ico</ApplicationIcon>
<Configurations>Debug;Release;TEST</Configurations> <Configurations>Debug;Release;TEST</Configurations>
<AssemblyVersion>2.61</AssemblyVersion> <AssemblyVersion>2.62</AssemblyVersion>
<FileVersion>2.61</FileVersion> <FileVersion>2.62</FileVersion>
<Company>SPT-AKI</Company> <Company>SPT-AKI</Company>
</PropertyGroup> </PropertyGroup>

View File

@ -11,13 +11,15 @@ namespace SPTInstaller.ViewModels;
public class InstallViewModel : ViewModelBase public class InstallViewModel : ViewModelBase
{ {
private IProgressableTask _currentTask; private IProgressableTask _currentTask;
public IProgressableTask CurrentTask public IProgressableTask CurrentTask
{ {
get => _currentTask; get => _currentTask;
set => this.RaiseAndSetIfChanged(ref _currentTask, value); set => this.RaiseAndSetIfChanged(ref _currentTask, value);
} }
public ObservableCollection<InstallerTaskBase> MyTasks { get; set; } = new(ServiceHelper.GetAll<InstallerTaskBase>()); public ObservableCollection<InstallerTaskBase> MyTasks { get; set; } =
new(ServiceHelper.GetAll<InstallerTaskBase>());
public InstallViewModel(IScreen host) : base(host) public InstallViewModel(IScreen host) : base(host)
{ {

View File

@ -12,6 +12,7 @@ public class MainWindowViewModel : ReactiveObject, IActivatableViewModel, IScree
public ViewModelActivator Activator { get; } = new(); public ViewModelActivator Activator { get; } = new();
private string _title; private string _title;
public string Title public string Title
{ {
get => _title; get => _title;
@ -20,7 +21,8 @@ public class MainWindowViewModel : ReactiveObject, IActivatableViewModel, IScree
public MainWindowViewModel(bool debugging) public MainWindowViewModel(bool debugging)
{ {
Title = $"{(debugging ? "-debug-" : "")} SPT Installer {"v" + Assembly.GetExecutingAssembly().GetName()?.Version?.ToString() ?? "--unknown version--"}"; Title =
$"{(debugging ? "-debug-" : "")} SPT Installer {"v" + Assembly.GetExecutingAssembly().GetName()?.Version?.ToString() ?? "--unknown version--"}";
Log.Information($"========= {Title} Started ========="); Log.Information($"========= {Title} Started =========");
Log.Information(Environment.OSVersion.VersionString); Log.Information(Environment.OSVersion.VersionString);
@ -34,7 +36,8 @@ public class MainWindowViewModel : ReactiveObject, IActivatableViewModel, IScree
public void CloseCommand() public void CloseCommand()
{ {
if (Application.Current.ApplicationLifetime is Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime desktopApp) if (Application.Current.ApplicationLifetime is
Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime desktopApp)
{ {
desktopApp.MainWindow.Close(); desktopApp.MainWindow.Close();
} }
@ -42,7 +45,8 @@ public class MainWindowViewModel : ReactiveObject, IActivatableViewModel, IScree
public void MinimizeCommand() public void MinimizeCommand()
{ {
if (Application.Current.ApplicationLifetime is Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime desktopApp) if (Application.Current.ApplicationLifetime is
Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime desktopApp)
{ {
desktopApp.MainWindow.WindowState = Avalonia.Controls.WindowState.Minimized; desktopApp.MainWindow.WindowState = Avalonia.Controls.WindowState.Minimized;
} }

View File

@ -12,6 +12,7 @@ namespace SPTInstaller.ViewModels;
public class MessageViewModel : ViewModelBase public class MessageViewModel : ViewModelBase
{ {
private bool _HasErrors; private bool _HasErrors;
public bool HasErrors public bool HasErrors
{ {
get => _HasErrors; get => _HasErrors;
@ -19,6 +20,7 @@ public class MessageViewModel : ViewModelBase
} }
private string _Message; private string _Message;
public string Message public string Message
{ {
get => _Message; get => _Message;
@ -26,6 +28,7 @@ public class MessageViewModel : ViewModelBase
} }
private bool _showCloseButton; private bool _showCloseButton;
public bool ShowCloseButton public bool ShowCloseButton
{ {
get => _showCloseButton; get => _showCloseButton;
@ -33,6 +36,7 @@ public class MessageViewModel : ViewModelBase
} }
private string _cacheInfoText; private string _cacheInfoText;
public string CacheInfoText public string CacheInfoText
{ {
get => _cacheInfoText; get => _cacheInfoText;
@ -40,6 +44,7 @@ public class MessageViewModel : ViewModelBase
} }
private StatusSpinner.SpinnerState _cacheCheckState; private StatusSpinner.SpinnerState _cacheCheckState;
public StatusSpinner.SpinnerState CacheCheckState public StatusSpinner.SpinnerState CacheCheckState
{ {
get => _cacheCheckState; get => _cacheCheckState;
@ -48,7 +53,8 @@ public class MessageViewModel : ViewModelBase
public ICommand CloseCommand { get; set; } = ReactiveCommand.Create(() => public ICommand CloseCommand { get; set; } = ReactiveCommand.Create(() =>
{ {
if (Application.Current.ApplicationLifetime is Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime desktopApp) if (Application.Current.ApplicationLifetime is
Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime desktopApp)
{ {
desktopApp.MainWindow.Close(); desktopApp.MainWindow.Close();
} }

View File

@ -36,6 +36,8 @@ public class PreChecksViewModel : ViewModelBase
public ICommand DismissUpdateCommand { get; set; } public ICommand DismissUpdateCommand { get; set; }
public ICommand WhatsNewCommand { get; set; }
public ICommand LaunchWithDebug { get; set; } public ICommand LaunchWithDebug { get; set; }
public InstallerUpdateInfo UpdateInfo { get; set; } = new(); public InstallerUpdateInfo UpdateInfo { get; set; } = new();
@ -49,6 +51,7 @@ public class PreChecksViewModel : ViewModelBase
} }
private string _installPath; private string _installPath;
public string InstallPath public string InstallPath
{ {
get => _installPath; get => _installPath;
@ -64,6 +67,7 @@ public class PreChecksViewModel : ViewModelBase
} }
private bool _allowInstall; private bool _allowInstall;
public bool AllowInstall public bool AllowInstall
{ {
get => _allowInstall; get => _allowInstall;
@ -71,6 +75,7 @@ public class PreChecksViewModel : ViewModelBase
} }
private bool _allowDetailsButton = false; private bool _allowDetailsButton = false;
public bool AllowDetailsButton public bool AllowDetailsButton
{ {
get => _allowDetailsButton; get => _allowDetailsButton;
@ -78,6 +83,7 @@ public class PreChecksViewModel : ViewModelBase
} }
private string _cacheInfoText; private string _cacheInfoText;
public string CacheInfoText public string CacheInfoText
{ {
get => _cacheInfoText; get => _cacheInfoText;
@ -85,6 +91,7 @@ public class PreChecksViewModel : ViewModelBase
} }
private StatusSpinner.SpinnerState _cacheCheckState; private StatusSpinner.SpinnerState _cacheCheckState;
public StatusSpinner.SpinnerState CacheCheckState public StatusSpinner.SpinnerState CacheCheckState
{ {
get => _cacheCheckState; get => _cacheCheckState;
@ -92,6 +99,7 @@ public class PreChecksViewModel : ViewModelBase
} }
private StatusSpinner.SpinnerState _installButtonCheckState; private StatusSpinner.SpinnerState _installButtonCheckState;
public StatusSpinner.SpinnerState InstallButtonCheckState public StatusSpinner.SpinnerState InstallButtonCheckState
{ {
get => _installButtonCheckState; get => _installButtonCheckState;
@ -123,7 +131,8 @@ public class PreChecksViewModel : ViewModelBase
if (data == null || installer == null) if (data == null || installer == null)
{ {
NavigateTo(new MessageViewModel(HostScreen, Result.FromError("Failed to get required service for prechecks"))); NavigateTo(new MessageViewModel(HostScreen,
Result.FromError("Failed to get required service for prechecks")));
return; return;
} }
@ -137,7 +146,8 @@ public class PreChecksViewModel : ViewModelBase
#if !TEST #if !TEST
if (data.OriginalGamePath == null) if (data.OriginalGamePath == null)
{ {
NavigateTo(new MessageViewModel(HostScreen, Result.FromError("Could not find EFT install.\n\nDo you own and have the game installed?"))); NavigateTo(new MessageViewModel(HostScreen,
Result.FromError("Could not find EFT install.\n\nDo you own and have the game installed?")));
return; return;
} }
#endif #endif
@ -155,10 +165,15 @@ public class PreChecksViewModel : ViewModelBase
{ {
File.Delete(file); File.Delete(file);
} }
catch { } catch
{
}
} }
NavigateTo(new MessageViewModel(HostScreen, Result.FromError("Installer is located in EFT's original directory. Please move the installer to a seperate folder as per the guide"), noLog: true)); NavigateTo(new MessageViewModel(HostScreen,
Result.FromError(
"Installer is located in EFT's original directory. Please move the installer to a seperate folder as per the guide"),
noLog: true));
return; return;
} }
@ -190,7 +205,9 @@ public class PreChecksViewModel : ViewModelBase
case PathCheckAction.Deny: case PathCheckAction.Deny:
{ {
Log.Error("Problem path detected, install denied"); Log.Error("Problem path detected, install denied");
NavigateTo(new MessageViewModel(HostScreen, Result.FromError($"We suspect you may be installing into a problematic folder: {failedCheck.Target}.\nWe won't be letting you install here. Please move the installer to another folder.\nSuggestion: a folder under your drive root, such as 'C:\\spt\\'\nDenied Path: {InstallPath}"))); NavigateTo(new MessageViewModel(HostScreen,
Result.FromError(
$"We suspect you may be installing into a problematic folder: {failedCheck.Target}.\nWe won't be letting you install here. Please move the installer to another folder.\nSuggestion: a folder under your drive root, such as 'C:\\spt\\'\nDenied Path: {InstallPath}")));
break; break;
} }
default: default:
@ -250,10 +267,10 @@ public class PreChecksViewModel : ViewModelBase
await UpdateInfo.UpdateInstaller(); await UpdateInfo.UpdateInstaller();
}); });
DismissUpdateCommand = ReactiveCommand.Create(() => DismissUpdateCommand = ReactiveCommand.Create(() => { UpdateInfo.Show = false; });
{
UpdateInfo.Show = false; WhatsNewCommand =
}); ReactiveCommand.Create(async () => await DialogHost.Show(new ChangeLogDialog(UpdateInfo.NewVersion.ToString(), UpdateInfo.ChangeLog)));
Task.Run(async () => Task.Run(async () =>
@ -262,14 +279,16 @@ public class PreChecksViewModel : ViewModelBase
var result = await installer.RunPreChecks(); var result = await installer.RunPreChecks();
// check for updates // check for updates
//await UpdateInfo.CheckForUpdates(Assembly.GetExecutingAssembly().GetName()?.Version); await UpdateInfo.CheckForUpdates(Assembly.GetExecutingAssembly().GetName()?.Version);
// get latest spt version // get latest spt version
InstallButtonText = "Getting latest release ..."; InstallButtonText = "Getting latest release ...";
InstallButtonCheckState = StatusSpinner.SpinnerState.Running; InstallButtonCheckState = StatusSpinner.SpinnerState.Running;
var progress = new Progress<double>((d) => { }); var progress = new Progress<double>((d) => { });
var akiReleaseInfoFile = await DownloadCacheHelper.DownloadFileAsync("release.json", DownloadCacheHelper.ReleaseMirrorUrl, progress); var akiReleaseInfoFile =
await DownloadCacheHelper.DownloadFileAsync("release.json", DownloadCacheHelper.ReleaseMirrorUrl,
progress);
if (akiReleaseInfoFile == null) if (akiReleaseInfoFile == null)
{ {
InstallButtonText = "Could not get SPT release metadata"; InstallButtonText = "Could not get SPT release metadata";
@ -277,7 +296,8 @@ public class PreChecksViewModel : ViewModelBase
return; return;
} }
var akiReleaseInfo = JsonConvert.DeserializeObject<ReleaseInfo>(File.ReadAllText(akiReleaseInfoFile.FullName)); var akiReleaseInfo =
JsonConvert.DeserializeObject<ReleaseInfo>(File.ReadAllText(akiReleaseInfoFile.FullName));
if (akiReleaseInfo == null) if (akiReleaseInfo == null)
{ {
InstallButtonText = "Could not parse latest SPT release"; InstallButtonText = "Could not parse latest SPT release";

View File

@ -45,10 +45,7 @@ public class ViewModelBase : ReactiveObject, IActivatableViewModel, IRoutableVie
/// <param name="ViewModel"></param> /// <param name="ViewModel"></param>
public void NavigateTo(ViewModelBase ViewModel) public void NavigateTo(ViewModelBase ViewModel)
{ {
Dispatcher.UIThread.InvokeAsync(() => Dispatcher.UIThread.InvokeAsync(() => { HostScreen.Router.Navigate.Execute(ViewModel); });
{
HostScreen.Router.Navigate.Execute(ViewModel);
});
} }
public ViewModelBase(IScreen Host) public ViewModelBase(IScreen Host)

View File

@ -12,14 +12,12 @@
Background="{StaticResource AKI_Background_Dark}" Background="{StaticResource AKI_Background_Dark}"
PendingColor="Gray" PendingColor="Gray"
RunningColor="DodgerBlue" RunningColor="DodgerBlue"
CompletedColor="{StaticResource AKI_Brush_Yellow}" CompletedColor="{StaticResource AKI_Brush_Yellow}" />
/>
<cc:TaskDetails Grid.Column="1" <cc:TaskDetails Grid.Column="1"
Message="{Binding CurrentTask.StatusMessage}" Message="{Binding CurrentTask.StatusMessage}"
Details="{Binding CurrentTask.StatusDetails}" Details="{Binding CurrentTask.StatusDetails}"
Progress="{Binding CurrentTask.Progress}" Progress="{Binding CurrentTask.Progress}"
IndeterminateProgress="{Binding CurrentTask.IndeterminateProgress}" IndeterminateProgress="{Binding CurrentTask.IndeterminateProgress}"
ShowProgress="{Binding CurrentTask.ShowProgress}" ShowProgress="{Binding CurrentTask.ShowProgress}" />
/>
</Grid> </Grid>
</UserControl> </UserControl>

View File

@ -16,8 +16,7 @@
ExtendClientAreaChromeHints="NoChrome" ExtendClientAreaChromeHints="NoChrome"
ExtendClientAreaTitleBarHeightHint="-1" ExtendClientAreaTitleBarHeightHint="-1"
Background="{StaticResource AKI_Background_Light}" Background="{StaticResource AKI_Background_Light}"
MinWidth="800" MinHeight="400" MinWidth="800" MinHeight="400">
>
<Window.Styles> <Window.Styles>
<StyleInclude Source="/Assets/Styles.axaml" /> <StyleInclude Source="/Assets/Styles.axaml" />
@ -31,8 +30,7 @@
<Grid RowDefinitions="AUTO,*"> <Grid RowDefinitions="AUTO,*">
<cc:TitleBar Title="{Binding Title}" <cc:TitleBar Title="{Binding Title}"
XButtonCommand="{Binding CloseCommand}" XButtonCommand="{Binding CloseCommand}"
MinButtonCommand="{Binding MinimizeCommand}" MinButtonCommand="{Binding MinimizeCommand}" />
/>
<dialogHost:DialogHost Grid.Row="1" Background="{StaticResource AKI_Background_Light}"> <dialogHost:DialogHost Grid.Row="1" Background="{StaticResource AKI_Background_Light}">
<rxui:RoutedViewHost Router="{Binding Router}" /> <rxui:RoutedViewHost Router="{Binding Router}" />

View File

@ -4,8 +4,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:cc="using:SPTInstaller.CustomControls" xmlns:cc="using:SPTInstaller.CustomControls"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="SPTInstaller.Views.MessageView" x:Class="SPTInstaller.Views.MessageView">
>
<UserControl.Styles> <UserControl.Styles>
<Style Selector="Grid.error"> <Style Selector="Grid.error">
<Setter Property="Background" Value="#330000" /> <Setter Property="Background" Value="#330000" />
@ -24,8 +23,7 @@
<TextBlock Text="{Binding Message}" FontSize="18" <TextBlock Text="{Binding Message}" FontSize="18"
TextWrapping="Wrap" TextWrapping="Wrap"
MaxWidth="500" MaxWidth="500"
HorizontalAlignment="Center" HorizontalAlignment="Center" />
/>
</Label> </Label>
<Button Grid.Column="1" Grid.Row="3" <Button Grid.Column="1" Grid.Row="3"
@ -35,12 +33,10 @@
IsVisible="{Binding ShowCloseButton}" IsVisible="{Binding ShowCloseButton}"
HorizontalAlignment="Center" HorizontalAlignment="Center"
VerticalContentAlignment="Center" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" HorizontalContentAlignment="Center"
Padding="20 10" Padding="20 10" />
/>
<cc:CacheInfo Grid.Row="4" Grid.ColumnSpan="3" Padding="10" Margin="10 0 0 0" <cc:CacheInfo Grid.Row="4" Grid.ColumnSpan="3" Padding="10" Margin="10 0 0 0"
VerticalAlignment="Bottom" VerticalAlignment="Bottom"
InfoText="{Binding CacheInfoText}" State="{Binding CacheCheckState}" InfoText="{Binding CacheInfoText}" State="{Binding CacheCheckState}" />
/>
</Grid> </Grid>
</UserControl> </UserControl>

View File

@ -16,24 +16,33 @@
<Grid RowDefinitions="10, Auto, *, Auto, 10" ColumnDefinitions="10, 2*, Auto,*, 10"> <Grid RowDefinitions="10, Auto, *, Auto, 10" ColumnDefinitions="10, 2*, Auto,*, 10">
<Label Grid.Row="1" Grid.Column="1" HorizontalAlignment="Center" <Label Grid.Row="1" Grid.Column="1" HorizontalAlignment="Center"
Content="Details" FontSize="20" Content="Details" FontSize="20" Margin="0 2 0 0"/>
/>
<!-- selected precheck details grid --> <!-- selected precheck details grid -->
<cc:PreCheckDetails Grid.Row="2" Grid.Column="1" <cc:PreCheckDetails Grid.Row="2" Grid.Column="1"
PreChecks="{Binding PreChecks}" PreChecks="{Binding PreChecks}"
HasSelection="{Binding HasPreCheckSelected}" HasSelection="{Binding HasPreCheckSelected}" />
/>
<!-- info card vertical separator --> <!-- info card vertical separator -->
<Rectangle Grid.Row="1" Grid.RowSpan="2" Grid.Column="2" VerticalAlignment="Stretch" <Rectangle Grid.Row="1" Grid.RowSpan="2" Grid.Column="2" VerticalAlignment="Stretch"
Fill="black" Width="1" Margin="10 0" Fill="black" Width="1" Margin="10 0" />
/>
<!-- precheck list --> <!-- precheck list -->
<Label Grid.Row="1" Grid.Column="3" HorizontalAlignment="Center" <Grid Grid.Row="1" Grid.Column="3">
<Label HorizontalAlignment="Center"
Margin="0 2 0 0"
Content="Pre-Checks" FontSize="20" Content="Pre-Checks" FontSize="20"
/> />
<Button Padding="10" x:Name="debugBtn"
HorizontalAlignment="Right" VerticalAlignment="Top"
Classes="icon"
Command="{Binding LaunchWithDebug}"
IsVisible="{Binding !Debugging}"
>
<Path Data="{StaticResource Bug}" Fill="{Binding ElementName=debugBtn, Path=Foreground}"
/>
</Button>
</Grid>
<ItemsControl ItemsSource="{Binding PreChecks}" Grid.Row="2" Grid.Column="3"> <ItemsControl ItemsSource="{Binding PreChecks}" Grid.Row="2" Grid.Column="3">
<ItemsControl.ItemsPanel> <ItemsControl.ItemsPanel>
@ -48,8 +57,7 @@
IsSelected="{Binding IsSelected}" IsSelected="{Binding IsSelected}"
State="{Binding State}" State="{Binding State}"
SelectCommand="{Binding $parent[ItemsControl].DataContext.SelectPreCheckCommand}" SelectCommand="{Binding $parent[ItemsControl].DataContext.SelectPreCheckCommand}"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch" />
/>
</DataTemplate> </DataTemplate>
</ItemsControl.ItemTemplate> </ItemsControl.ItemTemplate>
</ItemsControl> </ItemsControl>
@ -57,8 +65,7 @@
<!-- cache info --> <!-- cache info -->
<cc:CacheInfo Grid.Row="2" Grid.Column="3" Padding="10" <cc:CacheInfo Grid.Row="2" Grid.Column="3" Padding="10"
VerticalAlignment="Bottom" HorizontalAlignment="Left" VerticalAlignment="Bottom" HorizontalAlignment="Left"
InfoText="{Binding CacheInfoText}" State="{Binding CacheCheckState}" InfoText="{Binding CacheInfoText}" State="{Binding CacheCheckState}" />
/>
</Grid> </Grid>
</Border> </Border>
@ -69,8 +76,7 @@
<TextBlock TextWrapping="Wrap" Margin="3 0" <TextBlock TextWrapping="Wrap" Margin="3 0"
Text="{Binding InstallPath}" Text="{Binding InstallPath}"
Foreground="DodgerBlue" FontWeight="SemiBold" Foreground="DodgerBlue" FontWeight="SemiBold" />
/>
</StackPanel> </StackPanel>
<!-- Start install button --> <!-- Start install button -->
@ -80,11 +86,11 @@
FontSize="15" FontWeight="SemiBold" FontSize="15" FontWeight="SemiBold"
Classes="yellow" Classes="yellow"
Command="{Binding StartInstallCommand}" Command="{Binding StartInstallCommand}"
CornerRadius="15" CornerRadius="15">
>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding InstallButtonText}" VerticalAlignment="Center" Foreground="Black" /> <TextBlock Text="{Binding InstallButtonText}" VerticalAlignment="Center" Foreground="Black" />
<cc:StatusSpinner State="{Binding InstallButtonCheckState}" Margin="2" IsVisible="{Binding !AllowInstall}"/> <cc:StatusSpinner State="{Binding InstallButtonCheckState}" Margin="2"
IsVisible="{Binding !AllowInstall}" />
</StackPanel> </StackPanel>
</Button> </Button>
@ -97,9 +103,9 @@
Updating="{Binding UpdateInfo.Updating}" Updating="{Binding UpdateInfo.Updating}"
DismissCommand="{Binding DismissUpdateCommand}" DismissCommand="{Binding DismissUpdateCommand}"
UpdateCommand="{Binding UpdateInstallerCommand}" UpdateCommand="{Binding UpdateInstallerCommand}"
WhatsNewCommand="{Binding WhatsNewCommand}"
DownloadProgress="{Binding UpdateInfo.DownloadProgress}" DownloadProgress="{Binding UpdateInfo.DownloadProgress}"
UpdateAvailable="{Binding UpdateInfo.UpdateAvailable}" UpdateAvailable="{Binding UpdateInfo.UpdateAvailable}"
CheckingForUpdate="{Binding UpdateInfo.CheckingForUpdates}" CheckingForUpdate="{Binding UpdateInfo.CheckingForUpdates}" />
/>
</Grid> </Grid>
</UserControl> </UserControl>

View File

@ -0,0 +1,9 @@
{
// You will want to remove comments before using this template
"latestVersion": "0.0", // the new version you are pushing
"changes": [ // a list of chnages. These will be formated for you. Don't add leading bullets or such
"changes",
"go",
"here"
]
}