mirror of
https://github.com/morluto/rea.git
synced 2026-11-12 09:52:27 +00:00
* feat(desktop): suggest managed members for Unity predefined script assemblies Unity Mono triage listed only inspect_managed_artifact probes, mostly for engine and framework assemblies. Assembly-CSharp.dll and Assembly-CSharp-firstpass.dll directly in a managed directory with a UnityEngine reference witness now get an inspect_managed_members probe, ordered before every managed artifact probe. * docs(desktop): describe Unity Mono managed member probes * refactor(desktop): read Unity managed directory from the witness fact * refactor(desktop): reuse the managed directory index for predefined assemblies * feat(desktop): point Unity member probes at summary delivery first * test(desktop): no member probes without a Unity reference witness * docs(desktop): trim Unity member probe note * feat(desktop): suggest managed members in Unity IL2CPP backup folders Unity IL2CPP builds write <Product>_BackUpThisFolder_ButDontShipItWithYourGame next to the player. When shipped, its Managed/ holds pre-IL2CPP assemblies. A path convention now records the folder on Assembly-CSharp*.dll directly in its Managed/, including when the admitted root is the folder itself, and triage offers inspect_managed_members for those files. Correspondence to the shipped native code stays unknown. * docs(desktop): note IL2CPP backup folder member probes * docs(desktop): list IL2CPP metadata versions 24 and 27 for the metadata probe * style(desktop): merge stacked doc comments on desktopPathConventions * test(desktop): cover member probes in a macOS bundle managed directory * docs(desktop): note that member probes need the game folder as root * test(desktop): no member probe when the root is the Unity data folder * test(desktop): check the data-folder root still gets managed probes * test(desktop): check the IL2CPP backup convention against the probe bound --------- Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
127 lines
4.5 KiB
TypeScript
127 lines
4.5 KiB
TypeScript
/** Path-segment conventions. These are inferences, not observations of runtime behavior. */
|
|
|
|
export type NativeRuntimeConvention = "react-native" | "flutter" | "unity";
|
|
|
|
const foldSegment = (segment: string): string =>
|
|
segment.toLocaleLowerCase("en-US");
|
|
|
|
const REACT_NATIVE_SEGMENTS = new Set([
|
|
"react.framework",
|
|
"reactnative.framework",
|
|
"libreactnative.so",
|
|
"libreactnativejni.so",
|
|
"libhermes_executor.so",
|
|
"libhermes.so",
|
|
"hermes.framework",
|
|
]);
|
|
const FLUTTER_SEGMENTS = new Set(["libflutter.so", "flutter.framework"]);
|
|
const UNITY_SEGMENTS = new Set(["libunity.so", "unityframework.framework"]);
|
|
const DESKTOP_IL2CPP_LIBRARIES = new Set([
|
|
"gameassembly.dll",
|
|
"gameassembly.so",
|
|
"gameassembly.dylib",
|
|
"libil2cpp.so",
|
|
]);
|
|
/** Unity's predefined script assembly names; asmdef assembly names are project-chosen. */
|
|
const UNITY_PREDEFINED_ASSEMBLIES = new Set([
|
|
"assembly-csharp.dll",
|
|
"assembly-csharp-firstpass.dll",
|
|
]);
|
|
|
|
/**
|
|
* A runtime family named by the library filename or a containing framework.
|
|
* A library-named ancestor directory does not establish a convention.
|
|
*/
|
|
export const nativeRuntimeConvention = (
|
|
path: string,
|
|
): NativeRuntimeConvention | null => {
|
|
const segments = path.split("/");
|
|
for (const [index, segment] of segments.entries()) {
|
|
const folded = foldSegment(segment);
|
|
if (index !== segments.length - 1 && !folded.endsWith(".framework"))
|
|
continue;
|
|
if (REACT_NATIVE_SEGMENTS.has(folded)) return "react-native";
|
|
if (FLUTTER_SEGMENTS.has(folded)) return "flutter";
|
|
if (UNITY_SEGMENTS.has(folded)) return "unity";
|
|
}
|
|
return null;
|
|
};
|
|
|
|
/** Swift runtime libraries are `libswift*.dylib` segments, not every dynamic library. */
|
|
export const isSwiftRuntimeLibrary = (path: string): boolean =>
|
|
path
|
|
.split("/")
|
|
.some((segment) => /^libswift.+\.dylib$/u.test(foldSegment(segment)));
|
|
|
|
/** Unity names the pre-IL2CPP managed backup `<Product>_BackUpThisFolder_ButDontShipItWithYourGame`. */
|
|
const UNITY_IL2CPP_BACKUP_SUFFIX =
|
|
"_backupthisfolder_butdontshipitwithyourgame";
|
|
|
|
/**
|
|
* Parse desktop layout conventions once while preserving original path segments.
|
|
* `rootName` is the admitted root directory name; `.` denotes that root in returned directories.
|
|
*/
|
|
export const desktopPathConventions = (path: string, rootName?: string) => {
|
|
const segments = path.split("/");
|
|
const folded = segments.map(foldSegment);
|
|
let unityManagedDirectory: string | null = null;
|
|
let unityManagedIndex = -1;
|
|
for (let index = 0; index + 2 < folded.length; index++) {
|
|
const segment = folded[index];
|
|
const namedDataDirectory =
|
|
segment !== undefined && segment.length > 5 && segment.endsWith("_data");
|
|
const bundleDataDirectory =
|
|
segment === "data" &&
|
|
folded[index - 1] === "resources" &&
|
|
folded[index - 2] === "contents";
|
|
if (
|
|
(namedDataDirectory || bundleDataDirectory) &&
|
|
folded[index + 1] === "managed"
|
|
) {
|
|
unityManagedDirectory = segments.slice(0, index + 2).join("/");
|
|
unityManagedIndex = index + 1;
|
|
}
|
|
}
|
|
// The admitted root may itself be the backup folder, so its name precedes the relative segments.
|
|
const rooted =
|
|
rootName === undefined ? folded : [foldSegment(rootName), ...folded];
|
|
const shift = rooted.length - folded.length;
|
|
let unityIl2cppBackupDirectory: string | null = null;
|
|
let unityIl2cppBackupIndex = -1;
|
|
for (let index = 0; index + 1 < rooted.length; index++) {
|
|
const segment = rooted[index] ?? "";
|
|
if (
|
|
segment.length > UNITY_IL2CPP_BACKUP_SUFFIX.length &&
|
|
segment.endsWith(UNITY_IL2CPP_BACKUP_SUFFIX)
|
|
) {
|
|
unityIl2cppBackupDirectory =
|
|
index < shift ? "." : segments.slice(0, index - shift + 1).join("/");
|
|
unityIl2cppBackupIndex = index - shift;
|
|
}
|
|
}
|
|
const name = folded.at(-1) ?? "";
|
|
return {
|
|
unityIl2cppBackupDirectory,
|
|
unityIl2cppBackupAssembly:
|
|
unityIl2cppBackupIndex === segments.length - 3 &&
|
|
folded[unityIl2cppBackupIndex + 1] === "managed" &&
|
|
UNITY_PREDEFINED_ASSEMBLIES.has(name),
|
|
unityManagedDirectory,
|
|
unityPredefinedAssembly:
|
|
unityManagedIndex === segments.length - 2 &&
|
|
UNITY_PREDEFINED_ASSEMBLIES.has(name),
|
|
il2cppLibrary: DESKTOP_IL2CPP_LIBRARIES.has(name),
|
|
electronArchive:
|
|
folded.at(-2) === "resources" &&
|
|
(name === "app.asar" || name === "default_app.asar"),
|
|
unrealContainer:
|
|
(name.endsWith(".pak") || name.endsWith(".utoc")) &&
|
|
folded.some(
|
|
(segment, index) =>
|
|
segment === "content" &&
|
|
folded[index + 1] === "paks" &&
|
|
index + 2 < folded.length,
|
|
),
|
|
};
|
|
};
|