Skip to content

Commit 00b901b

Browse files
committed
feat: support linux(WIP)
1 parent 79a6bc1 commit 00b901b

File tree

11 files changed

+246
-77
lines changed

11 files changed

+246
-77
lines changed

internal/extractor/embed.go

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

internal/extractor/extractor.go

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,30 +7,28 @@ import (
77
"fmt"
88
"bytes"
99
"crypto/sha256"
10-
_ "embed"
1110
"io"
1211
"os"
13-
"path/filepath"
14-
"strings"
15-
"sync"
16-
"syscall"
12+
"path/filepath"
13+
"strings"
14+
"sync"
15+
"syscall"
1716
"unsafe"
1817

1918
"golang.org/x/sys/windows"
2019
"github.com/liteldev/LeviLauncher/internal/vcruntime"
2120
)
2221

2322
var (
24-
loadOnce sync.Once
25-
loadErr error
26-
miProc *windows.LazyProc
27-
useWide bool
28-
k32 *windows.LazyDLL
29-
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
3029
)
3130

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

3533
func prepareDLL() (string, error) {
3634
if len(embeddedLauncherCoreDLL) == 0 {

internal/extractor/extractor_linux.go

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,72 @@
22

33
package extractor
44

5+
import (
6+
"os"
7+
"path/filepath"
8+
"strings"
9+
10+
"github.com/liteldev/LeviLauncher/internal/utils"
11+
)
12+
513
func Init() {}
614

15+
func ensureEmbeddedSO() string {
16+
if len(embeddedLauncherCoreSO) == 0 {
17+
return ""
18+
}
19+
base := utils.BaseRoot()
20+
dir := filepath.Join(base, "bin")
21+
_ = os.MkdirAll(dir, 0755)
22+
target := filepath.Join(dir, "liblauncher_core.so")
23+
if fi, err := os.Stat(target); err == nil && fi.Size() > 0 {
24+
return target
25+
}
26+
tmp := target + ".tmp"
27+
if err := os.WriteFile(tmp, embeddedLauncherCoreSO, 0644); err == nil {
28+
if err := os.Rename(tmp, target); err == nil {
29+
return target
30+
}
31+
_ = os.Remove(tmp)
32+
}
33+
return ""
34+
}
35+
736
func MiHoYo(msixvcPath string, outDir string) (int, string) {
8-
return 1, "ERR_APPX_INSTALL_UNSUPPORTED_ON_LINUX"
9-
}
37+
if strings.TrimSpace(msixvcPath) == "" || strings.TrimSpace(outDir) == "" {
38+
return 1, "ERR_ARGS"
39+
}
40+
if _, err := os.Stat(msixvcPath); err != nil {
41+
return 1, "ERR_MSIXVC_NOT_FOUND"
42+
}
43+
if err := os.MkdirAll(outDir, 0755); err != nil {
44+
return 1, "ERR_CREATE_TARGET_DIR"
45+
}
46+
47+
so := strings.TrimSpace(os.Getenv("LAUNCHER_CORE_SO"))
48+
if so == "" {
49+
if p := ensureEmbeddedSO(); p != "" {
50+
so = p
51+
} else {
52+
base := utils.BaseRoot()
53+
cand := filepath.Join(base, "bin", "liblauncher_core.so")
54+
if _, err := os.Stat(cand); err == nil {
55+
so = cand
56+
}
57+
}
58+
}
59+
if strings.TrimSpace(so) == "" {
60+
return 1, "ERR_SO_NOT_AVAILABLE"
61+
}
62+
if _, err := os.Stat(so); err != nil {
63+
return 1, "ERR_SO_NOT_AVAILABLE"
64+
}
65+
rc, msg := callLauncherCoreSO(so, msixvcPath, outDir)
66+
if rc == 0 {
67+
return 0, ""
68+
}
69+
if strings.TrimSpace(msg) == "" {
70+
msg = "ERR_APPX_INSTALL_FAILED"
71+
}
72+
return rc, msg
73+
}

internal/extractor/so_linux_cgo.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//go:build linux && cgo
2+
3+
package extractor
4+
5+
/*
6+
#cgo LDFLAGS: -ldl
7+
#include <dlfcn.h>
8+
#include <stdlib.h>
9+
typedef int (*mihoyo_fn)(const char*, const char*);
10+
11+
int call_mihoyo(const char* so, const char* msix, const char* out) {
12+
void* h = dlopen(so, RTLD_LAZY);
13+
if (!h) return -2;
14+
mihoyo_fn fn = (mihoyo_fn)dlsym(h, "MiHoYo");
15+
if (!fn) { dlclose(h); return -3; }
16+
int rc = fn(msix, out);
17+
dlclose(h);
18+
return rc;
19+
}
20+
*/
21+
import "C"
22+
23+
import (
24+
"unsafe"
25+
)
26+
27+
func callLauncherCoreSO(soPath string, msixvcPath string, outDir string) (int, string) {
28+
cso := C.CString(soPath)
29+
cmsix := C.CString(msixvcPath)
30+
cout := C.CString(outDir)
31+
rc := C.call_mihoyo(cso, cmsix, cout)
32+
C.free(unsafe.Pointer(cso))
33+
C.free(unsafe.Pointer(cmsix))
34+
C.free(unsafe.Pointer(cout))
35+
if int(rc) != 0 {
36+
return int(rc), "ERR_APPX_INSTALL_FAILED"
37+
}
38+
return 0, ""
39+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
//go:build linux && !cgo
2+
3+
package extractor
4+
5+
func callLauncherCoreSO(soPath string, msixvcPath string, outDir string) (int, string) {
6+
return 1, "ERR_SO_NOT_AVAILABLE"
7+
}

internal/peeditor/peeditor.go

Lines changed: 10 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
11
package peeditor
22

33
import (
4-
"bytes"
5-
"context"
6-
"crypto/sha256"
7-
_ "embed"
8-
"io"
9-
"os"
10-
"os/exec"
11-
"path/filepath"
12-
"strings"
13-
"syscall"
4+
"bytes"
5+
"context"
6+
"crypto/sha256"
7+
_ "embed"
8+
"io"
9+
"os"
10+
"path/filepath"
11+
"strings"
1412

15-
"github.com/wailsapp/wails/v3/pkg/application"
13+
"github.com/wailsapp/wails/v3/pkg/application"
1614
)
1715

1816
const (
@@ -76,30 +74,7 @@ func EnsureForVersion(ctx context.Context, versionDir string) bool {
7674
return true
7775
}
7876

79-
func RunForVersion(ctx context.Context, versionDir string) bool {
80-
dir := strings.TrimSpace(versionDir)
81-
if dir == "" {
82-
application.Get().Event.Emit(EventEnsureDone, false)
83-
return false
84-
}
85-
exe := filepath.Join(dir, "Minecraft.Windows.exe")
86-
tool := filepath.Join(dir, "PeEditor.exe")
87-
bak := filepath.Join(dir, "Minecraft.Windows.exe.bak")
88-
if fileExists(bak) {
89-
return true
90-
}
91-
if !fileExists(tool) || !fileExists(exe) {
92-
application.Get().Event.Emit(EventEnsureDone, false)
93-
return false
94-
}
95-
application.Get().Event.Emit(EventEnsureStart, struct{}{})
96-
cmd := exec.Command(tool, "-m", "-b", "--inplace", "--exe", "./Minecraft.Windows.exe")
97-
cmd.Dir = dir
98-
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
99-
_ = cmd.Run()
100-
application.Get().Event.Emit(EventEnsureDone, true)
101-
return true
102-
}
77+
// RunForVersion implemented per-OS
10378

10479
func copyFile(src, dst string) bool {
10580
s, err := os.Open(src)
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//go:build linux
2+
3+
package peeditor
4+
5+
import (
6+
"context"
7+
"os/exec"
8+
"path/filepath"
9+
"strings"
10+
"github.com/wailsapp/wails/v3/pkg/application"
11+
)
12+
13+
func RunForVersion(ctx context.Context, versionDir string) bool {
14+
dir := strings.TrimSpace(versionDir)
15+
if dir == "" { application.Get().Event.Emit(EventEnsureDone, false); return false }
16+
exe := filepath.Join(dir, "Minecraft.Windows.exe")
17+
tool := filepath.Join(dir, "PeEditor.exe")
18+
bak := filepath.Join(dir, "Minecraft.Windows.exe.bak")
19+
if fileExists(bak) { return true }
20+
if !fileExists(tool) || !fileExists(exe) { application.Get().Event.Emit(EventEnsureDone, false); return false }
21+
application.Get().Event.Emit(EventEnsureStart, struct{}{})
22+
cmd := exec.Command("wine", tool, "-m", "-b", "--inplace", "--exe", "./Minecraft.Windows.exe")
23+
cmd.Dir = dir
24+
_ = cmd.Run()
25+
application.Get().Event.Emit(EventEnsureDone, true)
26+
return true
27+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//go:build windows
2+
3+
package peeditor
4+
5+
import (
6+
"context"
7+
"os/exec"
8+
"path/filepath"
9+
"strings"
10+
"github.com/wailsapp/wails/v3/pkg/application"
11+
"github.com/liteldev/LeviLauncher/internal/procutil"
12+
)
13+
14+
func RunForVersion(ctx context.Context, versionDir string) bool {
15+
dir := strings.TrimSpace(versionDir)
16+
if dir == "" { application.Get().Event.Emit(EventEnsureDone, false); return false }
17+
exe := filepath.Join(dir, "Minecraft.Windows.exe")
18+
tool := filepath.Join(dir, "PeEditor.exe")
19+
bak := filepath.Join(dir, "Minecraft.Windows.exe.bak")
20+
if fileExists(bak) { return true }
21+
if !fileExists(tool) || !fileExists(exe) { application.Get().Event.Emit(EventEnsureDone, false); return false }
22+
application.Get().Event.Emit(EventEnsureStart, struct{}{})
23+
cmd := exec.Command(tool, "-m", "-b", "--inplace", "--exe", "./Minecraft.Windows.exe")
24+
cmd.Dir = dir
25+
procutil.NoWindow(cmd)
26+
_ = cmd.Run()
27+
application.Get().Event.Emit(EventEnsureDone, true)
28+
return true
29+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
//go:build linux
2+
3+
package procutil
4+
5+
import "os/exec"
6+
7+
func NoWindow(cmd *exec.Cmd) {}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//go:build windows
2+
3+
package procutil
4+
5+
import (
6+
"os/exec"
7+
"syscall"
8+
)
9+
10+
func NoWindow(cmd *exec.Cmd) {
11+
if cmd != nil {
12+
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
13+
}
14+
}

0 commit comments

Comments
 (0)