Skip to content

Commit f255443

Browse files
committed
feat: linux(WIP)
1 parent f5e28a6 commit f255443

31 files changed

+837
-327
lines changed

internal/config/config.go

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,10 @@ type AppConfig struct {
1414
}
1515

1616
func localAppData() string {
17-
if v := os.Getenv("APPDATA"); strings.TrimSpace(v) != "" {
18-
return v
19-
}
20-
if v, _ := os.UserCacheDir(); strings.TrimSpace(v) != "" {
21-
return v
22-
}
23-
return "."
17+
if v, _ := os.UserConfigDir(); strings.TrimSpace(v) != "" {
18+
return v
19+
}
20+
return "."
2421
}
2522

2623
func configPath() string {

internal/explorer/explorer.go

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
1+
//go:build windows
2+
13
package explorer
24

35
import (
4-
"log"
5-
"os"
6-
"os/exec"
7-
"path/filepath"
8-
"strings"
9-
"syscall"
10-
11-
"github.com/liteldev/LeviLauncher/internal/utils"
6+
"log"
7+
"os"
8+
"os/exec"
9+
"path/filepath"
10+
"strings"
11+
"syscall"
12+
"github.com/liteldev/LeviLauncher/internal/utils"
1213
)
1314

1415
func OpenPath(dir string) bool {
@@ -21,9 +22,9 @@ func OpenPath(dir string) bool {
2122
return false
2223
}
2324
}
24-
cmd := exec.Command("powershell", "explorer \""+d+"\"")
25-
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
26-
if err := cmd.Run(); err != nil {
25+
cmd := exec.Command("powershell", "explorer \""+d+"\"")
26+
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
27+
if err := cmd.Run(); err != nil {
2728
log.Println("explorer.OpenPath error:", err)
2829
return false
2930
}
@@ -35,9 +36,9 @@ func SelectFile(path string) bool {
3536
if p == "" || !utils.FileExists(p) {
3637
return false
3738
}
38-
cmd := exec.Command("explorer", "/select,\""+p+"\"")
39-
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
40-
if err := cmd.Run(); err != nil {
39+
cmd := exec.Command("explorer", "/select,\""+p+"\"")
40+
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
41+
if err := cmd.Run(); err != nil {
4142
log.Println("explorer.SelectFile error:", err)
4243
return false
4344
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
//go:build linux
2+
3+
package explorer
4+
5+
import (
6+
"log"
7+
"os"
8+
"os/exec"
9+
"path/filepath"
10+
"strings"
11+
"github.com/liteldev/LeviLauncher/internal/utils"
12+
)
13+
14+
func OpenPath(dir string) bool {
15+
d := strings.TrimSpace(dir)
16+
if d == "" {
17+
return false
18+
}
19+
if !utils.DirExists(d) {
20+
if err := os.MkdirAll(d, 0755); err != nil {
21+
return false
22+
}
23+
}
24+
cmd := exec.Command("xdg-open", d)
25+
if err := cmd.Run(); err != nil {
26+
log.Println("explorer.OpenPath error:", err)
27+
return false
28+
}
29+
return true
30+
}
31+
32+
func SelectFile(path string) bool {
33+
p := strings.TrimSpace(path)
34+
if p == "" || !utils.FileExists(p) {
35+
return false
36+
}
37+
dir := filepath.Dir(p)
38+
cmd := exec.Command("xdg-open", dir)
39+
if err := cmd.Run(); err != nil {
40+
log.Println("explorer.SelectFile error:", err)
41+
return false
42+
}
43+
return true
44+
}
45+
46+
func OpenMods(name string) bool {
47+
n := strings.TrimSpace(name)
48+
if n == "" {
49+
return false
50+
}
51+
vdir, err := utils.GetVersionsDir()
52+
if err != nil || strings.TrimSpace(vdir) == "" {
53+
return false
54+
}
55+
dir := filepath.Join(vdir, n, "mods")
56+
return OpenPath(dir)
57+
}
58+
59+
func OpenWorlds(isPreview bool) bool {
60+
dir := filepath.Join(utils.GetMinecraftGDKDataPath(isPreview), "worlds")
61+
return OpenPath(dir)
62+
}
63+
64+
func OpenInstallers() bool {
65+
dir, err := utils.GetInstallerDir()
66+
if err != nil || dir == "" {
67+
return false
68+
}
69+
return OpenPath(dir)
70+
}

internal/extractor/embed.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package extractor
2+
3+
import _ "embed"
4+
5+
//go:embed launcher_core.dll
6+
var embeddedLauncherCoreDLL []byte
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
//go:build linux
2+
3+
package extractor
4+
5+
import _ "embed"
6+
7+
//go:embed launcher_core_cli.exe
8+
var embeddedLauncherCoreCLI []byte

internal/extractor/extractor.go

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,34 @@
1+
//go:build windows
2+
13
package extractor
24

35
import (
46
"context"
57
"fmt"
68
"bytes"
79
"crypto/sha256"
8-
_ "embed"
910
"io"
1011
"os"
11-
"path/filepath"
12-
"strings"
13-
"sync"
14-
"syscall"
12+
"path/filepath"
13+
"strings"
14+
"sync"
15+
"syscall"
1516
"unsafe"
1617

1718
"golang.org/x/sys/windows"
1819
"github.com/liteldev/LeviLauncher/internal/vcruntime"
1920
)
2021

2122
var (
22-
loadOnce sync.Once
23-
loadErr error
24-
miProc *windows.LazyProc
25-
useWide bool
26-
k32 *windows.LazyDLL
27-
pWideCharToMultiByte *windows.LazyProc
23+
loadOnce sync.Once
24+
loadErr error
25+
miProc *windows.LazyProc
26+
useWide bool
27+
k32 *windows.LazyDLL
28+
pWideCharToMultiByte *windows.LazyProc
2829
)
2930

30-
//go:embed launcher_core.dll
31-
var embeddedLauncherCoreDLL []byte
31+
// embeddedLauncherCoreDLL is provided by embed.go (cross-platform)
3232

3333
func prepareDLL() (string, error) {
3434
if len(embeddedLauncherCoreDLL) == 0 {
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
//go:build linux
2+
3+
package extractor
4+
5+
import (
6+
"os"
7+
"os/exec"
8+
"path/filepath"
9+
"strings"
10+
11+
"github.com/liteldev/LeviLauncher/internal/utils"
12+
)
13+
14+
func Init() {}
15+
16+
func ensureEmbeddedDLL() string {
17+
if len(embeddedLauncherCoreDLL) == 0 {
18+
return ""
19+
}
20+
base := utils.BaseRoot()
21+
dir := filepath.Join(base, "bin")
22+
_ = os.MkdirAll(dir, 0755)
23+
target := filepath.Join(dir, "launcher_core.dll")
24+
if fi, err := os.Stat(target); err == nil && fi.Size() > 0 {
25+
return target
26+
}
27+
tmp := target + ".tmp"
28+
if err := os.WriteFile(tmp, embeddedLauncherCoreDLL, 0644); err == nil {
29+
if err := os.Rename(tmp, target); err == nil {
30+
return target
31+
}
32+
_ = os.Remove(tmp)
33+
}
34+
return ""
35+
}
36+
37+
func ensureEmbeddedWrapper() string {
38+
if len(embeddedLauncherCoreCLI) == 0 {
39+
return ""
40+
}
41+
base := utils.BaseRoot()
42+
dir := filepath.Join(base, "bin")
43+
_ = os.MkdirAll(dir, 0755)
44+
target := filepath.Join(dir, "launcher_core_cli.exe")
45+
if fi, err := os.Stat(target); err == nil && fi.Size() > 0 {
46+
return target
47+
}
48+
tmp := target + ".tmp"
49+
if err := os.WriteFile(tmp, embeddedLauncherCoreCLI, 0644); err == nil {
50+
if err := os.Rename(tmp, target); err == nil {
51+
return target
52+
}
53+
_ = os.Remove(tmp)
54+
}
55+
return ""
56+
}
57+
58+
func MiHoYo(msixvcPath string, outDir string) (int, string) {
59+
if strings.TrimSpace(msixvcPath) == "" || strings.TrimSpace(outDir) == "" {
60+
return 1, "ERR_ARGS"
61+
}
62+
if _, err := os.Stat(msixvcPath); err != nil {
63+
return 1, "ERR_MSIXVC_NOT_FOUND"
64+
}
65+
if err := os.MkdirAll(outDir, 0755); err != nil {
66+
return 1, "ERR_CREATE_TARGET_DIR"
67+
}
68+
69+
dll := strings.TrimSpace(os.Getenv("LAUNCHER_CORE_DLL"))
70+
if dll == "" {
71+
if p := ensureEmbeddedDLL(); p != "" {
72+
dll = p
73+
} else {
74+
base := utils.BaseRoot()
75+
cand := filepath.Join(base, "bin", "launcher_core.dll")
76+
if _, err := os.Stat(cand); err == nil {
77+
dll = cand
78+
}
79+
}
80+
}
81+
if strings.TrimSpace(dll) == "" {
82+
return 1, "ERR_DLL_NOT_AVAILABLE"
83+
}
84+
if _, err := os.Stat(dll); err != nil {
85+
return 1, "ERR_DLL_NOT_AVAILABLE"
86+
}
87+
wrapper := strings.TrimSpace(os.Getenv("LAUNCHER_CORE_WRAPPER"))
88+
if wrapper == "" {
89+
if p := ensureEmbeddedWrapper(); p != "" {
90+
wrapper = p
91+
} else {
92+
base := filepath.Dir(dll)
93+
cand := filepath.Join(base, "launcher_core_cli.exe")
94+
if _, err := os.Stat(cand); err == nil {
95+
wrapper = cand
96+
}
97+
}
98+
}
99+
if strings.TrimSpace(wrapper) == "" {
100+
return 1, "ERR_WRAPPER_NOT_FOUND"
101+
}
102+
if _, err := os.Stat(wrapper); err != nil {
103+
return 1, "ERR_WRAPPER_NOT_FOUND"
104+
}
105+
cmd := exec.Command("wine", wrapper, msixvcPath, outDir)
106+
if err := cmd.Run(); err != nil {
107+
return 1, "ERR_APPX_INSTALL_FAILED"
108+
}
109+
return 0, ""
110+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#include <windows.h>
2+
#include <string>
3+
4+
int wmain(int argc, wchar_t* argv[]) {
5+
if (argc < 3) return 2;
6+
std::wstring msix = argv[1];
7+
std::wstring out = argv[2];
8+
9+
wchar_t dllPath[MAX_PATH];
10+
std::wstring envdll;
11+
wchar_t* envp = _wgetenv(L"LAUNCHER_CORE_DLL");
12+
if (envp && *envp) envdll = envp;
13+
if (!envdll.empty()) {
14+
wcsncpy_s(dllPath, envdll.c_str(), _TRUNCATE);
15+
} else {
16+
wchar_t exePath[MAX_PATH];
17+
GetModuleFileNameW(NULL, exePath, MAX_PATH);
18+
wchar_t dir[MAX_PATH];
19+
wcsncpy_s(dir, exePath, _TRUNCATE);
20+
for (int i = (int)wcslen(dir)-1; i >= 0; --i) { if (dir[i] == L'\\' || dir[i] == L'/') { dir[i] = 0; break; } }
21+
std::wstring cand = std::wstring(dir) + L"\\launcher_core.dll";
22+
WIN32_FILE_ATTRIBUTE_DATA fad;
23+
if (GetFileAttributesExW(cand.c_str(), GetFileExInfoStandard, &fad)) {
24+
wcsncpy_s(dllPath, cand.c_str(), _TRUNCATE);
25+
} else {
26+
wcsncpy_s(dllPath, L"launcher_core.dll", _TRUNCATE);
27+
}
28+
}
29+
30+
HMODULE h = LoadLibraryW(dllPath);
31+
if (!h) return 3;
32+
typedef int (__stdcall *MIW)(const wchar_t*, const wchar_t*);
33+
typedef int (__stdcall *MIA)(const char*, const char*);
34+
MIW miw = (MIW)GetProcAddress(h, "miHoYoW");
35+
if (miw) {
36+
int rc = miw(msix.c_str(), out.c_str());
37+
FreeLibrary(h);
38+
return rc;
39+
}
40+
MIA mia = (MIA)GetProcAddress(h, "miHoYo");
41+
if (mia) {
42+
int rc = mia(std::string(msix.begin(), msix.end()).c_str(), std::string(out.begin(), out.end()).c_str());
43+
FreeLibrary(h);
44+
return rc;
45+
}
46+
FreeLibrary(h);
47+
return 3;
48+
}
92 KB
Binary file not shown.

internal/gameinput/gameinput.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
//go:build windows
2+
13
package gameinput
24

35
import (

0 commit comments

Comments
 (0)