From 1f77c1032aeb1062207b6a41af601fa987ade4de Mon Sep 17 00:00:00 2001 From: koren <131921956+kkorenn@users.noreply.github.com> Date: Sun, 7 Jun 2026 12:02:25 +0900 Subject: [PATCH 1/4] Add macOS arm64 support and dlsym interpose Add support for osx-arm64: add ARM64 platform and runtime identifier in Directory.Build.props and define an ARM64 build constant. Include osx-arm64 native libraries (libdobby.a and libplthook.a) and update the osx-x64 plthook. Implement a DYLD dlsym interpose flow because plthook cannot patch modern macOS GOTs: export MLRegisterDlsymHook, MLRealDlsym and MLMacOSJitCopy from native osxentry.cpp, register the managed detour from ModuleSymbolRedirect, and use RealDlsym to resolve genuine runtime symbols. Update the bootstrap project file to keep additional linker symbols and append native exports before native linking. Improve macOS-specific behavior: add JIT copy support for arm64, open a Terminal tailing the MelonLoader log from the macOS launcher script, better resolve the game .app path and frameworks path, and select the appropriate DetourNative platform for macOS ARM64. Also update osxexports.def with the new native exports. --- Directory.Build.props | 7 +- .../Deps/osx-arm64/libdobby.a | Bin 0 -> 91856 bytes .../Deps/osx-arm64/libplthook.a | Bin 0 -> 10536 bytes .../Deps/osx-x64/libplthook.a | Bin 11240 -> 10816 bytes .../MelonLoader.Bootstrap.csproj | 14 ++++ MelonLoader.Bootstrap/ModuleSymbolRedirect.cs | 59 +++++++++++++-- .../OSXEntry/melonloader-launch.sh | 66 +++++++++++++++++ MelonLoader.Bootstrap/OSXEntry/osxentry.cpp | 68 ++++++++++++++++++ MelonLoader.Bootstrap/osxexports.def | 3 + MelonLoader/Core.cs | 9 +++ MelonLoader/MelonUtils.cs | 11 +++ MelonLoader/Utils/MelonEnvironment.cs | 36 +++++++++- 12 files changed, 265 insertions(+), 8 deletions(-) create mode 100644 MelonLoader.Bootstrap/Deps/osx-arm64/libdobby.a create mode 100644 MelonLoader.Bootstrap/Deps/osx-arm64/libplthook.a diff --git a/Directory.Build.props b/Directory.Build.props index 68aa6f4b4..04bb8f05b 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -27,8 +27,8 @@ false Debug;Release - x86;x64 - win-x86;win-x64;linux-x64;osx-x64 + x86;x64;arm64 + win-x86;win-x64;linux-x64;osx-x64;osx-arm64 false false @@ -109,6 +109,9 @@ $(DefineConstants);X64 + + $(DefineConstants);ARM64 + $(SolutionDir)Output/$(Configuration)/$(RuntimeIdentifier) diff --git a/MelonLoader.Bootstrap/Deps/osx-arm64/libdobby.a b/MelonLoader.Bootstrap/Deps/osx-arm64/libdobby.a new file mode 100644 index 0000000000000000000000000000000000000000..4f6999e1be8e4e2e2e6b3ce85e03160b7f779514 GIT binary patch literal 91856 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 literal 0 HcmV?d00001 diff --git a/MelonLoader.Bootstrap/Deps/osx-x64/libplthook.a b/MelonLoader.Bootstrap/Deps/osx-x64/libplthook.a index 9240b35c56b495d5e658bb847d9d76ca6933a942..91ba75d5f8a2a81eb6836907c6458fbcb98f932b 100644 GIT binary patch literal 10816 zcmb_ie{@vUou7da28_PgMu?RyQw^1D9nEOKjDqIjOu0|qKq^q_DxHL6LUKrE()mGx zg%Ua$%iZTx&MCI+_PB@Bx_j7NyX9;vrafvWKuBn-X3TPK+trv?JIFa3t!>-(y6>5_+*`Mfq1Mt>*`cAHI+@b4sa zOQ=b>Lc&%FH%j;;33mVnx;kQQ;qaP3b4NHD97ltpcxNFN2!>*jbwzdE?Xk8%S0o(E ztB*v&k#RKK6$}+(fmQ1QO)V{vU^IFqp{*&@(vhb}B-qu_)Eu;t9WBd{fh;?^?=G~X zFe1@wYNg#9r&>b{M|%Q2b8Bx8W87AG^`ldlC);fHiKu`{ZIf`dO~9WM7s`#e4iVu~ zO8rceu$kJR)X`+1K&H4z9+f~K7VL?c_a?r~w_@2iZr%N2nt4a^N^qNo{Ku%O#MVo| zDuF}qO`1>&LZ_E1Y8pbP)C_-9Lfw#$Y)Z?f5F4z}E4vt4U4<@H4N3$_JXBTbz_ zlUO|7;V+o=)_5DBFUhmYHJDcGp5UFhDsh?JqwyqDKsD;hab18O4nR_Z_-s`++pPdy zU$)t1qZ0CL4OEXpb4=G1T-V_$GBs-cc3IB&d+NjL*VX0fvZW0yIjZy-Xr=K{#y5xzmELpKO$^c8|Ibxc6u06|$bc!G^vsTdbvE zWr@9xN7P5vmFhRuz{)+$*f2sI2N-{&kd&eRl$pH*3dYYioxFfLjlZJt*ZBvGzqlfo z%P9}1mFi>--Bl-@CMY+7%>-vI=W=W)Sr3e4N_#Qx!^o2tK=dd7tn?)@9zPlQN!;i> zKB5|rR+T5;Qu-F4LQCv*sxLu8&gVV_+p;0wFbT`fXhpeQnb}8}&5!9ayj3NdeQ)k} zv4>Toq-y@nHd}A(tMh9K97$ZbG5$w2k#i`0b4hG!eU*cy=1hcXrS*M^_q^6TgelPZ zGlLHFn0>a@lCSgNaQ100f02f!^Pe#QFMqBCKuuiEDSbaAC6cF z?SInwb6r%$SN5t#Z57#V1t{>6pJM7^BWvnWY0Lb`B;5xRgWP-?+q9P^HP17obHbl# zI+EYQ1dUbg%dDcwOwIpeMWHD2u|5 zPZi!PEN(1y8jGq#t;MMFA)aQbl6pN=JJ^Qx`x{IZxxQ!sYY2}bYn-Av zGsL#wHR|A9+t8zJ$k$KNW61XcVXCnbmi;qJzNPcd5kvyvKuJ;%Gr3bsC40qfvXN#6 z4hlf->g5~KK7MRTYTjC`(wz?)^DbbLmCuj4T&&bZVc1>%R6+oyRB zD~YdASH`?5v^)4Y*$zvh5@F?SA3wJ!RRYoe5qR1_4|Gs-dw~u!&j3>%7-rl*ta%1? z<*O%D&)dp($#F7oU_hq*>rAVr=Km)&f{_{DZ8->;J@e)zn#ax&mU>aRTq?dzw7|%c z>3A(mJx$d*s#~|TCN^vlVy21e(Gj zCQrVQsHa3HetuTUjz0sT+aRA*VD@3-DN-Ww?&R2Wx+fFe$vnQ{=zp;04|020s&=i$ z%g$-sd(NTqW#`J(A@4aXGn*~(aZ5az`8ry9`3YJVEHz_%T`+!F^hSG(=H;`8e4D9J z>yYmWIOdRV3t@SahxKBOyl&k3O5Y_6S`acH;0}haz22N5e%SH{Ve1EJUE71hxDcUE zuu0Ye@bZs+?$K+fAUXcHM-}O=$p)VK~Nw!%_eqN zvafW{&eVYwMp%oq!x}yXxy&NC+SqljZl>Xs)P1W1vO z^`&E9#y&|ALa9!XGbq)|dzI>yTa@bHcBQ&=r&1lqhPnZ3SwtgdP;f!O5L+y6&%po6 z0Z*`g2fD4H$>qMIP!)HWXaCWca#&hAKOl_D#>L$0yAYp(cu(4{G?!Yz{@x(cnvX>@ zg#OrfRK6D*%n1Cd5nHZ1_Yxe!Ts7ot6^sudr4j79=L4nh*PyD2Pwes4k{fE6I$Vt# z&MCN+Iliv5*P*HnRhIE778NEcdsMM6pP<~_35cs3^@L~70q(fes376Y23@x$4rdMawi*7%9GX^6H@ zO6T&{FCws)*H9j(dG;xZe};U&afi-NV<&tW*}jlPyJTXhGPegpU)FN3&~~U9eS5`L zm{?y`iL7V&aBTjs9B7_(=F8u7aS1x@zFAGIFPjZ7)GA}sG*3xc_j{VhTjuD-db!vD zj>us|XzZ~3MxFmXZjid#k-1|F&dZ#H8M1%LlV4!329e<{C|9nZ;5Ft7X79asD~Th} zit!IL>?eHr2q~|ps)n>pLjOX0_>m}{F-QOE)Uh2bJ_8o;^1D=HG9?ADROAmre1@`O zgh<5cJ2a!Lit!~5$~-Udh2?B$_aV%hJDr`LXYa)B8n)@9I*d39tH-9-lh0`$eR@sz zWCC^FH&OKpRF@OZx_?igVeMdcf_s#AR~XkIENiN=YwN)nq-=h;GB?1lDO^o^3gYM3 z`3n9toBYXZ+$W85zmibkqZ+?7=26_&za&+)ak25sif7$?E;ZH2LhwO;81bM$_x!+;WeMB>! z7$k>cL;J{XuxdYScT?f~u*5#t@3LjTWpnD22Za6RnD(0^?6+*r3RIK*=9u=IW7_Wk zdZhf^%kQK0tMj=4@U-h#lCq=e*-64KFy`z$ughOA1^xzm*5Rl9x#fNxG zW6ncj4`sM-BC)`#*yuN25KDp$rD+DT70AaD=^FJ#%%Y9W7e-fclq#wBB~Qg_{hswD zusVUd?rN%ELG?4Jf^`oPXjuDFR>`*?HUZLsf+z9~OYP5*+rXtuN})|OaiJ;}0#-V& zKJLQ8`&$hbP=ooRK|o3V7TMEP8!5@3)6vT6i5fYOCnx_&)cSX_G!0H3fXBe(akF{6~`1U)4Zot1kee?dI(avvTwB=;+Q^Pn1q$mWF=`~Wk&LzQe|15&ky+05v6Aa3+C z`}-;%*6es>Vm=%|HYz66m(G5Umh?V{&fnENyOqRgIKHy$PCd2C&d&~HN)iK?6Bnm; zA59!nF!p!pxZGU!B(q=4y+az0Jzr3J=T+JKsg^2x&$;Y>E4v=Ev&8NR^~$dE^?jqt zW;%kq_oL5DC+;*up-D?(@6n892P^qu%JC_1==Sq$;$zr6`_9rT^YUlUb^#j8U*Ej- zEQL+RUpPy_CgX#n>-@N|sZKUeRg|#YZk->^+y#>C;@0WvNf!fu8qZbc8nlaAvk> z=RQF3#0p5Ucx+xOJn{*HXCKR-^S*UC!F;gdahFCZ!W>fMK@N$R3G>!exq^`FBw_LJ4PH{ z!~goi&H6~$68`8&d3J!@6-LT1s12`j?@t`t_}VdT%7>U1W!F7$1k?3$J;&;MbM?`w zf|$7x?7!*7daFMF+HtM=QpSM_eyC{Qs72*Dob>)(!Ucd^Vawm+YQp_qT=(Igp3qtL zD&AiznX#nYYpkv`t`}RA>Umx1TZ3SZG*&mdonGbn@C@41vtJPTE*?x^g3X96c<%?V zmml{U>wi_^N1!;oKzV+>r=nH$T#6mg?63Ni=LZ)m&+7}PwW^7Wxp=jj8x+Ln{Hceh zZIR@%1tztes9w+M`0?yhIKjj#ROR`FC7SZ{G#JnNCw4nDJ08b<8*u=SO|AI&MP^XM zdlgG|{QS`m%$C`XKax>OxnC={v(!2VGZr~sa)9t_=#GJI7#4z9;(|Tyqxh}!u5vW$ zE=^o6jW0qCHWM2gIB}PI|EwI|4k=p)_--uZA2P)|qS{~f^5)7`FJA*gEp;Mr-p-7> zpwFWeOwvNOP$oOg)N^%SJ;iZMYkm^?KvO6bjyYTLbRT~mL1$CU+1}Z-I_N}Au*W$o zZYyTF$2lu%TfAJ?o$@_TK0~YxEk*n0a0G9My220~3rkk#tgZra@(mIs7S@kTF|}w7 zN1UsgqR`HKXW@v?kcn9<#UvQ)?5G+raI2nNVt<& z@>S>xw})cE$hZnp3_NhWT*U4Fte8X#qp--D=}7IaMt9PBLP?8%`TEDa7ym~k{o9ap zwf2oT3{m-4T-RSyaz+2;^kN8Q)~B?u$H|n+^iQET;z0xz@2Ajr(hDY(6xQa|^sA>Q zCnyqJpsWV82_+sn%)JGOxLHq_0) zRrwy02^5+?QPk_oAD{5RwWSXxCO=V{n)LmWCn+-&{5s$X$xrhsipAeh$X^BGdR&K2 zn)1_v8GjbP#7U25aCMzB`DmU+vG}f{$LnB7|JCHDc^AdvUz8|)VoYmOZ}N)z(@$NP;b(wmiW(@{Nxt}{8!PZ1w1upO@8u|0{*MW-wK|F_f3BCo6q7WJMIPl&T}Te zbcZYKNPH*3w?pztf4YK?#z%+0Es~G?N)*d}SJBr?I$kjQCqMfv{vYEF7K&>tAe!Bf zkobCu-wKFV%dSz=Z(Sz=iSIBVjx{chXZ$wwOrO z_)Y;5-?I{bM&jGcO!|)jiT)GBAL4%+knpcde6hrHh+irFF`xkdnqFD>B?Kn&TH z1cd2aYXPyQT@8TK0qX%P0c!wZI@c6Hl6MqoHql1_iJk@|`s08ou8n}y&svFhNcmI;afQNBZB=|3YByTGqwR;?p{Nr1I)P4ydwXc-$ zBPc`srvQon7~q$H9|6Ray0!s20e>LrjS_!E;%6Z2ix^)95ba%mkT5Ob7C_Q7BJqHP znuJw=)c=hpe#2BinuqfcM)UdxAkkj|#P5a|CH@PEe-{v6Lb%pSoZfpnfu8_G)eR2= z;_aR5LkPjGYd1*)?8hI`>DwX{R|#edLYewL`5Q<-E zqlEPmIwd3`ex*mjPcSXvb_shWY?QEGLZ^fPqTSo*{5E(_J%n6`%gPH}5+^1q8*mZb z+81Gafm{0^#oy~-W9$2vZvm$#iS=gOuw5&0D=z?Q(y?BtkR6EM+V{zR0+;R2OB_Iy z7W@l3)*Y1}O8HjaazNr%p73wrp+2p06OerTPeu68!I)3)Eu#Oj2wzylf2oK*R77tq zqMsJuOA1&ga1SbT`SRp^m$6qbN$#3)Nlws!M2aD*m9`fnaiu%)w z=;L3!(HC~sH+3>M516@mpdIONAVlAbjeVXcxbwcY3$(23XbFhp627#Ghrrq?k09fX zC|xi3+AbQ51yT?ut*BlXZ4QTU)6yZoWNa<^qEVv70}9b6Wn*TxEM|R%*cu6fTHI!B zCk;Lj#gQ?ut@Wv5Ab%XI<4utk8@^nF-E4toaZbZjH8r;dVolLC7*0oP03+!PM1rev N9t+H^g@@T}{|hjztX}{C literal 11240 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 qL)gvYtJ4p1a8+$xgD+Ye*#vc(0zPQc> + + + + + + + + + /dev/null 2>&1; then + return + fi + + local log_path="$SCRIPT_DIR/MelonLoader/Latest.log" + local title="MelonLoader - $(basename "$APP" .app)" + local watcher_base watcher_script + watcher_base="$(mktemp "${TMPDIR:-/tmp}/melonloader-log-tail.XXXXXX")" || return + watcher_script="$watcher_base.command" + mv "$watcher_base" "$watcher_script" || { + rm -f "$watcher_base" + return + } + + local quoted_log_path quoted_title + quoted_log_path="$(shell_quote "$log_path")" + quoted_title="$(shell_quote "$title")" + + cat > "$watcher_script" </dev/null; do + sleep 0.2 +done + +if [ -f "\$LOG_PATH" ]; then + tail -n +1 -F "\$LOG_PATH" & + TAIL_PID=\$! + while kill -0 "\$GAME_PID" 2>/dev/null; do + sleep 1 + done + kill "\$TAIL_PID" 2>/dev/null || true + wait "\$TAIL_PID" 2>/dev/null || true +else + echo "MelonLoader log was not created." +fi + +rm -f "\$0" +echo +echo "Game exited. You can close this window." +EOF + + chmod +x "$watcher_script" + open -a Terminal "$watcher_script" >/dev/null 2>&1 || true +} + # Find the one .app bundle next to this script. shopt -s nullglob APPS=("$SCRIPT_DIR"/*.app) @@ -78,4 +142,6 @@ else export DYLD_INSERT_LIBRARIES="$BOOTSTRAP_PATH" fi +open_melonloader_terminal + exec "$@" diff --git a/MelonLoader.Bootstrap/OSXEntry/osxentry.cpp b/MelonLoader.Bootstrap/OSXEntry/osxentry.cpp index 99dc0b150..8239e76e3 100644 --- a/MelonLoader.Bootstrap/OSXEntry/osxentry.cpp +++ b/MelonLoader.Bootstrap/OSXEntry/osxentry.cpp @@ -1,9 +1,17 @@ #include +#include +#include +#include +#include extern "C" { void Init(); int SetRlimitHook(int resource, rlimit* rlp); + void MLRegisterDlsymHook(void* detour); + void* MLRealDlsym(void* handle, const char* symbol); + int MLMacOSJitCopy(void* dst, const void* src, size_t len); + void* DlsymHook(void* handle, const char* symbol); } #define DYLD_INTERPOSE(_replacement,_replacee) \ @@ -18,3 +26,63 @@ int SetRlimitHook(int resource, rlimit* rlp) } DYLD_INTERPOSE(SetRlimitHook, setrlimit) + +// Detour registered by the managed bootstrap (ModuleSymbolRedirect.Attach). +// Receives the symbol AND its already-resolved real address, and returns either +// MelonLoader's hook (for the runtime entry points) or the real address. +typedef void* (*DlsymDetourFn)(void* handle, const char* symbol, void* real); +static DlsymDetourFn g_dlsymDetour = nullptr; + +void MLRegisterDlsymHook(void* detour) +{ + g_dlsymDetour = (DlsymDetourFn)detour; +} + +// The real dlsym, exposed to managed code. dyld does not interpose the image +// that declares the interpose, so this call resolves the genuine export. The +// managed side MUST use this (not its own dlsym P/Invoke, whose GOT slot IS +// interposed) so MelonLoader resolves the real mono/il2cpp exports rather than +// its own detours -- otherwise the detours call themselves and recurse. +void* MLRealDlsym(void* handle, const char* symbol) +{ + return dlsym(handle, symbol); +} + +int MLMacOSJitCopy(void* dst, const void* src, size_t len) +{ +#if defined(__APPLE__) && defined(__arm64__) + if (!pthread_jit_write_protect_supported_np()) + return 0; + if (len == 0) + return 1; + if (dst == nullptr || src == nullptr) + return 0; + + pthread_jit_write_protect_np(0); + memcpy(dst, src, len); + sys_icache_invalidate(dst, len); + pthread_jit_write_protect_np(1); + return 1; +#else + return 0; +#endif +} + +// The game's engine (UnityPlayer.dylib) resolves the mono/il2cpp entry points +// via dlsym, so we interpose dlsym to return MelonLoader's detours for them. +// plthook cannot patch UnityPlayer.dylib's GOT on modern macOS +// (LC_DYLD_CHAINED_FIXUPS); DYLD interpose is the mechanism that reliably works +// here (it is what triggers Init via setrlimit). Only runtime-entry symbols +// (mono_/il2cpp_) are handed to managed code -- routing every dlsym in the +// process (e.g. AppKit's during startup) through a managed callback is needless +// and unsafe. +void* DlsymHook(void* handle, const char* symbol) +{ + void* real = dlsym(handle, symbol); + if (g_dlsymDetour != nullptr && symbol != nullptr && + (strncmp(symbol, "mono_", 5) == 0 || strncmp(symbol, "il2cpp_", 7) == 0)) + return g_dlsymDetour(handle, symbol, real); + return real; +} + +DYLD_INTERPOSE(DlsymHook, dlsym) diff --git a/MelonLoader.Bootstrap/osxexports.def b/MelonLoader.Bootstrap/osxexports.def index 8eaf59df6..39860ab74 100644 --- a/MelonLoader.Bootstrap/osxexports.def +++ b/MelonLoader.Bootstrap/osxexports.def @@ -10,3 +10,6 @@ _MonoGetDomainPtr _MonoGetRuntimeHandle _IsConsoleOpen _GetLoaderConfig +_MLRegisterDlsymHook +_MLRealDlsym +_MLMacOSJitCopy diff --git a/MelonLoader/Core.cs b/MelonLoader/Core.cs index 536776136..738f188e3 100644 --- a/MelonLoader/Core.cs +++ b/MelonLoader/Core.cs @@ -59,7 +59,16 @@ internal static int Initialize() // Since MonoMod's PlatformHelper (used by DetourHelper.Native) runs Process.Start to determine ARM/x86 // platform, this causes the unpatched TermInfoReader to kick in before it can be patched and fixed when // installing the XTermFix below. To work around this, we can force the platform directly +#if OSX && ARM64 + var macOSARM64Platform = Type.GetType( + "MonoMod.RuntimeDetour.Platforms.DetourNativeMacOSARM64Platform, MonoMod.RuntimeDetour", + false); + DetourHelper.Native = macOSARM64Platform == null + ? new DetourNativeMonoPosixPlatform(new DetourNativeARMPlatform()) + : (IDetourNativePlatform)Activator.CreateInstance(macOSARM64Platform, new DetourNativeARMPlatform()); +#else DetourHelper.Native = new DetourNativeMonoPosixPlatform(new DetourNativeX86Platform()); +#endif #endif HarmonyInstance = new HarmonyLib.Harmony(Properties.BuildInfo.Name); diff --git a/MelonLoader/MelonUtils.cs b/MelonLoader/MelonUtils.cs index e036d81cf..50ce208e9 100644 --- a/MelonLoader/MelonUtils.cs +++ b/MelonLoader/MelonUtils.cs @@ -85,6 +85,17 @@ public static bool IsOldMono() { #if OSX string frameworksPath = Path.Combine(MelonEnvironment.GameExecutablePath, "Contents/Frameworks"); + if (!Directory.Exists(frameworksPath)) + { + string[] apps = Directory.Exists(MelonEnvironment.MelonBaseDirectory) + ? Directory.GetDirectories(MelonEnvironment.MelonBaseDirectory, "*.app", SearchOption.TopDirectoryOnly) + : new string[0]; + if (apps.Length == 1) + frameworksPath = Path.Combine(apps[0], "Contents/Frameworks"); + } + if (!Directory.Exists(frameworksPath)) + return false; + var libs = Directory.GetFiles(frameworksPath, "*.dylib", SearchOption.AllDirectories); return libs.Select(Path.GetFileName).Contains("libmono.0.dylib"); #else diff --git a/MelonLoader/Utils/MelonEnvironment.cs b/MelonLoader/Utils/MelonEnvironment.cs index d5e0f50be..4021018c6 100644 --- a/MelonLoader/Utils/MelonEnvironment.cs +++ b/MelonLoader/Utils/MelonEnvironment.cs @@ -19,7 +19,7 @@ public static class MelonEnvironment public static string GameExecutablePath { get; } = #if OSX - MelonUtils.GetPathAncestor(Process.GetCurrentProcess()!.MainModule!.FileName, 3); + GetOSXGameExecutablePath(); #else Process.GetCurrentProcess().MainModule.FileName; #endif @@ -52,6 +52,38 @@ public static class MelonEnvironment public static string MelonManagedDirectory { get; } = Path.Combine(DependenciesDirectory, "Mono"); public static string Il2CppAssembliesDirectory { get; } = Path.Combine(MelonLoaderDirectory, "Il2CppAssemblies"); +#if OSX + private static string GetOSXGameExecutablePath() + { + string path = Process.GetCurrentProcess()!.MainModule!.FileName; + string current = Directory.Exists(path) ? path : Path.GetDirectoryName(path); + + while (!string.IsNullOrEmpty(current)) + { + if (current.EndsWith(".app")) + return current; + current = Path.GetDirectoryName(current); + } + + try + { + string baseDirectory = MelonBaseDirectory; + if (!string.IsNullOrEmpty(baseDirectory) && Directory.Exists(baseDirectory)) + { + string[] apps = Directory.GetDirectories(baseDirectory, "*.app", SearchOption.TopDirectoryOnly); + if (apps.Length == 1) + return apps[0]; + } + } + catch + { + // Fall through to the legacy ancestor fallback below. + } + + return MelonUtils.GetPathAncestor(path, 3); + } +#endif + internal static void PrintEnvironment() { //These must not be changed, lum needs them @@ -63,4 +95,4 @@ internal static void PrintEnvironment() MelonLogger.MsgDirect($"Runtime Type: {OurRuntimeName}"); } } -} \ No newline at end of file +} From a706cf12dffbd628c94820aca15d125ab2da38ce Mon Sep 17 00:00:00 2001 From: koren <131921956+kkorenn@users.noreply.github.com> Date: Sun, 7 Jun 2026 12:21:52 +0900 Subject: [PATCH 2/4] Build macOS arm64 release artifacts --- .github/workflows/compile_macos.yml | 32 ++++++++++------- .github/workflows/upload_github.yml | 11 ++++++ MelonLoader.sln | 56 +++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+), 12 deletions(-) diff --git a/.github/workflows/compile_macos.yml b/.github/workflows/compile_macos.yml index e4d7e848e..67509d7e1 100644 --- a/.github/workflows/compile_macos.yml +++ b/.github/workflows/compile_macos.yml @@ -10,8 +10,16 @@ on: jobs: compile_macos: - name: Build MacOS Binaries + name: Build MacOS ${{ matrix.arch }} Binaries runs-on: macos-latest + strategy: + fail-fast: false + matrix: + include: + - arch: x64 + rid: osx-x64 + - arch: arm64 + rid: osx-arm64 steps: - uses: actions/checkout@v6 - name: Setup dotnet @@ -20,24 +28,24 @@ jobs: dotnet-version: 9.0.x - name: Restore Dependencies - run: dotnet restore + run: dotnet restore -p:Platform="${{ matrix.arch }}" -p:ForceRID="${{ matrix.rid }}" - - name: Build Debug for osx-x64 + - name: Build Debug for ${{ matrix.rid }} if: inputs.SKIP_DEBUG != true - run: dotnet build --no-restore -c Debug -p:Platform="x64" -p:Version="${{ inputs.VERSION }}" + run: dotnet build --no-restore -c Debug -p:Platform="${{ matrix.arch }}" -p:ForceRID="${{ matrix.rid }}" -p:Version="${{ inputs.VERSION }}" - - name: Upload Debug for osx-x64 + - name: Upload Debug for ${{ matrix.rid }} if: inputs.SKIP_DEBUG != true uses: actions/upload-artifact@v7 with: - name: MelonLoader.macOS.x64.CI.Debug - path: Output/Debug/osx-x64 + name: MelonLoader.macOS.${{ matrix.arch }}.CI.Debug + path: Output/Debug/${{ matrix.rid }} - - name: Build Release for osx-x64 - run: dotnet build --no-restore -c Release -p:Platform="x64" -p:Version="${{ inputs.VERSION }}" + - name: Build Release for ${{ matrix.rid }} + run: dotnet build --no-restore -c Release -p:Platform="${{ matrix.arch }}" -p:ForceRID="${{ matrix.rid }}" -p:Version="${{ inputs.VERSION }}" - - name: Upload Release for osx-x64 + - name: Upload Release for ${{ matrix.rid }} uses: actions/upload-artifact@v7 with: - name: MelonLoader.macOS.x64.CI.Release - path: Output/Release/osx-x64 \ No newline at end of file + name: MelonLoader.macOS.${{ matrix.arch }}.CI.Release + path: Output/Release/${{ matrix.rid }} diff --git a/.github/workflows/upload_github.yml b/.github/workflows/upload_github.yml index 7a24d72fc..50292688b 100644 --- a/.github/workflows/upload_github.yml +++ b/.github/workflows/upload_github.yml @@ -54,6 +54,16 @@ jobs: run: | cd compiled_release/macos_x64 zip -r ../MelonLoader.macOS.x64.zip ./ + + - name: Download MacOS arm64 Artifact + uses: actions/download-artifact@v8 + with: + name: MelonLoader.macOS.arm64.CI.Release + path: ./compiled_release/macos_arm64 + - name: Package MacOS arm64 into Zip Archive + run: | + cd compiled_release/macos_arm64 + zip -r ../MelonLoader.macOS.arm64.zip ./ - name: Download MelonLoader Installer run: | @@ -72,6 +82,7 @@ jobs: compiled_release/MelonLoader.x64.zip compiled_release/MelonLoader.Linux.x64.zip compiled_release/MelonLoader.macOS.x64.zip + compiled_release/MelonLoader.macOS.arm64.zip compiled_release/MelonLoader.Installer.exe compiled_release/MelonLoader.Installer.Linux compiled_release/MelonLoader.Installer.MacOS.dmg diff --git a/MelonLoader.sln b/MelonLoader.sln index 31574dbbf..c457cd876 100644 --- a/MelonLoader.sln +++ b/MelonLoader.sln @@ -70,118 +70,174 @@ Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x86 = Debug|x86 Debug|x64 = Debug|x64 + Debug|arm64 = Debug|arm64 Release|x86 = Release|x86 Release|x64 = Release|x64 + Release|arm64 = Release|arm64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {10CC7C0B-E7BC-43CC-BCE1-B0181337CCF5}.Debug|x86.ActiveCfg = Debug|x86 {10CC7C0B-E7BC-43CC-BCE1-B0181337CCF5}.Debug|x86.Build.0 = Debug|x86 {10CC7C0B-E7BC-43CC-BCE1-B0181337CCF5}.Debug|x64.ActiveCfg = Debug|x64 + {10CC7C0B-E7BC-43CC-BCE1-B0181337CCF5}.Debug|arm64.ActiveCfg = Debug|arm64 {10CC7C0B-E7BC-43CC-BCE1-B0181337CCF5}.Debug|x64.Build.0 = Debug|x64 + {10CC7C0B-E7BC-43CC-BCE1-B0181337CCF5}.Debug|arm64.Build.0 = Debug|arm64 {10CC7C0B-E7BC-43CC-BCE1-B0181337CCF5}.Release|x86.ActiveCfg = Release|x86 {10CC7C0B-E7BC-43CC-BCE1-B0181337CCF5}.Release|x86.Build.0 = Release|x86 {10CC7C0B-E7BC-43CC-BCE1-B0181337CCF5}.Release|x64.ActiveCfg = Release|x64 + {10CC7C0B-E7BC-43CC-BCE1-B0181337CCF5}.Release|arm64.ActiveCfg = Release|arm64 {10CC7C0B-E7BC-43CC-BCE1-B0181337CCF5}.Release|x64.Build.0 = Release|x64 + {10CC7C0B-E7BC-43CC-BCE1-B0181337CCF5}.Release|arm64.Build.0 = Release|arm64 {3AF58371-2E0A-4256-B3A3-C100DB07E599}.Debug|x64.ActiveCfg = Debug|x64 + {3AF58371-2E0A-4256-B3A3-C100DB07E599}.Debug|arm64.ActiveCfg = Debug|arm64 {3AF58371-2E0A-4256-B3A3-C100DB07E599}.Debug|x64.Build.0 = Debug|x64 + {3AF58371-2E0A-4256-B3A3-C100DB07E599}.Debug|arm64.Build.0 = Debug|arm64 {3AF58371-2E0A-4256-B3A3-C100DB07E599}.Debug|x86.ActiveCfg = Debug|x86 {3AF58371-2E0A-4256-B3A3-C100DB07E599}.Debug|x86.Build.0 = Debug|x86 {3AF58371-2E0A-4256-B3A3-C100DB07E599}.Release|x64.ActiveCfg = Release|x64 + {3AF58371-2E0A-4256-B3A3-C100DB07E599}.Release|arm64.ActiveCfg = Release|arm64 {3AF58371-2E0A-4256-B3A3-C100DB07E599}.Release|x64.Build.0 = Release|x64 + {3AF58371-2E0A-4256-B3A3-C100DB07E599}.Release|arm64.Build.0 = Release|arm64 {3AF58371-2E0A-4256-B3A3-C100DB07E599}.Release|x86.ActiveCfg = Release|x86 {3AF58371-2E0A-4256-B3A3-C100DB07E599}.Release|x86.Build.0 = Release|x86 {4BEAF9B5-F780-414B-8F2E-59DA4A91BE50}.Debug|x64.ActiveCfg = Debug|x64 + {4BEAF9B5-F780-414B-8F2E-59DA4A91BE50}.Debug|arm64.ActiveCfg = Debug|arm64 {4BEAF9B5-F780-414B-8F2E-59DA4A91BE50}.Debug|x64.Build.0 = Debug|x64 + {4BEAF9B5-F780-414B-8F2E-59DA4A91BE50}.Debug|arm64.Build.0 = Debug|arm64 {4BEAF9B5-F780-414B-8F2E-59DA4A91BE50}.Debug|x86.ActiveCfg = Debug|x86 {4BEAF9B5-F780-414B-8F2E-59DA4A91BE50}.Debug|x86.Build.0 = Debug|x86 {4BEAF9B5-F780-414B-8F2E-59DA4A91BE50}.Release|x64.ActiveCfg = Release|x64 + {4BEAF9B5-F780-414B-8F2E-59DA4A91BE50}.Release|arm64.ActiveCfg = Release|arm64 {4BEAF9B5-F780-414B-8F2E-59DA4A91BE50}.Release|x64.Build.0 = Release|x64 + {4BEAF9B5-F780-414B-8F2E-59DA4A91BE50}.Release|arm64.Build.0 = Release|arm64 {4BEAF9B5-F780-414B-8F2E-59DA4A91BE50}.Release|x86.ActiveCfg = Release|x86 {4BEAF9B5-F780-414B-8F2E-59DA4A91BE50}.Release|x86.Build.0 = Release|x86 {A6452A3F-4BD6-497A-97DA-24F7DF97B234}.Debug|x64.ActiveCfg = Debug|x64 + {A6452A3F-4BD6-497A-97DA-24F7DF97B234}.Debug|arm64.ActiveCfg = Debug|arm64 {A6452A3F-4BD6-497A-97DA-24F7DF97B234}.Debug|x64.Build.0 = Debug|x64 + {A6452A3F-4BD6-497A-97DA-24F7DF97B234}.Debug|arm64.Build.0 = Debug|arm64 {A6452A3F-4BD6-497A-97DA-24F7DF97B234}.Debug|x86.ActiveCfg = Debug|x86 {A6452A3F-4BD6-497A-97DA-24F7DF97B234}.Debug|x86.Build.0 = Debug|x86 {A6452A3F-4BD6-497A-97DA-24F7DF97B234}.Release|x64.ActiveCfg = Release|x64 + {A6452A3F-4BD6-497A-97DA-24F7DF97B234}.Release|arm64.ActiveCfg = Release|arm64 {A6452A3F-4BD6-497A-97DA-24F7DF97B234}.Release|x64.Build.0 = Release|x64 + {A6452A3F-4BD6-497A-97DA-24F7DF97B234}.Release|arm64.Build.0 = Release|arm64 {A6452A3F-4BD6-497A-97DA-24F7DF97B234}.Release|x86.ActiveCfg = Release|x86 {A6452A3F-4BD6-497A-97DA-24F7DF97B234}.Release|x86.Build.0 = Release|x86 {4FDDBEC0-9C20-456E-8906-77E9CD39C3CA}.Debug|x64.ActiveCfg = Debug|x64 + {4FDDBEC0-9C20-456E-8906-77E9CD39C3CA}.Debug|arm64.ActiveCfg = Debug|arm64 {4FDDBEC0-9C20-456E-8906-77E9CD39C3CA}.Debug|x64.Build.0 = Debug|x64 + {4FDDBEC0-9C20-456E-8906-77E9CD39C3CA}.Debug|arm64.Build.0 = Debug|arm64 {4FDDBEC0-9C20-456E-8906-77E9CD39C3CA}.Debug|x86.ActiveCfg = Debug|x86 {4FDDBEC0-9C20-456E-8906-77E9CD39C3CA}.Debug|x86.Build.0 = Debug|x86 {4FDDBEC0-9C20-456E-8906-77E9CD39C3CA}.Release|x64.ActiveCfg = Release|x64 + {4FDDBEC0-9C20-456E-8906-77E9CD39C3CA}.Release|arm64.ActiveCfg = Release|arm64 {4FDDBEC0-9C20-456E-8906-77E9CD39C3CA}.Release|x64.Build.0 = Release|x64 + {4FDDBEC0-9C20-456E-8906-77E9CD39C3CA}.Release|arm64.Build.0 = Release|arm64 {4FDDBEC0-9C20-456E-8906-77E9CD39C3CA}.Release|x86.ActiveCfg = Release|x86 {4FDDBEC0-9C20-456E-8906-77E9CD39C3CA}.Release|x86.Build.0 = Release|x86 {F9700790-414B-431B-9F9C-1D9210FAD682}.Debug|x64.ActiveCfg = Debug|x64 + {F9700790-414B-431B-9F9C-1D9210FAD682}.Debug|arm64.ActiveCfg = Debug|arm64 {F9700790-414B-431B-9F9C-1D9210FAD682}.Debug|x64.Build.0 = Debug|x64 + {F9700790-414B-431B-9F9C-1D9210FAD682}.Debug|arm64.Build.0 = Debug|arm64 {F9700790-414B-431B-9F9C-1D9210FAD682}.Debug|x86.ActiveCfg = Debug|x86 {F9700790-414B-431B-9F9C-1D9210FAD682}.Debug|x86.Build.0 = Debug|x86 {F9700790-414B-431B-9F9C-1D9210FAD682}.Release|x64.ActiveCfg = Release|x64 + {F9700790-414B-431B-9F9C-1D9210FAD682}.Release|arm64.ActiveCfg = Release|arm64 {F9700790-414B-431B-9F9C-1D9210FAD682}.Release|x64.Build.0 = Release|x64 + {F9700790-414B-431B-9F9C-1D9210FAD682}.Release|arm64.Build.0 = Release|arm64 {F9700790-414B-431B-9F9C-1D9210FAD682}.Release|x86.ActiveCfg = Release|x86 {F9700790-414B-431B-9F9C-1D9210FAD682}.Release|x86.Build.0 = Release|x86 {60D688E3-ECE0-43A6-9922-9D1D3B8C07CC}.Debug|x64.ActiveCfg = Debug|x64 + {60D688E3-ECE0-43A6-9922-9D1D3B8C07CC}.Debug|arm64.ActiveCfg = Debug|arm64 {60D688E3-ECE0-43A6-9922-9D1D3B8C07CC}.Debug|x64.Build.0 = Debug|x64 + {60D688E3-ECE0-43A6-9922-9D1D3B8C07CC}.Debug|arm64.Build.0 = Debug|arm64 {60D688E3-ECE0-43A6-9922-9D1D3B8C07CC}.Debug|x86.ActiveCfg = Debug|x86 {60D688E3-ECE0-43A6-9922-9D1D3B8C07CC}.Debug|x86.Build.0 = Debug|x86 {60D688E3-ECE0-43A6-9922-9D1D3B8C07CC}.Release|x64.ActiveCfg = Release|x64 + {60D688E3-ECE0-43A6-9922-9D1D3B8C07CC}.Release|arm64.ActiveCfg = Release|arm64 {60D688E3-ECE0-43A6-9922-9D1D3B8C07CC}.Release|x64.Build.0 = Release|x64 + {60D688E3-ECE0-43A6-9922-9D1D3B8C07CC}.Release|arm64.Build.0 = Release|arm64 {60D688E3-ECE0-43A6-9922-9D1D3B8C07CC}.Release|x86.ActiveCfg = Release|x86 {60D688E3-ECE0-43A6-9922-9D1D3B8C07CC}.Release|x86.Build.0 = Release|x86 {762D7545-6F6B-441A-B040-49CC31A1713B}.Debug|x64.ActiveCfg = Debug|x64 + {762D7545-6F6B-441A-B040-49CC31A1713B}.Debug|arm64.ActiveCfg = Debug|arm64 {762D7545-6F6B-441A-B040-49CC31A1713B}.Debug|x86.ActiveCfg = Debug|x86 {762D7545-6F6B-441A-B040-49CC31A1713B}.Release|x64.ActiveCfg = Release|x64 + {762D7545-6F6B-441A-B040-49CC31A1713B}.Release|arm64.ActiveCfg = Release|arm64 {762D7545-6F6B-441A-B040-49CC31A1713B}.Release|x86.ActiveCfg = Release|x86 {542EC51D-E480-4802-B5AA-96EEC05AF19C}.Debug|x64.ActiveCfg = Debug|x64 + {542EC51D-E480-4802-B5AA-96EEC05AF19C}.Debug|arm64.ActiveCfg = Debug|arm64 {542EC51D-E480-4802-B5AA-96EEC05AF19C}.Debug|x64.Build.0 = Debug|x64 + {542EC51D-E480-4802-B5AA-96EEC05AF19C}.Debug|arm64.Build.0 = Debug|arm64 {542EC51D-E480-4802-B5AA-96EEC05AF19C}.Debug|x86.ActiveCfg = Debug|x86 {542EC51D-E480-4802-B5AA-96EEC05AF19C}.Debug|x86.Build.0 = Debug|x86 {542EC51D-E480-4802-B5AA-96EEC05AF19C}.Release|x64.ActiveCfg = Release|x64 + {542EC51D-E480-4802-B5AA-96EEC05AF19C}.Release|arm64.ActiveCfg = Release|arm64 {542EC51D-E480-4802-B5AA-96EEC05AF19C}.Release|x64.Build.0 = Release|x64 + {542EC51D-E480-4802-B5AA-96EEC05AF19C}.Release|arm64.Build.0 = Release|arm64 {542EC51D-E480-4802-B5AA-96EEC05AF19C}.Release|x86.ActiveCfg = Release|x86 {542EC51D-E480-4802-B5AA-96EEC05AF19C}.Release|x86.Build.0 = Release|x86 {15CFF766-420B-47EF-9E4A-E73D35A4AB44}.Debug|x64.ActiveCfg = Debug|x64 + {15CFF766-420B-47EF-9E4A-E73D35A4AB44}.Debug|arm64.ActiveCfg = Debug|arm64 {15CFF766-420B-47EF-9E4A-E73D35A4AB44}.Debug|x64.Build.0 = Debug|x64 + {15CFF766-420B-47EF-9E4A-E73D35A4AB44}.Debug|arm64.Build.0 = Debug|arm64 {15CFF766-420B-47EF-9E4A-E73D35A4AB44}.Debug|x86.ActiveCfg = Debug|x86 {15CFF766-420B-47EF-9E4A-E73D35A4AB44}.Debug|x86.Build.0 = Debug|x86 {15CFF766-420B-47EF-9E4A-E73D35A4AB44}.Release|x64.ActiveCfg = Release|x64 + {15CFF766-420B-47EF-9E4A-E73D35A4AB44}.Release|arm64.ActiveCfg = Release|arm64 {15CFF766-420B-47EF-9E4A-E73D35A4AB44}.Release|x64.Build.0 = Release|x64 + {15CFF766-420B-47EF-9E4A-E73D35A4AB44}.Release|arm64.Build.0 = Release|arm64 {15CFF766-420B-47EF-9E4A-E73D35A4AB44}.Release|x86.ActiveCfg = Release|x86 {15CFF766-420B-47EF-9E4A-E73D35A4AB44}.Release|x86.Build.0 = Release|x86 {1DB3679C-DCCA-492D-A725-75604A379C7A}.Debug|x64.ActiveCfg = Debug|x64 + {1DB3679C-DCCA-492D-A725-75604A379C7A}.Debug|arm64.ActiveCfg = Debug|arm64 {1DB3679C-DCCA-492D-A725-75604A379C7A}.Debug|x64.Build.0 = Debug|x64 + {1DB3679C-DCCA-492D-A725-75604A379C7A}.Debug|arm64.Build.0 = Debug|arm64 {1DB3679C-DCCA-492D-A725-75604A379C7A}.Debug|x86.ActiveCfg = Debug|x86 {1DB3679C-DCCA-492D-A725-75604A379C7A}.Debug|x86.Build.0 = Debug|x86 {1DB3679C-DCCA-492D-A725-75604A379C7A}.Release|x64.ActiveCfg = Release|x64 + {1DB3679C-DCCA-492D-A725-75604A379C7A}.Release|arm64.ActiveCfg = Release|arm64 {1DB3679C-DCCA-492D-A725-75604A379C7A}.Release|x64.Build.0 = Release|x64 + {1DB3679C-DCCA-492D-A725-75604A379C7A}.Release|arm64.Build.0 = Release|arm64 {1DB3679C-DCCA-492D-A725-75604A379C7A}.Release|x86.ActiveCfg = Release|x86 {1DB3679C-DCCA-492D-A725-75604A379C7A}.Release|x86.Build.0 = Release|x86 {5C270941-AAA4-4FF4-870E-BDE170C5407C}.Debug|x64.ActiveCfg = Debug|x64 + {5C270941-AAA4-4FF4-870E-BDE170C5407C}.Debug|arm64.ActiveCfg = Debug|arm64 {5C270941-AAA4-4FF4-870E-BDE170C5407C}.Debug|x64.Build.0 = Debug|x64 + {5C270941-AAA4-4FF4-870E-BDE170C5407C}.Debug|arm64.Build.0 = Debug|arm64 {5C270941-AAA4-4FF4-870E-BDE170C5407C}.Debug|x86.ActiveCfg = Debug|x86 {5C270941-AAA4-4FF4-870E-BDE170C5407C}.Debug|x86.Build.0 = Debug|x86 {5C270941-AAA4-4FF4-870E-BDE170C5407C}.Release|x64.ActiveCfg = Release|x64 + {5C270941-AAA4-4FF4-870E-BDE170C5407C}.Release|arm64.ActiveCfg = Release|arm64 {5C270941-AAA4-4FF4-870E-BDE170C5407C}.Release|x64.Build.0 = Release|x64 + {5C270941-AAA4-4FF4-870E-BDE170C5407C}.Release|arm64.Build.0 = Release|arm64 {5C270941-AAA4-4FF4-870E-BDE170C5407C}.Release|x86.ActiveCfg = Release|x86 {5C270941-AAA4-4FF4-870E-BDE170C5407C}.Release|x86.Build.0 = Release|x86 {8D271ABF-209A-4AF9-8C62-EF92806B7DC1}.Debug|x64.ActiveCfg = Debug|x64 + {8D271ABF-209A-4AF9-8C62-EF92806B7DC1}.Debug|arm64.ActiveCfg = Debug|arm64 {8D271ABF-209A-4AF9-8C62-EF92806B7DC1}.Debug|x64.Build.0 = Debug|x64 + {8D271ABF-209A-4AF9-8C62-EF92806B7DC1}.Debug|arm64.Build.0 = Debug|arm64 {8D271ABF-209A-4AF9-8C62-EF92806B7DC1}.Debug|x86.ActiveCfg = Debug|x86 {8D271ABF-209A-4AF9-8C62-EF92806B7DC1}.Debug|x86.Build.0 = Debug|x86 {8D271ABF-209A-4AF9-8C62-EF92806B7DC1}.Release|x64.ActiveCfg = Release|x64 + {8D271ABF-209A-4AF9-8C62-EF92806B7DC1}.Release|arm64.ActiveCfg = Release|arm64 {8D271ABF-209A-4AF9-8C62-EF92806B7DC1}.Release|x64.Build.0 = Release|x64 + {8D271ABF-209A-4AF9-8C62-EF92806B7DC1}.Release|arm64.Build.0 = Release|arm64 {8D271ABF-209A-4AF9-8C62-EF92806B7DC1}.Release|x86.ActiveCfg = Release|x86 {8D271ABF-209A-4AF9-8C62-EF92806B7DC1}.Release|x86.Build.0 = Release|x86 {90832409-6D54-445D-9310-DCE81F0BB991}.Debug|x86.ActiveCfg = Debug|x86 {90832409-6D54-445D-9310-DCE81F0BB991}.Debug|x86.Build.0 = Debug|x86 {90832409-6D54-445D-9310-DCE81F0BB991}.Debug|x64.ActiveCfg = Debug|x64 + {90832409-6D54-445D-9310-DCE81F0BB991}.Debug|arm64.ActiveCfg = Debug|arm64 {90832409-6D54-445D-9310-DCE81F0BB991}.Release|x86.ActiveCfg = Release|x86 {90832409-6D54-445D-9310-DCE81F0BB991}.Release|x86.Build.0 = Release|x86 {90832409-6D54-445D-9310-DCE81F0BB991}.Release|x64.ActiveCfg = Release|x64 + {90832409-6D54-445D-9310-DCE81F0BB991}.Release|arm64.ActiveCfg = Release|arm64 {90832409-6D54-445D-9310-DCE81F0BB991}.Release|x64.Build.0 = Release|x64 + {90832409-6D54-445D-9310-DCE81F0BB991}.Release|arm64.Build.0 = Release|arm64 {90832409-6D54-445D-9310-DCE81F0BB991}.Debug|x64.Build.0 = Debug|x64 + {90832409-6D54-445D-9310-DCE81F0BB991}.Debug|arm64.Build.0 = Debug|arm64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 5c8c784e13bd814d1368ca25f885f228625d9b52 Mon Sep 17 00:00:00 2001 From: koren <131921956+kkorenn@users.noreply.github.com> Date: Sun, 7 Jun 2026 13:06:49 +0900 Subject: [PATCH 3/4] Fix GitHub release upload permissions --- .github/workflows/release.yml | 7 ++++++- .github/workflows/upload_all.yml | 4 +++- .github/workflows/upload_github.yml | 7 +++++-- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ec4a8625a..b87b0509f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,6 +7,9 @@ on: tags: - '*.*.*' +permissions: + contents: write + jobs: fix_tag_version: name: Fix Tag Version @@ -36,6 +39,8 @@ jobs: upload: name: Upload needs: [ build, fix_tag_version ] + permissions: + contents: write uses: ./.github/workflows/upload_all.yml with: TAG: ${{ github.ref_name }} @@ -43,4 +48,4 @@ jobs: VERSION: ${{ needs.fix_tag_version.outputs.clean_version }} PACKAGE_VERSION: ${{ needs.fix_tag_version.outputs.clean_version }} PACKAGE_NAME: MelonLoader - secrets: inherit \ No newline at end of file + secrets: inherit diff --git a/.github/workflows/upload_all.yml b/.github/workflows/upload_all.yml index ca731e4d6..9c29c9892 100644 --- a/.github/workflows/upload_all.yml +++ b/.github/workflows/upload_all.yml @@ -20,6 +20,8 @@ on: jobs: upload_github: name: GitHub + permissions: + contents: write uses: ./.github/workflows/upload_github.yml with: TAG: ${{ inputs.TAG }} @@ -37,4 +39,4 @@ jobs: with: VERSION: ${{ inputs.PACKAGE_VERSION }} PACKAGE_NAME: ${{ inputs.PACKAGE_NAME }} - secrets: inherit \ No newline at end of file + secrets: inherit diff --git a/.github/workflows/upload_github.yml b/.github/workflows/upload_github.yml index 50292688b..f5fcc4d4d 100644 --- a/.github/workflows/upload_github.yml +++ b/.github/workflows/upload_github.yml @@ -12,6 +12,8 @@ jobs: upload_github: name: Upload GitHub Release runs-on: ubuntu-latest + permissions: + contents: write steps: - uses: actions/checkout@v6 @@ -75,8 +77,9 @@ jobs: uses: softprops/action-gh-release@v3 with: body_path: RELEASE-NOTES.md - tag: ${{ inputs.TAG }} - release_name: ${{ inputs.RELEASE_NAME }} + tag_name: ${{ inputs.TAG }} + name: ${{ inputs.RELEASE_NAME }} + overwrite_files: true files: | compiled_release/MelonLoader.x86.zip compiled_release/MelonLoader.x64.zip From b95601c2449595dbb8a771636b9115c28b0c30f2 Mon Sep 17 00:00:00 2001 From: koren <131921956+kkorenn@users.noreply.github.com> Date: Sun, 7 Jun 2026 13:57:24 +0900 Subject: [PATCH 4/4] Use arm64-safe macOS detour writes --- MelonLoader/Core.cs | 8 +- .../MonoMod/MacOSArm64NativeDetourPlatform.cs | 208 ++++++++++++++++++ 2 files changed, 210 insertions(+), 6 deletions(-) create mode 100644 MelonLoader/Fixes/MonoMod/MacOSArm64NativeDetourPlatform.cs diff --git a/MelonLoader/Core.cs b/MelonLoader/Core.cs index 738f188e3..b567ded54 100644 --- a/MelonLoader/Core.cs +++ b/MelonLoader/Core.cs @@ -60,12 +60,8 @@ internal static int Initialize() // platform, this causes the unpatched TermInfoReader to kick in before it can be patched and fixed when // installing the XTermFix below. To work around this, we can force the platform directly #if OSX && ARM64 - var macOSARM64Platform = Type.GetType( - "MonoMod.RuntimeDetour.Platforms.DetourNativeMacOSARM64Platform, MonoMod.RuntimeDetour", - false); - DetourHelper.Native = macOSARM64Platform == null - ? new DetourNativeMonoPosixPlatform(new DetourNativeARMPlatform()) - : (IDetourNativePlatform)Activator.CreateInstance(macOSARM64Platform, new DetourNativeARMPlatform()); + DetourHelper.Native = new Fixes.MonoMod.MacOSArm64NativeDetourPlatform( + new DetourNativeARMPlatform()); #else DetourHelper.Native = new DetourNativeMonoPosixPlatform(new DetourNativeX86Platform()); #endif diff --git a/MelonLoader/Fixes/MonoMod/MacOSArm64NativeDetourPlatform.cs b/MelonLoader/Fixes/MonoMod/MacOSArm64NativeDetourPlatform.cs new file mode 100644 index 000000000..5f90f43cd --- /dev/null +++ b/MelonLoader/Fixes/MonoMod/MacOSArm64NativeDetourPlatform.cs @@ -0,0 +1,208 @@ +using System; +using System.Runtime.InteropServices; +using MonoMod.RuntimeDetour; + +namespace MelonLoader.Fixes.MonoMod; + +internal sealed class MacOSArm64NativeDetourPlatform : IDetourNativePlatform +{ + private readonly IDetourNativePlatform _inner; + private static bool? _nativeJitCopySupported; + + public MacOSArm64NativeDetourPlatform(IDetourNativePlatform inner) + { + _inner = inner; + } + + public NativeDetourData Create(IntPtr from, IntPtr to, byte? type = null) + { + return _inner.Create(from, to, type); + } + + public void Free(NativeDetourData detour) + { + _inner.Free(detour); + } + + public void Apply(NativeDetourData detour) + { + if (TryApplyWithNativeCopy(detour)) + return; + + _inner.Apply(detour); + } + + public void Copy(IntPtr src, IntPtr dst, byte type) + { + if (TryCopyWithNativeCopy(src, dst, type)) + return; + + _inner.Copy(src, dst, type); + } + + public void MakeWritable(IntPtr src, uint size) + { + if (NativeJitCopySupported) + return; + + _inner.MakeWritable(src, size); + } + + public void MakeExecutable(IntPtr src, uint size) + { + if (NativeJitCopySupported) + { + TryFlushICache(src, size); + return; + } + + _inner.MakeExecutable(src, size); + } + + public void MakeReadWriteExecutable(IntPtr src, uint size) + { + if (NativeJitCopySupported) + { + TryFlushICache(src, size); + return; + } + + _inner.MakeReadWriteExecutable(src, size); + } + + public void FlushICache(IntPtr src, uint size) + { + if (TryFlushICache(src, size)) + return; + + _inner.FlushICache(src, size); + } + + public IntPtr MemAlloc(uint size) + { + return _inner.MemAlloc(size); + } + + public void MemFree(IntPtr ptr) + { + _inner.MemFree(ptr); + } + + private bool TryApplyWithNativeCopy(NativeDetourData detour) + { + if (detour.Size == 0 || detour.Extra != IntPtr.Zero || !NativeJitCopySupported) + return false; + + IntPtr copy = Marshal.AllocHGlobal((int)detour.Size); + try + { + NativeDetourData copyDetour = detour; + copyDetour.Method = copy; + _inner.Apply(copyDetour); + + return TryNativeJitCopy(detour.Method, copy, detour.Size); + } + finally + { + Marshal.FreeHGlobal(copy); + } + } + + private bool TryCopyWithNativeCopy(IntPtr src, IntPtr dst, byte type) + { + uint size = GetDetourSize(type); + if (size == 0) + return false; + + return TryNativeJitCopy(dst, src, size); + } + + private static bool TryFlushICache(IntPtr src, uint size) + { + try + { + sys_icache_invalidate(src, (UIntPtr)size); + return true; + } + catch + { + return false; + } + } + + private static uint GetDetourSize(byte type) + { + switch (type) + { + case 0: + return 5; + case 1: + return 6; + case 2: + return 14; + case 3: + return 6; + case 4: + return 16; + default: + return 0; + } + } + + private static bool TryNativeJitCopy(IntPtr dst, IntPtr src, uint size) + { + if (!NativeJitCopySupported) + return false; + + try + { + return CallNativeJitCopy(dst, src, (UIntPtr)size) != 0; + } + catch + { + _nativeJitCopySupported = false; + return false; + } + } + + private static bool NativeJitCopySupported + { + get + { + if (_nativeJitCopySupported.HasValue) + return _nativeJitCopySupported.Value; + + try + { + _nativeJitCopySupported = CallNativeJitCopy(IntPtr.Zero, IntPtr.Zero, UIntPtr.Zero) != 0; + } + catch + { + _nativeJitCopySupported = false; + } + + return _nativeJitCopySupported.Value; + } + } + + private static int CallNativeJitCopy(IntPtr dst, IntPtr src, UIntPtr size) + { + try + { + return MLMacOSJitCopyBootstrap(dst, src, size); + } + catch + { + return MLMacOSJitCopyInternal(dst, src, size); + } + } + + [DllImport("MelonLoader.Bootstrap.dylib", EntryPoint = "MLMacOSJitCopy", CallingConvention = CallingConvention.Cdecl)] + private static extern int MLMacOSJitCopyBootstrap(IntPtr dst, IntPtr src, UIntPtr size); + + [DllImport("__Internal", EntryPoint = "MLMacOSJitCopy", CallingConvention = CallingConvention.Cdecl)] + private static extern int MLMacOSJitCopyInternal(IntPtr dst, IntPtr src, UIntPtr size); + + [DllImport("libSystem.B.dylib", CallingConvention = CallingConvention.Cdecl)] + private static extern void sys_icache_invalidate(IntPtr start, UIntPtr len); +}