Compare commits

..

25 Commits

Author SHA1 Message Date
SpikeHD
01fce477ef Merge branch 'main' of github.com:Grasscutters/Cultivation 2022-08-23 23:52:21 -07:00
SpikeHD
2f610ce0af version bump 2022-08-23 23:52:11 -07:00
SpikeHD
d9772d9ffb resource link update 2022-08-23 23:47:52 -07:00
SpikeHD
93097db5cc Merge pull request #58 from Untitled/main
Chinese Simplified Translations Update
2022-08-04 10:55:53 -07:00
SpikeHD
c06aa9f885 Merge pull request #57 from Kimi898246/patch-3
Traditional Chinese | Translation Patches
2022-08-04 10:55:21 -07:00
ChapterII
f7bcad0a90 Update chs.json
Updated translations
2022-08-04 15:04:27 +08:00
Kimi
7120f846f7 Traditional Chinese | Translation Patches
hi
2022-08-04 02:18:21 +08:00
SpikeHD
f6ed37d2e4 Merge pull request #53 from DasIschBims/main
Update de.json
2022-08-02 17:21:54 -07:00
DasIschBims
99b210b3be Update de.json 2022-07-30 13:49:13 +02:00
SpikeHD
7d52766c07 Merge pull request #51 from TotalyNotOndre/patch-1
Update ru.json
2022-07-28 12:18:13 -07:00
Abdul
212d974ac6 Fixed missing separators 2022-07-28 20:23:55 +03:00
SpikeHD
a6e06e3005 revert to 50 per page 2022-07-27 21:52:26 -07:00
Abdul
56c1f2dcc2 Update ru.json
- Added and translated missing localization strings
2022-07-28 03:33:22 +03:00
SpikeHD
a843888cb8 cleanup 2022-07-27 17:01:55 -07:00
SpikeHD
281bfb5cea cheeky key path fix 2022-07-26 22:30:25 -07:00
SpikeHD
79891238b6 fix reshade 2022-07-26 20:59:33 -07:00
SpikeHD
0971f5b826 progress bar 'fix' 2022-07-26 20:49:20 -07:00
SpikeHD
7e5f3be4fa cleanup 2022-07-26 20:41:55 -07:00
SpikeHD
e29e269c4c Merge branch 'mod_management' 2022-07-26 20:39:54 -07:00
SpikeHD
75f1eef587 help text for some options 2022-07-25 18:45:48 -07:00
SpikeHD
43a6348b7a cleanup 2022-07-25 18:28:06 -07:00
SpikeHD
cc74107dfe move help buttons to alerts 2022-07-25 18:26:04 -07:00
SpikeHD
afa40f437f english fallback when using other languages 2022-07-25 18:13:37 -07:00
SpikeHD
a06a8af7df Merge branch 'main' of github.com:Grasscutters/Cultivation 2022-07-24 16:43:59 -07:00
SpikeHD
cd628b4f3d open with cwd, restore akebi option stuff 2022-07-21 19:49:51 -07:00
24 changed files with 226 additions and 117 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "cultivation",
"version": "1.0.4",
"version": "1.0.6",
"private": true,
"dependencies": {
"@tauri-apps/api": "^1.0.0-rc.5",

View File

@@ -11,13 +11,21 @@
"files_extracting": "文件解压中:"
},
"options": {
"enabled": "已启用",
"disabled": "已禁用",
"game_path": "选择游戏安装路径",
"game_executable": "选择游戏可执行文件",
"recover_metadata": "紧急情况下恢复元数据文件",
"grasscutter_jar": "选择 Grasscutter JAR 文件",
"java_path": "设置自定义 Java 路径",
"toggle_encryption": "启用加密",
"install_certificate": "安装代理证书",
"java_path": "选择自定义 Java 路径",
"grasscutter_with_game": "随游戏自动启动 Grasscutter",
"language": "语言",
"language": "选择语言",
"background": "设置自定义背景(链接或文件)",
"theme": "设置主题"
"theme": "设置主题",
"patch_metadata": "自动修改元数据",
"use_proxy": "使用内置代理"
},
"downloads": {
"grasscutter_stable_data": "下载 Grasscutter 稳定版数据",
@@ -28,7 +36,8 @@
"grasscutter_latest": "下载 Grasscutter 开发版",
"grasscutter_stable_update": "更新 Grasscutter 稳定版",
"grasscutter_latest_update": "更新 Grasscutter 开发版",
"resources": "下载 Grasscutter 资源"
"resources": "下载 Grasscutter 资源",
"game": "下载游戏"
},
"download_status": {
"downloading": "下载中",
@@ -40,10 +49,11 @@
"components": {
"select_file": "选择文件或文件夹...",
"select_folder": "选择文件夹...",
"download": "下载"
"download": "下载",
"install": "安装"
},
"news": {
"latest_commits": "最近的PR",
"latest_commits": "最近提交",
"latest_version": "最新版本"
},
"help": {
@@ -53,6 +63,17 @@
"gc_dev_jar": "下载最新的 Grasscutter 开发版,包括 JAR 文件和数据。",
"gc_stable_data": "下载当前的 Grasscutter 稳定版数据,不包括 JAR 文件。此选项在更新时有帮助。",
"gc_dev_data": "下载最新的 Grasscutter 开发版数据,不包括 JAR 文件。此选项在更新时有帮助。",
"resources": "资源文件在运行 Grasscutter 服务器时是必要的。此选项在已经存在资源文件时不可选。"
"resources": "资源文件在运行 Grasscutter 服务器时是必要的。此选项在已经存在资源文件时不可选。",
"emergency_metadata": "在出现意外情况时,自动将元数据恢复至原始版本",
"use_proxy": "使用 Cultivation 的内置代理。除非你使用 Fiddler 等软件,否则应启用此项。",
"patch_metadata": "自动修改和恢复游戏元数据。除非要游玩旧版本/非官方版本,抑或你已经手动修改了元数据,否则应启用此项。"
},
"swag": {
"akebi_name": "Akebi",
"migoto_name": "Migoto",
"reshade_name": "Reshade",
"akebi": "选择 Akebi 可执行文件",
"migoto": "选择 3DMigoto 可执行文件",
"reshade": "选择 Reshade 注入器"
}
}

