mirror of
https://github.com/knah/VRCMelonAssistant.git
synced 2024-06-13 07:55:03 +12:00
Compare commits
536 commits
Author | SHA1 | Date | |
---|---|---|---|
f905428238 | |||
b74c421bbb | |||
71efc3990f | |||
d57e730baa | |||
2adb976b21 | |||
59cd085130 | |||
992480facc | |||
8160521d10 | |||
a6321a9ecc | |||
88c987d714 | |||
610098dcbf | |||
ec8cd210f7 | |||
66d2b41de8 | |||
ed27510ccc | |||
0f60e68c02 | |||
f250bb893c | |||
b1e2b3510b | |||
29ebdd82f9 | |||
6cc5510c5d | |||
e1b7320449 | |||
8f93c766ac | |||
75a3cc8392 | |||
ea59c178e6 | |||
9d453e7419 | |||
3a9b80afdb | |||
4b69295fda | |||
f755fc8b4a | |||
c9b6c594cc | |||
3cebca05b6 | |||
5b4a6a9c1b | |||
7788c2573c | |||
ab775ea496 | |||
2d2477d19d | |||
37375b1026 | |||
ee06f36ab9 | |||
84c879c16f | |||
2c32da0ed5 | |||
b9e58c0b8c | |||
6b4450156e | |||
6d7e02aaa4 | |||
7a824f8230 | |||
260441282a | |||
5198383b2f | |||
bc001f5852 | |||
3bf63e3122 | |||
8fe1e5494e | |||
177820b954 | |||
02c30d5158 | |||
140dde19fd | |||
3b9dc222d0 | |||
c33baacc9e | |||
2c7018a9d1 | |||
a298d226ec | |||
df1cd91358 | |||
d5a2884390 | |||
118352b5f3 | |||
ed8170c5e0 | |||
c592e0ccbc | |||
71819281a5 | |||
89bdd100bb | |||
688857aea9 | |||
5901ffb5de | |||
d3ad20d082 | |||
1884b7f9ec | |||
c10f9b88f6 | |||
cbdc019b1c | |||
1c0f6dcaba | |||
d4753910f4 | |||
cfe66c84e1 | |||
2f0375bb86 | |||
57822f3721 | |||
ac027753ad | |||
47602291a0 | |||
b5223f36a9 | |||
0ff1c5164c | |||
be06ee7a99 | |||
8659cbb7f7 | |||
7ba79ecd41 | |||
5f814ab58e | |||
1c1332f1b5 | |||
0b702e8840 | |||
503aad4af2 | |||
1c14aba0d6 | |||
e4a6fe0335 | |||
f41b7a7b23 | |||
b268d472bf | |||
ad62d5ecbd | |||
223da16d7f | |||
e3726cbc63 | |||
d9d9c9e5d0 | |||
69f3f0556a | |||
bb5729ef9b | |||
24918e6079 | |||
8a05d0e4a0 | |||
3039f744a5 | |||
60893fd40c | |||
ea5b271954 | |||
271f649cb5 | |||
9eda65c5bc | |||
9e92464ceb | |||
4832649002 | |||
3f9cf7c2cd | |||
f105e5336f | |||
84382c48b2 | |||
7f94b2bf33 | |||
1b1c209df4 | |||
5294455fdd | |||
18c35bb70f | |||
b0f9e8b6db | |||
db9c241d26 | |||
98181a50df | |||
f905e0d172 | |||
a3ded0c9bf | |||
4878bac01e | |||
def19229f5 | |||
e852ad3516 | |||
a0314af766 | |||
6651df67ee | |||
addf3e858f | |||
ca87635473 | |||
272251f79b | |||
5a36a2f4db | |||
b850b928db | |||
1c8ae4e604 | |||
5245c7838c | |||
10c90ef9ec | |||
215bc0f081 | |||
068647f8f5 | |||
ff357cbc03 | |||
9f013d3af6 | |||
58de249f1d | |||
c170d91d26 | |||
2d53be9009 | |||
e10cd23a91 | |||
29322a4807 | |||
d9594ff6d8 | |||
c92da4c012 | |||
7ff93ec823 | |||
eead2780e9 | |||
57496ff873 | |||
e01cb6b968 | |||
8ecba1e076 | |||
bc3b5b6748 | |||
9fefe75735 | |||
3a459a262a | |||
05890fec66 | |||
7e17946202 | |||
44e87bf3ec | |||
adaca10ddb | |||
86977c68eb | |||
4fc3417360 | |||
8d8bf90088 | |||
6c2894e5d4 | |||
2301eb3303 | |||
98d1549fcb | |||
4cbf2c244f | |||
de39c64ddd | |||
436593a509 | |||
474c51d165 | |||
3072ded757 | |||
c54165ec72 | |||
f78e26ac2f | |||
1fd55d9df6 | |||
869802f59d | |||
359d77a96b | |||
eb246cf7d0 | |||
8aa828739c | |||
4e70f55bf8 | |||
affa4aa6ff | |||
cbc84472a7 | |||
e91fc54a90 | |||
fa9dec967c | |||
2c7d6fc349 | |||
e785ccea3e | |||
2c2a44b3c3 | |||
b9319f9990 | |||
6327c197fb | |||
a120caf001 | |||
fe2fcfb7f9 | |||
d1203ac255 | |||
53dd2115ef | |||
d96cb8bb95 | |||
11664afdf4 | |||
3dc29753f1 | |||
97ef2c1285 | |||
e3f8ab9696 | |||
79936c79f3 | |||
ff327ecffc | |||
3fe99bb5e2 | |||
83760c6a0a | |||
6495cab513 | |||
ef197d1611 | |||
a6b6c79515 | |||
c16568dfd2 | |||
9319d0dbc0 | |||
28e1624b64 | |||
425d96f83d | |||
b81e264709 | |||
626881b730 | |||
cf4d0c4c51 | |||
56d21f1432 | |||
145c350c14 | |||
9622cef810 | |||
0576d293b8 | |||
c9bceb8a21 | |||
ec03c8127b | |||
dba0a7d951 | |||
d880ae4024 | |||
3a146cb8f4 | |||
932dd2f0f3 | |||
23135b0725 | |||
86818d44b4 | |||
cf8cec9a65 | |||
c59105b91c | |||
16dd4ba72f | |||
05dc6b502a | |||
a2dd0f71b1 | |||
d353ce7f4b | |||
ff2a63429c | |||
62abdfa0b5 | |||
0ed8aafd99 | |||
1d65e3ad13 | |||
7d60f3f4a4 | |||
80a9339227 | |||
9009ea4a87 | |||
1a55d70928 | |||
4a71c5cfe1 | |||
3bb30e57f3 | |||
a556b56b75 | |||
e40a791beb | |||
2a8f2c3a47 | |||
57c358b0c8 | |||
290a6072e3 | |||
0eae0730a1 | |||
ed2249cd7a | |||
4084f7b803 | |||
9a6e0aa08c | |||
ff3b45e031 | |||
70e8116611 | |||
a9559d8cb2 | |||
0b677b75fb | |||
eb1cd8c497 | |||
7a6770c8bb | |||
cca63ad28e | |||
7f570b8aa0 | |||
ffba3ca42a | |||
4e185333ad | |||
e7dbe58025 | |||
07c2d90b09 | |||
7d5c42862f | |||
b4583a3530 | |||
82deee8bc6 | |||
1f2640bd78 | |||
72f73cac5b | |||
39843e706c | |||
5b5a68bd6b | |||
2fb34aac5d | |||
6010314835 | |||
11551bfcd5 | |||
caeb2557ea | |||
51f99aebbf | |||
c345cea27c | |||
3213942190 | |||
a1b9176570 | |||
3a5c2061b1 | |||
35830234d8 | |||
a6c5db47b7 | |||
4d8f67e293 | |||
94f09dd2e7 | |||
2dea0440e0 | |||
2799bdd4aa | |||
1d906810b9 | |||
3379379a45 | |||
27ff6576e9 | |||
14ac339270 | |||
139790ec06 | |||
e43c8c8ef9 | |||
49e8c04441 | |||
e69ab66062 | |||
a51f8016d3 | |||
c9ac2b7b9c | |||
00561c98b7 | |||
d29aa406a7 | |||
aeceb27226 | |||
05715487da | |||
c8459e5292 | |||
2367876df1 | |||
a39938ccea | |||
6eac4d7258 | |||
4a9fd20ff3 | |||
8c53243031 | |||
05e62191ac | |||
9b878a43c3 | |||
bd28a2e8b6 | |||
5ca687fe81 | |||
715a867653 | |||
183f469d85 | |||
a452ae3d79 | |||
42d3bef0dc | |||
6ad4d0f984 | |||
8d9efc1577 | |||
e1490bcb1c | |||
b13605d700 | |||
0002c9e058 | |||
f8507924c4 | |||
efd01ff9c3 | |||
90537aae3a | |||
5dbc50c4b9 | |||
abace2afc0 | |||
925de391e2 | |||
51bdb82a15 | |||
b0d685d356 | |||
aa606835c3 | |||
8f79fba624 | |||
58c1550fd9 | |||
f9e05641fb | |||
a1af3d7413 | |||
8040af8954 | |||
cb4c475d46 | |||
1f1fccd953 | |||
8227be4ec2 | |||
c04f8403aa | |||
759888e53b | |||
800abf1663 | |||
b13b922c5f | |||
b7bc73a1ff | |||
b7831df8e8 | |||
c840f76172 | |||
d5885f1a60 | |||
2eae95e13a | |||
4cb40f7906 | |||
ba58d0a859 | |||
cc5a6b1cb2 | |||
c34934bca8 | |||
303a8fed49 | |||
fa13916251 | |||
1b2e49908b | |||
e5bb3bc661 | |||
eb2ab28c73 | |||
97d98b809d | |||
b7eb58525a | |||
47a5d07ede | |||
d1d58241c4 | |||
ea85e6fef2 | |||
4b81c0df2d | |||
87e09d7aa9 | |||
1fbf96f5d2 | |||
56da2c1eda | |||
d024112dc5 | |||
e587e2cb82 | |||
f7942d3822 | |||
4e8efb0534 | |||
fbaaa54ca2 | |||
3216a5792d | |||
b894963d7d | |||
6c1f1193a9 | |||
a5e6331e69 | |||
59b68b1054 | |||
d51be2d4d6 | |||
65fddf3e70 | |||
4e2fc3c79a | |||
7336e00d68 | |||
689187f8bb | |||
709889fc35 | |||
b3abd002a3 | |||
2fc367a8e4 | |||
1f643e2a88 | |||
ae1387b695 | |||
92e8fcf77b | |||
a484a93f4c | |||
352562288a | |||
c48a14bcb3 | |||
e091f8dfde | |||
225a1f2b91 | |||
5b0b1ad606 | |||
e7021b870c | |||
fa3616fafc | |||
757b38c186 | |||
f46fd8c92a | |||
c317b65f30 | |||
07707e37ae | |||
a6d779e0a3 | |||
3f2bdff6d1 | |||
03ecbee5f9 | |||
c67c6094c8 | |||
35b995e6f0 | |||
f0e68fdd61 | |||
ab3c39b26b | |||
73ecc0590c | |||
711deb50a8 | |||
0b12e6a90a | |||
79e434f508 | |||
8ea83222dd | |||
7e999af2be | |||
f1b6e31914 | |||
07caa52ef6 | |||
b884f8da75 | |||
e9ca17cfc9 | |||
1fef8716d7 | |||
c9c5885a3e | |||
df6423354a | |||
0b4313f907 | |||
4b3cd45b11 | |||
cfb4e34ffc | |||
039102c1fc | |||
8e9d1038bb | |||
142fef3e44 | |||
ba9db1c5b8 | |||
9c2e25426c | |||
4ca4e53890 | |||
d121c02336 | |||
88927d8549 | |||
87f9d2e06d | |||
8e30bed653 | |||
e45e88b371 | |||
3eee3ee5dd | |||
fbf9aab537 | |||
afebb611e3 | |||
7dd557e6c8 | |||
b79d3419b4 | |||
719f98d5f1 | |||
2826ca6002 | |||
6534f04e07 | |||
fed6b2e746 | |||
57d825fc22 | |||
c77a91544f | |||
c2aff84bf9 | |||
87f540853c | |||
b367c412a0 | |||
1ce49aea6e | |||
d2c4bcd435 | |||
d85b887cf6 | |||
5b009adab3 | |||
eac8b9bbbe | |||
f509f9e64e | |||
26991f4dd4 | |||
82815a4bea | |||
4acb3058ab | |||
784e0ac48d | |||
8ca141eeb2 | |||
e59986ced2 | |||
79be3b7f9c | |||
5cbabda14b | |||
3dbcbaf481 | |||
0f4ed495e8 | |||
c222316b41 | |||
eaba02cfc5 | |||
bd222a5f17 | |||
4e0e7c1900 | |||
621880a925 | |||
dd68fa16c1 | |||
f25fe2ade2 | |||
1c25ceb0c7 | |||
23a94aa36c | |||
c07f6da8ce | |||
d7676b4ba9 | |||
9cb7935ce4 | |||
9f0e7eb18c | |||
bb6cd41ee6 | |||
3ae5b01bec | |||
6870bcfb10 | |||
2346b4036e | |||
b5c71387a6 | |||
8a680df028 | |||
359f0a91cd | |||
05fb6312d7 | |||
055d5042a3 | |||
b5dee1095a | |||
df09b992e8 | |||
d06ad4122a | |||
23dc1d0109 | |||
98e504d297 | |||
35bc6a33dd | |||
57629554bb | |||
b31a9ce527 | |||
b81b04c6fa | |||
b8582543b9 | |||
26eaf1323b | |||
7c88cba45f | |||
613c5c4e40 | |||
96710f8596 | |||
fd1ec80ff9 | |||
b89de856a2 | |||
d5a1443b67 | |||
698b13699e | |||
48b37d2ac6 | |||
c32327551c | |||
99fa678ea0 | |||
edfcff1af3 | |||
d97b5c89e0 | |||
519d03c0f3 | |||
1d30ce6515 | |||
bbfb318a64 | |||
ec6210fc5f | |||
3d451ea8f8 | |||
2590e5cd94 | |||
8dc0ce4a9b | |||
7908f64b24 | |||
434a59278f | |||
9deebf090a | |||
6cbdfbfa2d | |||
710aea9b61 | |||
86b3891b13 | |||
9a4d1c3b6c | |||
062e2145c9 | |||
ce2fda7595 | |||
fe158c7072 | |||
e6e32af20c | |||
9b8441c7ff | |||
8eb6c20aba | |||
5881a63be3 | |||
501e3e5322 | |||
b952efc5ee | |||
539696c630 | |||
448a71a151 | |||
cc3b9f44ce | |||
3f00d8b85f | |||
cb7f7256d1 | |||
eec9a57046 | |||
73254785b0 | |||
2fad27fc68 | |||
88f3078232 | |||
637cf096fd | |||
4382f22fdc | |||
8d4ffd0e35 | |||
0746527aac | |||
b2e775f97f | |||
0793cb7c5d | |||
49cf321b39 | |||
00e9e261b4 | |||
e4631efbb1 | |||
96bc3b407a | |||
a1c3b111c2 | |||
2da74c454b | |||
e3246856f1 | |||
f081bc151f |
19
.editorconfig
Normal file
19
.editorconfig
Normal file
|
@ -0,0 +1,19 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.cs]
|
||||
dotnet_sort_system_directives_first = true
|
||||
dotnet_separate_import_directive_groups = false
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.yml]
|
||||
indent_size = 2
|
19
.gitattributes
vendored
Normal file
19
.gitattributes
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
# CRLF Normalisation
|
||||
* text eol=lf
|
||||
|
||||
# Filetype definitions
|
||||
*.cs text
|
||||
*.xaml text
|
||||
*.yml text
|
||||
*.sln text
|
||||
*.resx text
|
||||
*.config text
|
||||
*.csproj text
|
||||
*.settings text
|
||||
|
||||
*.ico binary
|
||||
*.png binary
|
||||
|
||||
# Ignore meta files in ZIP export
|
||||
.gitattributes export-ignore
|
||||
.gitignore export-ignore
|
5
.github/FUNDING.yml
vendored
5
.github/FUNDING.yml
vendored
|
@ -1,4 +1 @@
|
|||
patreon: AssistantMoe
|
||||
ko_fi: N4N8JX7B
|
||||
liberapay: Assistant
|
||||
custom: ['https://paypal.me/AssistantMoe', 'https://bs.assistant.moe/Donate/']
|
||||
custom: ['https://github.com/Assistant/ModAssistant']
|
||||
|
|
44
.github/workflows/dotnet.yml
vendored
Normal file
44
.github/workflows/dotnet.yml
vendored
Normal file
|
@ -0,0 +1,44 @@
|
|||
name: .NET Build
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup msbuild
|
||||
uses: microsoft/setup-msbuild@v1.0.2
|
||||
- name: Restore NuGet packages
|
||||
working-directory: ${{env.GITHUB_WORKSPACE}}
|
||||
run: nuget restore VRCMelonAssistant.sln
|
||||
- name: Build project
|
||||
run: msbuild VRCMelonAssistant/VRCMelonAssistant.csproj /t:Build /p:Configuration=Release
|
||||
- name: Cleanup release
|
||||
shell: bash
|
||||
run: |
|
||||
find "VRCMelonAssistant/bin/Release" -type f ! -name "VRCMelonAssistant.exe" -delete
|
||||
cp "LICENSE" "VRCMelonAssistant/bin/Release/LICENSE.VRCMelonAssistant.txt"
|
||||
- name: Upload Build
|
||||
if: startsWith(github.ref, 'refs/tags/') == false
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: VRCMelonAssistant-${{ github.sha }}
|
||||
path: ./VRCMelonAssistant/bin/Release/
|
||||
- name: Extract Release Version
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
id: get_version
|
||||
shell: bash
|
||||
run: echo ::set-output name=version::${GITHUB_REF#refs/tags/v}
|
||||
- name: Release
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
uses: softprops/action-gh-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
name: VRChat Melon Assistant v${{ steps.get_version.outputs.version }}
|
||||
files: ./VRCMelonAssistant/bin/Release/VRCMelonAssistant.exe
|
2
LICENSE
2
LICENSE
|
@ -1,6 +1,6 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2019 Assistant
|
||||
Copyright (c) 2019-2020 Assistant
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -1,57 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<configSections>
|
||||
<sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
|
||||
<section name="ModAssistant.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
|
||||
<section name="ModAssistant.Settings1" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
|
||||
</sectionGroup>
|
||||
</configSections>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
|
||||
</startup>
|
||||
<userSettings>
|
||||
<ModAssistant.Properties.Settings>
|
||||
<setting name="InstallFolder" serializeAs="String">
|
||||
<value />
|
||||
</setting>
|
||||
<setting name="StoreType" serializeAs="String">
|
||||
<value />
|
||||
</setting>
|
||||
<setting name="SaveSelected" serializeAs="String">
|
||||
<value>False</value>
|
||||
</setting>
|
||||
<setting name="CheckInstalled" serializeAs="String">
|
||||
<value>False</value>
|
||||
</setting>
|
||||
<setting name="SavedMods" serializeAs="String">
|
||||
<value>False</value>
|
||||
</setting>
|
||||
<setting name="Agreed" serializeAs="String">
|
||||
<value>False</value>
|
||||
</setting>
|
||||
<setting name="SelectInstalled" serializeAs="String">
|
||||
<value>False</value>
|
||||
</setting>
|
||||
<setting name="GameVersion" serializeAs="String">
|
||||
<value />
|
||||
</setting>
|
||||
<setting name="AllGameVersions" serializeAs="String">
|
||||
<value />
|
||||
</setting>
|
||||
<setting name="UpgradeRequired" serializeAs="String">
|
||||
<value>True</value>
|
||||
</setting>
|
||||
<setting name="LastTab" serializeAs="String">
|
||||
<value />
|
||||
</setting>
|
||||
</ModAssistant.Properties.Settings>
|
||||
<ModAssistant.Settings1>
|
||||
<setting name="InstallFolder" serializeAs="String">
|
||||
<value />
|
||||
</setting>
|
||||
<setting name="StoreVariant" serializeAs="String">
|
||||
<value />
|
||||
</setting>
|
||||
</ModAssistant.Settings1>
|
||||
</userSettings>
|
||||
</configuration>
|
|
@ -1,9 +0,0 @@
|
|||
<Application x:Class="ModAssistant.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
DispatcherUnhandledException="Application_DispatcherUnhandledException"
|
||||
Startup="Application_Startup">
|
||||
<Application.Resources>
|
||||
|
||||
</Application.Resources>
|
||||
</Application>
|
|
@ -1,113 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using ModAssistant;
|
||||
|
||||
namespace ModAssistant
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for App.xaml
|
||||
/// </summary>
|
||||
public partial class App : Application
|
||||
{
|
||||
public static string BeatSaberInstallDirectory;
|
||||
public static string BeatSaberInstallType;
|
||||
public static bool SaveModSelection;
|
||||
public static bool CheckInstalledMods;
|
||||
public static bool SelectInstalledMods;
|
||||
public static string Version = Assembly.GetExecutingAssembly().GetName().Version.ToString();
|
||||
public static List<string> SavedMods = ModAssistant.Properties.Settings.Default.SavedMods.Split(',').ToList();
|
||||
|
||||
|
||||
private void Application_Startup(object sender, StartupEventArgs e)
|
||||
{
|
||||
if (ModAssistant.Properties.Settings.Default.UpgradeRequired)
|
||||
{
|
||||
ModAssistant.Properties.Settings.Default.Upgrade();
|
||||
ModAssistant.Properties.Settings.Default.UpgradeRequired = false;
|
||||
ModAssistant.Properties.Settings.Default.Save();
|
||||
}
|
||||
|
||||
Version = Version.Substring(0, Version.Length - 2);
|
||||
BeatSaberInstallDirectory = Utils.GetInstallDir();
|
||||
|
||||
while (String.IsNullOrEmpty(App.BeatSaberInstallDirectory))
|
||||
{
|
||||
if (System.Windows.Forms.MessageBox.Show($"Press OK to try again, or Cancel to close application.", $"Couldn't find your Beat Saber install folder!", System.Windows.Forms.MessageBoxButtons.OKCancel) == System.Windows.Forms.DialogResult.OK)
|
||||
{
|
||||
App.BeatSaberInstallDirectory = Utils.GetManualDir();
|
||||
}
|
||||
else
|
||||
{
|
||||
Environment.Exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
BeatSaberInstallType = ModAssistant.Properties.Settings.Default.StoreType;
|
||||
SaveModSelection = ModAssistant.Properties.Settings.Default.SaveSelected;
|
||||
CheckInstalledMods = ModAssistant.Properties.Settings.Default.CheckInstalled;
|
||||
SelectInstalledMods = ModAssistant.Properties.Settings.Default.SelectInstalled;
|
||||
|
||||
if (e.Args.Length == 0)
|
||||
{
|
||||
Updater.Run();
|
||||
|
||||
MainWindow window = new MainWindow();
|
||||
window.Show();
|
||||
}
|
||||
else
|
||||
{
|
||||
ArgumentHandler(e.Args);
|
||||
}
|
||||
}
|
||||
|
||||
private void ArgumentHandler(string[] Args)
|
||||
{
|
||||
switch (Args[0])
|
||||
{
|
||||
case "--install":
|
||||
if (!String.IsNullOrEmpty(Args[1]))
|
||||
OneClickInstaller.InstallAsset(Args[1]);
|
||||
else
|
||||
Utils.SendNotify("Invalid argument! '--install' requires an option.");
|
||||
break;
|
||||
|
||||
case "--no-update":
|
||||
MainWindow window = new MainWindow();
|
||||
window.Show();
|
||||
break;
|
||||
|
||||
case "--register":
|
||||
if (!String.IsNullOrEmpty(Args[1]))
|
||||
OneClickInstaller.Register(Args[1], true);
|
||||
else
|
||||
Utils.SendNotify("Invalid argument! '--register' requires an option.");
|
||||
break;
|
||||
|
||||
case "--unregister":
|
||||
if (!String.IsNullOrEmpty(Args[1]))
|
||||
OneClickInstaller.Unregister(Args[1], true);
|
||||
else
|
||||
Utils.SendNotify("Invalid argument! '--unregister' requires an option.");
|
||||
break;
|
||||
|
||||
default:
|
||||
Utils.SendNotify("Unrecognized argument. Closing Mod Assistant.");
|
||||
break;
|
||||
}
|
||||
Current.Shutdown();
|
||||
}
|
||||
|
||||
private void Application_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
|
||||
{
|
||||
MessageBox.Show("An unhandled exception just occurred: " + e.Exception, "Exception", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
e.Handled = true;
|
||||
Application.Current.Shutdown();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ModAssistant.Pages;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ModAssistant
|
||||
{
|
||||
public class Mod
|
||||
{
|
||||
public string name;
|
||||
public string version;
|
||||
public string gameVersion;
|
||||
public string _id;
|
||||
public string authorId;
|
||||
public string uploadedDate;
|
||||
public string updatedDate;
|
||||
public Author author;
|
||||
public string description;
|
||||
public string link;
|
||||
public string category;
|
||||
public DownloadLink[] downloads;
|
||||
public bool required;
|
||||
public Dependency[] dependencies;
|
||||
public List<Mod> Dependents = new List<Mod>();
|
||||
public Mods.ModListItem ListItem;
|
||||
|
||||
public class Author
|
||||
{
|
||||
public string _id;
|
||||
public string username;
|
||||
public string lastLogin;
|
||||
}
|
||||
|
||||
public class DownloadLink
|
||||
{
|
||||
public string type;
|
||||
public string url;
|
||||
public FileHashes[] hashMd5;
|
||||
}
|
||||
|
||||
public class FileHashes
|
||||
{
|
||||
public string hash;
|
||||
public string file;
|
||||
}
|
||||
|
||||
public class Dependency
|
||||
{
|
||||
public string name;
|
||||
public string _id;
|
||||
public Mod Mod;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,306 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Web;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Security.Principal;
|
||||
using Microsoft.Win32;
|
||||
using System.IO.Compression;
|
||||
using System.Web.Script.Serialization;
|
||||
|
||||
namespace ModAssistant
|
||||
{
|
||||
class OneClickInstaller
|
||||
{
|
||||
private const string ModelSaberURLPrefix = "https://modelsaber.com/files/";
|
||||
private const string BeatSaverURLPrefix = "https://beatsaver.com";
|
||||
|
||||
private static string BeatSaberPath = App.BeatSaberInstallDirectory;
|
||||
|
||||
private const string CustomAvatarsFolder = "CustomAvatars";
|
||||
private const string CustomSabersFolder = "CustomSabers";
|
||||
private const string CustomPlatformsFolder = "CustomPlatforms";
|
||||
private static readonly string CustomSongsFolder = Path.Combine("Beat Saber_Data", "CustomLevels");
|
||||
|
||||
private static readonly string[] Protocols = new[] { "modelsaber", "beatsaver" };
|
||||
|
||||
public static void InstallAsset(string link)
|
||||
{
|
||||
Uri uri = new Uri(link);
|
||||
if (!Protocols.Contains(uri.Scheme)) return;
|
||||
|
||||
switch (uri.Scheme)
|
||||
{
|
||||
case "modelsaber":
|
||||
ModelSaber(uri);
|
||||
break;
|
||||
case "beatsaver":
|
||||
BeatSaver(uri);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void BeatSaver(Uri uri)
|
||||
{
|
||||
string Key = uri.Host;
|
||||
|
||||
string json = string.Empty;
|
||||
BeatSaverApiResponse Response;
|
||||
|
||||
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(BeatSaverURLPrefix + "/api/maps/detail/" + Key);
|
||||
request.AutomaticDecompression = DecompressionMethods.GZip;
|
||||
request.UserAgent = "ModAssistant/" + App.Version;
|
||||
|
||||
try
|
||||
{
|
||||
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
|
||||
using (Stream stream = response.GetResponseStream())
|
||||
using (StreamReader reader = new StreamReader(stream))
|
||||
{
|
||||
var serializer = new JavaScriptSerializer();
|
||||
Response = serializer.Deserialize<BeatSaverApiResponse>(reader.ReadToEnd());
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
MessageBox.Show("Could not get map details.\n\n" + e);
|
||||
return;
|
||||
}
|
||||
|
||||
string zip = Path.Combine(BeatSaberPath, CustomSongsFolder, Response.hash) + ".zip";
|
||||
string directory = Path.Combine(
|
||||
BeatSaberPath,
|
||||
CustomSongsFolder,
|
||||
String.Concat(
|
||||
(Response.key + " (" + Response.metadata.songName + " - " + Response.metadata.levelAuthorName + ")")
|
||||
.Split(Utils.Constants.IllegalCharacters)
|
||||
)
|
||||
);
|
||||
|
||||
DownloadAsset(BeatSaverURLPrefix + Response.downloadURL, CustomSongsFolder, Response.hash + ".zip");
|
||||
|
||||
using (FileStream stream = new FileStream(zip, FileMode.Open))
|
||||
{
|
||||
using (ZipArchive archive = new ZipArchive(stream))
|
||||
{
|
||||
foreach (ZipArchiveEntry file in archive.Entries)
|
||||
{
|
||||
string fileDirectory = Path.GetDirectoryName(Path.Combine(directory, file.FullName));
|
||||
if (!Directory.Exists(fileDirectory))
|
||||
Directory.CreateDirectory(fileDirectory);
|
||||
|
||||
if (!String.IsNullOrEmpty(file.Name))
|
||||
file.ExtractToFile(Path.Combine(directory, file.FullName), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File.Delete(zip);
|
||||
}
|
||||
|
||||
private static void ModelSaber(Uri uri)
|
||||
{
|
||||
switch (uri.Host)
|
||||
{
|
||||
case "avatar":
|
||||
DownloadAsset(ModelSaberURLPrefix + uri.Host + uri.AbsolutePath, CustomAvatarsFolder);
|
||||
break;
|
||||
case "saber":
|
||||
DownloadAsset(ModelSaberURLPrefix + uri.Host + uri.AbsolutePath, CustomSabersFolder);
|
||||
break;
|
||||
case "platform":
|
||||
DownloadAsset(ModelSaberURLPrefix + uri.Host + uri.AbsolutePath, CustomPlatformsFolder);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void DownloadAsset(string link, string folder, string fileName = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(BeatSaberPath))
|
||||
{
|
||||
Utils.SendNotify("Beat Saber installation path not found.");
|
||||
}
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(Path.Combine(BeatSaberPath, folder));
|
||||
if (String.IsNullOrEmpty(fileName))
|
||||
fileName = WebUtility.UrlDecode(Path.Combine(BeatSaberPath, folder, new Uri(link).Segments.Last()));
|
||||
else
|
||||
fileName = WebUtility.UrlDecode(Path.Combine(BeatSaberPath, folder, fileName));
|
||||
|
||||
Utils.Download(link, fileName);
|
||||
Utils.SendNotify("Installed: " + Path.GetFileNameWithoutExtension(fileName));
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
Utils.SendNotify("Failed to install.");
|
||||
}
|
||||
}
|
||||
|
||||
public static void Register(string Protocol, bool Background = false)
|
||||
{
|
||||
if (IsRegistered(Protocol) == true)
|
||||
return;
|
||||
try
|
||||
{
|
||||
if (Utils.IsAdmin)
|
||||
{
|
||||
RegistryKey ProtocolKey = Registry.ClassesRoot.OpenSubKey(Protocol, true);
|
||||
if (ProtocolKey == null)
|
||||
ProtocolKey = Registry.ClassesRoot.CreateSubKey(Protocol, true);
|
||||
RegistryKey CommandKey = ProtocolKey.CreateSubKey(@"shell\open\command", true);
|
||||
if (CommandKey == null)
|
||||
CommandKey = Registry.ClassesRoot.CreateSubKey(@"shell\open\command", true);
|
||||
|
||||
if (ProtocolKey.GetValue("OneClick-Provider", "").ToString() != "ModAssistant")
|
||||
{
|
||||
ProtocolKey.SetValue("URL Protocol", "", RegistryValueKind.String);
|
||||
ProtocolKey.SetValue("OneClick-Provider", "ModAssistant", RegistryValueKind.String);
|
||||
CommandKey.SetValue("", $"\"{Utils.ExePath}\" \"--install\" \"%1\"");
|
||||
}
|
||||
|
||||
Utils.SendNotify($"{Protocol} One Click Install handlers registered!");
|
||||
}
|
||||
else
|
||||
{
|
||||
Utils.StartAsAdmin($"\"--register\" \"{Protocol}\"");
|
||||
}
|
||||
} catch (Exception e)
|
||||
{
|
||||
MessageBox.Show(e.ToString());
|
||||
}
|
||||
|
||||
if (Background)
|
||||
Application.Current.Shutdown();
|
||||
else
|
||||
Pages.Options.Instance.UpdateHandlerStatus();
|
||||
}
|
||||
|
||||
public static void Unregister(string Protocol, bool Background = false)
|
||||
{
|
||||
if (IsRegistered(Protocol) == false)
|
||||
return;
|
||||
try
|
||||
{
|
||||
if (Utils.IsAdmin)
|
||||
{
|
||||
using (RegistryKey ProtocolKey = Registry.ClassesRoot.OpenSubKey(Protocol, true))
|
||||
{
|
||||
if (ProtocolKey != null
|
||||
&& ProtocolKey.GetValue("OneClick-Provider", "").ToString() == "ModAssistant")
|
||||
{
|
||||
Registry.ClassesRoot.DeleteSubKeyTree(Protocol);
|
||||
}
|
||||
}
|
||||
Utils.SendNotify($"{Protocol} One Click Install handlers unregistered!");
|
||||
}
|
||||
else
|
||||
{
|
||||
Utils.StartAsAdmin($"\"--unregister\" \"{Protocol}\"");
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
MessageBox.Show(e.ToString());
|
||||
}
|
||||
|
||||
if (Background)
|
||||
Application.Current.Shutdown();
|
||||
else
|
||||
Pages.Options.Instance.UpdateHandlerStatus();
|
||||
}
|
||||
|
||||
public static bool IsRegistered(string Protocol)
|
||||
{
|
||||
RegistryKey ProtocolKey = Registry.ClassesRoot.OpenSubKey(Protocol);
|
||||
if (ProtocolKey != null
|
||||
&& ProtocolKey.GetValue("OneClick-Provider", "").ToString() == "ModAssistant")
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class BeatSaverApiResponse
|
||||
{
|
||||
public Metadata metadata { get; set; }
|
||||
public Stats stats { get; set; }
|
||||
public string description { get; set; }
|
||||
public DateTime? deletedAt { get; set; }
|
||||
public string _id { get; set; }
|
||||
public string key { get; set; }
|
||||
public string name { get; set; }
|
||||
public Uploader uploader { get; set; }
|
||||
public DateTime uploaded { get; set; }
|
||||
public string hash { get; set; }
|
||||
public string downloadURL { get; set; }
|
||||
public string coverURL { get; set; }
|
||||
|
||||
public class Difficulties
|
||||
{
|
||||
public bool easy { get; set; }
|
||||
public bool normal { get; set; }
|
||||
public bool hard { get; set; }
|
||||
public bool expert { get; set; }
|
||||
public bool expertPlus { get; set; }
|
||||
}
|
||||
|
||||
public class Metadata
|
||||
{
|
||||
public Difficulties difficulties { get; set; }
|
||||
public Characteristic[] characteristics { get; set; }
|
||||
public string songName { get; set; }
|
||||
public string songSubName { get; set; }
|
||||
public string songAuthorName { get; set; }
|
||||
public string levelAuthorName { get; set; }
|
||||
public double bpm { get; set; }
|
||||
}
|
||||
|
||||
public class Characteristic
|
||||
{
|
||||
public string name { get; set; }
|
||||
public CharacteristicDifficulties difficulties { get; set; }
|
||||
}
|
||||
|
||||
public class CharacteristicDifficulties
|
||||
{
|
||||
public Difficulty easy { get; set; }
|
||||
public Difficulty normal { get; set; }
|
||||
public Difficulty hard { get; set; }
|
||||
public Difficulty expert { get; set; }
|
||||
public Difficulty expertPlus { get; set; }
|
||||
}
|
||||
|
||||
public class Difficulty
|
||||
{
|
||||
public double? duration { get; set; }
|
||||
public double? length { get; set; }
|
||||
public double bombs { get; set; }
|
||||
public double notes { get; set; }
|
||||
public double obstacles { get; set; }
|
||||
public double njs { get; set; }
|
||||
}
|
||||
|
||||
public class Stats
|
||||
{
|
||||
public int downloads { get; set; }
|
||||
public int plays { get; set; }
|
||||
public int downVotes { get; set; }
|
||||
public int upVotes { get; set; }
|
||||
public double heat { get; set; }
|
||||
public double rating { get; set; }
|
||||
}
|
||||
|
||||
public class Uploader
|
||||
{
|
||||
public string _id { get; set; }
|
||||
public string username { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,102 +0,0 @@
|
|||
<Window x:Class="ModAssistant.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
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:local="clr-namespace:ModAssistant"
|
||||
mc:Ignorable="d"
|
||||
Icon="Resources/icon.ico"
|
||||
Title="Mod Assistant" Width="1280" Height="720"
|
||||
UIElement.PreviewMouseDown="Window_PreviewMouseDown">
|
||||
<Grid Margin="10">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="50"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="70"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid Grid.Column="0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="65" />
|
||||
<RowDefinition Height="70" />
|
||||
<RowDefinition Height="70" />
|
||||
<RowDefinition Height="70" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Button Name="IntroButton" Grid.Row="0" Height="60" Margin="0,0,10,5" Background="white" Click="IntroButton_Click">
|
||||
<StackPanel Margin="0,8,0,0">
|
||||
<Image Height="30" Source="Resources/Intro.png" VerticalAlignment="Bottom"></Image>
|
||||
<TextBlock HorizontalAlignment="Center" Margin="0,0,0,5" VerticalAlignment="Bottom">Intro</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<Button IsEnabled="false" Name="ModsButton" Grid.Row="1" Height="60" Margin="0,5,10,5" Background="white" Click="ModsButton_Click">
|
||||
<StackPanel Margin="0,6,0,0">
|
||||
<Image Height="30" Source="Resources/Mods.png" VerticalAlignment="Bottom"></Image>
|
||||
<TextBlock HorizontalAlignment="Center" Margin="0,0,0,5" VerticalAlignment="Bottom">Mods</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<Button Name="AboutButton" Grid.Row="2" Height="60" Margin="0,5,10,5" Background="white" Click="AboutButton_Click">
|
||||
<StackPanel Margin="0,6,0,0">
|
||||
<Image Height="30" Source="Resources/About.png" VerticalAlignment="Bottom"></Image>
|
||||
<TextBlock HorizontalAlignment="Center" Margin="0,0,0,5" VerticalAlignment="Bottom">About</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<Button Name="OptionsButton" Grid.Row="3" Height="60" Margin="0,5,10,5" Background="white" Click="OptionsButton_Click">
|
||||
<StackPanel Margin="0,5,0,0">
|
||||
<Image Height="30" Source="Resources/Options.png" VerticalAlignment="Bottom"></Image>
|
||||
<TextBlock HorizontalAlignment="Center" Margin="0,0,0,5" VerticalAlignment="Bottom">Options</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<StackPanel Name="GameVersions" Grid.Row="5" VerticalAlignment="Bottom">
|
||||
<TextBlock Text="Game Version:" FontSize="10"/>
|
||||
<ComboBox Name="GameVersionsBox" Margin="0,5,5,0" SelectionChanged="GameVersionsBox_SelectionChanged">
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
|
||||
</Grid>
|
||||
|
||||
<StackPanel Grid.Row="1" VerticalAlignment="Center">
|
||||
<TextBlock Text="Version" HorizontalAlignment="Center"/>
|
||||
<TextBlock Name="VersionText" HorizontalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
|
||||
<Frame Grid.Column="1" Name="Main" NavigationUIVisibility="Hidden" />
|
||||
|
||||
<Grid Grid.Row="1" Grid.Column="1" >
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="115" />
|
||||
<ColumnDefinition Width="115" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock Name="MainTextBlock" Padding="5" Height="40" VerticalAlignment="Bottom" Background="LightGray" FontSize="20" />
|
||||
<Button Grid.Column="1" Name="InfoButton" IsEnabled="False" Height="40" Width="100" HorizontalAlignment="Right" Margin="0,10,0,0" Click="InfoButton_Click">
|
||||
<StackPanel>
|
||||
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Bottom">
|
||||
Mod Info
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<Button Grid.Column="2" Name="InstallButton" IsEnabled="False" Height="40" Width="100" HorizontalAlignment="Right" Margin="0,10,0,0" Click="InstallButton_Click">
|
||||
<StackPanel>
|
||||
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Bottom">
|
||||
Install
|
||||
</TextBlock>
|
||||
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Bottom">
|
||||
or Update
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Window>
|
|
@ -1,235 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Net;
|
||||
using System.IO;
|
||||
using System.Web.Script.Serialization;
|
||||
using System.Runtime.Serialization;
|
||||
using ModAssistant.Pages;
|
||||
using System.Reflection;
|
||||
|
||||
namespace ModAssistant
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for MainWindow.xaml
|
||||
/// </summary>
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
public static MainWindow Instance;
|
||||
public static bool ModsOpened = false;
|
||||
public static string GameVersion;
|
||||
|
||||
public string MainText
|
||||
{
|
||||
get
|
||||
{
|
||||
return MainTextBlock.Text;
|
||||
}
|
||||
set
|
||||
{
|
||||
Dispatcher.Invoke(new Action(() => { MainWindow.Instance.MainTextBlock.Text = value; }));
|
||||
}
|
||||
}
|
||||
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
Instance = this;
|
||||
|
||||
VersionText.Text = App.Version;
|
||||
|
||||
if (Utils.isVoid())
|
||||
{
|
||||
Main.Content = Invalid.Instance;
|
||||
MainWindow.Instance.ModsButton.IsEnabled = false;
|
||||
MainWindow.Instance.OptionsButton.IsEnabled = false;
|
||||
MainWindow.Instance.IntroButton.IsEnabled = false;
|
||||
MainWindow.Instance.AboutButton.IsEnabled = false;
|
||||
MainWindow.Instance.GameVersionsBox.IsEnabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
List<string> versions;
|
||||
string json = string.Empty;
|
||||
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Utils.Constants.BeatModsAPIUrl + "version");
|
||||
request.AutomaticDecompression = DecompressionMethods.GZip;
|
||||
request.UserAgent = "ModAssistant/" + App.Version;
|
||||
|
||||
versions = null;
|
||||
try
|
||||
{
|
||||
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
|
||||
using (Stream stream = response.GetResponseStream())
|
||||
using (StreamReader reader = new StreamReader(stream))
|
||||
{
|
||||
JavaScriptSerializer serializer = new JavaScriptSerializer();
|
||||
versions = serializer.Deserialize<string[]>(reader.ReadToEnd()).ToList();
|
||||
}
|
||||
|
||||
GameVersion = GetGameVersion(versions);
|
||||
|
||||
GameVersionsBox.ItemsSource = versions;
|
||||
GameVersionsBox.SelectedValue = GameVersion;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
GameVersionsBox.IsEnabled = false;
|
||||
MessageBox.Show("Could not load game versions, Mods tab will be unavailable.\n" + e);
|
||||
}
|
||||
|
||||
if (!String.IsNullOrEmpty(GameVersion) && Properties.Settings.Default.Agreed)
|
||||
{
|
||||
MainWindow.Instance.ModsButton.IsEnabled = true;
|
||||
}
|
||||
|
||||
if (!Properties.Settings.Default.Agreed || String.IsNullOrEmpty(Properties.Settings.Default.LastTab))
|
||||
{
|
||||
Main.Content = Intro.Instance;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (Properties.Settings.Default.LastTab)
|
||||
{
|
||||
case "Intro":
|
||||
Main.Content = Intro.Instance;
|
||||
break;
|
||||
case "Mods":
|
||||
Mods.Instance.LoadMods();
|
||||
ModsOpened = true;
|
||||
Main.Content = Mods.Instance;
|
||||
break;
|
||||
case "About":
|
||||
Main.Content = About.Instance;
|
||||
break;
|
||||
case "Options":
|
||||
Main.Content = Options.Instance;
|
||||
break;
|
||||
default:
|
||||
Main.Content = Intro.Instance;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string GetGameVersion(List<string> versions)
|
||||
{
|
||||
string version = Utils.GetVersion();
|
||||
if (!String.IsNullOrEmpty(version) && versions.Contains(version))
|
||||
{
|
||||
return version;
|
||||
}
|
||||
|
||||
string versionsString = String.Join(",", versions.ToArray());
|
||||
if (Properties.Settings.Default.AllGameVersions != versionsString)
|
||||
{
|
||||
Properties.Settings.Default.AllGameVersions = versionsString;
|
||||
Properties.Settings.Default.Save();
|
||||
Utils.ShowMessageBoxAsync("It looks like there's been a game update.\n\nPlease double check that the correct version is selected at the bottom left corner!", "New Game Version Detected!");
|
||||
return versions[0];
|
||||
}
|
||||
|
||||
if (!String.IsNullOrEmpty(Properties.Settings.Default.GameVersion) && versions.Contains(Properties.Settings.Default.GameVersion))
|
||||
return Properties.Settings.Default.GameVersion;
|
||||
return versions[0];
|
||||
}
|
||||
|
||||
private void ModsButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Main.Content = Mods.Instance;
|
||||
Properties.Settings.Default.LastTab = "Mods";
|
||||
Properties.Settings.Default.Save();
|
||||
|
||||
if (!ModsOpened)
|
||||
{
|
||||
Mods.Instance.LoadMods();
|
||||
ModsOpened = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Mods.Instance.PendingChanges)
|
||||
{
|
||||
Mods.Instance.LoadMods();
|
||||
Mods.Instance.PendingChanges = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void IntroButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Main.Content = Intro.Instance;
|
||||
Properties.Settings.Default.LastTab = "Intro";
|
||||
Properties.Settings.Default.Save();
|
||||
}
|
||||
|
||||
private void AboutButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Main.Content = About.Instance;
|
||||
Properties.Settings.Default.LastTab = "About";
|
||||
Properties.Settings.Default.Save();
|
||||
}
|
||||
|
||||
private void OptionsButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Main.Content = Options.Instance;
|
||||
Properties.Settings.Default.LastTab = "Options";
|
||||
Properties.Settings.Default.Save();
|
||||
}
|
||||
|
||||
private void InstallButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Mods.Instance.InstallMods();
|
||||
}
|
||||
|
||||
private void InfoButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if ((Mods.ModListItem)Mods.Instance.ModsListView.SelectedItem == null)
|
||||
{
|
||||
MessageBox.Show("No mod selected");
|
||||
return;
|
||||
}
|
||||
Mods.ModListItem mod = ((Mods.ModListItem)Mods.Instance.ModsListView.SelectedItem);
|
||||
string infoUrl = mod.ModInfo.link;
|
||||
if (String.IsNullOrEmpty(infoUrl))
|
||||
{
|
||||
MessageBox.Show(mod.ModName + " does not have an info page");
|
||||
}
|
||||
else
|
||||
{
|
||||
System.Diagnostics.Process.Start(infoUrl);
|
||||
}
|
||||
}
|
||||
|
||||
private void GameVersionsBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
string oldGameVersion = GameVersion;
|
||||
|
||||
GameVersion = (sender as ComboBox).SelectedItem.ToString();
|
||||
|
||||
if (String.IsNullOrEmpty(oldGameVersion)) return;
|
||||
|
||||
Properties.Settings.Default.GameVersion = GameVersion;
|
||||
Properties.Settings.Default.Save();
|
||||
|
||||
if (ModsOpened)
|
||||
{
|
||||
Mods.Instance.LoadMods();
|
||||
}
|
||||
}
|
||||
|
||||
private void Window_PreviewMouseDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
About.Instance.PatUp.IsOpen = false;
|
||||
About.Instance.PatButton.IsEnabled = true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,158 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{6A224B82-40DA-40B3-94DC-EFBEC2BDDA39}</ProjectGuid>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<RootNamespace>ModAssistant</RootNamespace>
|
||||
<AssemblyName>ModAssistant</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ApplicationIcon>Resources\icon.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.IO.Compression" />
|
||||
<Reference Include="System.IO.Compression.FileSystem" />
|
||||
<Reference Include="System.Management" />
|
||||
<Reference Include="System.Web" />
|
||||
<Reference Include="System.Web.Extensions" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xaml">
|
||||
<RequiredTargetFramework>4.0</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="WindowsBase" />
|
||||
<Reference Include="PresentationCore" />
|
||||
<Reference Include="PresentationFramework" />
|
||||
<Reference Include="WindowsFormsIntegration" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ApplicationDefinition Include="App.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</ApplicationDefinition>
|
||||
<Compile Include="Classes\Promotions.cs" />
|
||||
<Compile Include="Classes\Diagnostics.cs" />
|
||||
<Compile Include="Classes\Updater.cs" />
|
||||
<Compile Include="Pages\Intro.xaml.cs">
|
||||
<DependentUpon>Intro.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Classes\Mod.cs" />
|
||||
<Compile Include="Pages\Invalid.xaml.cs">
|
||||
<DependentUpon>Invalid.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Pages\Mods.xaml.cs">
|
||||
<DependentUpon>Mods.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Pages\About.xaml.cs">
|
||||
<DependentUpon>About.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Classes\Utils.cs" />
|
||||
<Page Include="Pages\Intro.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="MainWindow.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Compile Include="App.xaml.cs">
|
||||
<DependentUpon>App.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Classes\OneClickInstaller.cs" />
|
||||
<Compile Include="MainWindow.xaml.cs">
|
||||
<DependentUpon>MainWindow.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Page Include="Pages\Invalid.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Pages\Mods.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Pages\About.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Pages\Options.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Pages\Options.xaml.cs">
|
||||
<DependentUpon>Options.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Properties\AssemblyInfo.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Properties\Settings.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
</Compile>
|
||||
<EmbeddedResource Include="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
<None Include="Properties\Settings.settings">
|
||||
<Generator>PublicSettingsSingleFileGenerator</Generator>
|
||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="Resources\icon.ico" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="Resources\About.png" />
|
||||
<Resource Include="Resources\Intro.png" />
|
||||
<Resource Include="Resources\Mods.png" />
|
||||
<Resource Include="Resources\Options.png" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
|
@ -1,108 +0,0 @@
|
|||
<Page x:Class="ModAssistant.Pages.About"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:wfi="clr-namespace:System.Windows.Forms.Integration;assembly=WindowsFormsIntegration"
|
||||
xmlns:winForms="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:ModAssistant"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="629" d:DesignWidth="1182"
|
||||
Title="About">
|
||||
|
||||
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock VerticalAlignment="Top" HorizontalAlignment="Left" FontSize="24" Margin="0">About Mod Assistant</TextBlock>
|
||||
<TextBlock Grid.Row="1" TextWrapping="Wrap" Margin="0,5" FontSize="16" >
|
||||
I'm Assistant, and I made Mod Assistant for mod assistance, with a few principles in mind:
|
||||
</TextBlock>
|
||||
<TextBlock Grid.Row="2" Margin="15,5" FontSize="16" Text="• Simplicty" />
|
||||
<TextBlock Grid.Row="3" Margin="15,5" FontSize="16" Text="• Portability" />
|
||||
<TextBlock Grid.Row="4" Margin="15,5" FontSize="16" Text="• Single Executable" />
|
||||
<TextBlock Grid.Row="5" Margin="15,5" FontSize="16" Text="• Responsible use" />
|
||||
|
||||
<TextBlock TextWrapping="Wrap" Grid.Row="6" Margin="30,5,5,5" FontSize="16">
|
||||
If you enjoy this program and would like to support me, please visit my
|
||||
<Hyperlink NavigateUri="https://bs.assistant.moe/Donate/" RequestNavigate="Hyperlink_RequestNavigate">
|
||||
donation page
|
||||
</Hyperlink>
|
||||
or my
|
||||
<Hyperlink NavigateUri="https://www.patreon.com/AssistantMoe" RequestNavigate="Hyperlink_RequestNavigate">
|
||||
Patreon
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock Grid.Row="7" Margin="0,10,5,5" FontSize="20" Text="Special Thanks ♥" />
|
||||
|
||||
<StackPanel Grid.Row="8" Orientation="Horizontal" HorizontalAlignment="Center">
|
||||
|
||||
<StackPanel Margin="5" Orientation="Vertical">
|
||||
<TextBlock FontSize="20" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<Hyperlink NavigateUri="https://www.vanzeben.ca/" RequestNavigate="Hyperlink_RequestNavigate">
|
||||
vanZeben
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
<TextBlock FontSize="16" VerticalAlignment="Center" HorizontalAlignment="Center">Creating BeatMods</TextBlock>
|
||||
<TextBlock FontSize="16" VerticalAlignment="Center" HorizontalAlignment="Center">The current Mod repository</TextBlock>
|
||||
<TextBlock FontSize="16" VerticalAlignment="Center" HorizontalAlignment="Center">Used by this Installer</TextBlock>
|
||||
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Margin="5" Orientation="Vertical">
|
||||
<TextBlock FontSize="20" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<Hyperlink NavigateUri="https://linkkle.com/umbranoxus" RequestNavigate="Hyperlink_RequestNavigate">
|
||||
Umbranox
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
<TextBlock FontSize="16" VerticalAlignment="Center" HorizontalAlignment="Center">Inspiration</TextBlock>
|
||||
<TextBlock FontSize="16" VerticalAlignment="Center" HorizontalAlignment="Center">Creating the Mod Manager</TextBlock>
|
||||
<TextBlock FontSize="16" VerticalAlignment="Center" HorizontalAlignment="Center">
|
||||
<Hyperlink NavigateUri="https://www.patreon.com/scoresaber" RequestNavigate="Hyperlink_RequestNavigate">
|
||||
Donate
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Margin="5" Orientation="Vertical">
|
||||
<TextBlock FontSize="20" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<Hyperlink NavigateUri="https://www.jackbaron.com/" RequestNavigate="Hyperlink_RequestNavigate">
|
||||
lolPants
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
<TextBlock FontSize="16" VerticalAlignment="Center" HorizontalAlignment="Center">Inspiration</TextBlock>
|
||||
<TextBlock FontSize="16" VerticalAlignment="Center" HorizontalAlignment="Center">Creating ModSaber</TextBlock>
|
||||
<TextBlock FontSize="16" VerticalAlignment="Center" HorizontalAlignment="Center">The first Mod repository</TextBlock>
|
||||
<TextBlock FontSize="16" VerticalAlignment="Center" HorizontalAlignment="Center">
|
||||
<Hyperlink NavigateUri="https://www.paypal.me/jackmbaron" RequestNavigate="Hyperlink_RequestNavigate">
|
||||
Donate
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
|
||||
</StackPanel>
|
||||
|
||||
</StackPanel>
|
||||
<Button x:Name="PatButton" x:FieldModifier="public" Grid.Row="9" Margin="5" Height="30" Width="80" Content="Headpats" Click="HeadpatsButton_Click"/>
|
||||
<Popup Placement="Center" x:Name="PatUp" Width="auto" Height="auto">
|
||||
<Border BorderBrush="Gray" BorderThickness="3">
|
||||
<wfi:WindowsFormsHost>
|
||||
<winForms:PictureBox x:Name="PatImage"></winForms:PictureBox>
|
||||
</wfi:WindowsFormsHost>
|
||||
</Border>
|
||||
</Popup>
|
||||
</Grid>
|
||||
</Page>
|
|
@ -1,64 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web.Script.Serialization;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace ModAssistant.Pages
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for Page1.xaml
|
||||
/// </summary>
|
||||
public partial class About : Page
|
||||
{
|
||||
public static About Instance = new About();
|
||||
|
||||
public About()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e)
|
||||
{
|
||||
Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri));
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private async void HeadpatsButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
PatButton.IsEnabled = false;
|
||||
await Task.Run(() => HeadPat());
|
||||
PatUp.IsOpen = true;
|
||||
}
|
||||
|
||||
private void HeadPat()
|
||||
{
|
||||
Utils.WeebCDNRandomResponse Pat;
|
||||
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Utils.Constants.WeebCDNAPIURL + "pats/random");
|
||||
request.AutomaticDecompression = DecompressionMethods.GZip;
|
||||
request.UserAgent = "ModAssistant/" + App.Version;
|
||||
|
||||
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
|
||||
using (Stream stream = response.GetResponseStream())
|
||||
using (StreamReader reader = new StreamReader(stream))
|
||||
{
|
||||
var serializer = new JavaScriptSerializer();
|
||||
Pat = serializer.Deserialize<Utils.WeebCDNRandomResponse>(reader.ReadToEnd());
|
||||
}
|
||||
PatImage.Load(Pat.url);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
<Page x:Class="ModAssistant.Pages.Intro"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:ModAssistant"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="629" d:DesignWidth="1182"
|
||||
Title="Intro">
|
||||
|
||||
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Left" FontSize="48" Margin="0">Welcome to Mod Assistant</TextBlock>
|
||||
<TextBlock Grid.Row="1" FontWeight="Bold" Text="Please read this page entirely and carefully" Margin="0" FontSize="28" />
|
||||
<TextBlock Grid.Row="2" Margin="0,5" FontSize="16" TextWrapping="Wrap" Text="By using this program attest to have read and agree to the following terms:" />
|
||||
|
||||
<TextBlock Grid.Row="3" Margin="0,5" FontSize="16" TextWrapping="Wrap">
|
||||
Beat Saber <Bold>does not</Bold> natively support mods. This means:
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock Grid.Row="4" Margin="15,5" FontSize="16" TextWrapping="Wrap">
|
||||
Mods <Bold>will break</Bold> every update. This is normal, and <Bold>not</Bold> Beat Games' fault.
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock Grid.Row="5" Margin="15,5" FontSize="16" TextWrapping="Wrap">
|
||||
Mods <Bold>will</Bold> cause bugs and performance issues. This is <Bold>not</Bold> Beat Games' fault.
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock Grid.Row="6" Margin="15,5" FontSize="16" TextWrapping="Wrap">
|
||||
Mods are made for <Bold>free</Bold> by people in their <Bold>free time.</Bold> Please be patient and understanding.
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock Grid.Row="7" Margin="0,5" FontSize="16" TextWrapping="Wrap">
|
||||
<Bold>DO NOT</Bold> leave negative reviews because mods broke. This is <Bold>not</Bold> Beat Games' fault. <LineBreak/> They are not trying to kill mods.
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock Grid.Row="8" Margin="0,5" FontSize="16" TextWrapping="Wrap">
|
||||
If I keep seeing people leave negative reviews <Italic>because</Italic> mods broke, <LineBreak/><Bold>I will personally kill mods with a rusty spoon</Bold>
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock Grid.Row="9" Margin="0,5" FontSize="16" TextWrapping="Wrap">
|
||||
Please read the Beginners Guide on the
|
||||
<Hyperlink NavigateUri="https://bsmg.wiki/beginners-guide" RequestNavigate="Hyperlink_RequestNavigate">
|
||||
Wiki
|
||||
</Hyperlink>.
|
||||
</TextBlock>
|
||||
|
||||
<StackPanel Margin="10" Grid.Row="10" Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<Button Margin="0,0,10,0" Name="Agree" Height="35" Width="100" VerticalAlignment="Center" HorizontalAlignment="Center" Click="Agree_Click">
|
||||
<TextBlock FontSize="18" HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
I Agree
|
||||
</TextBlock>
|
||||
</Button>
|
||||
<Button Margin="10,0,0,0" Name="Disagree" Height="35" Width="100" VerticalAlignment="Center" HorizontalAlignment="Center" Click="Disagree_Click">
|
||||
<TextBlock FontSize="18" HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
Disagree
|
||||
</TextBlock>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Page>
|
|
@ -1,66 +0,0 @@
|
|||
<Page x:Class="ModAssistant.Pages.Invalid"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:ModAssistant.Pages"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="629" d:DesignWidth="1182"
|
||||
Title="Invalid">
|
||||
|
||||
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock VerticalAlignment="Center" Grid.ColumnSpan="2" HorizontalAlignment="Left" FontSize="48" Margin="0">Invalid Installation Detected</TextBlock>
|
||||
<TextBlock Grid.Row="1" Grid.ColumnSpan="2" FontWeight="Bold" Margin="0" FontSize="28">Your game installation is corrupted or otherwise invalid</TextBlock>
|
||||
<TextBlock Grid.Row="2" Grid.ColumnSpan="2" Margin="0,5" FontSize="16" TextWrapping="Wrap">This can happen if your copy of the game is pirated, or if you copied a pirated copy over your legit install</TextBlock>
|
||||
|
||||
<TextBlock Grid.Row="3" Grid.ColumnSpan="2" Margin="15,5" FontSize="16" TextWrapping="Wrap">
|
||||
If your copy of the game is pirated,
|
||||
<Bold>please purchase the game
|
||||
<Hyperlink NavigateUri="https://beatgames.com/" RequestNavigate="Hyperlink_RequestNavigate">
|
||||
HERE
|
||||
</Hyperlink>.
|
||||
</Bold>
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock Grid.Row="4" Grid.ColumnSpan="2" Margin="15,5" FontSize="16" TextWrapping="Wrap">
|
||||
If your copy of the game is <Bold>not</Bold> pirated, please
|
||||
<Hyperlink NavigateUri="https://bsmg.wiki/support#clean-installation" RequestNavigate="Hyperlink_RequestNavigate">
|
||||
do a clean install
|
||||
</Hyperlink>.
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock Grid.Row="5" Grid.ColumnSpan="2" Margin="15,5" FontSize="16" TextWrapping="Wrap">
|
||||
If those don't help, ask for support in the <Span Foreground="Blue">#support</Span> channel in
|
||||
<Hyperlink NavigateUri="https://discord.gg/beatsabermods" RequestNavigate="Hyperlink_RequestNavigate">
|
||||
BSMG
|
||||
</Hyperlink>.
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock Grid.Row="6" Grid.ColumnSpan="2" FontWeight="Bold" Margin="0" FontSize="28"></TextBlock>
|
||||
<TextBlock Grid.Row="7" Grid.ColumnSpan="2" FontWeight="Bold" Margin="0" FontSize="20">If you used to have a pirated version but have since bought the game:</TextBlock>
|
||||
<Border Grid.Row="8" Background="LightGray" Height="30" MinWidth="650" >
|
||||
<TextBlock Name="DirectoryTextBlock" Margin="5" Text="{Binding InstallDirectory}" HorizontalAlignment="Left" VerticalAlignment="Center" FontSize="16"/>
|
||||
</Border>
|
||||
<Button Grid.Row="8" Grid.Column="2" Margin="3" Height="30" Width="80" Content="Select Folder" Click="SelectDirButton_Click"/>
|
||||
<TextBlock Grid.Row="9" Grid.ColumnSpan="2" FontWeight="Bold" Margin="0" FontSize="20">You will need to restart Mod Assistant after changing to the legit install.</TextBlock>
|
||||
|
||||
</Grid>
|
||||
</Page>
|
|
@ -1,71 +0,0 @@
|
|||
<Page x:Class="ModAssistant.Pages.Mods"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:ModAssistant"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="629" d:DesignWidth="1182"
|
||||
Title="Mods">
|
||||
|
||||
<Grid>
|
||||
<ListView Name="ModsListView" Grid.Column="1" SelectionMode="Single" SelectionChanged="ModsListView_SelectionChanged">
|
||||
<ListView.View>
|
||||
<GridView>
|
||||
<GridView.Columns>
|
||||
<GridViewColumn Width="30">
|
||||
<GridViewColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<CheckBox Name="ModCheckBox" IsEnabled="{Binding IsEnabled}" Tag="{Binding ModInfo}" IsChecked="{Binding Path=IsSelected, Mode=TwoWay}" Checked="ModCheckBox_Checked" Unchecked="ModCheckBox_Unchecked"/>
|
||||
</DataTemplate>
|
||||
</GridViewColumn.CellTemplate>
|
||||
</GridViewColumn>
|
||||
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding ModName}" />
|
||||
|
||||
<GridViewColumn x:Name="InstalledColumn" Header="Installed">
|
||||
<GridViewColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding InstalledVersion}" Foreground="{Binding GetVersionColor}" TextDecorations="{Binding GetVersionDecoration}" />
|
||||
</DataTemplate>
|
||||
</GridViewColumn.CellTemplate>
|
||||
</GridViewColumn>
|
||||
|
||||
<GridViewColumn Header="Latest" DisplayMemberBinding="{Binding ModVersion}" />
|
||||
<GridViewColumn x:Name="DescriptionColumn" Header="Description" Width="750" >
|
||||
<GridViewColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Margin="{Binding PromotionMargin}">
|
||||
<Hyperlink NavigateUri="{Binding PromotionLink}" RequestNavigate="Hyperlink_RequestNavigate">
|
||||
<TextBlock Text="{Binding PromotionText}" />
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
<TextBlock Text="{Binding ModDescription}" />
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
|
||||
</GridViewColumn.CellTemplate>
|
||||
</GridViewColumn>
|
||||
|
||||
<GridViewColumn x:Name="UninstallColumn" Header="Uninstall" Width="70">
|
||||
<GridViewColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<Button Name="Uninstall" Content="Uninstall" Visibility="{Binding CanSeeDelete}" IsEnabled="{Binding CanDelete}" Foreground="Red" Tag="{Binding ModInfo}" Click="Uninstall_Click"/>
|
||||
</DataTemplate>
|
||||
</GridViewColumn.CellTemplate>
|
||||
</GridViewColumn>
|
||||
</GridView.Columns>
|
||||
</GridView>
|
||||
</ListView.View>
|
||||
<ListView.GroupStyle>
|
||||
<GroupStyle>
|
||||
<GroupStyle.HeaderTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock FontWeight="Bold" FontSize="14" Text="{Binding Name}"/>
|
||||
</DataTemplate>
|
||||
</GroupStyle.HeaderTemplate>
|
||||
</GroupStyle>
|
||||
</ListView.GroupStyle>
|
||||
</ListView>
|
||||
</Grid>
|
||||
</Page>
|
|
@ -1,623 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web.Script.Serialization;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.IO.Compression;
|
||||
using System.Diagnostics;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace ModAssistant.Pages
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for Mods.xaml
|
||||
/// </summary>
|
||||
public sealed partial class Mods : Page
|
||||
{
|
||||
public static Mods Instance = new Mods();
|
||||
|
||||
public List<string> DefaultMods = new List<string>(){ "SongLoader", "ScoreSaber", "BeatSaverDownloader", "BeatSaverVoting" };
|
||||
public Mod[] ModsList;
|
||||
public Mod[] AllModsList;
|
||||
public static List<Mod> InstalledMods = new List<Mod>();
|
||||
public List<string> CategoryNames = new List<string>();
|
||||
public CollectionView view;
|
||||
public bool PendingChanges;
|
||||
|
||||
public List<ModListItem> ModList { get; set; }
|
||||
|
||||
public Mods()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void RefreshModsList()
|
||||
{
|
||||
if (view != null)
|
||||
view.Refresh();
|
||||
}
|
||||
|
||||
public async void LoadMods()
|
||||
{
|
||||
MainWindow.Instance.InstallButton.IsEnabled = false;
|
||||
MainWindow.Instance.GameVersionsBox.IsEnabled = false;
|
||||
MainWindow.Instance.InfoButton.IsEnabled = false;
|
||||
|
||||
if (ModsList != null)
|
||||
Array.Clear(ModsList, 0, ModsList.Length);
|
||||
if (AllModsList != null)
|
||||
Array.Clear(AllModsList, 0, AllModsList.Length);
|
||||
|
||||
InstalledMods = new List<Mod>();
|
||||
CategoryNames = new List<string>();
|
||||
ModList = new List<ModListItem>();
|
||||
|
||||
ModsListView.Visibility = Visibility.Hidden;
|
||||
|
||||
if (App.CheckInstalledMods)
|
||||
{
|
||||
MainWindow.Instance.MainText = "Checking Installed Mods...";
|
||||
await Task.Run(() => CheckInstalledMods());
|
||||
InstalledColumn.Width = Double.NaN;
|
||||
UninstallColumn.Width = 70;
|
||||
DescriptionColumn.Width = 750;
|
||||
} else
|
||||
{
|
||||
InstalledColumn.Width = 0;
|
||||
UninstallColumn.Width = 0;
|
||||
DescriptionColumn.Width = 800;
|
||||
}
|
||||
|
||||
MainWindow.Instance.MainText = "Loading Mods...";
|
||||
await Task.Run(() => PopulateModsList());
|
||||
|
||||
ModsListView.ItemsSource = ModList;
|
||||
|
||||
view = (CollectionView)CollectionViewSource.GetDefaultView(ModsListView.ItemsSource);
|
||||
PropertyGroupDescription groupDescription = new PropertyGroupDescription("Category");
|
||||
view.GroupDescriptions.Add(groupDescription);
|
||||
|
||||
this.DataContext = this;
|
||||
|
||||
RefreshModsList();
|
||||
ModsListView.Visibility = Visibility.Visible;
|
||||
MainWindow.Instance.MainText = "Finished Loading Mods.";
|
||||
|
||||
MainWindow.Instance.InstallButton.IsEnabled = true;
|
||||
MainWindow.Instance.GameVersionsBox.IsEnabled = true;
|
||||
}
|
||||
|
||||
public void CheckInstalledMods()
|
||||
{
|
||||
GetAllMods();
|
||||
List<string> empty = new List<string>();
|
||||
GetBSIPAVersion();
|
||||
CheckInstallDir("IPA/Pending/Plugins", empty);
|
||||
CheckInstallDir("IPA/Pending/Libs", empty);
|
||||
CheckInstallDir("Plugins", empty);
|
||||
CheckInstallDir("Libs", empty);
|
||||
}
|
||||
|
||||
public void GetAllMods()
|
||||
{
|
||||
string json = string.Empty;
|
||||
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Utils.Constants.BeatModsAPIUrl + "mod");
|
||||
request.AutomaticDecompression = DecompressionMethods.GZip;
|
||||
request.UserAgent = "ModAssistant/" + App.Version;
|
||||
|
||||
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
|
||||
using (Stream stream = response.GetResponseStream())
|
||||
using (StreamReader reader = new StreamReader(stream))
|
||||
{
|
||||
var serializer = new JavaScriptSerializer();
|
||||
AllModsList = serializer.Deserialize<Mod[]>(reader.ReadToEnd());
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckInstallDir(string directory, List<string> blacklist)
|
||||
{
|
||||
if (!Directory.Exists(Path.Combine(App.BeatSaberInstallDirectory, directory)))
|
||||
return;
|
||||
foreach (string file in Directory.GetFileSystemEntries(Path.Combine(App.BeatSaberInstallDirectory, directory)))
|
||||
{
|
||||
if (File.Exists(file) && Path.GetExtension(file) == ".dll" || Path.GetExtension(file) == ".manifest")
|
||||
{
|
||||
Mod mod = GetModFromHash(Utils.CalculateMD5(file));
|
||||
if (mod != null)
|
||||
{
|
||||
AddDetectedMod(mod);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void GetBSIPAVersion()
|
||||
{
|
||||
string InjectorPath = Path.Combine(App.BeatSaberInstallDirectory, "Beat Saber_Data", "Managed", "IPA.Injector.dll");
|
||||
if (!File.Exists(InjectorPath)) return;
|
||||
|
||||
string InjectorHash = Utils.CalculateMD5(InjectorPath);
|
||||
foreach (Mod mod in AllModsList)
|
||||
{
|
||||
if (mod.name.ToLower() == "bsipa")
|
||||
{
|
||||
foreach (Mod.DownloadLink download in mod.downloads)
|
||||
{
|
||||
foreach (Mod.FileHashes fileHash in download.hashMd5)
|
||||
{
|
||||
if (fileHash.hash == InjectorHash)
|
||||
{
|
||||
AddDetectedMod(mod);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddDetectedMod(Mod mod)
|
||||
{
|
||||
if (!InstalledMods.Contains(mod))
|
||||
{
|
||||
InstalledMods.Add(mod);
|
||||
if (App.SelectInstalledMods && !DefaultMods.Contains(mod.name))
|
||||
{
|
||||
DefaultMods.Add(mod.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Mod GetModFromHash(string hash)
|
||||
{
|
||||
foreach (Mod mod in AllModsList)
|
||||
{
|
||||
if (mod.name.ToLower() != "bsipa")
|
||||
{
|
||||
foreach (Mod.DownloadLink download in mod.downloads)
|
||||
{
|
||||
foreach (Mod.FileHashes fileHash in download.hashMd5)
|
||||
{
|
||||
if (fileHash.hash == hash)
|
||||
return mod;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void PopulateModsList()
|
||||
{
|
||||
string json = string.Empty;
|
||||
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Utils.Constants.BeatModsAPIUrl + Utils.Constants.BeatModsModsOptions + "&gameVersion=" + MainWindow.GameVersion);
|
||||
request.AutomaticDecompression = DecompressionMethods.GZip;
|
||||
request.UserAgent = "ModAssistant/" + App.Version;
|
||||
|
||||
try
|
||||
{
|
||||
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
|
||||
using (Stream stream = response.GetResponseStream())
|
||||
using (StreamReader reader = new StreamReader(stream))
|
||||
{
|
||||
var serializer = new JavaScriptSerializer();
|
||||
ModsList = serializer.Deserialize<Mod[]>(reader.ReadToEnd());
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
System.Windows.MessageBox.Show("Could not load mods list.\n\n" + e);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (Mod mod in ModsList)
|
||||
{
|
||||
bool preSelected = mod.required;
|
||||
if (DefaultMods.Contains(mod.name) || (App.SaveModSelection && App.SavedMods.Contains(mod.name)))
|
||||
{
|
||||
preSelected = true;
|
||||
if(!App.SavedMods.Contains(mod.name))
|
||||
{
|
||||
App.SavedMods.Add(mod.name);
|
||||
}
|
||||
}
|
||||
|
||||
RegisterDependencies(mod);
|
||||
|
||||
ModListItem ListItem = new ModListItem()
|
||||
{
|
||||
IsSelected = preSelected,
|
||||
IsEnabled = !mod.required,
|
||||
ModName = mod.name,
|
||||
ModVersion = mod.version,
|
||||
ModDescription = mod.description.Replace("\r\n", " ").Replace("\n", " "),
|
||||
ModInfo = mod,
|
||||
Category = mod.category
|
||||
};
|
||||
|
||||
foreach (Promotion promo in Promotions.ActivePromotions)
|
||||
{
|
||||
if (mod.name == promo.ModName)
|
||||
{
|
||||
ListItem.PromotionText = promo.Text;
|
||||
ListItem.PromotionLink = promo.Link;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Mod installedMod in InstalledMods)
|
||||
{
|
||||
if (mod.name == installedMod.name)
|
||||
{
|
||||
ListItem.InstalledModInfo = installedMod;
|
||||
ListItem.IsInstalled = true;
|
||||
ListItem.InstalledVersion = installedMod.version;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mod.ListItem = ListItem;
|
||||
|
||||
ModList.Add(ListItem);
|
||||
}
|
||||
|
||||
foreach (Mod mod in ModsList)
|
||||
{
|
||||
ResolveDependencies(mod);
|
||||
}
|
||||
}
|
||||
|
||||
public async void InstallMods ()
|
||||
{
|
||||
MainWindow.Instance.InstallButton.IsEnabled = false;
|
||||
string installDirectory = App.BeatSaberInstallDirectory;
|
||||
|
||||
foreach (Mod mod in ModsList)
|
||||
{
|
||||
if (mod.name.ToLower() == "bsipa")
|
||||
{
|
||||
MainWindow.Instance.MainText = $"Installing {mod.name}...";
|
||||
await Task.Run(() => InstallMod(mod, installDirectory));
|
||||
MainWindow.Instance.MainText = $"Installed {mod.name}.";
|
||||
if (!File.Exists(Path.Combine(installDirectory, "winhttp.dll")))
|
||||
{
|
||||
await Task.Run(() =>
|
||||
Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = Path.Combine(installDirectory, "IPA.exe"),
|
||||
WorkingDirectory = installDirectory,
|
||||
Arguments = "-n"
|
||||
}).WaitForExit()
|
||||
);
|
||||
}
|
||||
Pages.Options.Instance.YeetBSIPA.IsEnabled = true;
|
||||
}
|
||||
else if(mod.ListItem.IsSelected)
|
||||
{
|
||||
MainWindow.Instance.MainText = $"Installing {mod.name}...";
|
||||
await Task.Run(() => InstallMod(mod, Path.Combine(installDirectory, @"IPA\Pending")));
|
||||
MainWindow.Instance.MainText = $"Installed {mod.name}.";
|
||||
}
|
||||
}
|
||||
MainWindow.Instance.MainText = "Finished installing mods.";
|
||||
MainWindow.Instance.InstallButton.IsEnabled = true;
|
||||
RefreshModsList();
|
||||
}
|
||||
|
||||
private void InstallMod (Mod mod, string directory)
|
||||
{
|
||||
string downloadLink = null;
|
||||
|
||||
foreach (Mod.DownloadLink link in mod.downloads)
|
||||
{
|
||||
if (link.type == "universal")
|
||||
{
|
||||
downloadLink = link.url;
|
||||
break;
|
||||
} else if (link.type.ToLower() == App.BeatSaberInstallType.ToLower())
|
||||
{
|
||||
downloadLink = link.url;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (String.IsNullOrEmpty(downloadLink))
|
||||
{
|
||||
System.Windows.MessageBox.Show($"Could not find download link for {mod.name}");
|
||||
return;
|
||||
}
|
||||
|
||||
using (MemoryStream stream = new MemoryStream(DownloadMod(Utils.Constants.BeatModsURL + downloadLink)))
|
||||
{
|
||||
using (ZipArchive archive = new ZipArchive(stream))
|
||||
{
|
||||
foreach (ZipArchiveEntry file in archive.Entries)
|
||||
{
|
||||
string fileDirectory = Path.GetDirectoryName(Path.Combine(directory, file.FullName));
|
||||
if (!Directory.Exists(fileDirectory))
|
||||
Directory.CreateDirectory(fileDirectory);
|
||||
|
||||
if(!String.IsNullOrEmpty(file.Name))
|
||||
file.ExtractToFile(Path.Combine(directory, file.FullName), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (App.CheckInstalledMods)
|
||||
{
|
||||
mod.ListItem.IsInstalled = true;
|
||||
mod.ListItem.InstalledVersion = mod.version;
|
||||
mod.ListItem.InstalledModInfo = mod;
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] DownloadMod (string link)
|
||||
{
|
||||
byte[] zip = new WebClient().DownloadData(link);
|
||||
return zip;
|
||||
}
|
||||
|
||||
private void RegisterDependencies(Mod dependent)
|
||||
{
|
||||
if (dependent.dependencies.Length == 0)
|
||||
return;
|
||||
|
||||
foreach (Mod mod in ModsList)
|
||||
{
|
||||
foreach (Mod.Dependency dep in dependent.dependencies)
|
||||
{
|
||||
|
||||
if (dep.name == mod.name)
|
||||
{
|
||||
dep.Mod = mod;
|
||||
mod.Dependents.Add(dependent);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ResolveDependencies(Mod dependent)
|
||||
{
|
||||
if (dependent.ListItem.IsSelected && dependent.dependencies.Length > 0)
|
||||
{
|
||||
foreach (Mod.Dependency dependency in dependent.dependencies)
|
||||
{
|
||||
if (dependency.Mod.ListItem.IsEnabled)
|
||||
{
|
||||
dependency.Mod.ListItem.PreviousState = dependency.Mod.ListItem.IsSelected;
|
||||
dependency.Mod.ListItem.IsSelected = true;
|
||||
dependency.Mod.ListItem.IsEnabled = false;
|
||||
ResolveDependencies(dependency.Mod);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UnresolveDependencies(Mod dependent)
|
||||
{
|
||||
if (!dependent.ListItem.IsSelected && dependent.dependencies.Length > 0)
|
||||
{
|
||||
foreach (Mod.Dependency dependency in dependent.dependencies)
|
||||
{
|
||||
if (!dependency.Mod.ListItem.IsEnabled)
|
||||
{
|
||||
bool needed = false;
|
||||
foreach (Mod dep in dependency.Mod.Dependents)
|
||||
{
|
||||
if (dep.ListItem.IsSelected)
|
||||
{
|
||||
needed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!needed && !dependency.Mod.required)
|
||||
{
|
||||
dependency.Mod.ListItem.IsSelected = dependency.Mod.ListItem.PreviousState;
|
||||
dependency.Mod.ListItem.IsEnabled = true;
|
||||
UnresolveDependencies(dependency.Mod);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ModCheckBox_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Mod mod = ((sender as System.Windows.Controls.CheckBox).Tag as Mod);
|
||||
mod.ListItem.IsSelected = true;
|
||||
ResolveDependencies(mod);
|
||||
App.SavedMods.Add(mod.name);
|
||||
Properties.Settings.Default.SavedMods = String.Join(",", App.SavedMods.ToArray());
|
||||
Properties.Settings.Default.Save();
|
||||
RefreshModsList();
|
||||
}
|
||||
|
||||
private void ModCheckBox_Unchecked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Mod mod = ((sender as System.Windows.Controls.CheckBox).Tag as Mod);
|
||||
mod.ListItem.IsSelected = false;
|
||||
UnresolveDependencies(mod);
|
||||
App.SavedMods.Remove(mod.name);
|
||||
Properties.Settings.Default.SavedMods = String.Join(",", App.SavedMods.ToArray());
|
||||
Properties.Settings.Default.Save();
|
||||
RefreshModsList();
|
||||
}
|
||||
|
||||
public class Category
|
||||
{
|
||||
public string CategoryName { get; set; }
|
||||
public List<ModListItem> Mods = new List<ModListItem>();
|
||||
}
|
||||
|
||||
public class ModListItem
|
||||
{
|
||||
public string ModName { get; set; }
|
||||
public string ModVersion { get; set; }
|
||||
public string ModDescription { get; set; }
|
||||
public bool PreviousState { get; set; }
|
||||
|
||||
public bool IsEnabled { get; set; }
|
||||
public bool IsSelected { get; set; }
|
||||
public Mod ModInfo { get; set; }
|
||||
public string Category { get; set; }
|
||||
|
||||
public Mod InstalledModInfo { get; set; }
|
||||
public bool IsInstalled { get; set; }
|
||||
private string _installedVersion { get; set; }
|
||||
public string InstalledVersion
|
||||
{
|
||||
get
|
||||
{
|
||||
return (String.IsNullOrEmpty(_installedVersion) || !IsInstalled) ? "-" : _installedVersion;
|
||||
}
|
||||
set
|
||||
{
|
||||
_installedVersion = value;
|
||||
}
|
||||
}
|
||||
|
||||
public string GetVersionColor
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!IsInstalled) return "Black";
|
||||
return InstalledVersion == ModVersion ? "Green" : "Red";
|
||||
}
|
||||
}
|
||||
|
||||
public string GetVersionDecoration
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!IsInstalled) return "None";
|
||||
return InstalledVersion == ModVersion ? "None" : "Strikethrough";
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanDelete
|
||||
{
|
||||
get
|
||||
{
|
||||
return (!ModInfo.required && IsInstalled);
|
||||
}
|
||||
}
|
||||
|
||||
public string CanSeeDelete
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!ModInfo.required && IsInstalled)
|
||||
return "Visible";
|
||||
else
|
||||
return "Hidden";
|
||||
}
|
||||
}
|
||||
|
||||
public string PromotionText { get; set; }
|
||||
public string PromotionLink { get; set; }
|
||||
public string PromotionMargin
|
||||
{
|
||||
get
|
||||
{
|
||||
if (String.IsNullOrEmpty(PromotionText)) return "0";
|
||||
return "0,0,5,0";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ModsListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if ((Mods.ModListItem)Mods.Instance.ModsListView.SelectedItem == null)
|
||||
{
|
||||
MainWindow.Instance.InfoButton.IsEnabled = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
MainWindow.Instance.InfoButton.IsEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void UninstallBSIPA(Mod.DownloadLink links)
|
||||
{
|
||||
Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = Path.Combine(App.BeatSaberInstallDirectory, "IPA.exe"),
|
||||
WorkingDirectory = App.BeatSaberInstallDirectory,
|
||||
Arguments = "--revert -n"
|
||||
}).WaitForExit();
|
||||
|
||||
foreach (Mod.FileHashes files in links.hashMd5)
|
||||
{
|
||||
string file = files.file.Replace("IPA/", "").Replace("Data", "Beat Saber_Data");
|
||||
if (File.Exists(Path.Combine(App.BeatSaberInstallDirectory, file)))
|
||||
File.Delete(Path.Combine(App.BeatSaberInstallDirectory, file));
|
||||
}
|
||||
Pages.Options.Instance.YeetBSIPA.IsEnabled = false;
|
||||
}
|
||||
|
||||
private void Uninstall_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Mod mod = ((sender as System.Windows.Controls.Button).Tag as Mod);
|
||||
if (System.Windows.Forms.MessageBox.Show($"Are you sure you want to remove {mod.name}?\nThis could break your other mods.", $"Uninstall {mod.name}?", MessageBoxButtons.YesNo) == DialogResult.Yes)
|
||||
{
|
||||
UninstallModFromList(mod);
|
||||
}
|
||||
}
|
||||
|
||||
private void UninstallModFromList(Mod mod)
|
||||
{
|
||||
UninstallMod(mod.ListItem.InstalledModInfo);
|
||||
mod.ListItem.IsInstalled = false;
|
||||
mod.ListItem.InstalledVersion = null;
|
||||
if (App.SelectInstalledMods)
|
||||
{
|
||||
mod.ListItem.IsSelected = false;
|
||||
UnresolveDependencies(mod);
|
||||
App.SavedMods.Remove(mod.name);
|
||||
Properties.Settings.Default.SavedMods = String.Join(",", App.SavedMods.ToArray());
|
||||
Properties.Settings.Default.Save();
|
||||
RefreshModsList();
|
||||
}
|
||||
view.Refresh();
|
||||
}
|
||||
|
||||
public void UninstallMod(Mod mod)
|
||||
{
|
||||
Mod.DownloadLink links = null;
|
||||
foreach (Mod.DownloadLink link in mod.downloads)
|
||||
{
|
||||
if (link.type.ToLower() == "universal" || link.type.ToLower() == App.BeatSaberInstallType.ToLower())
|
||||
{
|
||||
links = link;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (mod.name.ToLower() == "bsipa")
|
||||
UninstallBSIPA(links);
|
||||
foreach (Mod.FileHashes files in links.hashMd5)
|
||||
{
|
||||
if (File.Exists(Path.Combine(App.BeatSaberInstallDirectory, files.file)))
|
||||
File.Delete(Path.Combine(App.BeatSaberInstallDirectory, files.file));
|
||||
if (File.Exists(Path.Combine(App.BeatSaberInstallDirectory, "IPA", "Pending", files.file)))
|
||||
File.Delete(Path.Combine(App.BeatSaberInstallDirectory, "IPA", "Pending", files.file));
|
||||
}
|
||||
}
|
||||
|
||||
private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e)
|
||||
{
|
||||
Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri));
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
<Page x:Class="ModAssistant.Pages.Options"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:ModAssistant.Pages"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="629" d:DesignWidth="1182"
|
||||
Title="Options">
|
||||
<Page.Resources>
|
||||
|
||||
</Page.Resources>
|
||||
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock Grid.Row="0" Margin="15,5,5,5" Text="Settings" FontSize="24" FontWeight="Bold" HorizontalAlignment="Left"/>
|
||||
|
||||
<TextBlock Grid.Row="1" Margin="5" Text="Install Folder" FontWeight="Bold" HorizontalAlignment="Left" FontSize="16"/>
|
||||
<Border Grid.ColumnSpan="2" Grid.Row="2" Margin="5" Background="LightGray" Height="30" MinWidth="450" >
|
||||
<TextBlock Name="DirectoryTextBlock" Margin="5" Text="{Binding InstallDirectory}" HorizontalAlignment="Left" VerticalAlignment="Center" FontSize="16"/>
|
||||
</Border>
|
||||
<Button Grid.Row="2" Grid.Column="2" Margin="3" Height="30" Width="80" Content="Select Folder" Click="SelectDirButton_Click"/>
|
||||
<Button Grid.Row="2" Grid.Column="3" Margin="3" Height="30" Width="80" Content="Open Folder" Click="OpenDirButton_Click"/>
|
||||
|
||||
<TextBlock Grid.Row="3" Margin="5" Text="Save Selected Mods" FontWeight="Bold" HorizontalAlignment="Left" FontSize="16" />
|
||||
<CheckBox Grid.Row="3" Grid.Column="1" Name="SaveSelected" VerticalAlignment="Center" HorizontalAlignment="Left" IsChecked="{Binding SaveSelection, Mode=TwoWay}" Checked="SaveSelected_Checked" Unchecked="SaveSelected_Unchecked"/>
|
||||
|
||||
<TextBlock Grid.Row="4" Margin="5" Text="Check Installed Mods" FontWeight="Bold" HorizontalAlignment="Left" FontSize="16" />
|
||||
<CheckBox Grid.Row="4" Grid.Column="1" Name="CheckInstalled" VerticalAlignment="Center" HorizontalAlignment="Left" IsChecked="{Binding CheckInstalledMods, Mode=TwoWay}" Checked="CheckInstalled_Checked" Unchecked="CheckInstalled_Unchecked"/>
|
||||
|
||||
<TextBlock Grid.Row="5" Margin="50,5,5,5" Text="Select Installed Mods" FontWeight="Bold" HorizontalAlignment="Left" FontSize="16" />
|
||||
<CheckBox Grid.Row="5" Grid.Column="1" Name="SelectInstalled" VerticalAlignment="Center" HorizontalAlignment="Left" IsChecked="{Binding SelectInstalledMods, Mode=TwoWay}" Checked="SelectInstalled_Checked" Unchecked="SelectInstalled_Unchecked"/>
|
||||
|
||||
<TextBlock Grid.Row="6" Margin="5" Text="Enable OneClick Installs: ↳" FontWeight="Bold" HorizontalAlignment="Left" FontSize="16" />
|
||||
|
||||
<TextBlock Grid.Row="7" Margin="50,5,5,5" Text="BeatSaver" FontWeight="Bold" HorizontalAlignment="Left" FontSize="16" />
|
||||
<CheckBox Grid.Row="7" Grid.Column="1" Name="BeatSaverProtocolHandler" VerticalAlignment="Center" HorizontalAlignment="Left" IsChecked="{Binding BeatSaverProtocolHandlerEnabled}" Checked="BeatSaverProtocolHandler_Checked" Unchecked="BeatSaverProtocolHandler_Unchecked"/>
|
||||
|
||||
<TextBlock Grid.Row="8" Margin="50,5,5,5" Text="ModelSaber" FontWeight="Bold" HorizontalAlignment="Left" FontSize="16" />
|
||||
<CheckBox Grid.Row="8" Grid.Column="1" Name="ModelSaberProtocolHandler" VerticalAlignment="Center" HorizontalAlignment="Left" IsChecked="{Binding ModelSaberProtocolHandlerEnabled}" Checked="ModelSaberProtocolHandler_Checked" Unchecked="ModelSaberProtocolHandler_Unchecked"/>
|
||||
|
||||
<StackPanel Grid.Row="12" Margin="5" Orientation="Horizontal" HorizontalAlignment="Left">
|
||||
<TextBlock Text="Game Type: " FontWeight="Bold" FontSize="16" />
|
||||
<TextBlock Name="GameTypeTextBlock" Text="{Binding InstallType}" FontSize="16"/>
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock Grid.Row="13" Margin="15,5,5,5" Text="Diagnostics" FontSize="24" FontWeight="Bold" HorizontalAlignment="Left"/>
|
||||
<StackPanel Grid.Row="14" Margin="0" Orientation="Horizontal" HorizontalAlignment="Left">
|
||||
<Button Margin="5" Height="30" Width="80" Content="Open Logs" Click="OpenLogsDirButton_Click"/>
|
||||
<Button Margin="5" Height="30" x:Name="YeetBSIPA" Width="100" Content="Uninstall BSIPA" Click="YeetBSIPAButton_Click"/>
|
||||
<Button Margin="5" Height="30" Width="110" Background="Red" Content="Remove All Mods" Click="YeetModsButton_Click"/>
|
||||
</StackPanel>
|
||||
|
||||
</Grid>
|
||||
</Page>
|
|
@ -1,249 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using Path = System.IO.Path;
|
||||
using System.Net;
|
||||
using System.Web.Script.Serialization;
|
||||
using System.Web;
|
||||
|
||||
namespace ModAssistant.Pages
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for Options.xaml
|
||||
/// </summary>
|
||||
///
|
||||
public partial class Options : Page
|
||||
{
|
||||
public static Options Instance = new Options();
|
||||
|
||||
public string InstallDirectory { get; set; }
|
||||
public string InstallType { get; set; }
|
||||
public bool SaveSelection { get; set; }
|
||||
public bool CheckInstalledMods { get; set; }
|
||||
public bool SelectInstalledMods { get; set; }
|
||||
public bool ModelSaberProtocolHandlerEnabled { get; set; }
|
||||
public bool BeatSaverProtocolHandlerEnabled { get; set; }
|
||||
public bool ModSaberProtocolHandlerEnabled { get; set; }
|
||||
public string LogURL { get; private set; }
|
||||
|
||||
public Options()
|
||||
{
|
||||
InitializeComponent();
|
||||
InstallDirectory = App.BeatSaberInstallDirectory;
|
||||
InstallType = App.BeatSaberInstallType;
|
||||
SaveSelection = App.SaveModSelection;
|
||||
CheckInstalledMods = App.CheckInstalledMods;
|
||||
SelectInstalledMods = App.SelectInstalledMods;
|
||||
if (!CheckInstalledMods)
|
||||
SelectInstalled.IsEnabled = false;
|
||||
|
||||
UpdateHandlerStatus();
|
||||
|
||||
this.DataContext = this;
|
||||
}
|
||||
|
||||
public void UpdateHandlerStatus()
|
||||
{
|
||||
ModelSaberProtocolHandlerEnabled = OneClickInstaller.IsRegistered("modelsaber");
|
||||
BeatSaverProtocolHandlerEnabled = OneClickInstaller.IsRegistered("beatsaver");
|
||||
ModSaberProtocolHandlerEnabled = OneClickInstaller.IsRegistered("modsaber");
|
||||
}
|
||||
|
||||
private void SelectDirButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Utils.GetManualDir();
|
||||
DirectoryTextBlock.Text = InstallDirectory;
|
||||
GameTypeTextBlock.Text = InstallType;
|
||||
}
|
||||
|
||||
private void OpenDirButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
System.Diagnostics.Process.Start(InstallDirectory);
|
||||
}
|
||||
|
||||
private void Test_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
MessageBox.Show(Utils.GetSteamDir());
|
||||
}
|
||||
|
||||
private void SaveSelected_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Properties.Settings.Default.SaveSelected = true;
|
||||
App.SaveModSelection = true;
|
||||
Properties.Settings.Default.Save();
|
||||
}
|
||||
|
||||
private void SaveSelected_Unchecked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Properties.Settings.Default.SaveSelected = false;
|
||||
App.SaveModSelection = false;
|
||||
Properties.Settings.Default.Save();
|
||||
}
|
||||
|
||||
private void CheckInstalled_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Properties.Settings.Default.CheckInstalled = true;
|
||||
App.CheckInstalledMods = true;
|
||||
CheckInstalledMods = true;
|
||||
Properties.Settings.Default.Save();
|
||||
SelectInstalled.IsEnabled = true;
|
||||
if (MainWindow.ModsOpened)
|
||||
Mods.Instance.PendingChanges = true;
|
||||
}
|
||||
|
||||
private void CheckInstalled_Unchecked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Properties.Settings.Default.CheckInstalled = false;
|
||||
App.CheckInstalledMods = false;
|
||||
CheckInstalledMods = false;
|
||||
Properties.Settings.Default.Save();
|
||||
SelectInstalled.IsEnabled = false;
|
||||
if (MainWindow.ModsOpened)
|
||||
Mods.Instance.PendingChanges = true;
|
||||
}
|
||||
|
||||
public void ModelSaberProtocolHandler_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
OneClickInstaller.Register("modelsaber");
|
||||
}
|
||||
|
||||
public void ModelSaberProtocolHandler_Unchecked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
OneClickInstaller.Unregister("modelsaber");
|
||||
}
|
||||
|
||||
public void BeatSaverProtocolHandler_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
OneClickInstaller.Register("beatsaver");
|
||||
}
|
||||
|
||||
public void BeatSaverProtocolHandler_Unchecked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
OneClickInstaller.Unregister("beatsaver");
|
||||
}
|
||||
|
||||
private void SelectInstalled_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Properties.Settings.Default.SelectInstalled = true;
|
||||
App.SelectInstalledMods = true;
|
||||
SelectInstalledMods = true;
|
||||
Properties.Settings.Default.Save();
|
||||
}
|
||||
|
||||
private void SelectInstalled_Unchecked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Properties.Settings.Default.SelectInstalled = false;
|
||||
App.SelectInstalledMods = false;
|
||||
SelectInstalledMods = false;
|
||||
Properties.Settings.Default.Save();
|
||||
}
|
||||
|
||||
private async void OpenLogsDirButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
MainWindow.Instance.MainText = "Uploading Log...";
|
||||
await Task.Run(() => UploadLog());
|
||||
|
||||
System.Diagnostics.Process.Start(LogURL);
|
||||
Clipboard.SetText(LogURL);
|
||||
MainWindow.Instance.MainText = "Log URL Copied To Clipboard!";
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
MainWindow.Instance.MainText = "Uploading Log Failed.";
|
||||
MessageBox.Show("Could not upload log file to Teknik, please try again or send the file manually.\n ================= \n" + exception, "Uploading log failed!");
|
||||
System.Diagnostics.Process.Start(Path.Combine(InstallDirectory, "Logs"));
|
||||
}
|
||||
}
|
||||
|
||||
private void UploadLog()
|
||||
{
|
||||
const string DateFormat = "yyyy-mm-dd HH:mm:ss";
|
||||
DateTime now = DateTime.Now;
|
||||
Utils.TeknikPasteResponse TeknikResponse;
|
||||
|
||||
string postData =
|
||||
"title=" + "_latest.log (" + now.ToString(DateFormat) + ")" +
|
||||
"&expireUnit=hour&expireLength=5" +
|
||||
"&code=" + HttpUtility.UrlEncode(File.ReadAllText(Path.Combine(InstallDirectory, "Logs", "_latest.log")));
|
||||
byte[] byteArray = Encoding.UTF8.GetBytes(postData);
|
||||
|
||||
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Utils.Constants.TeknikAPIUrl + "Paste");
|
||||
request.AutomaticDecompression = DecompressionMethods.GZip;
|
||||
request.UserAgent = "ModAssistant/" + App.Version;
|
||||
request.Method = "POST";
|
||||
request.ContentType = "application/x-www-form-urlencoded";
|
||||
request.ContentLength = byteArray.Length;
|
||||
|
||||
Stream dataStream = request.GetRequestStream();
|
||||
dataStream.Write(byteArray, 0, byteArray.Length);
|
||||
dataStream.Close();
|
||||
|
||||
using (WebResponse response = (WebResponse)request.GetResponse())
|
||||
using (Stream stream = response.GetResponseStream())
|
||||
using (StreamReader reader = new StreamReader(stream))
|
||||
{
|
||||
var serializer = new JavaScriptSerializer();
|
||||
TeknikResponse = serializer.Deserialize<Utils.TeknikPasteResponse>(reader.ReadToEnd());
|
||||
}
|
||||
LogURL = TeknikResponse.result.url;
|
||||
}
|
||||
|
||||
private async void YeetBSIPAButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (Mods.Instance.AllModsList == null)
|
||||
{
|
||||
MainWindow.Instance.MainText = "Getting Mod List...";
|
||||
await Task.Run(() => Mods.Instance.GetAllMods());
|
||||
MainWindow.Instance.MainText = "Finding BSIPA Version...";
|
||||
await Task.Run(() => Mods.Instance.GetBSIPAVersion());
|
||||
}
|
||||
foreach(Mod mod in Mods.InstalledMods)
|
||||
{
|
||||
if (mod.name.ToLower() == "bsipa")
|
||||
{
|
||||
Mods.Instance.UninstallMod(mod);
|
||||
break;
|
||||
}
|
||||
}
|
||||
MainWindow.Instance.MainText = "BSIPA Uninstalled...";
|
||||
}
|
||||
private async void YeetModsButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (System.Windows.Forms.MessageBox.Show($"Are you sure you want to remove ALL mods?\nThis cannot be undone.", $"Uninstall All Mods?", System.Windows.Forms.MessageBoxButtons.YesNo) == System.Windows.Forms.DialogResult.Yes)
|
||||
{
|
||||
|
||||
if (Mods.Instance.AllModsList == null)
|
||||
{
|
||||
MainWindow.Instance.MainText = "Getting Mod List...";
|
||||
await Task.Run(() => Mods.Instance.CheckInstalledMods());
|
||||
}
|
||||
foreach (Mod mod in Mods.InstalledMods)
|
||||
{
|
||||
Mods.Instance.UninstallMod(mod);
|
||||
}
|
||||
if (Directory.Exists(Path.Combine(App.BeatSaberInstallDirectory, "Plugins")))
|
||||
Directory.Delete(Path.Combine(App.BeatSaberInstallDirectory, "Plugins"), true);
|
||||
if (Directory.Exists(Path.Combine(App.BeatSaberInstallDirectory, "Libs")))
|
||||
Directory.Delete(Path.Combine(App.BeatSaberInstallDirectory, "Libs"), true);
|
||||
if (Directory.Exists(Path.Combine(App.BeatSaberInstallDirectory, "IPA")))
|
||||
Directory.Delete(Path.Combine(App.BeatSaberInstallDirectory, "IPA"), true);
|
||||
MainWindow.Instance.MainText = "All Mods Uninstalled...";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 5.7 KiB |
Binary file not shown.
Before Width: | Height: | Size: 4.6 KiB |
Binary file not shown.
Before Width: | Height: | Size: 2.7 KiB |
Binary file not shown.
Before Width: | Height: | Size: 5.1 KiB |
Binary file not shown.
Before Width: | Height: | Size: 132 KiB |
57
README.md
57
README.md
|
@ -1,12 +1,51 @@
|
|||
# ModAssistant
|
||||
Simple Beat Saber Mod Installer similar to the [Beat Saber Mod Manager](https://github.com/beat-saber-modding-group/BeatSaberModInstaller)
|
||||
# [Download here!](https://github.com/knah/VRCMelonAssistant/releases/latest)
|
||||
|
||||
[Download here!](https://github.com/Assistant/ModAssistant/releases/latest)
|
||||
VRChat Melon Assistant is a PC mod installer for VRChat. It uses mods published in [VRChat Modding Group Discord](https://discord.gg/rCqKSvR).
|
||||
It's a (very stripped down) fork of [Assistant's Mod Assistant](https://github.com/Assistant/ModAssistant), a mod manager for Beat Saber.
|
||||
**Modifying the VRChat client is not allowed by VRChat Terms of Service and can lead to your account being banned.** Mods available via this installer are manually checked to minimize the chance of that happening, but the risk is always there.
|
||||
VRChat Melon Assistant is not affiliated with and/or endorsed by VRChat Inc.
|
||||
|
||||
Has features such as:
|
||||
* Dependency resolution
|
||||
* Installed Mod detection
|
||||
* Mod removal
|
||||
* One Click Installs support
|
||||
* [Features](#Features)
|
||||
* [Usage](#Usage)
|
||||
* [Themes](#Themes)
|
||||
* [Common Issues](#Common-Issues)
|
||||
|
||||
![Preview](https://assistant.moe/files/ModAssistant.png)
|
||||
## Features
|
||||
|
||||
VRChat Melon Assistant boasts a rich feature set, some of which include:
|
||||
* Installed mod detection
|
||||
* Mod uninstallation
|
||||
* Broken mod move-aside (temporarily uninstalls them until a fix is available)
|
||||
* Complex theming engine
|
||||
* Headpats and Hugs
|
||||
|
||||
## Usage
|
||||
Download the newest build from the release section and run it. This application auto-updates when launched, there is no need to download a new release each time.
|
||||
Then, simply check off the mods that you wish to install or update and click the <kbd>Install or Update</kbd> button. Likewise, click the <kbd>Uninstall</kbd> button to remove any mods.
|
||||
|
||||
|
||||
## Themes
|
||||
VRChat Melon Assistant should support themes for Mod Assistant. Check [its README](https://github.com/Assistant/ModAssistant#themes) for more info on theming.
|
||||
However, this is not a supported feature. If something doesn't work, don't complain to either Assistant or knah.
|
||||
|
||||
## Common Issues
|
||||
**I hit install but I don't see anything in game!**
|
||||
Double check that you followed the [Usage](#usage) instructions correctly.
|
||||
Make sure you're looking in the right place. Sometimes mod menus move as modding libraries/practices change.
|
||||
Additionally, make sure the proper VRChat installation directory is selected in option tab.
|
||||
|
||||
**I don't see a certain mod in the mods list!**
|
||||
VRChat Melon Assistant uses mods from VRChat Modding Group and shows whatever is available for download. It's recommended to avoid non-VRCMG mods due to rampant spread of malware disguised as mods.
|
||||
|
||||
**I hit install but now my game won't launch, I can't click any buttons, I only see a black screen, etc**
|
||||
Please visit the [VRChat Modding Group](https://discord.gg/rCqKSvR) `#help-and-support` channel. Check the pinned messages or ask for help and see if you can work out things out.
|
||||
|
||||
## Credits
|
||||
Lemon icon from Twitter Emoji
|
||||
https://github.com/twitter/twemoji
|
||||
|
||||
Original Mod Assistant is made by Assistant and used under the terms of MIT License.
|
||||
https://github.com/Assistant/ModAssistant
|
||||
|
||||
semver by Max Hauser
|
||||
https://github.com/maxhauser/semver
|
||||
|
|
|
@ -3,7 +3,14 @@ Microsoft Visual Studio Solution File, Format Version 12.00
|
|||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.27130.2027
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModAssistant", "ModAssistant\ModAssistant.csproj", "{6A224B82-40DA-40B3-94DC-EFBEC2BDDA39}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VRCMelonAssistant", "VRCMelonAssistant\VRCMelonAssistant.csproj", "{6A224B82-40DA-40B3-94DC-EFBEC2BDDA39}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8DBEE4E3-1033-433D-B1F3-F7D789808141}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
README.md = README.md
|
||||
.github\workflows\dotnet.yml = .github\workflows\dotnet.yml
|
||||
.github\FUNDING.yml = .github\FUNDING.yml
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
39
VRCMelonAssistant/App.config
Normal file
39
VRCMelonAssistant/App.config
Normal file
|
@ -0,0 +1,39 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<configSections>
|
||||
<sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<section name="VRCMelonAssistant.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
|
||||
</sectionGroup>
|
||||
</configSections>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
|
||||
</startup>
|
||||
<userSettings>
|
||||
<VRCMelonAssistant.Properties.Settings>
|
||||
<setting name="InstallFolder" serializeAs="String">
|
||||
<value />
|
||||
</setting>
|
||||
<setting name="StoreType" serializeAs="String">
|
||||
<value />
|
||||
</setting>
|
||||
<setting name="Agreed" serializeAs="String">
|
||||
<value>False</value>
|
||||
</setting>
|
||||
<setting name="UpgradeRequired" serializeAs="String">
|
||||
<value>True</value>
|
||||
</setting>
|
||||
<setting name="LastTab" serializeAs="String">
|
||||
<value />
|
||||
</setting>
|
||||
<setting name="SelectedTheme" serializeAs="String">
|
||||
<value />
|
||||
</setting>
|
||||
<setting name="CloseWindowOnFinish" serializeAs="String">
|
||||
<value>False</value>
|
||||
</setting>
|
||||
<setting name="LanguageCode" serializeAs="String">
|
||||
<value />
|
||||
</setting>
|
||||
</VRCMelonAssistant.Properties.Settings>
|
||||
</userSettings>
|
||||
</configuration>
|
45
VRCMelonAssistant/App.xaml
Normal file
45
VRCMelonAssistant/App.xaml
Normal file
|
@ -0,0 +1,45 @@
|
|||
<Application
|
||||
x:Class="VRCMelonAssistant.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
DispatcherUnhandledException="Application_DispatcherUnhandledException"
|
||||
Startup="Application_Startup">
|
||||
<Application.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<!-- Global i88n Definitions -->
|
||||
<ResourceDictionary Source="Localisation/en.xaml" />
|
||||
<!-- Swapped i88n Definitions -->
|
||||
<ResourceDictionary Source="Localisation/en.xaml" />
|
||||
|
||||
<!-- Load default Scrollbar theme, in case LoadedTheme doesn't have Scrollbar properties applied. -->
|
||||
<ResourceDictionary x:Name="DefaultSidebarTheme" Source="Themes/Default Scrollbar.xaml" />
|
||||
|
||||
<!-- Load theme to be modified via the Theme engine. -->
|
||||
<ResourceDictionary x:Name="LoadedTheme" Source="Themes/Dark.xaml" />
|
||||
|
||||
<!-- Load SVG icons, courtesy of lolPants. -->
|
||||
<ResourceDictionary x:Name="Icons" Source="Resources/Icons.xaml" />
|
||||
|
||||
<ResourceDictionary x:Name="AppResources" Source="Resources/AppResources.xaml" />
|
||||
|
||||
<!-- Load Styles needed for the Theme engine to work. -->
|
||||
<ResourceDictionary x:Name="Button_Style" Source="Styles/Button.xaml" />
|
||||
<ResourceDictionary x:Name="Label_Style" Source="Styles/Label.xaml" />
|
||||
<ResourceDictionary x:Name="TextBlock_Style" Source="Styles/TextBlock.xaml" />
|
||||
<ResourceDictionary x:Name="GridViewColumnHeader_Style" Source="Styles/GridViewColumnHeader.xaml" />
|
||||
<ResourceDictionary x:Name="ListView_Style" Source="Styles/ListView.xaml" />
|
||||
<ResourceDictionary x:Name="ListViewItem_Style" Source="Styles/ListViewItem.xaml" />
|
||||
<ResourceDictionary x:Name="Menu_Style" Source="Styles/Menu.xaml" />
|
||||
<ResourceDictionary x:Name="MenuItem_Style" Source="Styles/MenuItem.xaml" />
|
||||
<ResourceDictionary x:Name="CheckBox_Style" Source="Styles/CheckBox.xaml" />
|
||||
<ResourceDictionary x:Name="ComboBoxItem_Style" Source="Styles/ComboBoxItem.xaml" />
|
||||
<ResourceDictionary x:Name="ComboBox_Style" Source="Styles/ComboBox.xaml" />
|
||||
<ResourceDictionary x:Name="ToggleButton_Style" Source="Styles/ToggleButton.xaml" />
|
||||
<ResourceDictionary x:Name="ScrollBar_Style" Source="Styles/ScrollBar.xaml" />
|
||||
<ResourceDictionary x:Name="RepeatButton_Style" Source="Styles/RepeatButton.xaml" />
|
||||
<ResourceDictionary x:Name="Thumb_Style" Source="Styles/Thumb.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
</Application>
|
198
VRCMelonAssistant/App.xaml.cs
Normal file
198
VRCMelonAssistant/App.xaml.cs
Normal file
|
@ -0,0 +1,198 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
|
||||
namespace VRCMelonAssistant
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for App.xaml
|
||||
/// </summary>
|
||||
public partial class App : Application
|
||||
{
|
||||
public static string VRChatInstallDirectory;
|
||||
public static string VRChatInstallType;
|
||||
|
||||
public static bool CloseWindowOnFinish;
|
||||
public static string Version = Assembly.GetExecutingAssembly().GetName().Version.ToString();
|
||||
public static MainWindow window;
|
||||
public static string Arguments;
|
||||
public static bool Update = true;
|
||||
public static bool GUI = true;
|
||||
|
||||
private async void Application_Startup(object sender, StartupEventArgs e)
|
||||
{
|
||||
// Set SecurityProtocol to prevent crash with TLS
|
||||
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;
|
||||
|
||||
if (VRCMelonAssistant.Properties.Settings.Default.UpgradeRequired)
|
||||
{
|
||||
VRCMelonAssistant.Properties.Settings.Default.Upgrade();
|
||||
VRCMelonAssistant.Properties.Settings.Default.UpgradeRequired = false;
|
||||
VRCMelonAssistant.Properties.Settings.Default.Save();
|
||||
}
|
||||
|
||||
Pages.Options options = Pages.Options.Instance;
|
||||
options.InstallDirectory =
|
||||
VRChatInstallDirectory = Utils.GetInstallDir();
|
||||
|
||||
Languages.LoadLanguages();
|
||||
|
||||
while (string.IsNullOrEmpty(VRChatInstallDirectory))
|
||||
{
|
||||
string title = (string)Current.FindResource("App:InstallDirDialog:Title");
|
||||
string body = (string)Current.FindResource("App:InstallDirDialog:OkCancel");
|
||||
|
||||
if (System.Windows.Forms.MessageBox.Show(body, title, System.Windows.Forms.MessageBoxButtons.OKCancel) == System.Windows.Forms.DialogResult.OK)
|
||||
{
|
||||
VRChatInstallDirectory = Utils.GetManualDir();
|
||||
}
|
||||
else
|
||||
{
|
||||
Environment.Exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
options.InstallType =
|
||||
VRChatInstallType = VRCMelonAssistant.Properties.Settings.Default.StoreType;
|
||||
options.CloseWindowOnFinish =
|
||||
CloseWindowOnFinish = VRCMelonAssistant.Properties.Settings.Default.CloseWindowOnFinish;
|
||||
|
||||
await ArgumentHandler(e.Args);
|
||||
await Init();
|
||||
}
|
||||
|
||||
private async Task Init()
|
||||
{
|
||||
if (Update)
|
||||
{
|
||||
try
|
||||
{
|
||||
await Task.Run(async () => await Updater.Run());
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
Utils.StartAsAdmin(Arguments, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (GUI)
|
||||
{
|
||||
window = new MainWindow();
|
||||
window.Show();
|
||||
}
|
||||
else
|
||||
{
|
||||
//Application.Current.Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ArgumentHandler(string[] args)
|
||||
{
|
||||
Arguments = string.Join(" ", args);
|
||||
while (args.Length > 0)
|
||||
{
|
||||
switch (args[0])
|
||||
{
|
||||
case "--install":
|
||||
if (args.Length < 2 || string.IsNullOrEmpty(args[1]))
|
||||
{
|
||||
Utils.SendNotify(string.Format((string)Current.FindResource("App:InvalidArgument"), "--install"));
|
||||
}
|
||||
|
||||
if (CloseWindowOnFinish)
|
||||
{
|
||||
await Task.Delay(5 * 1000);
|
||||
Current.Shutdown();
|
||||
}
|
||||
|
||||
Update = false;
|
||||
GUI = false;
|
||||
args = Shift(args, 2);
|
||||
break;
|
||||
|
||||
case "--no-update":
|
||||
Update = false;
|
||||
args = Shift(args);
|
||||
break;
|
||||
|
||||
case "--language":
|
||||
if (args.Length < 2 || string.IsNullOrEmpty(args[1]))
|
||||
{
|
||||
Utils.SendNotify(string.Format((string)Current.FindResource("App:InvalidArgument"), "--language"));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Languages.LoadLanguage(args[1]))
|
||||
{
|
||||
VRCMelonAssistant.Properties.Settings.Default.LanguageCode = args[1];
|
||||
VRCMelonAssistant.Properties.Settings.Default.Save();
|
||||
Languages.UpdateUI(args[1]);
|
||||
}
|
||||
}
|
||||
|
||||
args = Shift(args, 2);
|
||||
break;
|
||||
|
||||
case "--register":
|
||||
if (args.Length < 3 || string.IsNullOrEmpty(args[1]))
|
||||
{
|
||||
Utils.SendNotify(string.Format((string)Current.FindResource("App:InvalidArgument"), "--register"));
|
||||
}
|
||||
|
||||
Update = false;
|
||||
GUI = false;
|
||||
args = Shift(args, 3);
|
||||
break;
|
||||
|
||||
case "--unregister":
|
||||
if (args.Length < 2 || string.IsNullOrEmpty(args[1]))
|
||||
{
|
||||
Utils.SendNotify(string.Format((string)Current.FindResource("App:InvalidArgument"), "--unregister"));
|
||||
}
|
||||
|
||||
Update = false;
|
||||
GUI = false;
|
||||
args = Shift(args, 2);
|
||||
break;
|
||||
|
||||
case "--runforever":
|
||||
while (true)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
default:
|
||||
Utils.SendNotify((string)Current.FindResource("App:UnrecognizedArgument"));
|
||||
args = Shift(args);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string[] Shift(string[] array, int places = 1)
|
||||
{
|
||||
if (places >= array.Length) return Array.Empty<string>();
|
||||
string[] newArray = new string[array.Length - places];
|
||||
for (int i = places; i < array.Length; i++)
|
||||
{
|
||||
newArray[i - places] = array[i];
|
||||
}
|
||||
|
||||
return newArray;
|
||||
}
|
||||
|
||||
private void Application_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
|
||||
{
|
||||
string title = (string)Current.FindResource("App:Exception");
|
||||
string body = (string)Current.FindResource("App:UnhandledException");
|
||||
MessageBox.Show($"{body}: {e.Exception}", title, MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
|
||||
e.Handled = true;
|
||||
Current.Shutdown();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,12 +1,8 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
|
||||
namespace ModAssistant
|
||||
namespace VRCMelonAssistant
|
||||
{
|
||||
class Diagnostics
|
||||
{
|
||||
|
@ -16,11 +12,11 @@ namespace ModAssistant
|
|||
|
||||
foreach (string file in Directory.GetFileSystemEntries(path))
|
||||
{
|
||||
string line = String.Empty;
|
||||
string line = string.Empty;
|
||||
|
||||
if (File.Exists(file))
|
||||
{
|
||||
line = Utils.CalculateMD5(file) + " " +LevelSeparator(level) + "├─ " + Path.GetFileName(file);
|
||||
line = Utils.CalculateMD5(file) + " " + LevelSeparator(level) + "├─ " + Path.GetFileName(file);
|
||||
entries.Add(line);
|
||||
|
||||
}
|
||||
|
@ -40,21 +36,21 @@ namespace ModAssistant
|
|||
{
|
||||
MessageBox.Show("! " + file);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
if (entries.Count > 0)
|
||||
{
|
||||
entries[entries.Count - 1] = entries[entries.Count - 1].Replace("├", "└");
|
||||
}
|
||||
|
||||
return entries.ToArray();
|
||||
}
|
||||
|
||||
private static string LevelSeparator(int level)
|
||||
{
|
||||
string separator = String.Empty;
|
||||
for(int i = 0; i < level; i++)
|
||||
string separator = string.Empty;
|
||||
for (int i = 0; i < level; i++)
|
||||
{
|
||||
separator = separator + "│ ";
|
||||
separator += "│ ";
|
||||
}
|
||||
return separator;
|
||||
}
|
125
VRCMelonAssistant/Classes/HardcodedCategories.cs
Normal file
125
VRCMelonAssistant/Classes/HardcodedCategories.cs
Normal file
|
@ -0,0 +1,125 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace VRCMelonAssistant
|
||||
{
|
||||
public static class HardcodedCategories
|
||||
{
|
||||
private static readonly Dictionary<string, List<string>> CategoryContents = new()
|
||||
{
|
||||
{"Safety & Security", new() {"Advanced Safety", "Finitizer", "True Shader Anticrash", "Safety-Presets", "Final IK Sanity"}},
|
||||
{"Core mods and libraries", new() {"UI Expansion Kit", "ActionMenuApi", "VRCModUpdater.Loader", "VRChatUtilityKit"}},
|
||||
{"All-in-one mods", new() {"emmVRCLoader"}},
|
||||
{"Camera mods", new() {
|
||||
"CameraMinus", "DesktopCamera", "BetterSteadycam", "ITR's Melon Cameras", "CameraResChanger",
|
||||
"LocalCameraMod", "Lag Free Screenshots"
|
||||
}},
|
||||
{"Performance & Fidelity", new() {
|
||||
"Core Limiter", "MirrorResolutionUnlimiter", "AvatarHider", "Runtime Graphics Settings",
|
||||
"GamePriority", "FrameFocus", "ClearVRAM", "NoPerformanceStats", "Turbones"
|
||||
}},
|
||||
{"Utilities & Tweaks", new() {
|
||||
"ReloadAvatars", "KeyboardPaste", "No Outlines", "UnmuteSound", "SparkleBeGone",
|
||||
"BTKSAImmersiveHud", "OGTrustRanks", "ToggleMicIcon", "Friends+ home",
|
||||
"MicSensitivity", "CloningBeGone", "ToggleFullScreen", "View Point Tweaker",
|
||||
"SettingsRestart", "SmallUserVolume", "TeleportCameraToYou",
|
||||
"BetterPortalPlacement", "BetterDirections", "ChairExitController",
|
||||
"Panic Button Rework", "SelectYourself", "Trust Color Changer", "Voice Falloff Override"
|
||||
}},
|
||||
{"Hardware support", new() {"LeapMotionExtension", "ThumbParams", "VRCFaceTracking", "VRCPimaxEyeTracker", "VRCBhapticsIntegration"}},
|
||||
{"Dynamic bones", new() {
|
||||
"ImmersiveTouch", "Dynamic Bones Safety", "MultiplayerDynamicBonesMod", "Multiplayer Dynamic Bones",
|
||||
}},
|
||||
{"World tweaks", new() {
|
||||
"PostProcessing", "NearClipPlaneAdj", "RemoveChairs", "ComponentToggle", "No Grabby Hands", "AOOverride"
|
||||
}},
|
||||
{"Fixes", new() {"Invite+ fix", "CursorLockFix", "DownloadFix", "ProneUiFix"}},
|
||||
{"New features & Overhauls", new() {
|
||||
"IKTweaks", "JoinNotifier", "FBT Saver", "BTKSANameplateMod", "AdvancedInvites", "VRCVideoLibrary",
|
||||
"BTKSASelfPortrait", "OldMate", "BetterLoadingScreen", "Loading Screen Pictures", "FavCat",
|
||||
"ActionMenuUtils", "WorldPredownload", "AskToPortal", "Headlight", "ITR's Player Tracer",
|
||||
"InstanceHistory", "PortableMirrorMod", "VRCBonesController", "CalibrationLinesVisualizer",
|
||||
"ITR's Collider Mod", "RememberMe", "TriggerESP"
|
||||
}},
|
||||
{"UI mods", new() {
|
||||
"Particle and DynBone limiter settings UI", "CalibrateConfirm", "Emoji Page Buttons",
|
||||
"UserInfoExtensions", "MLConsoleViewer", "OwO Mod", "ActiveBackground", "PlayerList", "ComfyVRMenu",
|
||||
"DiscordMute", "MicToggle", "VRCPlusPet", "AMMusic", "Friend Notes", "GestureIndicator",
|
||||
"NameplateStats", "PreviewScroller", "ProPlates", "QuickMenuVolume", "ToggleUIStickers"
|
||||
}},
|
||||
{"Movement", new() {
|
||||
"TeleporterVR", "ImmobilizePlayerMod", "TrackingRotator", "OculusPlayspaceMover",
|
||||
"ITR's Gravity Controller", "QMFreeze", "Double-Tap Runner", "Player Rotater",
|
||||
"HeadTurn"
|
||||
}},
|
||||
{"Very Niche Mods", new() {"HWIDPatch", "No Steam. At all.", "Vertex Animation Remover", "LocalPlayerPrefs", "BTKSAGestureMod",}}
|
||||
};
|
||||
|
||||
private static readonly Dictionary<string, string> CategoryDescriptions = new()
|
||||
{
|
||||
{"Safety & Security", "Crash less, block annoyances"},
|
||||
{"Core mods and libraries", "Other mods might require these"},
|
||||
{"All-in-one mods", "It does a lot of stuff"},
|
||||
{"Camera mods", "For all your screenshot or streaming needs"},
|
||||
{"Performance & Fidelity", "Improve performance or make the game look better"},
|
||||
{"Utilities & Tweaks", "Small mods that address specific issues"},
|
||||
{"Hardware support", "For all exotic hardware out there"},
|
||||
{"Dynamic bones", "Mods that affect jiggly bits"},
|
||||
{"World tweaks", "Change aspects of the world you're in"},
|
||||
{"Fixes", "It's not a bug, it's a feature"},
|
||||
{"New features & Overhauls", "Mods that introduce new features or significantly change existing ones"},
|
||||
{"UI mods", "Modify the user interface or introduce new functionality to it"},
|
||||
{"Movement", "Move in new exciting ways"},
|
||||
{"Very Niche Mods", "Only use these if you're really sure you need them"}
|
||||
};
|
||||
|
||||
private static readonly Dictionary<string, string> ModNameToCategory = new();
|
||||
|
||||
static HardcodedCategories()
|
||||
{
|
||||
foreach (var keyValuePair in CategoryContents)
|
||||
foreach (var s in keyValuePair.Value)
|
||||
ModNameToCategory.Add(s.ToLowerInvariant(), keyValuePair.Key);
|
||||
}
|
||||
|
||||
public static string GetCategoryFor(Mod mod)
|
||||
{
|
||||
foreach (var alias in mod.aliases)
|
||||
{
|
||||
if (ModNameToCategory.TryGetValue(alias.ToLowerInvariant(), out var result)) return result;
|
||||
}
|
||||
|
||||
foreach (var version in mod.versions)
|
||||
{
|
||||
if (ModNameToCategory.TryGetValue(version.name.ToLowerInvariant(), out var result)) return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static string GetCategoryDescription(string category)
|
||||
{
|
||||
return CategoryDescriptions.TryGetValue(category, out var result) ? result : "";
|
||||
}
|
||||
|
||||
private static List<(string Original, string Replace)> ourAuthorReplaces =
|
||||
new()
|
||||
{
|
||||
("<@!170953680718266369>", "ImTiara"),
|
||||
("<@!286669951987613706>", "Rafa"),
|
||||
("<@!168795588366696450>", "Grummus"),
|
||||
("<@!167335587488071682>", "KortyBoi/Lily"),
|
||||
("<@!127978642981650432>", "tetra"),
|
||||
("<@!155396491853168640>", "Dawn/arion")
|
||||
};
|
||||
|
||||
public static string FixupAuthor(string authorName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(authorName) || !authorName.Contains("@")) return authorName;
|
||||
|
||||
foreach (var authorReplace in ourAuthorReplaces)
|
||||
authorName = authorName.Replace(authorReplace.Original, authorReplace.Replace);
|
||||
|
||||
return authorName;
|
||||
}
|
||||
}
|
||||
}
|
40
VRCMelonAssistant/Classes/Http.cs
Normal file
40
VRCMelonAssistant/Classes/Http.cs
Normal file
|
@ -0,0 +1,40 @@
|
|||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Web.Script.Serialization;
|
||||
|
||||
namespace VRCMelonAssistant
|
||||
{
|
||||
static class Http
|
||||
{
|
||||
private static HttpClient _client = null;
|
||||
|
||||
public static HttpClient HttpClient
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_client != null) return _client;
|
||||
|
||||
var handler = new HttpClientHandler()
|
||||
{
|
||||
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate,
|
||||
};
|
||||
|
||||
_client = new HttpClient(handler)
|
||||
{
|
||||
Timeout = TimeSpan.FromSeconds(240),
|
||||
};
|
||||
|
||||
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
|
||||
_client.DefaultRequestHeaders.Add("User-Agent", "VRCMelonAssistant/" + App.Version);
|
||||
|
||||
return _client;
|
||||
}
|
||||
}
|
||||
|
||||
public static JavaScriptSerializer JsonSerializer = new JavaScriptSerializer()
|
||||
{
|
||||
MaxJsonLength = int.MaxValue,
|
||||
};
|
||||
}
|
||||
}
|
49
VRCMelonAssistant/Classes/HyperlinkExtensions.cs
Normal file
49
VRCMelonAssistant/Classes/HyperlinkExtensions.cs
Normal file
|
@ -0,0 +1,49 @@
|
|||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Documents;
|
||||
|
||||
namespace VRCMelonAssistant
|
||||
{
|
||||
public static class HyperlinkExtensions
|
||||
{
|
||||
public static bool GetIsExternal(DependencyObject obj)
|
||||
{
|
||||
return (bool)obj.GetValue(IsExternalProperty);
|
||||
}
|
||||
|
||||
public static void SetIsExternal(DependencyObject obj, bool value)
|
||||
{
|
||||
obj.SetValue(IsExternalProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty IsExternalProperty = DependencyProperty.RegisterAttached("IsExternal", typeof(bool), typeof(HyperlinkExtensions), new UIPropertyMetadata(false, OnIsExternalChanged));
|
||||
|
||||
private static void OnIsExternalChanged(object sender, DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
var hyperlink = sender as Hyperlink;
|
||||
|
||||
if ((bool)args.NewValue)
|
||||
hyperlink.RequestNavigate += Hyperlink_RequestNavigate;
|
||||
else
|
||||
hyperlink.RequestNavigate -= Hyperlink_RequestNavigate;
|
||||
}
|
||||
|
||||
public static void Hyperlink_RequestNavigate(object sender, System.Windows.Navigation.RequestNavigateEventArgs e)
|
||||
{
|
||||
Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri));
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
public static void NoAwait(this Task task)
|
||||
{
|
||||
task.ContinueWith(t =>
|
||||
{
|
||||
if (t.IsFaulted)
|
||||
{
|
||||
Utils.ShowErrorMessageBox("Exception in free-floating task", t.Exception);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
118
VRCMelonAssistant/Classes/InstallHandlers.cs
Normal file
118
VRCMelonAssistant/Classes/InstallHandlers.cs
Normal file
|
@ -0,0 +1,118 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using VRCMelonAssistant.Pages;
|
||||
|
||||
namespace VRCMelonAssistant
|
||||
{
|
||||
public static class InstallHandlers
|
||||
{
|
||||
public static bool IsMelonLoaderInstalled()
|
||||
{
|
||||
return File.Exists(Path.Combine(App.VRChatInstallDirectory, "version.dll")) && File.Exists(Path.Combine(App.VRChatInstallDirectory, "MelonLoader", "Dependencies", "Bootstrap.dll"));
|
||||
}
|
||||
|
||||
public static bool RemoveMelonLoader()
|
||||
{
|
||||
MainWindow.Instance.MainText = $"{(string)App.Current.FindResource("Mods:UnInstallingMelonLoader")}...";
|
||||
|
||||
try
|
||||
{
|
||||
var versionDllPath = Path.Combine(App.VRChatInstallDirectory, "version.dll");
|
||||
var melonLoaderDirPath = Path.Combine(App.VRChatInstallDirectory, "MelonLoader");
|
||||
|
||||
if (File.Exists(versionDllPath))
|
||||
File.Delete(versionDllPath);
|
||||
if (Directory.Exists(melonLoaderDirPath))
|
||||
Directory.Delete(melonLoaderDirPath, true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"{App.Current.FindResource("Mods:UninstallMLFailed")}.\n\n" + ex);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static async Task InstallMelonLoader()
|
||||
{
|
||||
if (!RemoveMelonLoader()) return;
|
||||
|
||||
try
|
||||
{
|
||||
MainWindow.Instance.MainText = $"{(string) App.Current.FindResource("Mods:DownloadingMelonLoader")}...";
|
||||
|
||||
using var installerZip = await DownloadFileToMemory("https://github.com/LavaGang/MelonLoader/releases/latest/download/MelonLoader.x64.zip");
|
||||
using var zipReader = new ZipArchive(installerZip, ZipArchiveMode.Read);
|
||||
|
||||
MainWindow.Instance.MainText = $"{(string) App.Current.FindResource("Mods:UnpackingMelonLoader")}...";
|
||||
|
||||
foreach (var zipArchiveEntry in zipReader.Entries)
|
||||
{
|
||||
var targetFileName = Path.Combine(App.VRChatInstallDirectory, zipArchiveEntry.FullName);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(targetFileName));
|
||||
using var targetFile = File.OpenWrite(targetFileName);
|
||||
using var entryStream = zipArchiveEntry.Open();
|
||||
await entryStream.CopyToAsync(targetFile);
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(Path.Combine(App.VRChatInstallDirectory, "Mods"));
|
||||
Directory.CreateDirectory(Path.Combine(App.VRChatInstallDirectory, "Plugins"));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"{App.Current.FindResource("Mods:InstallMLFailed")}.\n\n" + ex);
|
||||
}
|
||||
}
|
||||
|
||||
internal static async Task<Stream> DownloadFileToMemory(string link)
|
||||
{
|
||||
using var resp = await Http.HttpClient.GetAsync(link);
|
||||
var newStream = new MemoryStream();
|
||||
await resp.Content.CopyToAsync(newStream);
|
||||
newStream.Position = 0;
|
||||
return newStream;
|
||||
}
|
||||
|
||||
public static async Task InstallMod(Mod mod)
|
||||
{
|
||||
string downloadLink = mod.versions[0].downloadLink;
|
||||
|
||||
if (string.IsNullOrEmpty(downloadLink))
|
||||
{
|
||||
MessageBox.Show(string.Format((string)App.Current.FindResource("Mods:ModDownloadLinkMissing"), mod.versions[0].name));
|
||||
return;
|
||||
}
|
||||
|
||||
if (mod.installedFilePath != null)
|
||||
File.Delete(mod.installedFilePath);
|
||||
|
||||
|
||||
string targetFilePath = "";
|
||||
|
||||
using (var resp = await Http.HttpClient.GetAsync(downloadLink))
|
||||
{
|
||||
var stream = new MemoryStream();
|
||||
await resp.Content.CopyToAsync(stream);
|
||||
stream.Position = 0;
|
||||
|
||||
targetFilePath = Path.Combine(App.VRChatInstallDirectory, mod.versions[0].IsPlugin ? "Plugins" : "Mods",
|
||||
mod.versions[0].IsBroken ? "Broken" : (mod.versions[0].IsRetired ? "Retired" : ""), resp.RequestMessage.RequestUri.Segments.Last());
|
||||
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(targetFilePath));
|
||||
|
||||
using var targetFile = File.OpenWrite(targetFilePath);
|
||||
await stream.CopyToAsync(targetFile);
|
||||
}
|
||||
|
||||
mod.ListItem.IsInstalled = true;
|
||||
mod.installedFilePath = targetFilePath;
|
||||
mod.ListItem.InstalledVersion = mod.versions[0].modVersion;
|
||||
mod.ListItem.InstalledModInfo = mod;
|
||||
}
|
||||
}
|
||||
}
|
74
VRCMelonAssistant/Classes/Languages.cs
Normal file
74
VRCMelonAssistant/Classes/Languages.cs
Normal file
|
@ -0,0 +1,74 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using VRCMelonAssistant.Pages;
|
||||
|
||||
namespace VRCMelonAssistant
|
||||
{
|
||||
class Languages
|
||||
{
|
||||
public static string LoadedLanguage { get; private set; }
|
||||
public static List<CultureInfo> LoadedLanguages { get => availableCultures.ToList(); }
|
||||
public static bool FirstRun = true;
|
||||
private static readonly string[] availableLanguageCodes = { "en" };
|
||||
private static IEnumerable<CultureInfo> availableCultures;
|
||||
|
||||
public static void LoadLanguages()
|
||||
{
|
||||
var allCultures = CultureInfo.GetCultures(CultureTypes.AllCultures);
|
||||
|
||||
// Get CultureInfo for any of the available translations
|
||||
availableCultures = allCultures.Where(cultureInfo => availableLanguageCodes.Any(code => code.Equals(cultureInfo.Name)));
|
||||
|
||||
string savedLanguageCode = Properties.Settings.Default.LanguageCode;
|
||||
if (!LoadLanguage(savedLanguageCode))
|
||||
{
|
||||
// If no language code was saved, load system language
|
||||
if (!LoadLanguage(CultureInfo.CurrentUICulture.Name))
|
||||
{
|
||||
LoadLanguage("en");
|
||||
}
|
||||
}
|
||||
UpdateUI(LoadedLanguage);
|
||||
}
|
||||
|
||||
public static void UpdateUI(string languageCode)
|
||||
{
|
||||
if (Options.Instance != null && Options.Instance.LanguageSelectComboBox != null)
|
||||
{
|
||||
Options.Instance.LanguageSelectComboBox.ItemsSource = availableCultures.Select(cultureInfo => cultureInfo.NativeName).ToList();
|
||||
Options.Instance.LanguageSelectComboBox.SelectedIndex = LoadedLanguages.FindIndex(cultureInfo => cultureInfo.Name.Equals(languageCode));
|
||||
}
|
||||
}
|
||||
|
||||
public static ResourceDictionary LanguagesDict
|
||||
{
|
||||
get
|
||||
{
|
||||
return Application.Current.Resources.MergedDictionaries[1];
|
||||
}
|
||||
}
|
||||
|
||||
public static bool LoadLanguage(string languageCode)
|
||||
{
|
||||
if (string.IsNullOrEmpty(languageCode)) return false;
|
||||
try
|
||||
{
|
||||
LanguagesDict.Source = new Uri($"Localisation/{languageCode}.xaml", UriKind.Relative);
|
||||
LoadedLanguage = languageCode;
|
||||
return true;
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
if (languageCode.Contains("-"))
|
||||
{
|
||||
return LoadLanguage(languageCode.Split('-').First());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
41
VRCMelonAssistant/Classes/Mod.cs
Normal file
41
VRCMelonAssistant/Classes/Mod.cs
Normal file
|
@ -0,0 +1,41 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using VRCMelonAssistant.Pages;
|
||||
|
||||
namespace VRCMelonAssistant
|
||||
{
|
||||
public class Mod
|
||||
{
|
||||
public int _id;
|
||||
public string uploadDate;
|
||||
public string category;
|
||||
public string[] aliases;
|
||||
public ModVersion[] versions;
|
||||
public Mods.ModListItem ListItem;
|
||||
public string installedFilePath;
|
||||
public string installedVersion;
|
||||
public bool installedInBrokenDir;
|
||||
public bool installedInRetiredDir;
|
||||
|
||||
public class ModVersion
|
||||
{
|
||||
public int _version;
|
||||
public string name;
|
||||
public string modVersion;
|
||||
public string modType;
|
||||
public string author;
|
||||
public string description;
|
||||
public string downloadLink;
|
||||
public string sourceLink;
|
||||
public string hash;
|
||||
public string updateDate;
|
||||
public string vrchatVersion;
|
||||
public string loaderVersion;
|
||||
public int approvalStatus;
|
||||
|
||||
public bool IsBroken => approvalStatus == 2;
|
||||
public bool IsRetired => approvalStatus == 3;
|
||||
public bool IsPlugin => modType.Equals("plugin", StringComparison.InvariantCultureIgnoreCase);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,10 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ModAssistant
|
||||
namespace VRCMelonAssistant
|
||||
{
|
||||
class Promotions
|
||||
{
|
||||
|
@ -12,9 +6,9 @@ namespace ModAssistant
|
|||
{
|
||||
new Promotion
|
||||
{
|
||||
ModName = "YUR Fit Calorie Tracker",
|
||||
ModName = "emmVRCLoader",
|
||||
Text = "Join our Discord!",
|
||||
Link = "https://yur.chat"
|
||||
Link = "https://discord.gg/emmvrc"
|
||||
}
|
||||
};
|
||||
}
|
546
VRCMelonAssistant/Classes/Themes.cs
Normal file
546
VRCMelonAssistant/Classes/Themes.cs
Normal file
|
@ -0,0 +1,546 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Windows;
|
||||
using System.Windows.Markup;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using Microsoft.Win32;
|
||||
using VRCMelonAssistant.Pages;
|
||||
|
||||
namespace VRCMelonAssistant
|
||||
{
|
||||
public class Themes
|
||||
{
|
||||
public static string LoadedTheme { get; private set; }
|
||||
public static List<string> LoadedThemes { get => loadedThemes.Keys.ToList(); }
|
||||
public static string ThemeDirectory = Path.Combine(Path.GetDirectoryName(Utils.ExePath), "Themes");
|
||||
|
||||
/// <summary>
|
||||
/// Local dictionary of Resource Dictionaries mapped by their names.
|
||||
/// </summary>
|
||||
private static readonly Dictionary<string, Theme> loadedThemes = new Dictionary<string, Theme>();
|
||||
private static readonly List<string> preInstalledThemes = new List<string> { "Light", "Dark", "Light Pink" };
|
||||
|
||||
/// <summary>
|
||||
/// Index of "LoadedTheme" in App.xaml
|
||||
/// </summary>
|
||||
private static readonly int LOADED_THEME_INDEX = 3;
|
||||
|
||||
private static readonly List<string> supportedVideoExtensions = new List<string>() { ".mp4", ".webm", ".mkv", ".avi", ".m2ts" };
|
||||
|
||||
/// <summary>
|
||||
/// Load all themes from local Themes subfolder and from embedded resources.
|
||||
/// This also refreshes the Themes dropdown in the Options screen.
|
||||
/// </summary>
|
||||
public static void LoadThemes()
|
||||
{
|
||||
loadedThemes.Clear();
|
||||
|
||||
/*
|
||||
* Begin by loading local themes. We should always load these first.
|
||||
* I am doing loading here to prevent the LoadTheme function from becoming too crazy.
|
||||
*/
|
||||
foreach (string localTheme in preInstalledThemes)
|
||||
{
|
||||
string location = $"Themes/{localTheme}.xaml";
|
||||
Uri local = new Uri(location, UriKind.Relative);
|
||||
|
||||
ResourceDictionary localDictionary = new ResourceDictionary
|
||||
{
|
||||
Source = local
|
||||
};
|
||||
|
||||
/*
|
||||
* Load any Waifus that come with these built-in themes, too.
|
||||
* The format must be: Background.png and Sidebar.png as a subfolder with the same name as the theme name.
|
||||
* For example: "Themes/Dark/Background.png", or "Themes/Ugly Kulu-Ya-Ku/Sidebar.png"
|
||||
*/
|
||||
Waifus waifus = new Waifus
|
||||
{
|
||||
Background = GetImageFromEmbeddedResources(localTheme, "Background"),
|
||||
Sidebar = GetImageFromEmbeddedResources(localTheme, "Sidebar")
|
||||
};
|
||||
|
||||
Theme theme = new Theme(localTheme, localDictionary)
|
||||
{
|
||||
Waifus = waifus
|
||||
};
|
||||
|
||||
loadedThemes.Add(localTheme, theme);
|
||||
}
|
||||
|
||||
// Load themes from Themes subfolder if it exists.
|
||||
if (Directory.Exists(ThemeDirectory))
|
||||
{
|
||||
foreach (string file in Directory.EnumerateFiles(ThemeDirectory))
|
||||
{
|
||||
FileInfo info = new FileInfo(file);
|
||||
string name = Path.GetFileNameWithoutExtension(info.Name);
|
||||
|
||||
if (info.Extension.ToLower().Equals(".mat"))
|
||||
{
|
||||
Theme theme = LoadZipTheme(ThemeDirectory, name, ".mat");
|
||||
if (theme is null) continue;
|
||||
|
||||
AddOrModifyTheme(name, theme);
|
||||
}
|
||||
}
|
||||
|
||||
// Finally load any loose theme files in subfolders.
|
||||
foreach (string directory in Directory.EnumerateDirectories(ThemeDirectory))
|
||||
{
|
||||
string name = directory.Split('\\').Last();
|
||||
Theme theme = LoadTheme(directory, name);
|
||||
|
||||
if (theme is null) continue;
|
||||
AddOrModifyTheme(name, theme);
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh Themes dropdown in Options screen.
|
||||
if (Options.Instance != null && Options.Instance.ApplicationThemeComboBox != null)
|
||||
{
|
||||
Options.Instance.ApplicationThemeComboBox.ItemsSource = LoadedThemes;
|
||||
Options.Instance.ApplicationThemeComboBox.SelectedIndex = LoadedThemes.IndexOf(LoadedTheme);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs once at the start of the program, performs settings checking.
|
||||
/// </summary>
|
||||
/// <param name="savedTheme">Theme name retrieved from the settings file.</param>
|
||||
public static void FirstLoad(string savedTheme)
|
||||
{
|
||||
if (string.IsNullOrEmpty(savedTheme))
|
||||
{
|
||||
try
|
||||
{
|
||||
ApplyWindowsTheme();
|
||||
}
|
||||
catch
|
||||
{
|
||||
ApplyTheme("Light", false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ApplyTheme(savedTheme, false);
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
ApplyWindowsTheme();
|
||||
MainWindow.Instance.MainText = (string)Application.Current.FindResource("Themes:ThemeNotFound");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies a loaded theme to VRCMelonAssistant.
|
||||
/// </summary>
|
||||
/// <param name="theme">Name of the theme.</param>
|
||||
/// <param name="sendMessage">Send message to MainText (default: true).</param>
|
||||
public static void ApplyTheme(string theme, bool sendMessage = true)
|
||||
{
|
||||
if (loadedThemes.TryGetValue(theme, out Theme newTheme))
|
||||
{
|
||||
LoadedTheme = theme;
|
||||
MainWindow.Instance.BackgroundVideo.Pause();
|
||||
MainWindow.Instance.BackgroundVideo.Visibility = Visibility.Hidden;
|
||||
|
||||
if (newTheme.ThemeDictionary != null)
|
||||
{
|
||||
// TODO: Search by name
|
||||
Application.Current.Resources.MergedDictionaries.RemoveAt(LOADED_THEME_INDEX);
|
||||
Application.Current.Resources.MergedDictionaries.Insert(LOADED_THEME_INDEX, newTheme.ThemeDictionary);
|
||||
}
|
||||
|
||||
Properties.Settings.Default.SelectedTheme = theme;
|
||||
Properties.Settings.Default.Save();
|
||||
|
||||
if (sendMessage)
|
||||
{
|
||||
MainWindow.Instance.MainText = string.Format((string)Application.Current.FindResource("Themes:ThemeSet"), theme);
|
||||
}
|
||||
|
||||
ApplyWaifus();
|
||||
|
||||
if (File.Exists(newTheme.VideoLocation))
|
||||
{
|
||||
Uri videoUri = new Uri(newTheme.VideoLocation, UriKind.Absolute);
|
||||
MainWindow.Instance.BackgroundVideo.Visibility = Visibility.Visible;
|
||||
|
||||
// Load the source video if it's not the same as what's playing, or if the theme is loading for the first time.
|
||||
if (!sendMessage || MainWindow.Instance.BackgroundVideo.Source?.AbsoluteUri != videoUri.AbsoluteUri)
|
||||
{
|
||||
MainWindow.Instance.BackgroundVideo.Stop();
|
||||
MainWindow.Instance.BackgroundVideo.Source = videoUri;
|
||||
}
|
||||
|
||||
MainWindow.Instance.BackgroundVideo.Play();
|
||||
}
|
||||
|
||||
ReloadIcons();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException(string.Format((string)Application.Current.FindResource("Themes:ThemeMissing"), theme));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes an Embedded Resource theme to disk. You cannot write an outside theme to disk.
|
||||
/// </summary>
|
||||
/// <param name="themeName">Name of local theme.</param>
|
||||
public static void WriteThemeToDisk(string themeName)
|
||||
{
|
||||
Directory.CreateDirectory(ThemeDirectory);
|
||||
Directory.CreateDirectory($"{ThemeDirectory}\\{themeName}");
|
||||
|
||||
if (File.Exists($@"{ThemeDirectory}\\{themeName}.xaml") == false)
|
||||
{
|
||||
/*
|
||||
* Any theme that you want to write must be set as an Embedded Resource instead of the default Page.
|
||||
* This is so that we can grab its exact content from Manifest, shown below.
|
||||
* Writing it as is instead of using XAMLWriter keeps the source as is with comments, spacing, and organization.
|
||||
* Using XAMLWriter would compress it into an unorganized mess.
|
||||
*/
|
||||
using (Stream s = Assembly.GetExecutingAssembly().GetManifestResourceStream($"VRCMelonAssistant.Themes.{themeName}.xaml"))
|
||||
using (FileStream writer = new FileStream($@"{ThemeDirectory}\\{themeName}\\{themeName}.xaml", FileMode.Create))
|
||||
{
|
||||
byte[] buffer = new byte[s.Length];
|
||||
int read = s.Read(buffer, 0, (int)s.Length);
|
||||
writer.Write(buffer, 0, buffer.Length);
|
||||
}
|
||||
|
||||
MainWindow.Instance.MainText = string.Format((string)Application.Current.FindResource("Themes:SavedTemplateTheme"), themeName);
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show((string)Application.Current.FindResource("Themes:TemplateThemeExists"));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the theme set on Windows and applies it.
|
||||
/// </summary>
|
||||
public static void ApplyWindowsTheme()
|
||||
{
|
||||
using (RegistryKey key = Registry.CurrentUser
|
||||
.OpenSubKey("Software").OpenSubKey("Microsoft")
|
||||
.OpenSubKey("Windows").OpenSubKey("CurrentVersion")
|
||||
.OpenSubKey("Themes").OpenSubKey("Personalize"))
|
||||
{
|
||||
object registryValueObject = key?.GetValue("AppsUseLightTheme");
|
||||
if (registryValueObject != null)
|
||||
{
|
||||
if ((int)registryValueObject <= 0)
|
||||
{
|
||||
ApplyTheme("Dark", false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ApplyTheme("Light", false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads a Theme from a directory location.
|
||||
/// </summary>
|
||||
/// <param name="directory">The full directory path to the theme.</param>
|
||||
/// <param name="name">Name of the containing folder.</param>
|
||||
/// <returns></returns>
|
||||
private static Theme LoadTheme(string directory, string name)
|
||||
{
|
||||
Theme theme = new Theme(name, null)
|
||||
{
|
||||
Waifus = new Waifus()
|
||||
};
|
||||
|
||||
foreach (string file in Directory.EnumerateFiles(directory).OrderBy(x => x))
|
||||
{
|
||||
FileInfo info = new FileInfo(file);
|
||||
bool isPng = info.Name.EndsWith(".png", StringComparison.OrdinalIgnoreCase);
|
||||
bool isSidePng = info.Name.EndsWith(".side.png", StringComparison.OrdinalIgnoreCase);
|
||||
bool isXaml = info.Name.EndsWith(".xaml", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
if (isPng && !isSidePng)
|
||||
{
|
||||
theme.Waifus.Background = new BitmapImage(new Uri(info.FullName));
|
||||
}
|
||||
|
||||
if (isSidePng)
|
||||
{
|
||||
theme.Waifus.Sidebar = new BitmapImage(new Uri(info.FullName));
|
||||
}
|
||||
|
||||
if (isXaml)
|
||||
{
|
||||
try
|
||||
{
|
||||
Uri resourceSource = new Uri(info.FullName);
|
||||
ResourceDictionary dictionary = new ResourceDictionary
|
||||
{
|
||||
Source = resourceSource
|
||||
};
|
||||
|
||||
theme.ThemeDictionary = dictionary;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
string message = string.Format((string)Application.Current.FindResource("Themes:FailedToLoadXaml"), name, ex.Message);
|
||||
MessageBox.Show(message);
|
||||
}
|
||||
}
|
||||
|
||||
if (supportedVideoExtensions.Contains(info.Extension))
|
||||
{
|
||||
if (info.Name != $"_{name}{info.Extension}" || theme.VideoLocation is null)
|
||||
{
|
||||
theme.VideoLocation = info.FullName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return theme;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Modifies an already existing theme, or adds the theme if it doesn't exist
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the theme.</param>
|
||||
/// <param name="theme">Theme to modify/apply</param>
|
||||
private static void AddOrModifyTheme(string name, Theme theme)
|
||||
{
|
||||
if (loadedThemes.TryGetValue(name, out _))
|
||||
{
|
||||
if (theme.ThemeDictionary != null)
|
||||
{
|
||||
loadedThemes[name].ThemeDictionary = theme.ThemeDictionary;
|
||||
}
|
||||
|
||||
if (theme.Waifus?.Background != null)
|
||||
{
|
||||
if (loadedThemes[name].Waifus is null) loadedThemes[name].Waifus = new Waifus();
|
||||
loadedThemes[name].Waifus.Background = theme.Waifus.Background;
|
||||
}
|
||||
|
||||
if (theme.Waifus?.Sidebar != null)
|
||||
{
|
||||
if (loadedThemes[name].Waifus is null) loadedThemes[name].Waifus = new Waifus();
|
||||
loadedThemes[name].Waifus.Sidebar = theme.Waifus.Sidebar;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(theme.VideoLocation))
|
||||
{
|
||||
loadedThemes[name].VideoLocation = theme.VideoLocation;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
loadedThemes.Add(name, theme);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads themes from pre-packged zips.
|
||||
/// </summary>
|
||||
/// <param name="directory">Theme directory</param>
|
||||
/// <param name="name">Theme name</param>
|
||||
/// <param name="extension">Theme extension</param>
|
||||
private static Theme LoadZipTheme(string directory, string name, string extension)
|
||||
{
|
||||
Waifus waifus = new Waifus();
|
||||
ResourceDictionary dictionary = null;
|
||||
|
||||
using (FileStream stream = new FileStream(Path.Combine(directory, name + extension), FileMode.Open, FileAccess.Read))
|
||||
using (ZipArchive archive = new ZipArchive(stream))
|
||||
{
|
||||
foreach (ZipArchiveEntry file in archive.Entries)
|
||||
{
|
||||
bool isPng = file.Name.EndsWith(".png", StringComparison.OrdinalIgnoreCase);
|
||||
bool isSidePng = file.Name.EndsWith(".side.png", StringComparison.OrdinalIgnoreCase);
|
||||
bool isXaml = file.Name.EndsWith(".xaml", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
if (isPng && !isSidePng)
|
||||
{
|
||||
waifus.Background = GetImageFromStream(Utils.StreamToArray(file.Open()));
|
||||
}
|
||||
|
||||
if (isSidePng)
|
||||
{
|
||||
waifus.Sidebar = GetImageFromStream(Utils.StreamToArray(file.Open()));
|
||||
}
|
||||
|
||||
string videoExtension = $".{file.Name.Split('.').Last()}";
|
||||
if (supportedVideoExtensions.Contains(videoExtension))
|
||||
{
|
||||
string videoName = $"{ThemeDirectory}\\{name}\\_{name}{videoExtension}";
|
||||
Directory.CreateDirectory($"{ThemeDirectory}\\{name}");
|
||||
|
||||
if (File.Exists(videoName) == false)
|
||||
{
|
||||
file.ExtractToFile(videoName, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Check to see if the lengths of each file are different. If they are, overwrite what currently exists.
|
||||
* The reason we are also checking LoadedTheme against the name variable is to prevent overwriting a file that's
|
||||
* already being used by VRCMelonAssistant and causing a System.IO.IOException.
|
||||
*/
|
||||
FileInfo existingInfo = new FileInfo(videoName);
|
||||
if (existingInfo.Length != file.Length && LoadedTheme != name)
|
||||
{
|
||||
file.ExtractToFile(videoName, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isXaml && loadedThemes.ContainsKey(name) == false)
|
||||
{
|
||||
try
|
||||
{
|
||||
dictionary = (ResourceDictionary)XamlReader.Load(file.Open());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
string message = string.Format((string)Application.Current.FindResource("Themes:FailedToLoadXaml"), name, ex.Message);
|
||||
MessageBox.Show(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Theme theme = new Theme(name, dictionary)
|
||||
{
|
||||
Waifus = waifus
|
||||
};
|
||||
|
||||
return theme;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a BeatmapImage from a byte array.
|
||||
/// </summary>
|
||||
/// <param name="array">byte array containing an image.</param>
|
||||
/// <returns></returns>
|
||||
private static BitmapImage GetImageFromStream(byte[] array)
|
||||
{
|
||||
using (var mStream = new MemoryStream(array))
|
||||
{
|
||||
BitmapImage image = new BitmapImage();
|
||||
image.BeginInit();
|
||||
image.CacheOption = BitmapCacheOption.OnLoad;
|
||||
image.StreamSource = mStream;
|
||||
image.EndInit();
|
||||
if (image.CanFreeze) image.Freeze();
|
||||
|
||||
return image;
|
||||
}
|
||||
}
|
||||
|
||||
private static BitmapImage GetImageFromEmbeddedResources(string themeName, string imageName)
|
||||
{
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
var resourceNames = assembly.GetManifestResourceNames();
|
||||
var desiredResourceName = $"VRCMelonAssistant.Themes.{themeName}.{imageName}.png";
|
||||
|
||||
// Don't attempt to access non-existent manifest resources
|
||||
if (!resourceNames.Contains(desiredResourceName))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
using (Stream stream = assembly.GetManifestResourceStream(desiredResourceName))
|
||||
{
|
||||
byte[] imageBytes = new byte[stream.Length];
|
||||
stream.Read(imageBytes, 0, (int)stream.Length);
|
||||
return GetImageFromStream(imageBytes);
|
||||
}
|
||||
}
|
||||
catch { return null; } //We're going to ignore errors here because backgrounds/sidebars should be optional.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies waifus from currently loaded Theme.
|
||||
/// </summary>
|
||||
private static void ApplyWaifus()
|
||||
{
|
||||
Waifus waifus = loadedThemes[LoadedTheme].Waifus;
|
||||
|
||||
if (waifus?.Background is null)
|
||||
{
|
||||
MainWindow.Instance.BackgroundImage.Opacity = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
MainWindow.Instance.BackgroundImage.Opacity = 1;
|
||||
MainWindow.Instance.BackgroundImage.ImageSource = waifus.Background;
|
||||
}
|
||||
|
||||
if (waifus?.Sidebar is null)
|
||||
{
|
||||
MainWindow.Instance.SideImage.Visibility = Visibility.Hidden;
|
||||
}
|
||||
else
|
||||
{
|
||||
MainWindow.Instance.SideImage.Visibility = Visibility.Visible;
|
||||
MainWindow.Instance.SideImage.Source = waifus.Sidebar;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reload the icon colors for the About, Info, Options, and Mods buttons from the currently loaded theme.
|
||||
/// </summary>
|
||||
private static void ReloadIcons()
|
||||
{
|
||||
ResourceDictionary icons = Application.Current.Resources.MergedDictionaries.First(x => x.Source?.ToString() == "Resources/Icons.xaml");
|
||||
|
||||
ChangeColor(icons, "AboutIconColor", "heartDrawingGroup");
|
||||
ChangeColor(icons, "InfoIconColor", "info_circleDrawingGroup");
|
||||
ChangeColor(icons, "OptionsIconColor", "cogDrawingGroup");
|
||||
ChangeColor(icons, "ModsIconColor", "microchipDrawingGroup");
|
||||
ChangeColor(icons, "LoadingIconColor", "loadingOuterDrawingGroup");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Change the color of an image from the loaded theme.
|
||||
/// </summary>
|
||||
/// <param name="icons">ResourceDictionary that contains the image.</param>
|
||||
/// <param name="ResourceColorName">Resource name of the color to change.</param>
|
||||
/// <param name="DrawingGroupName">DrawingGroup name for the image.</param>
|
||||
private static void ChangeColor(ResourceDictionary icons, string ResourceColorName, string DrawingGroupName)
|
||||
{
|
||||
Application.Current.Resources[ResourceColorName] = loadedThemes[LoadedTheme].ThemeDictionary[ResourceColorName];
|
||||
((GeometryDrawing)((DrawingGroup)icons[DrawingGroupName]).Children[0]).Brush = (Brush)Application.Current.Resources[ResourceColorName];
|
||||
}
|
||||
|
||||
private class Waifus
|
||||
{
|
||||
public BitmapImage Background = null;
|
||||
public BitmapImage Sidebar = null;
|
||||
}
|
||||
|
||||
private class Theme
|
||||
{
|
||||
public string Name;
|
||||
public ResourceDictionary ThemeDictionary;
|
||||
public Waifus Waifus = null;
|
||||
public string VideoLocation = null;
|
||||
|
||||
public Theme(string name, ResourceDictionary dictionary)
|
||||
{
|
||||
Name = name;
|
||||
ThemeDictionary = dictionary;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,96 +1,92 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Security.Principal;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web.Script.Serialization;
|
||||
using System.Windows;
|
||||
using static VRCMelonAssistant.Http;
|
||||
|
||||
namespace ModAssistant
|
||||
namespace VRCMelonAssistant
|
||||
{
|
||||
class Updater
|
||||
{
|
||||
private static string APILatestURL = "https://api.github.com/repos/Assistant/ModAssistant/releases/latest";
|
||||
private static readonly string APILatestURL = "https://api.github.com/repos/knah/VRCMelonAssistant/releases/latest";
|
||||
|
||||
private static Update LatestUpdate;
|
||||
private static Version CurrentVersion;
|
||||
private static Version LatestVersion;
|
||||
private static bool NeedsUpdate = false;
|
||||
private static readonly string NewExe = Path.Combine(Path.GetDirectoryName(Utils.ExePath), "VRCMelonAssistant.exe");
|
||||
private static readonly string Arguments = App.Arguments;
|
||||
|
||||
public static bool CheckForUpdate()
|
||||
#pragma warning disable CS0162 // Unreachable code detected
|
||||
public static async Task<bool> CheckForUpdate()
|
||||
{
|
||||
string json = string.Empty;
|
||||
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(APILatestURL);
|
||||
request.AutomaticDecompression = DecompressionMethods.GZip;
|
||||
request.UserAgent = "ModAssistant/" + App.Version;
|
||||
#if DEBUG
|
||||
return false;
|
||||
#endif
|
||||
|
||||
var resp = await HttpClient.GetAsync(APILatestURL);
|
||||
var body = await resp.Content.ReadAsStringAsync();
|
||||
LatestUpdate = JsonSerializer.Deserialize<Update>(body);
|
||||
|
||||
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
|
||||
using (Stream stream = response.GetResponseStream())
|
||||
using (StreamReader reader = new StreamReader(stream))
|
||||
{
|
||||
var serializer = new JavaScriptSerializer();
|
||||
LatestUpdate = serializer.Deserialize<Update>(reader.ReadToEnd());
|
||||
}
|
||||
|
||||
LatestVersion = new Version(LatestUpdate.tag_name.Substring(1));
|
||||
CurrentVersion = new Version(App.Version);
|
||||
|
||||
|
||||
return (LatestVersion > CurrentVersion);
|
||||
}
|
||||
#pragma warning restore CS0162 // Unreachable code detected
|
||||
|
||||
public static void Run()
|
||||
public static async Task Run()
|
||||
{
|
||||
if (Path.GetFileName(Utils.ExePath).Equals("VRCMelonAssistant.old.exe")) RunNew();
|
||||
try
|
||||
{
|
||||
NeedsUpdate = CheckForUpdate();
|
||||
NeedsUpdate = await CheckForUpdate();
|
||||
}
|
||||
catch
|
||||
{
|
||||
Utils.SendNotify("Couldn't check for updates.");
|
||||
Utils.SendNotify((string)Application.Current.FindResource("Updater:CheckFailed"));
|
||||
}
|
||||
|
||||
if (NeedsUpdate) StartUpdate();
|
||||
if (NeedsUpdate) await StartUpdate();
|
||||
}
|
||||
|
||||
public static void StartUpdate()
|
||||
public static async Task StartUpdate()
|
||||
{
|
||||
string Directory = Path.GetDirectoryName(Utils.ExePath);
|
||||
string OldExe = Path.Combine(Directory, "ModAssistant.old.exe");
|
||||
|
||||
string OldExe = Path.Combine(Path.GetDirectoryName(Utils.ExePath), "VRCMelonAssistant.old.exe");
|
||||
string DownloadLink = null;
|
||||
|
||||
foreach (Update.Asset asset in LatestUpdate.assets)
|
||||
{
|
||||
if (asset.name == "ModAssistant.exe")
|
||||
if (asset.name == "VRCMelonAssistant.exe")
|
||||
{
|
||||
DownloadLink = asset.browser_download_url;
|
||||
}
|
||||
}
|
||||
|
||||
if (String.IsNullOrEmpty(DownloadLink))
|
||||
if (string.IsNullOrEmpty(DownloadLink))
|
||||
{
|
||||
Utils.SendNotify("Couldn't download update.");
|
||||
Utils.SendNotify((string)Application.Current.FindResource("Updater:DownloadFailed"));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (File.Exists(OldExe))
|
||||
{
|
||||
File.Delete(OldExe);
|
||||
}
|
||||
|
||||
File.Move(Utils.ExePath, OldExe);
|
||||
|
||||
Utils.Download(DownloadLink, Utils.ExePath);
|
||||
Process.Start(Utils.ExePath);
|
||||
App.Current.Shutdown();
|
||||
|
||||
await Utils.Download(DownloadLink, NewExe);
|
||||
RunNew();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void RunNew()
|
||||
{
|
||||
Process.Start(NewExe, Arguments);
|
||||
Application.Current.Dispatcher.Invoke(() => { Application.Current.Shutdown(); });
|
||||
}
|
||||
}
|
||||
|
||||
public class Update
|
|
@ -1,20 +1,21 @@
|
|||
using Microsoft.Win32;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Management;
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Principal;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Management;
|
||||
using ModAssistant.Properties;
|
||||
using System.Net;
|
||||
using System.Diagnostics;
|
||||
using System.Security.Principal;
|
||||
using Microsoft.Win32;
|
||||
using VRCMelonAssistant.Pages;
|
||||
using static VRCMelonAssistant.Http;
|
||||
|
||||
namespace ModAssistant
|
||||
namespace VRCMelonAssistant
|
||||
{
|
||||
public class Utils
|
||||
{
|
||||
|
@ -23,12 +24,9 @@ namespace ModAssistant
|
|||
|
||||
public class Constants
|
||||
{
|
||||
public const string BeatSaberAPPID = "620980";
|
||||
public const string BeatModsAPIUrl = "https://beatmods.com/api/v1/";
|
||||
public const string TeknikAPIUrl = "https://api.teknik.io/v1/";
|
||||
public const string BeatModsURL = "https://beatmods.com";
|
||||
public const string VRChatAppId = "438100";
|
||||
public const string VRCMGModsJson = "https://api.vrcmg.com/v1/mods";
|
||||
public const string WeebCDNAPIURL = "https://pat.assistant.moe/api/v1.0/";
|
||||
public const string BeatModsModsOptions = "mod?status=approved";
|
||||
public const string MD5Spacer = " ";
|
||||
public static readonly char[] IllegalCharacters = new char[]
|
||||
{
|
||||
|
@ -40,20 +38,6 @@ namespace ModAssistant
|
|||
};
|
||||
}
|
||||
|
||||
public class TeknikPasteResponse
|
||||
{
|
||||
public Result result;
|
||||
public class Result
|
||||
{
|
||||
public string id;
|
||||
public string url;
|
||||
public string title;
|
||||
public string syntax;
|
||||
public DateTime? expiration;
|
||||
public string password;
|
||||
}
|
||||
}
|
||||
|
||||
public class WeebCDNRandomResponse
|
||||
{
|
||||
public int index;
|
||||
|
@ -61,13 +45,15 @@ namespace ModAssistant
|
|||
public string ext;
|
||||
}
|
||||
|
||||
public static void SendNotify(string message, string title = "Mod Assistant")
|
||||
public static void SendNotify(string message, string title = null)
|
||||
{
|
||||
string defaultTitle = (string)Application.Current.FindResource("Utils:NotificationTitle");
|
||||
|
||||
var notification = new System.Windows.Forms.NotifyIcon()
|
||||
{
|
||||
Visible = true,
|
||||
Icon = System.Drawing.SystemIcons.Information,
|
||||
BalloonTipTitle = title,
|
||||
BalloonTipTitle = title ?? defaultTitle,
|
||||
BalloonTipText = message
|
||||
};
|
||||
|
||||
|
@ -78,24 +64,29 @@ namespace ModAssistant
|
|||
|
||||
public static void StartAsAdmin(string Arguments, bool Close = false)
|
||||
{
|
||||
Process process = new Process();
|
||||
process.StartInfo.FileName = Process.GetCurrentProcess().MainModule.FileName;
|
||||
process.StartInfo.Arguments = Arguments;
|
||||
process.StartInfo.UseShellExecute = true;
|
||||
process.StartInfo.Verb = "runas";
|
||||
using (Process process = new Process())
|
||||
{
|
||||
process.StartInfo.FileName = Process.GetCurrentProcess().MainModule.FileName;
|
||||
process.StartInfo.Arguments = Arguments;
|
||||
process.StartInfo.UseShellExecute = true;
|
||||
process.StartInfo.Verb = "runas";
|
||||
|
||||
try
|
||||
{
|
||||
process.Start();
|
||||
if (!Close)
|
||||
process.WaitForExit();
|
||||
try
|
||||
{
|
||||
process.Start();
|
||||
|
||||
if (!Close)
|
||||
{
|
||||
process.WaitForExit();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
MessageBox.Show((string)Application.Current.FindResource("Utils:RunAsAdmin"));
|
||||
}
|
||||
|
||||
if (Close) Application.Current.Shutdown();
|
||||
}
|
||||
catch
|
||||
{
|
||||
MessageBox.Show("Mod Assistant needs to run this task as Admin. Please try again.");
|
||||
}
|
||||
if (Close)
|
||||
App.Current.Shutdown();
|
||||
}
|
||||
|
||||
public static string CalculateMD5(string filename)
|
||||
|
@ -112,13 +103,12 @@ namespace ModAssistant
|
|||
|
||||
public static string GetInstallDir()
|
||||
{
|
||||
string InstallDir = null;
|
||||
|
||||
InstallDir = Properties.Settings.Default.InstallFolder;
|
||||
if (!String.IsNullOrEmpty(InstallDir)
|
||||
string InstallDir = Properties.Settings.Default.InstallFolder;
|
||||
|
||||
if (!string.IsNullOrEmpty(InstallDir)
|
||||
&& Directory.Exists(InstallDir)
|
||||
&& Directory.Exists(Path.Combine(InstallDir, "Beat Saber_Data", "Plugins"))
|
||||
&& File.Exists(Path.Combine(InstallDir, "Beat Saber.exe")))
|
||||
&& Directory.Exists(Path.Combine(InstallDir, "VRChat_Data", "Plugins"))
|
||||
&& File.Exists(Path.Combine(InstallDir, "VRChat.exe")))
|
||||
{
|
||||
return InstallDir;
|
||||
}
|
||||
|
@ -128,7 +118,7 @@ namespace ModAssistant
|
|||
InstallDir = GetSteamDir();
|
||||
}
|
||||
catch { }
|
||||
if (!String.IsNullOrEmpty(InstallDir))
|
||||
if (!string.IsNullOrEmpty(InstallDir))
|
||||
{
|
||||
return InstallDir;
|
||||
}
|
||||
|
@ -138,15 +128,15 @@ namespace ModAssistant
|
|||
InstallDir = GetOculusDir();
|
||||
}
|
||||
catch { }
|
||||
if (!String.IsNullOrEmpty(InstallDir))
|
||||
if (!string.IsNullOrEmpty(InstallDir))
|
||||
{
|
||||
return InstallDir;
|
||||
}
|
||||
|
||||
MessageBox.Show("Could not detect your Beat Saber install folder. Please select it manually.");
|
||||
MessageBox.Show((string)Application.Current.FindResource("Utils:NoInstallFolder"));
|
||||
|
||||
InstallDir = GetManualDir();
|
||||
if (!String.IsNullOrEmpty(InstallDir))
|
||||
if (!string.IsNullOrEmpty(InstallDir))
|
||||
{
|
||||
return InstallDir;
|
||||
}
|
||||
|
@ -156,13 +146,14 @@ namespace ModAssistant
|
|||
|
||||
public static string SetDir(string directory, string store)
|
||||
{
|
||||
App.BeatSaberInstallDirectory = directory;
|
||||
App.BeatSaberInstallType = store;
|
||||
App.VRChatInstallDirectory = directory;
|
||||
App.VRChatInstallType = store;
|
||||
Pages.Options.Instance.InstallDirectory = directory;
|
||||
Pages.Options.Instance.InstallType = store;
|
||||
Properties.Settings.Default.InstallFolder = directory;
|
||||
Properties.Settings.Default.StoreType = store;
|
||||
Properties.Settings.Default.Save();
|
||||
MainWindow.Instance?.MarkModsPageForRefresh();
|
||||
return directory;
|
||||
}
|
||||
|
||||
|
@ -170,18 +161,21 @@ namespace ModAssistant
|
|||
{
|
||||
|
||||
string SteamInstall = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64)?.OpenSubKey("SOFTWARE")?.OpenSubKey("WOW6432Node")?.OpenSubKey("Valve")?.OpenSubKey("Steam")?.GetValue("InstallPath").ToString();
|
||||
if (String.IsNullOrEmpty(SteamInstall))
|
||||
if (string.IsNullOrEmpty(SteamInstall))
|
||||
{
|
||||
SteamInstall = Registry.LocalMachine.OpenSubKey("SOFTWARE")?.OpenSubKey("WOW6432Node")?.OpenSubKey("Valve")?.OpenSubKey("Steam")?.GetValue("InstallPath").ToString();
|
||||
}
|
||||
if (String.IsNullOrEmpty(SteamInstall)) return null;
|
||||
|
||||
if (string.IsNullOrEmpty(SteamInstall)) return null;
|
||||
|
||||
string vdf = Path.Combine(SteamInstall, @"steamapps\libraryfolders.vdf");
|
||||
if (!File.Exists(@vdf)) return null;
|
||||
|
||||
Regex regex = new Regex("\\s\"\\d\"\\s+\"(.+)\"");
|
||||
List<string> SteamPaths = new List<string>();
|
||||
SteamPaths.Add(Path.Combine(SteamInstall, @"steamapps"));
|
||||
List<string> SteamPaths = new List<string>
|
||||
{
|
||||
Path.Combine(SteamInstall, @"steamapps")
|
||||
};
|
||||
|
||||
using (StreamReader reader = new StreamReader(@vdf))
|
||||
{
|
||||
|
@ -199,9 +193,9 @@ namespace ModAssistant
|
|||
regex = new Regex("\\s\"installdir\"\\s+\"(.+)\"");
|
||||
foreach (string path in SteamPaths)
|
||||
{
|
||||
if (File.Exists(Path.Combine(@path, @"appmanifest_" + Constants.BeatSaberAPPID + ".acf")))
|
||||
if (File.Exists(Path.Combine(@path, @"appmanifest_" + Constants.VRChatAppId + ".acf")))
|
||||
{
|
||||
using (StreamReader reader = new StreamReader(Path.Combine(@path, @"appmanifest_" + Constants.BeatSaberAPPID + ".acf")))
|
||||
using (StreamReader reader = new StreamReader(Path.Combine(@path, @"appmanifest_" + Constants.VRChatAppId + ".acf")))
|
||||
{
|
||||
string line;
|
||||
while ((line = reader.ReadLine()) != null)
|
||||
|
@ -209,7 +203,7 @@ namespace ModAssistant
|
|||
Match match = regex.Match(line);
|
||||
if (match.Success)
|
||||
{
|
||||
if (File.Exists(Path.Combine(@path, @"common", match.Groups[1].Value, "Beat Saber.exe")))
|
||||
if (File.Exists(Path.Combine(@path, @"common", match.Groups[1].Value, "VRChat.exe")))
|
||||
{
|
||||
return SetDir(Path.Combine(@path, @"common", match.Groups[1].Value), "Steam");
|
||||
}
|
||||
|
@ -223,18 +217,18 @@ namespace ModAssistant
|
|||
|
||||
public static string GetVersion()
|
||||
{
|
||||
string filename = Path.Combine(App.BeatSaberInstallDirectory, "Beat Saber_Data", "globalgamemanagers");
|
||||
string filename = Path.Combine(App.VRChatInstallDirectory, "VRChat_Data", "globalgamemanagers");
|
||||
using (FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read))
|
||||
{
|
||||
byte[] file = File.ReadAllBytes(filename);
|
||||
byte[] bytes = new byte[16];
|
||||
byte[] bytes = new byte[32];
|
||||
|
||||
fs.Read(file, 0, Convert.ToInt32(fs.Length));
|
||||
fs.Close();
|
||||
int index = Encoding.Default.GetString(file).IndexOf("public.app-category.games") + 136;
|
||||
int index = Encoding.UTF8.GetString(file).IndexOf("public.app-category.games") + 136;
|
||||
|
||||
Array.Copy(file, index, bytes, 0, 16);
|
||||
string version = Encoding.Default.GetString(bytes).Trim(Utils.Constants.IllegalCharacters);
|
||||
Array.Copy(file, index, bytes, 0, 32);
|
||||
string version = Encoding.UTF8.GetString(bytes).Trim(Constants.IllegalCharacters);
|
||||
|
||||
return version;
|
||||
}
|
||||
|
@ -243,13 +237,13 @@ namespace ModAssistant
|
|||
public static string GetOculusDir()
|
||||
{
|
||||
string OculusInstall = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64)?.OpenSubKey("SOFTWARE")?.OpenSubKey("Wow6432Node")?.OpenSubKey("Oculus VR, LLC")?.OpenSubKey("Oculus")?.OpenSubKey("Config")?.GetValue("InitialAppLibrary").ToString();
|
||||
if (String.IsNullOrEmpty(OculusInstall)) return null;
|
||||
if (string.IsNullOrEmpty(OculusInstall)) return null;
|
||||
|
||||
if (!String.IsNullOrEmpty(OculusInstall))
|
||||
if (!string.IsNullOrEmpty(OculusInstall))
|
||||
{
|
||||
if (File.Exists(Path.Combine(OculusInstall, "Software", "hyperbolic-magnetism-beat-saber", "Beat Saber.exe")))
|
||||
if (File.Exists(Path.Combine(OculusInstall, "Software", "vrchat-vrchat", "VRChat.exe")))
|
||||
{
|
||||
return SetDir(Path.Combine(OculusInstall, "Software", "hyperbolic-magnetism-beat-saber"), "Oculus");
|
||||
return SetDir(Path.Combine(OculusInstall, "Software", "vrchat-vrchat"), "Oculus");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -258,47 +252,50 @@ namespace ModAssistant
|
|||
{
|
||||
// Oculus libraries uses GUID volume paths like this "\\?\Volume{0fea75bf-8ad6-457c-9c24-cbe2396f1096}\Games\Oculus Apps", we need to transform these to "D:\Game"\Oculus Apps"
|
||||
WqlObjectQuery wqlQuery = new WqlObjectQuery("SELECT * FROM Win32_Volume");
|
||||
ManagementObjectSearcher searcher = new ManagementObjectSearcher(wqlQuery);
|
||||
Dictionary<string, string> guidLetterVolumes = new Dictionary<string, string>();
|
||||
|
||||
foreach (ManagementBaseObject disk in searcher.Get())
|
||||
using (ManagementObjectSearcher searcher = new ManagementObjectSearcher(wqlQuery))
|
||||
{
|
||||
var diskId = ((string)disk.GetPropertyValue("DeviceID")).Substring(11, 36);
|
||||
var diskLetter = ((string)disk.GetPropertyValue("DriveLetter")) + @"\";
|
||||
Dictionary<string, string> guidLetterVolumes = new Dictionary<string, string>();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(diskLetter))
|
||||
foreach (ManagementBaseObject disk in searcher.Get())
|
||||
{
|
||||
guidLetterVolumes.Add(diskId, diskLetter);
|
||||
}
|
||||
}
|
||||
var diskId = ((string)disk.GetPropertyValue("DeviceID")).Substring(11, 36);
|
||||
var diskLetter = ((string)disk.GetPropertyValue("DriveLetter")) + @"\";
|
||||
|
||||
// Search among the library folders
|
||||
foreach (string libraryKeyName in librariesKey.GetSubKeyNames())
|
||||
{
|
||||
using (RegistryKey libraryKey = librariesKey.OpenSubKey(libraryKeyName))
|
||||
{
|
||||
string libraryPath = (string)libraryKey.GetValue("Path");
|
||||
// Yoinked this code from Megalon's fix. <3
|
||||
string GUIDLetter = guidLetterVolumes.FirstOrDefault(x => libraryPath.Contains(x.Key)).Value;
|
||||
if (!String.IsNullOrEmpty(GUIDLetter))
|
||||
if (!string.IsNullOrWhiteSpace(diskLetter))
|
||||
{
|
||||
string finalPath = Path.Combine(GUIDLetter, libraryPath.Substring(49), @"Software\hyperbolic-magnetism-beat-saber");
|
||||
if (File.Exists(Path.Combine(finalPath, "Beat Saber.exe")))
|
||||
guidLetterVolumes.Add(diskId, diskLetter);
|
||||
}
|
||||
}
|
||||
|
||||
// Search among the library folders
|
||||
foreach (string libraryKeyName in librariesKey.GetSubKeyNames())
|
||||
{
|
||||
using (RegistryKey libraryKey = librariesKey.OpenSubKey(libraryKeyName))
|
||||
{
|
||||
string libraryPath = (string)libraryKey.GetValue("Path");
|
||||
// Yoinked this code from Megalon's fix. <3
|
||||
string GUIDLetter = guidLetterVolumes.FirstOrDefault(x => libraryPath.Contains(x.Key)).Value;
|
||||
if (!string.IsNullOrEmpty(GUIDLetter))
|
||||
{
|
||||
return SetDir(finalPath, "Oculus");
|
||||
string finalPath = Path.Combine(GUIDLetter, libraryPath.Substring(49), @"Software\vrchat-vrchat");
|
||||
if (File.Exists(Path.Combine(finalPath, "VRChat.exe")))
|
||||
{
|
||||
return SetDir(finalPath, "Oculus");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static string GetManualDir()
|
||||
{
|
||||
var dialog = new Microsoft.Win32.SaveFileDialog()
|
||||
var dialog = new SaveFileDialog()
|
||||
{
|
||||
Title = "Select your Beat Saber install folder",
|
||||
Title = (string)Application.Current.FindResource("Utils:InstallDir:DialogTitle"),
|
||||
Filter = "Directory|*.this.directory",
|
||||
FileName = "select"
|
||||
};
|
||||
|
@ -309,10 +306,10 @@ namespace ModAssistant
|
|||
path = path.Replace("\\select.this.directory", "");
|
||||
path = path.Replace(".this.directory", "");
|
||||
path = path.Replace("\\select.directory", "");
|
||||
if (File.Exists(Path.Combine(path, "Beat Saber.exe")))
|
||||
if (File.Exists(Path.Combine(path, "VRChat.exe")))
|
||||
{
|
||||
string store;
|
||||
if (File.Exists(Path.Combine(path, "Beat Saber_Data", "Plugins", "steam_api64.dll")))
|
||||
if (File.Exists(Path.Combine(path, "VRChat_Data", "Plugins", "steam_api64.dll")))
|
||||
{
|
||||
store = "Steam";
|
||||
}
|
||||
|
@ -326,27 +323,71 @@ namespace ModAssistant
|
|||
return null;
|
||||
}
|
||||
|
||||
public static bool isVoid()
|
||||
public static string GetManualFile(string filter = "", string title = "Open File")
|
||||
{
|
||||
string directory = App.BeatSaberInstallDirectory;
|
||||
var dialog = new OpenFileDialog()
|
||||
{
|
||||
Title = title,
|
||||
Filter = filter,
|
||||
Multiselect = false,
|
||||
};
|
||||
|
||||
if (File.Exists(Path.Combine(directory, "IGG-GAMES.COM.url")) ||
|
||||
File.Exists(Path.Combine(directory, "SmartSteamEmu.ini")) ||
|
||||
File.Exists(Path.Combine(directory, "GAMESTORRENT.CO.url")) ||
|
||||
File.Exists(Path.Combine(directory, "Beat Saber_Data", "Plugins", "BSteam crack.dll")) ||
|
||||
File.Exists(Path.Combine(directory, "Beat Saber_Data", "Plugins", "HUHUVR_steam_api64.dll")) ||
|
||||
Directory.GetFiles(Path.Combine(directory, "Beat Saber_Data", "Plugins"), "*.ini", SearchOption.TopDirectoryOnly).Length > 0)
|
||||
return true;
|
||||
return false;
|
||||
if (dialog.ShowDialog() == true)
|
||||
{
|
||||
return dialog.FileName;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void Download(string link, string output)
|
||||
public static byte[] StreamToArray(Stream input)
|
||||
{
|
||||
WebClient webClient = new WebClient();
|
||||
webClient.Headers.Add("user-agent", "ModAssistant/" + App.Version);
|
||||
byte[] buffer = new byte[16 * 1024];
|
||||
using (MemoryStream ms = new MemoryStream())
|
||||
{
|
||||
int read;
|
||||
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
|
||||
{
|
||||
ms.Write(buffer, 0, read);
|
||||
}
|
||||
return ms.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
byte[] file = webClient.DownloadData(link);
|
||||
File.WriteAllBytes(output, file);
|
||||
public static void OpenFolder(string location)
|
||||
{
|
||||
if (!location.EndsWith(Path.DirectorySeparatorChar.ToString())) location += Path.DirectorySeparatorChar;
|
||||
if (Directory.Exists(location))
|
||||
{
|
||||
try
|
||||
{
|
||||
Process.Start(new System.Diagnostics.ProcessStartInfo()
|
||||
{
|
||||
FileName = location,
|
||||
UseShellExecute = true,
|
||||
Verb = "open"
|
||||
});
|
||||
return;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
MessageBox.Show($"{string.Format((string)Application.Current.FindResource("Utils:CannotOpenFolder"), location)}.");
|
||||
}
|
||||
|
||||
public static void Log(string message, string severity = "LOG")
|
||||
{
|
||||
string path = Path.GetDirectoryName(ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoamingAndLocal).FilePath);
|
||||
string logFile = $"{path}{Path.DirectorySeparatorChar}log.log";
|
||||
File.AppendAllText(logFile, $"[{DateTime.UtcNow:yyyy-mm-dd HH:mm:ss.ffffff}][{severity.ToUpper()}] {message}\n");
|
||||
}
|
||||
|
||||
public static async Task Download(string link, string output)
|
||||
{
|
||||
var resp = await HttpClient.GetAsync(link);
|
||||
using (var stream = await resp.Content.ReadAsStreamAsync())
|
||||
using (var fs = new FileStream(output, FileMode.OpenOrCreate, FileAccess.Write))
|
||||
{
|
||||
await stream.CopyToAsync(fs);
|
||||
}
|
||||
}
|
||||
|
||||
private delegate void ShowMessageBoxDelegate(string Message, string Caption);
|
||||
|
@ -367,5 +408,36 @@ namespace ModAssistant
|
|||
ShowMessageBoxDelegate caller = new ShowMessageBoxDelegate(ShowMessageBox);
|
||||
caller.BeginInvoke(Message, null, null, null);
|
||||
}
|
||||
|
||||
public static void ShowErrorMessageBox(string title, Exception ex)
|
||||
{
|
||||
MessageBox.Show(MainWindow.Instance, $"{title}\n{ex}", "Error",
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to write the specified string to the <see cref="System.Windows.Clipboard"/>.
|
||||
/// </summary>
|
||||
/// <param name="text">The string to be written</param>
|
||||
public static void SetClipboard(string text)
|
||||
{
|
||||
bool success = false;
|
||||
try
|
||||
{
|
||||
Clipboard.SetText(text);
|
||||
success = true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Swallow exceptions relating to writing data to clipboard.
|
||||
}
|
||||
|
||||
// This could be placed in the try/catch block but we don't
|
||||
// want to suppress exceptions for non-clipboard operations
|
||||
if (success)
|
||||
{
|
||||
SendNotify($"Copied text to clipboard");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
55
VRCMelonAssistant/Libs/semver/IntExtensions.cs
Normal file
55
VRCMelonAssistant/Libs/semver/IntExtensions.cs
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
Copyright (c) 2013 Max Hauser
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
using System.Text;
|
||||
|
||||
namespace VRCMelonAssistant.Libs
|
||||
{
|
||||
internal static class IntExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// The number of digits in a non-negative number. Returns 1 for all
|
||||
/// negative numbers. That is ok because we are using it to calculate
|
||||
/// string length for a <see cref="StringBuilder"/> for numbers that
|
||||
/// aren't supposed to be negative, but when they are it is just a little
|
||||
/// slower.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This approach is based on https://stackoverflow.com/a/51099524/268898
|
||||
/// where the poster offers performance benchmarks showing this is the
|
||||
/// fastest way to get a number of digits.
|
||||
/// </remarks>
|
||||
public static int Digits(this int n)
|
||||
{
|
||||
if (n < 10) return 1;
|
||||
if (n < 100) return 2;
|
||||
if (n < 1_000) return 3;
|
||||
if (n < 10_000) return 4;
|
||||
if (n < 100_000) return 5;
|
||||
if (n < 1_000_000) return 6;
|
||||
if (n < 10_000_000) return 7;
|
||||
if (n < 100_000_000) return 8;
|
||||
if (n < 1_000_000_000) return 9;
|
||||
return 10;
|
||||
}
|
||||
}
|
||||
}
|
630
VRCMelonAssistant/Libs/semver/SemVersion.cs
Normal file
630
VRCMelonAssistant/Libs/semver/SemVersion.cs
Normal file
|
@ -0,0 +1,630 @@
|
|||
/*
|
||||
Copyright (c) 2013 Max Hauser
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
#if !NETSTANDARD
|
||||
using System.Runtime.Serialization;
|
||||
using System.Security.Permissions;
|
||||
#endif
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace VRCMelonAssistant.Libs
|
||||
{
|
||||
/// <summary>
|
||||
/// A semantic version implementation.
|
||||
/// Conforms with v2.0.0 of http://semver.org
|
||||
/// </summary>
|
||||
#if NETSTANDARD
|
||||
public sealed class SemVersion : IComparable<SemVersion>, IComparable
|
||||
#else
|
||||
[Serializable]
|
||||
public sealed class SemVersion : IComparable<SemVersion>, IComparable, ISerializable
|
||||
#endif
|
||||
{
|
||||
private static readonly Regex ParseEx =
|
||||
new Regex(@"^(?<major>\d+)" +
|
||||
@"(?>\.(?<minor>\d+))?" +
|
||||
@"(?>\.(?<patch>\d+))?" +
|
||||
@"(?>\.(?<extra>\d+))?" +
|
||||
@"(?>\-(?<pre>[0-9A-Za-z\-\.]+))?" +
|
||||
@"(?>\+(?<build>[0-9A-Za-z\-\.]+))?$",
|
||||
#if NETSTANDARD
|
||||
RegexOptions.CultureInvariant | RegexOptions.ExplicitCapture,
|
||||
#else
|
||||
RegexOptions.CultureInvariant | RegexOptions.Compiled | RegexOptions.ExplicitCapture,
|
||||
#endif
|
||||
TimeSpan.FromSeconds(0.5));
|
||||
|
||||
#if !NETSTANDARD
|
||||
#pragma warning disable CA1801 // Parameter unused
|
||||
/// <summary>
|
||||
/// Deserialize a <see cref="SemVersion"/>.
|
||||
/// </summary>
|
||||
/// <exception cref="ArgumentNullException">The <paramref name="info"/> parameter is null.</exception>
|
||||
private SemVersion(SerializationInfo info, StreamingContext context)
|
||||
#pragma warning restore CA1801 // Parameter unused
|
||||
{
|
||||
if (info == null) throw new ArgumentNullException(nameof(info));
|
||||
var semVersion = Parse(info.GetString("SemVersion"));
|
||||
Major = semVersion.Major;
|
||||
Minor = semVersion.Minor;
|
||||
Patch = semVersion.Patch;
|
||||
Prerelease = semVersion.Prerelease;
|
||||
Build = semVersion.Build;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new instance of the <see cref="SemVersion" /> class.
|
||||
/// </summary>
|
||||
/// <param name="major">The major version.</param>
|
||||
/// <param name="minor">The minor version.</param>
|
||||
/// <param name="patch">The patch version.</param>
|
||||
/// <param name="prerelease">The prerelease version (e.g. "alpha").</param>
|
||||
/// <param name="build">The build metadata (e.g. "nightly.232").</param>
|
||||
public SemVersion(int major, int minor = 0, int patch = 0, int extra = 0, string prerelease = "", string build = "")
|
||||
{
|
||||
Major = major;
|
||||
Minor = minor;
|
||||
Patch = patch;
|
||||
Extra = extra;
|
||||
|
||||
Prerelease = prerelease ?? "";
|
||||
Build = build ?? "";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new instance of the <see cref="SemVersion"/> class from
|
||||
/// a <see cref="System.Version"/>.
|
||||
/// </summary>
|
||||
/// <param name="version">The <see cref="Version"/> that is used to initialize
|
||||
/// the Major, Minor, Patch and Build.</param>
|
||||
/// <returns>A <see cref="SemVersion"/> with the same Major and Minor version.
|
||||
/// The Patch version will be the fourth part of the version number. The
|
||||
/// build meta data will contain the third part of the version number if
|
||||
/// it is greater than zero.</returns>
|
||||
public SemVersion(Version version)
|
||||
{
|
||||
if (version == null)
|
||||
throw new ArgumentNullException(nameof(version));
|
||||
|
||||
Major = version.Major;
|
||||
Minor = version.Minor;
|
||||
|
||||
if (version.Revision >= 0)
|
||||
Patch = version.Revision;
|
||||
|
||||
Prerelease = "";
|
||||
|
||||
Build = version.Build > 0 ? version.Build.ToString(CultureInfo.InvariantCulture) : "";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the string representation of a semantic version to its <see cref="SemVersion"/> equivalent.
|
||||
/// </summary>
|
||||
/// <param name="version">The version string.</param>
|
||||
/// <param name="strict">If set to <see langword="true"/> minor and patch version are required,
|
||||
/// otherwise they are optional.</param>
|
||||
/// <returns>The <see cref="SemVersion"/> object.</returns>
|
||||
/// <exception cref="ArgumentNullException">The <paramref name="version"/> is <see langword="null"/>.</exception>
|
||||
/// <exception cref="ArgumentException">The <paramref name="version"/> has an invalid format.</exception>
|
||||
/// <exception cref="InvalidOperationException">The <paramref name="version"/> is missing Minor or Patch versions and <paramref name="strict"/> is <see langword="true"/>.</exception>
|
||||
/// <exception cref="OverflowException">The Major, Minor, or Patch versions are larger than <code>int.MaxValue</code>.</exception>
|
||||
public static SemVersion Parse(string version, bool strict = false)
|
||||
{
|
||||
var match = ParseEx.Match(version);
|
||||
if (!match.Success)
|
||||
throw new ArgumentException($"Invalid version '{version}'.", nameof(version));
|
||||
|
||||
var major = int.Parse(match.Groups["major"].Value, CultureInfo.InvariantCulture);
|
||||
|
||||
var minorMatch = match.Groups["minor"];
|
||||
int minor = 0;
|
||||
if (minorMatch.Success)
|
||||
minor = int.Parse(minorMatch.Value, CultureInfo.InvariantCulture);
|
||||
else if (strict)
|
||||
throw new InvalidOperationException("Invalid version (no minor version given in strict mode)");
|
||||
|
||||
var patchMatch = match.Groups["patch"];
|
||||
int patch = 0;
|
||||
if (patchMatch.Success)
|
||||
patch = int.Parse(patchMatch.Value, CultureInfo.InvariantCulture);
|
||||
else if (strict)
|
||||
throw new InvalidOperationException("Invalid version (no patch version given in strict mode)");
|
||||
|
||||
var extraMatch = match.Groups["extra"];
|
||||
int extra = 0;
|
||||
if (extraMatch.Success)
|
||||
extra = int.Parse(extraMatch.Value, CultureInfo.InvariantCulture);
|
||||
|
||||
var prerelease = match.Groups["pre"].Value;
|
||||
var build = match.Groups["build"].Value;
|
||||
|
||||
|
||||
|
||||
return new SemVersion(major, minor, patch, extra, prerelease, build);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the string representation of a semantic version to its <see cref="SemVersion"/>
|
||||
/// equivalent and returns a value that indicates whether the conversion succeeded.
|
||||
/// </summary>
|
||||
/// <param name="version">The version string.</param>
|
||||
/// <param name="semver">When the method returns, contains a <see cref="SemVersion"/> instance equivalent
|
||||
/// to the version string passed in, if the version string was valid, or <see langword="null"/> if the
|
||||
/// version string was not valid.</param>
|
||||
/// <param name="strict">If set to <see langword="true"/> minor and patch version are required,
|
||||
/// otherwise they are optional.</param>
|
||||
/// <returns><see langword="false"/> when a invalid version string is passed, otherwise <see langword="true"/>.</returns>
|
||||
public static bool TryParse(string version, out SemVersion semver, bool strict = false)
|
||||
{
|
||||
semver = null;
|
||||
if (version is null) return false;
|
||||
|
||||
var match = ParseEx.Match(version);
|
||||
if (!match.Success) return false;
|
||||
|
||||
if (!int.TryParse(match.Groups["major"].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var major))
|
||||
return false;
|
||||
|
||||
var minorMatch = match.Groups["minor"];
|
||||
int minor = 0;
|
||||
if (minorMatch.Success)
|
||||
{
|
||||
if (!int.TryParse(minorMatch.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out minor))
|
||||
return false;
|
||||
}
|
||||
else if (strict) return false;
|
||||
|
||||
var patchMatch = match.Groups["patch"];
|
||||
int patch = 0;
|
||||
if (patchMatch.Success)
|
||||
{
|
||||
if (!int.TryParse(patchMatch.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out patch))
|
||||
return false;
|
||||
}
|
||||
else if (strict) return false;
|
||||
|
||||
var extraMatch = match.Groups["extra"];
|
||||
int extra = 0;
|
||||
if (extraMatch.Success)
|
||||
{
|
||||
if (!int.TryParse(extraMatch.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out extra))
|
||||
return false;
|
||||
}
|
||||
|
||||
var prerelease = match.Groups["pre"].Value;
|
||||
var build = match.Groups["build"].Value;
|
||||
|
||||
semver = new SemVersion(major, minor, patch, extra, prerelease, build);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether two semantic versions are equal.
|
||||
/// </summary>
|
||||
/// <param name="versionA">The first version to compare.</param>
|
||||
/// <param name="versionB">The second version to compare.</param>
|
||||
/// <returns><see langword="true"/> if the two values are equal, otherwise <see langword="false"/>.</returns>
|
||||
public static bool Equals(SemVersion versionA, SemVersion versionB)
|
||||
{
|
||||
if (ReferenceEquals(versionA, versionB)) return true;
|
||||
if (versionA is null || versionB is null) return false;
|
||||
return versionA.Equals(versionB);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares the specified versions.
|
||||
/// </summary>
|
||||
/// <param name="versionA">The first version to compare.</param>
|
||||
/// <param name="versionB">The second version to compare.</param>
|
||||
/// <returns>A signed number indicating the relative values of <paramref name="versionA"/> and <paramref name="versionB"/>.</returns>
|
||||
public static int Compare(SemVersion versionA, SemVersion versionB)
|
||||
{
|
||||
if (ReferenceEquals(versionA, versionB)) return 0;
|
||||
if (versionA is null) return -1;
|
||||
if (versionB is null) return 1;
|
||||
return versionA.CompareTo(versionB);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make a copy of the current instance with changed properties.
|
||||
/// </summary>
|
||||
/// <param name="major">The value to replace the major version or <see langword="null"/> to leave it unchanged.</param>
|
||||
/// <param name="minor">The value to replace the minor version or <see langword="null"/> to leave it unchanged.</param>
|
||||
/// <param name="patch">The value to replace the patch version or <see langword="null"/> to leave it unchanged.</param>
|
||||
/// <param name="prerelease">The value to replace the prerelease version or <see langword="null"/> to leave it unchanged.</param>
|
||||
/// <param name="build">The value to replace the build metadata or <see langword="null"/> to leave it unchanged.</param>
|
||||
/// <returns>The new version object.</returns>
|
||||
/// <remarks>
|
||||
/// The change method is intended to be called using named argument syntax, passing only
|
||||
/// those fields to be changed.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// To change only the patch version:
|
||||
/// <code>version.Change(patch: 4)</code>
|
||||
/// </example>
|
||||
public SemVersion Change(int? major = null, int? minor = null, int? patch = null, int? extra = null,
|
||||
string prerelease = null, string build = null)
|
||||
{
|
||||
return new SemVersion(
|
||||
major ?? Major,
|
||||
minor ?? Minor,
|
||||
patch ?? Patch,
|
||||
extra ?? Extra,
|
||||
prerelease ?? Prerelease,
|
||||
build ?? Build);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the major version.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The major version.
|
||||
/// </value>
|
||||
public int Major { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the minor version.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The minor version.
|
||||
/// </value>
|
||||
public int Minor { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the patch version.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The patch version.
|
||||
/// </value>
|
||||
public int Patch { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Handles the fourth number present in assembly versions
|
||||
/// </summary>
|
||||
public int Extra { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the prerelease version.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The prerelease version. Empty string if this is a release version.
|
||||
/// </value>
|
||||
public string Prerelease { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the build metadata.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The build metadata. Empty string if there is no build metadata.
|
||||
/// </value>
|
||||
public string Build { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="string" /> equivalent of this version.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The <see cref="string" /> equivalent of this version.
|
||||
/// </returns>
|
||||
public override string ToString()
|
||||
{
|
||||
// Assume all separators ("..-+"), at most 2 extra chars
|
||||
var estimatedLength = 4 + Major.Digits() + Minor.Digits() + Patch.Digits()
|
||||
+ Prerelease.Length + Build.Length;
|
||||
var version = new StringBuilder(estimatedLength);
|
||||
version.Append(Major);
|
||||
version.Append('.');
|
||||
version.Append(Minor);
|
||||
version.Append('.');
|
||||
version.Append(Patch);
|
||||
if (Extra != 0)
|
||||
{
|
||||
version.Append('.');
|
||||
version.Append(Extra);
|
||||
}
|
||||
|
||||
if (Prerelease.Length > 0)
|
||||
{
|
||||
version.Append('-');
|
||||
version.Append(Prerelease);
|
||||
}
|
||||
if (Build.Length > 0)
|
||||
{
|
||||
version.Append('+');
|
||||
version.Append(Build);
|
||||
}
|
||||
return version.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares the current instance with another object of the same type and returns an integer that indicates
|
||||
/// whether the current instance precedes, follows, or occurs in the same position in the sort order as the
|
||||
/// other object.
|
||||
/// </summary>
|
||||
/// <param name="obj">An object to compare with this instance.</param>
|
||||
/// <returns>
|
||||
/// A value that indicates the relative order of the objects being compared.
|
||||
/// The return value has these meanings:
|
||||
/// Less than zero: This instance precedes <paramref name="obj" /> in the sort order.
|
||||
/// Zero: This instance occurs in the same position in the sort order as <paramref name="obj" />.
|
||||
/// Greater than zero: This instance follows <paramref name="obj" /> in the sort order.
|
||||
/// </returns>
|
||||
/// <exception cref="InvalidCastException">The <paramref name="obj"/> is not a <see cref="SemVersion"/>.</exception>
|
||||
public int CompareTo(object obj)
|
||||
{
|
||||
return CompareTo((SemVersion)obj);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares the current instance with another object of the same type and returns an integer that indicates
|
||||
/// whether the current instance precedes, follows, or occurs in the same position in the sort order as the
|
||||
/// other object.
|
||||
/// </summary>
|
||||
/// <param name="other">An object to compare with this instance.</param>
|
||||
/// <returns>
|
||||
/// A value that indicates the relative order of the objects being compared.
|
||||
/// The return value has these meanings:
|
||||
/// Less than zero: This instance precedes <paramref name="other" /> in the sort order.
|
||||
/// Zero: This instance occurs in the same position in the sort order as <paramref name="other" />.
|
||||
/// Greater than zero: This instance follows <paramref name="other" /> in the sort order.
|
||||
/// </returns>
|
||||
public int CompareTo(SemVersion other)
|
||||
{
|
||||
var r = CompareByPrecedence(other);
|
||||
if (r != 0) return r;
|
||||
|
||||
#pragma warning disable CA1062 // Validate arguments of public methods
|
||||
// If other is null, CompareByPrecedence() returns 1
|
||||
return CompareComponent(Build, other.Build);
|
||||
#pragma warning restore CA1062 // Validate arguments of public methods
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether two semantic versions have the same precedence. Versions
|
||||
/// that differ only by build metadata have the same precedence.
|
||||
/// </summary>
|
||||
/// <param name="other">The semantic version to compare to.</param>
|
||||
/// <returns><see langword="true"/> if the version precedences are equal.</returns>
|
||||
public bool PrecedenceMatches(SemVersion other)
|
||||
{
|
||||
return CompareByPrecedence(other) == 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two semantic versions by precedence as defined in the SemVer spec. Versions
|
||||
/// that differ only by build metadata have the same precedence.
|
||||
/// </summary>
|
||||
/// <param name="other">The semantic version.</param>
|
||||
/// <returns>
|
||||
/// A value that indicates the relative order of the objects being compared.
|
||||
/// The return value has these meanings:
|
||||
/// Less than zero: This instance precedes <paramref name="other" /> in the sort order.
|
||||
/// Zero: This instance occurs in the same position in the sort order as <paramref name="other" />.
|
||||
/// Greater than zero: This instance follows <paramref name="other" /> in the sort order.
|
||||
/// </returns>
|
||||
public int CompareByPrecedence(SemVersion other)
|
||||
{
|
||||
if (other is null)
|
||||
return 1;
|
||||
|
||||
var r = Major.CompareTo(other.Major);
|
||||
if (r != 0) return r;
|
||||
|
||||
r = Minor.CompareTo(other.Minor);
|
||||
if (r != 0) return r;
|
||||
|
||||
r = Patch.CompareTo(other.Patch);
|
||||
if (r != 0) return r;
|
||||
|
||||
r = Extra.CompareTo(other.Extra);
|
||||
if (r != 0) return r;
|
||||
|
||||
return CompareComponent(Prerelease, other.Prerelease, true);
|
||||
}
|
||||
|
||||
private static int CompareComponent(string a, string b, bool nonemptyIsLower = false)
|
||||
{
|
||||
var aEmpty = string.IsNullOrEmpty(a);
|
||||
var bEmpty = string.IsNullOrEmpty(b);
|
||||
if (aEmpty && bEmpty)
|
||||
return 0;
|
||||
|
||||
if (aEmpty)
|
||||
return nonemptyIsLower ? 1 : -1;
|
||||
if (bEmpty)
|
||||
return nonemptyIsLower ? -1 : 1;
|
||||
|
||||
var aComps = a.Split('.');
|
||||
var bComps = b.Split('.');
|
||||
|
||||
var minLen = Math.Min(aComps.Length, bComps.Length);
|
||||
for (int i = 0; i < minLen; i++)
|
||||
{
|
||||
var ac = aComps[i];
|
||||
var bc = bComps[i];
|
||||
var aIsNum = int.TryParse(ac, out var aNum);
|
||||
var bIsNum = int.TryParse(bc, out var bNum);
|
||||
int r;
|
||||
if (aIsNum && bIsNum)
|
||||
{
|
||||
r = aNum.CompareTo(bNum);
|
||||
if (r != 0) return r;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (aIsNum)
|
||||
return -1;
|
||||
if (bIsNum)
|
||||
return 1;
|
||||
r = string.CompareOrdinal(ac, bc);
|
||||
if (r != 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
return aComps.Length.CompareTo(bComps.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified <see cref="object" /> is equal to this instance.
|
||||
/// </summary>
|
||||
/// <param name="obj">The <see cref="object" /> to compare with this instance.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true"/> if the specified <see cref="object" /> is equal to this instance, otherwise <see langword="false"/>.
|
||||
/// </returns>
|
||||
/// <exception cref="InvalidCastException">The <paramref name="obj"/> is not a <see cref="SemVersion"/>.</exception>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is null)
|
||||
return false;
|
||||
|
||||
if (ReferenceEquals(this, obj))
|
||||
return true;
|
||||
|
||||
var other = (SemVersion)obj;
|
||||
|
||||
return Major == other.Major
|
||||
&& Minor == other.Minor
|
||||
&& Patch == other.Patch
|
||||
&& string.Equals(Prerelease, other.Prerelease, StringComparison.Ordinal)
|
||||
&& string.Equals(Build, other.Build, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a hash code for this instance.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
|
||||
/// </returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
// TODO verify this. Some versions start result = 17. Some use 37 instead of 31
|
||||
int result = Major.GetHashCode();
|
||||
result = result * 31 + Minor.GetHashCode();
|
||||
result = result * 31 + Patch.GetHashCode();
|
||||
result = result * 31 + Prerelease.GetHashCode();
|
||||
result = result * 31 + Build.GetHashCode();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
#if !NETSTANDARD
|
||||
/// <summary>
|
||||
/// Populates a <see cref="SerializationInfo"/> with the data needed to serialize the target object.
|
||||
/// </summary>
|
||||
/// <param name="info">The <see cref="SerializationInfo"/> to populate with data.</param>
|
||||
/// <param name="context">The destination (see <see cref="SerializationInfo"/>) for this serialization.</param>
|
||||
[SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
|
||||
public void GetObjectData(SerializationInfo info, StreamingContext context)
|
||||
{
|
||||
if (info == null) throw new ArgumentNullException(nameof(info));
|
||||
info.AddValue("SemVersion", ToString());
|
||||
}
|
||||
#endif
|
||||
|
||||
#pragma warning disable CA2225 // Operator overloads have named alternates
|
||||
/// <summary>
|
||||
/// Implicit conversion from <see cref="string"/> to <see cref="SemVersion"/>.
|
||||
/// </summary>
|
||||
/// <param name="version">The semantic version.</param>
|
||||
/// <returns>The <see cref="SemVersion"/> object.</returns>
|
||||
/// <exception cref="ArgumentNullException">The <paramref name="version"/> is <see langword="null"/>.</exception>
|
||||
/// <exception cref="ArgumentException">The version number has an invalid format.</exception>
|
||||
/// <exception cref="OverflowException">The Major, Minor, or Patch versions are larger than <code>int.MaxValue</code>.</exception>
|
||||
public static implicit operator SemVersion(string version)
|
||||
#pragma warning restore CA2225 // Operator overloads have named alternates
|
||||
{
|
||||
return Parse(version);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two semantic versions for equality.
|
||||
/// </summary>
|
||||
/// <param name="left">The left value.</param>
|
||||
/// <param name="right">The right value.</param>
|
||||
/// <returns>If left is equal to right <see langword="true"/>, otherwise <see langword="false"/>.</returns>
|
||||
public static bool operator ==(SemVersion left, SemVersion right)
|
||||
{
|
||||
return Equals(left, right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two semantic versions for inequality.
|
||||
/// </summary>
|
||||
/// <param name="left">The left value.</param>
|
||||
/// <param name="right">The right value.</param>
|
||||
/// <returns>If left is not equal to right <see langword="true"/>, otherwise <see langword="false"/>.</returns>
|
||||
public static bool operator !=(SemVersion left, SemVersion right)
|
||||
{
|
||||
return !Equals(left, right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two semantic versions.
|
||||
/// </summary>
|
||||
/// <param name="left">The left value.</param>
|
||||
/// <param name="right">The right value.</param>
|
||||
/// <returns>If left is greater than right <see langword="true"/>, otherwise <see langword="false"/>.</returns>
|
||||
public static bool operator >(SemVersion left, SemVersion right)
|
||||
{
|
||||
return Compare(left, right) > 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two semantic versions.
|
||||
/// </summary>
|
||||
/// <param name="left">The left value.</param>
|
||||
/// <param name="right">The right value.</param>
|
||||
/// <returns>If left is greater than or equal to right <see langword="true"/>, otherwise <see langword="false"/>.</returns>
|
||||
public static bool operator >=(SemVersion left, SemVersion right)
|
||||
{
|
||||
return Equals(left, right) || Compare(left, right) > 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two semantic versions.
|
||||
/// </summary>
|
||||
/// <param name="left">The left value.</param>
|
||||
/// <param name="right">The right value.</param>
|
||||
/// <returns>If left is less than right <see langword="true"/>, otherwise <see langword="false"/>.</returns>
|
||||
public static bool operator <(SemVersion left, SemVersion right)
|
||||
{
|
||||
return Compare(left, right) < 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two semantic versions.
|
||||
/// </summary>
|
||||
/// <param name="left">The left value.</param>
|
||||
/// <param name="right">The right value.</param>
|
||||
/// <returns>If left is less than or equal to right <see langword="true"/>, otherwise <see langword="false"/>.</returns>
|
||||
public static bool operator <=(SemVersion left, SemVersion right)
|
||||
{
|
||||
return Equals(left, right) || Compare(left, right) < 0;
|
||||
}
|
||||
}
|
||||
}
|
190
VRCMelonAssistant/Localisation/en-DEBUG.xaml
Normal file
190
VRCMelonAssistant/Localisation/en-DEBUG.xaml
Normal file
|
@ -0,0 +1,190 @@
|
|||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:VRCMelonAssistant"
|
||||
xmlns:sys="clr-namespace:System;assembly=mscorlib">
|
||||
<sys:String x:Key="ResourceDictionaryName">i18n:en-DEBUG</sys:String>
|
||||
|
||||
<!-- App -->
|
||||
<sys:String x:Key="App:InstallDirDialog:Title">App:InstallDirDialog:Title</sys:String>
|
||||
<sys:String x:Key="App:InstallDirDialog:OkCancel">App:InstallDirDialog:OkCancel</sys:String>
|
||||
<sys:String x:Key="App:InvalidArgument">{0} App:InvalidArgument</sys:String>
|
||||
<sys:String x:Key="App:UnrecognizedArgument">App:UnrecognizedArgument</sys:String>
|
||||
<sys:String x:Key="App:UnhandledException">App:UnhandledException</sys:String>
|
||||
<sys:String x:Key="App:Exception">App:Exception</sys:String>
|
||||
|
||||
<!-- Main Window -->
|
||||
<sys:String x:Key="MainWindow:WindowTitle">MainWindow:WindowTitle</sys:String>
|
||||
<sys:String x:Key="MainWindow:IntroButton">MainWindow:IntroButton</sys:String>
|
||||
<sys:String x:Key="MainWindow:ModsButton">MainWindow:ModsButton</sys:String>
|
||||
<sys:String x:Key="MainWindow:AboutButton">MainWindow:AboutButton</sys:String>
|
||||
<sys:String x:Key="MainWindow:OptionsButton">MainWindow:OptionsButton</sys:String>
|
||||
<sys:String x:Key="MainWindow:GameVersionLabel">MainWindow:GameVersionLabel</sys:String>
|
||||
<sys:String x:Key="MainWindow:VersionLabel">MainWindow:VersionLabel</sys:String>
|
||||
<sys:String x:Key="MainWindow:ModInfoButton">MainWindow:ModInfoButton</sys:String>
|
||||
<sys:String x:Key="MainWindow:InstallButtonTop">MainWindow:InstallButtonTop</sys:String>
|
||||
<sys:String x:Key="MainWindow:InstallButtonBottom">MainWindow:InstallButtonBottom</sys:String>
|
||||
<sys:String x:Key="MainWindow:GameVersionLoadFailed">MainWindow:GameVersionLoadFailed</sys:String>
|
||||
<sys:String x:Key="MainWindow:GameUpdateDialog:Title">MainWindow:GameUpdateDialog:Title</sys:String>
|
||||
<sys:String x:Key="MainWindow:GameUpdateDialog:Line1">MainWindow:GameUpdateDialog:Line1</sys:String>
|
||||
<sys:String x:Key="MainWindow:GameUpdateDialog:Line2">MainWindow:GameUpdateDialog:Line2</sys:String>
|
||||
<sys:String x:Key="MainWindow:NoModSelected">MainWindow:NoModSelected</sys:String>
|
||||
<sys:String x:Key="MainWindow:NoModInfoPage">{0} MainWindow:NoModInfoPage</sys:String>
|
||||
|
||||
<!-- Intro Page -->
|
||||
<sys:String x:Key="Intro:Title">Intro:Title</sys:String>
|
||||
<sys:String x:Key="Intro:PageTitle">Intro:PageTitle</sys:String>
|
||||
<sys:String x:Key="Intro:Terms:Header">Intro:Terms:Header</sys:String>
|
||||
<Span x:Key="Intro:Terms:Line1">Intro:Terms:Line1</Span>
|
||||
<Span x:Key="Intro:Terms:Line2">Intro:Terms:Line2</Span>
|
||||
<Span x:Key="Intro:Terms:Term1">Intro:Terms:Term1</Span>
|
||||
<Span x:Key="Intro:Terms:Term2">Intro:Terms:Term2</Span>
|
||||
<Span x:Key="Intro:Terms:Term3">Intro:Terms:Term3</Span>
|
||||
<Span x:Key="Intro:ReviewsBeatGamesFault">Intro:ReviewsBeatGamesFault</Span>
|
||||
<Span x:Key="Intro:ReviewsRustySpoon">Intro:ReviewsRustySpoon</Span>
|
||||
<Span x:Key="Intro:WikiGuide">Intro:WikiGuide</Span>
|
||||
<sys:String x:Key="Intro:AgreeButton">Intro:AgreeButton</sys:String>
|
||||
<sys:String x:Key="Intro:DisagreeButton">Intro:DisagreeButton</sys:String>
|
||||
<sys:String x:Key="Intro:ClosingApp">Intro:ClosingApp</sys:String>
|
||||
<sys:String x:Key="Intro:VersionDownloadFailed">Intro:VersionDownloadFailed</sys:String>
|
||||
<sys:String x:Key="Intro:ModsTabDisabled">Intro:ModsTabDisabled</sys:String>
|
||||
<sys:String x:Key="Intro:ModsTabEnabled">Intro:ModsTabEnabled</sys:String>
|
||||
|
||||
<!-- Mods Page -->
|
||||
<sys:String x:Key="Mods:Title">Mods:Title</sys:String>
|
||||
<sys:String x:Key="Mods:Header:Name">Mods:Header:Name</sys:String>
|
||||
<sys:String x:Key="Mods:Header:Installed">Mods:Header:Installed</sys:String>
|
||||
<sys:String x:Key="Mods:Header:Latest">Mods:Header:Latest</sys:String>
|
||||
<sys:String x:Key="Mods:Header:Description">Mods:Header:Description</sys:String>
|
||||
<sys:String x:Key="Mods:Header:Uninstall">Mods:Header:Uninstall</sys:String>
|
||||
<sys:String x:Key="Mods:UninstallButton">Mods:UninstallButton</sys:String>
|
||||
<sys:String x:Key="Mods:LoadFailed">Mods:LoadFailed</sys:String>
|
||||
<sys:String x:Key="Mods:CheckingInstalledMods">Mods:CheckingInstalledMods</sys:String>
|
||||
<sys:String x:Key="Mods:LoadingMods">Mods:LoadingMods</sys:String>
|
||||
<sys:String x:Key="Mods:FinishedLoadingMods">Mods:FinishedLoadingMods</sys:String>
|
||||
<sys:String x:Key="Mods:NoMods">Mods:NoMods</sys:String>
|
||||
<sys:String x:Key="Mods:InstallingMod">{0} Mods:InstallingMod</sys:String>
|
||||
<sys:String x:Key="Mods:InstalledMod">{0} Mods:InstalledMod</sys:String>
|
||||
<sys:String x:Key="Mods:FinishedInstallingMods">Mods:FinishedInstallingMods</sys:String>
|
||||
<sys:String x:Key="Mods:ModDownloadLinkMissing">Mods:ModDownloadLinkMissing</sys:String>
|
||||
<sys:String x:Key="Mods:UninstallBox:Title">{0} Mods:UninstallBox:Title</sys:String>
|
||||
<sys:String x:Key="Mods:UninstallBox:Body1">{0} Mods:UninstallBox:Body1</sys:String>
|
||||
<sys:String x:Key="Mods:UninstallBox:Body2">Mods:UninstallBox:Body2</sys:String>
|
||||
<sys:String x:Key="Mods:FailedExtract">{0} {1} {2} {3} Mods:FailedExtract</sys:String>
|
||||
<sys:String x:Key="Mods:FailedExtractMaxReached">{0} {1} Mods:FailedExtractMaxReached</sys:String>
|
||||
<sys:String x:Key="Mods:SearchLabel">Mods:SearchLabel</sys:String>
|
||||
<sys:String x:Key="Mods:UninstallBSIPANotFound:Title">Mods:UninstallBSIPANotFound:Title</sys:String>
|
||||
<sys:String x:Key="Mods:UninstallBSIPANotFound:Body">Mods:UninstallBSIPANotFound:Body</sys:String>
|
||||
|
||||
<!-- About Page -->
|
||||
<sys:String x:Key="About:Title">About:Title</sys:String>
|
||||
<sys:String x:Key="About:PageTitle">About:PageTitle</sys:String>
|
||||
<sys:String x:Key="About:List:Header">About:List:Header</sys:String>
|
||||
<sys:String x:Key="About:List:Item1">About:List:Item1</sys:String>
|
||||
<sys:String x:Key="About:List:Item2">About:List:Item2</sys:String>
|
||||
<sys:String x:Key="About:List:Item3">About:List:Item3</sys:String>
|
||||
<sys:String x:Key="About:List:Item4">About:List:Item4</sys:String>
|
||||
<Span x:Key="About:SupportAssistant">About:SupportAssistant</Span>
|
||||
<sys:String x:Key="About:SpecialThanks">About:SpecialThanks</sys:String>
|
||||
<sys:String x:Key="About:Donate">About:Donate</sys:String>
|
||||
<sys:String x:Key="About:HeadpatsButton">About:HeadpatsButton</sys:String>
|
||||
<sys:String x:Key="About:HugsButton">About:HugsButton</sys:String>
|
||||
|
||||
<!-- Options Page -->
|
||||
<sys:String x:Key="Options:Title">Options:Title</sys:String>
|
||||
<sys:String x:Key="Options:PageTitle">Options:PageTitle</sys:String>
|
||||
<sys:String x:Key="Options:InstallFolder">Options:InstalFolder</sys:String>
|
||||
<sys:String x:Key="Options:SelectFolderButton">Options:SelectFolderButton</sys:String>
|
||||
<sys:String x:Key="Options:OpenFolderButton">Options:OpenFolderButton</sys:String>
|
||||
<sys:String x:Key="Options:SaveSelectedMods">Options:SaveSelectedMods</sys:String>
|
||||
<sys:String x:Key="Options:CheckInstalledMods">Options:CheckInstalledMods</sys:String>
|
||||
<sys:String x:Key="Options:SelectInstalledMods">Options:SelectInstalledMods</sys:String>
|
||||
<sys:String x:Key="Options:ReinstallInstalledMods">Options:Reinstall Installed Mods</sys:String>
|
||||
<sys:String x:Key="Options:EnableOneClickInstalls">Options:EnableOneClickInstalls</sys:String>
|
||||
<sys:String x:Key="Options:CloseWindow">Options:CloseWindow</sys:String>
|
||||
<sys:String x:Key="Options:GameType">Options:GameType</sys:String>
|
||||
<sys:String x:Key="Options:GameType:Steam">Options:GameType:Steam</sys:String>
|
||||
<sys:String x:Key="Options:GameType:Oculus">Options:GameType:Oculus</sys:String>
|
||||
<sys:String x:Key="Options:Tools">Options:Tools</sys:String>
|
||||
<sys:String x:Key="Options:InstallPlaylist">Options:InstallPlaylist</sys:String>
|
||||
<sys:String x:Key="Options:InstallingPlaylist">{0} Options:InstallingPlaylist</sys:String>
|
||||
<sys:String x:Key="Options:FailedPlaylistSong">{0} Options:FailedPlaylistSong</sys:String>
|
||||
<sys:String x:Key="Options:FinishedPlaylist">{0} {1} Options:FinishedPlaylist</sys:String>
|
||||
<sys:String x:Key="Options:ShowOCIWindow">Options:ShowOCIWindow</sys:String>
|
||||
<sys:String x:Key="Options:OCIWindowYes">Options:OCIWindowYes</sys:String>
|
||||
<sys:String x:Key="Options:OCIWindowClose">Options:OCIWindowClose</sys:String>
|
||||
<sys:String x:Key="Options:OCIWindowNo">Options:OCIWindowNo</sys:String>
|
||||
<sys:String x:Key="Options:Diagnostics">Options:Diagnostics</sys:String>
|
||||
<sys:String x:Key="Options:OpenLogsButton">Options:OpenLogsButton</sys:String>
|
||||
<sys:String x:Key="Options:OpenAppDataButton">Options:OpenAppDataButton</sys:String>
|
||||
<sys:String x:Key="Options:UninstallBSIPAButton">Options:UninstallBSIPAButton</sys:String>
|
||||
<sys:String x:Key="Options:RemoveAllModsButton">Options:RemoveAllModsButton</sys:String>
|
||||
<sys:String x:Key="Options:ApplicationTheme">Options:ApplicationTheme</sys:String>
|
||||
<sys:String x:Key="Options:ExportTemplateButton">Options:ExportTemplateButton</sys:String>
|
||||
<sys:String x:Key="Options:UploadingLog">Options:UploadingLog</sys:String>
|
||||
<sys:String x:Key="Options:LogUrlCopied">Options:LogUrlCopied</sys:String>
|
||||
<sys:String x:Key="Options:LogUploadFailed">Options:LogUploadFailed</sys:String>
|
||||
<sys:String x:Key="Options:LogUploadFailed:Title">Options:LogUploadFailed:Title</sys:String>
|
||||
<sys:String x:Key="Options:LogUploadFailed:Body">Options:LogUploadFailed:Body</sys:String>
|
||||
<sys:String x:Key="Options:GettingModList">Options:GettingModList</sys:String>
|
||||
<sys:String x:Key="Options:FindingBSIPAVersion">Options:FindingBSIPAVersion</sys:String>
|
||||
<sys:String x:Key="Options:BSIPAUninstalled">Options:BSIPAUninstalled</sys:String>
|
||||
<sys:String x:Key="Options:YeetModsBox:Title">Options:YeetModsBox:Title</sys:String>
|
||||
<sys:String x:Key="Options:YeetModsBox:RemoveAllMods">Options:YeetModsBox:RemoveAllMods</sys:String>
|
||||
<sys:String x:Key="Options:YeetModsBox:CannotBeUndone">Options:YeetModsBox:CannotBeUndone</sys:String>
|
||||
<sys:String x:Key="Options:AllModsUninstalled">Options:AllModsUninstalled</sys:String>
|
||||
<sys:String x:Key="Options:CurrentThemeRemoved">Options:CurrentThemeRemoved</sys:String>
|
||||
<sys:String x:Key="Options:ThemeFolderNotFound">Options:ThemeFolderNotFound</sys:String>
|
||||
<sys:String x:Key="Options:AppDataNotFound">Options:AppDataNotFound</sys:String>
|
||||
|
||||
<!-- Loading Page -->
|
||||
<sys:String x:Key="Loading:Loading">Loading:Loading</sys:String>
|
||||
|
||||
<!-- Invalid Page -->
|
||||
<sys:String x:Key="Invalid:Title">Invalid:Title</sys:String>
|
||||
<sys:String x:Key="Invalid:PageTitle">Invalid:PageTitle</sys:String>
|
||||
<sys:String x:Key="Invalid:PageSubtitle">Invalid:PageSubtitle</sys:String>
|
||||
<sys:String x:Key="Invalid:List:Header">Invalid:List:Header</sys:String>
|
||||
<Span x:Key="Invalid:List:Line1">Invalid:List:Line1</Span>
|
||||
<Span x:Key="Invalid:List:Line2">Invalid:List:Line2</Span>
|
||||
<Span x:Key="Invalid:List:Line3">Invalid:List:Line3</Span>
|
||||
<sys:String x:Key="Invalid:BoughtGame1">Invalid:BoughtGame1</sys:String>
|
||||
<sys:String x:Key="Invalid:SelectFolderButton">Invalid:SelectFolderButton</sys:String>
|
||||
<sys:String x:Key="Invalid:BoughtGame2">Invalid:BoughtGame2</sys:String>
|
||||
|
||||
<!-- OneClick Class -->
|
||||
<sys:String x:Key="OneClick:MapDownloadFailed">OneClick:MapDownloadFailed</sys:String>
|
||||
<sys:String x:Key="OneClick:SongDownloadFailed">OneClick:SongDownloadFailed</sys:String>
|
||||
<sys:String x:Key="OneClick:SongDownload:Failed">OneClick:SongDownload:Failed</sys:String>
|
||||
<sys:String x:Key="OneClick:SongDownload:NetworkIssues">OneClick:SongDownload:NetworkIssues</sys:String>
|
||||
<sys:String x:Key="OneClick:SongDownload:FailedTitle">OneClick:SongDownload:FailedTitle</sys:String>
|
||||
<sys:String x:Key="OneClick:InstallDirNotFound">OneClick:InstallDirNotFound</sys:String>
|
||||
<sys:String x:Key="OneClick:InstalledAsset">{0} OneClick:InstalledAsset</sys:String>
|
||||
<sys:String x:Key="OneClick:AssetInstallFailed">OneClick:AssetInstallFailed</sys:String>
|
||||
<sys:String x:Key="OneClick:ProtocolHandler:Registered">{0} OneClick:ProtocolHandler:Registered</sys:String>
|
||||
<sys:String x:Key="OneClick:ProtocolHandler:Unregistered">{0} OneClick:ProtocolHandler:Unregistered</sys:String>
|
||||
<sys:String x:Key="OneClick:Installing">{0} OneClick:Installing</sys:String>
|
||||
<sys:String x:Key="OneClick:RatelimitSkip">{0} OneClick:RatelimitSkip</sys:String>
|
||||
<sys:String x:Key="OneClick:RatelimitHit">{0} OneClick:RatelimitHit</sys:String>
|
||||
<sys:String x:Key="OneClick:Failed">{0} OneClick:Failed</sys:String>
|
||||
<sys:String x:Key="OneClick:Done">OneClick:Done</sys:String>
|
||||
|
||||
<!-- Themes Class -->
|
||||
<sys:String x:Key="Themes:ThemeNotFound">Themes:ThemeNotFound</sys:String>
|
||||
<sys:String x:Key="Themes:ThemeSet">Themes:ThemeSet {0}</sys:String>
|
||||
<sys:String x:Key="Themes:ThemeMissing">{0} Themes:ThemeMissing</sys:String>
|
||||
<sys:String x:Key="Themes:SavedTemplateTheme">Themes:SavedTemplateTheme {0}</sys:String>
|
||||
<sys:String x:Key="Themes:TemplateThemeExists">Themes:TemplateThemeExists</sys:String>
|
||||
<sys:String x:Key="Themes:FailedToLoadXaml">Themes:FailedToLoadXaml {0} {1}</sys:String>
|
||||
|
||||
<!-- Updater Class -->
|
||||
<sys:String x:Key="Updater:CheckFailed">Updater:CheckFailed</sys:String>
|
||||
<sys:String x:Key="Updater:DownloadFailed">Updater:DownloadFailed</sys:String>
|
||||
|
||||
<!-- Utils Class -->
|
||||
<sys:String x:Key="Utils:NotificationTitle">Utils:NotificationTitle</sys:String>
|
||||
<sys:String x:Key="Utils:NoInstallFolder">Utils:NoInstallFolder</sys:String>
|
||||
<sys:String x:Key="Utils:RunAsAdmin">Utils:RunAsAdmin</sys:String>
|
||||
<sys:String x:Key="Utils:InstallDir:DialogTitle">Utils:InstallDir:DialogTitle</sys:String>
|
||||
<sys:String x:Key="Utils:CannotOpenFolder">Utils:CannotOpenFolder {0}</sys:String>
|
||||
</ResourceDictionary>
|
205
VRCMelonAssistant/Localisation/en.xaml
Normal file
205
VRCMelonAssistant/Localisation/en.xaml
Normal file
|
@ -0,0 +1,205 @@
|
|||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:VRCMelonAssistant"
|
||||
xmlns:sys="clr-namespace:System;assembly=mscorlib">
|
||||
<sys:String x:Key="ResourceDictionaryName">i18n:en-US</sys:String>
|
||||
|
||||
<!-- App -->
|
||||
<sys:String x:Key="App:InstallDirDialog:Title">Couldn't find your VRChat install folder!</sys:String>
|
||||
<sys:String x:Key="App:InstallDirDialog:OkCancel">Press OK to try again, or Cancel to close application.</sys:String>
|
||||
<sys:String x:Key="App:InvalidArgument">Invalid argument! '{0}' requires an option.</sys:String>
|
||||
<sys:String x:Key="App:UnrecognizedArgument">Unrecognized argument. Closing VRChat Melon Assistant.</sys:String>
|
||||
<sys:String x:Key="App:UnhandledException">An unhandled exception just occurred</sys:String>
|
||||
<sys:String x:Key="App:Exception">Exception</sys:String>
|
||||
|
||||
<!-- Main Window -->
|
||||
<sys:String x:Key="MainWindow:WindowTitle">VRChat Melon Assistant</sys:String>
|
||||
<sys:String x:Key="MainWindow:IntroButton">Intro</sys:String>
|
||||
<sys:String x:Key="MainWindow:ModsButton">Mods</sys:String>
|
||||
<sys:String x:Key="MainWindow:AboutButton">About</sys:String>
|
||||
<sys:String x:Key="MainWindow:OptionsButton">Options</sys:String>
|
||||
<sys:String x:Key="MainWindow:GameVersionLabel">Game Version</sys:String>
|
||||
<sys:String x:Key="MainWindow:VersionLabel">Version</sys:String>
|
||||
<sys:String x:Key="MainWindow:ModInfoButton">Mod Info</sys:String>
|
||||
<sys:String x:Key="MainWindow:InstallButtonTop">Install</sys:String>
|
||||
<sys:String x:Key="MainWindow:InstallButtonBottom">or Update</sys:String>
|
||||
<sys:String x:Key="MainWindow:NoModSelected">No mod selected!</sys:String>
|
||||
<sys:String x:Key="MainWindow:NoModInfoPage">{0} does not have an info page.</sys:String>
|
||||
|
||||
<!-- Intro Page -->
|
||||
<sys:String x:Key="Intro:Title">Intro</sys:String>
|
||||
<sys:String x:Key="Intro:PageTitle">Welcome to VRChat Melon Assistant</sys:String>
|
||||
<sys:String x:Key="Intro:Terms:Header">Please read this page entirely and carefully</sys:String>
|
||||
<Span x:Key="Intro:Terms:Line1">
|
||||
By using this program attest to have read and agree to the following terms:
|
||||
</Span>
|
||||
<Span x:Key="Intro:Terms:Line2">
|
||||
VRChat
|
||||
<Bold>explicitly forbids</Bold> modding the game client in their Terms of Service. This means:
|
||||
</Span>
|
||||
<Span x:Key="Intro:Terms:Term0">
|
||||
Modding the client
|
||||
<Bold>can</Bold> lead to bans. Mods available via this installer are manually checked to minimize the chance of that happening, but <Bold>the risk is always there</Bold>.
|
||||
</Span>
|
||||
<Span x:Key="Intro:Terms:Term1">
|
||||
Mods
|
||||
<Bold>will break</Bold> every update. This is normal, and
|
||||
<Bold>not</Bold> VRChat's fault.
|
||||
</Span>
|
||||
<Span x:Key="Intro:Terms:Term2">
|
||||
Mods
|
||||
<Bold>will</Bold> cause bugs and performance issues. This is
|
||||
<Bold>not</Bold> VRChat's fault.
|
||||
</Span>
|
||||
<Span x:Key="Intro:Terms:Term3">
|
||||
Mods are made for
|
||||
<Bold>free</Bold> by people in their
|
||||
<Bold>free time.</Bold> Please be patient and understanding.
|
||||
</Span>
|
||||
<Span x:Key="Intro:ReviewsBeatGamesFault">
|
||||
<Bold>DO NOT</Bold> complain to VRChat Team because mods broke. This is
|
||||
<Bold>not</Bold> VRChat's fault.
|
||||
<LineBreak/> They are not trying to kill mods. Most likely.
|
||||
</Span>
|
||||
<Span x:Key="Intro:ReportIssuesNoMods">
|
||||
If you want to report an issue with the game to VRChat Team, <Bold>make sure the issue is not caused by mods.</Bold>
|
||||
<LineBreak />To do so, remove all mods and/or MelonLoader and check if the issue still happens.
|
||||
</Span>
|
||||
<Span x:Key="Intro:ReviewsRustySpoon">
|
||||
If I keep seeing people complain to the devs
|
||||
<Italic>because</Italic> mods broke,
|
||||
<LineBreak/>
|
||||
<Bold>I will personally kill mods with a rusty spoon</Bold>
|
||||
</Span>
|
||||
<Span x:Key="Intro:WikiGuide">
|
||||
Feel free to join the
|
||||
<Hyperlink local:HyperlinkExtensions.IsExternal="True" NavigateUri="https://discord.gg/rCqKSvR">
|
||||
VRChat Modding Group discord
|
||||
</Hyperlink> for more info, help, and support.
|
||||
</Span>
|
||||
<sys:String x:Key="Intro:AgreeButton">I Agree</sys:String>
|
||||
<sys:String x:Key="Intro:DisagreeButton">Disagree</sys:String>
|
||||
<sys:String x:Key="Intro:ClosingApp">Closing Application: You did not agree to terms and conditions.</sys:String>
|
||||
<sys:String x:Key="Intro:VersionDownloadFailed">Could not download versions list</sys:String>
|
||||
<sys:String x:Key="Intro:ModsTabDisabled">Mods tab disabled. Please restart to try again.</sys:String>
|
||||
<sys:String x:Key="Intro:ModsTabEnabled">You can now use the Mods tab!</sys:String>
|
||||
|
||||
<!-- Mods Page -->
|
||||
<sys:String x:Key="Mods:Title">Mods</sys:String>
|
||||
<sys:String x:Key="Mods:Header:Name">Name</sys:String>
|
||||
<sys:String x:Key="Mods:Header:Author">Author</sys:String>
|
||||
<sys:String x:Key="Mods:Header:Installed">Installed</sys:String>
|
||||
<sys:String x:Key="Mods:Header:Latest">Latest</sys:String>
|
||||
<sys:String x:Key="Mods:Header:Description">Description</sys:String>
|
||||
<sys:String x:Key="Mods:Header:Uninstall">Uninstall</sys:String>
|
||||
<sys:String x:Key="Mods:UninstallButton">Uninstall</sys:String>
|
||||
<sys:String x:Key="Mods:LoadFailed">Could not load mods list</sys:String>
|
||||
<sys:String x:Key="Mods:CheckingInstalledMods">Checking installed mods</sys:String>
|
||||
<sys:String x:Key="Mods:LoadingMods">Loading Mods</sys:String>
|
||||
<sys:String x:Key="Mods:FinishedLoadingMods">Finished loading mods</sys:String>
|
||||
<sys:String x:Key="Mods:NoMods">No mods available</sys:String>
|
||||
<sys:String x:Key="Mods:InstallingMod">Installing {0}</sys:String>
|
||||
<sys:String x:Key="Mods:InstalledMod">Installed {0}</sys:String>
|
||||
<sys:String x:Key="Mods:UnInstallingMelonLoader">Uninstalling MelonLoader</sys:String>
|
||||
<sys:String x:Key="Mods:DownloadingMelonLoader">Downloading MelonLoader</sys:String>
|
||||
<sys:String x:Key="Mods:UnpackingMelonLoader">Unpacking MelonLoader</sys:String>
|
||||
<sys:String x:Key="Mods:FinishedInstallingMods">Finished installing mods</sys:String>
|
||||
<sys:String x:Key="Mods:ModDownloadLinkMissing">Could not find download link for {0}</sys:String>
|
||||
<sys:String x:Key="Mods:UninstallBox:Title">Uninstall {0}?</sys:String>
|
||||
<sys:String x:Key="Mods:UninstallBox:Body1">Are you sure you want to remove {0}?</sys:String>
|
||||
<sys:String x:Key="Mods:UninstallBox:Body2">This could break your other mods</sys:String>
|
||||
<sys:String x:Key="Mods:UninstallSingleFailed">Error uninstalling mod. Make sure VRChat is not running.</sys:String>
|
||||
<sys:String x:Key="Mods:UninstallMLFailed">Error uninstalling MelonLoader. Make sure VRChat is not running.</sys:String>
|
||||
<sys:String x:Key="Mods:InstallMLFailed">Error installing MelonLoader. Try using the standalone installer.</sys:String>
|
||||
<sys:String x:Key="Mods:FailedExtract">Failed to extract {0}, trying again in {1} seconds. ({2}/{3})</sys:String>
|
||||
<sys:String x:Key="Mods:FailedExtractMaxReached">Failed to extract {0} after max attempts ({1}), skipping. This mod might not work properly so proceed at your own risk</sys:String>
|
||||
<sys:String x:Key="Mods:SearchLabel">Search...</sys:String>
|
||||
|
||||
<!-- About Page -->
|
||||
<sys:String x:Key="About:Title">About</sys:String>
|
||||
<sys:String x:Key="About:PageTitle">About VRChat Melon Assistant</sys:String>
|
||||
<sys:String x:Key="About:List:Header">VRChat Melon Assistant is a fork of Assistant's Mod Assistant for Beat Saber.</sys:String>
|
||||
<Span x:Key="About:SupportAssistant">
|
||||
Visit
|
||||
<Hyperlink local:HyperlinkExtensions.IsExternal="True" NavigateUri="https://github.com/Assistant/ModAssistant">
|
||||
Mod Assistant GitHub repo
|
||||
</Hyperlink>
|
||||
to appreciate their hard work and/or throw some money at them!
|
||||
</Span>
|
||||
<sys:String x:Key="About:SpecialThanks">Special Thanks ♥</sys:String>
|
||||
<sys:String x:Key="About:Donate">Donate</sys:String>
|
||||
<sys:String x:Key="About:HeadpatsButton">Headpats</sys:String>
|
||||
<sys:String x:Key="About:HugsButton">Hugs</sys:String>
|
||||
|
||||
<!-- Options Page -->
|
||||
<sys:String x:Key="Options:Title">Options</sys:String>
|
||||
<sys:String x:Key="Options:PageTitle">Settings</sys:String>
|
||||
<sys:String x:Key="Options:InstallFolder">Install Folder</sys:String>
|
||||
<sys:String x:Key="Options:SelectFolderButton">Select Folder</sys:String>
|
||||
<sys:String x:Key="Options:OpenFolderButton">Open Folder</sys:String>
|
||||
<sys:String x:Key="Options:SaveSelectedMods">Save Selected Mods</sys:String>
|
||||
<sys:String x:Key="Options:CheckInstalledMods">Detect Installed Mods</sys:String>
|
||||
<sys:String x:Key="Options:SelectInstalledMods">Select Installed Mods</sys:String>
|
||||
<sys:String x:Key="Options:ReinstallInstalledMods">Reinstall Installed Mods</sys:String>
|
||||
<sys:String x:Key="Options:CloseWindow">Close window when finished</sys:String>
|
||||
<sys:String x:Key="Options:GameType">Game Type</sys:String>
|
||||
<sys:String x:Key="Options:GameType:Steam">Steam</sys:String>
|
||||
<sys:String x:Key="Options:GameType:Oculus">Oculus</sys:String>
|
||||
<sys:String x:Key="Options:Tools">Tools</sys:String>
|
||||
<sys:String x:Key="Options:InstallPlaylist">Install Playlist</sys:String>
|
||||
<sys:String x:Key="Options:InstallingPlaylist">Installing Playlist: {0}</sys:String>
|
||||
<sys:String x:Key="Options:FailedPlaylistSong">Failed song: {0}</sys:String>
|
||||
<sys:String x:Key="Options:FinishedPlaylist">[{0} fails] Finished Installing Playlist: {1}</sys:String>
|
||||
<sys:String x:Key="Options:Diagnostics">Diagnostics</sys:String>
|
||||
<sys:String x:Key="Options:OpenLogsButton">Open Logs</sys:String>
|
||||
<sys:String x:Key="Options:OpenAppDataButton">Open AppData</sys:String>
|
||||
<sys:String x:Key="Options:RemoveAllModsButton">Remove All Mods</sys:String>
|
||||
<sys:String x:Key="Options:RemoveMLButton">Remove MelonLoader</sys:String>
|
||||
<sys:String x:Key="Options:InstallMLButton">Install MelonLoader</sys:String>
|
||||
<sys:String x:Key="Options:ApplicationTheme">Application Theme</sys:String>
|
||||
<sys:String x:Key="Options:ExportTemplateButton">Export Template</sys:String>
|
||||
<sys:String x:Key="Options:GettingModList">Getting Mod List</sys:String>
|
||||
<sys:String x:Key="Options:YeetModsBox:Title">Uninstall All Mods?</sys:String>
|
||||
<sys:String x:Key="Options:YeetModsBox:RemoveAllMods">Are you sure you want to remove ALL mods?</sys:String>
|
||||
<sys:String x:Key="Options:YeetModsBox:CannotBeUndone">This cannot be undone.</sys:String>
|
||||
<sys:String x:Key="Options:AllModsUninstalled">All Mods Uninstalled</sys:String>
|
||||
<sys:String x:Key="Options:YeetMLBox:Title">Uninstall MelonLoader?</sys:String>
|
||||
<sys:String x:Key="Options:YeetMLBox:RemoveAllMods">Are you sure you want to uninstall MelonLoader?</sys:String>
|
||||
<sys:String x:Key="Options:YeetMLBox:CannotBeUndone">All your mods and mod data will stay intact.</sys:String>
|
||||
<sys:String x:Key="Options:MLUninstalled">MelonLoader Uninstalled</sys:String>
|
||||
<sys:String x:Key="Options:MLInstalled">MelonLoader Installed</sys:String>
|
||||
<sys:String x:Key="Options:CurrentThemeRemoved">Current theme has been removed, reverting to default...</sys:String>
|
||||
<sys:String x:Key="Options:ThemeFolderNotFound">Themes folder not found! Try exporting the template...</sys:String>
|
||||
<sys:String x:Key="Options:AppDataNotFound">AppData folder not found! Try launching your game.</sys:String>
|
||||
|
||||
<!-- Loading Page -->
|
||||
<sys:String x:Key="Loading:Loading">Loading Mods</sys:String>
|
||||
|
||||
<!-- Themes Class -->
|
||||
<sys:String x:Key="Themes:ThemeNotFound">Theme not found, reverting to default theme...</sys:String>
|
||||
<sys:String x:Key="Themes:ThemeSet">Theme set to {0}.</sys:String>
|
||||
<sys:String x:Key="Themes:ThemeMissing">{0} does not exist.</sys:String>
|
||||
<sys:String x:Key="Themes:SavedTemplateTheme">Template theme "{0}" saved to Themes folder.</sys:String>
|
||||
<sys:String x:Key="Themes:TemplateThemeExists">Template theme already exists!</sys:String>
|
||||
<sys:String x:Key="Themes:FailedToLoadXaml">Failed to load .xaml file for theme {0}: {1}</sys:String>
|
||||
|
||||
<!-- Updater Class -->
|
||||
<sys:String x:Key="Updater:CheckFailed">Couldn't check for updates.</sys:String>
|
||||
<sys:String x:Key="Updater:DownloadFailed">Couldn't download update.</sys:String>
|
||||
|
||||
<!-- Utils Class -->
|
||||
<sys:String x:Key="Utils:NotificationTitle">VRChat Melon Assistant</sys:String>
|
||||
<sys:String x:Key="Utils:NoInstallFolder">Could not detect your VRChat install folder. Please select it manually.</sys:String>
|
||||
<sys:String x:Key="Utils:RunAsAdmin">VRChat Melon Assistant needs to run this task as Admin. Please try again.</sys:String>
|
||||
<sys:String x:Key="Utils:InstallDir:DialogTitle">Select your VRChat install folder</sys:String>
|
||||
<sys:String x:Key="Utils:CannotOpenFolder">Can't open folder: {0}</sys:String>
|
||||
|
||||
<sys:String x:Key="ModInfoWindow:Title">Mod info for {0}</sys:String>
|
||||
<sys:String x:Key="ModInfoWindow:Author">Author: {0}</sys:String>
|
||||
<sys:String x:Key="ModInfoWindow:NoAuthor">Unknown author</sys:String>
|
||||
<sys:String x:Key="ModInfoWindow:NoDescription">No description available</sys:String>
|
||||
<sys:String x:Key="ModInfoWindow:DownloadLink">Download link: </sys:String>
|
||||
<sys:String x:Key="ModInfoWindow:SourceCodeLink">Source code: </sys:String>
|
||||
<sys:String x:Key="ModInfoWindow:InternalIds">Internal ID: {0} {1}</sys:String>
|
||||
</ResourceDictionary>
|
210
VRCMelonAssistant/MainWindow.xaml
Normal file
210
VRCMelonAssistant/MainWindow.xaml
Normal file
|
@ -0,0 +1,210 @@
|
|||
<Window
|
||||
x:Class="VRCMelonAssistant.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:VRCMelonAssistant"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
Title="{DynamicResource MainWindow:WindowTitle}"
|
||||
Icon="Resources/icon.ico"
|
||||
SizeChanged="Window_SizeChanged"
|
||||
UIElement.PreviewMouseDown="Window_PreviewMouseDown"
|
||||
mc:Ignorable="d">
|
||||
<Grid>
|
||||
<Rectangle Fill="{DynamicResource VRCMelonAssistantBackground}" />
|
||||
<Rectangle>
|
||||
<Rectangle.Fill>
|
||||
<ImageBrush x:Name="BackgroundImage" Stretch="{DynamicResource BackgroundImageStretch}" />
|
||||
</Rectangle.Fill>
|
||||
</Rectangle>
|
||||
<MediaElement
|
||||
Name="BackgroundVideo"
|
||||
LoadedBehavior="Manual"
|
||||
MediaEnded="BackgroundVideo_MediaEnded"
|
||||
Visibility="Hidden" />
|
||||
<Image
|
||||
x:Name="SideImage"
|
||||
Width="{Binding RelativeSource={RelativeSource Self}, Path=Source.PixelWidth}"
|
||||
Height="{Binding RelativeSource={RelativeSource Self}, Path=Source.PixelHeight}"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="{DynamicResource SideImageYPosition}"
|
||||
SnapsToDevicePixels="True"
|
||||
Stretch="Fill"
|
||||
UseLayoutRounding="True" />
|
||||
<Grid Margin="10">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="50" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="70" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid Grid.Column="0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="65" />
|
||||
<RowDefinition Height="70" />
|
||||
<RowDefinition Height="70" />
|
||||
<RowDefinition Height="70" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Button
|
||||
Name="IntroButton"
|
||||
Grid.Row="0"
|
||||
Height="60"
|
||||
Margin="0,0,10,5"
|
||||
Click="IntroButton_Click"
|
||||
Style="{DynamicResource MainPageButton}">
|
||||
<StackPanel Margin="0,6,0,0">
|
||||
<Image
|
||||
Height="30"
|
||||
VerticalAlignment="Bottom"
|
||||
Source="{StaticResource info_circleDrawingImage}" />
|
||||
<Viewbox Stretch="Uniform" Height="16">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
Padding="2,0,2,0"
|
||||
Text="{DynamicResource MainWindow:IntroButton}" />
|
||||
</Viewbox>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
Name="ModsButton"
|
||||
Grid.Row="1"
|
||||
Height="60"
|
||||
Margin="0,5,10,5"
|
||||
Click="ModsButton_Click"
|
||||
IsEnabled="false"
|
||||
Style="{DynamicResource MainPageButton}">
|
||||
<StackPanel Margin="0,6,0,0">
|
||||
<Image
|
||||
Height="30"
|
||||
VerticalAlignment="Bottom"
|
||||
Source="{StaticResource microchipDrawingImage}" />
|
||||
<Viewbox Stretch="Uniform" Height="16">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
Padding="2,0,2,0"
|
||||
Text="{DynamicResource MainWindow:ModsButton}" />
|
||||
</Viewbox>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
Name="AboutButton"
|
||||
Grid.Row="2"
|
||||
Height="60"
|
||||
Margin="0,5,10,5"
|
||||
Click="AboutButton_Click"
|
||||
Style="{DynamicResource MainPageButton}">
|
||||
<StackPanel Margin="0,6,0,0">
|
||||
<Image
|
||||
Height="30"
|
||||
VerticalAlignment="Bottom"
|
||||
Source="{StaticResource heartDrawingImage}" />
|
||||
<Viewbox Stretch="Uniform" Height="16">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
Padding="2,0,2,0"
|
||||
Text="{DynamicResource MainWindow:AboutButton}" />
|
||||
</Viewbox>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
Name="OptionsButton"
|
||||
Grid.Row="3"
|
||||
Height="60"
|
||||
Margin="0,5,10,5"
|
||||
Click="OptionsButton_Click"
|
||||
Style="{DynamicResource MainPageButton}">
|
||||
<StackPanel Margin="0,5,0,0">
|
||||
<Image
|
||||
Height="30"
|
||||
VerticalAlignment="Bottom"
|
||||
Source="{StaticResource cogDrawingImage}" />
|
||||
<Viewbox Stretch="Uniform" Height="16">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
Padding="2,0,2,0"
|
||||
Text="{DynamicResource MainWindow:OptionsButton}" />
|
||||
</Viewbox>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</Grid>
|
||||
|
||||
<StackPanel Grid.Row="1" VerticalAlignment="Center">
|
||||
<TextBlock HorizontalAlignment="Center" Text="{DynamicResource MainWindow:VersionLabel}" />
|
||||
<TextBlock Name="VersionText" HorizontalAlignment="Center" />
|
||||
</StackPanel>
|
||||
|
||||
<Frame
|
||||
Name="Main"
|
||||
Grid.Column="1"
|
||||
Background="{DynamicResource FrameBackgroundColor}"
|
||||
NavigationUIVisibility="Hidden" />
|
||||
|
||||
<Grid Grid.Row="1" Grid.Column="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="auto"/>
|
||||
<ColumnDefinition Width="auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Border
|
||||
Height="40"
|
||||
VerticalAlignment="Bottom"
|
||||
BorderBrush="{DynamicResource BottomStatusBarOutline}"
|
||||
BorderThickness="1">
|
||||
<TextBlock
|
||||
Name="MainTextBlock"
|
||||
Padding="5"
|
||||
Background="{DynamicResource BottomStatusBarBackground}"
|
||||
FontSize="20" />
|
||||
</Border>
|
||||
<Button
|
||||
Name="InfoButton"
|
||||
Grid.Column="1"
|
||||
Height="40"
|
||||
MinWidth="115"
|
||||
Margin="10,10,0,0"
|
||||
Padding="20,0,20,0"
|
||||
HorizontalAlignment="Right"
|
||||
Click="InfoButton_Click"
|
||||
IsEnabled="False">
|
||||
<StackPanel>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Bottom"
|
||||
Text="{DynamicResource MainWindow:ModInfoButton}" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button
|
||||
Name="InstallButton"
|
||||
Grid.Column="2"
|
||||
Height="40"
|
||||
MinWidth="115"
|
||||
Margin="10,10,0,0"
|
||||
Padding="20,0,20,0"
|
||||
HorizontalAlignment="Right"
|
||||
Click="InstallButton_Click"
|
||||
IsEnabled="False">
|
||||
<StackPanel>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Bottom"
|
||||
Text="{DynamicResource MainWindow:InstallButtonTop}" />
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Bottom"
|
||||
Text="{DynamicResource MainWindow:InstallButtonBottom}" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Window>
|
219
VRCMelonAssistant/MainWindow.xaml.cs
Normal file
219
VRCMelonAssistant/MainWindow.xaml.cs
Normal file
|
@ -0,0 +1,219 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using VRCMelonAssistant.Pages;
|
||||
using static VRCMelonAssistant.Http;
|
||||
|
||||
namespace VRCMelonAssistant
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for MainWindow.xaml
|
||||
/// </summary>
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
public static MainWindow Instance;
|
||||
public static bool ModsOpened = false;
|
||||
public static bool ModsLoading = false;
|
||||
|
||||
public string MainText
|
||||
{
|
||||
get
|
||||
{
|
||||
return MainTextBlock.Text;
|
||||
}
|
||||
set
|
||||
{
|
||||
Dispatcher.Invoke(new Action(() => { Instance.MainTextBlock.Text = value; }));
|
||||
}
|
||||
}
|
||||
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
Instance = this;
|
||||
|
||||
const int ContentWidth = 1280;
|
||||
const int ContentHeight = 720;
|
||||
|
||||
double ChromeWidth = SystemParameters.WindowNonClientFrameThickness.Left + SystemParameters.WindowNonClientFrameThickness.Right;
|
||||
double ChromeHeight = SystemParameters.WindowNonClientFrameThickness.Top + SystemParameters.WindowNonClientFrameThickness.Bottom;
|
||||
double ResizeBorder = SystemParameters.ResizeFrameVerticalBorderWidth;
|
||||
|
||||
Width = ChromeWidth + ContentWidth + 2 * ResizeBorder;
|
||||
Height = ChromeHeight + ContentHeight + 2 * ResizeBorder;
|
||||
|
||||
VersionText.Text = App.Version;
|
||||
|
||||
Themes.LoadThemes();
|
||||
Themes.FirstLoad(Properties.Settings.Default.SelectedTheme);
|
||||
|
||||
if (Properties.Settings.Default.Agreed)
|
||||
Instance.ModsButton.IsEnabled = true;
|
||||
|
||||
if (!Properties.Settings.Default.Agreed || string.IsNullOrEmpty(Properties.Settings.Default.LastTab))
|
||||
{
|
||||
Main.Content = Intro.Instance;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (Properties.Settings.Default.LastTab)
|
||||
{
|
||||
case "Intro":
|
||||
Main.Content = Intro.Instance;
|
||||
break;
|
||||
case "Mods":
|
||||
ShowModsPage().NoAwait();
|
||||
break;
|
||||
case "About":
|
||||
Main.Content = About.Instance;
|
||||
break;
|
||||
case "Options":
|
||||
Main.Content = Options.Instance;
|
||||
Themes.LoadThemes();
|
||||
break;
|
||||
default:
|
||||
Main.Content = Intro.Instance;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Force the app to shutdown when The main window is closed.
|
||||
*
|
||||
* Explaination:
|
||||
* OneClickStatus is initialized as a static object,
|
||||
* so the window will exist, even if it is unused.
|
||||
* This would cause VRChat Melon Assistant to not shutdown,
|
||||
* because technically a window was still open.
|
||||
*/
|
||||
protected override void OnClosed(EventArgs e)
|
||||
{
|
||||
base.OnClosed(e);
|
||||
|
||||
Application.Current.Shutdown();
|
||||
}
|
||||
|
||||
internal void MarkModsPageForRefresh()
|
||||
{
|
||||
ModsOpened = false;
|
||||
InstallButton.IsEnabled = false;
|
||||
}
|
||||
|
||||
public async Task ShowModsPage()
|
||||
{
|
||||
void OpenModsPage()
|
||||
{
|
||||
Main.Content = Mods.Instance;
|
||||
Properties.Settings.Default.LastTab = "Mods";
|
||||
Properties.Settings.Default.Save();
|
||||
Mods.Instance.RefreshColumns();
|
||||
}
|
||||
|
||||
if (ModsOpened && !Mods.Instance.PendingChanges)
|
||||
{
|
||||
OpenModsPage();
|
||||
return;
|
||||
}
|
||||
|
||||
Main.Content = Loading.Instance;
|
||||
|
||||
if (ModsLoading) return;
|
||||
ModsLoading = true;
|
||||
await Mods.Instance.LoadMods();
|
||||
ModsLoading = false;
|
||||
|
||||
if (ModsOpened == false) ModsOpened = true;
|
||||
if (Mods.Instance.PendingChanges == true) Mods.Instance.PendingChanges = false;
|
||||
|
||||
if (Main.Content == Loading.Instance)
|
||||
{
|
||||
OpenModsPage();
|
||||
}
|
||||
}
|
||||
|
||||
private void ModsButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ShowModsPage().NoAwait();
|
||||
}
|
||||
|
||||
private void IntroButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Main.Content = Intro.Instance;
|
||||
Properties.Settings.Default.LastTab = "Intro";
|
||||
Properties.Settings.Default.Save();
|
||||
}
|
||||
|
||||
private void AboutButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Main.Content = About.Instance;
|
||||
Properties.Settings.Default.LastTab = "About";
|
||||
Properties.Settings.Default.Save();
|
||||
}
|
||||
|
||||
private void OptionsButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Main.Content = Options.Instance;
|
||||
Themes.LoadThemes();
|
||||
Properties.Settings.Default.LastTab = "Options";
|
||||
Properties.Settings.Default.Save();
|
||||
}
|
||||
|
||||
private void InstallButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Mods.Instance.InstallMods();
|
||||
}
|
||||
|
||||
private void InfoButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if ((Mods.ModListItem)Mods.Instance.ModsListView.SelectedItem == null)
|
||||
{
|
||||
MessageBox.Show((string)Application.Current.FindResource("MainWindow:NoModSelected"));
|
||||
return;
|
||||
}
|
||||
Mods.ModListItem mod = ((Mods.ModListItem)Mods.Instance.ModsListView.SelectedItem);
|
||||
ShowModInfoWindow(mod.ModInfo);
|
||||
}
|
||||
|
||||
internal static void ShowModInfoWindow(Mod mod)
|
||||
{
|
||||
var infoWindow = new ModInfoWindow();
|
||||
infoWindow.SetMod(mod);
|
||||
infoWindow.Owner = Instance;
|
||||
infoWindow.WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
||||
infoWindow.ShowDialog();
|
||||
}
|
||||
|
||||
private void Window_PreviewMouseDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (About.Instance.PatUp.IsOpen)
|
||||
{
|
||||
About.Instance.PatUp.IsOpen = false;
|
||||
About.Instance.PatButton.IsEnabled = true;
|
||||
}
|
||||
|
||||
if (About.Instance.HugUp.IsOpen)
|
||||
{
|
||||
About.Instance.HugUp.IsOpen = false;
|
||||
About.Instance.HugButton.IsEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
|
||||
{
|
||||
if (Main.Content == Mods.Instance)
|
||||
{
|
||||
Mods.Instance.RefreshColumns();
|
||||
}
|
||||
}
|
||||
|
||||
private void BackgroundVideo_MediaEnded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
BackgroundVideo.Position = TimeSpan.Zero;
|
||||
BackgroundVideo.Play();
|
||||
}
|
||||
}
|
||||
}
|
26
VRCMelonAssistant/ModInfoWindow.xaml
Normal file
26
VRCMelonAssistant/ModInfoWindow.xaml
Normal file
|
@ -0,0 +1,26 @@
|
|||
<Window x:Class="VRCMelonAssistant.ModInfoWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:VRCMelonAssistant"
|
||||
mc:Ignorable="d"
|
||||
Height="450" Width="800">
|
||||
<Grid>
|
||||
<Rectangle Fill="{DynamicResource VRCMelonAssistantBackground}" />
|
||||
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel Orientation="Vertical" Margin="10">
|
||||
<TextBlock FontSize="16" Name="ModName" />
|
||||
<TextBlock Name="ModVersion"/>
|
||||
<TextBlock Name="ModAuthor" />
|
||||
<TextBlock />
|
||||
<TextBlock Name="ModDescription" TextWrapping="Wrap" />
|
||||
<TextBlock />
|
||||
<TextBlock Name="SourceCodeLink" />
|
||||
<TextBlock Name="DownloadLink" />
|
||||
<TextBlock Name="InternalIds" />
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</Window>
|
||||
|
50
VRCMelonAssistant/ModInfoWindow.xaml.cs
Normal file
50
VRCMelonAssistant/ModInfoWindow.xaml.cs
Normal file
|
@ -0,0 +1,50 @@
|
|||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Documents;
|
||||
|
||||
namespace VRCMelonAssistant
|
||||
{
|
||||
public partial class ModInfoWindow : Window
|
||||
{
|
||||
public ModInfoWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public void SetMod(Mod mod)
|
||||
{
|
||||
Title = string.Format((string) FindResource("ModInfoWindow:Title"), mod.versions[0].name);
|
||||
|
||||
ModDescription.Text = mod.versions[0].description ?? (string) FindResource("ModInfoWindow:NoDescription");
|
||||
ModName.Text = mod.versions[0].name;
|
||||
ModAuthor.Text = string.Format((string) FindResource("ModInfoWindow:Author"), mod.versions[0].author ?? FindResource("ModInfoWindow:NoAuthor"));
|
||||
ModVersion.Text = mod.versions[0].modVersion;
|
||||
|
||||
var dlLink = mod.versions[0].downloadLink;
|
||||
DownloadLink.Text = (string) FindResource("ModInfoWindow:DownloadLink");
|
||||
DownloadLink.Inlines.Add(new Run(" "));
|
||||
if (dlLink?.StartsWith("http") == true)
|
||||
DownloadLink.Inlines.Add(CreateHyperlink(dlLink));
|
||||
else
|
||||
DownloadLink.Inlines.Add(new Run(dlLink));
|
||||
|
||||
var srcLink = mod.versions[0].sourceLink;
|
||||
SourceCodeLink.Text = (string) FindResource("ModInfoWindow:SourceCodeLink");
|
||||
SourceCodeLink.Inlines.Add(new Run(" "));
|
||||
if (srcLink?.StartsWith("http") == true)
|
||||
SourceCodeLink.Inlines.Add(CreateHyperlink(srcLink));
|
||||
else
|
||||
SourceCodeLink.Inlines.Add(new Run(srcLink));
|
||||
|
||||
InternalIds.Text = string.Format((string) FindResource("ModInfoWindow:InternalIds"), mod._id, mod.versions[0]._version);
|
||||
}
|
||||
|
||||
private static Hyperlink CreateHyperlink(string uri)
|
||||
{
|
||||
var link = new Hyperlink(new Run(uri)) {NavigateUri = new Uri(uri)};
|
||||
link.RequestNavigate += HyperlinkExtensions.Hyperlink_RequestNavigate;
|
||||
return link;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
299
VRCMelonAssistant/Pages/About.xaml
Normal file
299
VRCMelonAssistant/Pages/About.xaml
Normal file
|
@ -0,0 +1,299 @@
|
|||
<Page
|
||||
x:Class="VRCMelonAssistant.Pages.About"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:VRCMelonAssistant"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:wfi="clr-namespace:System.Windows.Forms.Integration;assembly=WindowsFormsIntegration"
|
||||
xmlns:winForms="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
|
||||
Title="{DynamicResource About:Title}"
|
||||
d:DesignHeight="629"
|
||||
d:DesignWidth="1182"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Margin="0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Top"
|
||||
FontSize="24"
|
||||
Text="{DynamicResource About:PageTitle}" />
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Margin="0,5"
|
||||
FontSize="16"
|
||||
Text="{DynamicResource About:List:Header}"
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="6"
|
||||
Margin="0,5,5,5"
|
||||
FontSize="16"
|
||||
TextWrapping="Wrap">
|
||||
<StaticResource ResourceKey="About:SupportAssistant" />
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="7"
|
||||
Margin="0,10,5,5"
|
||||
FontSize="20"
|
||||
Text="{DynamicResource About:SpecialThanks}" />
|
||||
|
||||
<StackPanel
|
||||
Grid.Row="8"
|
||||
HorizontalAlignment="Center"
|
||||
Orientation="Horizontal">
|
||||
|
||||
<StackPanel Margin="10" Orientation="Vertical">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="20"
|
||||
FontWeight="Bold">
|
||||
<Hyperlink NavigateUri="https://github.com/Assistant/" RequestNavigate="Hyperlink_RequestNavigate">
|
||||
Assistant
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="16">
|
||||
Creating the original
|
||||
</TextBlock>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="16">
|
||||
Mod Assistant
|
||||
</TextBlock>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="16">
|
||||
<Hyperlink local:HyperlinkExtensions.IsExternal="True" NavigateUri="https://ko-fi.com/N4N8JX7B">
|
||||
<Run Text="{DynamicResource About:Donate}" />
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Margin="10" Orientation="Vertical">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="20"
|
||||
FontWeight="Bold">
|
||||
<Hyperlink NavigateUri="https://umbranox.carrd.co/" RequestNavigate="Hyperlink_RequestNavigate">
|
||||
Umbranox
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="16">
|
||||
Inspiration
|
||||
</TextBlock>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="16">
|
||||
Creating the Mod Manager
|
||||
</TextBlock>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="16">
|
||||
<Hyperlink local:HyperlinkExtensions.IsExternal="True" NavigateUri="https://www.patreon.com/scoresaber">
|
||||
<Run Text="{DynamicResource About:Donate}" />
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Margin="10" Orientation="Vertical">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="20"
|
||||
FontWeight="Bold">
|
||||
<Hyperlink local:HyperlinkExtensions.IsExternal="True" NavigateUri="https://www.jackbaron.com/">
|
||||
lolPants
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="16">
|
||||
Inspiration
|
||||
</TextBlock>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="16">
|
||||
Creating ModSaber
|
||||
</TextBlock>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="16">
|
||||
The first Mod repository
|
||||
</TextBlock>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="16">
|
||||
<Hyperlink NavigateUri="https://www.paypal.me/jackbarondev" RequestNavigate="Hyperlink_RequestNavigate">
|
||||
<Run Text="{DynamicResource About:Donate}" />
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Margin="10" Orientation="Vertical">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="20"
|
||||
FontWeight="Bold">
|
||||
<Hyperlink local:HyperlinkExtensions.IsExternal="True" NavigateUri="https://www.github.com/Caeden117">
|
||||
Caeden117
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="16">
|
||||
Theme Support
|
||||
</TextBlock>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="16">
|
||||
<Hyperlink NavigateUri="https://ko-fi.com/Caeden117" RequestNavigate="Hyperlink_RequestNavigate">
|
||||
<Run Text="{DynamicResource About:Donate}" />
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Margin="10" Orientation="Vertical">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="20"
|
||||
FontWeight="Bold">
|
||||
<Hyperlink local:HyperlinkExtensions.IsExternal="True" NavigateUri="https://twitter.com/moarinterz">
|
||||
Interz
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="16">
|
||||
Logos and icon design
|
||||
</TextBlock>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="16">
|
||||
<Hyperlink NavigateUri="https://streamlabs.com/lnterz/tip" RequestNavigate="Hyperlink_RequestNavigate">
|
||||
<Run Text="{DynamicResource About:Donate}" />
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Margin="10" Orientation="Vertical">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="20"
|
||||
FontWeight="Bold">
|
||||
<Hyperlink local:HyperlinkExtensions.IsExternal="True" NavigateUri="https://github.com/megalon">
|
||||
Megalon2D
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="16">
|
||||
BSMG Theme
|
||||
</TextBlock>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="16">
|
||||
Lots of fixes
|
||||
</TextBlock>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="16">
|
||||
<Hyperlink NavigateUri="https://ko-fi.com/megalon" RequestNavigate="Hyperlink_RequestNavigate">
|
||||
<Run Text="{DynamicResource About:Donate}" />
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Grid.Row="9"
|
||||
Margin="5"
|
||||
HorizontalAlignment="Center"
|
||||
Orientation="Horizontal">
|
||||
<Button
|
||||
x:Name="PatButton"
|
||||
Height="30"
|
||||
MinWidth="80"
|
||||
Margin="0,0,5,0"
|
||||
Padding="20,0,20,0"
|
||||
x:FieldModifier="public"
|
||||
Click="HeadpatsButton_Click"
|
||||
Content="{DynamicResource About:HeadpatsButton}" />
|
||||
<Button
|
||||
x:Name="HugButton"
|
||||
Height="30"
|
||||
MinWidth="80"
|
||||
Margin="5,0,0,0"
|
||||
Padding="20,0,20,0"
|
||||
x:FieldModifier="public"
|
||||
Click="HugsButton_Click"
|
||||
Content="{DynamicResource About:HugsButton}" />
|
||||
</StackPanel>
|
||||
<Popup
|
||||
x:Name="PatUp"
|
||||
Width="auto"
|
||||
Height="auto"
|
||||
Placement="Center">
|
||||
<Border BorderBrush="Gray" BorderThickness="3">
|
||||
<wfi:WindowsFormsHost>
|
||||
<winForms:PictureBox x:Name="PatImage" />
|
||||
</wfi:WindowsFormsHost>
|
||||
</Border>
|
||||
</Popup>
|
||||
<Popup
|
||||
x:Name="HugUp"
|
||||
Width="auto"
|
||||
Height="auto"
|
||||
Placement="Center">
|
||||
<Border BorderBrush="Gray" BorderThickness="3">
|
||||
<wfi:WindowsFormsHost>
|
||||
<winForms:PictureBox x:Name="HugImage" />
|
||||
</wfi:WindowsFormsHost>
|
||||
</Border>
|
||||
</Popup>
|
||||
</Grid>
|
||||
</Page>
|
82
VRCMelonAssistant/Pages/About.xaml.cs
Normal file
82
VRCMelonAssistant/Pages/About.xaml.cs
Normal file
|
@ -0,0 +1,82 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Navigation;
|
||||
using static VRCMelonAssistant.Http;
|
||||
|
||||
namespace VRCMelonAssistant.Pages
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for Page1.xaml
|
||||
/// </summary>
|
||||
public partial class About : Page
|
||||
{
|
||||
public static About Instance = new About();
|
||||
|
||||
public About()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e)
|
||||
{
|
||||
Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri));
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private async void HeadpatsButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
PatButton.IsEnabled = false;
|
||||
await Task.Run(async () => await HeadPat());
|
||||
PatUp.IsOpen = true;
|
||||
}
|
||||
|
||||
private async void HugsButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
HugButton.IsEnabled = false;
|
||||
await Task.Run(async () => await Hug());
|
||||
HugUp.IsOpen = true;
|
||||
}
|
||||
|
||||
private async Task<string> WeebCDN(string type)
|
||||
{
|
||||
var resp = await HttpClient.GetAsync(Utils.Constants.WeebCDNAPIURL + type + "/random");
|
||||
var body = await resp.Content.ReadAsStringAsync();
|
||||
|
||||
var response = JsonSerializer.Deserialize<Utils.WeebCDNRandomResponse>(body);
|
||||
return response.url;
|
||||
}
|
||||
|
||||
private async Task HeadPat()
|
||||
{
|
||||
try
|
||||
{
|
||||
PatImage.Load(await WeebCDN("pats"));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
Utils.ShowErrorMessageBox("Oops! Can't get headpats right now!", ex);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async Task Hug()
|
||||
{
|
||||
try
|
||||
{
|
||||
HugImage.Load(await WeebCDN("hugs"));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
Utils.ShowErrorMessageBox("Oops! Can't get hugs right now!", ex);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
159
VRCMelonAssistant/Pages/Intro.xaml
Normal file
159
VRCMelonAssistant/Pages/Intro.xaml
Normal file
|
@ -0,0 +1,159 @@
|
|||
<Page
|
||||
x:Class="VRCMelonAssistant.Pages.Intro"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:VRCMelonAssistant"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
Title="DynamicResource Intro:Title"
|
||||
d:DesignHeight="629"
|
||||
d:DesignWidth="1182"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Margin="0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="48"
|
||||
Text="{DynamicResource Intro:PageTitle}" />
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Margin="0"
|
||||
FontSize="28"
|
||||
FontWeight="Bold"
|
||||
Text="{DynamicResource Intro:Terms:Header}" />
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Margin="0,5"
|
||||
FontSize="16"
|
||||
TextWrapping="Wrap">
|
||||
<StaticResource ResourceKey="Intro:Terms:Line1" />
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="3"
|
||||
Margin="0,5"
|
||||
FontSize="16"
|
||||
TextWrapping="Wrap">
|
||||
<StaticResource ResourceKey="Intro:Terms:Line2" />
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="4"
|
||||
Margin="15,5"
|
||||
FontSize="16"
|
||||
TextWrapping="Wrap">
|
||||
<StaticResource ResourceKey="Intro:Terms:Term0" />
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="5"
|
||||
Margin="15,5"
|
||||
FontSize="16"
|
||||
TextWrapping="Wrap">
|
||||
<StaticResource ResourceKey="Intro:Terms:Term1" />
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="6"
|
||||
Margin="15,5"
|
||||
FontSize="16"
|
||||
TextWrapping="Wrap">
|
||||
<StaticResource ResourceKey="Intro:Terms:Term2" />
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="7"
|
||||
Margin="15,5"
|
||||
FontSize="16"
|
||||
TextWrapping="Wrap">
|
||||
<StaticResource ResourceKey="Intro:Terms:Term3" />
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="8"
|
||||
Margin="0,5"
|
||||
FontSize="16"
|
||||
TextWrapping="Wrap">
|
||||
<StaticResource ResourceKey="Intro:ReviewsBeatGamesFault" />
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="9"
|
||||
Margin="0,5"
|
||||
FontSize="16"
|
||||
TextWrapping="Wrap">
|
||||
<StaticResource ResourceKey="Intro:ReportIssuesNoMods" />
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="10"
|
||||
Margin="0,5"
|
||||
FontSize="16"
|
||||
TextWrapping="Wrap">
|
||||
<StaticResource ResourceKey="Intro:ReviewsRustySpoon" />
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="11"
|
||||
Margin="0,5"
|
||||
FontSize="16"
|
||||
TextWrapping="Wrap">
|
||||
<StaticResource ResourceKey="Intro:WikiGuide" />
|
||||
</TextBlock>
|
||||
|
||||
<StackPanel
|
||||
Grid.Row="12"
|
||||
Margin="10"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Orientation="Horizontal">
|
||||
<Button
|
||||
Name="Agree"
|
||||
Height="35"
|
||||
Margin="0,0,10,0"
|
||||
Padding="20,0,20,0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Click="Agree_Click">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="18"
|
||||
Text="{DynamicResource Intro:AgreeButton}" />
|
||||
</Button>
|
||||
<Button
|
||||
Name="Disagree"
|
||||
Height="35"
|
||||
Margin="10,0,0,0"
|
||||
Padding="20,0,20,0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Click="Disagree_Click">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="18"
|
||||
Text="{DynamicResource Intro:DisagreeButton}" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Page>
|
|
@ -1,20 +1,9 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace ModAssistant.Pages
|
||||
namespace VRCMelonAssistant.Pages
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for Intro.xaml
|
||||
|
@ -39,24 +28,20 @@ namespace ModAssistant.Pages
|
|||
MainWindow.Instance.ModsButton.IsEnabled = false;
|
||||
Properties.Settings.Default.Agreed = false;
|
||||
Properties.Settings.Default.Save();
|
||||
MessageBox.Show("Closing Application: You did not agree to terms and conditions.");
|
||||
System.Windows.Application.Current.Shutdown();
|
||||
MessageBox.Show((string)FindResource("Intro:ClosingApp"));
|
||||
Application.Current.Shutdown();
|
||||
}
|
||||
|
||||
private void Agree_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (String.IsNullOrEmpty(MainWindow.GameVersion))
|
||||
{
|
||||
MessageBox.Show("Could not download versions list.\nMods tab disabled. Please restart to try again.");
|
||||
}
|
||||
else
|
||||
{
|
||||
MainWindow.Instance.ModsButton.IsEnabled = true;
|
||||
Utils.SendNotify("You can now use the Mods tab!");
|
||||
MainWindow.Instance.MainText = "You can now use the Mods tab!";
|
||||
}
|
||||
MainWindow.Instance.ModsButton.IsEnabled = true;
|
||||
|
||||
string text = (string) FindResource("Intro:ModsTabEnabled");
|
||||
MainWindow.Instance.MainText = text;
|
||||
Properties.Settings.Default.Agreed = true;
|
||||
Properties.Settings.Default.Save();
|
||||
|
||||
MainWindow.Instance.ShowModsPage().NoAwait();
|
||||
}
|
||||
}
|
||||
}
|
127
VRCMelonAssistant/Pages/Invalid.xaml
Normal file
127
VRCMelonAssistant/Pages/Invalid.xaml
Normal file
|
@ -0,0 +1,127 @@
|
|||
<Page
|
||||
x:Class="VRCMelonAssistant.Pages.Invalid"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:VRCMelonAssistant.Pages"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
Title="{DynamicResource Invalid:Title}"
|
||||
d:DesignHeight="629"
|
||||
d:DesignWidth="1182"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto" />
|
||||
<ColumnDefinition Width="auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.ColumnSpan="2"
|
||||
Margin="0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="48"
|
||||
Text="{DynamicResource Invalid:PageTitle}" />
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.ColumnSpan="2"
|
||||
Margin="0"
|
||||
FontSize="28"
|
||||
FontWeight="Bold"
|
||||
Text="{DynamicResource Invalid:PageSubtitle}" />
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Grid.ColumnSpan="2"
|
||||
Margin="0,5"
|
||||
FontSize="16"
|
||||
Text="{DynamicResource Invalid:List:Header}"
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="3"
|
||||
Grid.ColumnSpan="2"
|
||||
Margin="15,5"
|
||||
FontSize="16"
|
||||
TextWrapping="Wrap">
|
||||
<StaticResource ResourceKey="Invalid:List:Line1" />
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="4"
|
||||
Grid.ColumnSpan="2"
|
||||
Margin="15,5"
|
||||
FontSize="16"
|
||||
TextWrapping="Wrap">
|
||||
<StaticResource ResourceKey="Invalid:List:Line2" />
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="5"
|
||||
Grid.ColumnSpan="2"
|
||||
Margin="15,5"
|
||||
FontSize="16"
|
||||
TextWrapping="Wrap">
|
||||
<StaticResource ResourceKey="Invalid:List:Line3" />
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="6"
|
||||
Grid.ColumnSpan="2"
|
||||
Margin="0"
|
||||
FontSize="28"
|
||||
FontWeight="Bold" />
|
||||
<TextBlock
|
||||
Grid.Row="7"
|
||||
Grid.ColumnSpan="2"
|
||||
Margin="0"
|
||||
FontSize="20"
|
||||
FontWeight="Bold">
|
||||
<TextBlock Text="{DynamicResource Invalid:BoughtGame1}" />
|
||||
:
|
||||
</TextBlock>
|
||||
<Border
|
||||
Grid.Row="8"
|
||||
Height="30"
|
||||
MinWidth="650"
|
||||
Background="LightGray">
|
||||
<TextBlock
|
||||
Name="DirectoryTextBlock"
|
||||
Margin="5"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="16"
|
||||
Text="{Binding InstallDirectory}" />
|
||||
</Border>
|
||||
<Button
|
||||
Grid.Row="8"
|
||||
Grid.Column="2"
|
||||
Width="80"
|
||||
Height="30"
|
||||
Margin="3"
|
||||
Click="SelectDirButton_Click"
|
||||
Content="{DynamicResource Invalid:SelectFolderButton}" />
|
||||
<TextBlock
|
||||
Grid.Row="9"
|
||||
Grid.ColumnSpan="2"
|
||||
Margin="0"
|
||||
FontSize="20"
|
||||
FontWeight="Bold">
|
||||
<TextBlock Text="{DynamicResource Invalid:BoughtGame2}" />
|
||||
.
|
||||
</TextBlock>
|
||||
</Grid>
|
||||
</Page>
|
|
@ -1,20 +1,9 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace ModAssistant.Pages
|
||||
namespace VRCMelonAssistant.Pages
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for Invalid.xaml
|
||||
|
@ -27,7 +16,7 @@ namespace ModAssistant.Pages
|
|||
public Invalid()
|
||||
{
|
||||
InitializeComponent();
|
||||
InstallDirectory = App.BeatSaberInstallDirectory;
|
||||
InstallDirectory = App.VRChatInstallDirectory;
|
||||
DirectoryTextBlock.Text = InstallDirectory;
|
||||
}
|
||||
|
62
VRCMelonAssistant/Pages/Loading.xaml
Normal file
62
VRCMelonAssistant/Pages/Loading.xaml
Normal file
|
@ -0,0 +1,62 @@
|
|||
<Page
|
||||
x:Class="VRCMelonAssistant.Pages.Loading"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:VRCMelonAssistant.Pages"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
Title="Loading"
|
||||
d:DesignHeight="450"
|
||||
d:DesignWidth="800"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Page.Resources>
|
||||
<local:DivideDoubleByTwoConverter x:Key="DivideDoubleByTwoConverter" />
|
||||
<Style x:Key="Spin" TargetType="{x:Type Image}">
|
||||
<Setter Property="RenderTransform">
|
||||
<Setter.Value>
|
||||
<RotateTransform Angle="0" CenterX="{Binding Path=ActualWidth, Converter={StaticResource DivideDoubleByTwoConverter}, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Image}}" CenterY="{Binding Path=ActualHeight, Converter={StaticResource DivideDoubleByTwoConverter}, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Image}}" />
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsEnabled" Value="True">
|
||||
<Trigger.EnterActions>
|
||||
<BeginStoryboard>
|
||||
<Storyboard x:Name="RotateStarCompass">
|
||||
<DoubleAnimation
|
||||
AutoReverse="False"
|
||||
RepeatBehavior="Forever"
|
||||
Storyboard.TargetProperty="RenderTransform.Angle"
|
||||
From="0"
|
||||
To="360"
|
||||
Duration="0:0:3" />
|
||||
</Storyboard>
|
||||
</BeginStoryboard>
|
||||
</Trigger.EnterActions>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Page.Resources>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Image
|
||||
Grid.Row="0"
|
||||
Margin="100,100,100,0"
|
||||
VerticalAlignment="Center"
|
||||
Source="{DynamicResource loadingOuterDrawingImage}"
|
||||
Stretch="Uniform"
|
||||
Style="{StaticResource Spin}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Margin="100,10,100,100"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="45"
|
||||
Text="{DynamicResource Loading:Loading}" />
|
||||
</Grid>
|
||||
</Page>
|
38
VRCMelonAssistant/Pages/Loading.xaml.cs
Normal file
38
VRCMelonAssistant/Pages/Loading.xaml.cs
Normal file
|
@ -0,0 +1,38 @@
|
|||
using System;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace VRCMelonAssistant.Pages
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for Loading.xaml
|
||||
/// </summary>
|
||||
public partial class Loading : Page
|
||||
{
|
||||
public static Loading Instance = new Loading();
|
||||
|
||||
public Loading()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
|
||||
[ValueConversion(typeof(double), typeof(double))]
|
||||
public class DivideDoubleByTwoConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
|
||||
{
|
||||
if (targetType != typeof(double))
|
||||
{
|
||||
throw new InvalidOperationException("The target must be a double");
|
||||
}
|
||||
double d = (double)value;
|
||||
return ((double)d) / 2;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
164
VRCMelonAssistant/Pages/Mods.xaml
Normal file
164
VRCMelonAssistant/Pages/Mods.xaml
Normal file
|
@ -0,0 +1,164 @@
|
|||
<Page
|
||||
x:Class="VRCMelonAssistant.Pages.Mods"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:VRCMelonAssistant"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:system="clr-namespace:System;assembly=mscorlib"
|
||||
Title="{DynamicResource Mods:Title}"
|
||||
d:DesignHeight="629"
|
||||
d:DesignWidth="1182"
|
||||
Loaded="Page_Loaded"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid>
|
||||
<Grid x:Name="NoModsGrid" Visibility="Visible">
|
||||
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="32" FontWeight="SemiBold">
|
||||
<TextBlock Text="{DynamicResource Mods:NoMods}" />.
|
||||
</TextBlock>
|
||||
</Grid>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Name="SearchText"
|
||||
Grid.Row="0"
|
||||
Height="20"
|
||||
Padding="5,0,0,0"
|
||||
Panel.ZIndex="1"
|
||||
Background="{DynamicResource BottomStatusBarBackground}"
|
||||
Foreground="{DynamicResource TextColor}"
|
||||
Text="{DynamicResource Mods:SearchLabel}" />
|
||||
<TextBox
|
||||
Name="SearchBar"
|
||||
Grid.Row="0"
|
||||
Height="20"
|
||||
Margin="0,-1,0,0"
|
||||
Padding="3,1,0,0"
|
||||
Panel.ZIndex="2"
|
||||
Background="#00000000"
|
||||
BorderThickness="0"
|
||||
Foreground="{DynamicResource TextColor}"
|
||||
TextChanged="SearchBar_TextChanged" />
|
||||
<ListView
|
||||
Name="ModsListView"
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
SelectionChanged="ModsListView_SelectionChanged"
|
||||
SelectionMode="Single"
|
||||
MouseDoubleClick="ModsListView_OnMouseDoubleClick">
|
||||
<ListView.View>
|
||||
<GridView>
|
||||
<GridView.Columns>
|
||||
<GridViewColumn Width="30">
|
||||
<GridViewColumn.Header>
|
||||
<Button
|
||||
Name="SearchButton"
|
||||
Margin="-5"
|
||||
Padding="9,-1,9,0"
|
||||
Background="#00000000"
|
||||
BorderThickness="0"
|
||||
Click="SearchButton_Click"
|
||||
Content="🔍"
|
||||
FontSize="11" />
|
||||
</GridViewColumn.Header>
|
||||
<GridViewColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<CheckBox
|
||||
Name="ModCheckBox"
|
||||
Checked="ModCheckBox_Checked"
|
||||
IsChecked="{Binding Path=IsSelected, Mode=TwoWay}"
|
||||
IsEnabled="{Binding IsEnabled}"
|
||||
Tag="{Binding ModInfo}"
|
||||
Unchecked="ModCheckBox_Unchecked" />
|
||||
</DataTemplate>
|
||||
</GridViewColumn.CellTemplate>
|
||||
</GridViewColumn>
|
||||
<GridViewColumn DisplayMemberBinding="{Binding ModName}" Header="{DynamicResource Mods:Header:Name}" />
|
||||
<GridViewColumn Header="{DynamicResource Mods:Header:Author}">
|
||||
<GridViewColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock
|
||||
Text="{Binding ModAuthor}"
|
||||
MaxWidth="150" TextWrapping="NoWrap" TextTrimming="WordEllipsis"
|
||||
/>
|
||||
</DataTemplate>
|
||||
</GridViewColumn.CellTemplate>
|
||||
</GridViewColumn>
|
||||
|
||||
<GridViewColumn x:Name="InstalledColumn" Header="{DynamicResource Mods:Header:Installed}">
|
||||
<GridViewColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock
|
||||
Foreground="{Binding GetVersionColor}"
|
||||
Text="{Binding InstalledVersion}"
|
||||
TextDecorations="{Binding GetVersionDecoration}" />
|
||||
</DataTemplate>
|
||||
</GridViewColumn.CellTemplate>
|
||||
</GridViewColumn>
|
||||
|
||||
<GridViewColumn DisplayMemberBinding="{Binding ModVersion}" Header="{DynamicResource Mods:Header:Latest}" />
|
||||
<GridViewColumn x:Name="DescriptionColumn" Header="{DynamicResource Mods:Header:Description}">
|
||||
<GridViewColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Margin="{Binding PromotionMargin}" Visibility="{Binding PromotionVisibility}">
|
||||
<Hyperlink NavigateUri="{Binding PromotionLink, TargetNullValue=about:blank}" RequestNavigate="Hyperlink_RequestNavigate">
|
||||
<Run Text="{Binding PromotionText}" />
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
<TextBlock Text="{Binding ModDescription}" MouseLeftButtonDown="ModsListView_OnMouseDoubleClick" />
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
|
||||
</GridViewColumn.CellTemplate>
|
||||
</GridViewColumn>
|
||||
|
||||
<GridViewColumn
|
||||
x:Name="UninstallColumn"
|
||||
Width="70"
|
||||
Header="{DynamicResource Mods:Header:Uninstall}">
|
||||
<GridViewColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<Button
|
||||
Name="Uninstall"
|
||||
Click="Uninstall_Click"
|
||||
Content="{DynamicResource Mods:UninstallButton}"
|
||||
Foreground="Red"
|
||||
IsEnabled="{Binding CanDelete}"
|
||||
Tag="{Binding ModInfo}"
|
||||
Visibility="{Binding CanSeeDelete}" />
|
||||
</DataTemplate>
|
||||
</GridViewColumn.CellTemplate>
|
||||
</GridViewColumn>
|
||||
</GridView.Columns>
|
||||
</GridView>
|
||||
</ListView.View>
|
||||
<ListView.GroupStyle>
|
||||
<GroupStyle>
|
||||
<GroupStyle.HeaderTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Padding="6,0,0,0">
|
||||
<Run FontSize="16" FontWeight="Bold" Text="{Binding Name.Name, Mode=OneWay}" />
|
||||
<Run FontSize="12" FontStyle="Italic" Text="{Binding Name.Description, Mode=OneWay}" />
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</GroupStyle.HeaderTemplate>
|
||||
</GroupStyle>
|
||||
</ListView.GroupStyle>
|
||||
<ListView.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<VirtualizingStackPanel />
|
||||
</ItemsPanelTemplate>
|
||||
</ListView.ItemsPanel>
|
||||
</ListView>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Page>
|
577
VRCMelonAssistant/Pages/Mods.xaml.cs
Normal file
577
VRCMelonAssistant/Pages/Mods.xaml.cs
Normal file
|
@ -0,0 +1,577 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Forms;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media.Animation;
|
||||
using System.Windows.Navigation;
|
||||
using Mono.Cecil;
|
||||
using VRCMelonAssistant.Libs;
|
||||
using static VRCMelonAssistant.Http;
|
||||
using MessageBox = System.Windows.MessageBox;
|
||||
using TextBox = System.Windows.Controls.TextBox;
|
||||
|
||||
namespace VRCMelonAssistant.Pages
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for Mods.xaml
|
||||
/// </summary>
|
||||
public sealed partial class Mods : Page
|
||||
{
|
||||
public static Mods Instance = new Mods();
|
||||
|
||||
private static readonly ModListItem.CategoryInfo BrokenCategory = new("Broken", "These mods were broken by a game update. They will be temporarily removed and restored once they are updated for the current game version");
|
||||
private static readonly ModListItem.CategoryInfo RetiredCategory = new("Retired", "These mods are either no longer needed due to VRChat updates or are no longer being maintained");
|
||||
private static readonly ModListItem.CategoryInfo UncategorizedCategory = new("Uncategorized", "Mods without a category assigned");
|
||||
private static readonly ModListItem.CategoryInfo UnknownCategory = new("Unknown/Unverified", "Mods not coming from VRCMG. Potentially dangerous.");
|
||||
|
||||
public List<string> DefaultMods = new List<string>() { "UI Expansion Kit", "Finitizer", "VRCModUpdater.Loader", "VRChatUtilityKit", "Final IK Sanity", "ActionMenuApi" };
|
||||
public Mod[] AllModsList;
|
||||
public List<Mod> UnknownMods = new List<Mod>();
|
||||
public CollectionView view;
|
||||
public bool PendingChanges;
|
||||
public bool HaveInstalledMods;
|
||||
|
||||
private readonly SemaphoreSlim _modsLoadSem = new SemaphoreSlim(1, 1);
|
||||
|
||||
public List<ModListItem> ModList { get; set; }
|
||||
|
||||
public Mods()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void RefreshModsList()
|
||||
{
|
||||
view?.Refresh();
|
||||
}
|
||||
|
||||
public void RefreshColumns()
|
||||
{
|
||||
if (MainWindow.Instance.Main.Content != Instance) return;
|
||||
double viewWidth = ModsListView.ActualWidth;
|
||||
double totalSize = 0;
|
||||
GridViewColumn description = null;
|
||||
|
||||
if (ModsListView.View is GridView grid)
|
||||
{
|
||||
foreach (var column in grid.Columns)
|
||||
{
|
||||
if (column.Header?.ToString() == FindResource("Mods:Header:Description").ToString())
|
||||
{
|
||||
description = column;
|
||||
}
|
||||
else
|
||||
{
|
||||
totalSize += column.ActualWidth;
|
||||
}
|
||||
if (double.IsNaN(column.Width))
|
||||
{
|
||||
column.Width = column.ActualWidth;
|
||||
column.Width = double.NaN;
|
||||
}
|
||||
}
|
||||
double descriptionNewWidth = viewWidth - totalSize - 35;
|
||||
description.Width = descriptionNewWidth > 200 ? descriptionNewWidth : 200;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task LoadMods()
|
||||
{
|
||||
await _modsLoadSem.WaitAsync();
|
||||
|
||||
try
|
||||
{
|
||||
MainWindow.Instance.InstallButton.IsEnabled = false;
|
||||
MainWindow.Instance.InfoButton.IsEnabled = false;
|
||||
|
||||
AllModsList = null;
|
||||
|
||||
ModList = new List<ModListItem>();
|
||||
UnknownMods.Clear();
|
||||
HaveInstalledMods = false;
|
||||
|
||||
ModsListView.Visibility = Visibility.Hidden;
|
||||
|
||||
MainWindow.Instance.MainText = $"{FindResource("Mods:CheckingInstalledMods")}...";
|
||||
await CheckInstalledMods();
|
||||
InstalledColumn.Width = double.NaN;
|
||||
UninstallColumn.Width = 70;
|
||||
DescriptionColumn.Width = 750;
|
||||
|
||||
MainWindow.Instance.MainText = $"{FindResource("Mods:LoadingMods")}...";
|
||||
await PopulateModsList();
|
||||
|
||||
ModsListView.ItemsSource = ModList;
|
||||
|
||||
view = (CollectionView)CollectionViewSource.GetDefaultView(ModsListView.ItemsSource);
|
||||
PropertyGroupDescription groupDescription = new PropertyGroupDescription("Category");
|
||||
view.GroupDescriptions.Add(groupDescription);
|
||||
|
||||
this.DataContext = this;
|
||||
|
||||
RefreshModsList();
|
||||
ModsListView.Visibility = ModList.Count == 0 ? Visibility.Hidden : Visibility.Visible;
|
||||
NoModsGrid.Visibility = ModList.Count == 0 ? Visibility.Visible : Visibility.Hidden;
|
||||
|
||||
MainWindow.Instance.MainText = $"{FindResource("Mods:FinishedLoadingMods")}.";
|
||||
MainWindow.Instance.InstallButton.IsEnabled = ModList.Count != 0;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_modsLoadSem.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task CheckInstalledMods()
|
||||
{
|
||||
await GetAllMods();
|
||||
|
||||
await Task.Run(() =>
|
||||
{
|
||||
CheckInstallDir("Plugins");
|
||||
CheckInstallDir("Mods");
|
||||
CheckInstallDir("Plugins/Broken", isBrokenDir: true);
|
||||
CheckInstallDir("Mods/Broken", isBrokenDir: true);
|
||||
CheckInstallDir("Plugins/Retired", isRetiredDir: true);
|
||||
CheckInstallDir("Mods/Retired", isRetiredDir: true);
|
||||
});
|
||||
}
|
||||
|
||||
public async Task GetAllMods()
|
||||
{
|
||||
try
|
||||
{
|
||||
var resp = await HttpClient.GetAsync(Utils.Constants.VRCMGModsJson);
|
||||
var body = await resp.Content.ReadAsStringAsync();
|
||||
AllModsList = JsonSerializer.Deserialize<Mod[]>(body);
|
||||
foreach (var mod in AllModsList)
|
||||
mod.category ??= HardcodedCategories.GetCategoryFor(mod) ?? "Uncategorized";
|
||||
|
||||
Array.Sort(AllModsList, (a, b) =>
|
||||
{
|
||||
var categoryCompare = String.Compare(a.category, b.category, StringComparison.Ordinal);
|
||||
if (categoryCompare != 0) return categoryCompare;
|
||||
return String.Compare(a.versions[0].name, b.versions[0].name, StringComparison.Ordinal);
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
System.Windows.MessageBox.Show($"{FindResource("Mods:LoadFailed")}.\n\n" + e);
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckInstallDir(string directory, bool isBrokenDir = false, bool isRetiredDir = false)
|
||||
{
|
||||
if (!Directory.Exists(Path.Combine(App.VRChatInstallDirectory, directory)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (string file in Directory.GetFileSystemEntries(Path.Combine(App.VRChatInstallDirectory, directory), "*.dll", SearchOption.TopDirectoryOnly))
|
||||
{
|
||||
if (!File.Exists(file) || Path.GetExtension(file) != ".dll") continue;
|
||||
|
||||
var modInfo = ExtractModVersions(file);
|
||||
if (modInfo.Item1 != null && modInfo.Item2 != null)
|
||||
{
|
||||
var haveFoundMod = false;
|
||||
|
||||
foreach (var mod in AllModsList)
|
||||
{
|
||||
if (!mod.aliases.Contains(modInfo.ModName) && mod.versions.All(it => it.name != modInfo.ModName)) continue;
|
||||
|
||||
HaveInstalledMods = true;
|
||||
haveFoundMod = true;
|
||||
mod.installedFilePath = file;
|
||||
mod.installedVersion = modInfo.ModVersion;
|
||||
mod.installedInBrokenDir = isBrokenDir;
|
||||
mod.installedInRetiredDir = isRetiredDir;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!haveFoundMod)
|
||||
{
|
||||
var mod = new Mod()
|
||||
{
|
||||
installedFilePath = file,
|
||||
installedVersion = modInfo.ModVersion,
|
||||
installedInBrokenDir = isBrokenDir,
|
||||
installedInRetiredDir = isRetiredDir,
|
||||
versions = new []
|
||||
{
|
||||
new Mod.ModVersion()
|
||||
{
|
||||
name = modInfo.ModName,
|
||||
modVersion = modInfo.ModVersion,
|
||||
author = modInfo.ModAuthor,
|
||||
description = ""
|
||||
}
|
||||
}
|
||||
};
|
||||
UnknownMods.Add(mod);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private (string ModName, string ModVersion, string ModAuthor) ExtractModVersions(string dllPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var asmdef = AssemblyDefinition.ReadAssembly(dllPath);
|
||||
foreach (var attr in asmdef.CustomAttributes)
|
||||
if (attr.AttributeType.Name == "MelonInfoAttribute" ||
|
||||
attr.AttributeType.Name == "MelonModInfoAttribute")
|
||||
return ((string) attr.ConstructorArguments[1].Value,
|
||||
(string) attr.ConstructorArguments[2].Value, (string) attr.ConstructorArguments[3].Value);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var result = MessageBox.Show(
|
||||
$"A mod in {Path.GetFileName(dllPath)} is invalid. Would you like to delete it to avoid this error in the future?",
|
||||
"Invalid mod", MessageBoxButton.YesNo, MessageBoxImage.Warning);
|
||||
|
||||
if (result == MessageBoxResult.Yes)
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete(dllPath);
|
||||
}
|
||||
catch (Exception ex2)
|
||||
{
|
||||
Utils.ShowErrorMessageBox($"Unable to delete file {dllPath}", ex2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (null, null, null);
|
||||
}
|
||||
|
||||
public async Task PopulateModsList()
|
||||
{
|
||||
foreach (Mod mod in AllModsList.Where(x => !x.versions[0].IsBroken && !x.versions[0].IsRetired))
|
||||
AddModToList(mod);
|
||||
|
||||
foreach (var mod in UnknownMods)
|
||||
AddModToList(mod, UnknownCategory);
|
||||
|
||||
foreach (Mod mod in AllModsList.Where(x => x.versions[0].IsBroken))
|
||||
AddModToList(mod);
|
||||
|
||||
foreach (Mod mod in AllModsList.Where(x => x.versions[0].IsRetired))
|
||||
AddModToList(mod);
|
||||
}
|
||||
|
||||
private void AddModToList(Mod mod, ModListItem.CategoryInfo categoryOverride = null)
|
||||
{
|
||||
bool preSelected = false;
|
||||
|
||||
var latestVersion = mod.versions[0];
|
||||
|
||||
if (DefaultMods.Contains(latestVersion.name) && !HaveInstalledMods || mod.installedFilePath != null)
|
||||
{
|
||||
preSelected = true;
|
||||
}
|
||||
|
||||
ModListItem.CategoryInfo GetCategory(Mod mod)
|
||||
{
|
||||
if (mod.category == null) return UncategorizedCategory;
|
||||
return new ModListItem.CategoryInfo(mod.category,
|
||||
HardcodedCategories.GetCategoryDescription(mod.category));
|
||||
}
|
||||
|
||||
ModListItem ListItem = new ModListItem()
|
||||
{
|
||||
IsSelected = preSelected,
|
||||
IsEnabled = true,
|
||||
ModName = latestVersion.name,
|
||||
ModVersion = latestVersion.modVersion,
|
||||
ModAuthor = HardcodedCategories.FixupAuthor(latestVersion.author),
|
||||
ModDescription = latestVersion.description.Replace("\r\n", " ").Replace("\n", " "),
|
||||
ModInfo = mod,
|
||||
IsInstalled = mod.installedFilePath != null,
|
||||
InstalledVersion = mod.installedVersion,
|
||||
InstalledModInfo = mod,
|
||||
Category = categoryOverride ?? (latestVersion.IsBroken ? BrokenCategory : (latestVersion.IsRetired ? RetiredCategory : GetCategory(mod)))
|
||||
};
|
||||
|
||||
foreach (Promotion promo in Promotions.ActivePromotions)
|
||||
{
|
||||
if (latestVersion.name == promo.ModName)
|
||||
{
|
||||
ListItem.PromotionText = promo.Text;
|
||||
ListItem.PromotionLink = promo.Link;
|
||||
}
|
||||
}
|
||||
|
||||
mod.ListItem = ListItem;
|
||||
|
||||
ModList.Add(ListItem);
|
||||
}
|
||||
|
||||
public async void InstallMods()
|
||||
{
|
||||
MainWindow.Instance.InstallButton.IsEnabled = false;
|
||||
|
||||
if (!InstallHandlers.IsMelonLoaderInstalled())
|
||||
await InstallHandlers.InstallMelonLoader();
|
||||
|
||||
foreach (Mod mod in AllModsList)
|
||||
{
|
||||
// Ignore mods that are newer than installed version or up-to-date
|
||||
if (mod.ListItem.GetVersionComparison >= 0 && mod.installedInBrokenDir == mod.versions[0].IsBroken && mod.installedInRetiredDir == mod.versions[0].IsRetired) continue;
|
||||
|
||||
if (mod.ListItem.IsSelected)
|
||||
{
|
||||
MainWindow.Instance.MainText = $"{string.Format((string)FindResource("Mods:InstallingMod"), mod.versions[0].name)}...";
|
||||
await InstallHandlers.InstallMod(mod);
|
||||
MainWindow.Instance.MainText = $"{string.Format((string)FindResource("Mods:InstalledMod"), mod.versions[0].name)}.";
|
||||
}
|
||||
}
|
||||
|
||||
MainWindow.Instance.MainText = $"{FindResource("Mods:FinishedInstallingMods")}.";
|
||||
MainWindow.Instance.InstallButton.IsEnabled = true;
|
||||
RefreshModsList();
|
||||
}
|
||||
|
||||
private void ModCheckBox_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Mod mod = ((sender as System.Windows.Controls.CheckBox).Tag as Mod);
|
||||
mod.ListItem.IsSelected = true;
|
||||
}
|
||||
|
||||
private void ModCheckBox_Unchecked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Mod mod = ((sender as System.Windows.Controls.CheckBox).Tag as Mod);
|
||||
mod.ListItem.IsSelected = false;
|
||||
}
|
||||
|
||||
public class Category
|
||||
{
|
||||
public string CategoryName { get; set; }
|
||||
public List<ModListItem> Mods = new List<ModListItem>();
|
||||
}
|
||||
|
||||
public class ModListItem
|
||||
{
|
||||
public string ModName { get; set; }
|
||||
public string ModVersion { get; set; }
|
||||
public string ModAuthor { get; set; }
|
||||
public string ModDescription { get; set; }
|
||||
public bool PreviousState { get; set; }
|
||||
|
||||
public bool IsEnabled { get; set; }
|
||||
public bool IsSelected { get; set; }
|
||||
public Mod ModInfo { get; set; }
|
||||
public CategoryInfo Category { get; set; }
|
||||
|
||||
public Mod InstalledModInfo { get; set; }
|
||||
public bool IsInstalled { get; set; }
|
||||
private SemVersion _installedVersion { get; set; }
|
||||
public string InstalledVersion
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!IsInstalled || _installedVersion == null) return "-";
|
||||
return _installedVersion.ToString();
|
||||
}
|
||||
set
|
||||
{
|
||||
if (SemVersion.TryParse(value, out SemVersion tempInstalledVersion))
|
||||
{
|
||||
_installedVersion = tempInstalledVersion;
|
||||
}
|
||||
else
|
||||
{
|
||||
_installedVersion = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string GetVersionColor
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!IsInstalled || _installedVersion == null) return "Black";
|
||||
return _installedVersion >= ModVersion ? "Green" : "Red";
|
||||
}
|
||||
}
|
||||
|
||||
public string GetVersionDecoration
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!IsInstalled || _installedVersion == null) return "None";
|
||||
return _installedVersion >= ModVersion ? "None" : "Strikethrough";
|
||||
}
|
||||
}
|
||||
|
||||
public int GetVersionComparison
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!IsInstalled || _installedVersion == null || _installedVersion < ModVersion) return -1;
|
||||
if (_installedVersion > ModVersion) return 1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanDelete => IsInstalled;
|
||||
|
||||
public string CanSeeDelete => IsInstalled ? "Visible" : "Hidden";
|
||||
|
||||
public string PromotionText { get; set; }
|
||||
public string PromotionLink { get; set; }
|
||||
public string PromotionMargin
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(PromotionText)) return "0";
|
||||
return "0,0,5,0";
|
||||
}
|
||||
}
|
||||
|
||||
public Visibility PromotionVisibility => string.IsNullOrEmpty(PromotionText) ? Visibility.Collapsed : Visibility.Visible;
|
||||
|
||||
public record CategoryInfo(string Name, string Description)
|
||||
{
|
||||
public string Name { get; } = Name;
|
||||
public string Description { get; } = Description;
|
||||
}
|
||||
}
|
||||
|
||||
private void ModsListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if ((Mods.ModListItem)Instance.ModsListView.SelectedItem == null)
|
||||
{
|
||||
MainWindow.Instance.InfoButton.IsEnabled = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
MainWindow.Instance.InfoButton.IsEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void Uninstall_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Mod mod = ((sender as System.Windows.Controls.Button).Tag as Mod);
|
||||
|
||||
string title = string.Format((string)FindResource("Mods:UninstallBox:Title"), mod.versions[0].name);
|
||||
string body1 = string.Format((string)FindResource("Mods:UninstallBox:Body1"), mod.versions[0].name);
|
||||
string body2 = string.Format((string)FindResource("Mods:UninstallBox:Body2"), mod.versions[0].name);
|
||||
var result = System.Windows.Forms.MessageBox.Show($"{body1}\n{body2}", title, MessageBoxButtons.YesNo);
|
||||
|
||||
if (result == DialogResult.Yes)
|
||||
{
|
||||
UninstallModFromList(mod);
|
||||
}
|
||||
}
|
||||
|
||||
private void UninstallModFromList(Mod mod)
|
||||
{
|
||||
UninstallMod(mod.ListItem.InstalledModInfo);
|
||||
mod.ListItem.IsInstalled = false;
|
||||
mod.ListItem.InstalledVersion = null;
|
||||
mod.ListItem.IsSelected = false;
|
||||
RefreshModsList();
|
||||
view.Refresh();
|
||||
}
|
||||
|
||||
public void UninstallMod(Mod mod)
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete(mod.installedFilePath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Windows.MessageBox.Show($"{FindResource("Mods:UninstallSingleFailed")}.\n\n" + ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e)
|
||||
{
|
||||
Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri));
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void Page_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
RefreshColumns();
|
||||
}
|
||||
|
||||
private void SearchButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (SearchBar.Height == 0)
|
||||
{
|
||||
SearchBar.Focus();
|
||||
Animate(SearchBar, 0, 20, new TimeSpan(0, 0, 0, 0, 300));
|
||||
Animate(SearchText, 0, 20, new TimeSpan(0, 0, 0, 0, 300));
|
||||
ModsListView.Items.Filter = new Predicate<object>(SearchFilter);
|
||||
}
|
||||
else
|
||||
{
|
||||
Animate(SearchBar, 20, 0, new TimeSpan(0, 0, 0, 0, 300));
|
||||
Animate(SearchText, 20, 0, new TimeSpan(0, 0, 0, 0, 300));
|
||||
ModsListView.Items.Filter = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void SearchBar_TextChanged(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
ModsListView.Items.Filter = new Predicate<object>(SearchFilter);
|
||||
if (SearchBar.Text.Length > 0)
|
||||
{
|
||||
SearchText.Text = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
SearchText.Text = (string)FindResource("Mods:SearchLabel");
|
||||
}
|
||||
}
|
||||
|
||||
private bool SearchFilter(object mod)
|
||||
{
|
||||
ModListItem item = mod as ModListItem;
|
||||
if (item.ModName.ToLower().Contains(SearchBar.Text.ToLower())) return true;
|
||||
if (item.ModDescription.ToLower().Contains(SearchBar.Text.ToLower())) return true;
|
||||
if (item.ModName.ToLower().Replace(" ", string.Empty).Contains(SearchBar.Text.ToLower().Replace(" ", string.Empty))) return true;
|
||||
if (item.ModDescription.ToLower().Replace(" ", string.Empty).Contains(SearchBar.Text.ToLower().Replace(" ", string.Empty))) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private void Animate(TextBlock target, double oldHeight, double newHeight, TimeSpan duration)
|
||||
{
|
||||
target.Height = oldHeight;
|
||||
DoubleAnimation animation = new DoubleAnimation(newHeight, duration);
|
||||
target.BeginAnimation(HeightProperty, animation);
|
||||
}
|
||||
|
||||
private void Animate(TextBox target, double oldHeight, double newHeight, TimeSpan duration)
|
||||
{
|
||||
target.Height = oldHeight;
|
||||
DoubleAnimation animation = new DoubleAnimation(newHeight, duration);
|
||||
target.BeginAnimation(HeightProperty, animation);
|
||||
}
|
||||
|
||||
private void ModsListView_OnMouseDoubleClick(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (e.ClickCount != 2) return;
|
||||
var selectedMod = ModsListView.SelectedItem as ModListItem;
|
||||
if (selectedMod == null) return;
|
||||
MainWindow.ShowModInfoWindow(selectedMod.ModInfo);
|
||||
}
|
||||
}
|
||||
}
|
216
VRCMelonAssistant/Pages/Options.xaml
Normal file
216
VRCMelonAssistant/Pages/Options.xaml
Normal file
|
@ -0,0 +1,216 @@
|
|||
<Page
|
||||
x:Class="VRCMelonAssistant.Pages.Options"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:VRCMelonAssistant.Pages"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
Title="{DynamicResource Options:Title}"
|
||||
d:DesignHeight="629"
|
||||
d:DesignWidth="1182"
|
||||
mc:Ignorable="d">
|
||||
<Page.Resources />
|
||||
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto" />
|
||||
<ColumnDefinition Width="auto" />
|
||||
<ColumnDefinition Width="auto" />
|
||||
<ColumnDefinition Width="auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Margin="15,5,5,5"
|
||||
HorizontalAlignment="Left"
|
||||
FontSize="24"
|
||||
FontWeight="Bold"
|
||||
Text="{DynamicResource Options:PageTitle}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Grid.Column="2"
|
||||
Margin="5"
|
||||
HorizontalAlignment="Right"
|
||||
FontSize="22"
|
||||
FontWeight="Bold"
|
||||
Text="A 文" />
|
||||
<ComboBox
|
||||
Name="LanguageSelectComboBox"
|
||||
Grid.Row="0"
|
||||
Grid.Column="3"
|
||||
Height="30"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalContentAlignment="Center"
|
||||
SelectionChanged="LanguageSelectComboBox_SelectionChanged" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Margin="5"
|
||||
HorizontalAlignment="Left"
|
||||
FontSize="16"
|
||||
FontWeight="Bold"
|
||||
Text="{DynamicResource Options:InstallFolder}" />
|
||||
<Border
|
||||
Grid.Row="2"
|
||||
Grid.ColumnSpan="2"
|
||||
Height="30"
|
||||
MinWidth="450"
|
||||
Margin="5"
|
||||
Background="{DynamicResource DirectoryBackground}"
|
||||
BorderBrush="{DynamicResource DirectoryOutline}"
|
||||
BorderThickness="1">
|
||||
<TextBlock
|
||||
Name="DirectoryTextBlock"
|
||||
Margin="5"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="16"
|
||||
Text="{Binding InstallDirectory}" />
|
||||
</Border>
|
||||
<Button
|
||||
Grid.Row="2"
|
||||
Grid.Column="2"
|
||||
Height="30"
|
||||
Margin="5"
|
||||
Padding="5"
|
||||
Click="SelectDirButton_Click"
|
||||
Content="{DynamicResource Options:SelectFolderButton}" />
|
||||
<Button
|
||||
Grid.Row="2"
|
||||
Grid.Column="3"
|
||||
Height="30"
|
||||
Margin="5"
|
||||
Padding="5"
|
||||
Click="OpenDirButton_Click"
|
||||
Content="{DynamicResource Options:OpenFolderButton}" />
|
||||
|
||||
<StackPanel
|
||||
Grid.Row="12"
|
||||
Margin="5"
|
||||
HorizontalAlignment="Left"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock FontSize="16" FontWeight="Bold">
|
||||
<TextBlock Text="{DynamicResource Options:GameType}" />
|
||||
: 
|
||||
</TextBlock>
|
||||
<TextBlock
|
||||
Name="GameTypeTextBlock"
|
||||
FontSize="16"
|
||||
Text="{Binding InstallType}" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel
|
||||
Grid.Row="13"
|
||||
Grid.ColumnSpan="2"
|
||||
Margin="5"
|
||||
HorizontalAlignment="Stretch">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Left"
|
||||
FontSize="16"
|
||||
FontWeight="Bold">
|
||||
<TextBlock Text="{DynamicResource Options:ApplicationTheme}" />
|
||||
: 
|
||||
</TextBlock>
|
||||
<ComboBox
|
||||
Name="ApplicationThemeComboBox"
|
||||
Grid.Column="2"
|
||||
Height="30"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalContentAlignment="Center"
|
||||
SelectionChanged="ApplicationThemeComboBox_SelectionChanged" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
<Button
|
||||
Name="ApplicationThemeExportTemplate"
|
||||
Grid.Row="13"
|
||||
Grid.Column="2"
|
||||
Height="30"
|
||||
Margin="5"
|
||||
Padding="5"
|
||||
Click="ApplicationThemeExportTemplate_Click"
|
||||
Content="{DynamicResource Options:ExportTemplateButton}" />
|
||||
<Button
|
||||
Name="ApplicationThemeOpenThemesFolder"
|
||||
Grid.Row="13"
|
||||
Grid.Column="3"
|
||||
Height="30"
|
||||
Margin="5"
|
||||
Padding="5"
|
||||
Click="ApplicationThemeOpenThemesFolder_Click"
|
||||
Content="{DynamicResource Options:OpenFolderButton}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="16"
|
||||
Margin="15,5,5,5"
|
||||
HorizontalAlignment="Left"
|
||||
FontSize="24"
|
||||
FontWeight="Bold"
|
||||
Text="{DynamicResource Options:Diagnostics}" />
|
||||
<StackPanel
|
||||
Grid.Row="17"
|
||||
Grid.ColumnSpan="4"
|
||||
Margin="0"
|
||||
HorizontalAlignment="Left"
|
||||
Orientation="Horizontal">
|
||||
<Button
|
||||
x:Name="OpenAppData"
|
||||
Height="30"
|
||||
Margin="5"
|
||||
Padding="5"
|
||||
Click="OpenAppDataButton_Click"
|
||||
Content="{DynamicResource Options:OpenAppDataButton}" />
|
||||
<Button
|
||||
Height="30"
|
||||
Margin="5"
|
||||
Padding="5"
|
||||
Background="{DynamicResource ButtonDangerBackground}"
|
||||
Click="YeetModsButton_Click">
|
||||
<TextBlock Foreground="White" Text="{DynamicResource Options:RemoveAllModsButton}" />
|
||||
</Button>
|
||||
<Button
|
||||
Height="30"
|
||||
Margin="5"
|
||||
Padding="5"
|
||||
Background="{DynamicResource ButtonDangerBackground}"
|
||||
Click="YeetMelonLoaderButton_Click">
|
||||
<TextBlock Foreground="White" Text="{DynamicResource Options:RemoveMLButton}" />
|
||||
</Button>
|
||||
<Button
|
||||
Height="30"
|
||||
Margin="5"
|
||||
Padding="5"
|
||||
Click="InstallMelonLoaderButton_Click">
|
||||
<TextBlock Foreground="White" Text="{DynamicResource Options:InstallMLButton}" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
</Grid>
|
||||
</Page>
|
187
VRCMelonAssistant/Pages/Options.xaml.cs
Normal file
187
VRCMelonAssistant/Pages/Options.xaml.cs
Normal file
|
@ -0,0 +1,187 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using Path = System.IO.Path;
|
||||
|
||||
namespace VRCMelonAssistant.Pages
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for Options.xaml
|
||||
/// </summary>
|
||||
public partial class Options : Page
|
||||
{
|
||||
public static Options Instance = new Options();
|
||||
|
||||
public string InstallDirectory { get; set; }
|
||||
public string InstallType { get; set; }
|
||||
public bool CloseWindowOnFinish { get; set; }
|
||||
public string LogURL { get; private set; }
|
||||
|
||||
public Options()
|
||||
{
|
||||
InitializeComponent();
|
||||
this.DataContext = this;
|
||||
}
|
||||
|
||||
private void SelectDirButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Utils.GetManualDir();
|
||||
DirectoryTextBlock.Text = InstallDirectory;
|
||||
GameTypeTextBlock.Text = InstallType;
|
||||
}
|
||||
|
||||
private void OpenDirButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Utils.OpenFolder(InstallDirectory);
|
||||
}
|
||||
|
||||
private void Test_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
MessageBox.Show(Utils.GetSteamDir());
|
||||
}
|
||||
|
||||
private void CloseWindowOnFinish_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Properties.Settings.Default.CloseWindowOnFinish = true;
|
||||
App.CloseWindowOnFinish = true;
|
||||
CloseWindowOnFinish = true;
|
||||
Properties.Settings.Default.Save();
|
||||
}
|
||||
|
||||
private void CloseWindowOnFinish_Unchecked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Properties.Settings.Default.CloseWindowOnFinish = false;
|
||||
App.CloseWindowOnFinish = false;
|
||||
CloseWindowOnFinish = false;
|
||||
Properties.Settings.Default.Save();
|
||||
}
|
||||
|
||||
private void OpenAppDataButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
string location = Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
|
||||
"AppData", "LocalLow", "VRChat");
|
||||
if (Directory.Exists(location))
|
||||
{
|
||||
Utils.OpenFolder(location);
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show((string)Application.Current.FindResource("Options:AppDataNotFound"));
|
||||
}
|
||||
}
|
||||
|
||||
private async void YeetModsButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
string title = (string)Application.Current.FindResource("Options:YeetModsBox:Title");
|
||||
string line1 = (string)Application.Current.FindResource("Options:YeetModsBox:RemoveAllMods");
|
||||
string line2 = (string)Application.Current.FindResource("Options:YeetModsBox:CannotBeUndone");
|
||||
|
||||
var resp = System.Windows.Forms.MessageBox.Show($"{line1}\n{line2}", title, System.Windows.Forms.MessageBoxButtons.YesNo);
|
||||
if (resp == System.Windows.Forms.DialogResult.Yes)
|
||||
{
|
||||
var modsDir = Path.Combine(App.VRChatInstallDirectory, "Mods");
|
||||
if (Directory.Exists(modsDir))
|
||||
Directory.Delete(modsDir, true);
|
||||
var pluginsDir = Path.Combine(App.VRChatInstallDirectory, "Plugins");
|
||||
if (Directory.Exists(pluginsDir))
|
||||
Directory.Delete(pluginsDir, true);
|
||||
|
||||
Directory.CreateDirectory(modsDir);
|
||||
Directory.CreateDirectory(pluginsDir);
|
||||
|
||||
MainWindow.Instance.MainText = $"{Application.Current.FindResource("Options:AllModsUninstalled")}...";
|
||||
}
|
||||
}
|
||||
|
||||
private async void YeetMelonLoaderButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
string title = (string)Application.Current.FindResource("Options:YeetMLBox:Title");
|
||||
string line1 = (string)Application.Current.FindResource("Options:YeetMLBox:RemoveAllMods");
|
||||
string line2 = (string)Application.Current.FindResource("Options:YeetMLBox:CannotBeUndone");
|
||||
|
||||
var resp = System.Windows.Forms.MessageBox.Show($"{line1}\n{line2}", title, System.Windows.Forms.MessageBoxButtons.YesNo);
|
||||
if (resp == System.Windows.Forms.DialogResult.Yes)
|
||||
{
|
||||
InstallHandlers.RemoveMelonLoader();
|
||||
|
||||
MainWindow.Instance.MainText = $"{Application.Current.FindResource("Options:MLUninstalled")}...";
|
||||
}
|
||||
}
|
||||
|
||||
private async void InstallMelonLoaderButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await InstallHandlers.InstallMelonLoader();
|
||||
|
||||
MainWindow.Instance.MainText = $"{Application.Current.FindResource("Options:MLInstalled")}...";
|
||||
}
|
||||
|
||||
private void ApplicationThemeComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if ((sender as ComboBox).SelectedItem == null)
|
||||
{
|
||||
Themes.ApplyWindowsTheme();
|
||||
MainWindow.Instance.MainText = (string)Application.Current.FindResource("Options:CurrentThemeRemoved");
|
||||
}
|
||||
else
|
||||
{
|
||||
Themes.ApplyTheme((sender as ComboBox).SelectedItem.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public void LanguageSelectComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if ((sender as ComboBox).SelectedItem == null)
|
||||
{
|
||||
// Apply default language
|
||||
Console.WriteLine("Applying default language");
|
||||
Languages.LoadLanguage("en");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get the matching language from the LoadedLanguages array, then try and use it
|
||||
var languageName = (sender as ComboBox).SelectedItem.ToString();
|
||||
var selectedLanguage = Languages.LoadedLanguages.Find(language => language.NativeName.CompareTo(languageName) == 0);
|
||||
if (Languages.LoadLanguage(selectedLanguage.Name))
|
||||
{
|
||||
Properties.Settings.Default.LanguageCode = selectedLanguage.Name;
|
||||
Properties.Settings.Default.Save();
|
||||
if (Languages.FirstRun)
|
||||
{
|
||||
Languages.FirstRun = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Process.Start(Utils.ExePath, App.Arguments);
|
||||
Application.Current.Dispatcher.Invoke(() => { Application.Current.Shutdown(); });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplicationThemeExportTemplate_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Themes.WriteThemeToDisk("Ugly Kulu-Ya-Ku");
|
||||
Themes.LoadThemes();
|
||||
}
|
||||
|
||||
private void ApplicationThemeOpenThemesFolder_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (Directory.Exists(Themes.ThemeDirectory))
|
||||
{
|
||||
Utils.OpenFolder(Themes.ThemeDirectory);
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show((string)Application.Current.FindResource("Options:ThemeFolderNotFound"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
using System.Reflection;
|
||||
using System.Reflection;
|
||||
using System.Resources;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
@ -7,12 +7,12 @@ using System.Windows;
|
|||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("ModAssistant")]
|
||||
[assembly: AssemblyTitle("VRChat Melon Assistant")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("ModAssistant")]
|
||||
[assembly: AssemblyCopyright("Copyright © Assistant 2019")]
|
||||
[assembly: AssemblyProduct("VRChat Melon Assistant")]
|
||||
[assembly: AssemblyCopyright("Copyright © knah 2021; based on ModAssistant © Assistant 2019-2021")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
|
@ -51,5 +51,5 @@ using System.Windows;
|
|||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.27.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.27.0")]
|
||||
[assembly: AssemblyVersion("1.1.26.1029")]
|
||||
[assembly: AssemblyFileVersion("1.1.26.1029+vrc")]
|
|
@ -1,4 +1,4 @@
|
|||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
|
@ -8,7 +8,7 @@
|
|||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace ModAssistant.Properties
|
||||
namespace VRCMelonAssistant.Properties
|
||||
{
|
||||
|
||||
|
||||
|
@ -44,7 +44,7 @@ namespace ModAssistant.Properties
|
|||
{
|
||||
if ((resourceMan == null))
|
||||
{
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ModAssistant.Properties.Resources", typeof(Resources).Assembly);
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("VRCMelonAssistant.Properties.Resources", typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
|
@ -1,17 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
|
@ -26,36 +26,36 @@
|
|||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
value : The object must be serialized with
|
||||
: System.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
|
@ -114,4 +114,4 @@
|
|||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
</root>
|
|
@ -8,11 +8,11 @@
|
|||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace ModAssistant.Properties {
|
||||
namespace VRCMelonAssistant.Properties {
|
||||
|
||||
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.1.0.0")]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.9.0.0")]
|
||||
public sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
|
||||
|
||||
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||
|
@ -47,42 +47,6 @@ namespace ModAssistant.Properties {
|
|||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("False")]
|
||||
public bool SaveSelected {
|
||||
get {
|
||||
return ((bool)(this["SaveSelected"]));
|
||||
}
|
||||
set {
|
||||
this["SaveSelected"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("False")]
|
||||
public bool CheckInstalled {
|
||||
get {
|
||||
return ((bool)(this["CheckInstalled"]));
|
||||
}
|
||||
set {
|
||||
this["CheckInstalled"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("False")]
|
||||
public string SavedMods {
|
||||
get {
|
||||
return ((string)(this["SavedMods"]));
|
||||
}
|
||||
set {
|
||||
this["SavedMods"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("False")]
|
||||
|
@ -95,42 +59,6 @@ namespace ModAssistant.Properties {
|
|||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("False")]
|
||||
public bool SelectInstalled {
|
||||
get {
|
||||
return ((bool)(this["SelectInstalled"]));
|
||||
}
|
||||
set {
|
||||
this["SelectInstalled"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("")]
|
||||
public string GameVersion {
|
||||
get {
|
||||
return ((string)(this["GameVersion"]));
|
||||
}
|
||||
set {
|
||||
this["GameVersion"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("")]
|
||||
public string AllGameVersions {
|
||||
get {
|
||||
return ((string)(this["AllGameVersions"]));
|
||||
}
|
||||
set {
|
||||
this["AllGameVersions"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("True")]
|
||||
|
@ -154,5 +82,41 @@ namespace ModAssistant.Properties {
|
|||
this["LastTab"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("")]
|
||||
public string SelectedTheme {
|
||||
get {
|
||||
return ((string)(this["SelectedTheme"]));
|
||||
}
|
||||
set {
|
||||
this["SelectedTheme"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("False")]
|
||||
public bool CloseWindowOnFinish {
|
||||
get {
|
||||
return ((bool)(this["CloseWindowOnFinish"]));
|
||||
}
|
||||
set {
|
||||
this["CloseWindowOnFinish"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("")]
|
||||
public string LanguageCode {
|
||||
get {
|
||||
return ((string)(this["LanguageCode"]));
|
||||
}
|
||||
set {
|
||||
this["LanguageCode"] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="ModAssistant.Properties" GeneratedClassName="Settings">
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="VRCMelonAssistant.Properties" GeneratedClassName="Settings">
|
||||
<Profiles />
|
||||
<Settings>
|
||||
<Setting Name="InstallFolder" Type="System.String" Scope="User">
|
||||
|
@ -8,32 +8,24 @@
|
|||
<Setting Name="StoreType" Type="System.String" Scope="User">
|
||||
<Value Profile="(Default)" />
|
||||
</Setting>
|
||||
<Setting Name="SaveSelected" Type="System.Boolean" Scope="User">
|
||||
<Value Profile="(Default)">False</Value>
|
||||
</Setting>
|
||||
<Setting Name="CheckInstalled" Type="System.Boolean" Scope="User">
|
||||
<Value Profile="(Default)">False</Value>
|
||||
</Setting>
|
||||
<Setting Name="SavedMods" Type="System.String" Scope="User">
|
||||
<Value Profile="(Default)">False</Value>
|
||||
</Setting>
|
||||
<Setting Name="Agreed" Type="System.Boolean" Scope="User">
|
||||
<Value Profile="(Default)">False</Value>
|
||||
</Setting>
|
||||
<Setting Name="SelectInstalled" Type="System.Boolean" Scope="User">
|
||||
<Value Profile="(Default)">False</Value>
|
||||
</Setting>
|
||||
<Setting Name="GameVersion" Type="System.String" Scope="User">
|
||||
<Value Profile="(Default)" />
|
||||
</Setting>
|
||||
<Setting Name="AllGameVersions" Type="System.String" Scope="User">
|
||||
<Value Profile="(Default)" />
|
||||
</Setting>
|
||||
<Setting Name="UpgradeRequired" Type="System.Boolean" Scope="User">
|
||||
<Value Profile="(Default)">True</Value>
|
||||
</Setting>
|
||||
<Setting Name="LastTab" Type="System.String" Scope="User">
|
||||
<Value Profile="(Default)" />
|
||||
</Setting>
|
||||
<Setting Name="SelectedTheme" Type="System.String" Scope="User">
|
||||
<Value Profile="(Default)" />
|
||||
</Setting>
|
||||
<Setting Name="CloseWindowOnFinish" Type="System.Boolean" Scope="User">
|
||||
<Value Profile="(Default)">False</Value>
|
||||
</Setting>
|
||||
<Setting Name="LanguageCode" Type="System.String" Scope="User">
|
||||
<Value Profile="(Default)" />
|
||||
</Setting>
|
||||
</Settings>
|
||||
</SettingsFile>
|
||||
</SettingsFile>
|
||||
|
8
VRCMelonAssistant/Resources/AppResources.xaml
Normal file
8
VRCMelonAssistant/Resources/AppResources.xaml
Normal file
|
@ -0,0 +1,8 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:VRCMelonAssistant"
|
||||
xmlns:system="clr-namespace:System;assembly=mscorlib"
|
||||
xmlns:pages="clr-namespace:VRCMelonAssistant.Pages">
|
||||
|
||||
</ResourceDictionary>
|
||||
|
50
VRCMelonAssistant/Resources/Icons.xaml
Normal file
50
VRCMelonAssistant/Resources/Icons.xaml
Normal file
|
@ -0,0 +1,50 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<Geometry x:Key="heartGeometry">F1 M512,512z M0,0z M458.4,64.3C400.6,15.7 311.3,23 256,79.3 200.7,23 111.4,15.6 53.6,64.3 -21.6,127.6 -10.6,230.8 43,285.5L218.4,464.2C228.4,474.4 241.8,480.1 256,480.1 270.3,480.1 283.6,474.5 293.6,464.3L469,285.6C522.5,230.9,533.7,127.7,458.4,64.3z M434.8,251.8L259.4,430.5C257,432.9,255,432.9,252.6,430.5L77.2,251.8C40.7,214.6 33.3,144.2 84.5,101.1 123.4,68.4 183.4,73.3 221,111.6L256,147.3 291,111.6C328.8,73.1 388.8,68.4 427.5,101 478.6,144.1 471,214.9 434.8,251.8z</Geometry>
|
||||
<DrawingGroup x:Key="heartDrawingGroup" ClipGeometry="M0,0 V512 H512 V0 H0 Z">
|
||||
<DrawingGroup.Transform>
|
||||
<TranslateTransform X="0.00011691695544868708" Y="0" />
|
||||
</DrawingGroup.Transform>
|
||||
<GeometryDrawing Brush="{DynamicResource AboutIconColor}" Geometry="{StaticResource heartGeometry}" />
|
||||
</DrawingGroup>
|
||||
<DrawingImage x:Key="heartDrawingImage" Drawing="{StaticResource heartDrawingGroup}" />
|
||||
|
||||
<Geometry x:Key="info_circleGeometry">F1 M512,512z M0,0z M256,8C119.043,8 8,119.083 8,256 8,392.997 119.043,504 256,504 392.957,504 504,392.997 504,256 504,119.083 392.957,8 256,8z M256,118C279.196,118 298,136.804 298,160 298,183.196 279.196,202 256,202 232.804,202 214,183.196 214,160 214,136.804 232.804,118 256,118z M312,372C312,378.627,306.627,384,300,384L212,384C205.373,384,200,378.627,200,372L200,348C200,341.373,205.373,336,212,336L224,336 224,272 212,272C205.373,272,200,266.627,200,260L200,236C200,229.373,205.373,224,212,224L276,224C282.627,224,288,229.373,288,236L288,336 300,336C306.627,336,312,341.373,312,348L312,372z</Geometry>
|
||||
<DrawingGroup x:Key="info_circleDrawingGroup" ClipGeometry="M0,0 V512 H512 V0 H0 Z">
|
||||
<GeometryDrawing Brush="{DynamicResource InfoIconColor}" Geometry="{StaticResource info_circleGeometry}" />
|
||||
</DrawingGroup>
|
||||
<DrawingImage x:Key="info_circleDrawingImage" Drawing="{StaticResource info_circleDrawingGroup}" />
|
||||
|
||||
<Geometry x:Key="cogGeometry">F1 M512,512z M0,0z M487.4,315.7L444.8,291.1C449.1,267.9,449.1,244.1,444.8,220.9L487.4,196.3C492.3,193.5 494.5,187.7 492.9,182.3 481.8,146.7 462.9,114.5 438.2,87.7 434.4,83.6 428.2,82.6 423.4,85.4L380.8,110C362.9,94.6,342.3,82.7,320,74.9L320,25.8C320,20.2 316.1,15.3 310.6,14.1 273.9,5.9 236.3,6.3 201.4,14.1 195.9,15.3 192,20.2 192,25.8L192,75C169.8,82.9,149.2,94.8,131.2,110.1L88.7,85.5C83.8,82.7 77.7,83.6 73.9,87.8 49.2,114.5 30.3,146.7 19.2,182.4 17.5,187.8 19.8,193.6 24.7,196.4L67.3,221C63,244.2,63,268,67.3,291.2L24.7,315.8C19.8,318.6 17.6,324.4 19.2,329.8 30.3,365.4 49.2,397.6 73.9,424.4 77.7,428.5 83.9,429.5 88.7,426.7L131.3,402.1C149.2,417.5,169.8,429.4,192.1,437.2L192.1,486.4C192.1,492 196,496.9 201.5,498.1 238.2,506.3 275.8,505.9 310.7,498.1 316.2,496.9 320.1,492 320.1,486.4L320.1,437.2C342.3,429.3,362.9,417.4,380.9,402.1L423.5,426.7C428.4,429.5 434.5,428.6 438.3,424.4 463,397.7 481.9,365.5 493,329.8 494.5,324.3 492.3,318.5 487.4,315.7z M256,336C211.9,336 176,300.1 176,256 176,211.9 211.9,176 256,176 300.1,176 336,211.9 336,256 336,300.1 300.1,336 256,336z</Geometry>
|
||||
<DrawingGroup x:Key="cogDrawingGroup" ClipGeometry="M0,0 V512 H512 V0 H0 Z">
|
||||
<GeometryDrawing Brush="{DynamicResource OptionsIconColor}" Geometry="{StaticResource cogGeometry}" />
|
||||
</DrawingGroup>
|
||||
<DrawingImage x:Key="cogDrawingImage" Drawing="{StaticResource cogDrawingGroup}" />
|
||||
|
||||
<Geometry x:Key="microchipGeometry">F1 M512,512z M0,0z M416,48L416,464C416,490.51,394.51,512,368,512L144,512C117.49,512,96,490.51,96,464L96,48C96,21.49,117.49,0,144,0L368,0C394.51,0,416,21.49,416,48z M512,106L512,118A6,6,0,0,1,506,124L488,124 488,130A6,6,0,0,1,482,136L440,136 440,88 482,88A6,6,0,0,1,488,94L488,100 506,100A6,6,0,0,1,512,106z M512,202L512,214A6,6,0,0,1,506,220L488,220 488,226A6,6,0,0,1,482,232L440,232 440,184 482,184A6,6,0,0,1,488,190L488,196 506,196A6,6,0,0,1,512,202z M512,298L512,310A6,6,0,0,1,506,316L488,316 488,322A6,6,0,0,1,482,328L440,328 440,280 482,280A6,6,0,0,1,488,286L488,292 506,292A6,6,0,0,1,512,298z M512,394L512,406A6,6,0,0,1,506,412L488,412 488,418A6,6,0,0,1,482,424L440,424 440,376 482,376A6,6,0,0,1,488,382L488,388 506,388A6,6,0,0,1,512,394z M30,376L72,376 72,424 30,424A6,6,0,0,1,24,418L24,412 6,412A6,6,0,0,1,0,406L0,394A6,6,0,0,1,6,388L24,388 24,382A6,6,0,0,1,30,376z M30,280L72,280 72,328 30,328A6,6,0,0,1,24,322L24,316 6,316A6,6,0,0,1,0,310L0,298A6,6,0,0,1,6,292L24,292 24,286A6,6,0,0,1,30,280z M30,184L72,184 72,232 30,232A6,6,0,0,1,24,226L24,220 6,220A6,6,0,0,1,0,214L0,202A6,6,0,0,1,6,196L24,196 24,190A6,6,0,0,1,30,184z M30,88L72,88 72,136 30,136A6,6,0,0,1,24,130L24,124 6,124A6,6,0,0,1,0,118L0,106A6,6,0,0,1,6,100L24,100 24,94A6,6,0,0,1,30,88z</Geometry>
|
||||
<DrawingGroup x:Key="microchipDrawingGroup" ClipGeometry="M0,0 V512 H512 V0 H0 Z">
|
||||
<GeometryDrawing Brush="{DynamicResource ModsIconColor}" Geometry="{StaticResource microchipGeometry}" />
|
||||
</DrawingGroup>
|
||||
<DrawingImage x:Key="microchipDrawingImage" Drawing="{StaticResource microchipDrawingGroup}" />
|
||||
|
||||
<Geometry x:Key="loadingOuterGeometry">M53.0751762,0.2930124c-0.3906021-0.3907-1.0234032-0.3907-1.4141006,0l-4.7492027,4.7500997
|
||||
c-0.0007973,0.0008001-0.0019989,0.0009999-0.002697,0.0016999c0,0-0.0009995,0.0019002-0.0017014,0.0027003L0.2929758,51.6696129
|
||||
c-0.3906,0.3907013-0.3906,1.0235977,0,1.414299c7.2772999,7.2787018,16.8319988,10.9170036,26.3915997,10.9161034
|
||||
c9.5555973-0.0010033,19.1151981-3.6394043,26.3906002-10.9161034c7.044899-7.0452003,10.9247971-16.4199982,10.9247971-26.3955002
|
||||
C63.9999733,16.7130127,60.1200752,7.3392124,53.0751762,0.2930124z M25.684576,55.2598114
|
||||
c-6.6767998-0.2311974-13.2832003-2.7827988-18.5009995-7.6531982L25.684576,29.1025124V55.2598114z M27.684576,29.1020126
|
||||
l18.3379002,18.3422012c0.0578003,0.0578995,0.129097,0.0895004,0.1959991,0.1301994
|
||||
c-5.2234001,4.8905983-11.8432007,7.4529991-18.5338993,7.6852989V29.1020126z M47.5670738,46.2267113
|
||||
c-0.040699-0.0671997-0.0724983-0.1386986-0.1306-0.1968002L29.0985756,27.6877117h26.1591988
|
||||
C55.0209732,34.6395111,52.3183746,41.1540108,47.5670738,46.2267113z M29.0990753,25.6873131L47.599575,7.1839123
|
||||
c4.730999,5.0665998,7.4213982,11.5671005,7.6581993,18.5034008H29.0990753z M51.6610756,51.6696129
|
||||
c-6.6669998,6.6681976-15.5371017,10.339798-24.9764996,10.339798c-9.1055012,0-17.6741009-3.4241982-24.2534008-9.6498985
|
||||
l3.3382998-3.3386002c5.8694005,5.5032005,13.3885002,8.2630005,20.9151001,8.262001
|
||||
c7.834898-0.0009995,15.6737995-2.9849014,21.6385975-8.9508018c5.777401-5.7775002,8.9589996-13.4643974,8.9589996-21.6436996
|
||||
c0-7.8449001-2.9330978-15.230299-8.2684822-20.9188004l3.3373833-3.3378999
|
||||
c6.2243004,6.5804005,9.6488991,15.1505995,9.6488991,24.2566986C61.9999733,36.129612,58.3280754,45.0014114,51.6610756,51.6696129
|
||||
z</Geometry>
|
||||
<DrawingGroup x:Key="loadingOuterDrawingGroup" ClipGeometry="M0,0 V4000 H4000 V0 H0 Z">
|
||||
<GeometryDrawing Brush="{DynamicResource LoadingIconColor}" Geometry="{StaticResource loadingOuterGeometry}" />
|
||||
</DrawingGroup>
|
||||
<DrawingImage x:Key="loadingOuterDrawingImage" Drawing="{DynamicResource loadingOuterDrawingGroup}" />
|
||||
</ResourceDictionary>
|
BIN
VRCMelonAssistant/Resources/icon.ico
Normal file
BIN
VRCMelonAssistant/Resources/icon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 361 KiB |
90
VRCMelonAssistant/Styles/Button.xaml
Normal file
90
VRCMelonAssistant/Styles/Button.xaml
Normal file
|
@ -0,0 +1,90 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<Style x:Key="DefaultButton" TargetType="Button">
|
||||
<Setter Property="Background" Value="{DynamicResource ButtonBackground}" />
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource ButtonBackground}" />
|
||||
<Setter Property="BorderThickness" Value="1" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource TextColor}" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center" />
|
||||
<Setter Property="VerticalContentAlignment" Value="Center" />
|
||||
<Setter Property="MinHeight" Value="25" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type Button}">
|
||||
<Border
|
||||
Name="Chrome"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{DynamicResource ButtonOutline}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
SnapsToDevicePixels="true"
|
||||
TextBlock.Foreground="{DynamicResource TextColor}">
|
||||
<ContentPresenter
|
||||
Name="Presenter"
|
||||
Margin="{TemplateBinding Padding}"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
RecognizesAccessKey="True"
|
||||
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
|
||||
TextBlock.Foreground="{DynamicResource TextColor}" />
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="UIElement.IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="{DynamicResource ButtonHighlightedBackground}" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource TextHighlighted}" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsPressed" Value="True">
|
||||
<Setter Property="Background" Value="{DynamicResource ButtonClickedBackground}" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter Property="Foreground" Value="{DynamicResource ButtonDisabledText}" />
|
||||
<Setter Property="Background" Value="{DynamicResource ButtonDisabledBackground}" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style BasedOn="{StaticResource DefaultButton}" TargetType="Button" />
|
||||
<Style
|
||||
x:Key="MainPageButton"
|
||||
BasedOn="{StaticResource DefaultButton}"
|
||||
TargetType="Button">
|
||||
<Setter Property="Background" Value="{DynamicResource PageButtonBackground}" />
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource PageButtonOutline}" />
|
||||
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type Button}">
|
||||
<Border
|
||||
Name="Chrome"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{DynamicResource PageButtonOutline}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
SnapsToDevicePixels="true"
|
||||
TextBlock.Foreground="{DynamicResource TextColor}">
|
||||
<ContentPresenter
|
||||
Name="Presenter"
|
||||
Margin="{TemplateBinding Padding}"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
RecognizesAccessKey="True"
|
||||
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
|
||||
TextBlock.Foreground="{DynamicResource TextColor}" />
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="UIElement.IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="{DynamicResource PageButtonHighlightedBackground}" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource TextHighlighted}" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsPressed" Value="True">
|
||||
<Setter Property="Background" Value="{DynamicResource PageButtonClickedBackground}" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter Property="Background" Value="{DynamicResource PageButtonDisabledBackground}" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
75
VRCMelonAssistant/Styles/CheckBox.xaml
Normal file
75
VRCMelonAssistant/Styles/CheckBox.xaml
Normal file
|
@ -0,0 +1,75 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<Style TargetType="CheckBox">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="CheckBox">
|
||||
<Grid
|
||||
Name="templateRoot"
|
||||
Background="{DynamicResource CheckboxDefaultOutlineColor}"
|
||||
SnapsToDevicePixels="True">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Border
|
||||
Name="checkBoxBorder"
|
||||
Margin="1"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
Background="{DynamicResource CheckboxDefaultBackground}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="0">
|
||||
<Grid Name="markGrid">
|
||||
<Path
|
||||
Name="optionMark"
|
||||
Margin="1"
|
||||
Data="F1 M9.97498,1.22334 L4.6983,9.09834 L4.52164,9.09834 L0,5.19331 L1.27664,3.52165 L4.255,6.08833 L8.33331,1.52588E-05 L9.97498,1.22334"
|
||||
Fill="{DynamicResource CheckboxTickColor}"
|
||||
Opacity="0"
|
||||
Stretch="None" />
|
||||
<Rectangle
|
||||
Name="indeterminateMark"
|
||||
Margin="2"
|
||||
Fill="{DynamicResource CheckboxTickColor}"
|
||||
Opacity="0" />
|
||||
</Grid>
|
||||
</Border>
|
||||
<ContentPresenter
|
||||
Name="contentPresenter"
|
||||
Grid.Column="1"
|
||||
Margin="{TemplateBinding Padding}"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
Focusable="False"
|
||||
RecognizesAccessKey="True"
|
||||
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
|
||||
</Grid>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="UIElement.IsMouseOver" Value="True">
|
||||
<Setter TargetName="checkBoxBorder" Property="Background" Value="{DynamicResource CheckboxHoveredBackground}" />
|
||||
<Setter TargetName="optionMark" Property="Fill" Value="{DynamicResource CheckboxHoveredTickColor}" />
|
||||
<Setter TargetName="indeterminateMark" Property="Fill" Value="{DynamicResource CheckboxHoveredTickColor}" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter TargetName="checkBoxBorder" Property="Background" Value="{DynamicResource CheckboxDisabledBackground}" />
|
||||
<Setter TargetName="checkBoxBorder" Property="BorderBrush" Value="{DynamicResource CheckboxDisabledOutlineColor}" />
|
||||
<Setter TargetName="optionMark" Property="Fill" Value="{DynamicResource CheckboxDisabledTickColor}" />
|
||||
<Setter TargetName="indeterminateMark" Property="Fill" Value="{DynamicResource CheckboxDisabledTickColor}" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsPressed" Value="True">
|
||||
<Setter TargetName="checkBoxBorder" Property="Background" Value="{DynamicResource CheckboxPressedBackground}" />
|
||||
</Trigger>
|
||||
<Trigger Property="ToggleButton.IsChecked" Value="True">
|
||||
<Setter TargetName="optionMark" Property="UIElement.Opacity" Value="1" />
|
||||
<Setter TargetName="indeterminateMark" Property="UIElement.Opacity" Value="0" />
|
||||
</Trigger>
|
||||
<Trigger Property="ToggleButton.IsChecked" Value="{x:Null}">
|
||||
<Setter TargetName="optionMark" Property="UIElement.Opacity" Value="0" />
|
||||
<Setter TargetName="indeterminateMark" Property="UIElement.Opacity" Value="1" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
103
VRCMelonAssistant/Styles/ComboBox.xaml
Normal file
103
VRCMelonAssistant/Styles/ComboBox.xaml
Normal file
|
@ -0,0 +1,103 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<Style TargetType="ComboBox">
|
||||
<Setter Property="Foreground" Value="{DynamicResource TextColor}" />
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource ComboBoxOutline}" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type ComboBox}">
|
||||
<Grid Name="templateRoot" SnapsToDevicePixels="True">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="0" MinWidth="{DynamicResource SystemParameters.VerticalScrollBarWidthKey}" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Popup
|
||||
Name="PART_Popup"
|
||||
Grid.ColumnSpan="2"
|
||||
AllowsTransparency="True"
|
||||
IsOpen="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}"
|
||||
Placement="Bottom"
|
||||
PopupAnimation="Slide">
|
||||
<Border
|
||||
Name="DropDownBorder"
|
||||
MinWidth="{Binding ActualWidth, ElementName=templateRoot}"
|
||||
Background="{DynamicResource ComboBoxBackground}"
|
||||
BorderBrush="{DynamicResource ComboBoxOutline}"
|
||||
BorderThickness="1">
|
||||
<ScrollViewer Name="DropDownScrollViewer">
|
||||
<Grid Name="grid" RenderOptions.ClearTypeHint="Enabled">
|
||||
<Canvas
|
||||
Name="canvas"
|
||||
Width="0"
|
||||
Height="0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Top">
|
||||
<Rectangle
|
||||
Name="OpaqueRect"
|
||||
MinWidth="{Binding ActualWidth, ElementName=DropDownBorder}"
|
||||
MinHeight="{Binding ActualHeight, ElementName=DropDownBorder}"
|
||||
Fill="{Binding Background, ElementName=DropDownBorder}" />
|
||||
</Canvas>
|
||||
<ItemsPresenter
|
||||
Name="ItemsPresenter"
|
||||
KeyboardNavigation.DirectionalNavigation="Contained"
|
||||
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
</Popup>
|
||||
<Border
|
||||
Name="Border"
|
||||
Margin="0"
|
||||
Background="Transparent">
|
||||
<TextBox
|
||||
Name="PART_EditableTextBox"
|
||||
Margin="2.5"
|
||||
HorizontalContentAlignment="Left"
|
||||
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
Background="Transparent"
|
||||
BorderBrush="Transparent"
|
||||
BorderThickness="0"
|
||||
Foreground="{DynamicResource TextColor}"
|
||||
IsReadOnly="True"
|
||||
Text="{TemplateBinding Text}" />
|
||||
</Border>
|
||||
<ToggleButton
|
||||
Name="toggleButton"
|
||||
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" />
|
||||
</Grid>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter TargetName="Border" Property="UIElement.Opacity" Value="0.56" />
|
||||
</Trigger>
|
||||
<Trigger Property="UIElement.IsKeyboardFocusWithin" Value="True">
|
||||
<Setter Property="Foreground" Value="#FF000000" />
|
||||
</Trigger>
|
||||
<Trigger SourceName="PART_Popup" Property="Popup.HasDropShadow" Value="True" />
|
||||
<Trigger Property="ItemsControl.HasItems" Value="False">
|
||||
<Setter TargetName="DropDownBorder" Property="Height" Value="95" />
|
||||
</Trigger>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="ItemsControl.IsGrouping" Value="True" />
|
||||
<Condition Property="VirtualizingPanel.IsVirtualizingWhenGrouping" Value="False" />
|
||||
</MultiTrigger.Conditions>
|
||||
<Setter Property="ScrollViewer.CanContentScroll" Value="False" />
|
||||
</MultiTrigger>
|
||||
<Trigger SourceName="DropDownScrollViewer" Property="CanContentScroll" Value="False">
|
||||
<Setter TargetName="OpaqueRect" Property="Canvas.Top" Value="{Binding VerticalOffset, ElementName=DropDownScrollViewer}" />
|
||||
<Setter TargetName="OpaqueRect" Property="Canvas.Left" Value="{Binding HorizontalOffset, ElementName=DropDownScrollViewer}" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="ComboBox.IsEditable" Value="True">
|
||||
<Setter Property="IsTabStop" Value="False" />
|
||||
<Setter Property="Padding" Value="2" />
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
81
VRCMelonAssistant/Styles/ComboBoxItem.xaml
Normal file
81
VRCMelonAssistant/Styles/ComboBoxItem.xaml
Normal file
|
@ -0,0 +1,81 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<Style x:Key="{x:Type ComboBoxItem}" TargetType="ComboBoxItem">
|
||||
<Setter Property="UIElement.SnapsToDevicePixels" Value="True" />
|
||||
<Setter Property="Padding" Value="4,1" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" />
|
||||
<Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" />
|
||||
<Setter Property="Background" Value="{DynamicResource ComboBoxBackground}" />
|
||||
<Setter Property="BorderBrush" Value="Transparent" />
|
||||
<Setter Property="BorderThickness" Value="0" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type ComboBoxItem}">
|
||||
<Border
|
||||
Name="Bd"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
Background="{DynamicResource ComboBoxBackground}"
|
||||
BorderBrush="{DynamicResource ComboBoxOutline}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
SnapsToDevicePixels="True">
|
||||
<ContentPresenter
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter TargetName="Bd" Property="Background" Value="{DynamicResource SystemColors.GrayTextBrushKey}" />
|
||||
</Trigger>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="ListBoxItem.IsSelected" Value="False" />
|
||||
<Condition Property="UIElement.IsMouseOver" Value="True" />
|
||||
<Condition Property="UIElement.IsKeyboardFocused" Value="False" />
|
||||
</MultiTrigger.Conditions>
|
||||
<Setter TargetName="Bd" Property="Background" Value="{DynamicResource ComboBoxHighlighted}" />
|
||||
</MultiTrigger>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="ListBoxItem.IsSelected" Value="True" />
|
||||
<Condition Property="UIElement.IsMouseOver" Value="False" />
|
||||
<Condition Property="UIElement.IsKeyboardFocused" Value="True" />
|
||||
</MultiTrigger.Conditions>
|
||||
<Setter TargetName="Bd" Property="Background" Value="{DynamicResource ComboBoxSelected}" />
|
||||
</MultiTrigger>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="ListBoxItem.IsSelected" Value="True" />
|
||||
<Condition Property="UIElement.IsMouseOver" Value="True" />
|
||||
</MultiTrigger.Conditions>
|
||||
<Setter TargetName="Bd" Property="Background" Value="{DynamicResource ComboBoxSelected}" />
|
||||
</MultiTrigger>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="ListBoxItem.IsSelected" Value="True" />
|
||||
<Condition Property="UIElement.IsMouseOver" Value="False" />
|
||||
<Condition Property="UIElement.IsKeyboardFocused" Value="False" />
|
||||
</MultiTrigger.Conditions>
|
||||
<Setter TargetName="Bd" Property="Background" Value="{DynamicResource ComboBoxSelected}" />
|
||||
</MultiTrigger>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="ListBoxItem.IsSelected" Value="False" />
|
||||
<Condition Property="UIElement.IsMouseOver" Value="False" />
|
||||
<Condition Property="UIElement.IsKeyboardFocused" Value="True" />
|
||||
</MultiTrigger.Conditions>
|
||||
<Setter TargetName="Bd" Property="Background" Value="{DynamicResource ComboBoxHighlighted}" />
|
||||
</MultiTrigger>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="ListBoxItem.IsSelected" Value="False" />
|
||||
<Condition Property="UIElement.IsMouseOver" Value="True" />
|
||||
<Condition Property="UIElement.IsKeyboardFocused" Value="True" />
|
||||
</MultiTrigger.Conditions>
|
||||
<Setter TargetName="Bd" Property="Background" Value="{DynamicResource ComboBoxHighlighted}" />
|
||||
</MultiTrigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
51
VRCMelonAssistant/Styles/GridViewColumnHeader.xaml
Normal file
51
VRCMelonAssistant/Styles/GridViewColumnHeader.xaml
Normal file
|
@ -0,0 +1,51 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<Style TargetType="GridViewColumnHeader">
|
||||
<Setter Property="GridViewColumnHeader.Foreground" Value="{DynamicResource TextColor}" />
|
||||
<Setter Property="GridViewColumnHeader.Background" Value="{DynamicResource 2ndBackgroundBrush}" />
|
||||
<Setter Property="GridViewColumnHeader.BorderBrush" Value="{DynamicResource ModColumnBorderBrush}" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type GridViewColumnHeader}">
|
||||
<Grid SnapsToDevicePixels="True">
|
||||
<Border
|
||||
Name="HeaderBorder"
|
||||
Margin="-2,-1,-1,0"
|
||||
Background="{DynamicResource ModColumnBackground}"
|
||||
BorderBrush="{DynamicResource ModColumnBorderBrush}"
|
||||
BorderThickness="0,1,0,1">
|
||||
<Grid>
|
||||
<Rectangle
|
||||
Name="UpperHighlight"
|
||||
Fill="#FFE3F7FF"
|
||||
Visibility="Collapsed" />
|
||||
<Border Grid.RowSpan="2" Padding="{TemplateBinding Padding}">
|
||||
<ContentPresenter
|
||||
Name="HeaderContent"
|
||||
Margin="0,0,0,1"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
RecognizesAccessKey="True"
|
||||
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
|
||||
</Border>
|
||||
<Border
|
||||
Name="HeaderHoverBorder"
|
||||
Margin="1,1,0,0"
|
||||
BorderThickness="1,0,1,1" />
|
||||
<Border
|
||||
Name="HeaderPressBorder"
|
||||
Margin="1,0,0,1"
|
||||
BorderThickness="1,1,1,0" />
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="UIElement.IsMouseOver" Value="True">
|
||||
<Setter TargetName="UpperHighlight" Property="Visibility" Value="Visible" />
|
||||
<Setter TargetName="UpperHighlight" Property="Fill" Value="{DynamicResource ModColumnHeaderHighlighted}" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
15
VRCMelonAssistant/Styles/Label.xaml
Normal file
15
VRCMelonAssistant/Styles/Label.xaml
Normal file
|
@ -0,0 +1,15 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<Style TargetType="Label">
|
||||
<Setter Property="Label.FontFamily" Value="{DynamicResource DefaultFontFamily}" />
|
||||
<Setter Property="Label.FontSize" Value="13" />
|
||||
<Setter Property="Label.Foreground" Value="{DynamicResource TextColor}" />
|
||||
<Setter Property="Label.Height" Value="Auto" />
|
||||
<Setter Property="Label.Width" Value="Auto" />
|
||||
<Setter Property="Label.HorizontalAlignment" Value="Center" />
|
||||
<Setter Property="Label.VerticalAlignment" Value="Center" />
|
||||
<Setter Property="Label.HorizontalContentAlignment" Value="Center" />
|
||||
<Setter Property="Label.VerticalContentAlignment" Value="Center" />
|
||||
<Setter Property="Label.FocusVisualStyle" Value="{x:Null}" />
|
||||
<Setter Property="TextOptions.TextFormattingMode" Value="Display" />
|
||||
</Style>
|
||||
</ResourceDictionary>
|
7
VRCMelonAssistant/Styles/ListView.xaml
Normal file
7
VRCMelonAssistant/Styles/ListView.xaml
Normal file
|
@ -0,0 +1,7 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<Style TargetType="ListView">
|
||||
<Setter Property="ListView.Background" Value="{DynamicResource ModListBackground}" />
|
||||
<Setter Property="ListView.BorderBrush" Value="{DynamicResource ModListBorderBrush}" />
|
||||
<Setter Property="ListView.BorderThickness" Value="1,1.1,1,1" />
|
||||
</Style>
|
||||
</ResourceDictionary>
|
42
VRCMelonAssistant/Styles/ListViewItem.xaml
Normal file
42
VRCMelonAssistant/Styles/ListViewItem.xaml
Normal file
|
@ -0,0 +1,42 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<Style TargetType="ListViewItem">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="ListViewItem">
|
||||
<Border
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="2"
|
||||
SnapsToDevicePixels="True">
|
||||
<Border
|
||||
Name="InnerBorder"
|
||||
BorderThickness="1"
|
||||
CornerRadius="1">
|
||||
<Grid>
|
||||
<Rectangle
|
||||
Name="BorderBox"
|
||||
Fill="Transparent"
|
||||
Visibility="Collapsed" />
|
||||
<GridViewRowPresenter
|
||||
Grid.RowSpan="2"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
|
||||
</Grid>
|
||||
</Border>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="{DynamicResource ModListItemHighlighted}" />
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource ModListItemHighlightedOutline}" />
|
||||
</Trigger>
|
||||
<Trigger Property="ListBoxItem.IsSelected" Value="True">
|
||||
<Setter Property="Background" Value="{DynamicResource ModListItemSelected}" />
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource ModListItemSelectedOutline}" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
8
VRCMelonAssistant/Styles/Menu.xaml
Normal file
8
VRCMelonAssistant/Styles/Menu.xaml
Normal file
|
@ -0,0 +1,8 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<Style TargetType="Menu">
|
||||
<Setter Property="Background" Value="{DynamicResource ButtonBackground}" />
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource ButtonBackground}" />
|
||||
<Setter Property="BorderThickness" Value="1" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource TextColor}" />
|
||||
</Style>
|
||||
</ResourceDictionary>
|
66
VRCMelonAssistant/Styles/MenuItem.xaml
Normal file
66
VRCMelonAssistant/Styles/MenuItem.xaml
Normal file
|
@ -0,0 +1,66 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<Style TargetType="MenuItem">
|
||||
<Setter Property="Background" Value="{DynamicResource ButtonBackground}" />
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource ButtonBackground}" />
|
||||
<Setter Property="BorderThickness" Value="1" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource TextColor}" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type MenuItem}">
|
||||
<Border
|
||||
x:Name="templateRoot"
|
||||
BorderBrush="{DynamicResource ButtonOutline}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
Padding="4"
|
||||
Background="{TemplateBinding Background}"
|
||||
SnapsToDevicePixels="True">
|
||||
<Grid VerticalAlignment="Center">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<ContentPresenter x:Name="Icon" Content="{TemplateBinding Icon}" ContentSource="Icon" HorizontalAlignment="Center" Height="16" Margin="3" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center" Width="16"/>
|
||||
<Path x:Name="GlyphPanel" Data="F1M10,1.2L4.7,9.1 4.5,9.1 0,5.2 1.3,3.5 4.3,6.1 8.3,0 10,1.2z" Fill="{TemplateBinding Foreground}" FlowDirection="LeftToRight" Margin="3" Visibility="Collapsed" VerticalAlignment="Center"/>
|
||||
<ContentPresenter ContentTemplate="{TemplateBinding HeaderTemplate}" Content="{TemplateBinding Header}" Grid.Column="1" ContentStringFormat="{TemplateBinding HeaderStringFormat}" ContentSource="Header" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
|
||||
<Popup x:Name="PART_Popup" AllowsTransparency="True" Focusable="False" IsOpen="{Binding IsSubmenuOpen, RelativeSource={RelativeSource TemplatedParent}}" PopupAnimation="{DynamicResource {x:Static SystemParameters.MenuPopupAnimationKey}}" Placement="Bottom">
|
||||
<ScrollViewer x:Name="SubMenuScrollViewer" Style="{DynamicResource {ComponentResourceKey ResourceId=MenuScrollViewer, TypeInTargetAssembly={x:Type FrameworkElement}}}">
|
||||
<Grid RenderOptions.ClearTypeHint="Enabled">
|
||||
<Canvas HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="1">
|
||||
<Rectangle x:Name="OpaqueRect" Fill="Black" Height="{Binding ActualHeight, ElementName=SubMenuBorder}" Width="{Binding ActualWidth, ElementName=SubMenuBorder}"/>
|
||||
</Canvas>
|
||||
<Rectangle Fill="#FFD7D7D7" HorizontalAlignment="Left" Margin="29,2,0,2" Width="1" />
|
||||
<ItemsPresenter x:Name="ItemsPresenter" Margin="0,0,0,0" KeyboardNavigation.DirectionalNavigation="Cycle" Grid.IsSharedSizeScope="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" KeyboardNavigation.TabNavigation="Cycle"/>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</Popup>
|
||||
</Grid>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsSuspendingPopupAnimation" Value="True">
|
||||
<Setter Property="PopupAnimation" TargetName="PART_Popup" Value="None"/>
|
||||
</Trigger>
|
||||
<Trigger Property="Icon" Value="{x:Null}">
|
||||
<Setter Property="Visibility" TargetName="Icon" Value="Collapsed"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsChecked" Value="True">
|
||||
<Setter Property="Visibility" TargetName="GlyphPanel" Value="Visible"/>
|
||||
<Setter Property="Visibility" TargetName="Icon" Value="Collapsed"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsHighlighted" Value="True">
|
||||
<Setter Property="Background" Value="{DynamicResource ButtonHighlightedBackground}" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource TextHighlighted}" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter Property="TextElement.Foreground" TargetName="templateRoot" Value="#FF707070"/>
|
||||
<Setter Property="Fill" TargetName="GlyphPanel" Value="#FF707070"/>
|
||||
</Trigger>
|
||||
<Trigger Property="CanContentScroll" SourceName="SubMenuScrollViewer" Value="False">
|
||||
<Setter Property="Canvas.Top" TargetName="OpaqueRect" Value="{Binding VerticalOffset, ElementName=SubMenuScrollViewer}"/>
|
||||
<Setter Property="Canvas.Left" TargetName="OpaqueRect" Value="{Binding HorizontalOffset, ElementName=SubMenuScrollViewer}"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
58
VRCMelonAssistant/Styles/RepeatButton.xaml
Normal file
58
VRCMelonAssistant/Styles/RepeatButton.xaml
Normal file
|
@ -0,0 +1,58 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<Style x:Key="VRCMelonAssistantRepeatButton" TargetType="{x:Type RepeatButton}">
|
||||
<Setter Property="BorderThickness" Value="1" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center" />
|
||||
<Setter Property="VerticalContentAlignment" Value="Center" />
|
||||
<Setter Property="Padding" Value="1" />
|
||||
<Setter Property="Focusable" Value="False" />
|
||||
<Setter Property="IsTabStop" Value="False" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type RepeatButton}">
|
||||
<Border
|
||||
Name="border"
|
||||
Background="{DynamicResource ScrollBarBackground}"
|
||||
BorderBrush="{StaticResource ScrollBarBorder}"
|
||||
BorderThickness="1"
|
||||
SnapsToDevicePixels="True">
|
||||
<ContentPresenter
|
||||
Name="contentPresenter"
|
||||
Margin="{TemplateBinding Padding}"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
Focusable="False"
|
||||
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="UIElement.IsMouseOver" Value="True">
|
||||
<Setter TargetName="border" Property="Background" Value="{DynamicResource ScrollBarArrowHovered}" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsPressed" Value="True">
|
||||
<Setter TargetName="border" Property="Background" Value="{DynamicResource ScrollBarArrowClicked}" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter TargetName="contentPresenter" Property="UIElement.Opacity" Value="0.56" />
|
||||
<Setter TargetName="border" Property="Background" Value="{DynamicResource ScrollBarDisabled}" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style x:Key="VRCMelonAssistantSmallRepeatButton" TargetType="{x:Type RepeatButton}">
|
||||
<Setter Property="FrameworkElement.OverridesDefaultStyle" Value="True" />
|
||||
<Setter Property="Background" Value="{DynamicResource ScrollBarBackground}" />
|
||||
<Setter Property="Focusable" Value="False" />
|
||||
<Setter Property="IsTabStop" Value="False" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type RepeatButton}">
|
||||
<Rectangle
|
||||
Width="{TemplateBinding Width}"
|
||||
Height="{TemplateBinding Height}"
|
||||
Fill="{TemplateBinding Background}" />
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
215
VRCMelonAssistant/Styles/ScrollBar.xaml
Normal file
215
VRCMelonAssistant/Styles/ScrollBar.xaml
Normal file
|
@ -0,0 +1,215 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<Style x:Key="{x:Type ScrollBar}" TargetType="{x:Type ScrollBar}">
|
||||
<Setter Property="Stylus.IsPressAndHoldEnabled" Value="False" />
|
||||
<Setter Property="Stylus.IsFlicksEnabled" Value="False" />
|
||||
<Setter Property="Background" Value="{DynamicResource ScrollBarBackground}" />
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource ScrollBarBorder}" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource ScrollBarTextColor}" />
|
||||
<Setter Property="BorderThickness" Value="1,0" />
|
||||
<Setter Property="Width" Value="{DynamicResource SystemParameters.VerticalScrollBarWidthKey}" />
|
||||
<Setter Property="MinWidth" Value="16" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type ScrollBar}">
|
||||
<Grid Name="Bg" SnapsToDevicePixels="True">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="{DynamicResource ResourceKey=ScrollBarButtonHeight}" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="{DynamicResource ResourceKey=ScrollBarButtonHeight}" />
|
||||
</Grid.RowDefinitions>
|
||||
<Border
|
||||
Grid.Row="1"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}" />
|
||||
<RepeatButton
|
||||
Name="PART_LineUpButton"
|
||||
Command="{x:Static ScrollBar.LineUpCommand}"
|
||||
IsEnabled="{TemplateBinding IsMouseOver}"
|
||||
Style="{DynamicResource ResourceKey=VRCMelonAssistantRepeatButton}">
|
||||
<Path
|
||||
Name="ArrowTop"
|
||||
Margin="3,4,3,3"
|
||||
Data="M0,4 C0,4 0,6 0,6 C0,6 3.5,2.5 3.5,2.5 C3.5,2.5 7,6 7,6 C7,6 7,4 7,4 C7,4 3.5,0.5 3.5,0.5 C3.5,0.5 0,4 0,4"
|
||||
Fill="{DynamicResource ScrollBarArrowColor}"
|
||||
Stretch="Uniform" />
|
||||
</RepeatButton>
|
||||
<Track
|
||||
Name="PART_Track"
|
||||
Grid.Row="1"
|
||||
IsDirectionReversed="True"
|
||||
IsEnabled="{TemplateBinding IsMouseOver}">
|
||||
<Track.DecreaseRepeatButton>
|
||||
<RepeatButton
|
||||
Background="{DynamicResource ScrollBarBackground}"
|
||||
Command="{x:Static ScrollBar.PageUpCommand}"
|
||||
Style="{DynamicResource ResourceKey=VRCMelonAssistantSmallRepeatButton}" />
|
||||
</Track.DecreaseRepeatButton>
|
||||
<Track.IncreaseRepeatButton>
|
||||
<RepeatButton
|
||||
Background="{DynamicResource ScrollBarBackground}"
|
||||
Command="{x:Static ScrollBar.PageDownCommand}"
|
||||
Style="{DynamicResource ResourceKey=VRCMelonAssistantSmallRepeatButton}" />
|
||||
</Track.IncreaseRepeatButton>
|
||||
<Track.Thumb>
|
||||
<Thumb Background="{DynamicResource ScrollBarHandle}" Style="{DynamicResource ResourceKey=VRCMelonAssistantThumb}" />
|
||||
</Track.Thumb>
|
||||
</Track>
|
||||
<RepeatButton
|
||||
Name="PART_LineDownButton"
|
||||
Grid.Row="2"
|
||||
Command="{x:Static ScrollBar.LineDownCommand}"
|
||||
IsEnabled="{TemplateBinding IsMouseOver}"
|
||||
Style="{DynamicResource ResourceKey=VRCMelonAssistantRepeatButton}">
|
||||
<Path
|
||||
Name="ArrowBottom"
|
||||
Margin="3,4,3,3"
|
||||
Data="M0,2.5 C0,2.5 0,0.5 0,0.5 C0,0.5 3.5,4 3.5,4 C3.5,4 7,0.5 7,0.5 C7,0.5 7,2.5 7,2.5 C7,2.5 3.5,6 3.5,6 C3.5,6 0,2.5 0,2.5"
|
||||
Fill="{DynamicResource ScrollBarArrowColor}"
|
||||
Stretch="Uniform" />
|
||||
</RepeatButton>
|
||||
</Grid>
|
||||
<ControlTemplate.Triggers>
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding ElementName=PART_LineDownButton, Path=IsMouseOver}" Value="true" />
|
||||
<Condition Binding="{Binding ElementName=PART_LineDownButton, Path=IsPressed}" Value="true" />
|
||||
</MultiDataTrigger.Conditions>
|
||||
<Setter TargetName="ArrowBottom" Property="Fill" Value="{DynamicResource ScrollBarArrowClicked}" />
|
||||
</MultiDataTrigger>
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding ElementName=PART_LineUpButton, Path=IsMouseOver}" Value="true" />
|
||||
<Condition Binding="{Binding ElementName=PART_LineUpButton, Path=IsPressed}" Value="true" />
|
||||
</MultiDataTrigger.Conditions>
|
||||
<Setter TargetName="ArrowTop" Property="Fill" Value="{DynamicResource ScrollBarArrowClicked}" />
|
||||
</MultiDataTrigger>
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding ElementName=PART_LineDownButton, Path=IsMouseOver}" Value="true" />
|
||||
<Condition Binding="{Binding ElementName=PART_LineDownButton, Path=IsPressed}" Value="false" />
|
||||
</MultiDataTrigger.Conditions>
|
||||
<Setter TargetName="ArrowBottom" Property="Fill" Value="{DynamicResource ScrollBarArrowHovered}" />
|
||||
</MultiDataTrigger>
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding ElementName=PART_LineUpButton, Path=IsMouseOver}" Value="true" />
|
||||
<Condition Binding="{Binding ElementName=PART_LineUpButton, Path=IsPressed}" Value="false" />
|
||||
</MultiDataTrigger.Conditions>
|
||||
<Setter TargetName="ArrowTop" Property="Fill" Value="{DynamicResource ScrollBarArrowHovered}" />
|
||||
</MultiDataTrigger>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter TargetName="ArrowTop" Property="Fill" Value="{DynamicResource ScrollBarDisabled}" />
|
||||
<Setter TargetName="ArrowBottom" Property="Fill" Value="{DynamicResource ScrollBarDisabled}" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="ScrollBar.Orientation" Value="Horizontal">
|
||||
<Setter Property="Width" Value="Auto" />
|
||||
<Setter Property="MinWidth" Value="0" />
|
||||
<Setter Property="Height" Value="{DynamicResource SystemParameters.HorizontalScrollBarHeightKey}" />
|
||||
<Setter Property="MinHeight" Value="{DynamicResource SystemParameters.HorizontalScrollBarHeightKey}" />
|
||||
<Setter Property="BorderThickness" Value="0,1" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type ScrollBar}">
|
||||
<Grid Name="Bg" SnapsToDevicePixels="True">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="{DynamicResource ResourceKey=ScrollBarButtonHeight}" />
|
||||
<ColumnDefinition Width="0.00001*" />
|
||||
<ColumnDefinition Width="{DynamicResource ResourceKey=ScrollBarButtonHeight}" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Border
|
||||
Grid.Column="1"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}" />
|
||||
<RepeatButton
|
||||
Name="PART_LineLeftButton"
|
||||
Command="{x:Static ScrollBar.LineLeftCommand}"
|
||||
IsEnabled="{TemplateBinding IsMouseOver}"
|
||||
Style="{DynamicResource ResourceKey=VRCMelonAssistantRepeatButton}">
|
||||
<Path
|
||||
Name="ArrowLeft"
|
||||
Margin="3"
|
||||
Data="M3.18,7 C3.18,7 5,7 5,7 C5,7 1.81,3.5 1.81,3.5 C1.81,3.5 5,0 5,0 C5,0 3.18,0 3.18,0 C3.18,0 0,3.5 0,3.5 C0,3.5 3.18,7 3.18,7"
|
||||
Fill="{DynamicResource ScrollBarArrowColor}"
|
||||
Stretch="Uniform" />
|
||||
</RepeatButton>
|
||||
<Track
|
||||
Name="PART_Track"
|
||||
Grid.Column="1"
|
||||
IsEnabled="{TemplateBinding IsMouseOver}">
|
||||
<Track.DecreaseRepeatButton>
|
||||
<RepeatButton
|
||||
Background="{DynamicResource ScrollBarBackground}"
|
||||
Command="{x:Static ScrollBar.PageLeftCommand}"
|
||||
Style="{DynamicResource ResourceKey=VRCMelonAssistantSmallRepeatButton}" />
|
||||
</Track.DecreaseRepeatButton>
|
||||
<Track.IncreaseRepeatButton>
|
||||
<RepeatButton
|
||||
Background="{DynamicResource ScrollBarBackground}"
|
||||
Command="{x:Static ScrollBar.PageRightCommand}"
|
||||
Style="{DynamicResource ResourceKey=VRCMelonAssistantSmallRepeatButton}" />
|
||||
</Track.IncreaseRepeatButton>
|
||||
<Track.Thumb>
|
||||
<Thumb Background="{DynamicResource ScrollBarHandle}" Style="{DynamicResource ResourceKey=VRCMelonAssistantThumb}" />
|
||||
</Track.Thumb>
|
||||
</Track>
|
||||
<RepeatButton
|
||||
Name="PART_LineRightButton"
|
||||
Grid.Column="2"
|
||||
Command="{x:Static ScrollBar.LineRightCommand}"
|
||||
IsEnabled="{TemplateBinding IsMouseOver}"
|
||||
Style="{DynamicResource ResourceKey=VRCMelonAssistantRepeatButton}">
|
||||
<Path
|
||||
Name="ArrowRight"
|
||||
Margin="3"
|
||||
Data="M1.81,7 C1.81,7 0,7 0,7 C0,7 3.18,3.5 3.18,3.5 C3.18,3.5 0,0 0,0 C0,0 1.81,0 1.81,0 C1.81,0 5,3.5 5,3.5 C5,3.5 1.81,7 1.81,7"
|
||||
Fill="{DynamicResource ScrollBarArrowColor}"
|
||||
Stretch="Uniform" />
|
||||
</RepeatButton>
|
||||
</Grid>
|
||||
<ControlTemplate.Triggers>
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding ElementName=PART_LineRightButton, Path=IsMouseOver}" Value="true" />
|
||||
<Condition Binding="{Binding ElementName=PART_LineRightButton, Path=IsPressed}" Value="true" />
|
||||
</MultiDataTrigger.Conditions>
|
||||
<Setter TargetName="ArrowRight" Property="Fill" Value="{DynamicResource ScrollBarArrowClicked}" />
|
||||
</MultiDataTrigger>
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding ElementName=PART_LineLeftButton, Path=IsMouseOver}" Value="true" />
|
||||
<Condition Binding="{Binding ElementName=PART_LineLeftButton, Path=IsPressed}" Value="true" />
|
||||
</MultiDataTrigger.Conditions>
|
||||
<Setter TargetName="ArrowLeft" Property="Fill" Value="{DynamicResource ScrollBarArrowClicked}" />
|
||||
</MultiDataTrigger>
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding ElementName=PART_LineRightButton, Path=IsMouseOver}" Value="true" />
|
||||
<Condition Binding="{Binding ElementName=PART_LineRightButton, Path=IsPressed}" Value="false" />
|
||||
</MultiDataTrigger.Conditions>
|
||||
<Setter TargetName="ArrowRight" Property="Fill" Value="{DynamicResource ScrollBarArrowHovered}" />
|
||||
</MultiDataTrigger>
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding ElementName=PART_LineLeftButton, Path=IsMouseOver}" Value="true" />
|
||||
<Condition Binding="{Binding ElementName=PART_LineLeftButton, Path=IsPressed}" Value="false" />
|
||||
</MultiDataTrigger.Conditions>
|
||||
<Setter TargetName="ArrowLeft" Property="Fill" Value="{DynamicResource ScrollBarArrowHovered}" />
|
||||
</MultiDataTrigger>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter TargetName="ArrowLeft" Property="Fill" Value="{DynamicResource ScrollBarDisabled}" />
|
||||
<Setter TargetName="ArrowRight" Property="Fill" Value="{DynamicResource ScrollBarDisabled}" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
7
VRCMelonAssistant/Styles/TextBlock.xaml
Normal file
7
VRCMelonAssistant/Styles/TextBlock.xaml
Normal file
|
@ -0,0 +1,7 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<Style TargetType="TextBlock">
|
||||
<Setter Property="TextBlock.Foreground" Value="{DynamicResource TextColor}" />
|
||||
<Setter Property="TextWrapping" Value="NoWrap" />
|
||||
<Setter Property="TextTrimming" Value="None" />
|
||||
</Style>
|
||||
</ResourceDictionary>
|
26
VRCMelonAssistant/Styles/Thumb.xaml
Normal file
26
VRCMelonAssistant/Styles/Thumb.xaml
Normal file
|
@ -0,0 +1,26 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<Style x:Key="VRCMelonAssistantThumb" TargetType="{x:Type Thumb}">
|
||||
<Setter Property="FrameworkElement.OverridesDefaultStyle" Value="True" />
|
||||
<Setter Property="IsTabStop" Value="False" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type Thumb}">
|
||||
<Rectangle
|
||||
Name="rectangle"
|
||||
Width="{TemplateBinding Width}"
|
||||
Height="{TemplateBinding Height}"
|
||||
Fill="{DynamicResource ScrollBarHandle}"
|
||||
SnapsToDevicePixels="True" />
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="UIElement.IsMouseOver" Value="True">
|
||||
<Setter TargetName="rectangle" Property="Fill" Value="{DynamicResource ScrollBarHandleHovered}" />
|
||||
</Trigger>
|
||||
<Trigger Property="Thumb.IsDragging" Value="True">
|
||||
<Setter TargetName="rectangle" Property="Fill" Value="{DynamicResource ScrollBarHandleClick}" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
80
VRCMelonAssistant/Styles/ToggleButton.xaml
Normal file
80
VRCMelonAssistant/Styles/ToggleButton.xaml
Normal file
|
@ -0,0 +1,80 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<Style TargetType="{x:Type ToggleButton}">
|
||||
<Setter Property="Foreground" Value="{DynamicResource SystemColors.ControlTextBrushKey}" />
|
||||
<Setter Property="Padding" Value="2" />
|
||||
<Setter Property="BorderThickness" Value="1" />
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource ComboBoxOutline}" />
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch" />
|
||||
<Setter Property="VerticalAlignment" Value="Stretch" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center" />
|
||||
<Setter Property="VerticalContentAlignment" Value="Center" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type ToggleButton}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="ComboBoxButton" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Border
|
||||
Padding="{TemplateBinding Padding}"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalAlignment}"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
Opacity="0.5"
|
||||
SnapsToDevicePixels="True">
|
||||
<Path
|
||||
Name="ArrowDownPath"
|
||||
Margin="5"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Data="M2.5,0 L8.5,0 L5.5,3"
|
||||
Fill="{DynamicResource ComboBoxArrow}" />
|
||||
</Border>
|
||||
</Grid>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="ToggleButton.IsChecked" Value="true">
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource ComboBoxOutline}" />
|
||||
<Setter Property="Background" Value="{DynamicResource ComboBoxSelected}" />
|
||||
</Trigger>
|
||||
<Trigger Property="UIElement.IsMouseOver" Value="True">
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource ComboBoxHighlighted}" />
|
||||
<Setter Property="Background" Value="{DynamicResource ComboBoxHighlighted}" />
|
||||
</Trigger>
|
||||
<Trigger Property="UIElement.IsKeyboardFocused" Value="True">
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource ComboBoxHighlighted}" />
|
||||
<Setter Property="Background" Value="{DynamicResource ComboBoxHighlighted}" />
|
||||
</Trigger>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="UIElement.IsMouseOver" Value="True" />
|
||||
<Condition Property="ToggleButton.IsChecked" Value="true" />
|
||||
</MultiTrigger.Conditions>
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource ComboBoxHighlighted}" />
|
||||
<Setter Property="Background" Value="{DynamicResource ComboBoxSelected}" />
|
||||
</MultiTrigger>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="UIElement.IsKeyboardFocused" Value="True" />
|
||||
<Condition Property="ToggleButton.IsChecked" Value="true" />
|
||||
</MultiTrigger.Conditions>
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource ComboBoxHighlighted}" />
|
||||
<Setter Property="Background" Value="{DynamicResource ComboBoxSelected}" />
|
||||
</MultiTrigger>
|
||||
<Trigger Property="IsPressed" Value="True">
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource ComboBoxClicked}" />
|
||||
<Setter Property="Background" Value="{DynamicResource ComboBoxClicked}" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter Property="Foreground" Value="{DynamicResource SystemColors.GrayTextBrushKey}" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
</ResourceDictionary>
|
97
VRCMelonAssistant/Themes/Dark.xaml
Normal file
97
VRCMelonAssistant/Themes/Dark.xaml
Normal file
|
@ -0,0 +1,97 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
|
||||
<!-- VRChat Melon Assistant Dark Theme by Caeden117 and lolPants -->
|
||||
|
||||
<!-- Standard Styles -->
|
||||
<Color x:Key="StandardContent">#E0E0E0</Color>
|
||||
<Color x:Key="StandardPrimary">#2E2E2E</Color>
|
||||
<Color x:Key="StandardSecondary">#0F0F0F</Color>
|
||||
<Color x:Key="StandardBorder">#696969</Color>
|
||||
<Color x:Key="StandardHighlight">#454545</Color>
|
||||
<Color x:Key="StandardActive">#696969</Color>
|
||||
<Color x:Key="StandardIcon">#AEAEAE</Color>
|
||||
|
||||
<!-- Default Text -->
|
||||
<SolidColorBrush x:Key="TextColor" Color="{DynamicResource ResourceKey=StandardContent}" />
|
||||
<SolidColorBrush x:Key="TextHighlighted" Color="White" />
|
||||
|
||||
<!-- Buttons (Info/Mods/About/Options as well as More Info and Install/Update) -->
|
||||
<SolidColorBrush x:Key="ButtonBackground" Color="{DynamicResource ResourceKey=StandardPrimary}" />
|
||||
<SolidColorBrush x:Key="ButtonOutline" Color="{DynamicResource ResourceKey=StandardBorder}" />
|
||||
<SolidColorBrush x:Key="ButtonHighlightedBackground" Color="{DynamicResource ResourceKey=StandardHighlight}" />
|
||||
<SolidColorBrush x:Key="ButtonClickedBackground" Color="{DynamicResource ResourceKey=StandardActive}" />
|
||||
<SolidColorBrush x:Key="ButtonDisabledBackground" Color="{DynamicResource ResourceKey=StandardSecondary}" />
|
||||
<SolidColorBrush x:Key="ButtonDangerBackground" Color="#ED172F" />
|
||||
|
||||
<!-- Page Buttons (Side of Main Page) -->
|
||||
<SolidColorBrush x:Key="PageButtonBackground" Color="{DynamicResource ResourceKey=StandardPrimary}" />
|
||||
<SolidColorBrush x:Key="PageButtonOutline" Color="{DynamicResource ResourceKey=StandardBorder}" />
|
||||
<SolidColorBrush x:Key="PageButtonHighlightedBackground" Color="{DynamicResource ResourceKey=StandardHighlight}" />
|
||||
<SolidColorBrush x:Key="PageButtonClickedBackground" Color="{DynamicResource ResourceKey=StandardActive}" />
|
||||
<SolidColorBrush x:Key="PageButtonDisabledBackground" Color="{DynamicResource ResourceKey=StandardSecondary}" />
|
||||
|
||||
<!-- Mod List -->
|
||||
<SolidColorBrush x:Key="ModColumnBackground" Color="{DynamicResource ResourceKey=StandardPrimary}" />
|
||||
<SolidColorBrush x:Key="ModColumnBorderBrush" Color="{DynamicResource ResourceKey=StandardBorder}" />
|
||||
<SolidColorBrush x:Key="ModColumnHeaderHighlighted" Color="{DynamicResource ResourceKey=StandardHighlight}" />
|
||||
<SolidColorBrush x:Key="ModListBackground" Color="#10FFFFFF" />
|
||||
<SolidColorBrush x:Key="ModListBorderBrush" Color="{DynamicResource ResourceKey=StandardBorder}" />
|
||||
<SolidColorBrush x:Key="ModListItemHighlighted" Color="#70404040" />
|
||||
<SolidColorBrush x:Key="ModListItemHighlightedOutline" Color="#40FFFFFF" />
|
||||
<SolidColorBrush x:Key="ModListItemSelected" Color="#FF353535" />
|
||||
<SolidColorBrush x:Key="ModListItemSelectedOutline" Color="#40FFFFFF" />
|
||||
|
||||
<!-- Combo Box (Version select) -->
|
||||
<SolidColorBrush x:Key="ComboBoxBackground" Color="{DynamicResource ResourceKey=StandardPrimary}" />
|
||||
<SolidColorBrush x:Key="ComboBoxOutline" Color="{DynamicResource ResourceKey=StandardBorder}" />
|
||||
<SolidColorBrush x:Key="ComboBoxHighlighted" Color="{DynamicResource ResourceKey=StandardHighlight}" />
|
||||
<SolidColorBrush x:Key="ComboBoxSelected" Color="{DynamicResource ResourceKey=StandardActive}" />
|
||||
<SolidColorBrush x:Key="ComboBoxClicked" Color="#A2A2A2" />
|
||||
<SolidColorBrush x:Key="ComboBoxArrow" Color="White" />
|
||||
|
||||
<!-- Checkboxes (Mod List and Options) -->
|
||||
<SolidColorBrush x:Key="CheckboxDefaultBackground" Color="{DynamicResource ResourceKey=StandardPrimary}" />
|
||||
<SolidColorBrush x:Key="CheckboxDefaultOutlineColor" Color="{DynamicResource ResourceKey=StandardBorder}" />
|
||||
<SolidColorBrush x:Key="CheckboxDisabledBackground" Color="#1A1A1A" />
|
||||
<SolidColorBrush x:Key="CheckboxDisabledOutlineColor" Color="#2A2A2A" />
|
||||
<SolidColorBrush x:Key="CheckboxDisabledTickColor" Color="#66FFFFFF" />
|
||||
<SolidColorBrush x:Key="CheckboxHoveredBackground" Color="#696969" />
|
||||
<SolidColorBrush x:Key="CheckboxHoveredTickColor" Color="White" />
|
||||
<SolidColorBrush x:Key="CheckboxTickColor" Color="{DynamicResource ResourceKey=StandardContent}" />
|
||||
<SolidColorBrush x:Key="CheckboxPressedBackground" Color="#FFBFBFBF" />
|
||||
|
||||
<!-- Scroll Bars -->
|
||||
<SolidColorBrush x:Key="ScrollBarBackground" Color="{DynamicResource ResourceKey=StandardSecondary}" />
|
||||
<SolidColorBrush x:Key="ScrollBarBorder" Color="{DynamicResource ResourceKey=StandardBorder}" />
|
||||
<SolidColorBrush x:Key="ScrollBarTextColor" Color="{DynamicResource ResourceKey=StandardPrimary}" />
|
||||
<SolidColorBrush x:Key="ScrollBarDisabled" Color="{DynamicResource ResourceKey=StandardSecondary}" />
|
||||
<SolidColorBrush x:Key="ScrollBarArrowColor" Color="{DynamicResource ResourceKey=StandardActive}" />
|
||||
<SolidColorBrush x:Key="ScrollBarArrowClicked" Color="{DynamicResource ResourceKey=StandardPrimary}" />
|
||||
<SolidColorBrush x:Key="ScrollBarArrowHovered" Color="{DynamicResource ResourceKey=StandardHighlight}" />
|
||||
<SolidColorBrush x:Key="ScrollBarHandle" Color="{DynamicResource ResourceKey=StandardPrimary}" />
|
||||
<SolidColorBrush x:Key="ScrollBarHandleHovered" Color="{DynamicResource ResourceKey=StandardHighlight}" />
|
||||
<SolidColorBrush x:Key="ScrollBarHandleClick" Color="#55FFFFFF" />
|
||||
<GridLength x:Key="ScrollBarButtonHeight">0</GridLength>
|
||||
|
||||
<!-- Various important elements that need to be controlled independently -->
|
||||
<SolidColorBrush x:Key="VRCMelonAssistantBackground" Color="{DynamicResource ResourceKey=StandardSecondary}" />
|
||||
<SolidColorBrush x:Key="FrameBackgroundColor" Color="#CC0F0F0F" />
|
||||
<SolidColorBrush x:Key="BottomStatusBarBackground" Color="{DynamicResource ResourceKey=StandardPrimary}" />
|
||||
<SolidColorBrush x:Key="BottomStatusBarOutline" Color="{DynamicResource ResourceKey=StandardBorder}" />
|
||||
<SolidColorBrush x:Key="DirectoryBackground" Color="{DynamicResource ResourceKey=StandardPrimary}" />
|
||||
<SolidColorBrush x:Key="DirectoryOutline" Color="{DynamicResource ResourceKey=StandardBorder}" />
|
||||
|
||||
<!-- Colors for the corresponding icons -->
|
||||
<SolidColorBrush x:Key="InfoIconColor" Color="{DynamicResource ResourceKey=StandardIcon}" />
|
||||
<SolidColorBrush x:Key="ModsIconColor" Color="{DynamicResource ResourceKey=StandardIcon}" />
|
||||
<SolidColorBrush x:Key="AboutIconColor" Color="{DynamicResource ResourceKey=StandardIcon}" />
|
||||
<SolidColorBrush x:Key="OptionsIconColor" Color="{DynamicResource ResourceKey=StandardIcon}" />
|
||||
<SolidColorBrush x:Key="LoadingIconColor" Color="{DynamicResource ResourceKey=StandardContent}" />
|
||||
|
||||
<!-- Background and Side image settings. -->
|
||||
<!-- Fill, None, Uniform, UniformToFill -->
|
||||
<Stretch x:Key="BackgroundImageStretch">UniformToFill</Stretch>
|
||||
<!-- Bottom, Center, Top, Stretch -->
|
||||
<VerticalAlignment x:Key="SideImageYPosition">Bottom</VerticalAlignment>
|
||||
|
||||
</ResourceDictionary>
|
16
VRCMelonAssistant/Themes/Default Scrollbar.xaml
Normal file
16
VRCMelonAssistant/Themes/Default Scrollbar.xaml
Normal file
|
@ -0,0 +1,16 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<!-- This is a default theme for the Scrollbars, in case the provided Theme doesn't have them -->
|
||||
|
||||
<!-- Scroll Bars -->
|
||||
<SolidColorBrush x:Key="ScrollBarBackground" Color="{DynamicResource ResourceKey=StandardSecondary}" />
|
||||
<SolidColorBrush x:Key="ScrollBarBorder" Color="{DynamicResource ResourceKey=StandardBorder}" />
|
||||
<SolidColorBrush x:Key="ScrollBarTextColor" Color="{DynamicResource ResourceKey=StandardPrimary}" />
|
||||
<SolidColorBrush x:Key="ScrollBarDisabled" Color="{DynamicResource ResourceKey=StandardSecondary}" />
|
||||
<SolidColorBrush x:Key="ScrollBarArrowColor" Color="{DynamicResource ResourceKey=StandardActive}" />
|
||||
<SolidColorBrush x:Key="ScrollBarArrowClicked" Color="{DynamicResource ResourceKey=StandardPrimary}" />
|
||||
<SolidColorBrush x:Key="ScrollBarArrowHovered" Color="{DynamicResource ResourceKey=StandardHighlight}" />
|
||||
<SolidColorBrush x:Key="ScrollBarHandle" Color="{DynamicResource ResourceKey=StandardPrimary}" />
|
||||
<SolidColorBrush x:Key="ScrollBarHandleHovered" Color="{DynamicResource ResourceKey=StandardHighlight}" />
|
||||
<SolidColorBrush x:Key="ScrollBarHandleClick" Color="{DynamicResource ResourceKey=StandardBorder}" />
|
||||
<GridLength x:Key="ScrollBarButtonHeight">0</GridLength>
|
||||
</ResourceDictionary>
|
85
VRCMelonAssistant/Themes/Light Pink.xaml
Normal file
85
VRCMelonAssistant/Themes/Light Pink.xaml
Normal file
|
@ -0,0 +1,85 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
|
||||
<!-- VRChat Melon Assistant Light Pink Theme by Caeden117 -->
|
||||
|
||||
<!-- Default text -->
|
||||
<SolidColorBrush x:Key="TextColor" Color="#101010" />
|
||||
<SolidColorBrush x:Key="TextHighlighted" Color="Black" />
|
||||
|
||||
<!-- Buttons (Info/Mods/About/Options as well as More Info and Install/Update) -->
|
||||
<SolidColorBrush x:Key="ButtonBackground" Color="#FFE5A3EC" />
|
||||
<SolidColorBrush x:Key="ButtonOutline" Color="#FF640052" />
|
||||
<SolidColorBrush x:Key="ButtonHighlightedBackground" Color="#FFFF84EE" />
|
||||
<SolidColorBrush x:Key="ButtonClickedBackground" Color="Magenta" />
|
||||
<SolidColorBrush x:Key="ButtonDisabledBackground" Color="#FFD477C8" />
|
||||
<SolidColorBrush x:Key="ButtonDangerBackground" Color="#FF006E" />
|
||||
|
||||
<!-- Page Buttons (Side of Main Page) -->
|
||||
<SolidColorBrush x:Key="PageButtonBackground" Color="#FFE5A3EC" />
|
||||
<SolidColorBrush x:Key="PageButtonOutline" Color="#FF640052" />
|
||||
<SolidColorBrush x:Key="PageButtonHighlightedBackground" Color="#FFFF84EE" />
|
||||
<SolidColorBrush x:Key="PageButtonClickedBackground" Color="Magenta" />
|
||||
<SolidColorBrush x:Key="PageButtonDisabledBackground" Color="#FFD477C8" />
|
||||
|
||||
<!-- Mod List -->
|
||||
<SolidColorBrush x:Key="ModColumnBackground" Color="#FFFFC1F4" />
|
||||
<SolidColorBrush x:Key="ModColumnBorderBrush" Color="Gray" />
|
||||
<SolidColorBrush x:Key="ModColumnHeaderHighlighted" Color="#FFFF86FF" />
|
||||
<SolidColorBrush x:Key="ModListBackground" Color="#FFFCDCFF" />
|
||||
<SolidColorBrush x:Key="ModListItemHighlighted" Color="#FFFBD7FF" />
|
||||
<SolidColorBrush x:Key="ModListItemHighlightedOutline" Color="#FFCF61C4" />
|
||||
<SolidColorBrush x:Key="ModListItemSelected" Color="#FFFF88F4" />
|
||||
<SolidColorBrush x:Key="ModListItemSelectedOutline" Color="#FFFF88F4" />
|
||||
|
||||
<!-- Combo Box (Version select) -->
|
||||
<SolidColorBrush x:Key="ComboBoxBackground" Color="#FFE09EE0" />
|
||||
<SolidColorBrush x:Key="ComboBoxOutline" Color="#FF5D0048" />
|
||||
<SolidColorBrush x:Key="ComboBoxHighlighted" Color="#FF814D83" />
|
||||
<SolidColorBrush x:Key="ComboBoxSelected" Color="#FFF4ABFF" />
|
||||
<SolidColorBrush x:Key="ComboBoxClicked" Color="#FFFF63EA" />
|
||||
<SolidColorBrush x:Key="ComboBoxArrow" Color="Black" />
|
||||
|
||||
<!-- Checkboxes (Mod List and Options) -->
|
||||
<SolidColorBrush x:Key="CheckboxDefaultBackground" Color="#FFAD55B6" />
|
||||
<SolidColorBrush x:Key="CheckboxDefaultOutlineColor" Color="#FFD664D1" />
|
||||
<SolidColorBrush x:Key="CheckboxDisabledBackground" Color="#FF8C4693" />
|
||||
<SolidColorBrush x:Key="CheckboxDisabledOutlineColor" Color="#2a2a2a" />
|
||||
<SolidColorBrush x:Key="CheckboxDisabledTickColor" Color="#FFC9C9C9" />
|
||||
<SolidColorBrush x:Key="CheckboxHoveredBackground" Color="#FFF29CFF" />
|
||||
<SolidColorBrush x:Key="CheckboxHoveredTickColor" Color="White" />
|
||||
<SolidColorBrush x:Key="CheckboxTickColor" Color="White" />
|
||||
<SolidColorBrush x:Key="CheckboxPressedBackground" Color="#FFFF80FF" />
|
||||
|
||||
<!-- Scroll Bars -->
|
||||
<SolidColorBrush x:Key="ScrollBarBackground" Color="#FFFFE9FF" />
|
||||
<SolidColorBrush x:Key="ScrollBarBorder" Color="Black" />
|
||||
<SolidColorBrush x:Key="ScrollBarTextColor" Color="Black" />
|
||||
<SolidColorBrush x:Key="ScrollBarDisabled" Color="#FFBF95C5" />
|
||||
<SolidColorBrush x:Key="ScrollBarArrowColor" Color="#FFF9AEF9" />
|
||||
<SolidColorBrush x:Key="ScrollBarArrowClicked" Color="#FFEB6CFF" />
|
||||
<SolidColorBrush x:Key="ScrollBarArrowHovered" Color="#FFFF91EB" />
|
||||
<SolidColorBrush x:Key="ScrollBarHandle" Color="#FFF19AFF" />
|
||||
<SolidColorBrush x:Key="ScrollBarHandleHovered" Color="#FFEC72FF" />
|
||||
<SolidColorBrush x:Key="ScrollBarHandleClick" Color="#FFF744FF" />
|
||||
<GridLength x:Key="ScrollBarButtonHeight">0</GridLength>
|
||||
|
||||
<!-- Various important elements that need to be controlled independently -->
|
||||
<SolidColorBrush x:Key="VRCMelonAssistantBackground" Color="#FFFAE5FF" />
|
||||
<SolidColorBrush x:Key="FrameBackgroundColor" Color="#CCFAE5FF" />
|
||||
<SolidColorBrush x:Key="BottomStatusBarBackground" Color="#FFE288D6" />
|
||||
<SolidColorBrush x:Key="DirectoryBackground" Color="#FFE288D6" />
|
||||
|
||||
<!-- Colors for the corresponding icons. -->
|
||||
<SolidColorBrush x:Key="InfoIconColor" Color="White" />
|
||||
<SolidColorBrush x:Key="ModsIconColor" Color="White" />
|
||||
<SolidColorBrush x:Key="AboutIconColor" Color="White" />
|
||||
<SolidColorBrush x:Key="OptionsIconColor" Color="White" />
|
||||
<SolidColorBrush x:Key="LoadingIconColor" Color="#101010" />
|
||||
|
||||
<!-- Background and Side image settings. -->
|
||||
<!-- Fill, None, Uniform, UniformToFill -->
|
||||
<Stretch x:Key="BackgroundImageStretch">UniformToFill</Stretch>
|
||||
<!-- Bottom, Center, Top, Stretch -->
|
||||
<VerticalAlignment x:Key="SideImageYPosition">Bottom</VerticalAlignment>
|
||||
|
||||
</ResourceDictionary>
|
96
VRCMelonAssistant/Themes/Light.xaml
Normal file
96
VRCMelonAssistant/Themes/Light.xaml
Normal file
|
@ -0,0 +1,96 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
|
||||
<!-- VRChat Melon Assistant Light Theme by Caeden117 and lolPants -->
|
||||
|
||||
<!-- Standard Styles -->
|
||||
<Color x:Key="StandardContent">#101010</Color>
|
||||
<Color x:Key="StandardPrimary">#C8C8C8</Color>
|
||||
<Color x:Key="StandardSecondary">#F3F3F3</Color>
|
||||
<Color x:Key="StandardBorder">Gray</Color>
|
||||
<Color x:Key="StandardHighlight">#AEAEAE</Color>
|
||||
<Color x:Key="StandardActive">#C0C0C0</Color>
|
||||
|
||||
<!-- Default Text -->
|
||||
<SolidColorBrush x:Key="TextColor" Color="#101010" />
|
||||
<SolidColorBrush x:Key="TextHighlighted" Color="Black" />
|
||||
|
||||
<!-- Buttons (Info/Mods/About/Options as well as More Info and Install/Update) -->
|
||||
<SolidColorBrush x:Key="ButtonBackground" Color="{DynamicResource ResourceKey=StandardPrimary}" />
|
||||
<SolidColorBrush x:Key="ButtonOutline" Color="{DynamicResource ResourceKey=StandardBorder}" />
|
||||
<SolidColorBrush x:Key="ButtonHighlightedBackground" Color="{DynamicResource ResourceKey=StandardHighlight}" />
|
||||
<SolidColorBrush x:Key="ButtonClickedBackground" Color="{DynamicResource ResourceKey=StandardActive}" />
|
||||
<SolidColorBrush x:Key="ButtonDisabledBackground" Color="{DynamicResource ResourceKey=StandardSecondary}" />
|
||||
<SolidColorBrush x:Key="ButtonDangerBackground" Color="#ED172F" />
|
||||
|
||||
<!-- Page Buttons (Side of Main Page) -->
|
||||
<SolidColorBrush x:Key="PageButtonBackground" Color="Transparent" />
|
||||
<SolidColorBrush x:Key="PageButtonOutline" Color="{DynamicResource ResourceKey=StandardBorder}" />
|
||||
<SolidColorBrush x:Key="PageButtonHighlightedBackground" Color="#20000000" />
|
||||
<SolidColorBrush x:Key="PageButtonClickedBackground" Color="{DynamicResource ResourceKey=StandardActive}" />
|
||||
<SolidColorBrush x:Key="PageButtonDisabledBackground" Color="#50000000" />
|
||||
|
||||
<!-- Mod List -->
|
||||
<SolidColorBrush x:Key="ModColumnBackground" Color="{DynamicResource ResourceKey=StandardPrimary}" />
|
||||
<SolidColorBrush x:Key="ModColumnBorderBrush" Color="{DynamicResource ResourceKey=StandardBorder}" />
|
||||
<SolidColorBrush x:Key="ModColumnHeaderHighlighted" Color="{DynamicResource ResourceKey=StandardHighlight}" />
|
||||
<SolidColorBrush x:Key="ModListBackground" Color="#09000000" />
|
||||
<SolidColorBrush x:Key="ModListBorderBrush" Color="{DynamicResource ResourceKey=StandardBorder}" />
|
||||
<SolidColorBrush x:Key="ModListItemHighlighted" Color="#DADADA" />
|
||||
<SolidColorBrush x:Key="ModListItemHighlightedOutline" Color="#60000000" />
|
||||
<SolidColorBrush x:Key="ModListItemSelected" Color="{DynamicResource ResourceKey=StandardActive}" />
|
||||
<SolidColorBrush x:Key="ModListItemSelectedOutline" Color="#70000000" />
|
||||
|
||||
<!-- Combo Box (Version select) -->
|
||||
<SolidColorBrush x:Key="ComboBoxBackground" Color="{DynamicResource ResourceKey=StandardPrimary}" />
|
||||
<SolidColorBrush x:Key="ComboBoxOutline" Color="{DynamicResource ResourceKey=StandardBorder}" />
|
||||
<SolidColorBrush x:Key="ComboBoxHighlighted" Color="{DynamicResource ResourceKey=StandardHighlight}" />
|
||||
<SolidColorBrush x:Key="ComboBoxSelected" Color="{DynamicResource ResourceKey=StandardActive}" />
|
||||
<SolidColorBrush x:Key="ComboBoxClicked" Color="#AAAAAA" />
|
||||
<SolidColorBrush x:Key="ComboBoxArrow" Color="Black" />
|
||||
|
||||
<!-- Checkboxes (Mod List and Options) -->
|
||||
<SolidColorBrush x:Key="CheckboxDefaultBackground" Color="{DynamicResource ResourceKey=StandardPrimary}" />
|
||||
<SolidColorBrush x:Key="CheckboxDefaultOutlineColor" Color="{DynamicResource ResourceKey=StandardBorder}" />
|
||||
<SolidColorBrush x:Key="CheckboxDisabledBackground" Color="#5A5A5A" />
|
||||
<SolidColorBrush x:Key="CheckboxDisabledOutlineColor" Color="#4a4a4a" />
|
||||
<SolidColorBrush x:Key="CheckboxDisabledTickColor" Color="#DDD" />
|
||||
<SolidColorBrush x:Key="CheckboxHoveredBackground" Color="#909090" />
|
||||
<SolidColorBrush x:Key="CheckboxHoveredTickColor" Color="White" />
|
||||
<SolidColorBrush x:Key="CheckboxTickColor" Color="{DynamicResource ResourceKey=StandardContent}" />
|
||||
<SolidColorBrush x:Key="CheckboxPressedBackground" Color="#FFBFBFBF" />
|
||||
|
||||
<!-- Scroll Bars -->
|
||||
<SolidColorBrush x:Key="ScrollBarBackground" Color="#FFD8D8D8" />
|
||||
<SolidColorBrush x:Key="ScrollBarBorder" Color="{DynamicResource ResourceKey=StandardBorder}" />
|
||||
<SolidColorBrush x:Key="ScrollBarTextColor" Color="{DynamicResource ResourceKey=StandardPrimary}" />
|
||||
<SolidColorBrush x:Key="ScrollBarDisabled" Color="{DynamicResource ResourceKey=StandardSecondary}" />
|
||||
<SolidColorBrush x:Key="ScrollBarArrowColor" Color="{DynamicResource ResourceKey=StandardActive}" />
|
||||
<SolidColorBrush x:Key="ScrollBarArrowClicked" Color="{DynamicResource ResourceKey=StandardPrimary}" />
|
||||
<SolidColorBrush x:Key="ScrollBarArrowHovered" Color="{DynamicResource ResourceKey=StandardHighlight}" />
|
||||
<SolidColorBrush x:Key="ScrollBarHandle" Color="#FF9B9B9B" />
|
||||
<SolidColorBrush x:Key="ScrollBarHandleHovered" Color="#FFB9B9B9" />
|
||||
<SolidColorBrush x:Key="ScrollBarHandleClick" Color="#55000000" />
|
||||
<GridLength x:Key="ScrollBarButtonHeight">0</GridLength>
|
||||
|
||||
<!-- Various important elements that need to be controlled independently -->
|
||||
<SolidColorBrush x:Key="VRCMelonAssistantBackground" Color="{DynamicResource ResourceKey=StandardSecondary}" />
|
||||
<SolidColorBrush x:Key="FrameBackgroundColor" Color="#CCF3F3F3" />
|
||||
<SolidColorBrush x:Key="BottomStatusBarBackground" Color="{DynamicResource ResourceKey=StandardPrimary}" />
|
||||
<SolidColorBrush x:Key="BottomStatusBarOutline" Color="{DynamicResource ResourceKey=StandardBorder}" />
|
||||
<SolidColorBrush x:Key="DirectoryBackground" Color="{DynamicResource ResourceKey=StandardPrimary}" />
|
||||
<SolidColorBrush x:Key="DirectoryOutline" Color="{DynamicResource ResourceKey=StandardBorder}" />
|
||||
|
||||
<!-- Colors for page buttons -->
|
||||
<SolidColorBrush x:Key="InfoIconColor" Color="#0DCAC8" />
|
||||
<SolidColorBrush x:Key="ModsIconColor" Color="#833BCE" />
|
||||
<SolidColorBrush x:Key="AboutIconColor" Color="#FF0000" />
|
||||
<SolidColorBrush x:Key="OptionsIconColor" Color="#4E3BCE" />
|
||||
<SolidColorBrush x:Key="LoadingIconColor" Color="#101010" />
|
||||
|
||||
<!-- Background and Side image settings. -->
|
||||
<!-- Fill, None, Uniform, UniformToFill -->
|
||||
<Stretch x:Key="BackgroundImageStretch">UniformToFill</Stretch>
|
||||
<!-- Bottom, Center, Top, Stretch -->
|
||||
<VerticalAlignment x:Key="SideImageYPosition">Bottom</VerticalAlignment>
|
||||
|
||||
</ResourceDictionary>
|
117
VRCMelonAssistant/Themes/Ugly Kulu-Ya-Ku.xaml
Normal file
117
VRCMelonAssistant/Themes/Ugly Kulu-Ya-Ku.xaml
Normal file
|
@ -0,0 +1,117 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
|
||||
<!--
|
||||
VRChat Melon Assistant Ugly Kulu-Ya-Ku Theme by Assistant
|
||||
Feel free to use this as a template for designing your own themes for VRChat Melon Assistant.
|
||||
|
||||
The Color fields take in Hexadecimal RGB (#RRGGBB) or Hexadecimal ARGB (#AARRGGBB).
|
||||
They can also take in common names like "Gray", "Maroon", "Magenta", "LimeGreen", etc.
|
||||
For more information, see: https://docs.microsoft.com/en-us/dotnet/api/system.windows.media.colors
|
||||
You may set color variables like below in the Standard Styles section, or simply set the colors directly.
|
||||
|
||||
To reload your theme, exit and re-enter the Options menu.
|
||||
|
||||
Inspired by https://www.nexusmods.com/monsterhunterworld/mods/2080
|
||||
william requested I make this theme. One time william requested a hug from pook. pook denied said request.
|
||||
I then witnessed william hounding pook, until he siezed them by breaking their foot.
|
||||
As pook agonized in the floor, they were ravished by william. I know better than to deny william's request.
|
||||
-->
|
||||
|
||||
<!-- Standard Styles -->
|
||||
<Color x:Key="StandardContent">#75f6ff</Color>
|
||||
<Color x:Key="StandardPrimary">#4FC653</Color>
|
||||
<Color x:Key="StandardSecondary">#7A2E63</Color>
|
||||
<Color x:Key="StandardBorder">#75f6ff</Color>
|
||||
<Color x:Key="StandardHighlight">#508F4E</Color>
|
||||
<Color x:Key="StandardActive">#CA85B1</Color>
|
||||
<Color x:Key="StandardIcon">#FF51B6</Color>
|
||||
|
||||
<!-- Default text -->
|
||||
<SolidColorBrush x:Key="TextColor" Color="{StaticResource ResourceKey=StandardContent}" />
|
||||
<SolidColorBrush x:Key="TextHighlighted" Color="Black" />
|
||||
|
||||
<!-- Buttons (Info/Mods/About/Options as well as More Info and Install/Update) -->
|
||||
<SolidColorBrush x:Key="ButtonBackground" Color="{DynamicResource ResourceKey=StandardPrimary}" />
|
||||
<SolidColorBrush x:Key="ButtonOutline" Color="{DynamicResource ResourceKey=StandardBorder}" />
|
||||
<SolidColorBrush x:Key="ButtonHighlightedBackground" Color="{DynamicResource ResourceKey=StandardHighlight}" />
|
||||
<SolidColorBrush x:Key="ButtonClickedBackground" Color="{DynamicResource ResourceKey=StandardActive}" />
|
||||
<SolidColorBrush x:Key="ButtonDisabledBackground" Color="{DynamicResource ResourceKey=StandardSecondary}" />
|
||||
<SolidColorBrush x:Key="ButtonDangerBackground" Color="#FF006E" />
|
||||
|
||||
<!-- Page Buttons (Side of Main Page) -->
|
||||
<SolidColorBrush x:Key="PageButtonBackground" Color="{DynamicResource ResourceKey=StandardPrimary}" />
|
||||
<SolidColorBrush x:Key="PageButtonOutline" Color="{DynamicResource ResourceKey=StandardBorder}" />
|
||||
<SolidColorBrush x:Key="PageButtonHighlightedBackground" Color="{DynamicResource ResourceKey=StandardHighlight}" />
|
||||
<SolidColorBrush x:Key="PageButtonClickedBackground" Color="{DynamicResource ResourceKey=StandardActive}" />
|
||||
<SolidColorBrush x:Key="PageButtonDisabledBackground" Color="{DynamicResource ResourceKey=StandardSecondary}" />
|
||||
|
||||
<!-- Mod List -->
|
||||
<SolidColorBrush x:Key="ModColumnBackground" Color="{DynamicResource ResourceKey=StandardPrimary}" />
|
||||
<SolidColorBrush x:Key="ModColumnBorderBrush" Color="{DynamicResource ResourceKey=StandardPrimary}" />
|
||||
<SolidColorBrush x:Key="ModColumnHeaderHighlighted" Color="{DynamicResource ResourceKey=StandardHighlight}" />
|
||||
<SolidColorBrush x:Key="ModListBackground" Color="{DynamicResource ResourceKey=StandardPrimary}" />
|
||||
<SolidColorBrush x:Key="ModListBorderBrush" Color="{DynamicResource ResourceKey=StandardBorder}" />
|
||||
<SolidColorBrush x:Key="ModListItemHighlighted" Color="#A0C29F" />
|
||||
<SolidColorBrush x:Key="ModListItemHighlightedOutline" Color="{DynamicResource ResourceKey=StandardBorder}" />
|
||||
<SolidColorBrush x:Key="ModListItemSelected" Color="{DynamicResource ResourceKey=StandardActive}" />
|
||||
<SolidColorBrush x:Key="ModListItemSelectedOutline" Color="{DynamicResource ResourceKey=StandardBorder}" />
|
||||
|
||||
<!-- Combo Box (Version select) -->
|
||||
<SolidColorBrush x:Key="ComboBoxBackground" Color="{DynamicResource ResourceKey=StandardPrimary}" />
|
||||
<SolidColorBrush x:Key="ComboBoxOutline" Color="{DynamicResource ResourceKey=StandardBorder}" />
|
||||
<SolidColorBrush x:Key="ComboBoxHighlighted" Color="{DynamicResource ResourceKey=StandardHighlight}" />
|
||||
<SolidColorBrush x:Key="ComboBoxSelected" Color="{DynamicResource ResourceKey=StandardActive}" />
|
||||
<SolidColorBrush x:Key="ComboBoxClicked" Color="{DynamicResource ResourceKey=StandardPrimary}" />
|
||||
<SolidColorBrush x:Key="ComboBoxArrow" Color="{DynamicResource ResourceKey=StandardBorder}" />
|
||||
|
||||
<!-- Checkboxes (Mod List and Options) -->
|
||||
<SolidColorBrush x:Key="CheckboxDefaultBackground" Color="{DynamicResource ResourceKey=StandardPrimary}" />
|
||||
<SolidColorBrush x:Key="CheckboxDefaultOutlineColor" Color="{DynamicResource ResourceKey=StandardBorder}" />
|
||||
<SolidColorBrush x:Key="CheckboxDisabledBackground" Color="{DynamicResource ResourceKey=StandardSecondary}" />
|
||||
<SolidColorBrush x:Key="CheckboxDisabledOutlineColor" Color="{DynamicResource ResourceKey=StandardBorder}" />
|
||||
<SolidColorBrush x:Key="CheckboxDisabledTickColor" Color="{DynamicResource ResourceKey=StandardBorder}" />
|
||||
<SolidColorBrush x:Key="CheckboxHoveredBackground" Color="{DynamicResource ResourceKey=StandardHighlight}" />
|
||||
<SolidColorBrush x:Key="CheckboxHoveredTickColor" Color="{DynamicResource ResourceKey=StandardIcon}" />
|
||||
<SolidColorBrush x:Key="CheckboxTickColor" Color="{DynamicResource ResourceKey=StandardIcon}" />
|
||||
<SolidColorBrush x:Key="CheckboxPressedBackground" Color="{DynamicResource ResourceKey=StandardActive}" />
|
||||
|
||||
<!-- Scroll Bars -->
|
||||
<SolidColorBrush x:Key="ScrollBarBackground" Color="{DynamicResource ResourceKey=StandardSecondary}" />
|
||||
<SolidColorBrush x:Key="ScrollBarBorder" Color="{DynamicResource ResourceKey=StandardBorder}" />
|
||||
<SolidColorBrush x:Key="ScrollBarTextColor" Color="{DynamicResource ResourceKey=StandardPrimary}" />
|
||||
<SolidColorBrush x:Key="ScrollBarDisabled" Color="{DynamicResource ResourceKey=StandardSecondary}" />
|
||||
<SolidColorBrush x:Key="ScrollBarArrowColor" Color="{DynamicResource ResourceKey=StandardActive}" />
|
||||
<SolidColorBrush x:Key="ScrollBarArrowClicked" Color="{DynamicResource ResourceKey=StandardPrimary}" />
|
||||
<SolidColorBrush x:Key="ScrollBarArrowHovered" Color="{DynamicResource ResourceKey=StandardHighlight}" />
|
||||
<SolidColorBrush x:Key="ScrollBarHandle" Color="{DynamicResource ResourceKey=StandardPrimary}" />
|
||||
<SolidColorBrush x:Key="ScrollBarHandleHovered" Color="{DynamicResource ResourceKey=StandardHighlight}" />
|
||||
<SolidColorBrush x:Key="ScrollBarHandleClick" Color="{DynamicResource ResourceKey=StandardBorder}" />
|
||||
<GridLength x:Key="ScrollBarButtonHeight">0</GridLength>
|
||||
|
||||
<!-- Various important elements that need to be controlled independently -->
|
||||
<SolidColorBrush x:Key="VRCMelonAssistantBackground" Color="{DynamicResource ResourceKey=StandardIcon}" />
|
||||
<SolidColorBrush x:Key="FrameBackgroundColor" Color="#CCFF51B6" />
|
||||
<SolidColorBrush x:Key="BottomStatusBarBackground" Color="#BF489A" />
|
||||
<SolidColorBrush x:Key="BottomStatusBarOutline" Color="{DynamicResource ResourceKey=StandardBorder}" />
|
||||
<SolidColorBrush x:Key="DirectoryBackground" Color="#BF489A" />
|
||||
<SolidColorBrush x:Key="DirectoryOutline" Color="{DynamicResource ResourceKey=StandardBorder}" />
|
||||
|
||||
<!-- Colors for the corresponding icons. -->
|
||||
<!-- Info Default: #0DCAC8 -->
|
||||
<SolidColorBrush x:Key="InfoIconColor" Color="{DynamicResource ResourceKey=StandardIcon}" />
|
||||
<!-- Mods Default: #833BCE -->
|
||||
<SolidColorBrush x:Key="ModsIconColor" Color="{DynamicResource ResourceKey=StandardIcon}" />
|
||||
<!-- About Default: #FF0000 -->
|
||||
<SolidColorBrush x:Key="AboutIconColor" Color="{DynamicResource ResourceKey=StandardIcon}" />
|
||||
<!-- Options Default: #4E3BCE -->
|
||||
<SolidColorBrush x:Key="OptionsIconColor" Color="{DynamicResource ResourceKey=StandardIcon}" />
|
||||
<!-- Loading Icon Default: #6D3B8B -->
|
||||
<SolidColorBrush x:Key="LoadingIconColor" Color="{DynamicResource ResourceKey=StandardContent}" />
|
||||
|
||||
<!-- Background and Side image settings. -->
|
||||
<!-- Fill, None, Uniform, UniformToFill -->
|
||||
<Stretch x:Key="BackgroundImageStretch">UniformToFill</Stretch>
|
||||
<!-- Bottom, Center, Top, Stretch -->
|
||||
<VerticalAlignment x:Key="SideImageYPosition">Bottom</VerticalAlignment>
|
||||
|
||||
</ResourceDictionary>
|
319
VRCMelonAssistant/VRCMelonAssistant.csproj
Normal file
319
VRCMelonAssistant/VRCMelonAssistant.csproj
Normal file
|
@ -0,0 +1,319 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\packages\ILRepack.MSBuild.Task.2.0.13\build\ILRepack.MSBuild.Task.props" Condition="Exists('..\packages\ILRepack.MSBuild.Task.2.0.13\build\ILRepack.MSBuild.Task.props')" />
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{6A224B82-40DA-40B3-94DC-EFBEC2BDDA39}</ProjectGuid>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<RootNamespace>VRCMelonAssistant</RootNamespace>
|
||||
<AssemblyName>VRCMelonAssistant</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ApplicationIcon>Resources\icon.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.Build.Framework, Version=15.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<HintPath>..\packages\Microsoft.Build.Framework.15.9.20\lib\net46\Microsoft.Build.Framework.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Build.Utilities.Core, Version=15.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<HintPath>..\packages\Microsoft.Build.Utilities.Core.15.9.20\lib\net46\Microsoft.Build.Utilities.Core.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.VisualStudio.Setup.Configuration.Interop, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<HintPath>..\packages\Microsoft.VisualStudio.Setup.Configuration.Interop.1.16.30\lib\net35\Microsoft.VisualStudio.Setup.Configuration.Interop.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Mono.Cecil, Version=0.11.3.0, Culture=neutral, PublicKeyToken=50cebf1cceb9d05e">
|
||||
<HintPath>..\packages\Mono.Cecil.0.11.3\lib\net40\Mono.Cecil.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Mono.Cecil.Mdb, Version=0.11.3.0, Culture=neutral, PublicKeyToken=50cebf1cceb9d05e">
|
||||
<HintPath>..\packages\Mono.Cecil.0.11.3\lib\net40\Mono.Cecil.Mdb.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Mono.Cecil.Pdb, Version=0.11.3.0, Culture=neutral, PublicKeyToken=50cebf1cceb9d05e">
|
||||
<HintPath>..\packages\Mono.Cecil.0.11.3\lib\net40\Mono.Cecil.Pdb.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Mono.Cecil.Rocks, Version=0.11.3.0, Culture=neutral, PublicKeyToken=50cebf1cceb9d05e">
|
||||
<HintPath>..\packages\Mono.Cecil.0.11.3\lib\net40\Mono.Cecil.Rocks.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="mscorlib" />
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Collections.Immutable, Version=1.2.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<HintPath>..\packages\System.Collections.Immutable.1.5.0\lib\netstandard2.0\System.Collections.Immutable.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.IO.Compression" />
|
||||
<Reference Include="System.IO.Compression.FileSystem" />
|
||||
<Reference Include="System.Management" />
|
||||
<Reference Include="System.Runtime.InteropServices.RuntimeInformation, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<HintPath>..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Web" />
|
||||
<Reference Include="System.Web.Extensions" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xaml">
|
||||
<RequiredTargetFramework>4.0</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="WindowsBase" />
|
||||
<Reference Include="PresentationCore" />
|
||||
<Reference Include="PresentationFramework" />
|
||||
<Reference Include="WindowsFormsIntegration" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ApplicationDefinition Include="App.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</ApplicationDefinition>
|
||||
<Compile Include="Classes\HardcodedCategories.cs" />
|
||||
<Compile Include="Classes\Http.cs" />
|
||||
<Compile Include="Classes\HyperlinkExtensions.cs" />
|
||||
<Compile Include="Classes\InstallHandlers.cs" />
|
||||
<Compile Include="Classes\Languages.cs" />
|
||||
<Compile Include="Classes\Promotions.cs" />
|
||||
<Compile Include="Classes\Diagnostics.cs" />
|
||||
<Compile Include="Classes\Themes.cs" />
|
||||
<Compile Include="Classes\Updater.cs" />
|
||||
<Compile Include="Libs\semver\SemVersion.cs" />
|
||||
<Compile Include="Libs\semver\IntExtensions.cs" />
|
||||
<Compile Include="ModInfoWindow.xaml.cs">
|
||||
<DependentUpon>ModInfoWindow.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Pages\Intro.xaml.cs">
|
||||
<DependentUpon>Intro.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Classes\Mod.cs" />
|
||||
<Page Include="Localisation\en-DEBUG.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Compile Include="Pages\Invalid.xaml.cs">
|
||||
<DependentUpon>Invalid.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Pages\Loading.xaml.cs">
|
||||
<DependentUpon>Loading.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Pages\Mods.xaml.cs">
|
||||
<DependentUpon>Mods.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Pages\About.xaml.cs">
|
||||
<DependentUpon>About.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Classes\Utils.cs" />
|
||||
<Page Include="Localisation\en.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="ModInfoWindow.xaml" />
|
||||
<Page Include="Pages\Intro.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="MainWindow.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Compile Include="App.xaml.cs">
|
||||
<DependentUpon>App.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="MainWindow.xaml.cs">
|
||||
<DependentUpon>MainWindow.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Page Include="Pages\Loading.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Pages\Invalid.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Pages\Mods.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Pages\About.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Pages\Options.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Resources\AppResources.xaml" />
|
||||
<Page Include="Resources\Icons.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Styles\Button.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Styles\CheckBox.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Styles\ComboBox.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Styles\ComboBoxItem.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Styles\GridViewColumnHeader.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Styles\Label.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Styles\ListView.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Styles\ListViewItem.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Styles\MenuItem.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Styles\Menu.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Styles\RepeatButton.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Styles\ScrollBar.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Styles\TextBlock.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Styles\Thumb.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Styles\ToggleButton.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Themes\Dark.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Themes\Default Scrollbar.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Themes\Light.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Themes\Light Pink.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<EmbeddedResource Include="Themes\Ugly Kulu-Ya-Ku.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Pages\Options.xaml.cs">
|
||||
<DependentUpon>Options.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Properties\AssemblyInfo.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Properties\Settings.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
</Compile>
|
||||
<EmbeddedResource Include="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
<None Include="packages.config" />
|
||||
<None Include="Properties\Settings.settings">
|
||||
<Generator>PublicSettingsSingleFileGenerator</Generator>
|
||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="Resources\icon.ico" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105.The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\packages\ILRepack.MSBuild.Task.2.0.13\build\ILRepack.MSBuild.Task.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\ILRepack.MSBuild.Task.2.0.13\build\ILRepack.MSBuild.Task.props'))" />
|
||||
</Target>
|
||||
<Target Name="ILRepack" AfterTargets="Build" Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
|
||||
<PropertyGroup>
|
||||
<WorkingDirectory>$(MSBuildThisFileDirectory)bin\$(Configuration)\$(TargetFramework)</WorkingDirectory>
|
||||
</PropertyGroup>
|
||||
|
||||
<ILRepack OutputType="$(OutputType)" MainAssembly="$(AssemblyName).exe" OutputAssembly="$(AssemblyName).exe" InputAssemblies="$(WorkingDirectory)\Mono.Cecil.dll" WilcardInputAssemblies="true" WorkingDirectory="$(WorkingDirectory)" />
|
||||
|
||||
</Target>
|
||||
</Project>
|
10
VRCMelonAssistant/packages.config
Normal file
10
VRCMelonAssistant/packages.config
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="ILRepack.MSBuild.Task" version="2.0.13" targetFramework="net48" />
|
||||
<package id="Microsoft.Build.Framework" version="15.9.20" targetFramework="net48" />
|
||||
<package id="Microsoft.Build.Utilities.Core" version="15.9.20" targetFramework="net48" />
|
||||
<package id="Microsoft.VisualStudio.Setup.Configuration.Interop" version="1.16.30" targetFramework="net48" developmentDependency="true" />
|
||||
<package id="Mono.Cecil" version="0.11.3" targetFramework="net461" />
|
||||
<package id="System.Collections.Immutable" version="1.5.0" targetFramework="net48" />
|
||||
<package id="System.Runtime.InteropServices.RuntimeInformation" version="4.3.0" targetFramework="net48" />
|
||||
</packages>
|
7
tools/README.md
Normal file
7
tools/README.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
# ModAssistant Tools
|
||||
> These are tools used to build / improve ModAssistant. **They are not to be distributed with the compiled binary.**
|
||||
|
||||
## Translation Stubs
|
||||
Use `generate_translation_stubs.py` to read the English locale file and generate missing strings for all other locales. Requires Python 3.6 or above.
|
||||
|
||||
Simply run `python ./tools/generate_translation_stubs.py` and commit the stubs.
|
144
tools/generate_translation_stubs.py
Normal file
144
tools/generate_translation_stubs.py
Normal file
|
@ -0,0 +1,144 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
import re
|
||||
from shutil import copyfile
|
||||
|
||||
# --------------- Configuration ---------------- #
|
||||
master_file = "./ModAssistant/Localisation/en.xaml"
|
||||
source_dir = "./ModAssistant/Localisation"
|
||||
target_dir = source_dir
|
||||
comment = " <!-- NEEDS TRANSLATING -->"
|
||||
# --------------- Configuration ---------------- #
|
||||
|
||||
|
||||
def read_file(filepath):
|
||||
"""
|
||||
Read file content
|
||||
:param filepath:
|
||||
:return:
|
||||
"""
|
||||
with open(filepath, "r", encoding="utf-8") as file:
|
||||
data = file.read()
|
||||
return data
|
||||
|
||||
|
||||
def find_files(root_directory):
|
||||
"""
|
||||
Find all files to be processed
|
||||
:param root_directory:
|
||||
:return:
|
||||
"""
|
||||
for root, ds, fs in os.walk(root_directory):
|
||||
for fn in fs:
|
||||
fullname = os.path.join(root, fn)
|
||||
master_name = os.path.basename(master_file)
|
||||
if master_name in fullname:
|
||||
continue
|
||||
yield fullname
|
||||
|
||||
|
||||
def search(regex, m_string, group=1):
|
||||
"""
|
||||
Search for the first item that matches by regular expression
|
||||
:param regex:
|
||||
:param m_string:
|
||||
:return:
|
||||
"""
|
||||
found = re.compile(regex, re.M).search(m_string)
|
||||
result = ""
|
||||
if found:
|
||||
result = found.group(group)
|
||||
return result
|
||||
|
||||
|
||||
def write_file(filepath, content):
|
||||
"""
|
||||
Write text content to file
|
||||
:param filepath:
|
||||
:param content:
|
||||
:return:
|
||||
"""
|
||||
if not os.path.exists(target_dir):
|
||||
os.makedirs(target_dir, exist_ok=True)
|
||||
with open(filepath, "w", encoding="utf-8") as f:
|
||||
f.write(content)
|
||||
|
||||
|
||||
def find_items(data):
|
||||
keys_regex = r"<\s*([^\s>]+)\s.*Key[^>]+>"
|
||||
keys_matches = re.finditer(keys_regex, data, re.M)
|
||||
items = []
|
||||
for n, m in enumerate(keys_matches, start=1):
|
||||
if len(items):
|
||||
last_dict = items[-1]
|
||||
if len(last_dict):
|
||||
last_dict['end'] = m.start()
|
||||
items.append({"match": m[0], "name": m[1], "start": m.end()})
|
||||
items[-1]['end'] = len(data)
|
||||
for item in items:
|
||||
item_text = data[item['start']: item['end']]
|
||||
end_tag = "</%s>" % item['name']
|
||||
full_tag = item['match'] + item_text[:item_text.rfind(end_tag) + len(end_tag)]
|
||||
item['end_tag'] = end_tag
|
||||
item['full_tag'] = full_tag
|
||||
return items
|
||||
|
||||
|
||||
def wrapper_key(match, content):
|
||||
"""
|
||||
Try to find the comment behind the key
|
||||
:param match:
|
||||
:param content:
|
||||
:return:
|
||||
"""
|
||||
regex = "(\s*" + re.escape(match) + "(\ *<\!\-\-[^\-]+\-\->)?\s*)"
|
||||
with_comment = search(r"" + str(regex), content, 1)
|
||||
# print(regex)
|
||||
# print(match)
|
||||
# print(with_comment)
|
||||
# print("-----")
|
||||
if with_comment:
|
||||
match = with_comment
|
||||
return match
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
Cycle processing of xaml files except master_file
|
||||
:return:
|
||||
"""
|
||||
master_data = read_file(master_file)
|
||||
print("The master file is", master_file)
|
||||
items = find_items(master_data)
|
||||
|
||||
for f in find_files(source_dir):
|
||||
content = master_data + ""
|
||||
xml_data = read_file(f)
|
||||
xml_dict = {}
|
||||
for xml_item in find_items(xml_data):
|
||||
xml_dict[xml_item['match']] = xml_item['full_tag']
|
||||
for item in items:
|
||||
# print(f)
|
||||
# if "OneClick:Done" in item['match'] and "fr.xaml" in f:
|
||||
# print("----")
|
||||
# pass
|
||||
if item['match'] in xml_dict.keys():
|
||||
match = wrapper_key(xml_dict[item['match']], xml_data)
|
||||
master_match = wrapper_key(item['full_tag'], content)
|
||||
# print(master_match)
|
||||
content = content.replace(master_match, match)
|
||||
else:
|
||||
pre_blanks = search(r"(\s*)" + re.escape(item['full_tag']), master_data)
|
||||
content = re.sub(r"\s*" + re.escape(item['full_tag']), pre_blanks + item['full_tag'] + comment, content)
|
||||
# Put the processed files in the "dist" directory
|
||||
filepath = f.replace(source_dir, target_dir)
|
||||
write_file(filepath, content)
|
||||
print("Processing", f)
|
||||
# Copy "master_file" to the target directory
|
||||
if source_dir != target_dir:
|
||||
copyfile(master_file, master_file.replace(source_dir, target_dir))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
print("done'd!")
|
Loading…
Reference in a new issue