Skip to content

Commit 1a10be0

Browse files
author
StArray
committed
feat(runtime): 新增跨平台原生代码修补器支持及 Android Capstone 内嵌解析
1 parent f1c88ee commit 1a10be0

21 files changed

Lines changed: 1130 additions & 537 deletions

File tree

‎Android/library/src/main/java/starray/android/modmanager/ModManagerUpdater.java‎

Lines changed: 57 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,14 @@
4040
public class ModManagerUpdater {
4141

4242
private static final String TAG = "ModManagerUpdater";
43-
private static final String PROXY_URL = "https://gh-proxy.org/";
43+
private static final String DEFAULT_PROXY_URL = "https://gh-proxy.org/";
4444

4545
private final Activity activity;
4646
private String versionJsonUrl;
4747
private Path basePath;
4848
private boolean useProxy = true;
49+
/** 自定义代理前缀;null 时用 {@link #DEFAULT_PROXY_URL}(仅在 useProxy 开启时生效) */
50+
private String customProxyUrl;
4951

5052
public static ModManagerUpdater create(Activity activity) {
5153
return new ModManagerUpdater(activity);
@@ -188,7 +190,7 @@ private void showDownloadDialog(VersionInfo remote, Path mgr, Path rt, Path mods
188190
Log.w(TAG, "Failed to delete local version.json: " + e.getMessage());
189191
}
190192
}
191-
Log.i(TAG, "Starting download: url=" + remote.managerUrl + " target=" + mgr);
193+
Log.i(TAG, "Starting download: url=" + remote.downloadUrl() + " target=" + mgr);
192194
var dialog = new AlertDialog.Builder(activity)
193195
.setTitle(hasLocal ? "更新中" : "下载中")
194196
.setMessage("正在下载...")
@@ -244,18 +246,19 @@ private Path downloadAndExtractSync(VersionInfo version, Path targetDir, AlertDi
244246
var zipFile = targetDir.getParent().resolve("manager-download.zip");
245247

246248
updateDialog(dialog, "正在下载...", -1);
247-
downloadFile(version.managerUrl, zipFile,
249+
downloadFile(version.downloadUrl(), zipFile,
248250
pct -> updateDialog(dialog, "正在下载 " + pct + "%", pct));
249251

250252
long zipSize = Files.size(zipFile);
251253
Log.i(TAG, "Downloaded " + zipSize + " bytes");
252254

253-
if (version.sha256 != null && !version.sha256.isEmpty()) {
255+
var expected = version.downloadSha256();
256+
if (expected != null && !expected.isEmpty()) {
254257
updateDialog(dialog, "校验中...", -1);
255258
Log.i(TAG, "Verifying SHA-256...");
256259
var actual = sha256(zipFile);
257-
if (!actual.equalsIgnoreCase(version.sha256)) {
258-
Log.e(TAG, "SHA-256 mismatch! expected=" + version.sha256 + " actual=" + actual);
260+
if (!actual.equalsIgnoreCase(expected)) {
261+
Log.e(TAG, "SHA-256 mismatch! expected=" + expected + " actual=" + actual);
259262
Files.delete(zipFile);
260263
throw new IOException("SHA-256 mismatch");
261264
}
@@ -288,7 +291,7 @@ private void launch(Path mgr, Path rt, Path mods, boolean useNewThread) {
288291
Log.i(TAG, "Created mods directory: " + mods);
289292
}
290293

291-
// 从 version.json 读取入口配置,不存在则用默认值
294+
// 从 version.json 读取入口配置(platforms.android),无则用编译期默认值
292295
var verFile = mgr.resolve("version.json");
293296
String dll = "StArray.ModManager.dll";
294297
String type = "StArray.ModManager.Managed";
@@ -297,17 +300,20 @@ private void launch(Path mgr, Path rt, Path mods, boolean useNewThread) {
297300
try {
298301
var localVer = JSON.parseObject(
299302
new String(Files.readAllBytes(verFile)), VersionInfo.class);
300-
if (localVer.entryAssembly != null && !localVer.entryAssembly.isEmpty())
301-
dll = localVer.entryAssembly;
302-
if (localVer.entryMethod != null && !localVer.entryMethod.isEmpty()) {
303-
// 格式:"TypeName::MethodName" → type, method
304-
var parts = localVer.entryMethod.split("::", 2);
305-
if (parts.length == 2) {
306-
type = parts[0].trim();
307-
method = parts[1].trim();
308-
} else {
309-
// 单值当作方法名,类型取默认
310-
method = localVer.entryMethod.trim();
303+
var android = localVer.android();
304+
if (android != null) {
305+
if (android.entryAssembly != null && !android.entryAssembly.isEmpty())
306+
dll = android.entryAssembly;
307+
if (android.entryMethod != null && !android.entryMethod.isEmpty()) {
308+
// 格式:"TypeName::MethodName" → type, method
309+
var parts = android.entryMethod.split("::", 2);
310+
if (parts.length == 2) {
311+
type = parts[0].trim();
312+
method = parts[1].trim();
313+
} else {
314+
// 单值当作方法名,类型取默认
315+
method = android.entryMethod.trim();
316+
}
311317
}
312318
}
313319
Log.i(TAG, "Entry from version.json: " + dll + " → " + type + "::" + method);
@@ -348,22 +354,52 @@ public static class VersionInfo {
348354
public String version;
349355
@JSONField(name = "versionCode")
350356
public int versionCode;
357+
@JSONField(name = "platforms")
358+
public PlatformInfo[] platforms;
359+
360+
/** android 平台配置(platforms[].name == "android"),无则 null。 */
361+
public PlatformInfo android() {
362+
if (platforms == null) return null;
363+
for (var p : platforms) {
364+
if ("android".equalsIgnoreCase(p.name)) return p;
365+
}
366+
return null;
367+
}
368+
369+
/** 下载地址(android 平台的 manager 字段)。 */
370+
public String downloadUrl() {
371+
var p = android();
372+
return p != null ? p.managerUrl : null;
373+
}
374+
375+
/** SHA-256(android 平台)。 */
376+
public String downloadSha256() {
377+
var p = android();
378+
return p != null ? p.sha256 : null;
379+
}
380+
}
381+
382+
/** platforms 数组元素(name/manager/sha256/entryAssembly/entryMethod)。 */
383+
public static class PlatformInfo {
384+
@JSONField(name = "name")
385+
public String name;
351386
@JSONField(name = "manager")
352387
public String managerUrl;
353388
@JSONField(name = "sha256")
354389
public String sha256;
355-
/** 入口程序集文件名(如 "StArray.ModManager.dll") */
356390
@JSONField(name = "entryAssembly")
357391
public String entryAssembly;
358-
/** 入口类型与方法(如 "StArray.ModManager.Managed::Entry") */
359392
@JSONField(name = "entryMethod")
360393
public String entryMethod;
361394
}
362395

363396
// ──── HTTP ────
364397

365398
private String proxyUrl(String url) {
366-
return useProxy ? PROXY_URL + url : url;
399+
if (!useProxy) return url;
400+
var prefix = customProxyUrl != null && !customProxyUrl.isEmpty()
401+
? customProxyUrl : DEFAULT_PROXY_URL;
402+
return prefix + url;
367403
}
368404

369405
private String httpGetString(String urlStr) throws IOException {

‎StArray.ModManager.Android/Managed.cs‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212

1313
namespace StArray.ModManager.Android;
1414

15-
/// <summary>CoreCLR entry / 加载器入口 — called by native delegate</summary>
1615
/// <summary>CoreCLR 入口 / 加载器入口 — called by native delegate</summary>
1716
public static class Managed
1817
{
@@ -57,10 +56,14 @@ public static int Entry(int argc, IntPtr argv)
5756
NativeLibraryResolver.ResolveRequested += (libraryName, assembly) =>
5857
libraryName.Contains("IL2CPP_LIBRARY_NAME") ? Il2CppInit() : IntPtr.Zero;
5958

59+
// Capstone:DllImport("capstone") 在 Gee.External.Capstone 程序集内,
60+
// resolver 必须装到该程序集上才生效;触发时从内嵌资源解压并经 cache 加载
61+
NativeLibraryResolver.Install(typeof(Gee.External.Capstone.CapstoneDisassembler).Assembly);
62+
NativeLibraryResolver.ResolveRequested += CapstoneLibrary.Resolve;
63+
6064
// 文件日志 → manager 根目录(与 mods/、runtime/ 同级)
6165
var rootDir = Path.GetDirectoryName(modsPath)!;
6266
_logWriter = new StreamWriter(Path.Combine(rootDir, "manager.log"), append: true) { AutoFlush = true };
63-
_logWriter.AutoFlush = true;
6467
Logger.OnLog += (level, tag, msg) =>
6568
{
6669
lock (_logLock)

0 commit comments

Comments
 (0)