Files
vsix-hpl-toolbox/standalone/platform.go
2026-06-22 15:10:22 +08:00

244 lines
6.8 KiB
Go

package main
import (
"encoding/json"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"sync"
"syscall"
)
type PlecConfig struct {
UploadUrl string `json:"uploadUrl"`
OriginUrl string `json:"originUrl"`
PreviewBase string `json:"previewBase"`
SkipExistsCheck bool `json:"skipExistsCheck"`
}
type ApplovinConfig struct {
Cookie string `json:"cookie"`
DelayMs int `json:"delayMs"`
RandomizeUserAgent *bool `json:"randomizeUserAgent,omitempty"`
}
type SendToMobileConfig struct {
Port int `json:"port"`
}
type DeviceSimulatorConfig struct {
DevicesURL string `json:"devicesUrl"`
LanguagesURL string `json:"languagesUrl"`
}
type AppConfig struct {
Plec PlecConfig `json:"plec"`
Applovin ApplovinConfig `json:"applovin"`
SendToMobile SendToMobileConfig `json:"sendToMobile"`
DeviceSimulator DeviceSimulatorConfig `json:"deviceSimulator"`
BetaToolsEnabled bool `json:"betaToolsEnabled"`
LastPickDir string `json:"lastPickDir,omitempty"`
ProjectInitManifestURL string `json:"projectInitManifestUrl,omitempty"`
}
var configMu sync.Mutex
// userDataDir returns %LOCALAPPDATA%\HPLToolbox, creating it if needed.
// All mutable app files (config, lock, logs, WebView2 cache) live here so
// they are never placed next to the executable, which may be on a synced or
// antivirus-watched drive and would cause message-loop stalls.
func userDataDir() string {
local := os.Getenv("LOCALAPPDATA")
if local == "" {
local = os.Getenv("APPDATA")
}
if local == "" {
return appDir()
}
dir := filepath.Join(local, "HPLToolbox")
_ = os.MkdirAll(dir, 0755)
return dir
}
func defaultConfig() AppConfig {
return AppConfig{
Plec: PlecConfig{
UploadUrl: "http://167.99.227.249/src/upload_HTML_Experimental.php",
OriginUrl: "http://167.99.227.249",
PreviewBase: "https://playable.applovindemo.com/phaser/",
SkipExistsCheck: false,
},
Applovin: ApplovinConfig{Cookie: "", DelayMs: 5000},
SendToMobile: SendToMobileConfig{Port: 0},
DeviceSimulator: DeviceSimulatorConfig{
DevicesURL: "https://drive.google.com/file/d/1w04wviCmgNqtDCO1GLyYfwEeWpjBPGJ4/view?usp=drive_link",
LanguagesURL: "https://drive.google.com/file/d/1I3ZiI8CLU2JxYZEtWCz6Vr0r8Uj2732f/view?usp=drive_link",
},
}
}
func appDir() string {
exe, err := os.Executable()
if err != nil {
wd, _ := os.Getwd()
return wd
}
// `go run` puts the exe in a temp dir; fall back to cwd in that case.
resolved, err := filepath.EvalSymlinks(exe)
if err == nil {
exe = resolved
}
dir := filepath.Dir(exe)
if strings.Contains(strings.ToLower(dir), "go-build") {
wd, _ := os.Getwd()
return wd
}
return dir
}
func configPath() string {
return filepath.Join(userDataDir(), "config.json")
}
func LoadConfig() AppConfig {
configMu.Lock()
defer configMu.Unlock()
cfg := defaultConfig()
data, err := os.ReadFile(configPath())
if err != nil {
return cfg
}
_ = json.Unmarshal(data, &cfg)
return cfg
}
func SaveConfig(cfg AppConfig) error {
configMu.Lock()
defer configMu.Unlock()
data, err := json.MarshalIndent(cfg, "", " ")
if err != nil {
return err
}
return os.WriteFile(configPath(), data, 0644)
}
func OpenInBrowser(url string) {
cmd := hiddenCommand("rundll32.exe", "url.dll,FileProtocolHandler", url)
_ = cmd.Start()
}
func CopyToClipboard(text string) error {
cmd := hiddenCommand("clip")
cmd.Stdin = strings.NewReader(text)
return cmd.Run()
}
type FileFilter struct {
Name string
Extensions []string
}
func psEscape(s string) string { return strings.ReplaceAll(s, "'", "''") }
func hiddenCommand(name string, args ...string) *exec.Cmd {
cmd := exec.Command(name, args...)
cmd.SysProcAttr = &syscall.SysProcAttr{
HideWindow: true,
CreationFlags: 0x08000000,
}
return cmd
}
func PickFiles(title string, filters []FileFilter, multi bool, initialDir string) []string {
filterStr := "All files|*.*"
if len(filters) > 0 {
parts := make([]string, 0, len(filters))
for _, f := range filters {
exts := make([]string, 0, len(f.Extensions))
for _, e := range f.Extensions {
exts = append(exts, "*."+e)
}
parts = append(parts, fmt.Sprintf("%s|%s", f.Name, strings.Join(exts, ";")))
}
filterStr = strings.Join(parts, "|")
}
multiStr := "false"
if multi {
multiStr = "true"
}
script := fmt.Sprintf(`
Add-Type -AssemblyName System.Windows.Forms
$dlg = New-Object System.Windows.Forms.OpenFileDialog
$dlg.Title = '%s'
$dlg.Filter = '%s'
$dlg.Multiselect = $%s
if ('%s'.Length -gt 0) { $dlg.InitialDirectory = '%s' }
if ($dlg.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) {
$dlg.FileNames | ForEach-Object { Write-Output $_ }
}
`, psEscape(title), psEscape(filterStr), multiStr, psEscape(initialDir), psEscape(initialDir))
out, err := hiddenCommand("powershell", "-NoProfile", "-NonInteractive", "-STA", "-Command", script).Output()
if err != nil {
return nil
}
lines := strings.Split(string(out), "\n")
result := make([]string, 0, len(lines))
for _, l := range lines {
t := strings.TrimSpace(l)
if t != "" {
result = append(result, t)
}
}
return result
}
func PickSaveFile(title, defaultName string, filters []FileFilter, initialDir string) string {
filterStr := "All files|*.*"
if len(filters) > 0 {
parts := make([]string, 0, len(filters))
for _, f := range filters {
exts := make([]string, 0, len(f.Extensions))
for _, e := range f.Extensions {
exts = append(exts, "*."+e)
}
parts = append(parts, fmt.Sprintf("%s|%s", f.Name, strings.Join(exts, ";")))
}
filterStr = strings.Join(parts, "|")
}
script := fmt.Sprintf(`
Add-Type -AssemblyName System.Windows.Forms
$dlg = New-Object System.Windows.Forms.SaveFileDialog
$dlg.Title = '%s'
$dlg.Filter = '%s'
$dlg.FileName = '%s'
if ('%s'.Length -gt 0) { $dlg.InitialDirectory = '%s' }
if ($dlg.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) {
Write-Output $dlg.FileName
}
`, psEscape(title), psEscape(filterStr), psEscape(defaultName), psEscape(initialDir), psEscape(initialDir))
out, err := hiddenCommand("powershell", "-NoProfile", "-NonInteractive", "-STA", "-Command", script).Output()
if err != nil {
return ""
}
return strings.TrimSpace(string(out))
}
func PickFolder(title, initialDir string) string {
script := fmt.Sprintf(`
Add-Type -AssemblyName System.Windows.Forms
$dlg = New-Object System.Windows.Forms.FolderBrowserDialog
$dlg.Description = '%s'
if ('%s'.Length -gt 0) { $dlg.SelectedPath = '%s' }
if ($dlg.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) {
Write-Output $dlg.SelectedPath
}
`, psEscape(title), psEscape(initialDir), psEscape(initialDir))
out, err := hiddenCommand("powershell", "-NoProfile", "-NonInteractive", "-STA", "-Command", script).Output()
if err != nil {
return ""
}
return strings.TrimSpace(string(out))
}