View File

@@ -13,14 +13,19 @@
"options": {
"enabled": "已啟用",
"disabled": "未啟用",
"game_path": "選擇遊戲安裝路徑",
"game_executable": "選擇遊戲執行檔",
"recover_metadata": "緊急恢復Metadata",
"grasscutter_jar": "選擇伺服器JAR檔案",
"toggle_encryption": "設定加密",
"java_path": "設定自定義Java路徑",
"install_certificate": "安裝代理憑證",
"java_path": "選擇自定義Java路徑",
"grasscutter_with_game": "伴隨遊戲一起啟動Grasscutter",
"language": "語言",
"background": "設定自定義背景(網址或檔案)",
"theme": "設定主題"
"background": "選擇自定義背景(網址或檔案)",
"theme": "選擇主題",
"patch_metadata": "自動修補Metadata",
"use_proxy": "使用內建代理伺服器"
},
"downloads": {
"grasscutter_stable_data": "下載Grasscutter穩定版數據Data",
@@ -31,7 +36,8 @@
"grasscutter_latest": "下載Grasscutter開發板",
"grasscutter_stable_update": "更新Grasscutter穩定版",
"grasscutter_latest_update": "更新Grasscutter開發板",
"resources": "下載Grasscutter資源Resources"
"resources": "下載Grasscutter資源Resources",
"game": "下載遊戲"
},
"download_status": {
"downloading": "下載中",
@@ -43,7 +49,8 @@
"components": {
"select_file": "選擇檔案或資料夾...",
"select_folder": "選擇資料夾...",
"download": "下載"
"download": "下載",
"install": "安裝"
},
"news": {
"latest_commits": "最近的PR",
@@ -51,11 +58,23 @@
},
"help": {
"port_help_text": "確保這是Dispatch伺服器端口不是遊戲伺服器端口。 大部分伺服器的端口都是443。",
"game_help_text": "不需要另外一個遊戲備份來使用Grasscutter。這是給想要降級到2.6或者還沒安裝遊戲的人使用的。",
"game_help_text": "不需要另外一個遊戲備份來使用Grasscutter。這是給想要降級到2.6或者還沒安裝遊戲的人使用的。",
"gc_stable_jar": "下載當前的Grasscutter穩定版本包括JAR答案還有資料文件。",
"gc_dev_jar": "下載當前的Grasscutter穩定版本資料文件其中不會附帶JAR文件。這個選項在更新時很有用。",
"gc_stable_data": "下載當前最新的Grasscutter開發版本資料文件其中不會附帶JAR文件。這個選項在更新時很有用。",
"gc_dev_data": "下載當前最新的Grasscutter開發版本的資料文件其中不會附帶JAR文件。這個選項在更新時很有用。",
"resources": "資源文件在架設一個Grasscutter伺服器時是必要的。 這個選項會在你已經有裡面有檔案的資源資料夾時不可選。"
"encryption": "在正常情況下,此選項應該被關閉。",
"resources": "資源文件在架設一個Grasscutter伺服器時是必要的。 這個選項會在您已經有裡面有檔案的資源資料夾時不可選。",
"emergency_metadata": "一旦有東西出了問題此選項可以把您的Metadata恢復成官方版本。",
"use_proxy": "使用Cultivation內建的代理伺服器。此選項應該被啟用除非你使用其他的代理伺服器。",
"patch_metadata": "自動修補和恢復Metadata。除非您的遊戲版本是舊的或者是非官方的此選項應該被啟用。"
},
"swag": {
"akebi_name": "Akebi",
"migoto_name": "Migoto",
"reshade_name": "Reshade",
"akebi": "選擇Akebi執行檔",
"migoto": "選擇3DMigoto執行檔",
"reshade": "選擇Reshade注入器"
}
}

