diff --git a/Osu.Patcher.Hook/Patches/LivePerformance/AddPerformanceToUi.cs b/Osu.Patcher.Hook/Patches/LivePerformance/AddPerformanceToUi.cs index f95bd82..d2b65d3 100644 --- a/Osu.Patcher.Hook/Patches/LivePerformance/AddPerformanceToUi.cs +++ b/Osu.Patcher.Hook/Patches/LivePerformance/AddPerformanceToUi.cs @@ -40,7 +40,7 @@ private static void After( var scoreFont = SkinOsu.FontScore.Get(currentSkin); var scoreFontOverlap = SkinOsu.FontScoreOverlap.Get(currentSkin); - var performanceSprite = ((ConstructorInfo)pSpriteText.Constructor.Reference).Invoke( + var performanceSprite = pSpriteText.Constructor.Invoke( [ /* text: */ "00.0pp", /* fontName: */ scoreFont, @@ -48,7 +48,7 @@ private static void After( /* fieldType: */ alignRight ? Fields.TopRight : Fields.TopLeft, /* origin: */ alignRight ? Origins.TopRight : Origins.TopLeft, /* clock: */ Clocks.Game, - /* startPosition: */ ((ConstructorInfo)Vector2.Constructor.Reference).Invoke([0f, 0f]), + /* startPosition: */ Vector2.Constructor.Invoke([0f, 0f]), /* drawDepth: */ 0.95f, /* alwaysDraw: */ true, /* color: */ Color.White, @@ -60,7 +60,7 @@ private static void After( // TODO: don't add 9f offset if score-p@2x.png/score-p.png texture exists var positionX = Vector2.X.Get(position) + 8f; var positionY = GetYOffset(Vector2.Y.Get(position), scale, __instance); - var newPosition = ((ConstructorInfo)Vector2.Constructor.Reference).Invoke([positionX, positionY]); + var newPosition = Vector2.Constructor.Invoke([positionX, positionY]); pDrawable.Position.Set(performanceSprite, newPosition); pDrawable.Scale.Set(performanceSprite, 0.50f); @@ -76,9 +76,9 @@ private static void After( private static float GetYOffset(float baseYPosition, float scale, object scoreDisplay) { // Read the heights of both pSpriteTexts: s_Score, s_Accuracy - var sprites = ScoreDisplay.RuntimeType + var sprites = ScoreDisplay.Class.Reference .GetDeclaredFields() - .Where(f => f.FieldType == pSpriteText.RuntimeType) + .Where(f => f.FieldType == pSpriteText.Class.Reference) .Select(f => f.GetValue(scoreDisplay)); var spriteSizes = sprites .Where(s => s != null) diff --git a/Osu.Stubs/Beatmap.cs b/Osu.Stubs/Beatmap.cs index d90bbe7..88d0103 100644 --- a/Osu.Stubs/Beatmap.cs +++ b/Osu.Stubs/Beatmap.cs @@ -1,4 +1,3 @@ -using System; using System.Diagnostics; using System.IO; using System.Linq; @@ -11,21 +10,38 @@ namespace Osu.Stubs; -/// -/// Original: osu.GameplayElements.Beatmaps.Beatmap -/// b20240123: #=znWyq67N7qygzmzTavD7zPPRqI0zwVZPmTyOyayVHbxCf -/// -[UsedImplicitly] +[PublicAPI] public static class Beatmap { + /// + /// Original: osu.GameplayElements.Beatmaps.Beatmap + /// b20240123: #=znWyq67N7qygzmzTavD7zPPRqI0zwVZPmTyOyayVHbxCf + /// + public static readonly LazyType Class = new( + "osu.GameplayElements.Beatmaps.Beatmap", + () => Score.Constructor.Reference + .GetParameters()[1] // 2nd param is of type Beatmap + .ParameterType + ); + + /// + /// Original: Beatmap(string filename) + /// b20240123: #=znWyq67N7qygzmzTavD7zPPRqI0zwVZPmTyOyayVHbxCf + /// + public static readonly LazyConstructor Constructor = new( + "osu.GameplayElements.Beatmaps.Beatmap::Beatmap(string)", + () => Class.Reference + .GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic) + .Single() + ); + /// /// Original: Unknown, best guess: SetContainingFolder(string absoluteDirPath) /// b20240123: #=zQwzJucCIbIUZrSZR8Q== /// - private static readonly LazyMethod SetContainingFolder = new( - "Beatmap#SetContainingFolder(...)", - new[] - { + private static readonly LazyMethod SetContainingFolder = LazyMethod.ByPartialSignature( + "osu.GameplayElements.Beatmaps.Beatmap::[unknown, SetContainingFolder?]", + [ Callvirt, Starg_S, Ldarg_0, @@ -39,21 +55,20 @@ public static class Beatmap Callvirt, Stfld, // Reference to ContainingFolder Ret, - } + ] ); /// /// Original: Filename /// b20240123: #=zdZI_NOQ= /// - [UsedImplicitly] public static readonly LazyField Filename = new( - "Beatmap#Filename", + "osu.GameplayElements.Beatmaps.Beatmap::Filename", () => { // Find the field this code is referencing: "this.Filename = Path.GetFileName(filename);" var findMethod = typeof(Path).GetMethod(nameof(Path.GetFileName))!; - var storeInstruction = MethodReader.GetInstructions(PrimaryConstructor) + var storeInstruction = MethodReader.GetInstructions(Constructor.Reference) .SkipWhile(inst => !findMethod.Equals(inst.Operand)) .Skip(1) .First(); @@ -68,9 +83,8 @@ public static class Beatmap /// Original: Unknown, best guess: ContainingFolder (not absolute) /// b20240123: #=zDmW9P6igScNm /// - [UsedImplicitly] public static readonly LazyField ContainingFolder = new( - "Beatmap#ContainingFolder", + "osu.GameplayElements.Beatmaps.Beatmap::ContainingFolder", () => { // Last Stfld is a reference to ContainingFolder @@ -83,26 +97,11 @@ public static class Beatmap } ); - /// - /// Original: Beatmap(string filename) - /// b20240123: #=znWyq67N7qygzmzTavD7zPPRqI0zwVZPmTyOyayVHbxCf - /// - [UsedImplicitly] - public static ConstructorInfo PrimaryConstructor => RuntimeType - .GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic) - .Single(); - - [UsedImplicitly] - public static Type RuntimeType => (Score.ConstructorFromReplayAndMap.Reference as ConstructorInfo)! - .GetParameters()[1] // 2nd param is of type Beatmap - .ParameterType; - /// /// Utility wrapper to get the full beatmap path of a Beatmap. /// /// An instance of Beatmap that was initialized with the filepath. /// The absolute path, or null if this isn't a file-backed Beatmap. - [UsedImplicitly] public static string? GetBeatmapPath(object beatmap) { var filename = Filename.Get(beatmap); diff --git a/Osu.Stubs/BeatmapTreeItem.cs b/Osu.Stubs/BeatmapTreeItem.cs index 4a006e6..1b8b151 100644 --- a/Osu.Stubs/BeatmapTreeItem.cs +++ b/Osu.Stubs/BeatmapTreeItem.cs @@ -8,18 +8,16 @@ namespace Osu.Stubs; /// Original: osu.GameplayElements.Beatmaps.BeatmapTreeItem /// b20240102.2: #=z5dQ2d9vSoRzE9rWp7h5_dJ$Z1Sw13QcKf_J$7ZjIN21zOue2gQ== /// -[UsedImplicitly] +[PublicAPI] public static class BeatmapTreeItem { /// /// Original: PopulateSprites(TreeItemState lastState, bool instant) /// b20240102.2: #=ztf5JjnV1ubq2KLwXBg== /// - [UsedImplicitly] - public static readonly LazyMethod UpdateSprites = new( - "BeatmapTreeItem#UpdateSprites(...)", - new[] - { + public static readonly LazyMethod UpdateSprites = LazyMethod.ByPartialSignature( + "osu.GameplayElements.Beatmaps.BeatmapTreeItem::UpdateSprites(TreeItemState, bool)", + [ Pop, Ldsfld, Ldftn, @@ -33,18 +31,16 @@ public static class BeatmapTreeItem Ldloc_0, Ldftn, Newobj, - } + ] ); /// /// Original: PopulateSprites() /// b20240102.2: #=zGdedQLY8W$wSqdNzBA== /// - [UsedImplicitly] - public static readonly LazyMethod PopulateSprites = new( - "BeatmapTreeItem#PopulateSprites()", - new[] - { + public static readonly LazyMethod PopulateSprites = LazyMethod.ByPartialSignature( + "osu.GameplayElements.Beatmaps.BeatmapTreeItem::PopulateSprites()", + [ Brfalse, Ldsfld, Ldfld, @@ -57,6 +53,6 @@ public static class BeatmapTreeItem Ldc_I4_5, Newarr, Dup, - } + ] ); } \ No newline at end of file diff --git a/Osu.Stubs/BeatmapTreeManager.cs b/Osu.Stubs/BeatmapTreeManager.cs index 35b84e9..07dcbd7 100644 --- a/Osu.Stubs/BeatmapTreeManager.cs +++ b/Osu.Stubs/BeatmapTreeManager.cs @@ -7,15 +7,15 @@ namespace Osu.Stubs; /// Original: osu.GameplayElements.Beatmaps.BeatmapTreeManager /// b20240102.2: #=zV51QPv13Z_vZJRxEY28cvGye77gU6YZQsv_F5muuWuN62V5sIQ== /// -[UsedImplicitly] +[PublicAPI] public static class BeatmapTreeManager { /// - /// Original: CurrentGroupMode of type ]]> + /// Original: CurrentGroupMode of type Bindable{TreeGroupMode} /// b20240102.2: #=zbF4rnAiPRGJl /// public static readonly LazyField CurrentGroupMode = new( - "BeatmapTreeManager#CurrentGroupMode", + "osu.GameplayElements.Beatmaps.BeatmapTreeManager::CurrentGroupMode", () => SongSelection.FindReferences().CurrentGroupMode ); } \ No newline at end of file diff --git a/Osu.Stubs/Bindable.cs b/Osu.Stubs/Bindable.cs index 81440e9..bcd9fe0 100644 --- a/Osu.Stubs/Bindable.cs +++ b/Osu.Stubs/Bindable.cs @@ -11,21 +11,24 @@ namespace Osu.Stubs; /// Original: osu.Helpers.Bindable /// b20240102.2: #=zDruHkLGdhQjyjYxqzw== /// -[UsedImplicitly] +[PublicAPI] public static class Bindable { /// - /// Original: Value.get (property getter method) + /// Original: get_Value() (property getter method) /// b20240102.2: #=zHO4Uaog= /// - [UsedImplicitly] - public static readonly LazyMethod GetValue = new("Bindable#Value.get", () => - { - var instructions = MethodReader.GetInstructions(SongSelection.BeatmapTreeManagerOnRightClicked.Reference); + public static readonly LazyMethod GetValue = new( + "osu.Helpers.Bindable::get_Value()", + () => + { + var instructions = MethodReader + .GetInstructions(SongSelection.BeatmapTreeManagerOnRightClicked.Reference); - // Get reference to Bindable:Value.get (property getter) - return (MethodBase)instructions - .First(inst => inst.Opcode == Callvirt) - .Operand; - }); + // Get reference to Bindable:Value.get (property getter) + return (MethodInfo)instructions + .First(inst => inst.Opcode == Callvirt) + .Operand; + } + ); } \ No newline at end of file diff --git a/Osu.Stubs/ErrorSubmission.cs b/Osu.Stubs/ErrorSubmission.cs index ef32434..57595ad 100644 --- a/Osu.Stubs/ErrorSubmission.cs +++ b/Osu.Stubs/ErrorSubmission.cs @@ -8,18 +8,16 @@ namespace Osu.Stubs; /// Original: osu.Helpers.ErrorSubmission /// v20230326: #=zhC91LB1xsJMwYkF0UQ== /// -[UsedImplicitly] +[PublicAPI] public static class ErrorSubmission { /// /// Original: osu.Helpers.ErrorSubmission:Submit(OsuError) /// v20230326: #=zhC91LB1xsJMwYkF0UQ==:#=zPqLxZPA= /// - [UsedImplicitly] - public static readonly LazyMethod Submit = new( - "ErrorSubmission#Submit", - new[] - { + public static readonly LazyMethod Submit = LazyMethod.ByPartialSignature( + "osu.Helpers.ErrorSubmission::Submit(OsuError)", + [ Ldsfld, Ldc_I4_0, Ble_S, @@ -34,6 +32,6 @@ public static class ErrorSubmission Ldarg_0, Ldfld, Ldarg_0, - } + ] ); } \ No newline at end of file diff --git a/Osu.Stubs/EventManager.cs b/Osu.Stubs/EventManager.cs index 9ced76c..af5bd8d 100644 --- a/Osu.Stubs/EventManager.cs +++ b/Osu.Stubs/EventManager.cs @@ -11,18 +11,16 @@ namespace Osu.Stubs; /// Original: osu.GameplayElements.Events.EventManager /// b20240102.2: #=zbJqkrJF69yLASpsnblYMeq3jWETw /// -[UsedImplicitly] +[PublicAPI] public static class EventManager { /// - /// Original: set_ShowStoryboard (property setter) + /// Original: set_ShowStoryboard() (property setter) /// b20240102.2: #=zKZugEelWoTXb /// - [UsedImplicitly] - public static readonly LazyMethod SetShowStoryboard = new( - "EventManager#ShowStoryboard.set", - new[] - { + public static readonly LazyMethod SetShowStoryboard = LazyMethod.BySignature( + "osu.GameplayElements.Events.EventManager::set_ShowStoryBoard()", + [ Ldarg_0, Ldsfld, Bne_Un_S, @@ -34,16 +32,15 @@ public static class EventManager Ldsfld, Callvirt, Ret, - } + ] ); /// /// The compiler generated backing field for the ShowStoryboard property. /// See: /// - [UsedImplicitly] public static readonly LazyField ShowStoryboard = new( - "EventManager#ShowStoryboard.backing", + "osu.GameplayElements.Events.EventManager::[ShowStoryboard backing]", () => { // Find a single StoreField instruction in the setter for this property diff --git a/Osu.Stubs/GameBase.cs b/Osu.Stubs/GameBase.cs index a5c8ccc..7b002bf 100644 --- a/Osu.Stubs/GameBase.cs +++ b/Osu.Stubs/GameBase.cs @@ -8,18 +8,16 @@ namespace Osu.Stubs; /// Original: osu.GameBase /// b20240123: #=zduF3QmjgMG4eSc$fOQ== /// -[UsedImplicitly] -public class GameBase +[PublicAPI] +public static class GameBase { /// /// Original: get_ModeCanReload() /// b20240123: #=zL6aRJUMxZO5fmlF9KQ== /// - [UsedImplicitly] - public static readonly LazyMethod GetModeCanReload = new( - "GameBase#get_ModeCanReload()", - new[] - { + public static readonly LazyMethod GetModeCanReload = LazyMethod.ByPartialSignature( + "osu.GameBase::get_ModeCanReload()", + [ Ldc_I4_7, Bgt_S, Ldloc_0, @@ -29,18 +27,16 @@ public class GameBase Ldc_I4_7, Beq_S, Br_S, - } + ] ); /// /// Original: softHandle(Exception e) /// b20240123: #=z8BJOiJxSLUwM /// - [UsedImplicitly] - public static readonly LazyMethod SoftHandle = new( - "GameBase#softHandle(...)", - new[] - { + public static readonly LazyMethod SoftHandle = LazyMethod.ByPartialSignature( + "osu.GameBase::softHandle(Exception)", + [ Ldarg_0, Isinst, Brtrue_S, @@ -51,6 +47,6 @@ public class GameBase Isinst, Brfalse_S, Ldc_I4_8, - } + ] ); } \ No newline at end of file diff --git a/Osu.Stubs/IncreaseScoreType.cs b/Osu.Stubs/IncreaseScoreType.cs index 3c2581e..4686413 100644 --- a/Osu.Stubs/IncreaseScoreType.cs +++ b/Osu.Stubs/IncreaseScoreType.cs @@ -1,13 +1,9 @@ -using System; using JetBrains.Annotations; +using Osu.Utils.Lazy; namespace Osu.Stubs; -/// -/// Original: osu.GameModes.Play.Rulesets.IncreaseScoreType -/// b20240123: #=zGMiEmtRx7tdNjrx8B18dVB33Br1S9OJ2_XDs0XJSAtWZ -/// -[UsedImplicitly] +[PublicAPI] public class IncreaseScoreType { // For HitCircles, the only IST emitted is the final hit result (Osu50, Osu100, Osu300, OsuMiss) @@ -37,9 +33,15 @@ public class IncreaseScoreType public const int Osu300 = 1 << 10; public const int OsuMiss = -0x20000; - // Used as the first parameter in the constructor for ScoreChange - [UsedImplicitly] - public static Type RuntimeType => ScoreChange.PrimaryConstructor - .GetParameters()[0] - .ParameterType; + /// + /// Used as the first parameter in another method: ScoreChange::ScoreChange(IncreaseScoreType, ...) + /// Original: osu.GameModes.Play.Rulesets.IncreaseScoreType + /// b20240123: #=zGMiEmtRx7tdNjrx8B18dVB33Br1S9OJ2_XDs0XJSAtWZ + /// + public static readonly LazyType Class = new( + "osu.GameModes.Play.Rulesets.IncreaseScoreType", + () => ScoreChange.Constructor.Reference + .GetParameters()[0] + .ParameterType + ); } \ No newline at end of file diff --git a/Osu.Stubs/Logger.cs b/Osu.Stubs/Logger.cs index 6da9e0b..45433ce 100644 --- a/Osu.Stubs/Logger.cs +++ b/Osu.Stubs/Logger.cs @@ -8,18 +8,16 @@ namespace Osu.Stubs; /// Original: osu_common.Helpers.Logger /// b20240102.2: #=zN_Wxi7NCKC8KJdpmHc2RwYM= /// -[UsedImplicitly] -public class Logger +[PublicAPI] +public static class Logger { /// /// Original: Log(string message, LoggingTarget target, LogLevel level) /// b20240102.2: #=zzl_m9cI= /// - [UsedImplicitly] - public static readonly LazyMethod Log = new( - "Logger#Log(...)", - new[] - { + public static readonly LazyMethod Log = LazyMethod.BySignature( + "osu_common.Helpers.Logger::Log(string, LoggingTarget, LogLevel)", + [ Ldarg_1, Ldc_I4_1, Call, @@ -30,8 +28,6 @@ public class Logger Pop, Leave_S, Ret, - }, - false, - true + ] ); } \ No newline at end of file diff --git a/Osu.Stubs/NotificationManager.cs b/Osu.Stubs/NotificationManager.cs index 8bfe11f..61b7a6c 100644 --- a/Osu.Stubs/NotificationManager.cs +++ b/Osu.Stubs/NotificationManager.cs @@ -8,7 +8,7 @@ namespace Osu.Stubs; /// Original: osu.Graphics.Notifications.NotificationManager /// v20230326: #=zKAl4ByX632sEL0mCYHkNNO8= /// -[UsedImplicitly] +[PublicAPI] public static class NotificationManager { /// @@ -23,11 +23,9 @@ public static class NotificationManager /// /// v20230326: #=zKAl4ByX632sEL0mCYHkNNO8=:#=zKgeB1K0= /// - [UsedImplicitly] - public static readonly LazyMethod ShowMessage = new( - "NotificationManager#ShowMessage", - new[] - { + public static readonly LazyMethod ShowMessage = LazyMethod.ByPartialSignature( + "osu.Graphics.Notifications.NotificationManager::ShowMessage(string, Color, int, VoidDelegate)", + [ Ldarg_1, Stfld, Ldloc_0, @@ -37,6 +35,6 @@ public static class NotificationManager Ldloc_0, Ldftn, Newobj, - } + ] ); } \ No newline at end of file diff --git a/Osu.Stubs/Obfuscated.cs b/Osu.Stubs/Obfuscated.cs index 55c06af..af4b1e8 100644 --- a/Osu.Stubs/Obfuscated.cs +++ b/Osu.Stubs/Obfuscated.cs @@ -7,21 +7,25 @@ namespace Osu.Stubs; -/// -/// Original: osu.Helpers.Obfuscated{T} -/// b20240123: #=z7wrq$zbpx0eedoF3ocH29DF53lUE{#=z62rU_UA=} -/// -[UsedImplicitly] -public class Obfuscated +[PublicAPI] +public static class Obfuscated { + /// + /// Original: osu.Helpers.Obfuscated{T} + /// b20240123: #=z7wrq$zbpx0eedoF3ocH29DF53lUE{#=z62rU_UA=} + /// + public static readonly LazyType Class = new( + "osu.Helpers.Obfuscated{T}", + () => Finalize!.Reference.DeclaringType! + ); + /// /// Original: Finalize() /// b20240123: Finalize() /// - private static readonly LazyMethod Finalize = new( - "Obfuscated#Finalize()", - new[] - { + private static readonly LazyMethod Finalize = LazyMethod.ByPartialSignature( + "osu.Helpers.Obfuscated::Finalize()", + [ Newobj, Dup, Stsfld, @@ -34,25 +38,28 @@ public class Obfuscated Call, Endfinally, Ret, - } + ] ); /// /// Original: get_Value() (property getter) /// b20240123: #=zHT6xZVI= /// - [UsedImplicitly] public static readonly LazyMethod GetValue = new( - "Obfuscated#get_Value()", - () => RuntimeType.GetRuntimeMethods() - .First(mtd => mtd.GetParameters().Length == 0 && mtd.ReturnType.IsGenericParameter) + "osu.Helpers.Obfuscated{T}::get_Value()", + () => Class.Reference + .GetRuntimeMethods() + .First(mtd => + mtd.GetParameters().Length == 0 && + mtd.ReturnType.IsGenericParameter) ); - [UsedImplicitly] - public static Type RuntimeType => Finalize.Reference.DeclaringType!; - - // Binds the generic parameter in Obfuscated so the method becomes callable - public static MethodBase BindGetValue(Type type) => RuntimeType + /// + /// Binds the generic parameter in Obfuscated{T} so the method becomes callable + /// + /// The type that should be bound to the T generic parameter. + /// A new bound method info that is invokable. + public static MethodInfo BindGetValue(Type type) => Class.Reference .MakeGenericType(type) .GetMethod(GetValue.Reference.Name)!; } \ No newline at end of file diff --git a/Osu.Stubs/Options.cs b/Osu.Stubs/Options.cs index 277d8c4..8905649 100644 --- a/Osu.Stubs/Options.cs +++ b/Osu.Stubs/Options.cs @@ -8,16 +8,15 @@ namespace Osu.Stubs; /// Original: osu.GameModes.Options.Options /// b20240123: #=zIP$6zD_IcaL0ugvXAFTvJYG2gFLx /// -[UsedImplicitly] -public class Options +[PublicAPI] +public static class Options { /// /// Original: CanExpand (property getter) /// b20240123: #=zxcgzxWXF$WLr /// - [UsedImplicitly] - public static readonly LazyMethod GetCanExpand = new( - "Options#get_CanExpand", + public static readonly LazyMethod GetCanExpand = LazyMethod.ByPartialSignature( + "osu.GameModes.Options.Options.Options::get_CanExpand()", new[] { Ldc_I4, diff --git a/Osu.Stubs/OsuDirect.cs b/Osu.Stubs/OsuDirect.cs index 3e88669..f75763b 100644 --- a/Osu.Stubs/OsuDirect.cs +++ b/Osu.Stubs/OsuDirect.cs @@ -8,17 +8,16 @@ namespace Osu.Stubs; /// Original: osu.Online.OsuDirect /// b20240102.2: #=zz3BVwBujgm4LKna55Dwx3UA= /// -public abstract class OsuDirect +[PublicAPI] +public static class OsuDirect { /// - /// Original: HandlePickup() + /// Original: HandlePickup(...) /// b20240102.2: #=zJczNz_3lxxrQQnnZog== /// - [UsedImplicitly] - public static readonly LazyMethod HandlePickup = new( - "osu.Online.OsuDirect#HandlePickup", - new[] - { + public static readonly LazyMethod HandlePickup = LazyMethod.ByPartialSignature( + "osu.Online.OsuDirect::HandlePickup(...)", + [ Ldloc_0, Ldfld, Ldloc_0, @@ -29,6 +28,6 @@ public abstract class OsuDirect Newobj, Call, Ret, - } + ] ); } \ No newline at end of file diff --git a/Osu.Stubs/Player.cs b/Osu.Stubs/Player.cs index 3568525..647c55d 100644 --- a/Osu.Stubs/Player.cs +++ b/Osu.Stubs/Player.cs @@ -1,4 +1,3 @@ -using System; using System.Linq; using System.Reflection; using JetBrains.Annotations; @@ -7,22 +6,25 @@ namespace Osu.Stubs; -/// -/// Original: osu.GameModes.Play.Player -/// b20240124: #=zOTWUr4vq60U15SRmD_JItyatbhdR -/// -[UsedImplicitly] +[PublicAPI] public static class Player { /// - /// Original: AllowDoubleSkip.get (property getter) + /// Original: osu.GameModes.Play.Player + /// b20240124: #=zOTWUr4vq60U15SRmD_JItyatbhdR + /// + public static readonly LazyType Class = new( + "osu.GameModes.Play.Player", + () => GetAllowDoubleSkip!.Reference.DeclaringType! + ); + + /// + /// Original: get_AllowDoubleSkip() (property getter) /// b20240124: #=zp29IlAJ43g4WRArPQA== /// - [UsedImplicitly] - public static readonly LazyMethod GetAllowDoubleSkip = new( - "Player#AllowDoubleSkip.get", - new[] - { + public static readonly LazyMethod GetAllowDoubleSkip = LazyMethod.ByPartialSignature( + "osu.GameModes.Play.Player::get_AllowDoubleSkip()", + [ Neg, Stloc_0, Ldarg_0, @@ -34,18 +36,16 @@ public static class Player Brtrue_S, Ldc_I4_0, Br_S, - } + ] ); /// /// Original: OnLoadComplete(bool success) /// b20240124: #=zXb_K4cZvV$uy /// - [UsedImplicitly] - public static readonly LazyMethod OnLoadComplete = new( - "Player#OnLoadComplete(...)", - new[] - { + public static readonly LazyMethod OnLoadComplete = LazyMethod.ByPartialSignature( + "osu.GameModes.Play.Player::OnLoadComplete(bool)", + [ Br, Ldloc_S, Callvirt, @@ -54,21 +54,17 @@ public static class Player Ldsfld, Ldfld, Call, - } + ] ); /// /// Original: currentScore /// b20240124: #=zF6h5l4j0$TfX /// - [UsedImplicitly] public static readonly LazyField CurrentScore = new( - "Player#currentScore", - () => RuntimeType + "osu.GameModes.Play.Player::currentScore", + () => Class.Reference .GetFields(BindingFlags.Static | BindingFlags.NonPublic) - .Single(field => field.FieldType == Score.RuntimeType) + .Single(field => field.FieldType == Score.Class.Reference) ); - - [UsedImplicitly] - public static Type RuntimeType => GetAllowDoubleSkip.Reference.DeclaringType!; } \ No newline at end of file diff --git a/Osu.Stubs/Ruleset.cs b/Osu.Stubs/Ruleset.cs index 36adde6..e3cc481 100644 --- a/Osu.Stubs/Ruleset.cs +++ b/Osu.Stubs/Ruleset.cs @@ -1,4 +1,3 @@ -using System; using System.Linq; using System.Reflection; using JetBrains.Annotations; @@ -8,20 +7,25 @@ namespace Osu.Stubs; -/// -/// Original: osu.GameModes.Play.Rulesets.Ruleset -/// b20240123: #=zwRa71lIOJzp$VMz5GAIPMrt1N4rdQ4gC2Fx1Jtw= -/// +[PublicAPI] public static class Ruleset { + /// + /// Original: osu.GameModes.Play.Rulesets.Ruleset + /// b20240123: #=zwRa71lIOJzp$VMz5GAIPMrt1N4rdQ4gC2Fx1Jtw= + /// + public static readonly LazyType Class = new( + "osu.GameModes.Play.Rulesets.Ruleset", + () => OnIncreaseScoreHit!.Reference.DeclaringType! + ); + /// /// Original: Fail(bool continuousPlay) /// b20240123: #=zPAIY7AY= /// - public static readonly LazyMethod Fail = new( - "Ruleset#Fail(...)", - new[] - { + public static readonly LazyMethod Fail = LazyMethod.ByPartialSignature( + "osu.GameModes.Play.Rulesets.Ruleset::Fail(bool)", + [ Brtrue_S, Leave_S, Ldloca_S, @@ -30,18 +34,16 @@ public static class Ruleset Endfinally, Call, Ldc_I4_1, - } + ] ); /// /// Original: OnIncreaseScoreHit(IncreaseScoreType ist, double hpIncrease, bool increaseCombo, HitObject h) /// b20240123: #=zH8JNJ0F2$AwN /// - [UsedImplicitly] - public static readonly LazyMethod OnIncreaseScoreHit = new( - "Ruleset#OnIncreaseScoreHit(...)", - new[] - { + public static readonly LazyMethod OnIncreaseScoreHit = LazyMethod.ByPartialSignature( + "osu.GameModes.Play.Rulesets.Ruleset::OnIncreaseScoreHit(IncreaseScoreType, double, bool, HitObject)", + [ Ldarg_3, Brfalse_S, Ldarg_0, @@ -51,18 +53,16 @@ public static class Ruleset Bgt_Un_S, Ret, Ldarg_0, - } + ] ); /// /// Original: ResetScore(bool storeStatistics) /// b20240123: #=zLIXqTYl0LIQz /// - [UsedImplicitly] - public static readonly LazyMethod ResetScore = new( - "Ruleset#ResetScore(...)", - new[] - { + public static readonly LazyMethod ResetScore = LazyMethod.ByPartialSignature( + "osu.GameModes.Play.Rulesets.Ruleset::ResetScore(bool)", + [ Ldfld, Ldarg_1, Callvirt, @@ -76,16 +76,15 @@ public static class Ruleset Ldarg_0, Ldfld, Callvirt, - } + ] ); /// /// Original: Initialize() /// b20240123: #=znhXnLb4= /// - [UsedImplicitly] - public static readonly LazyMethod Initialize = new( - "Ruleset#Initialize()", + public static readonly LazyMethod Initialize = LazyMethod.ByPartialSignature( + "osu.GameModes.Play.Rulesets.Ruleset::Initialize()", new[] { Ldarg_0, @@ -97,35 +96,29 @@ public static class Ruleset /// Original: CurrentScore /// b20240123: Instance /// - [UsedImplicitly] public static readonly LazyField Instance = new( - "Ruleset#Instance", - () => RuntimeType.GetRuntimeFields() - .Single(field => field.FieldType == RuntimeType) + "osu.GameModes.Play.Rulesets.Ruleset::Instance", + () => Class.Reference.GetRuntimeFields() + .Single(field => field.FieldType == Class.Reference) ); /// /// Original: CurrentScore /// b20240123: #=zk4sdboE= /// - [UsedImplicitly] public static readonly LazyField CurrentScore = new( - "Ruleset#CurrentScore", - () => RuntimeType.GetRuntimeFields() - .Single(field => field.FieldType == Score.RuntimeType) + "osu.GameModes.Play.Rulesets.Ruleset::CurrentScore", + () => Class.Reference.GetRuntimeFields() + .Single(field => field.FieldType == Score.Class.Reference) ); /// /// Original: ScoreDisplay /// b20240123: #=znVUsSpw8w4aW /// - [UsedImplicitly] public static readonly LazyField ScoreDisplay = new( - "Ruleset#ScoreDisplay", - () => RuntimeType.GetRuntimeFields() - .Single(field => field.FieldType == Stubs.ScoreDisplay.RuntimeType) + "osu.GameModes.Play.Rulesets.Ruleset::ScoreDisplay", + () => Class.Reference.GetRuntimeFields() + .Single(field => field.FieldType == Stubs.ScoreDisplay.Class.Reference) ); - - [UsedImplicitly] - public static Type RuntimeType => OnIncreaseScoreHit.Reference.DeclaringType!; } \ No newline at end of file diff --git a/Osu.Stubs/Score.cs b/Osu.Stubs/Score.cs index ce7e966..b8a8f0a 100644 --- a/Osu.Stubs/Score.cs +++ b/Osu.Stubs/Score.cs @@ -11,22 +11,38 @@ namespace Osu.Stubs; -/// -/// Original: osu.GameplayElements.Scoring.Score -/// b20240123: #=zwswDPw49w3ZrQEjyKFYhq9$8W6WDMiSHDDrNV7k= -/// -[UsedImplicitly] -public class Score +[PublicAPI] +public static class Score { + /// + /// Original: osu.GameplayElements.Scoring.Score + /// b20240123: #=zwswDPw49w3ZrQEjyKFYhq9$8W6WDMiSHDDrNV7k= + /// + public static readonly LazyType Class = new( + "osu.GameplayElements.Scoring.Score", + () => GetAccuracy!.Reference.DeclaringType! + ); + + /// + /// Original: Score(string input, Beatmap beatmap) + /// b20240123: #=zwswDPw49w3ZrQEjyKFYhq9$8W6WDMiSHDDrNV7k= + /// + public static readonly LazyConstructor Constructor = new( + "osu.GameplayElements.Scoring.Score::Score(string, Beatmap)", + () => Class.Reference + .GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic) + .Single(ctor => ctor.GetParameters() + .GetOrDefault(0, null)? + .ParameterType == typeof(string)) // Find the only constructor with a "string" as the first parameter + ); + /// /// Original: get_Accuracy() (property getter) /// b20240123: #=zY_1A2REMae0xoSV0fA== /// - [UsedImplicitly] - public static readonly LazyMethod GetAccuracy = new( - "Score#get_Accuracy()", - new[] - { + public static readonly LazyMethod GetAccuracy = LazyMethod.ByPartialSignature( + "osu.GameplayElements.Scoring.Score::get_Accuracy()", + [ Ldc_R4, Ret, Ldarg_0, @@ -38,47 +54,33 @@ public class Score Ldc_I4_S, Mul, Add, - } - ); - - /// - /// Original: Score(string input, Beatmap beatmap) - /// b20240123: #=zwswDPw49w3ZrQEjyKFYhq9$8W6WDMiSHDDrNV7k= - /// - [UsedImplicitly] - public static readonly LazyMethod ConstructorFromReplayAndMap = new( - "Score#(string, Beatmap)", - () => RuntimeType.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic) - .Single(ctor => ctor.GetParameters() - .GetOrDefault(0, null)? - .ParameterType == typeof(string)) // Find the only constructor with a "string" as the first parameter + ] ); /// /// Original: Beatmap /// b20240123: #=zhcWn5UkrdlUu /// - [UsedImplicitly] public static readonly LazyField Beatmap = new( - "Score#Beatmap", - () => RuntimeType.GetRuntimeFields() - .Single(inst => inst.FieldType == Stubs.Beatmap.RuntimeType) + "osu.GameplayElements.Scoring.Score::Beatmap", + () => Class.Reference + .GetRuntimeFields() + .Single(inst => inst.FieldType == Stubs.Beatmap.Class.Reference) ); /// /// Original: MaxCombo /// b20240123: #=zkQ9fUTuRkHox /// - [UsedImplicitly] public static readonly LazyField MaxCombo = new( - "Score#MaxCombo", + "osu.GameplayElements.Scoring.Score::MaxCombo", () => { // Look for this: "this.MaxCombo = (int)Convert.ToUInt16(array[num++]);" var findMethod = AccessTools.Method(typeof(Convert), nameof(Convert.ToUInt16), [typeof(string)])!; var storeInstruction = MethodReader - .GetInstructions(ConstructorFromReplayAndMap.Reference) + .GetInstructions(Constructor.Reference) .SkipWhile(inst => !findMethod.Equals(inst.Operand)) .Skip(1) .First(); @@ -94,23 +96,25 @@ public class Score /// Original: EnabledMods /// b20240123: #=zxL1NzqBwrqNU /// - [UsedImplicitly] public static readonly LazyField EnabledMods = new( "Score#EnabledMods", - () => RuntimeType.GetDeclaredFields() - .Single(field => - field.FieldType.IsGenericType && field.FieldType.GetGenericTypeDefinition() == Obfuscated.RuntimeType) + () => Class.Reference + .GetDeclaredFields() + .Single(field => field.FieldType.IsGenericType && + field.FieldType.GetGenericTypeDefinition() == Obfuscated.Class.Reference) ); /// /// The generic method Obfuscated{T}::get_Value() with the type parameter T bound to Mods. /// - [UsedImplicitly] public static readonly LazyMethod EnabledModsGetValue = new( - "Obfuscated#get_Value()", - () => Obfuscated.BindGetValue(EnabledMods.Reference.FieldType.GetGenericArguments().First()) - ); + "osu.Helpers.Obfuscated::get_Value()", + () => + { + var modsType = EnabledMods.Reference.FieldType + .GetGenericArguments().First(); - [UsedImplicitly] - public static Type RuntimeType => GetAccuracy.Reference.DeclaringType!; + return Obfuscated.BindGetValue(modsType); + } + ); } \ No newline at end of file diff --git a/Osu.Stubs/ScoreChange.cs b/Osu.Stubs/ScoreChange.cs index fc0b7ad..b893bb2 100644 --- a/Osu.Stubs/ScoreChange.cs +++ b/Osu.Stubs/ScoreChange.cs @@ -1,36 +1,43 @@ -using System; using System.Linq; -using System.Reflection; using JetBrains.Annotations; using Osu.Utils.Lazy; namespace Osu.Stubs; -/// -/// Original: osu.GameplayElements.Scoring.Processors.ScoreChange -/// b20240123: #=zLpbYBJJuhPuc612JcL6v88v6458xNWATpDbhSpQKC6L_ -/// -[UsedImplicitly] -public class ScoreChange +[PublicAPI] +public static class ScoreChange { + /// + /// Used as the first parameter to another method: ScoreProcessor::Add(ScoreChange, ...) + /// Original: osu.GameplayElements.Scoring.Processors.ScoreChange + /// b20240123: #=zLpbYBJJuhPuc612JcL6v88v6458xNWATpDbhSpQKC6L_ + /// + public static readonly LazyType Class = new( + "osu.GameplayElements.Scoring.Processors.ScoreChange", + () => ScoreProcessor.AddScoreChange.Reference + .GetParameters()[0] + .ParameterType + ); + + /// + /// Original: ScoreChange(...) + /// b20240123: Same as class + /// + public static readonly LazyConstructor Constructor = new( + "osu.GameplayElements.Scoring.Processors.ScoreChange::ScoreChange(...)", + () => Class.Reference + .GetConstructors() + .Single(ctor => ctor.GetParameters().Length == 3) + ); + /// /// Original: HitValue /// b20240123: #=zifzkx$8= /// - [UsedImplicitly] public static readonly LazyField HitValue = new( - "ScoreChange#HitValue", - () => RuntimeType.GetFields() - .First(field => field.FieldType == IncreaseScoreType.RuntimeType) + "osu.GameplayElements.Scoring.Processors.ScoreChange::HitValue", + () => Class.Reference + .GetFields() + .First(field => field.FieldType == IncreaseScoreType.Class.Reference) ); - - // Used as the first parameter to ScoreProcessor#Add(...) - [UsedImplicitly] - public static Type RuntimeType => ScoreProcessor.AddScoreChange.Reference - .GetParameters()[0] - .ParameterType; - - [UsedImplicitly] - public static ConstructorInfo PrimaryConstructor => RuntimeType.GetConstructors() - .Single(ctor => ctor.GetParameters().Length == 3); } \ No newline at end of file diff --git a/Osu.Stubs/ScoreDisplay.cs b/Osu.Stubs/ScoreDisplay.cs index 2c5ff63..f0baf11 100644 --- a/Osu.Stubs/ScoreDisplay.cs +++ b/Osu.Stubs/ScoreDisplay.cs @@ -1,4 +1,3 @@ -using System; using System.Linq; using HarmonyLib; using JetBrains.Annotations; @@ -7,13 +6,18 @@ namespace Osu.Stubs; -/// -/// Original: osu.GameModes.Play.Components.ScoreDisplay -/// b20240124: #=z6dniqZasYGnUF21A3FQQhhWHV7POD$6AVg== -/// -[UsedImplicitly] -public class ScoreDisplay +[PublicAPI] +public static class ScoreDisplay { + /// + /// Original: osu.GameModes.Play.Components.ScoreDisplay + /// b20240124: #=z6dniqZasYGnUF21A3FQQhhWHV7POD$6AVg== + /// + public static readonly LazyType Class = new( + "osu.GameModes.Play.Components.ScoreDisplay", + () => Constructor!.Reference.DeclaringType! + ); + /// /// Original: /// @@ -26,11 +30,9 @@ public class ScoreDisplay /// /// b20240124: Same as class /// - [UsedImplicitly] - public static readonly LazyMethod Constructor = new( - "ScoreDisplay::(...)", - new[] - { + public static readonly LazyConstructor Constructor = LazyConstructor.ByPartialSignature( + "osu.GameModes.Play.Components.ScoreDisplay::ScoreDisplay(SpriteManager, Vector2, bool, float, bool, bool)", + [ Conv_R4, Ldarg_3, Brtrue_S, @@ -41,19 +43,16 @@ public class ScoreDisplay Brtrue_S, Ldc_I4_0, Br_S, - }, - true + ] ); /// /// Original: Hide() /// b20240123: #=zRjDThRI= /// - [UsedImplicitly] - public static readonly LazyMethod Hide = new( // TODO: support hiding performance counter - "ScoreDisplay#Hide()", - new[] - { + public static readonly LazyMethod Hide = LazyMethod.ByPartialSignature( // TODO: support hiding performance counter + "osu.GameModes.Play.Components.ScoreDisplay::Hide()", + [ Ldc_I4_0, Ldc_I4_0, Ldc_I4_0, @@ -68,20 +67,16 @@ public class ScoreDisplay Ldc_I4_0, Callvirt, Ret, - } + ] ); /// /// Original: spriteManager /// b20240123: #=zK4XquDeTazcx /// - [UsedImplicitly] public static readonly LazyField SpriteManager = new( - "ScoreDisplay#spriteManager", - () => RuntimeType.GetDeclaredFields() - .Single(field => field.FieldType == Stubs.SpriteManager.RuntimeType) + "osu.GameModes.Play.Components.ScoreDisplay::spriteManager", + () => Class.Reference.GetDeclaredFields() + .Single(field => field.FieldType == Stubs.SpriteManager.Class.Reference) ); - - [UsedImplicitly] - public static Type RuntimeType => Constructor.Reference.DeclaringType!; } \ No newline at end of file diff --git a/Osu.Stubs/ScoreProcessor.cs b/Osu.Stubs/ScoreProcessor.cs index 633b725..8c8cbc0 100644 --- a/Osu.Stubs/ScoreProcessor.cs +++ b/Osu.Stubs/ScoreProcessor.cs @@ -10,18 +10,16 @@ namespace Osu.Stubs; /// Original: osu.GameplayElements.Scoring.Processors.ScoreProcessor /// b20240123: #=zBbxc56nwToQ2q_6LjFIHXSZoq$I8pyNIyxBZVi76rJHE /// -[UsedImplicitly] -public class ScoreProcessor +[PublicAPI] +public static class ScoreProcessor { /// /// Original: Reset(bool storeStatistics) /// b20240123: #=z6nCyHQk= /// - [UsedImplicitly] - public static readonly LazyMethod Clear = new( - "ScoreProcessor#Clear(...)", - new[] - { + public static readonly LazyMethod Clear = LazyMethod.ByPartialSignature( + "osu.GameplayElements.Scoring.Processors.ScoreProcessor::Clear(bool)", + [ Ldc_R8, Stfld, Ldarg_0, @@ -31,16 +29,15 @@ public class ScoreProcessor Ldfld, Callvirt, Ret, - } + ] ); /// /// Original: Add(ScoreChange change) /// b20240123: #=zJdXS36o= /// - [UsedImplicitly] - public static readonly LazyMethod AddScoreChange = new( - "ScoreProcessor#Add(...)", + public static readonly LazyMethod AddScoreChange = LazyMethod.ByPartialSignature( + "osu.GameplayElements.Scoring.Processors.ScoreProcessor::Add(ScoreChange)", new[] { Brfalse_S, @@ -63,14 +60,10 @@ public class ScoreProcessor /// Original: MaximumCombo /// b20240123: #=zqaALxNP6RgRC /// - [UsedImplicitly] public static readonly LazyField MaximumCombo = new( - "ScoreProcessor#MaximumCombo", - () => - { - return AddScoreChange.Reference.DeclaringType! - .GetFields(BindingFlags.Instance | BindingFlags.NonPublic) - .First(field => field.FieldType == typeof(int) && field.IsFamily); // protected int - } + "osu.GameplayElements.Scoring.Processors.ScoreProcessor::MaximumCombo", + () => AddScoreChange.Reference.DeclaringType! + .GetFields(BindingFlags.Instance | BindingFlags.NonPublic) + .First(field => field.IsFamily && field.FieldType == typeof(int)) // checks for "protected int" ); } \ No newline at end of file diff --git a/Osu.Stubs/SkinManager.cs b/Osu.Stubs/SkinManager.cs index 5efe97c..5fb78f4 100644 --- a/Osu.Stubs/SkinManager.cs +++ b/Osu.Stubs/SkinManager.cs @@ -1,4 +1,3 @@ -using System; using System.Linq; using HarmonyLib; using JetBrains.Annotations; @@ -7,22 +6,25 @@ namespace Osu.Stubs; -/// -/// Original: osu.Graphics.Skinning.SkinManager -/// b20240123: #=zdwZLyAQXwqtPhTfOQ$e2PRLm39DcCX13EA== -/// -[UsedImplicitly] +[PublicAPI] public static class SkinManager { + /// + /// Original: osu.Graphics.Skinning.SkinManager + /// b20240123: #=zdwZLyAQXwqtPhTfOQ$e2PRLm39DcCX13EA== + /// + public static readonly LazyType Class = new( + "osu.Graphics.Skinning.SkinManager", + () => GetUseNewLayout!.Reference.DeclaringType! + ); + /// /// Original: get_UseNewLayout() /// b20240123: #=zOwgqVurLFLwR /// - [UsedImplicitly] - public static readonly LazyMethod GetUseNewLayout = new( - "SkinManager#get_UseNewLayout()", - new[] - { + public static readonly LazyMethod GetUseNewLayout = LazyMethod.ByPartialSignature( + "osu.Graphics.Skinning.SkinManager::get_UseNewLayout()", + [ Ldsfld, Brfalse_S, Call, @@ -31,21 +33,17 @@ public static class SkinManager Brfalse_S, Ldsfld, Ldfld, - } + ] ); /// /// Original: Current /// b20240123: #=zUzFTHbU= /// - [UsedImplicitly] public static readonly LazyField Current = new( "SkinManager#Current", // There is two fields with type SkinOsu; Current and CurrentUserSkin in that order - () => RuntimeType.GetDeclaredFields() - .First(f => f.FieldType == SkinOsu.RuntimeType) + () => Class.Reference.GetDeclaredFields() + .First(f => f.FieldType == SkinOsu.Class.Reference) ); - - [UsedImplicitly] - public static Type RuntimeType = GetUseNewLayout.Reference.DeclaringType!; } \ No newline at end of file diff --git a/Osu.Stubs/SkinOsu.cs b/Osu.Stubs/SkinOsu.cs index 6b4580a..58aeb86 100644 --- a/Osu.Stubs/SkinOsu.cs +++ b/Osu.Stubs/SkinOsu.cs @@ -1,27 +1,28 @@ -using System; using HarmonyLib; using JetBrains.Annotations; -using Osu.Utils; using Osu.Utils.Lazy; namespace Osu.Stubs; /// -/// Original: osu.Graphics.Skinning.OsuSkin -/// b20240123: osu.Graphics.Skinning.OsuSkin -/// Most names are present because of this class is [Serializable]. +/// Most names are present because this class is [Serializable]. /// -[UsedImplicitly] +[PublicAPI] public static class SkinOsu { + /// + /// Original: osu.Graphics.Skinning.SkinOsu + /// b20240123: osu.Graphics.Skinning.SkinOsu + /// + public static readonly LazyType Class = LazyType.ByName("osu.Graphics.Skinning.SkinOsu"); + /// /// Original: FontScoreOverlap /// b20240123: FontScoreOverlap /// - [UsedImplicitly] public static readonly LazyField FontScoreOverlap = new( - "OsuSkin#FontScoreOverlap", - () => RuntimeType.GetDeclaredFields() + "osu.Graphics.Skinning.SkinOsu::FontScoreOverlap", + () => Class.Reference.GetDeclaredFields() .Find(f => f.Name == "FontScoreOverlap") ); @@ -29,13 +30,9 @@ public static class SkinOsu /// Original: FontScore /// b20240123: FontScore /// - [UsedImplicitly] public static readonly LazyField FontScore = new( - "OsuSkin#FontScore", - () => RuntimeType.GetDeclaredFields() + "osu.Graphics.Skinning.SkinOsu::FontScore", + () => Class.Reference.GetDeclaredFields() .Find(f => f.Name == "FontScore") ); - - [UsedImplicitly] - public static Type RuntimeType => OsuAssembly.GetType("osu.Graphics.Skinning.SkinOsu"); } \ No newline at end of file diff --git a/Osu.Stubs/SongSelection.cs b/Osu.Stubs/SongSelection.cs index ae1d6b1..512a2d1 100644 --- a/Osu.Stubs/SongSelection.cs +++ b/Osu.Stubs/SongSelection.cs @@ -12,17 +12,16 @@ namespace Osu.Stubs; /// Original: osu.GameModes.Select.SongSelection /// b20240102.2: #=zKgaD0lVGl2RcuL9z0qvnoUGLjD870$Ll1w== /// -[UsedImplicitly] +[PublicAPI] public static class SongSelection { /// /// Original: chooseBestSortMode(TreeGroupMode mode) /// b20240102.2: #=zWDJY2KbbLKhn7OSo1w== /// - public static readonly LazyMethod ChoseBestSortMode = new( - "SongSelection#chooseBestSortMode", - new[] - { + public static readonly LazyMethod ChoseBestSortMode = LazyMethod.ByPartialSignature( + "osu.GameModes.Select.SongSelection::chooseBestSortMode", + [ Ldfld, Ldfld, Ldloc_0, @@ -33,18 +32,16 @@ public static class SongSelection Box, Ldc_I4_1, Callvirt, - } + ] ); /// /// Original: beatmapTreeManager_OnRightClicked(object sender, BeatmapTreeItem item) /// b20240102.2: #=zAmaE6G1Q0ysoWbGTpb40gD4dZN45 /// - [UsedImplicitly] - public static readonly LazyMethod BeatmapTreeManagerOnRightClicked = new( - "SongSelection#beatmapTreeManager_OnRightClicked", - new[] - { + public static readonly LazyMethod BeatmapTreeManagerOnRightClicked = LazyMethod.ByPartialSignature( + "osu.GameModes.Select.SongSelection::beatmapTreeManager_OnRightClicked", + [ Ldarg_2, Isinst, Brfalse_S, @@ -54,16 +51,15 @@ public static class SongSelection Callvirt, Ldc_I4_S, Bne_Un_S, - } + ] ); /// /// Original: beatmapTreeManager of type /// b20240102.2: #=zj0IgvXxTqseooUEFmQ== /// - [UsedImplicitly] public static readonly LazyField BeatmapTreeManager = new( - "SongSelection#beatmapTreeManager", + "osu.GameModes.Select.SongSelection::beatmapTreeManager", () => FindReferences().BeatmapTreeManager ); diff --git a/Osu.Stubs/SpriteManager.cs b/Osu.Stubs/SpriteManager.cs index 07f0e32..88ec354 100644 --- a/Osu.Stubs/SpriteManager.cs +++ b/Osu.Stubs/SpriteManager.cs @@ -1,26 +1,28 @@ -using System; using JetBrains.Annotations; using Osu.Utils.Lazy; using static System.Reflection.Emit.OpCodes; namespace Osu.Stubs; -/// -/// Original: osu.Graphics.Sprites.SpriteManager -/// b20240123: #=zaNwi4uR9iF1HqyG9UwEA2vinmw4mMbeYaQ== -/// -[UsedImplicitly] -public class SpriteManager +[PublicAPI] +public static class SpriteManager { + /// + /// Original: osu.Graphics.Sprites.SpriteManager + /// b20240123: #=zaNwi4uR9iF1HqyG9UwEA2vinmw4mMbeYaQ== + /// + public static readonly LazyType Class = new( + "osu.Graphics.Sprites.SpriteManager", + () => Add!.Reference.DeclaringType! + ); + /// /// Original: Add(pDrawable p) /// b20240123: #=zJdXS36o= /// - [UsedImplicitly] - public static readonly LazyMethod Add = new( - "SpriteManager#Add(...)", - new[] - { + public static readonly LazyMethod Add = LazyMethod.ByPartialSignature( + "osu.Graphics.Sprites.SpriteManager::Add(pDrawable)", + [ Not, Ldarg_1, Callvirt, @@ -30,9 +32,6 @@ public class SpriteManager Ldloc_1, Call, Endfinally, - } + ] ); - - [UsedImplicitly] - public static Type RuntimeType => Add.Reference.DeclaringType!; } \ No newline at end of file diff --git a/Osu.Stubs/Vector2.cs b/Osu.Stubs/Vector2.cs index 35b2ea8..7d04b77 100644 --- a/Osu.Stubs/Vector2.cs +++ b/Osu.Stubs/Vector2.cs @@ -1,49 +1,45 @@ -using System; using JetBrains.Annotations; -using Osu.Utils; using Osu.Utils.Lazy; namespace Osu.Stubs; /// -/// Original: Microsoft.Xna.Framework.Vector2 -/// b20240123: Microsoft.Xna.Framework.Vector2 -/// Most names are present because of this class is [Serializable]. +/// Most names are present because this class is [Serializable]. /// -[UsedImplicitly] -public class Vector2 +[PublicAPI] +public static class Vector2 { - [UsedImplicitly] - public static Type RuntimeType = OsuAssembly.GetType("Microsoft.Xna.Framework.Vector2"); + /// + /// Original: Microsoft.Xna.Framework.Vector2 + /// b20240123: Microsoft.Xna.Framework.Vector2 + /// + public static LazyType Class = LazyType.ByName("Microsoft.Xna.Framework.Vector2"); /// /// Constructor that creates a Vector2 from two distinct float values. /// Original: Vector2(float, float) /// b20240123: Vector2(Single, Single) /// - [UsedImplicitly] - public static readonly LazyMethod Constructor = new( - "Vector2#(float, float)", - () => RuntimeType.GetConstructor([typeof(float), typeof(float)]) + public static readonly LazyConstructor Constructor = new( + "Microsoft.Xna.Framework.Vector2::Vector2(float, float)", + () => Class.Reference.GetConstructor([typeof(float), typeof(float)]) ); /// /// Original: X /// b20240123: X /// - [UsedImplicitly] public static readonly LazyField X = new( - "Vector2#X", - () => RuntimeType.GetField("X") + "Microsoft.Xna.Framework.Vector2::X", + () => Class.Reference.GetField("X") ); /// /// Original: Y /// b20240123: Y /// - [UsedImplicitly] public static readonly LazyField Y = new( - "Vector2#Y", - () => RuntimeType.GetField("Y") + "Microsoft.Xna.Framework.Vector2::Y", + () => Class.Reference.GetField("Y") ); } \ No newline at end of file diff --git a/Osu.Stubs/Wrappers/Clocks.cs b/Osu.Stubs/Wrappers/Clocks.cs index f348a09..249ffe7 100644 --- a/Osu.Stubs/Wrappers/Clocks.cs +++ b/Osu.Stubs/Wrappers/Clocks.cs @@ -1,4 +1,3 @@ -using System.Diagnostics.CodeAnalysis; using JetBrains.Annotations; namespace Osu.Stubs.Wrappers; @@ -6,9 +5,8 @@ namespace Osu.Stubs.Wrappers; /// /// Original: osu.Graphics.Sprites.Clocks /// -[UsedImplicitly] -[SuppressMessage("ReSharper", "UnusedMember.Global")] -public class Clocks +[PublicAPI] +public static class Clocks { public const int Game = 0; public const int Audio = 0; diff --git a/Osu.Stubs/Wrappers/Color.cs b/Osu.Stubs/Wrappers/Color.cs index 79d0017..0d099cd 100644 --- a/Osu.Stubs/Wrappers/Color.cs +++ b/Osu.Stubs/Wrappers/Color.cs @@ -1,3 +1,4 @@ +using JetBrains.Annotations; using Osu.Utils; namespace Osu.Stubs.Wrappers; @@ -7,6 +8,7 @@ namespace Osu.Stubs.Wrappers; /// /// Original: Microsoft.Xna.Framework.Graphics.Color /// +[PublicAPI] public static class Color { public static readonly XnaColor Red = GetColor("Red"); diff --git a/Osu.Stubs/Wrappers/Fields.cs b/Osu.Stubs/Wrappers/Fields.cs index ce8cde5..6b19d24 100644 --- a/Osu.Stubs/Wrappers/Fields.cs +++ b/Osu.Stubs/Wrappers/Fields.cs @@ -1,4 +1,3 @@ -using System.Diagnostics.CodeAnalysis; using JetBrains.Annotations; namespace Osu.Stubs.Wrappers; @@ -6,8 +5,7 @@ namespace Osu.Stubs.Wrappers; /// /// Original: osu.Graphics.Sprites.Fields /// -[UsedImplicitly] -[SuppressMessage("ReSharper", "UnusedMember.Global")] +[PublicAPI] public class Fields { public const int GameField = 1; diff --git a/Osu.Stubs/Wrappers/Notifications.cs b/Osu.Stubs/Wrappers/Notifications.cs index 51c3b96..4a8e553 100644 --- a/Osu.Stubs/Wrappers/Notifications.cs +++ b/Osu.Stubs/Wrappers/Notifications.cs @@ -3,7 +3,7 @@ namespace Osu.Stubs.Wrappers; -[UsedImplicitly] +[PublicAPI] public static class Notifications { /// @@ -15,7 +15,6 @@ public static class Notifications /// Milliseconds to show the notification for after the user sees the notification for the first time. /// /// Notification onclick action. - [UsedImplicitly] public static void ShowMessage( string message, NotificationColor color, diff --git a/Osu.Stubs/Wrappers/Origins.cs b/Osu.Stubs/Wrappers/Origins.cs index f40e842..8579e17 100644 --- a/Osu.Stubs/Wrappers/Origins.cs +++ b/Osu.Stubs/Wrappers/Origins.cs @@ -1,4 +1,3 @@ -using System.Diagnostics.CodeAnalysis; using JetBrains.Annotations; namespace Osu.Stubs.Wrappers; @@ -6,9 +5,8 @@ namespace Osu.Stubs.Wrappers; /// /// Original: osu.Graphics.Sprites.Origins /// -[UsedImplicitly] -[SuppressMessage("ReSharper", "UnusedMember.Global")] -public class Origins +[PublicAPI] +public static class Origins { public const int TopLeft = 0; public const int Centre = 1; diff --git a/Osu.Stubs/Wrappers/SkinSource.cs b/Osu.Stubs/Wrappers/SkinSource.cs index 10b833d..6b294e3 100644 --- a/Osu.Stubs/Wrappers/SkinSource.cs +++ b/Osu.Stubs/Wrappers/SkinSource.cs @@ -1,4 +1,3 @@ -using System.Diagnostics.CodeAnalysis; using JetBrains.Annotations; namespace Osu.Stubs.Wrappers; @@ -6,10 +5,8 @@ namespace Osu.Stubs.Wrappers; /// /// Original: osu.Graphics.Skinning.SkinSource /// -[UsedImplicitly] -[SuppressMessage("ReSharper", "UnusedMember.Global")] -[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] -public class SkinSource +[PublicAPI] +public static class SkinSource { public const int None = 0; public const int Osu = 1; diff --git a/Osu.Stubs/Wrappers/VoidDelegate.cs b/Osu.Stubs/Wrappers/VoidDelegate.cs index c928b14..8ff9b11 100644 --- a/Osu.Stubs/Wrappers/VoidDelegate.cs +++ b/Osu.Stubs/Wrappers/VoidDelegate.cs @@ -1,11 +1,13 @@ using System; +using JetBrains.Annotations; namespace Osu.Stubs.Wrappers; /// /// Original: osu_common.Helpers.VoidDelegate /// -internal static class VoidDelegate +[PublicAPI] +public static class VoidDelegate { /// /// Transforms an into a VoidDelegate from osu!. diff --git a/Osu.Stubs/pDrawable.cs b/Osu.Stubs/pDrawable.cs index 99be1d3..1e33876 100644 --- a/Osu.Stubs/pDrawable.cs +++ b/Osu.Stubs/pDrawable.cs @@ -1,4 +1,3 @@ -using System; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; @@ -10,23 +9,26 @@ namespace Osu.Stubs; -/// -/// Original: osu.Graphics.pDrawable -/// b20240123: #=zB63SnFDTnRqYMKLlscCRpu_ww$IG -/// -[UsedImplicitly] +[PublicAPI] [SuppressMessage("ReSharper", "InconsistentNaming")] public static class pDrawable { + /// + /// Original: osu.Graphics.pDrawable + /// b20240123: #=zB63SnFDTnRqYMKLlscCRpu_ww$IG + /// + public static LazyType Class = new( + "osu.Graphics.pDrawable", + () => ScaleTo!.Reference.DeclaringType! + ); + /// /// Original: Click(bool confirmed) /// b20240123: #=zcJ6mazw= /// - [UsedImplicitly] - public static readonly LazyMethod Click = new( - "pDrawable#Click(...)", - new[] - { + public static readonly LazyMethod Click = LazyMethod.ByPartialSignature( + "osu.Graphics.pDrawable::Click(bool)", + [ Ret, Ldarg_0, Ldfld, @@ -36,18 +38,16 @@ public static class pDrawable Brfalse_S, Ldarg_0, Ldfld, - } + ] ); /// /// Original: ScaleTo(float final, int duration, EasingTypes easing) /// b20240123: #=zVyF2njk= /// - [UsedImplicitly] - public static readonly LazyMethod ScaleTo = new( - "pDrawable#ScaleTo(...)", - new[] - { + public static readonly LazyMethod ScaleTo = LazyMethod.ByPartialSignature( + "osu.Graphics.pDrawable::ScaleTo(float, int, EasingTypes)", + [ Ldc_I4_4, // TransformationType.Scale Ldarg_0, Ldfld, // this.Scale @@ -59,20 +59,19 @@ public static class pDrawable Ldsfld, Conv_I4, Sub, - } + ] ); /// /// Original: Position /// b20240123: #=ztOn8vDI= + /// There is 3 fields with a type of Vector2 on this class. The middle one is Position. /// - [UsedImplicitly] public static readonly LazyField Position = new( - "pDrawable#Position", - // There is 3 fields with a type of Vector2 on this class. The middle one is Position. - () => RuntimeType.GetDeclaredFields().AsEnumerable() + "osu.Graphics.pDrawable::Position", + () => Class.Reference.GetDeclaredFields().AsEnumerable() .Reverse() - .Where(field => field.FieldType == Vector2.RuntimeType) + .Where(field => field.FieldType == Vector2.Class.Reference) .Skip(1) .First() ); @@ -81,9 +80,8 @@ public static class pDrawable /// Original: Scale /// b20240123: #=zmbpQ79A= /// - [UsedImplicitly] public static readonly LazyField Scale = new( - "pDrawable#Scale", + "osu.Graphics.pDrawable::Scale", () => { // The last "ldsfld float" in the ScaleTo method is a reference to this.Scale @@ -95,7 +93,4 @@ public static class pDrawable return (FieldInfo)instruction.Operand; } ); - - [UsedImplicitly] - public static Type RuntimeType => ScaleTo.Reference.DeclaringType!; } \ No newline at end of file diff --git a/Osu.Stubs/pSpriteText.cs b/Osu.Stubs/pSpriteText.cs index f70389e..d9af203 100644 --- a/Osu.Stubs/pSpriteText.cs +++ b/Osu.Stubs/pSpriteText.cs @@ -1,4 +1,3 @@ -using System; using System.Diagnostics.CodeAnalysis; using System.Linq; using HarmonyLib; @@ -8,14 +7,19 @@ namespace Osu.Stubs; -/// -/// Original: osu.Graphics.Sprites.pSpriteText -/// b20240123: #=zckAvJzCXrz1Buo3s_qsuXqlnRaWrA6R8Iw== -/// -[UsedImplicitly] +[PublicAPI] [SuppressMessage("ReSharper", "InconsistentNaming")] -public class pSpriteText +public static class pSpriteText { + /// + /// Original: osu.Graphics.Sprites.pSpriteText + /// b20240123: #=zckAvJzCXrz1Buo3s_qsuXqlnRaWrA6R8Iw== + /// + public static readonly LazyType Class = new( + "osu.Graphics.Sprites.pSpriteText", + () => Constructor!.Reference.DeclaringType! + ); + /// /// Original: /// @@ -25,10 +29,9 @@ public class pSpriteText /// /// b20240123: /// - public static readonly LazyMethod Constructor = new( - "pSpriteText#(...)", - new[] - { + public static readonly LazyConstructor Constructor = LazyConstructor.ByPartialSignature( + "osu.Graphics.Sprites.pSpriteText::pSpriteText(...)", + [ Ldarg_2, Ldarg_S, Call, @@ -41,8 +44,7 @@ public class pSpriteText Ldarg_0, Ldfld, Ldloca_S, - }, - true + ] ); /// @@ -50,11 +52,9 @@ public class pSpriteText /// Original: MeasureText() /// b20240123: #=z2Klmy0o= /// - [UsedImplicitly] - public static readonly LazyMethod MeasureText = new( - "pSpriteText#MeasureText()", - new[] - { + public static readonly LazyMethod MeasureText = LazyMethod.ByPartialSignature( + "osu.Graphics.Sprites.pSpriteText::MeasureText()", + [ Ldarg_0, Ldfld, Brfalse_S, @@ -64,20 +64,16 @@ public class pSpriteText Ldarg_0, Ldfld, Ret, - } + ] ); /// /// Original: TextConstantSpacing /// b20240123: #=zWUFISsTiUxtU /// - [UsedImplicitly] public static readonly LazyField TextConstantSpacing = new( - "pSpriteText#TextConstantSpacing", - () => RuntimeType.GetDeclaredFields() + "osu.Graphics.Sprites.pSpriteText::TextConstantSpacing", + () => Class.Reference.GetDeclaredFields() .Single(field => field.FieldType == typeof(bool)) ); - - [UsedImplicitly] - public static Type RuntimeType => Constructor.Reference.DeclaringType!; } \ No newline at end of file diff --git a/Osu.Stubs/pText.cs b/Osu.Stubs/pText.cs index 6d56887..699153d 100644 --- a/Osu.Stubs/pText.cs +++ b/Osu.Stubs/pText.cs @@ -10,19 +10,17 @@ namespace Osu.Stubs; /// Original: osu.Graphics.Sprites.pText /// b20240123: #=zrPwR$Vhf84Bn$SnxyFkpTe8J9fMu /// -[UsedImplicitly] +[PublicAPI] [SuppressMessage("ReSharper", "InconsistentNaming")] -public class pText +public static class pText { /// /// Original: set_Text(string value) (property setter) /// b20240123: #=zeWQSmtI= /// - [UsedImplicitly] - public static readonly LazyMethod SetText = new( - "pText#set_Text(...)", - new[] - { + public static readonly LazyMethod SetText = LazyMethod.BySignature( + "osu.Graphics.Sprites.pText::set_Text(string)", + [ Ldarg_0, Ldfld, Ldarg_1, @@ -36,7 +34,6 @@ public class pText Ldarg_1, Stfld, Ret, - }, - entireMethod: true + ] ); } \ No newline at end of file diff --git a/Osu.Utils/Extensions/ArrayExtensions.cs b/Osu.Utils/Extensions/ArrayExtensions.cs index 4b63cea..8fad204 100644 --- a/Osu.Utils/Extensions/ArrayExtensions.cs +++ b/Osu.Utils/Extensions/ArrayExtensions.cs @@ -1,5 +1,3 @@ -using JetBrains.Annotations; - namespace Osu.Utils.Extensions; public static class ArrayExtensions @@ -12,7 +10,6 @@ public static class ArrayExtensions /// Alternative default value /// A non-null type inside the array. /// The value or default value - [UsedImplicitly] public static T? GetOrDefault(this T[] array, int index, T? defaultValue) => index < array.Length ? array[index] : defaultValue; @@ -22,7 +19,6 @@ public static class ArrayExtensions /// Source array /// Amount of times to copy the items. /// A new array with shallow copied items. - [UsedImplicitly] public static T[] Duplicate(this T[] array, int times) { var newArray = new T[array.Length * times]; diff --git a/Osu.Utils/Extensions/TranspilerExtensions.cs b/Osu.Utils/Extensions/TranspilerExtensions.cs index 534d503..1343255 100644 --- a/Osu.Utils/Extensions/TranspilerExtensions.cs +++ b/Osu.Utils/Extensions/TranspilerExtensions.cs @@ -3,14 +3,12 @@ using System.Linq; using System.Reflection.Emit; using HarmonyLib; -using JetBrains.Annotations; namespace Osu.Utils.Extensions; /// /// Utilities for patching bytecode through the usage of . /// -[UsedImplicitly] public static class TranspilerExtensions { /// @@ -19,7 +17,6 @@ public static class TranspilerExtensions /// The input instructions to patch, mainly coming from a HarmonyTranspiler. /// The signature to find within the instructions based on IL opcodes. /// The instructions to insert after a signature match. - [UsedImplicitly] public static IEnumerable InsertAfterSignature( this IEnumerable instructions, IReadOnlyList signature, @@ -62,7 +59,6 @@ public static IEnumerable InsertAfterSignature( /// The signature to find within the instructions based on IL opcodes. /// The instructions to insert before a signature match. /// Match all instances of a signature. - [UsedImplicitly] public static IEnumerable InsertBeforeSignature( this IEnumerable instructions, IReadOnlyList signature, @@ -112,7 +108,6 @@ public static IEnumerable InsertBeforeSignature( /// The input instructions to patch, mainly coming from a HarmonyTranspiler. /// The signature to find within the instructions based on IL opcodes. /// Match all instances of a signature. - [UsedImplicitly] public static IEnumerable NoopSignature( this IEnumerable instructions, IReadOnlyList signature, @@ -166,7 +161,6 @@ public static IEnumerable NoopSignature( /// The signature to find within the instructions based on IL opcodes. /// The amount of instructions to replace after the signature. /// Match all instances of a signature. - [UsedImplicitly] public static IEnumerable NoopAfterSignature( this IEnumerable instructions, OpCode[] signature, diff --git a/Osu.Utils/Lazy/LazyConstructor.cs b/Osu.Utils/Lazy/LazyConstructor.cs new file mode 100644 index 0000000..c239aba --- /dev/null +++ b/Osu.Utils/Lazy/LazyConstructor.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Reflection.Emit; +using JetBrains.Annotations; +using Osu.Utils.IL; + +namespace Osu.Utils.Lazy; + +/// +/// A reference to a constructor that gets located at runtime and invoked reflectively. +/// +[PublicAPI] +public class LazyConstructor : LazyInfo +{ + private readonly Lazy _lazy; + + /// + /// Make a wrapper around Lazy for constructor. + /// + /// of type what this is returning. + /// The lazy action to run when the value is needed. + public LazyConstructor(string name, Func action) + { + Name = name; + _lazy = new Lazy(action); + } + + public override string Name { get; } + + public override ConstructorInfo Reference => GetReference(Name, _lazy); + + /// + /// Find if not already cached and reflectively invoke this constructor to create a new instance of a class. + /// + /// Parameters to pass to the constructor, if any. + public object Invoke(object?[]? parameters = null) => + Reference.Invoke(parameters); + + /// + public static LazyConstructor BySignature(string name, IReadOnlyList signature) => + new(name, () => OpCodeMatcher.FindConstructorBySignature(signature, true)!); + + /// + public static LazyConstructor ByPartialSignature(string name, IReadOnlyList signature) => + new(name, () => OpCodeMatcher.FindConstructorBySignature(signature)!); +} \ No newline at end of file diff --git a/Osu.Utils/Lazy/LazyField.cs b/Osu.Utils/Lazy/LazyField.cs index 3bdc326..358a883 100644 --- a/Osu.Utils/Lazy/LazyField.cs +++ b/Osu.Utils/Lazy/LazyField.cs @@ -8,35 +8,25 @@ namespace Osu.Utils.Lazy; /// A reference to a field that gets located at runtime and interacted with reflectively. /// /// The type this field should be treated as. -public class LazyField +[PublicAPI] +public class LazyField : LazyInfo { - private readonly Lazy _lazy; - private readonly string _name; + private readonly Lazy _lazy; /// /// A wrapper around Lazy for fields. /// /// Class#Field name of what this signature is matching. /// The lazy action to run when the value is needed. - public LazyField(string name, Func action) + public LazyField(string name, Func action) { - _name = name; - _lazy = new Lazy(action); + Name = name; + _lazy = new Lazy(action); } - [UsedImplicitly] - public FieldInfo Reference - { - get - { - var value = _lazy.Value; - - if (value == null) - throw new Exception($"Field {_name} was not found"); + public override string Name { get; } - return value; - } - } + public override FieldInfo Reference => GetReference>(Name, _lazy); /// /// Gets the current value of this field. diff --git a/Osu.Utils/Lazy/LazyInfo.cs b/Osu.Utils/Lazy/LazyInfo.cs new file mode 100644 index 0000000..8b200ca --- /dev/null +++ b/Osu.Utils/Lazy/LazyInfo.cs @@ -0,0 +1,56 @@ +using System; +using System.Reflection; +using JetBrains.Annotations; + +namespace Osu.Utils.Lazy; + +/// +/// A base type for finding stuff with the use of reflection lazily. +/// +[PublicAPI] +public abstract class LazyInfo where T : MemberInfo +{ + /// + /// The assumed original human readable fully qualified name. + /// Example: System.Console::WriteLine(string message) + /// + public abstract string Name { get; } + + /// + /// The runtime type name of the resolved reflective type. + /// + public string RuntimeName => Reference.Name; + + /// + /// Reference to the reflective target type. + /// This retrieves or finds the type when called. + /// + public abstract T Reference { get; } + + /// + /// Gets the result of a otherwise throws appropriate exceptions. + /// + /// + /// + /// + /// The lazy to invoke. + /// Subtype of LazyInfo. + /// The Lazy's result value + protected static T GetReference(string name, Lazy lazy) + where TL : LazyInfo + { + try + { + var value = lazy.Value; + if (value != null) return value; + + var lazyName = typeof(TL).Name; + throw new Exception($"{lazyName} result cannot be null!"); + } + catch (Exception e) + { + var lazyName = typeof(TL).Name; + throw new AggregateException($"Failed to run {lazyName} {name}", e); + } + } +} \ No newline at end of file diff --git a/Osu.Utils/Lazy/LazyMethod.cs b/Osu.Utils/Lazy/LazyMethod.cs index 93a7a73..cc54f81 100644 --- a/Osu.Utils/Lazy/LazyMethod.cs +++ b/Osu.Utils/Lazy/LazyMethod.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Reflection; using System.Reflection.Emit; +using JetBrains.Annotations; using Osu.Utils.IL; namespace Osu.Utils.Lazy; @@ -9,51 +10,25 @@ namespace Osu.Utils.Lazy; /// /// A reference to a method that gets located at runtime and invoked reflectively. /// -public class LazyMethod +[PublicAPI] +public class LazyMethod : LazyInfo { - private readonly Lazy _lazy; - private readonly string _name; - - /// - /// A lazy method signature matcher - /// - /// Class#Method name of what this signature is matching. - /// Sequential opcodes to search the target method with. - /// Whether this method is a constructor. - /// Whether the signature is the entire method. - public LazyMethod(string name, IReadOnlyList signature, - bool isConstructor = false, - bool entireMethod = false) - { - _name = name; - _lazy = new Lazy(() => isConstructor - ? OpCodeMatcher.FindConstructorBySignature(signature, entireMethod) - : OpCodeMatcher.FindMethodBySignature(signature, entireMethod)); - } + private readonly Lazy _lazy; /// /// Make a wrapper around Lazy for methods. /// - /// Class#Method name of what this is returning. + /// of what type this is returning. /// The lazy action to run when the value is needed. - public LazyMethod(string name, Func action) + public LazyMethod(string name, Func action) { - _name = name; - _lazy = new Lazy(action); + Name = name; + _lazy = new Lazy(action); } - public MethodBase Reference - { - get - { - var value = _lazy.Value; - - if (value == null) - throw new Exception($"Method was not found for signature of {_name}"); + public override string Name { get; } - return value; - } - } + public override MethodInfo Reference => GetReference(Name, _lazy); /// /// Find if not already cached and reflectively invoke this method. Does not return any value. @@ -62,6 +37,28 @@ public MethodBase Reference /// Parameters to pass to the method, if any. public void Invoke(object? instance = null, object?[]? parameters = null) => Reference.Invoke(instance, parameters); + + /// + /// A lazy method opcode opcode matcher. + /// Searches every method to see if this is the entire method's bytecode. + /// + /// + /// + /// + /// Sequential opcodes to compare the target method's bytecode with. + public static LazyMethod BySignature(string name, IReadOnlyList signature) => + new(name, () => OpCodeMatcher.FindMethodBySignature(signature, true)!); + + /// + /// A lazy method opcode partial opcode matcher. + /// Searches every method to see if this is located in the method's bytecode. + /// + /// + /// + /// + /// Sequential opcodes to search the target method with. + public static LazyMethod ByPartialSignature(string name, IReadOnlyList signature) => + new(name, () => OpCodeMatcher.FindMethodBySignature(signature)!); } // ReSharper disable once InconsistentNaming @@ -69,19 +66,11 @@ public void Invoke(object? instance = null, object?[]? parameters = null) => /// A reference to a method that gets located at runtime and invoked reflectively. /// /// A type that the return value of this method can be casted to. +[PublicAPI] public class LazyMethod : LazyMethod { /// - public LazyMethod(string name, IReadOnlyList signature, - bool isConstructor = false, - bool entireMethod = false) - : base(name, signature, isConstructor, entireMethod) - { - } - - /// - public LazyMethod(string name, Func action) - : base(name, action) + public LazyMethod(string name, Func action) : base(name, action) { } @@ -98,4 +87,12 @@ public LazyMethod(string name, Func action) /// The return type of this method to cast to. public T Invoke(object? instance = null, object?[]? parameters = null) => (T)Reference.Invoke(instance, parameters); + + /// + public new static LazyMethod BySignature(string name, IReadOnlyList signature) => + new(name, () => OpCodeMatcher.FindMethodBySignature(signature, true)!); + + /// + public new static LazyMethod ByPartialSignature(string name, IReadOnlyList signature) => + new(name, () => OpCodeMatcher.FindMethodBySignature(signature)!); } \ No newline at end of file diff --git a/Osu.Utils/Lazy/LazyType.cs b/Osu.Utils/Lazy/LazyType.cs new file mode 100644 index 0000000..6b1e40e --- /dev/null +++ b/Osu.Utils/Lazy/LazyType.cs @@ -0,0 +1,41 @@ +using System; +using JetBrains.Annotations; + +namespace Osu.Utils.Lazy; + +/// +/// A reference to a Type that gets located at runtime and interacted with reflectively. +/// +[PublicAPI] +public class LazyType : LazyInfo +{ + private readonly Lazy _lazy; + + /// + /// A wrapper around Lazy for Types. + /// + /// The qualified name of this type. + /// The lazy action to run when the type is needed. + public LazyType(string name, Func action) + { + Name = name; + _lazy = new Lazy(action); + } + + public override string Name { get; } + + public override Type Reference => GetReference(Name, _lazy); + + /// + /// Finds a class based on it's full name including namespace. + /// + /// + /// + /// + /// + /// The full runtime name to search for, including any namespaces. If this isn't different from + /// then it can be omitted or null. + /// + public static LazyType ByName(string originalName, string? runtimeName = null) => + new(originalName, () => OsuAssembly.GetType(runtimeName ?? originalName)); +} \ No newline at end of file