diff --git a/.gitignore b/.gitignore
index 2ccb550..66207a5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,7 @@ bin/
dist/
ext/
temp/
+gc*/
resources/js/neutralino.js
resources/bg/official
diff --git a/languages/en.json b/languages/en.json
index a7ecafe..1fecb4d 100644
--- a/languages/en.json
+++ b/languages/en.json
@@ -63,5 +63,21 @@
"alertAuthNoRegister": "Authentication is disabled, no need to register!",
"alertRegisterSuccess": "Registration successful!",
+ "downloadTitle": "Downloads",
+ "grassclipperTitle": "GrassClipper",
+ "grasscutterTitle": "Grasscutter",
+ "installerTitle": "Installer",
+ "installerSubtitle": "Installs proxy and other tools. Required for Grasscutter servers.",
+ "downloadStable": "Download Grasscutter Stable Build",
+ "stableSubtitle": "Install Grasscutter stable branch. This build usually has less bugs, but also less features.",
+ "downloadDev": "Download Grasscutter Development Build",
+ "downloadSubtitle": "Install Grasscutter development branch. This build sometimes has bugs, and is frequently updated. Use at your own risk.",
+ "downloadResources": "Download Grasscutter Resources",
+ "devSubtitle": "Downloads Grasscutter resources into the currently set Grasscutter folder. This should be done unless you plan on getting resources externally.",
+
+ "gcScriptRunning": "Running...",
+ "stableInstall": "Download",
+ "devInstall": "Download",
+
"updateNotifText": "A new update is available! Newest version: "
}
diff --git a/resources/icons/download.svg b/resources/icons/download.svg
new file mode 100644
index 0000000..8baf9ed
--- /dev/null
+++ b/resources/icons/download.svg
@@ -0,0 +1,11 @@
+
\ No newline at end of file
diff --git a/resources/index.html b/resources/index.html
index f59f5ea..bb71778 100644
--- a/resources/index.html
+++ b/resources/index.html
@@ -8,6 +8,7 @@
+
@@ -114,6 +115,59 @@
+
+
+ Downloads
+
+
+
+
+
+
+ GrassClipper
+
+
+
+ Installer
+
+
+
+ Installs proxy and other tools. Required for Grasscutter servers.
+
+
+
+ Grasscutter
+
+
+
+ Download Grasscutter Stable Build
+
+
+
+ Install Grasscutter stable branch. This build usually has less bugs, but also less features.
+
+
+
+
+ Download Grasscutter Development Build
+
+
+
+ Install Grasscutter development branch. This build sometimes has bugs, and is frequently updated. Use at your own risk.
+
+
+
+
+ Download Grasscutter Resources
+
+
+
+ Downloads Grasscutter resources into the currently set Grasscutter folder. This should be done unless you plan on getting resources externally.
+
+
+
+
+
@@ -194,6 +248,9 @@
+
+
+
@@ -204,6 +261,8 @@
+
+
diff --git a/resources/js/gcdownloader.js b/resources/js/gcdownloader.js
new file mode 100644
index 0000000..e45a0f4
--- /dev/null
+++ b/resources/js/gcdownloader.js
@@ -0,0 +1,102 @@
+async function clearGCInstallation() {
+ Neutralino.os.execCommand(`del /s /q "./gc"`)
+}
+
+async function setDownloadButtonsToLoading() {
+ const stableBtn = document.querySelector('#stableInstall')
+ const devBtn = document.querySelector('#devInstall')
+
+ stableBtn.innerText = localeObj.gcScriptRunning || 'Running...'
+
+ devBtn.innerText = localeObj.gcScriptRunning || 'Running...'
+
+ // Set btns to disabled
+ stableBtn.disabled = true
+ stableBtn.classList.add('disabled')
+
+ devBtn.disabled = true
+ devBtn.classList.add('disabled')
+}
+
+async function resetDownloadButtons() {
+ const stableBtn = document.querySelector('#stableInstall')
+ const devBtn = document.querySelector('#devInstall')
+
+ stableBtn.innerText = localeObj.stableInstall || 'Download'
+ devBtn.innerText = localeObj.devInstall || 'Download'
+
+ // Set btns to enabled
+ stableBtn.disabled = false
+ stableBtn.classList.remove('disabled')
+
+ devBtn.disabled = false
+ devBtn.classList.remove('disabled')
+}
+
+async function downloadGC(branch) {
+ const config = await getCfg()
+
+ // If we are pulling from a new branch, delete the old installation
+ if (config.grasscutterBranch !== branch) await clearGCInstallation()
+
+ // Set current installation in config
+ config.grasscutterBranch = branch
+
+ // Set gc path for people with launcher enabled
+ config.serverFolder = `${NL_CWD}/gc-${branch}/grasscutter.jar`
+
+ // Enable server launcher
+ config.serverLaunchPanel = true
+
+ Neutralino.storage.setData('config', JSON.stringify(config))
+
+ setDownloadButtonsToLoading()
+
+ // Keystore for branch (since they can differ)
+ const keystoreUrl = `https://github.com/Grasscutters/Grasscutter/raw/${branch}/keystore.p12`
+
+ // External service that allows un-authed artifact downloading
+ const artiUrl = `https://nightly.link/Grasscutters/Grasscutter/workflows/build/${branch}/Grasscutter.zip`
+
+ // For data files
+ const dataFiles = await axios.get(`https://api.github.com/repos/Grasscutters/Grasscutter/contents/data?ref=${branch}`)
+ const dataList = dataFiles.data
+ .map(file => ({ path: file.path, filename: file.name }))
+ .map(o => ({ url: `https://raw.githubusercontent.com/Grasscutters/Grasscutter/${branch}/${o.path}`, filename: o.filename }))
+
+ // For key files
+ const keyFiles = await axios.get(`https://api.github.com/repos/Grasscutters/Grasscutter/contents/keys?ref=${branch}`)
+ const keyList = keyFiles.data
+ .map(file => ({ path: file.path, filename: file.name }))
+ .map(o => ({ url: `https://raw.githubusercontent.com/Grasscutters/Grasscutter/${branch}/${o.path}`, filename: o.filename }))
+
+ const serverFolderFixed = config.serverFolder.match(/.*\\|.*\//g, '')[0].replace(/\//g, '\\')
+
+ // Ensure data and key folders exist
+
+ await Neutralino.os.execCommand(`mkdir ${serverFolderFixed}\\data`)
+ await Neutralino.os.execCommand(`mkdir ${serverFolderFixed}\\keys`)
+
+ // Download data files
+ for (const o of dataList) {
+ const folder = 'data'
+ await Neutralino.os.execCommand(`powershell Invoke-WebRequest -Uri ${o.url} -OutFile "${serverFolderFixed}\\${folder}\\${o.filename}"`)
+ }
+
+ // Download key files
+ for (const o of keyList) {
+ const folder = 'keys'
+ await Neutralino.os.execCommand(`powershell Invoke-WebRequest -Uri ${o.url} -OutFile "${serverFolderFixed}\\${folder}\\${o.filename}"`)
+ }
+
+ // Run installer
+ createCmdWindow(`.\\scripts\\gc_download.cmd ${artiUrl} ${keystoreUrl} ${branch}`)
+
+ // Fix buttons
+ resetDownloadButtons()
+
+ // Display folder after saving config
+ displayServerFolder()
+ enableServerButton()
+ displayServerLaunchSection()
+}
\ No newline at end of file
diff --git a/resources/js/helpers.js b/resources/js/helpers.js
index c0b31c0..4e3a38b 100644
--- a/resources/js/helpers.js
+++ b/resources/js/helpers.js
@@ -12,6 +12,7 @@
serverLaunchPanel: false,
language: 'en',
useHttps: true,
+ grasscutterBranch: '',
}
const cfgStr = await Neutralino.storage.getData('config').catch(e => {
// The data isn't set, so this is our first time opening
@@ -97,7 +98,7 @@ async function openGameFolder() {
async function openGrasscutterFolder() {
const config = await getCfg()
- const folder = config.serverFolder.match(/.*\\/g, '')[0]
+ const folder = config.serverFolder.match(/.*\\|.*\//g, '')[0]
openInExplorer(folder)
}
diff --git a/resources/js/index.js b/resources/js/index.js
index 98b22d8..a9a0bee 100644
--- a/resources/js/index.js
+++ b/resources/js/index.js
@@ -246,6 +246,30 @@ async function handleFavoriteList() {
}
}
+async function openDownloads() {
+ const downloads = document.querySelector('#downloadPanel')
+ const config = await getCfg()
+
+ if (downloads.style.display === 'none') {
+ downloads.style.removeProperty('display')
+ }
+
+ // Disable the resource download button if a serverFolder path is not set
+ if (!config.serverFolder) {
+ document.querySelector('#resourceInstall').disabled = true
+ document.querySelector('#resourceInstall').classList.add('disabled')
+ } else {
+ document.querySelector('#resourceInstall').disabled = false
+ document.querySelector('#resourceInstall').classList.remove('disabled')
+ }
+}
+
+async function closeDownloads() {
+ const downloads = document.querySelector('#downloadPanel')
+
+ downloads.style.display = 'none'
+}
+
async function openSettings() {
const settings = document.querySelector('#settingsPanel')
const config = await getCfg()
@@ -387,6 +411,8 @@ async function setGameExe() {
]
})
+ if (!gameExe[0]) return;
+
// Set the folder in our configuration
const config = await getCfg()
@@ -408,6 +434,8 @@ async function setGrasscutterFolder() {
]
})
+ if (!folder[0]) return;
+
// Set the folder in our configuration
const config = await getCfg()
diff --git a/resources/js/onLoad.js b/resources/js/onLoad.js
index a1e8773..bc96832 100644
--- a/resources/js/onLoad.js
+++ b/resources/js/onLoad.js
@@ -38,6 +38,7 @@
window.addEventListener("click", function(e) {
const favList = document.querySelector('#ipList')
const settingsPanel = document.querySelector('#settingsPanel')
+ const downloadPanel = document.querySelector('#downloadPanel')
// This will close the favorites list no matter what is clicked
if (favList.style.display !== 'none') {
@@ -46,23 +47,32 @@
}
// This will close the settings panel no matter what is clicked
- let settingCheckElm = e.target
+ let checkElm = e.target
- while(settingCheckElm.tagName !== 'BODY') {
- if (settingCheckElm.id === 'settingsPanel'
- || settingCheckElm.id === 'settingsBtn') {
+ while(checkElm.tagName !== 'BODY') {
+ if (checkElm.id === 'settingsPanel'
+ || checkElm.id === 'settingsBtn') {
return
}
- settingCheckElm = settingCheckElm.parentElement
+ if (checkElm.id === 'downloadPanel' ||
+ checkElm.id === 'downloadBtn') {
+ return
+ }
+
+ checkElm = checkElm.parentElement
}
// We travelled through the parents, so if we are at the body, we clicked outside of the settings panel
- if (settingCheckElm.tagName === 'BODY') {
+ if (checkElm.tagName === 'BODY') {
// This will close the settings panel only when something outside of it is clicked
if (settingsPanel.style.display !== 'none') {
settingsPanel.style.display = 'none'
}
+
+ if (downloadPanel.style.display !== 'none') {
+ downloadPanel.style.display = 'none'
+ }
}
});
diff --git a/resources/js/translation.js b/resources/js/translation.js
index 986b03c..dd80b83 100644
--- a/resources/js/translation.js
+++ b/resources/js/translation.js
@@ -90,6 +90,21 @@ async function doTranslation() {
set('loginPopupContentBodyBtnRegister', 'authRegisterBtn')
set('noLoginBtn', 'launchWithoutAuth')
+ // Downloads section
+ set('downloadTitle', 'downloadTitle')
+ set('grassclipperTitle', 'grassclipperTitle')
+ set('grasscutterTitle', 'grasscutterTitle')
+ set('installerTitle', 'installerTitle')
+ set('installerSubtitle', 'installerSubtitle')
+ set('downloadStable', 'downloadStable')
+ set('stableSubtitle', 'stableSubtitle')
+ set('downloadDev', 'downloadDev')
+ set('devSubtitle', 'downloadSubtitle')
+ set('downloadResources', 'downloadResources')
+ set('devSubtitle', 'devSubtitle')
+ set('stableInstall', 'stableInstall')
+ set('devInstall', 'devInstall')
+
// update notification
set('updateNotifText', 'updateNotifText')
}
\ No newline at end of file
diff --git a/resources/style/index.css b/resources/style/index.css
index ff31870..f738e24 100644
--- a/resources/style/index.css
+++ b/resources/style/index.css
@@ -11,6 +11,10 @@ a {
color: #fff;
}
+img {
+ height: 20px;
+}
+
.darken {
filter: brightness(0.6);
}
@@ -157,6 +161,7 @@ a {
#firstTimeNotice,
#loginPanel,
+#downloadPanel,
#settingsPanel {
display: block;
position: absolute;
@@ -172,18 +177,21 @@ a {
font-family: system-ui;
}
+#downloadPanel,
#settingsPanel {
width: 35%;
height: 80%;
overflow: auto;
}
+#downloadTitle,
#fullSettingsTitle {
font-size: 1.5em;
font-weight: bold;
margin-bottom: 10px;
}
+#downloadPanelInner,
#settingsPanelInner {
display: flex;
flex-direction: column;
@@ -192,16 +200,23 @@ a {
padding: 10px 10%;
}
+.downloadRow,
.settingsRow {
width: 100%;
}
+.downloadTitle,
.settingTitle {
font-size: 1.2em;
font-weight: bold;
margin-bottom: 10px;
}
+.downloadTitle {
+ margin: 6px;
+}
+
+.downloadLabel,
.settingLabel {
display:inline-block;
font-size: 1em;
@@ -209,12 +224,14 @@ a {
margin: 10px 0px;
}
+.downloadSubtitle,
.settingSubtitle {
color: rgb(165, 165, 165);
font-size: 0.8em;
font-weight: normal;
}
+.downloadSection,
.settingSection {
display: flex;
flex-direction: row;
@@ -222,10 +239,12 @@ a {
justify-content: space-between;
}
+.downloadSection .smolBtn,
.settingSection .smolBtn {
height: 30px;
}
+#downloadTitleBar,
#settingsTitleBar {
display: flex;
flex-direction: row;
@@ -233,15 +252,18 @@ a {
justify-content: space-between;
}
+#downloadClose,
#settingsClose {
display: inline-block;
transition: filter 0.1s ease-in-out;
}
+#downloadClose img,
#settingsClose img {
height: 20px;
}
+#downloadClose:hover,
#settingsClose:hover {
filter: invert(85%) sepia(31%) saturate(560%) hue-rotate(329deg) brightness(100%) contrast(92%);
cursor: pointer;
diff --git a/scripts/gc_download.cmd b/scripts/gc_download.cmd
new file mode 100644
index 0000000..6d822d2
--- /dev/null
+++ b/scripts/gc_download.cmd
@@ -0,0 +1,58 @@
+@echo off
+
+set KEYSTORE_URL=%1
+set ARTIFACT_URL=%2
+set BRANCH=%3
+set FOLDER_NAME=".\gc-%BRANCH%"
+set FOLDER_NAME=%FOLDER_NAME:"=%
+
+title GC Download Script
+
+if not exist "%FOLDER_NAME%" mkdir "%FOLDER_NAME%"
+if not exist ".\temp" mkdir ".\temp"
+
+echo Downloading Grasscutter prebuilt jar...
+
+:: Download the jar
+powershell Invoke-WebRequest -Uri %KEYSTORE_URL% -OutFile "./temp/gcjar.zip"
+
+echo Extracting...
+
+:: Delete old file if there is one there
+if exist "%FOLDER_NAME%\grasscutter.jar" del "%FOLDER_NAME%\grasscutter.jar"
+
+powershell Expand-Archive -Path "./temp/gcjar.zip" -DestinationPath "%FOLDER_NAME%" -Force
+
+:: Find the jar file name and rename it, just in case
+for %%i in (%FOLDER_NAME%/*) do (
+ :: If the extension is jar, rename the file
+ if %%~xi equ .jar rename "%FOLDER_NAME%\%%i" grasscutter.jar
+)
+
+echo Downloading keystore.p12...
+
+:: Download the keystore.p12 file
+powershell Invoke-WebRequest -Uri %ARTIFACT_URL% -OutFile "./%FOLDER_NAME%/keystore.p12"
+
+:: Check java version, this will automatically output some tuff
+call .\scripts\javaver.cmd %BRANCH%
+
+:: Allow resource downloading to be optional, since it takes a while
+set REPLY=y
+set /p "REPLY=Download server resources? (This can take a while) [y|n]:"
+if /i not "%reply%" == "y" goto :finish
+
+call .\scripts\resources_download.cmd %FOLDER_NAME%
+
+goto :finish
+
+:finish
+ :: Remove temp stuff
+ del /s /q "./temp"
+
+ echo Done, latest Grasscutter %BRANCH% now downloaded in %FOLDER_NAME%
+
+ pause
+
+ taskkill /f /fi "WINDOWTITLE eq GC Download Script"
+
diff --git a/scripts/install.cmd b/scripts/install.cmd
index 99e27c3..63fbd41 100644
--- a/scripts/install.cmd
+++ b/scripts/install.cmd
@@ -3,6 +3,8 @@
set ORIGIN=%1
set ORIGIN=%ORIGIN:"=%
+title Grassclipper Installer
+
echo Downloading proxy server...
:: Make sure we are in the right directory
@@ -35,12 +37,12 @@ taskkill /f /im mitmdump.exe
echo Adding ceritifcate...
:: Ensure we are elevated for certs
->nul 2>&1 certutil -addstore root %USERPROFILE%\.mitmproxy\mitmproxy-ca-cert.cer || (
+>nul 2>&1 certutil -addstore root "%USERPROFILE%\.mitmproxy\mitmproxy-ca-cert.cer" || (
echo ============================================================================================================
echo !! Certificate install failed !!
echo.
echo Please manually run this command as Administrator:
- echo certutil -addstore root %USERPROFILE%\.mitmproxy\mitmproxy-ca-cert.cer
+ echo certutil -addstore root "%USERPROFILE%\.mitmproxy\mitmproxy-ca-cert.cer"
echo ============================================================================================================
)
@@ -48,4 +50,4 @@ echo Done! You can now open GrassClipper.exe!
pause
-exit /b
\ No newline at end of file
+taskkill /f /fi "WINDOWTITLE eq Grassclipper Installer"
\ No newline at end of file
diff --git a/scripts/javaver.cmd b/scripts/javaver.cmd
new file mode 100644
index 0000000..c22093a
--- /dev/null
+++ b/scripts/javaver.cmd
@@ -0,0 +1,70 @@
+@echo off
+
+set BRANCH=%1
+
+echo Checking java version...
+
+where java >nul 2>nul
+if %errorlevel%==1 (
+ echo =======================================================================================
+ echo No version of Java was found!
+
+ if %BRANCH% EQU stable (
+ echo To launch the stable branch server, you must install Java 8
+ )
+
+ if %BRANCH% EQU development (
+ echo To launch the development branch server, you must install Java 17
+ )
+
+ echo =======================================================================================
+
+ exit /b
+)
+
+:: https://stackoverflow.com/questions/5675459/how-to-get-java-version-from-batch-script
+for /f "tokens=3" %%g in ('java -version 2^>^&1 ^| findstr /i "version"') do (
+ @echo Output: %%g
+ set JAVAVER=%%g
+)
+set JAVAVER=%JAVAVER:"=%
+
+for /f "delims=. tokens=1-3" %%v in ("%JAVAVER%") do (
+ set MAJOR=%%v
+ set MINOR=%%w
+ set BUILD=%%x
+)
+
+if %BRANCH% EQU stable (
+ :: Ensure java 8
+ if %MAJOR% EQU 1 (
+ if %MINOR% LSS 8 (
+ echo =======================================================================================
+ echo !! Java version is less than 8 !!
+ echo Please download Java 8 to ensure %BRANCH% branch server launches correctly.
+ echo =======================================================================================
+ exit /b
+ )
+ )
+
+ if %MAJOR% NEQ 1 (
+ echo =======================================================================================
+ echo !! Java version is not 8 !!
+ echo Please download Java 8 to ensure %BRANCH% branch server launches correctly.
+ echo =======================================================================================
+ exit /b
+ )
+)
+
+if %BRANCH% EQU development (
+ :: Ensure java 17
+ if %MAJOR% LSS 17 (
+ echo =======================================================================================
+ echo !! Java version is less than 17 !!
+ echo Please download Java 17 to ensure %BRANCH% branch server launches correctly.
+ echo =======================================================================================
+ exit /b
+ )
+)
+
+echo Java version is compatible
\ No newline at end of file
diff --git a/scripts/local_server_launch.cmd b/scripts/local_server_launch.cmd
index e052d1a..75dde1f 100644
--- a/scripts/local_server_launch.cmd
+++ b/scripts/local_server_launch.cmd
@@ -3,15 +3,23 @@
set GRASSCUTTER_JAR=%1
set GRASSCUTTER_JAR=%GRASSCUTTER_JAR:"=%
+title Grasscutter
+
:: Get folder the jar is in
set "X=%GRASSCUTTER_JAR%"
:l
-if "%X:~-1%"=="\" goto al
-set "X=%X:~0,-1%"
-goto l
+ set IS_SLASH=false
+
+ if "%X:~-1%"=="\" set IS_SLASH=true
+ if "%X:~-1%"=="/" set IS_SLASH=true
+
+ if %IS_SLASH% equ true goto al
+
+ set "X=%X:~0,-1%"
+ goto l
:al
-set "X=%X:~0,-1%"
-set "GRASSCUTTER_ROOT=%X%"
+ set "X=%X:~0,-1%"
+ set "GRASSCUTTER_ROOT=%X%"
echo Starting local Grasscutter server...
diff --git a/scripts/private_server_launch.cmd b/scripts/private_server_launch.cmd
index 0cc7b12..5fcbf1e 100644
--- a/scripts/private_server_launch.cmd
+++ b/scripts/private_server_launch.cmd
@@ -27,7 +27,7 @@ if "%ENABLE_KILLSWITCH%" EQU "true" (
:: Restart in elevated if need be
>nul 2>&1 reg query "HKU\S-1-5-19" || (
set params = %*:"="""%
- cd /d "%~dp0" && ( if exist "%temp%\getadmin.vbs" del "%temp%\getadmin.vbs" ) && fsutil dirty query %systemdrive% 1>nul 2>nul || ( echo Set UAC = CreateObject^("Shell.Application"^) : UAC.ShellExecute "cmd.exe", "/k cd ""%~sdp0"" && %~s0 %1 %2 %3 "%4" ""%cd%/../"" %6", "", "runas", 1 >> "%temp%\getadmin.vbs" && "%temp%\getadmin.vbs" && taskkill /f /fi "WINDOWTITLE eq PS Launcher Script" && exit /b )
+ cd /d "%~dp0" && ( if exist "%temp%\getadmin.vbs" del "%temp%\getadmin.vbs" ) && fsutil dirty query %systemdrive% 1>nul 2>nul || ( echo Set UAC = CreateObject^("Shell.Application"^) : UAC.ShellExecute "cmd.exe", "/k cd ""%~sdp0"" && %~s0 %1 %2 %3 "%4" ""%cd%"" %6", "", "runas", 1 >> "%temp%\getadmin.vbs" && "%temp%\getadmin.vbs" && taskkill /f /fi "WINDOWTITLE eq PS Launcher Script" && exit /b )
)
)
@@ -41,7 +41,7 @@ reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings" /v Pr
reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings" /v ProxyServer /d "127.0.0.1:8080" /f >nul 2>nul
:: Start proxy server
-start "Proxy Server" "%ORIGIN%/ext/mitmdump.exe" -s "%ORIGIN%/proxy/proxy.py" -k --allow-hosts ".*\.yuanshen\.com|.*\.mihoyo\.com|.*\.hoyoverse\.com" --ssl-insecure --set ip=%IP% --set port=%PORT% --set use_https=%USE_HTTPS%
+start "Proxy Server" "%ORIGIN%\ext\mitmdump.exe" -s "%ORIGIN%/proxy/proxy.py" -k --allow-hosts ".*\.yuanshen\.com|.*\.mihoyo\.com|.*\.hoyoverse\.com" --ssl-insecure --set ip=%IP% --set port=%PORT% --set use_https=%USE_HTTPS%
echo Opening %GAME_PATH%
diff --git a/scripts/resources_download.cmd b/scripts/resources_download.cmd
new file mode 100644
index 0000000..f1e1574
--- /dev/null
+++ b/scripts/resources_download.cmd
@@ -0,0 +1,29 @@
+@echo off
+
+set FOLDER_NAME=%1
+set FOLDER_NAME=%FOLDER_NAME:"=%
+
+if not exist ".\temp" mkdir ".\temp"
+if not exist ".\resources" mkdir ".\resources"
+
+echo Downloading resources, this can take a while...
+
+:: Grab the giant ass resource zip
+powershell Invoke-WebRequest -Uri https://github.com/Koko-boya/Grasscutter_Resources/archive/refs/heads/main.zip -OutFile "./temp/resources.zip"
+
+echo Extracting...
+
+:: Extract resources to the folder
+powershell Expand-Archive -Path "./temp/resources.zip" -DestinationPath "%FOLDER_NAME%" -Force
+
+:: Delete old resources folder if there is one there
+del /s /q "%FOLDER_NAME%\resources">nul
+
+echo Moving resources to folder...
+
+robocopy "%FOLDER_NAME%\Grasscutter_Resources-main\Resources" "%FOLDER_NAME%\resources" /E /MOVE>nul
+
+:: Delete straggling files
+del /s /q "%FOLDER_NAME%\Grasscutter_Resources-main"
+
+echo Done, resources should be properly extracted
\ No newline at end of file