View File

@@ -13,14 +13,17 @@
"options": {
"enabled": "Aktiviert",
"disabled": "Deaktiviert",
"game_path": "Spielpfad",
"game_executable": "Spiel Datei auswählen",
"recover_metadata": "Notfall Wiederherstellung der Metadaten",
"grasscutter_jar": "Grasscuter JAR auswählen",
"toggle_encryption": "Verschlüsselung umschalten",
"java_path": "Benutzerdefinierten Java Pfad setzen",
"grasscutter_with_game": "Grasscutter automatisch mit dem Spiel starten",
"language": "Sprache auswählen",
"background": "Benutzerdefinierten Hintergrund festlegen (link oder bild)",
"theme": "Theme auswählen"
"theme": "Theme auswählen",
"patch_metadata": "Metadaten automatisch patchen"
},
"downloads": {
"grasscutter_stable_data": "Stabile Grasscutter Daten herunterladen",
@@ -31,7 +34,8 @@
"grasscutter_latest": "Aktuellste Grasscutter Version herunterladen",
"grasscutter_stable_update": "Stabile Grasscutter Version aktualisieren",
"grasscutter_latest_update": "Aktuellste Grasscutter Version aktualisieren",
"resources": "Grasscutter Ressourcen herunterladen"
"resources": "Grasscutter Ressourcen herunterladen",
"game": "Spiel herunterladen"
},
"download_status": {
"downloading": "Lädt herunter",
@@ -43,7 +47,8 @@
"components": {
"select_file": "Datei oder Ordner auswählen...",
"select_folder": "Ordner auswählen...",
"download": "Herunterladen"
"download": "Herunterladen",
"install": "Installieren"
},
"news": {
"latest_commits": "Letzte Commits",
@@ -56,6 +61,17 @@
"gc_dev_jar": "Laden Sie die neueste Grasscutter-Entwicklungsversion herunter, welche eine Jar-Datei und Datendateien enthält.",
"gc_stable_data": "Laden Sie die stabilen Grasscutter Daten herunter, welche keine Jar-Datei enthalten. Dies ist nützlich zum Aktualisieren.",
"gc_dev_data": "Laden Sie die neuesten Grasscutter-Entwicklungsdateien herunter, welche keine Jar-Datei enthält. Dies ist nützlich zum Aktualisieren.",
"resources": "Diese werden auch benötigt, um einen Grasscutter-Server auszuführen. Diese Schaltfläche ist grau, wenn Sie einen bestehenden Ressourcenordner mit Inhalten haben"
"resources": "Diese werden auch benötigt, um einen Grasscutter-Server auszuführen. Diese Schaltfläche ist grau, wenn Sie einen bestehenden Ressourcenordner mit Inhalten haben",
"emergency_metadata": "Im Fall, dass etwas schief laufen sollte, kannst du deine Metadaten auf die letzte offizielle Version zurücksetzen",
"use_proxy": "Nutze den internen Proxy von Cultivation. Du solltest dies aktivieren, es sei denn du nutzt Programme wie Fiddler",
"patch_metadata": "Patche und aktualisiere deine Metadaten automatisch. Solange du nicht mit einer alten/nicht offiziellen Version spielst oder deine Metadaten manuell gepatcht hast, sollte dies aktiviert sein."
},
"swag": {
"akebi_name": "Akebi",
"migoto_name": "Migoto",
"reshade_name": "Reshade",
"akebi": "Akebi.exe festlegen",
"migoto": "Migoto.exe festlegen",
"reshade": "Reshade injector festlegen"
}
}

