Some globalisation stuff

Game executable now selectable in settings. Translations need updating for "Set Game Path".
This commit is contained in:
Benj 2022-07-09 15:11:19 +08:00
parent 99687f0550
commit f35b596eb2
13 changed files with 26442 additions and 105 deletions

26278
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -11,7 +11,7 @@
"files_extracting": "文件解压中:" "files_extracting": "文件解压中:"
}, },
"options": { "options": {
"game_exec": "选择游戏可执行文件", "game_executable": "选择游戏可执行文件",
"grasscutter_jar": "选择 Grasscutter JAR 文件", "grasscutter_jar": "选择 Grasscutter JAR 文件",
"java_path": "设置自定义 Java 路径", "java_path": "设置自定义 Java 路径",
"grasscutter_with_game": "随游戏自动启动 Grasscutter", "grasscutter_with_game": "随游戏自动启动 Grasscutter",

View File

@ -13,7 +13,7 @@
"options": { "options": {
"enabled": "已啟用", "enabled": "已啟用",
"disabled": "未啟用", "disabled": "未啟用",
"game_exec": "選擇遊戲執行檔", "game_executable": "選擇遊戲執行檔",
"grasscutter_jar": "選擇伺服器JAR檔案", "grasscutter_jar": "選擇伺服器JAR檔案",
"toggle_encryption": "設定加密", "toggle_encryption": "設定加密",
"java_path": "設定自定義Java路徑", "java_path": "設定自定義Java路徑",

View File

@ -13,7 +13,7 @@
"options": { "options": {
"enabled": "Aktiviert", "enabled": "Aktiviert",
"disabled": "Deaktiviert", "disabled": "Deaktiviert",
"game_exec": "Spiel Datei auswählen", "game_executable": "Spiel Datei auswählen",
"grasscutter_jar": "Grasscuter JAR auswählen", "grasscutter_jar": "Grasscuter JAR auswählen",
"toggle_encryption": "Verschlüsselung umschalten", "toggle_encryption": "Verschlüsselung umschalten",
"java_path": "Benutzerdefinierten Java Pfad setzen", "java_path": "Benutzerdefinierten Java Pfad setzen",

View File

@ -13,7 +13,8 @@
"options": { "options": {
"enabled": "Enabled", "enabled": "Enabled",
"disabled": "Disabled", "disabled": "Disabled",
"game_exec": "Set Game Executable", "game_path": "Set Game Install Path",
"game_executable": "Set Game Executable",
"grasscutter_jar": "Set Grasscutter JAR", "grasscutter_jar": "Set Grasscutter JAR",
"toggle_encryption": "Toggle Encryption", "toggle_encryption": "Toggle Encryption",
"java_path": "Set Custom Java Path", "java_path": "Set Custom Java Path",
@ -57,7 +58,6 @@
"gc_dev_jar": "Download the latest development Grasscutter build, which includes jar file and data files.", "gc_dev_jar": "Download the latest development Grasscutter build, which includes jar file and data files.",
"gc_stable_data": "Download the current stable Grasscutter data files, which does not come with a jar file. This is useful for updating.", "gc_stable_data": "Download the current stable Grasscutter data files, which does not come with a jar file. This is useful for updating.",
"gc_dev_data": "Download the latest development Grasscutter data files, which does not come with a jar file. This is useful for updating.", "gc_dev_data": "Download the latest development Grasscutter data files, which does not come with a jar file. This is useful for updating.",
"resources": "These are also required to run a Grasscutter server. This button will be grey if you have an existing resources folder with contents inside", "resources": "These are also required to run a Grasscutter server. This button will be grey if you have an existing resources folder with contents inside"
"game": "This will download a fresh copy of \"the certain anime game\" and set your game executable to it. This is useful if you don't want to patch your main game."
} }
} }

View File

@ -13,7 +13,7 @@
"options": { "options": {
"enabled": "active", "enabled": "active",
"disabled": "desactiver", "disabled": "desactiver",
"game_exec": "definir l'executable du jeu", "game_executable": "definir l'executable du jeu",
"grasscutter_jar": "definir le Jar Grasscutter", "grasscutter_jar": "definir le Jar Grasscutter",
"toggle_encryption": "activer l'encryption", "toggle_encryption": "activer l'encryption",
"java_path": "definir un chemin java personnalise", "java_path": "definir un chemin java personnalise",

View File

@ -10,7 +10,7 @@
"files_extracting": "MengExtract File: " "files_extracting": "MengExtract File: "
}, },
"options": { "options": {
"game_exec": "Set Game Executable", "game_executable": "Set Game Executable",
"grasscutter_jar": "Path ke Grasscutter JAR", "grasscutter_jar": "Path ke Grasscutter JAR",
"java_path": "Atur kustom Java path", "java_path": "Atur kustom Java path",
"grasscutter_with_game": "Otomatis Menjalankan Grasscutter Dengan Game", "grasscutter_with_game": "Otomatis Menjalankan Grasscutter Dengan Game",

View File

@ -13,7 +13,7 @@
"options": { "options": {
"enabled": "Iespējots", "enabled": "Iespējots",
"disabled": "Atspējots", "disabled": "Atspējots",
"game_exec": "Iestatīt spēles izpildāmu", "game_executable": "Iestatīt spēles izpildāmu",
"grasscutter_jar": "Iestatiet Grasscutter JAR", "grasscutter_jar": "Iestatiet Grasscutter JAR",
"toggle_encryption": "Pārslēgt Šifrēšanu", "toggle_encryption": "Pārslēgt Šifrēšanu",
"java_path": "Iestatiet pielāgotu Java ceļu", "java_path": "Iestatiet pielāgotu Java ceļu",

View File

@ -13,7 +13,7 @@
"options": { "options": {
"enabled": "Включено", "enabled": "Включено",
"disabled": "Выключено", "disabled": "Выключено",
"game_exec": "Установить исполняемый файл игры", "game_executable": "Установить исполняемый файл игры",
"grasscutter_jar": "Установить Grasscutter JAR", "grasscutter_jar": "Установить Grasscutter JAR",
"toggle_encryption": "Переключить шифрование", "toggle_encryption": "Переключить шифрование",
"java_path": "Установить пользовательский путь Java", "java_path": "Установить пользовательский путь Java",

View File

@ -3,7 +3,7 @@ import Checkbox from './common/Checkbox'
import BigButton from './common/BigButton' import BigButton from './common/BigButton'
import TextInput from './common/TextInput' import TextInput from './common/TextInput'
import HelpButton from './common/HelpButton' import HelpButton from './common/HelpButton'
import { getConfig, saveConfig, setConfigOption } from '../../utils/configuration' import { Configuration, getConfig, saveConfig, setConfigOption } from '../../utils/configuration'
import { translate } from '../../utils/language' import { translate } from '../../utils/language'
import { invoke } from '@tauri-apps/api/tauri' import { invoke } from '@tauri-apps/api/tauri'
@ -72,6 +72,24 @@ export default class ServerLaunchSection extends React.Component<IProps, IState>
}) })
} }
getGameExecutable(config : Configuration) {
if(!config.game_install_path || !config.game_executable) {
alert('Game executable and/or path not set!')
return null
}
return config.game_install_path + '\\' + config.game_executable
}
getGameMetadataLocation(config : Configuration) {
if(!config.game_install_path || !config.game_executable) {
alert('Game executable and/or path not set!')
return null
}
return config.game_install_path + '\\' + config.game_executable.replace('.exe', '_Data') + '\\Managed\\Metadata'
}
async toggleGrasscutter() { async toggleGrasscutter() {
const config = await getConfig() const config = await getConfig()
@ -89,7 +107,7 @@ export default class ServerLaunchSection extends React.Component<IProps, IState>
const config = await getConfig() const config = await getConfig()
// Copy unpatched metadata to backup location // Copy unpatched metadata to backup location
if(await invoke('copy_file_with_new_name', { path: config.game_install_path + '\\GenshinImpact_Data\\Managed\\Metadata\\global-metadata.dat', newPath: await dataDir() + 'cultivation\\metadata', newName: 'global-metadata-unpatched.dat' })) { if(await invoke('copy_file_with_new_name', { path: this.getGameMetadataLocation(config) + '\\global-metadata.dat', newPath: await dataDir() + 'cultivation\\metadata', newName: 'global-metadata-unpatched.dat' })) {
// Backup successful // Backup successful
// Patch backedup metadata // Patch backedup metadata
@ -97,7 +115,7 @@ export default class ServerLaunchSection extends React.Component<IProps, IState>
// Patch successful // Patch successful
// Replace game metadata with patched metadata // Replace game metadata with patched metadata
if(!(await invoke('copy_file_with_new_name', { path: await dataDir() + 'cultivation/metadata/global-metadata-patched.dat', newPath: config.game_install_path + '\\GenshinImpact_Data\\Managed\\Metadata', newName: 'global-metadata.dat' }))) { if(!(await invoke('copy_file_with_new_name', { path: await dataDir() + 'cultivation/metadata/global-metadata-patched.dat', newPath: this.getGameMetadataLocation(config), newName: 'global-metadata.dat' }))) {
// Replace failed // Replace failed
alert('Failed to replace game metadata!') alert('Failed to replace game metadata!')
return return
@ -114,8 +132,10 @@ export default class ServerLaunchSection extends React.Component<IProps, IState>
async playGame() { async playGame() {
const config = await getConfig() const config = await getConfig()
if (!config.game_install_path) return alert('Game path not set!') if(this.getGameExecutable(config) == null) {
return
}
// Connect to proxy // Connect to proxy
if (config.toggle_grasscutter) { if (config.toggle_grasscutter) {
@ -124,13 +144,13 @@ export default class ServerLaunchSection extends React.Component<IProps, IState>
// Assume metadata has been patched // Assume metadata has been patched
// Compare metadata files // Compare metadata files
if (!(await invoke('are_files_identical', { path1: await dataDir() + 'cultivation/metadata/global-metadata-patched.dat', path2: config.game_install_path + '\\GenshinImpact_Data\\Managed\\Metadata\\global-metadata.dat'}))) { if (!(await invoke('are_files_identical', { path1: await dataDir() + 'cultivation/metadata/global-metadata-patched.dat', path2: this.getGameMetadataLocation(config) + '\\global-metadata.dat'}))) {
// Metadata is not patched // Metadata is not patched
// Check to see if unpatched backup matches the game's version // Check to see if unpatched backup matches the game's version
if (await invoke('are_files_identical', { path1: await dataDir() + 'cultivation/metadata/global-metadata-unpatched.dat', path2: config.game_install_path + '\\GenshinImpact_Data\\Managed\\Metadata\\global-metadata.dat'})) { if (await invoke('are_files_identical', { path1: await dataDir() + 'cultivation/metadata/global-metadata-unpatched.dat', path2: this.getGameMetadataLocation(config) + '\\global-metadata.dat'})) {
// Game's metadata is not patched, so we need to patch it // Game's metadata is not patched, so we need to patch it
if(!(await invoke('copy_file_with_new_name', { path: await dataDir() + 'cultivation/metadata/global-metadata-patched.dat', newPath: config.game_install_path + '\\GenshinImpact_Data\\Managed\\Metadata', newName: 'global-metadata.dat' }))) { if(!(await invoke('copy_file_with_new_name', { path: await dataDir() + 'cultivation/metadata/global-metadata-patched.dat', newPath: this.getGameMetadataLocation(config), newName: 'global-metadata.dat' }))) {
// Replace failed // Replace failed
alert('Failed to replace game metadata!') alert('Failed to replace game metadata!')
return return
@ -140,7 +160,7 @@ export default class ServerLaunchSection extends React.Component<IProps, IState>
alert('Deleting old metadata') alert('Deleting old metadata')
// Delete backed up metadata // Delete backed up metadata
if(!(await invoke('delete_file', { path: await dataDir() + 'cultivation/metadata/global-metadata-unpatched.dat' }) && !(await invoke('delete_file', { path: await dataDir() + 'cultivation/metadata/global-metadata-patched.dat' })))) { if(!(await invoke('delete_file', { path: await dataDir() + 'cultivation/metadata/global-metadata-unpatched.dat' })) && !(await invoke('delete_file', { path: await dataDir() + 'cultivation/metadata/global-metadata-patched.dat' }))) {
// Delete failed // Delete failed
alert('Failed to delete backed up metadata!') alert('Failed to delete backed up metadata!')
return return
@ -154,12 +174,12 @@ export default class ServerLaunchSection extends React.Component<IProps, IState>
await this.patchMetadata() await this.patchMetadata()
} }
let game_exe = config.game_install_path + '\\GenshinImpact.exe' let game_exe = this.getGameExecutable(config) as string
if (game_exe.includes('\\')) { if (game_exe.includes('\\')) {
game_exe = game_exe.substring(config.game_install_path.lastIndexOf('\\') + 1) game_exe = game_exe.substring((this.getGameExecutable(config) as string).lastIndexOf('\\') + 1)
} else { } else {
game_exe = game_exe.substring(config.game_install_path.lastIndexOf('/') + 1) game_exe = game_exe.substring((this.getGameExecutable(config) as string).lastIndexOf('/') + 1)
} }
// Save last connected server and port // Save last connected server and port
@ -197,9 +217,9 @@ export default class ServerLaunchSection extends React.Component<IProps, IState>
// Check if metadata is patched // Check if metadata is patched
// Compare metadata files // Compare metadata files
if (await invoke('are_files_identical', { path1: await dataDir() + 'cultivation/metadata/global-metadata-patched.dat', path2: config.game_install_path + '\\GenshinImpact_Data\\Managed\\Metadata\\global-metadata.dat'})) { if (await invoke('are_files_identical', { path1: await dataDir() + 'cultivation/metadata/global-metadata-patched.dat', path2: this.getGameMetadataLocation(config) + '\\global-metadata.dat'})) {
// Metadata is patched, so we need to unpatch it // Metadata is patched, so we need to unpatch it
if(!(await invoke('copy_file_with_new_name', { path: await dataDir() + 'cultivation/metadata/global-metadata-unpatched.dat', newPath: config.game_install_path + '\\GenshinImpact_Data\\Managed\\Metadata', newName: 'global-metadata.dat' }))) { if(!(await invoke('copy_file_with_new_name', { path: await dataDir() + 'cultivation/metadata/global-metadata-unpatched.dat', newPath: this.getGameMetadataLocation(config), newName: 'global-metadata.dat' }))) {
// Replace failed // Replace failed
alert('Failed to unpatch game metadata!') alert('Failed to unpatch game metadata!')
return return
@ -210,11 +230,11 @@ export default class ServerLaunchSection extends React.Component<IProps, IState>
// Launch the program // Launch the program
const gameExists = await invoke('dir_exists', { const gameExists = await invoke('dir_exists', {
path: config.game_install_path + '\\GenshinImpact.exe' path: this.getGameExecutable(config)
}) })
if (gameExists) await invoke('run_program', { path: config.game_install_path + '\\GenshinImpact.exe' }) if (gameExists) await invoke('run_program', { path: this.getGameExecutable(config) })
else alert('Game not found! At: ' + config.game_install_path) else alert('Game not found! At: ' + this.getGameExecutable(config))
} }
async launchServer() { async launchServer() {

View File

@ -14,7 +14,8 @@ interface IProps {
readonly?: boolean readonly?: boolean
placeholder?: string placeholder?: string
folder?: boolean folder?: boolean
customClearBehaviour?: () => void customClearBehaviour?: () => void,
openFolder?: string
} }
interface IState { interface IState {
@ -67,10 +68,12 @@ export default class DirInput extends React.Component<IProps, IState> {
directory: true directory: true
}) })
} else { } else {
console.log(this.props.openFolder)
path = await open({ path = await open({
filters: [ filters: [
{ name: 'Files', extensions: this.props.extensions || ['*'] } { name: 'Files', extensions: this.props.extensions || ['*'] }
] ],
defaultPath: this.props.openFolder
}) })
} }

View File

@ -18,16 +18,17 @@ interface IProps {
} }
interface IState { interface IState {
game_install_path: string game_install_path: string;
grasscutter_path: string game_executable: string;
java_path: string grasscutter_path: string;
grasscutter_with_game: boolean java_path: string;
language_options: { [key: string]: string }[], grasscutter_with_game: boolean;
current_language: string language_options: { [key: string]: string }[];
bg_url_or_path: string current_language: string;
themes: string[] bg_url_or_path: string;
theme: string themes: string[];
encryption: boolean theme: string;
encryption: boolean;
} }
export default class Options extends React.Component<IProps, IState> { export default class Options extends React.Component<IProps, IState> {
@ -36,6 +37,7 @@ export default class Options extends React.Component<IProps, IState> {
this.state = { this.state = {
game_install_path: '', game_install_path: '',
game_executable: '',
grasscutter_path: '', grasscutter_path: '',
java_path: '', java_path: '',
grasscutter_with_game: false, grasscutter_with_game: false,
@ -44,10 +46,11 @@ export default class Options extends React.Component<IProps, IState> {
bg_url_or_path: '', bg_url_or_path: '',
themes: ['default'], themes: ['default'],
theme: '', theme: '',
encryption: false encryption: false,
} }
this.setGameExec = this.setGameExec.bind(this) this.setGamePath = this.setGamePath.bind(this)
this.setGameExecutable = this.setGameExecutable.bind(this)
this.setGrasscutterJar = this.setGrasscutterJar.bind(this) this.setGrasscutterJar = this.setGrasscutterJar.bind(this)
this.setJavaPath = this.setJavaPath.bind(this) this.setJavaPath = this.setJavaPath.bind(this)
this.toggleGrasscutterWithGame = this.toggleGrasscutterWithGame.bind(this) this.toggleGrasscutterWithGame = this.toggleGrasscutterWithGame.bind(this)
@ -58,7 +61,7 @@ export default class Options extends React.Component<IProps, IState> {
async componentDidMount() { async componentDidMount() {
const config = await getConfig() const config = await getConfig()
const languages = await getLanguages() const languages = await getLanguages()
// Remove jar from path // Remove jar from path
const path = config.grasscutter_path.replace(/\\/g, '/') const path = config.grasscutter_path.replace(/\\/g, '/')
const folderPath = path.substring(0, path.lastIndexOf('/')) const folderPath = path.substring(0, path.lastIndexOf('/'))
@ -66,33 +69,52 @@ export default class Options extends React.Component<IProps, IState> {
this.setState({ this.setState({
game_install_path: config.game_install_path || '', game_install_path: config.game_install_path || '',
game_executable: config.game_executable || '',
grasscutter_path: config.grasscutter_path || '', grasscutter_path: config.grasscutter_path || '',
java_path: config.java_path || '', java_path: config.java_path || '',
grasscutter_with_game: config.grasscutter_with_game || false, grasscutter_with_game: config.grasscutter_with_game || false,
language_options: languages, language_options: languages,
current_language: config.language || 'en', current_language: config.language || 'en',
bg_url_or_path: config.customBackground || '', bg_url_or_path: config.customBackground || '',
themes: (await getThemeList()).map(t => t.name), themes: (await getThemeList()).map((t) => t.name),
theme: config.theme || 'default', theme: config.theme || 'default',
encryption: await translate(encEnabled ? 'options.enabled' : 'options.disabled') encryption: await translate(encEnabled ? 'options.enabled' : 'options.disabled'),
}) })
this.forceUpdate() this.forceUpdate()
} }
setGameExec(value: string) { setGamePath(value: string) {
setConfigOption('game_install_path', value) setConfigOption('game_install_path', value)
this.setState({ this.setState({
game_install_path: value game_install_path: value,
}) })
} }
setGameExecutable(value: string) {
if (this.state.game_install_path != '') {
if (value.includes(this.state.game_install_path)) {
value = value.replace(this.state.game_install_path + '\\', '')
setConfigOption('game_executable', value)
this.setState({
game_executable: value,
})
} else {
alert('Game executable must be inside the game folder!')
}
} else {
alert('Please set the game install path first!')
}
}
setGrasscutterJar(value: string) { setGrasscutterJar(value: string) {
setConfigOption('grasscutter_path', value) setConfigOption('grasscutter_path', value)
this.setState({ this.setState({
grasscutter_path: value grasscutter_path: value,
}) })
} }
@ -100,7 +122,7 @@ export default class Options extends React.Component<IProps, IState> {
setConfigOption('java_path', value) setConfigOption('java_path', value)
this.setState({ this.setState({
java_path: value java_path: value,
}) })
} }
@ -119,7 +141,7 @@ export default class Options extends React.Component<IProps, IState> {
setConfigOption('grasscutter_with_game', changedVal) setConfigOption('grasscutter_with_game', changedVal)
this.setState({ this.setState({
grasscutter_with_game: changedVal grasscutter_with_game: changedVal,
}) })
} }
@ -130,16 +152,16 @@ export default class Options extends React.Component<IProps, IState> {
if (!isUrl) { if (!isUrl) {
const filename = value.replace(/\\/g, '/').split('/').pop() const filename = value.replace(/\\/g, '/').split('/').pop()
const localBgPath = (await dataDir() as string).replace(/\\/g, '/') const localBgPath = ((await dataDir()) as string).replace(/\\/g, '/')
await setConfigOption('customBackground', `${localBgPath}/cultivation/bg/${filename}`) await setConfigOption('customBackground', `${localBgPath}/cultivation/bg/${filename}`)
// Copy the file over to the local directory // Copy the file over to the local directory
await invoke('copy_file', { await invoke('copy_file', {
path: value.replace(/\\/g, '/'), path: value.replace(/\\/g, '/'),
newPath: `${localBgPath}cultivation/bg/` newPath: `${localBgPath}cultivation/bg/`,
}) })
window.location.reload() window.location.reload()
} else { } else {
await setConfigOption('customBackground', value) await setConfigOption('customBackground', value)
@ -163,68 +185,81 @@ export default class Options extends React.Component<IProps, IState> {
await server.toggleEncryption(folderPath + '/config.json') await server.toggleEncryption(folderPath + '/config.json')
this.setState({ this.setState({
encryption: await translate(await server.encryptionEnabled(folderPath + '/config.json') ? 'options.enabled' : 'options.disabled') encryption: await translate(
(await server.encryptionEnabled(folderPath + '/config.json')) ? 'options.enabled' : 'options.disabled'
),
}) })
} }
render() { render() {
return ( return (
<Menu closeFn={this.props.closeFn} className="Options" heading="Options"> <Menu closeFn={this.props.closeFn} className="Options" heading="Options">
<div className='OptionSection' id="menuOptionsContainerGameExec"> <div className="OptionSection" id="menuOptionsContainerGamePath">
<div className='OptionLabel' id="menuOptionsLabelGameExec"> <div className="OptionLabel" id="menuOptionsLabelGamePath">
<Tr text="options.game_exec" /> <Tr text="options.game_path" />
</div> </div>
<div className='OptionValue' id="menuOptionsDirGameExec"> <div className="OptionValue" id="menuOptionsDirGamePath">
<DirInput onChange={this.setGameExec} value={this.state?.game_install_path} folder={true} /> <DirInput onChange={this.setGamePath} value={this.state?.game_install_path} folder={true} />
</div> </div>
</div> </div>
<div className='OptionSection' id="menuOptionsContainerGCJar"> <div className="OptionSection" id="menuOptionsContainerGameExecutable">
<div className='OptionLabel' id="menuOptionsLabelGCJar"> <div className="OptionLabel" id="menuOptionsLabelGameExecutable">
<Tr text="options.game_executable" />
</div>
<div className="OptionValue" id="menuOptionsDirGameExecutable">
<DirInput onChange={this.setGameExecutable} value={this.state?.game_executable} folder={false} extensions={['exe']} openFolder={this.state?.game_install_path} />
</div>
</div>
<div className="OptionSection" id="menuOptionsContainerGCJar">
<div className="OptionLabel" id="menuOptionsLabelGCJar">
<Tr text="options.grasscutter_jar" /> <Tr text="options.grasscutter_jar" />
</div> </div>
<div className='OptionValue' id="menuOptionsDirGCJar"> <div className="OptionValue" id="menuOptionsDirGCJar">
<DirInput onChange={this.setGrasscutterJar} value={this.state?.grasscutter_path} extensions={['jar']} /> <DirInput onChange={this.setGrasscutterJar} value={this.state?.grasscutter_path} extensions={['jar']} />
</div> </div>
</div> </div>
<div className='OptionSection' id="menuOptionsContainerToggleEnc"> <div className="OptionSection" id="menuOptionsContainerToggleEnc">
<div className='OptionLabel' id="menuOptionsLabelToggleEnc"> <div className="OptionLabel" id="menuOptionsLabelToggleEnc">
<Tr text="options.toggle_encryption" /> <Tr text="options.toggle_encryption" />
</div> </div>
<div className='OptionValue' id="menuOptionsButtonToggleEnc"> <div className="OptionValue" id="menuOptionsButtonToggleEnc">
<BigButton onClick={this.toggleEncryption} id="toggleEnc"> <BigButton onClick={this.toggleEncryption} id="toggleEnc">
{ {this.state.encryption}
this.state.encryption
}
</BigButton> </BigButton>
</div> </div>
</div> </div>
<Divider /> <Divider />
<div className='OptionSection' id="menuOptionsContainerGCWGame"> <div className="OptionSection" id="menuOptionsContainerGCWGame">
<div className='OptionLabel' id="menuOptionsLabelGCWDame"> <div className="OptionLabel" id="menuOptionsLabelGCWDame">
<Tr text="options.grasscutter_with_game" /> <Tr text="options.grasscutter_with_game" />
</div> </div>
<div className='OptionValue' id="menuOptionsCheckboxGCWGame"> <div className="OptionValue" id="menuOptionsCheckboxGCWGame">
<Checkbox onChange={this.toggleGrasscutterWithGame} checked={this.state?.grasscutter_with_game} id="gcWithGame" /> <Checkbox
onChange={this.toggleGrasscutterWithGame}
checked={this.state?.grasscutter_with_game}
id="gcWithGame"
/>
</div> </div>
</div> </div>
<Divider /> <Divider />
<div className='OptionSection' id="menuOptionsContainerThemes"> <div className="OptionSection" id="menuOptionsContainerThemes">
<div className='OptionLabel' id="menuOptionsLabelThemes"> <div className="OptionLabel" id="menuOptionsLabelThemes">
<Tr text="options.theme" /> <Tr text="options.theme" />
</div> </div>
<div className='OptionValue' id="menuOptionsSelectThemes"> <div className="OptionValue" id="menuOptionsSelectThemes">
<select value={this.state.theme} id="menuOptionsSelectMenuThemes" onChange={(event) => { <select
this.setTheme(event.target.value) value={this.state.theme}
}}> id="menuOptionsSelectMenuThemes"
{this.state.themes.map(t => ( onChange={(event) => {
<option this.setTheme(event.target.value)
key={t} }}
value={t}> >
{this.state.themes.map((t) => (
<option key={t} value={t}>
{t} {t}
</option> </option>
))} ))}
@ -234,20 +269,20 @@ export default class Options extends React.Component<IProps, IState> {
<Divider /> <Divider />
<div className='OptionSection' id="menuOptionsContainerJavaPath"> <div className="OptionSection" id="menuOptionsContainerJavaPath">
<div className='OptionLabel' id="menuOptionsLabelJavaPath"> <div className="OptionLabel" id="menuOptionsLabelJavaPath">
<Tr text="options.java_path" /> <Tr text="options.java_path" />
</div> </div>
<div className='OptionValue' id="menuOptionsDirJavaPath"> <div className="OptionValue" id="menuOptionsDirJavaPath">
<DirInput onChange={this.setJavaPath} value={this.state?.java_path} extensions={['exe']} /> <DirInput onChange={this.setJavaPath} value={this.state?.java_path} extensions={['exe']} />
</div> </div>
</div> </div>
<div className='OptionSection' id="menuOptionsContainerBG"> <div className="OptionSection" id="menuOptionsContainerBG">
<div className='OptionLabel' id="menuOptionsLabelBG"> <div className="OptionLabel" id="menuOptionsLabelBG">
<Tr text="options.background" /> <Tr text="options.background" />
</div> </div>
<div className='OptionValue' id="menuOptionsDirBG"> <div className="OptionValue" id="menuOptionsDirBG">
<DirInput <DirInput
onChange={this.setCustomBackground} onChange={this.setCustomBackground}
value={this.state?.bg_url_or_path} value={this.state?.bg_url_or_path}
@ -262,19 +297,20 @@ export default class Options extends React.Component<IProps, IState> {
</div> </div>
</div> </div>
<div className='OptionSection' id="menuOptionsContainerLang"> <div className="OptionSection" id="menuOptionsContainerLang">
<div className='OptionLabel' id="menuOptionsLabelLang"> <div className="OptionLabel" id="menuOptionsLabelLang">
<Tr text="options.language" /> <Tr text="options.language" />
</div> </div>
<div className='OptionValue' id="menuOptionsSelectLang"> <div className="OptionValue" id="menuOptionsSelectLang">
<select value={this.state.current_language} id="menuOptionsSelectMenuLang" onChange={(event) => { <select
this.setLanguage(event.target.value) value={this.state.current_language}
}}> id="menuOptionsSelectMenuLang"
{this.state.language_options.map(lang => ( onChange={(event) => {
<option this.setLanguage(event.target.value)
key={Object.keys(lang)[0]} }}
value={Object.keys(lang)[0]}> >
{this.state.language_options.map((lang) => (
<option key={Object.keys(lang)[0]} value={Object.keys(lang)[0]}>
{lang[Object.keys(lang)[0]]} {lang[Object.keys(lang)[0]]}
</option> </option>
))} ))}
@ -284,4 +320,4 @@ export default class Options extends React.Component<IProps, IState> {
</Menu> </Menu>
) )
} }
} }

View File

@ -8,7 +8,7 @@ let defaultConfig: Configuration
defaultConfig = { defaultConfig = {
toggle_grasscutter: false, toggle_grasscutter: false,
game_install_path: 'C:\\Program Files\\Genshin Impact\\Genshin Impact game', game_install_path: 'C:\\Program Files\\Genshin Impact\\Genshin Impact game',
game_version: 'global', game_executable: 'GenshinImpact.exe',
grasscutter_with_game: false, grasscutter_with_game: false,
grasscutter_path: '', grasscutter_path: '',
java_path: '', java_path: '',
@ -31,7 +31,7 @@ let defaultConfig: Configuration
export interface Configuration { export interface Configuration {
toggle_grasscutter: boolean toggle_grasscutter: boolean
game_install_path: string game_install_path: string
game_version: string game_executable: string
grasscutter_with_game: boolean grasscutter_with_game: boolean
grasscutter_path: string grasscutter_path: string
java_path: string java_path: string