Skip to content

Commit 92c396a

Browse files
committed
fix: multiple embedded plugins
1 parent 64b919a commit 92c396a

File tree

3 files changed

+101
-43
lines changed

3 files changed

+101
-43
lines changed

client/plugin/embed.go

Lines changed: 15 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -153,13 +153,20 @@ func (plug *EmbedPlugin) GetLevel() MalLevel {
153153
return plug.Level
154154
}
155155

156+
// EmbedURI 构建当前嵌入式插件的规范资源URI
157+
func (plug *EmbedPlugin) EmbedURI(resourcePath string) string {
158+
rootPath := strings.Trim(plug.RootPath, "/")
159+
resourcePath = strings.TrimPrefix(resourcePath, "/")
160+
return fmt.Sprintf("embed://%s/%s", rootPath, resourcePath)
161+
}
162+
156163
// registerEmbedResourceFunctions 注册嵌入式资源相关的Lua函数
157164
func (plug *EmbedPlugin) registerEmbedResourceFunctions() {
158165
// 重写script_resource函数 - 返回资源文件路径
159166
plug.registerFunction("script_resource", func(filename string) (string, error) {
160167
resourcePath := "resources/" + filename
161168
if _, exists := plug.GetFileContent(resourcePath); exists {
162-
return fmt.Sprintf("embed://%s/%s", plug.Name, resourcePath), nil
169+
return plug.EmbedURI(resourcePath), nil
163170
}
164171

165172
// 回退到文件系统
@@ -175,7 +182,7 @@ func (plug *EmbedPlugin) registerEmbedResourceFunctions() {
175182
for _, levelPlugin := range globalManager.GetEmbeddedPluginsByLevel(level) {
176183
resourcePath := "resources/" + filename
177184
if _, fileExists := levelPlugin.GetFileContent(resourcePath); fileExists {
178-
return fmt.Sprintf("embed://%s/%s", levelPlugin.Name, resourcePath), nil
185+
return levelPlugin.EmbedURI(resourcePath), nil
179186
}
180187
}
181188
}
@@ -192,7 +199,7 @@ func (plug *EmbedPlugin) registerEmbedResourceFunctions() {
192199

193200
resourcePath := "resources/" + filename
194201
if _, exists := plug.GetFileContent(resourcePath); exists {
195-
return fmt.Sprintf("embed://%s/%s", plug.Name, resourcePath), nil
202+
return plug.EmbedURI(resourcePath), nil
196203
}
197204

198205
resourceFile := filepath.Join(assets.GetMalsDir(), plug.Name, "resources", filename)
@@ -208,7 +215,7 @@ func (plug *EmbedPlugin) registerEmbedResourceFunctions() {
208215
for _, levelPlugin := range globalManager.GetEmbeddedPluginsByLevel(level) {
209216
resourcePath := "resources/" + filename
210217
if _, fileExists := levelPlugin.GetFileContent(resourcePath); fileExists {
211-
return fmt.Sprintf("embed://%s/%s", levelPlugin.Name, resourcePath), nil
218+
return levelPlugin.EmbedURI(resourcePath), nil
212219
}
213220
}
214221
}
@@ -262,35 +269,11 @@ func (plug *EmbedPlugin) registerEmbedResourceFunctions() {
262269
// 新增read_embed_resource函数 - 专门用于读取嵌入式资源,支持embed://路径
263270
plug.registerFunction("read_embed_resource", func(resourcePath string) (string, error) {
264271
if strings.HasPrefix(resourcePath, "embed://") {
265-
// 解析嵌入式资源路径: embed://pluginName/resourcePath
266-
parts := strings.TrimPrefix(resourcePath, "embed://")
267-
pathParts := strings.SplitN(parts, "/", 2)
268-
if len(pathParts) != 2 {
269-
return "", fmt.Errorf("invalid embedded resource path: %s", resourcePath)
270-
}
271-
272-
pluginName := pathParts[0]
273-
filename := strings.TrimPrefix(pathParts[1], "resources/")
274-
275-
// 如果是当前插件的资源
276-
if pluginName == plug.Name {
277-
resourceFilePath := "resources/" + filename
278-
if content, exists := plug.GetFileContent(resourceFilePath); exists {
279-
return string(content), nil
280-
}
281-
}
282-
283-
// 从全局管理器查找其他插件的资源
284-
if globalManager := GetGlobalMalManager(); globalManager != nil {
285-
if plugin, exists := globalManager.GetEmbedPlugin(pluginName); exists {
286-
resourceFilePath := "resources/" + filename
287-
if content, fileExists := plugin.GetFileContent(resourceFilePath); fileExists {
288-
return string(content), nil
289-
}
290-
}
272+
content, err := intl.ReadEmbedResource(resourcePath)
273+
if err != nil {
274+
return "", err
291275
}
292-
293-
return "", fmt.Errorf("embedded resource not found: %s", resourcePath)
276+
return string(content), nil
294277
}
295278

296279
// 如果不是embed://路径,直接从文件系统读取

helper/intl/intl.go

Lines changed: 54 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -78,34 +78,77 @@ func GetResourcePath(levelName, filename string) string {
7878
return fmt.Sprintf("embed://%s/resources/%s", levelName, filename)
7979
}
8080

81-
// ParseEmbedPath 解析embed://路径,返回level和文件路径
82-
func ParseEmbedPath(embedPath string) (levelName, filePath string, err error) {
81+
// ParseEmbedPath 解析embed://路径,返回命名空间和文件路径
82+
func ParseEmbedPath(embedPath string) (namespace, filePath string, err error) {
8383
if !strings.HasPrefix(embedPath, "embed://") {
8484
return "", "", fmt.Errorf("invalid embed path: %s", embedPath)
8585
}
8686

87-
// 移除embed://前缀
88-
path := strings.TrimPrefix(embedPath, "embed://")
89-
90-
// 分割为level和文件路径
87+
path := strings.Trim(strings.TrimPrefix(embedPath, "embed://"), "/")
9188
parts := strings.SplitN(path, "/", 2)
9289
if len(parts) != 2 {
9390
return "", "", fmt.Errorf("invalid embed path format: %s", embedPath)
9491
}
9592

96-
return parts[0], parts[1], nil
93+
return strings.TrimSpace(parts[0]), strings.TrimPrefix(parts[1], "/"), nil
94+
}
95+
96+
func isKnownLevel(namespace string) bool {
97+
switch namespace {
98+
case "custom", "professional", "community":
99+
return true
100+
default:
101+
return false
102+
}
103+
}
104+
105+
func buildFallbackEmbedCandidates(namespace, filePath string) []string {
106+
levels := []string{"custom", "professional", "community"}
107+
seen := make(map[string]struct{}, len(levels)+1)
108+
candidates := make([]string, 0, len(levels)+1)
109+
110+
add := func(path string) {
111+
path = strings.Trim(path, "/")
112+
if path == "" {
113+
return
114+
}
115+
if _, ok := seen[path]; ok {
116+
return
117+
}
118+
seen[path] = struct{}{}
119+
candidates = append(candidates, path)
120+
}
121+
122+
for _, level := range levels {
123+
add(level + "/" + namespace + "/" + filePath)
124+
}
125+
126+
if isKnownLevel(namespace) {
127+
add(namespace + "/" + namespace + "/" + filePath)
128+
}
129+
130+
return candidates
97131
}
98132

99133
// ReadEmbedResource 根据embed://路径读取嵌入式资源
100134
func ReadEmbedResource(embedPath string) ([]byte, error) {
101-
levelName, filePath, err := ParseEmbedPath(embedPath)
135+
namespace, filePath, err := ParseEmbedPath(embedPath)
102136
if err != nil {
103137
return nil, err
104138
}
105139

106-
// 构建完整路径
107-
fullPath := levelName + "/" + filePath
108-
return GetFileContent(fullPath)
140+
directPath := strings.Trim(strings.TrimPrefix(embedPath, "embed://"), "/")
141+
if content, readErr := GetFileContent(directPath); readErr == nil {
142+
return content, nil
143+
}
144+
145+
for _, candidate := range buildFallbackEmbedCandidates(namespace, filePath) {
146+
if content, readErr := GetFileContent(candidate); readErr == nil {
147+
return content, nil
148+
}
149+
}
150+
151+
return nil, fmt.Errorf("embedded resource not found: %s", embedPath)
109152
}
110153

111154
// ListLevels 列出所有可用的级别(community, professional)
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package intl
2+
3+
import "testing"
4+
5+
func TestReadEmbedResourceCanonicalPath(t *testing.T) {
6+
path := "embed://community/community/resources/bof/ipconfig/ipconfig.x64.o"
7+
content, err := ReadEmbedResource(path)
8+
if err != nil {
9+
t.Fatalf("canonical path should be readable: %v", err)
10+
}
11+
if len(content) == 0 {
12+
t.Fatal("canonical path returned empty content")
13+
}
14+
}
15+
16+
func TestReadEmbedResourceLegacyPathFallback(t *testing.T) {
17+
path := "embed://community/resources/bof/ipconfig/ipconfig.x64.o"
18+
content, err := ReadEmbedResource(path)
19+
if err != nil {
20+
t.Fatalf("legacy path should fallback correctly: %v", err)
21+
}
22+
if len(content) == 0 {
23+
t.Fatal("legacy fallback returned empty content")
24+
}
25+
}
26+
27+
func TestReadEmbedResourceInvalidPath(t *testing.T) {
28+
_, err := ReadEmbedResource("embed://invalid")
29+
if err == nil {
30+
t.Fatal("expected parse error for invalid embed path")
31+
}
32+
}

0 commit comments

Comments
 (0)