View File

@@ -63,7 +63,11 @@
"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_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"
"encryption": "This should usually be disabled.",
"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",
"emergency_metadata": "In case something went wrong, restore your metadata to the latest official versions metadata.",
"use_proxy": "Use the Cultivation internal proxy. You should have this enabled unless you use something like Fiddler",
"patch_metadata": "Patch and unpatch your game metadata automatically. Unless playing with old/non-official versions, or you have manually patched your metadata, this should be enabled."
},
"swag": {
"akebi_name": "Akebi",

View File

@@ -13,14 +13,19 @@
"options": {
"enabled": "Включено",
"disabled": "Выключено",
"game_path": "Установить путь к файлам игры",
"game_executable": "Установить исполняемый файл игры",
"recover_metadata": "Принудительное восстановление Метаданных",
"grasscutter_jar": "Установить Grasscutter JAR",
"toggle_encryption": "Переключить шифрование",
"install_certificate": "Установить сертификат для работы Прокси",
"java_path": "Установить пользовательский путь Java",
"grasscutter_with_game": "Автоматически запускать Grasscutter вместе с игрой",
"language": "Установить язык",
"background": "Установить свой фон (ссылка или файл)",
"theme": "Установить тему"
"theme": "Установить тему",
"patch_metadata": "Автоматический патч Метаданных при запуске",
"use_proxy": "Использовать встроенный Прокси"
},
"downloads": {
"grasscutter_stable_data": "Скачать стабильные данные Grasscutter",
@@ -31,7 +36,8 @@
"grasscutter_latest": "Скачать последнюю версию Grasscutter",
"grasscutter_stable_update": "Обновить стабильную версию Grasscutter",
"grasscutter_latest_update": "Обновить последнюю версию Grasscutter",
"resources": "Скачать ресурсы Grasscutter"
"resources": "Скачать ресурсы Grasscutter",
"game": "Скачать Игру"
},
"download_status": {
"downloading": "Скачивание",
@@ -43,7 +49,8 @@
"components": {
"select_file": "Выберите файл или папку...",
"select_folder": "Выберите папку...",
"download": "Скачать"
"download": "Скачать",
"install": "Установить"
},
"news": {
"latest_commits": "Последние коммиты",
@@ -56,6 +63,18 @@
"gc_dev_jar": "Скачать последнюю версию для разработки Grasscutter, которая содержит jar файл и данные.",
"gc_stable_data": "Скачать стабильные данные Grasscutter, в которой нету jar файла. Это полезно для обновления.",
"gc_dev_data": "Скачать последнюю версию для разработки Grasscutter, в которой нету jar файла. Это полезно для обновления.",
"resources": "Это необходимо для запуска сервера Grasscutter. Эта кнопка будет серой, если у Вас уже есть не пустая папка с ресурсами."
"encryption": "Обычно это должно быть выключено.",
"resources": "Это необходимо для запуска сервера Grasscutter. Эта кнопка будет серой, если у Вас уже есть не пустая папка с ресурсами.",
"emergency_metadata": "Если что-то пошло не так, восстановит Метаданные до последней официальной версии.",
"use_proxy": "Использовать встроенный Прокси. Отключите если используете Fiddler или подобную программу",
"patch_metadata": "Патчит и восстанавливает Метаданные автоматически. Если вы не играете на старых/модифицированых версиях, или сами в ручную патчите Метаданные, эта опция должна быть включена."
},
"swag": {
"akebi_name": "Akebi",
"migoto_name": "Migoto",
"reshade_name": "Reshade",
"akebi": "Путь к исполняемому файлу Akebi",
"migoto": "Путь к исполняемому файлу 3DMigoto ",
"reshade": "Путь к инжектору Reshade"
}
}

View File

@@ -18,7 +18,14 @@ pub fn rename(path: String, new_name: String) {
let path_replaced = &path.replace(&new_path.split('/').last().unwrap(), &new_name);
fs::rename(path, &path_replaced).unwrap();
match fs::rename(&path, &path_replaced) {
Ok(_) => {
println!("Renamed {} to {}", &path, path_replaced);
}
Err(e) => {
println!("Error: {}", e);
}
};
}
#[tauri::command]

View File

@@ -24,6 +24,11 @@ mod web;
static WATCH_GAME_PROCESS: Lazy<Mutex<String>> = Lazy::new(|| Mutex::new(String::new()));
fn main() {
// Always set CWD to the location of the executable.
let mut exe_path = std::env::current_exe().unwrap();
exe_path.pop();
std::env::set_current_dir(&exe_path).unwrap();
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![
enable_process_watcher,
@@ -88,6 +93,9 @@ fn enable_process_watcher(window: tauri::Window, process: String) {
println!("Starting process watcher...");
thread::spawn(move || {
// Initial sleep for 8 seconds, since running 20 different injectors or whatever can take a while
std::thread::sleep(std::time::Duration::from_secs(8));
let mut system = System::new_all();
loop {

View File

@@ -111,7 +111,12 @@ fn replace_keys(data: &[u8]) -> Vec<u8> {
fn replace_rsa_key(old_data: &str, to_replace: &str, file_name: &str) -> String {
// Read dispatch key file
unsafe {
let mut new_key_file = match File::open(&("keys/".to_owned() + file_name)) {
// Get key folder from exe path
let mut exe_path = std::env::current_exe().unwrap();
exe_path.pop();
let key_folder = exe_path.to_str().unwrap().to_string();
let mut new_key_file = match File::open(format!("{}/keys/{}", key_folder, file_name)) {
Ok(file) => file,
Err(e) => {
println!("Failed to open keys/{}: {}", file_name, e);
@@ -140,6 +145,5 @@ fn encrypt_metadata(old_data: &[u8]) -> Vec<u8> {
}
fn do_vecs_match<T: PartialEq>(a: &Vec<T>, b: &Vec<T>) -> bool {
let matching = a.iter().zip(b.iter()).filter(|&(a, b)| a == b).count();
matching == a.len() && matching == b.len()
a == b
}

View File

@@ -242,6 +242,7 @@ pub fn install_ca_files(cert_path: &Path) {
crate::system_helpers::run_command(
"certutil",
vec!["-user", "-addstore", "Root", cert_path.to_str().unwrap()],
None,
);
println!("Installed certificate.");
}

View File

@@ -18,8 +18,6 @@ pub fn run_program_relative(path: String, args: Option<String>) {
// Set new working directory
std::env::set_current_dir(&path_buf).unwrap();
println!("Opening {} {}", &path, args.clone().unwrap_or("".into()));
// Without unwrap_or, this can crash when UAC prompt is denied
open::that(format!("{} {}", &path, args.unwrap_or("".into()))).unwrap_or(());
@@ -28,13 +26,28 @@ pub fn run_program_relative(path: String, args: Option<String>) {
}
#[tauri::command]
pub fn run_command(program: &str, args: Vec<&str>) {
pub fn run_command(program: &str, args: Vec<&str>, relative: Option<bool>) {
let prog = program.to_string();
let args = args.iter().map(|s| s.to_string()).collect::<Vec<String>>();
// Commands should not block (this is for the reshade injector mostly)
std::thread::spawn(move || {
// Save the current working directory
let cwd = std::env::current_dir().unwrap();
if relative.unwrap_or(false) {
// Set the new working directory to the path before the executable
let mut path_buf = std::path::PathBuf::from(&prog);
path_buf.pop();
// Set new working directory
std::env::set_current_dir(&path_buf).unwrap();
}
cmd(prog, args).run().unwrap();
// Restore the original working directory
std::env::set_current_dir(&cwd).unwrap();
});
}

View File

@@ -1,7 +1,7 @@
use std::fs::{read_dir, File};
use std::path;
use std::thread;
use unrar::archive::{Archive, OpenArchive};
use unrar::archive::Archive;
#[tauri::command]
pub fn unzip(
@@ -63,7 +63,7 @@ pub fn unzip(
println!("Is rar file? {}", zipfile.ends_with(".rar"));
let mut name = "".into();
let name;
// If file ends in zip, OR is unknown, extract as zip, otherwise extract as rar
if zipfile.ends_with(".rar") {
@@ -132,9 +132,9 @@ pub fn unzip(
fn extract_rar(
window: &tauri::Window,
rarfile: &String,
f: &File,
_f: &File,
full_path: &path::PathBuf,
top_level: bool,
_top_level: bool,
) {
let archive = Archive::new(rarfile.clone());

View File

@@ -7,7 +7,7 @@
},
"package": {
"productName": "Cultivation",
"version": "1.0.4"
"version": "1.0.6"
},
"tauri": {
"allowlist": {

View File

@@ -114,9 +114,6 @@ select:focus {
padding: 0;
}
.ExtrasMenu {
}
@media (max-height: 580px) {
.BottomSection {
height: 150px;

View File

@@ -36,10 +36,6 @@ async function generateInfo() {
alert('check your dev console and send that in #cultivation')
}
function none() {
alert('none')
}
class Debug extends React.Component {
render() {
return (

View File

@@ -24,7 +24,6 @@ import DownloadHandler from '../utils/download'
import cogBtn from '../resources/icons/cog.svg'
import downBtn from '../resources/icons/download.svg'
import wrenchBtn from '../resources/icons/wrench.svg'
import Menu from './components/menu/Menu'
import { ExtrasMenu } from './components/menu/ExtrasMenu'
interface IProps {

View File

@@ -2,7 +2,7 @@ import React from 'react'
import './HelpButton.css'
import Help from '../../../resources/icons/help.svg'
import MiniDialog from '../MiniDialog'
import { translate } from '../../../utils/language'
interface IProps {
children?: React.ReactNode[] | React.ReactNode
@@ -10,45 +10,23 @@ interface IProps {
id?: string
}
interface IState {
opened: boolean
}
export default class HelpButton extends React.Component<IProps, IState> {
export default class HelpButton extends React.Component<IProps, never> {
constructor(props: IProps) {
super(props)
this.state = {
opened: false,
}
this.setOpen = this.setOpen.bind(this)
this.setClosed = this.setClosed.bind(this)
this.showAlert = this.showAlert.bind(this)
}
setOpen() {
this.setState({ opened: true })
}
setClosed() {
this.setState({ opened: false })
async showAlert() {
if (this.props.contents) alert(await translate(this.props.contents))
}
render() {
return (
<div className="HelpSection">
<div className="HelpButton" onMouseEnter={this.setOpen} onMouseLeave={this.setClosed}>
<div className="HelpButton" onClick={this.showAlert}>
<img src={Help} />
</div>
<div
className="HelpContents"
style={{
display: this.state.opened ? 'block' : 'none',
}}
>
<MiniDialog closeFn={this.setClosed}>{this.props.contents || this.props.children}</MiniDialog>
</div>
</div>
)
}

View File

@@ -57,7 +57,7 @@ export default class ProgressBar extends React.Component<IProps, IState> {
style={{
width: `${(() => {
// Handles no files downloading
if (this.state.files === 0) {
if (this.state.files === 0 || this.state.average >= 100) {
return '100'
}

View File

@@ -17,7 +17,7 @@ const STABLE_REPO_DOWNLOAD = 'https://github.com/Grasscutters/Grasscutter/archiv
const DEV_REPO_DOWNLOAD = 'https://github.com/Grasscutters/Grasscutter/archive/refs/heads/development.zip'
const STABLE_DOWNLOAD = 'https://nightly.link/Grasscutters/Grasscutter/workflows/build/stable/Grasscutter.zip'
const DEV_DOWNLOAD = 'https://nightly.link/Grasscutters/Grasscutter/workflows/build/development/Grasscutter.zip'
const RESOURCES_DOWNLOAD = 'https://gitlab.com/yukiz/GrasscutterResources/-/archive/2.8/GrasscutterResources-2.8.zip'
const RESOURCES_DOWNLOAD = 'https://gitlab.com/yukiz/GrasscutterResources/-/archive/3.0/GrasscutterResources-3.0.zip'
interface IProps {
closeFn: () => void
@@ -202,9 +202,7 @@ export default class Downloads extends React.Component<IProps, IState> {
<Tr
text={this.state.grasscutter_set ? 'downloads.grasscutter_stable' : 'downloads.grasscutter_stable_update'}
/>
<HelpButton>
<Tr text="help.gc_stable_jar" />
</HelpButton>
<HelpButton contents="help.gc_stable_jar" />
</div>
<div className="DownloadValue" id="downloadMenuButtonGCStable">
<BigButton
@@ -221,9 +219,7 @@ export default class Downloads extends React.Component<IProps, IState> {
<Tr
text={this.state.grasscutter_set ? 'downloads.grasscutter_latest' : 'downloads.grasscutter_latest_update'}
/>
<HelpButton>
<Tr text="help.gc_dev_jar" />
</HelpButton>
<HelpButton contents="help.gc_dev_jar" />
</div>
<div className="DownloadValue" id="downloadMenuButtonGCDev">
<BigButton
@@ -247,9 +243,7 @@ export default class Downloads extends React.Component<IProps, IState> {
: 'downloads.grasscutter_stable_data_update'
}
/>
<HelpButton>
<Tr text="help.gc_stable_data" />
</HelpButton>
<HelpButton contents="help.gc_stable_data" />
</div>
<div className="DownloadValue" id="downloadMenuButtonGCStableData">
<BigButton
@@ -270,9 +264,7 @@ export default class Downloads extends React.Component<IProps, IState> {
: 'downloads.grasscutter_latest_data_update'
}
/>
<HelpButton>
<Tr text="help.gc_dev_data" />
</HelpButton>
<HelpButton contents="help.gc_dev_data" />
</div>
<div className="DownloadValue" id="downloadMenuButtonGCDevData">
<BigButton
@@ -290,9 +282,7 @@ export default class Downloads extends React.Component<IProps, IState> {
<div className="DownloadMenuSection" id="downloadMenuContainerResources">
<div className="DownloadLabel" id="downloadMenuLabelResources">
<Tr text="downloads.resources" />
<HelpButton>
<Tr text="help.resources" />
</HelpButton>
<HelpButton contents="help.resources" />
</div>
<div className="DownloadValue" id="downloadMenuButtonResources">
<BigButton

View File

@@ -115,6 +115,7 @@ export class ExtrasMenu extends React.Component<IProps, IState> {
await invoke('run_command', {
program: config.reshade_path,
args: [await getGameExecutable()],
relative: true,
})
}

View File

@@ -1,6 +1,6 @@
import React from 'react'
import Menu from './Menu'
import Tr, { translate } from '../../../utils/language'
import { translate } from '../../../utils/language'
import DownloadHandler from '../../../utils/download'
import './Game.css'
@@ -70,9 +70,7 @@ export default class Downloads extends React.Component<IProps, IState> {
Download Game
</BigButton>
)}
<HelpButton>
<Tr text="main.game_help_text" />
</HelpButton>
<HelpButton contents="main.game_help_text" />
</div>
<div className="GameDownloadDir">

View File

@@ -16,3 +16,7 @@
.OptionSection .BigButtonText {
font-size: 12px;
}
.OptionSection .HelpButton img {
filter: invert(0%) sepia(91%) saturate(7464%) hue-rotate(101deg) brightness(0%) contrast(107%);
}

View File

@@ -14,6 +14,7 @@ import './Options.css'
import BigButton from '../common/BigButton'
import DownloadHandler from '../../../utils/download'
import * as meta from '../../../utils/metadata'
import HelpButton from '../common/HelpButton'
interface IProps {
closeFn: () => void
@@ -269,6 +270,7 @@ export default class Options extends React.Component<IProps, IState> {
<div className="OptionSection" id="menuOptionsContainermetaDownload">
<div className="OptionLabel" id="menuOptionsLabelmetaDownload">
<Tr text="options.recover_metadata" />
<HelpButton contents="help.emergency_metadata" />
</div>
<div className="OptionValue" id="menuOptionsButtonmetaDownload">
<BigButton onClick={this.restoreMetadata} id="metaDownload">
@@ -279,6 +281,7 @@ export default class Options extends React.Component<IProps, IState> {
<div className="OptionSection" id="menuOptionsContainerPatchMeta">
<div className="OptionLabel" id="menuOptionsLabelPatchMeta">
<Tr text="options.patch_metadata" />
<HelpButton contents="help.patch_metadata" />
</div>
<div className="OptionValue" id="menuOptionsCheckboxPatchMeta">
<Checkbox onChange={this.toggleMetadata} checked={this.state?.patch_metadata} id="patchMeta" />
@@ -287,6 +290,7 @@ export default class Options extends React.Component<IProps, IState> {
<div className="OptionSection" id="menuOptionsContainerUseProxy">
<div className="OptionLabel" id="menuOptionsLabelUseProxy">
<Tr text="options.use_proxy" />
<HelpButton contents="help.use_proxy" />
</div>
<div className="OptionValue" id="menuOptionsCheckboxUseProxy">
<Checkbox onChange={this.toggleProxy} checked={this.state?.use_internal_proxy} id="useProxy" />
@@ -306,6 +310,7 @@ export default class Options extends React.Component<IProps, IState> {
<div className="OptionSection" id="menuOptionsContainerToggleEnc">
<div className="OptionLabel" id="menuOptionsLabelToggleEnc">
<Tr text="options.toggle_encryption" />
<HelpButton contents="help.encryption" />
</div>
<div className="OptionValue" id="menuOptionsButtonToggleEnc">
<BigButton onClick={this.toggleEncryption} id="toggleEnc">

View File

@@ -21,38 +21,53 @@ export default class Tr extends React.Component<IProps, IState> {
}
}
componentDidMount() {
async componentDidMount() {
const { text } = this.props
getConfigOption('language').then((language: string) => {
// Get translation file
if (!language) language = 'en'
let language = await getConfigOption('language')
invoke('get_lang', { lang: language }).then((response) => {
const translation_obj = JSON.parse((response as string) || '{}')
// Get translation file
if (!language) language = 'en'
// Traversal
if (text.includes('.')) {
const keys = text.split('.')
let translation: string | Record<string, string> = translation_obj
const response = await invoke('get_lang', { lang: language })
const default_resp = await invoke('get_lang', { lang: 'en' })
for (let i = 0; i < keys.length; i++) {
if (!translation) {
translation = ''
} else {
translation = typeof translation !== 'string' ? translation[keys[i]] : (translation as string)
}
}
const translation_obj = JSON.parse((response as string) || '{}')
const default_obj = JSON.parse((default_resp as string) || '{}')
this.setState({
translated_text: translation as string,
})
// Traversal
if (text.includes('.')) {
const keys = text.split('.')
let translation: string | Record<string, string> = translation_obj
for (let i = 0; i < keys.length; i++) {
if (!translation) {
translation = ''
} else {
this.setState({
translated_text: translation_obj[text] || '',
})
translation = typeof translation !== 'string' ? translation[keys[i]] : (translation as string)
}
}
// If we could not find a translation, use the default one
if (!translation) {
translation = default_obj
for (let i = 0; i < keys.length; i++) {
if (!translation) {
translation = ''
} else {
translation = typeof translation !== 'string' ? translation[keys[i]] : (translation as string)
}
}
}
this.setState({
translated_text: translation as string,
})
})
} else {
this.setState({
translated_text: translation_obj[text] || '',
})
}
}
render() {
@@ -82,6 +97,7 @@ export async function getLanguages() {
export async function translate(text: string) {
const language = (await getConfigOption('language')) || 'en'
const translation_json = JSON.parse((await invoke('get_lang', { lang: language })) || '{}')
const default_json = JSON.parse(await invoke('get_lang', { lang: 'en' }))
// Traversal
if (text.includes('.')) {
@@ -96,6 +112,19 @@ export async function translate(text: string) {
}
}
// If we could not find a translation, use the default one
if (!translation) {
translation = default_json
for (let i = 0; i < keys.length; i++) {
if (!translation) {
translation = ''
} else {
translation = typeof translation !== 'string' ? translation[keys[i]] : (translation as string)
}
}
}
return translation
} else {
return translation_json[text] || ''