@ -0,0 +1,246 @@ | |||
# Remove the line below if you want to inherit .editorconfig settings from higher directories | |||
root = true | |||
# C# files | |||
[*.cs] | |||
#### Core EditorConfig Options #### | |||
# Indentation and spacing | |||
indent_size = 4 | |||
indent_style = space | |||
tab_width = 4 | |||
# New line preferences | |||
end_of_line = crlf | |||
insert_final_newline = false | |||
#### .NET Coding Conventions #### | |||
# Organize usings | |||
dotnet_separate_import_directive_groups = false | |||
dotnet_sort_system_directives_first = false | |||
file_header_template = unset | |||
# this. and Me. preferences | |||
dotnet_style_qualification_for_event = false | |||
dotnet_style_qualification_for_field = false | |||
dotnet_style_qualification_for_method = false | |||
dotnet_style_qualification_for_property = false | |||
# Language keywords vs BCL types preferences | |||
dotnet_style_predefined_type_for_locals_parameters_members = true | |||
dotnet_style_predefined_type_for_member_access = true | |||
# Parentheses preferences | |||
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:warning | |||
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:warning | |||
dotnet_style_parentheses_in_other_operators = never_if_unnecessary:suggestion | |||
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:warning | |||
# Modifier preferences | |||
dotnet_style_require_accessibility_modifiers = for_non_interface_members | |||
# Expression-level preferences | |||
dotnet_style_coalesce_expression = true | |||
dotnet_style_collection_initializer = true | |||
dotnet_style_explicit_tuple_names = true | |||
dotnet_style_namespace_match_folder = true | |||
dotnet_style_null_propagation = true | |||
dotnet_style_object_initializer = true | |||
dotnet_style_operator_placement_when_wrapping = beginning_of_line | |||
dotnet_style_prefer_auto_properties = true:warning | |||
dotnet_style_prefer_compound_assignment = true:warning | |||
dotnet_style_prefer_conditional_expression_over_assignment = true:warning | |||
dotnet_style_prefer_conditional_expression_over_return = true:warning | |||
dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed | |||
dotnet_style_prefer_inferred_anonymous_type_member_names = true | |||
dotnet_style_prefer_inferred_tuple_names = true | |||
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:warning | |||
dotnet_style_prefer_simplified_boolean_expressions = true:warning | |||
dotnet_style_prefer_simplified_interpolation = true | |||
# Field preferences | |||
dotnet_style_readonly_field = true | |||
# Parameter preferences | |||
dotnet_code_quality_unused_parameters = all | |||
# Suppression preferences | |||
dotnet_remove_unnecessary_suppression_exclusions = none | |||
# New line preferences | |||
dotnet_style_allow_multiple_blank_lines_experimental = true | |||
dotnet_style_allow_statement_immediately_after_block_experimental = true | |||
#### C# Coding Conventions #### | |||
# var preferences | |||
csharp_style_var_elsewhere = false | |||
csharp_style_var_for_built_in_types = false | |||
csharp_style_var_when_type_is_apparent = false | |||
# Expression-bodied members | |||
csharp_style_expression_bodied_accessors = true:silent | |||
csharp_style_expression_bodied_constructors = false:silent | |||
csharp_style_expression_bodied_indexers = true:silent | |||
csharp_style_expression_bodied_lambdas = true:suggestion | |||
csharp_style_expression_bodied_local_functions = when_on_single_line:suggestion | |||
csharp_style_expression_bodied_methods = false:silent | |||
csharp_style_expression_bodied_operators = false:silent | |||
csharp_style_expression_bodied_properties = true:silent | |||
# Pattern matching preferences | |||
csharp_style_pattern_matching_over_as_with_null_check = true | |||
csharp_style_pattern_matching_over_is_with_cast_check = true | |||
csharp_style_prefer_extended_property_pattern = true | |||
csharp_style_prefer_not_pattern = true:warning | |||
csharp_style_prefer_pattern_matching = true:warning | |||
csharp_style_prefer_switch_expression = true | |||
# Null-checking preferences | |||
csharp_style_conditional_delegate_call = true | |||
# Modifier preferences | |||
csharp_prefer_static_local_function = true:warning | |||
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async | |||
# Code-block preferences | |||
csharp_prefer_braces = true:silent | |||
csharp_prefer_simple_using_statement = true:suggestion | |||
csharp_style_namespace_declarations = block_scoped:silent | |||
csharp_style_prefer_method_group_conversion = true:silent | |||
csharp_style_prefer_top_level_statements = true:silent | |||
# Expression-level preferences | |||
csharp_prefer_simple_default_expression = true | |||
csharp_style_deconstructed_variable_declaration = true | |||
csharp_style_implicit_object_creation_when_type_is_apparent = true:warning | |||
csharp_style_inlined_variable_declaration = true | |||
csharp_style_prefer_index_operator = true:warning | |||
csharp_style_prefer_local_over_anonymous_function = true:warning | |||
csharp_style_prefer_null_check_over_type_check = true | |||
csharp_style_prefer_range_operator = true | |||
csharp_style_prefer_tuple_swap = true | |||
csharp_style_prefer_utf8_string_literals = true | |||
csharp_style_throw_expression = true | |||
csharp_style_unused_value_assignment_preference = discard_variable:warning | |||
csharp_style_unused_value_expression_statement_preference = discard_variable:warning | |||
# 'using' directive preferences | |||
csharp_using_directive_placement = outside_namespace:warning | |||
# New line preferences | |||
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true | |||
csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true | |||
csharp_style_allow_embedded_statements_on_same_line_experimental = true | |||
#### C# Formatting Rules #### | |||
# New line preferences | |||
csharp_new_line_before_catch = true | |||
csharp_new_line_before_else = true | |||
csharp_new_line_before_finally = true | |||
csharp_new_line_before_members_in_anonymous_types = true | |||
csharp_new_line_before_members_in_object_initializers = true | |||
csharp_new_line_before_open_brace = all | |||
csharp_new_line_between_query_expression_clauses = true | |||
# Indentation preferences | |||
csharp_indent_block_contents = true | |||
csharp_indent_braces = false | |||
csharp_indent_case_contents = true | |||
csharp_indent_case_contents_when_block = true | |||
csharp_indent_labels = one_less_than_current | |||
csharp_indent_switch_labels = true | |||
# Space preferences | |||
csharp_space_after_cast = false | |||
csharp_space_after_colon_in_inheritance_clause = true | |||
csharp_space_after_comma = true | |||
csharp_space_after_dot = false | |||
csharp_space_after_keywords_in_control_flow_statements = true | |||
csharp_space_after_semicolon_in_for_statement = true | |||
csharp_space_around_binary_operators = before_and_after | |||
csharp_space_around_declaration_statements = false | |||
csharp_space_before_colon_in_inheritance_clause = true | |||
csharp_space_before_comma = false | |||
csharp_space_before_dot = false | |||
csharp_space_before_open_square_brackets = false | |||
csharp_space_before_semicolon_in_for_statement = false | |||
csharp_space_between_empty_square_brackets = false | |||
csharp_space_between_method_call_empty_parameter_list_parentheses = false | |||
csharp_space_between_method_call_name_and_opening_parenthesis = false | |||
csharp_space_between_method_call_parameter_list_parentheses = false | |||
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false | |||
csharp_space_between_method_declaration_name_and_open_parenthesis = false | |||
csharp_space_between_method_declaration_parameter_list_parentheses = false | |||
csharp_space_between_parentheses = false | |||
csharp_space_between_square_brackets = false | |||
# Wrapping preferences | |||
csharp_preserve_single_line_blocks = true | |||
csharp_preserve_single_line_statements = true | |||
#### Naming styles #### | |||
# Naming rules | |||
dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion | |||
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface | |||
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i | |||
dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion | |||
dotnet_naming_rule.types_should_be_pascal_case.symbols = types | |||
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case | |||
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion | |||
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members | |||
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case | |||
# Symbol specifications | |||
dotnet_naming_symbols.interface.applicable_kinds = interface | |||
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected | |||
dotnet_naming_symbols.interface.required_modifiers = | |||
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum | |||
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected | |||
dotnet_naming_symbols.types.required_modifiers = | |||
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method | |||
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected | |||
dotnet_naming_symbols.non_field_members.required_modifiers = | |||
# Naming styles | |||
dotnet_naming_style.pascal_case.required_prefix = | |||
dotnet_naming_style.pascal_case.required_suffix = | |||
dotnet_naming_style.pascal_case.word_separator = | |||
dotnet_naming_style.pascal_case.capitalization = pascal_case | |||
dotnet_naming_style.begins_with_i.required_prefix = I | |||
dotnet_naming_style.begins_with_i.required_suffix = | |||
dotnet_naming_style.begins_with_i.word_separator = | |||
dotnet_naming_style.begins_with_i.capitalization = pascal_case | |||
[*.{cs,vb}] | |||
dotnet_style_operator_placement_when_wrapping = beginning_of_line | |||
tab_width = 4 | |||
indent_size = 4 | |||
end_of_line = crlf | |||
dotnet_style_coalesce_expression = true:suggestion | |||
dotnet_style_null_propagation = true:suggestion | |||
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:warning | |||
dotnet_style_prefer_auto_properties = true:warning | |||
dotnet_style_object_initializer = true:suggestion | |||
dotnet_style_collection_initializer = true:suggestion | |||
dotnet_style_prefer_simplified_boolean_expressions = true:warning | |||
dotnet_style_prefer_conditional_expression_over_assignment = true:warning | |||
dotnet_style_prefer_conditional_expression_over_return = true:warning | |||
dotnet_style_explicit_tuple_names = true:suggestion | |||
dotnet_style_prefer_inferred_tuple_names = true:suggestion | |||
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion | |||
dotnet_style_prefer_compound_assignment = true:warning | |||
dotnet_style_prefer_simplified_interpolation = true:suggestion | |||
dotnet_style_namespace_match_folder = true:suggestion |
@ -1,219 +0,0 @@ | |||
using Mono.Cecil; | |||
using Mono.Cecil.Rocks; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.IO; | |||
using System.Reflection; | |||
namespace IPA.Injector | |||
{ | |||
internal class VirtualizedModule : IDisposable | |||
{ | |||
private readonly FileInfo file; | |||
private ModuleDefinition module; | |||
public static VirtualizedModule Load(string engineFile) | |||
{ | |||
return new VirtualizedModule(engineFile); | |||
} | |||
private VirtualizedModule(string assemblyFile) | |||
{ | |||
file = new FileInfo(assemblyFile); | |||
LoadModules(); | |||
} | |||
private void LoadModules() | |||
{ | |||
module = ModuleDefinition.ReadModule(file.FullName, new ReaderParameters | |||
{ | |||
ReadWrite = false, | |||
InMemory = true, | |||
ReadingMode = ReadingMode.Immediate | |||
}); | |||
} | |||
public void Virtualize(AssemblyName selfName, Action beforeChangeCallback = null) | |||
{ | |||
var changed = false; | |||
var virtualize = true; | |||
foreach (var r in module.AssemblyReferences) | |||
{ | |||
if (r.Name == selfName.Name) | |||
{ | |||
virtualize = false; | |||
if (r.Version != selfName.Version) | |||
{ | |||
r.Version = selfName.Version; | |||
changed = true; | |||
} | |||
} | |||
} | |||
if (virtualize) | |||
{ | |||
changed = true; | |||
module.AssemblyReferences.Add(new AssemblyNameReference(selfName.Name, selfName.Version)); | |||
foreach (var type in module.Types) | |||
{ | |||
VirtualizeType(type); | |||
} | |||
} | |||
if (changed) | |||
{ | |||
beforeChangeCallback?.Invoke(); | |||
module.Write(file.FullName); | |||
} | |||
} | |||
private TypeReference inModreqRef; | |||
// private TypeReference outModreqRef; | |||
private void VirtualizeType(TypeDefinition type) | |||
{ | |||
if(type.IsSealed) | |||
{ | |||
// Unseal | |||
type.IsSealed = false; | |||
} | |||
if (type.IsNestedPrivate) | |||
{ | |||
type.IsNestedPrivate = false; | |||
type.IsNestedPublic = true; | |||
} | |||
if (type.IsInterface) return; | |||
if (type.IsAbstract) return; | |||
// These two don't seem to work. | |||
if (type.Name == "SceneControl" || type.Name == "ConfigUI") return; | |||
// Take care of sub types | |||
foreach (var subType in type.NestedTypes) | |||
{ | |||
VirtualizeType(subType); | |||
} | |||
foreach (var method in type.Methods) | |||
{ | |||
if (method.IsManaged | |||
&& method.IsIL | |||
&& !method.IsStatic | |||
&& (!method.IsVirtual || method.IsFinal) | |||
&& !method.IsAbstract | |||
&& !method.IsAddOn | |||
&& !method.IsConstructor | |||
&& !method.IsSpecialName | |||
&& !method.IsGenericInstance | |||
&& !method.HasOverrides) | |||
{ | |||
// fix In parameters to have the modreqs required by the compiler | |||
foreach (var param in method.Parameters) | |||
{ | |||
if (param.IsIn) | |||
{ | |||
inModreqRef ??= module.ImportReference(typeof(System.Runtime.InteropServices.InAttribute)); | |||
param.ParameterType = AddModreqIfNotExist(param.ParameterType, inModreqRef); | |||
} | |||
// Breaks override methods if modreq is applied to `out` parameters | |||
//if (param.IsOut) | |||
//{ | |||
// outModreqRef ??= module.ImportReference(typeof(System.Runtime.InteropServices.OutAttribute)); | |||
// param.ParameterType = AddModreqIfNotExist(param.ParameterType, outModreqRef); | |||
//} | |||
} | |||
method.IsVirtual = true; | |||
method.IsFinal = false; | |||
method.IsPublic = true; | |||
method.IsPrivate = false; | |||
method.IsNewSlot = true; | |||
method.IsHideBySig = true; | |||
} | |||
} | |||
foreach (var field in type.Fields) | |||
{ | |||
if (field.IsPrivate) field.IsFamily = true; | |||
} | |||
} | |||
private TypeReference AddModreqIfNotExist(TypeReference type, TypeReference mod) | |||
{ | |||
var (element, opt, req) = GetDecomposedModifiers(type); | |||
if (!req.Contains(mod)) | |||
{ | |||
req.Add(mod); | |||
} | |||
return BuildModifiedType(element, opt, req); | |||
} | |||
private (TypeReference Element, List<TypeReference> ModOpt, List<TypeReference> ModReq) GetDecomposedModifiers(TypeReference type) | |||
{ | |||
var opt = new List<TypeReference>(); | |||
var req = new List<TypeReference>(); | |||
while (type is IModifierType modif) | |||
{ | |||
if (type.IsOptionalModifier) | |||
opt.Add(modif.ModifierType); | |||
if (type.IsRequiredModifier) | |||
req.Add(modif.ModifierType); | |||
type = modif.ElementType; | |||
} | |||
return (type, opt, req); | |||
} | |||
private TypeReference BuildModifiedType(TypeReference type, IEnumerable<TypeReference> opt, IEnumerable<TypeReference> req) | |||
{ | |||
foreach (var mod in req) | |||
{ | |||
type = type.MakeRequiredModifierType(mod); | |||
} | |||
foreach (var mod in opt) | |||
{ | |||
type = type.MakeOptionalModifierType(mod); | |||
} | |||
return type; | |||
} | |||
#region IDisposable Support | |||
private bool disposedValue = false; // To detect redundant calls | |||
protected virtual void Dispose(bool disposing) | |||
{ | |||
if (!disposedValue) | |||
{ | |||
if (disposing) | |||
{ | |||
module.Dispose(); | |||
} | |||
disposedValue = true; | |||
} | |||
} | |||
~VirtualizedModule() | |||
{ | |||
// Do not change this code. Put cleanup code in Dispose(bool disposing) above. | |||
Dispose(false); | |||
} | |||
// This code added to correctly implement the disposable pattern. | |||
public void Dispose() | |||
{ | |||
// Do not change this code. Put cleanup code in Dispose(bool disposing) above. | |||
Dispose(true); | |||
GC.SuppressFinalize(this); | |||
} | |||
#endregion | |||
} | |||
} |
@ -1,151 +1,146 @@ | |||
#nullable enable | |||
using System; | |||
using System.Collections; | |||
using System.Collections.Generic; | |||
#nullable enable | |||
using System.Collections.Generic; | |||
using System.Diagnostics.CodeAnalysis; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
namespace IPA.Config.Data | |||
{ | |||
/// <summary> | |||
/// A base value type for config data abstract representations, to be serialized with an | |||
/// <see cref="IConfigProvider"/>. If a <see cref="Value"/> is <see langword="null"/>, then | |||
/// that represents just that: a <c>null</c> in whatever serialization is being used. | |||
/// Also contains factory functions for all derived types. | |||
/// </summary> | |||
public abstract class Value | |||
{ | |||
/// <summary> | |||
/// Converts this <see cref="Value"/> into a human-readable format. | |||
/// </summary> | |||
/// <returns>a human-readable string containing the value provided</returns> | |||
public abstract override string ToString(); | |||
/// <summary> | |||
/// Creates a Null <see cref="Value"/>. | |||
/// </summary> | |||
/// <returns><see langword="null"/></returns> | |||
public static Value? Null() => null; | |||
/// <summary> | |||
/// Creates an empty <see cref="List"/>. | |||
/// </summary> | |||
/// <returns>an empty <see cref="List"/></returns> | |||
/// <seealso cref="From(IEnumerable{Value})"/> | |||
public static List List() => new(); | |||
/// <summary> | |||
/// Creates an empty <see cref="Map"/>. | |||
/// </summary> | |||
/// <returns>an empty <see cref="Map"/></returns> | |||
/// <seealso cref="From(IDictionary{string, Value})"/> | |||
/// <seealso cref="From(IEnumerable{KeyValuePair{string, Value}})"/> | |||
public static Map Map() => new(); | |||
/// <summary> | |||
/// Creates a new <see cref="Value"/> representing a <see cref="string"/>. | |||
/// </summary> | |||
/// <param name="val">the value to wrap</param> | |||
/// <returns>a <see cref="Data.Text"/> wrapping <paramref name="val"/></returns> | |||
/// <seealso cref="Text(string)"/> | |||
[return: NotNullIfNotNull("val")] | |||
public static Text? From(string? val) => Text(val); | |||
/// <summary> | |||
/// Creates a new <see cref="Data.Text"/> object wrapping a <see cref="string"/>. | |||
/// </summary> | |||
/// <param name="val">the value to wrap</param> | |||
/// <returns>a <see cref="Data.Text"/> wrapping <paramref name="val"/></returns> | |||
/// <seealso cref="From(string)"/> | |||
[return: NotNullIfNotNull("val")] | |||
public static Text? Text(string? val) => val == null ? null : new(val); | |||
/// <summary> | |||
/// Creates a new <see cref="Value"/> wrapping a <see cref="long"/>. | |||
/// </summary> | |||
/// <param name="val">the value to wrap</param> | |||
/// <returns>a <see cref="Data.Integer"/> wrapping <paramref name="val"/></returns> | |||
/// <seealso cref="Integer(long)"/> | |||
public static Integer From(long val) => Integer(val); | |||
/// <summary> | |||
/// Creates a new <see cref="Data.Integer"/> wrapping a <see cref="long"/>. | |||
/// </summary> | |||
/// <param name="val">the value to wrap</param> | |||
/// <returns>a <see cref="Data.Integer"/> wrapping <paramref name="val"/></returns> | |||
/// <seealso cref="From(long)"/> | |||
public static Integer Integer(long val) => new(val); | |||
/// <summary> | |||
/// Creates a new <see cref="Value"/> wrapping a <see cref="double"/>. | |||
/// </summary> | |||
/// <param name="val">the value to wrap</param> | |||
/// <returns>a <see cref="FloatingPoint"/> wrapping <paramref name="val"/></returns> | |||
/// <seealso cref="Float(decimal)"/> | |||
public static FloatingPoint From(decimal val) => Float(val); | |||
/// <summary> | |||
/// Creates a new <see cref="FloatingPoint"/> wrapping a <see cref="decimal"/>. | |||
/// </summary> | |||
/// <param name="val">the value to wrap</param> | |||
/// <returns>a <see cref="FloatingPoint"/> wrapping <paramref name="val"/></returns> | |||
/// <seealso cref="From(decimal)"/> | |||
public static FloatingPoint Float(decimal val) => new(val); | |||
/// <summary> | |||
/// Creates a new <see cref="Value"/> wrapping a <see cref="bool"/>. | |||
/// </summary> | |||
/// <param name="val">the value to wrap</param> | |||
/// <returns>a <see cref="Boolean"/> wrapping <paramref name="val"/></returns> | |||
/// <seealso cref="Bool(bool)"/> | |||
public static Boolean From(bool val) => Bool(val); | |||
/// <summary> | |||
/// Creates a new <see cref="Boolean"/> wrapping a <see cref="bool"/>. | |||
/// </summary> | |||
/// <param name="val">the value to wrap</param> | |||
/// <returns>a <see cref="Boolean"/> wrapping <paramref name="val"/></returns> | |||
/// <seealso cref="From(bool)"/> | |||
public static Boolean Bool(bool val) => new(val); | |||
/// <summary> | |||
/// Creates a new <see cref="Data.List"/> holding the content of an <see cref="IEnumerable{T}"/> | |||
/// of <see cref="Value"/>. | |||
/// </summary> | |||
/// <param name="vals">the <see cref="Value"/>s to initialize the <see cref="Data.List"/> with</param> | |||
/// <returns>a <see cref="Data.List"/> containing the content of <paramref name="vals"/></returns> | |||
/// <seealso cref="List"/> | |||
[return: NotNullIfNotNull("vals")] | |||
public static List? From(IEnumerable<Value?>? vals) | |||
{ | |||
if (vals is null) return null; | |||
var l = List(); | |||
l.AddRange(vals); | |||
return l; | |||
} | |||
/// <summary> | |||
/// Creates a new <see cref="Data.Map"/> holding the content of an <see cref="IDictionary{TKey, TValue}"/> | |||
/// of <see cref="string"/> to <see cref="Value"/>. | |||
/// </summary> | |||
/// <param name="vals">the dictionary of <see cref="Value"/>s to initialize the <see cref="Data.Map"/> wtih</param> | |||
/// <returns>a <see cref="Data.Map"/> containing the content of <paramref name="vals"/></returns> | |||
/// <seealso cref="Map"/> | |||
/// <seealso cref="From(IEnumerable{KeyValuePair{string, Value}})"/> | |||
public static Map From(IDictionary<string, Value?> vals) => From(vals as IEnumerable<KeyValuePair<string, Value?>>); | |||
/// <summary> | |||
/// Creates a new <see cref="Data.Map"/> holding the content of an <see cref="IEnumerable{T}"/> | |||
/// of <see cref="KeyValuePair{TKey, TValue}"/> of <see cref="string"/> to <see cref="Value"/>. | |||
/// </summary> | |||
/// <param name="vals">the enumerable of <see cref="KeyValuePair{TKey, TValue}"/> of name to <see cref="Value"/></param> | |||
/// <returns>a <see cref="Data.Map"/> containing the content of <paramref name="vals"/></returns> | |||
/// <seealso cref="Map"/> | |||
/// <seealso cref="From(IDictionary{string, Value})"/> | |||
[return: NotNullIfNotNull("vals")] | |||
public static Map? From(IEnumerable<KeyValuePair<string, Value?>>? vals) | |||
{ | |||
if (vals is null) return null; | |||
var m = Map(); | |||
foreach (var v in vals) m.Add(v.Key, v.Value); | |||
return m; | |||
} | |||
} | |||
} | |||
namespace IPA.Config.Data | |||
{ | |||
/// <summary> | |||
/// A base value type for config data abstract representations, to be serialized with an | |||
/// <see cref="IConfigProvider"/>. If a <see cref="Value"/> is <see langword="null"/>, then | |||
/// that represents just that: a <c>null</c> in whatever serialization is being used. | |||
/// Also contains factory functions for all derived types. | |||
/// </summary> | |||
public abstract class Value | |||
{ | |||
/// <summary> | |||
/// Converts this <see cref="Value"/> into a human-readable format. | |||
/// </summary> | |||
/// <returns>a human-readable string containing the value provided</returns> | |||
public abstract override string ToString(); | |||
/// <summary> | |||
/// Creates a Null <see cref="Value"/>. | |||
/// </summary> | |||
/// <returns><see langword="null"/></returns> | |||
public static Value? Null() => null; | |||
/// <summary> | |||
/// Creates an empty <see cref="List"/>. | |||
/// </summary> | |||
/// <returns>an empty <see cref="List"/></returns> | |||
/// <seealso cref="From(IEnumerable{Value})"/> | |||
public static List List() => new(); | |||
/// <summary> | |||
/// Creates an empty <see cref="Map"/>. | |||
/// </summary> | |||
/// <returns>an empty <see cref="Map"/></returns> | |||
/// <seealso cref="From(IDictionary{string, Value})"/> | |||
/// <seealso cref="From(IEnumerable{KeyValuePair{string, Value}})"/> | |||
public static Map Map() => new(); | |||
/// <summary> | |||
/// Creates a new <see cref="Value"/> representing a <see cref="string"/>. | |||
/// </summary> | |||
/// <param name="val">the value to wrap</param> | |||
/// <returns>a <see cref="Data.Text"/> wrapping <paramref name="val"/></returns> | |||
/// <seealso cref="Text(string)"/> | |||
[return: NotNullIfNotNull("val")] | |||
public static Text? From(string? val) => Text(val); | |||
/// <summary> | |||
/// Creates a new <see cref="Data.Text"/> object wrapping a <see cref="string"/>. | |||
/// </summary> | |||
/// <param name="val">the value to wrap</param> | |||
/// <returns>a <see cref="Data.Text"/> wrapping <paramref name="val"/></returns> | |||
/// <seealso cref="From(string)"/> | |||
[return: NotNullIfNotNull("val")] | |||
public static Text? Text(string? val) => val == null ? null : new(val); | |||
/// <summary> | |||
/// Creates a new <see cref="Value"/> wrapping a <see cref="long"/>. | |||
/// </summary> | |||
/// <param name="val">the value to wrap</param> | |||
/// <returns>a <see cref="Data.Integer"/> wrapping <paramref name="val"/></returns> | |||
/// <seealso cref="Integer(long)"/> | |||
public static Integer From(long val) => Integer(val); | |||
/// <summary> | |||
/// Creates a new <see cref="Data.Integer"/> wrapping a <see cref="long"/>. | |||
/// </summary> | |||
/// <param name="val">the value to wrap</param> | |||
/// <returns>a <see cref="Data.Integer"/> wrapping <paramref name="val"/></returns> | |||
/// <seealso cref="From(long)"/> | |||
public static Integer Integer(long val) => new(val); | |||
/// <summary> | |||
/// Creates a new <see cref="Value"/> wrapping a <see cref="double"/>. | |||
/// </summary> | |||
/// <param name="val">the value to wrap</param> | |||
/// <returns>a <see cref="FloatingPoint"/> wrapping <paramref name="val"/></returns> | |||
/// <seealso cref="Float(decimal)"/> | |||
public static FloatingPoint From(decimal val) => Float(val); | |||
/// <summary> | |||
/// Creates a new <see cref="FloatingPoint"/> wrapping a <see cref="decimal"/>. | |||
/// </summary> | |||
/// <param name="val">the value to wrap</param> | |||
/// <returns>a <see cref="FloatingPoint"/> wrapping <paramref name="val"/></returns> | |||
/// <seealso cref="From(decimal)"/> | |||
public static FloatingPoint Float(decimal val) => new(val); | |||
/// <summary> | |||
/// Creates a new <see cref="Value"/> wrapping a <see cref="bool"/>. | |||
/// </summary> | |||
/// <param name="val">the value to wrap</param> | |||
/// <returns>a <see cref="Boolean"/> wrapping <paramref name="val"/></returns> | |||
/// <seealso cref="Bool(bool)"/> | |||
public static Boolean From(bool val) => Bool(val); | |||
/// <summary> | |||
/// Creates a new <see cref="Boolean"/> wrapping a <see cref="bool"/>. | |||
/// </summary> | |||
/// <param name="val">the value to wrap</param> | |||
/// <returns>a <see cref="Boolean"/> wrapping <paramref name="val"/></returns> | |||
/// <seealso cref="From(bool)"/> | |||
public static Boolean Bool(bool val) => new(val); | |||
/// <summary> | |||
/// Creates a new <see cref="Data.List"/> holding the content of an <see cref="IEnumerable{T}"/> | |||
/// of <see cref="Value"/>. | |||
/// </summary> | |||
/// <param name="vals">the <see cref="Value"/>s to initialize the <see cref="Data.List"/> with</param> | |||
/// <returns>a <see cref="Data.List"/> containing the content of <paramref name="vals"/></returns> | |||
/// <seealso cref="List"/> | |||
[return: NotNullIfNotNull("vals")] | |||
public static List? From(IEnumerable<Value?>? vals) | |||
{ | |||
if (vals is null) return null; | |||
var l = List(); | |||
l.AddRange(vals); | |||
return l; | |||
} | |||
/// <summary> | |||
/// Creates a new <see cref="Data.Map"/> holding the content of an <see cref="IDictionary{TKey, TValue}"/> | |||
/// of <see cref="string"/> to <see cref="Value"/>. | |||
/// </summary> | |||
/// <param name="vals">the dictionary of <see cref="Value"/>s to initialize the <see cref="Data.Map"/> wtih</param> | |||
/// <returns>a <see cref="Data.Map"/> containing the content of <paramref name="vals"/></returns> | |||
/// <seealso cref="Map"/> | |||
/// <seealso cref="From(IEnumerable{KeyValuePair{string, Value}})"/> | |||
public static Map From(IDictionary<string, Value?> vals) => From(vals as IEnumerable<KeyValuePair<string, Value?>>); | |||
/// <summary> | |||
/// Creates a new <see cref="Data.Map"/> holding the content of an <see cref="IEnumerable{T}"/> | |||
/// of <see cref="KeyValuePair{TKey, TValue}"/> of <see cref="string"/> to <see cref="Value"/>. | |||
/// </summary> | |||
/// <param name="vals">the enumerable of <see cref="KeyValuePair{TKey, TValue}"/> of name to <see cref="Value"/></param> | |||
/// <returns>a <see cref="Data.Map"/> containing the content of <paramref name="vals"/></returns> | |||
/// <seealso cref="Map"/> | |||
/// <seealso cref="From(IDictionary{string, Value})"/> | |||
[return: NotNullIfNotNull("vals")] | |||
public static Map? From(IEnumerable<KeyValuePair<string, Value?>>? vals) | |||
{ | |||
if (vals is null) return null; | |||
var m = Map(); | |||
foreach (var v in vals) m.Add(v.Key, v.Value); | |||
return m; | |||
} | |||
} | |||
} |
@ -1,44 +0,0 @@ | |||
using HarmonyLib; | |||
using System.Reflection; | |||
namespace IPA.Loader | |||
{ | |||
internal static class HarmonyProtectorProxy | |||
{ | |||
public static void ProtectNull() => HarmonyProtector.Protect(); | |||
} | |||
internal static class HarmonyProtector | |||
{ | |||
private static Harmony instance; | |||
private static Assembly selfAssem; | |||
private static Assembly harmonyAssem; | |||
public static void Protect(Harmony inst = null) | |||
{ | |||
selfAssem = Assembly.GetExecutingAssembly(); | |||
harmonyAssem = typeof(Harmony).Assembly; | |||
if (inst == null) | |||
{ | |||
if (instance == null) | |||
instance = new Harmony("BSIPA Safeguard"); | |||
inst = instance; | |||
} | |||
var target = typeof(PatchProcessor).GetMethod("Patch"); | |||
var patch = typeof(HarmonyProtector).GetMethod(nameof(PatchProcessor_Patch_Prefix), BindingFlags.NonPublic | BindingFlags.Static); | |||
inst.Patch(target, new HarmonyMethod(patch)); | |||
} | |||
private static bool PatchProcessor_Patch_Prefix(MethodBase ___original, out MethodInfo __result) | |||
{ | |||
var asm = ___original.DeclaringType.Assembly; | |||
__result = ___original as MethodInfo; | |||
return !(asm.Equals(selfAssem) || asm.Equals(harmonyAssem)); | |||
} | |||
} | |||
} |
@ -1,39 +1,40 @@ | |||
using System; | |||
namespace IPA.Logging | |||
{ | |||
/// <summary> | |||
/// The log printer's base class. | |||
/// </summary> | |||
public abstract class LogPrinter | |||
{ | |||
/// <summary> | |||
/// Provides a filter for which log levels to allow through. | |||
/// </summary> | |||
/// <value>the level to filter to</value> | |||
public abstract Logger.LogLevel Filter { get; set; } | |||
/// <summary> | |||
/// Prints a provided message from a given log at the specified time. | |||
/// </summary> | |||
/// <param name="level">the log level</param> | |||
/// <param name="time">the time the message was composed</param> | |||
/// <param name="logName">the name of the log that created this message</param> | |||
/// <param name="message">the message</param> | |||
public abstract void Print(Logger.Level level, DateTime time, string logName, string message); | |||
/// <summary> | |||
/// Called before the first print in a group. May be called multiple times. | |||
/// Use this to create file handles and the like. | |||
/// </summary> | |||
public virtual void StartPrint() { } | |||
/// <summary> | |||
/// Called after the last print in a group. May be called multiple times. | |||
/// Use this to dispose file handles and the like. | |||
/// </summary> | |||
public virtual void EndPrint() { } | |||
internal DateTime LastUse { get; set; } | |||
} | |||
#nullable enable | |||
using System; | |||
namespace IPA.Logging | |||
{ | |||
/// <summary> | |||
/// The log printer's base class. | |||
/// </summary> | |||
public abstract class LogPrinter | |||
{ | |||
/// <summary> | |||
/// Provides a filter for which log levels to allow through. | |||
/// </summary> | |||
/// <value>the level to filter to</value> | |||
public abstract Logger.LogLevel Filter { get; set; } | |||
/// <summary> | |||
/// Prints a provided message from a given log at the specified time. | |||
/// </summary> | |||
/// <param name="level">the log level</param> | |||
/// <param name="time">the time the message was composed</param> | |||
/// <param name="logName">the name of the log that created this message</param> | |||
/// <param name="message">the message</param> | |||
public abstract void Print(Logger.Level level, DateTime time, string logName, string message); | |||
/// <summary> | |||
/// Called before the first print in a group. May be called multiple times. | |||
/// Use this to create file handles and the like. | |||
/// </summary> | |||
public virtual void StartPrint() { } | |||
/// <summary> | |||
/// Called after the last print in a group. May be called multiple times. | |||
/// Use this to dispose file handles and the like. | |||
/// </summary> | |||
public virtual void EndPrint() { } | |||
internal DateTime LastUse { get; set; } | |||
} | |||
} |
@ -1,116 +1,159 @@ | |||
using System; | |||
using System.Runtime.InteropServices; | |||
namespace IPA.Logging.Printers | |||
{ | |||
/// <summary> | |||
/// Prints a pretty message to the console. | |||
/// </summary> | |||
public class ColoredConsolePrinter : LogPrinter | |||
{ | |||
private Logger.LogLevel filter = Logger.LogLevel.All; | |||
/// <summary> | |||
/// A filter for this specific printer. | |||
/// </summary> | |||
/// <value>the filter to apply to this printer</value> | |||
public override Logger.LogLevel Filter { get => filter; set => filter = value; } | |||
/// <summary> | |||
/// The color to print messages as. | |||
/// </summary> | |||
/// <value>the color to print this message as</value> | |||
// Initializer calls this function because Unity's .NET 3.5 doesn't have the color properties on Console | |||
public ConsoleColor Color { get; set; } = GetConsoleColor(WinConsole.OutHandle); | |||
/// <summary> | |||
/// Prints an entry to the console window. | |||
/// </summary> | |||
/// <param name="level">the <see cref="Logger.Level"/> of the message</param> | |||
/// <param name="time">the <see cref="DateTime"/> the message was recorded at</param> | |||
/// <param name="logName">the name of the log that sent the message</param> | |||
/// <param name="message">the message to print</param> | |||
public override void Print(Logger.Level level, DateTime time, string logName, string message) | |||
{ | |||
if (((byte)level & (byte)StandardLogger.PrintFilter) == 0) return; | |||
EnsureDefaultsPopulated(WinConsole.OutHandle); | |||
SetColor(Color, WinConsole.OutHandle); | |||
foreach (var line in message.Split(new[] { "\n", Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)) | |||
WinConsole.ConOut.WriteLine(Logger.LogFormat, line, logName, time, level.ToString().ToUpper()); | |||
ResetColor(WinConsole.OutHandle); | |||
} | |||
private static bool _haveReadDefaultColors; | |||
private static short _defaultColors; | |||
private void EnsureDefaultsPopulated(IntPtr handle, bool force = false) | |||
{ | |||
if (!_haveReadDefaultColors | force) | |||
{ | |||
GetConsoleScreenBufferInfo(handle, out var info); | |||
_defaultColors = (short)(info.Attribute & ~15); | |||
_haveReadDefaultColors = true; | |||
} | |||
} | |||
private void ResetColor(IntPtr handle) | |||
{ | |||
GetConsoleScreenBufferInfo(handle, out var info); | |||
var otherAttrs = (short)(info.Attribute & ~15); | |||
SetConsoleTextAttribute(handle, (short)(otherAttrs | _defaultColors)); | |||
} | |||
private void SetColor(ConsoleColor col, IntPtr handle) | |||
{ | |||
GetConsoleScreenBufferInfo(handle, out var info); | |||
var attr = GetAttrForeground(info.Attribute, col); | |||
SetConsoleTextAttribute(handle, attr); | |||
} | |||
private static short GetAttrForeground(int attr, ConsoleColor color) | |||
{ | |||
attr &= ~15; | |||
return (short)(attr | (int)color); | |||
} | |||
private static ConsoleColor GetConsoleColor(IntPtr handle) | |||
{ | |||
GetConsoleScreenBufferInfo(handle, out var info); | |||
return (ConsoleColor)(info.Attribute & 15); | |||
} | |||
// ReSharper disable NotAccessedField.Local | |||
#pragma warning disable 649 | |||
private struct Coordinate | |||
{ | |||
public short X; | |||
public short Y; | |||
} | |||
private struct SmallRect | |||
{ | |||
public short Left; | |||
public short Top; | |||
public short Right; | |||
public short Bottom; | |||
} | |||
private struct ConsoleScreenBufferInfo | |||
{ | |||
public Coordinate Size; | |||
public Coordinate CursorPosition; | |||
public short Attribute; | |||
public SmallRect Window; | |||
public Coordinate MaxWindowSize; | |||
} | |||
#pragma warning restore 649 | |||
// ReSharper restore NotAccessedField.Local | |||
[DllImport("kernel32.dll", EntryPoint = "GetConsoleScreenBufferInfo", SetLastError = true, CharSet = CharSet.Unicode)] | |||
private static extern bool GetConsoleScreenBufferInfo(IntPtr handle, out ConsoleScreenBufferInfo info); | |||
[DllImport("kernel32.dll", EntryPoint = "SetConsoleTextAttribute", SetLastError = true, CharSet = CharSet.Unicode)] | |||
private static extern bool SetConsoleTextAttribute(IntPtr handle, short attribute); | |||
} | |||
} | |||
#nullable enable | |||
using System; | |||
using System.Runtime.InteropServices; | |||
namespace IPA.Logging.Printers | |||
{ | |||
/// <summary> | |||
/// Prints a pretty message to the console. | |||
/// </summary> | |||
public class ColoredConsolePrinter : LogPrinter | |||
{ | |||
private Logger.LogLevel filter = Logger.LogLevel.All; | |||
/// <summary> | |||
/// A filter for this specific printer. | |||
/// </summary> | |||
/// <value>the filter to apply to this printer</value> | |||
public override Logger.LogLevel Filter { get => filter; set => filter = value; } | |||
/// <summary> | |||
/// The color to print messages as. | |||
/// </summary> | |||
/// <value>the color to print this message as</value> | |||
// Initializer calls this function because Unity's .NET 3.5 doesn't have the color properties on Console | |||
public ConsoleColor Color { get; set; } = GetConsoleColor(WinConsole.OutHandle); | |||
private static ConsoleColor GetDarkenedColor(ConsoleColor color) | |||
=> color switch | |||
{ | |||
ConsoleColor.Gray => ConsoleColor.DarkGray, | |||
ConsoleColor.Blue => ConsoleColor.DarkBlue, | |||
ConsoleColor.Green => ConsoleColor.DarkGreen, | |||
ConsoleColor.Cyan => ConsoleColor.DarkCyan, | |||
ConsoleColor.Red => ConsoleColor.DarkRed, | |||
ConsoleColor.Magenta => ConsoleColor.DarkMagenta, | |||
ConsoleColor.Yellow => ConsoleColor.DarkYellow, | |||
ConsoleColor.White => ConsoleColor.Gray, | |||
_ => color, | |||
}; | |||
private readonly bool darkenSetManually; | |||
private readonly bool darkenMessages; | |||
public ColoredConsolePrinter() : this(Config.SelfConfig.Debug_.DarkenMessages_) | |||
{ | |||
darkenSetManually = false; | |||
} | |||
public ColoredConsolePrinter(bool darkenMessages) | |||
{ | |||
darkenSetManually = true; | |||
this.darkenMessages = darkenMessages; | |||
} | |||
/// <summary> | |||
/// Prints an entry to the console window. | |||
/// </summary> | |||
/// <param name="level">the <see cref="Logger.Level"/> of the message</param> | |||
/// <param name="time">the <see cref="DateTime"/> the message was recorded at</param> | |||
/// <param name="logName">the name of the log that sent the message</param> | |||
/// <param name="message">the message to print</param> | |||
public override void Print(Logger.Level level, DateTime time, string logName, string message) | |||
{ | |||
if (((byte)level & (byte)StandardLogger.PrintFilter) == 0) return; | |||
EnsureDefaultsPopulated(WinConsole.OutHandle); | |||
SetColor(Color, WinConsole.OutHandle); | |||
var prefixStr = ""; | |||
var suffixStr = ""; | |||
if ((darkenSetManually && darkenMessages) || (!darkenSetManually && Config.SelfConfig.Debug_.DarkenMessages_)) | |||
{ | |||
var darkened = GetDarkenedColor(Color); | |||
if (darkened != Color) | |||
{ | |||
prefixStr = StdoutInterceptor.ConsoleColorToForegroundSet(darkened); | |||
suffixStr = StdoutInterceptor.ConsoleColorToForegroundSet(Color); | |||
} | |||
} | |||
foreach (var line in message.Split(new[] { "\n", Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)) | |||
WinConsole.ConOut.WriteLine(Logger.LogFormat, prefixStr + line + suffixStr, logName, time, level.ToString().ToUpperInvariant()); | |||
ResetColor(WinConsole.OutHandle); | |||
} | |||
private static bool _haveReadDefaultColors; | |||
private static short _defaultColors; | |||
private static void EnsureDefaultsPopulated(IntPtr handle, bool force = false) | |||
{ | |||
if (!_haveReadDefaultColors | force) | |||
{ | |||
_ = GetConsoleScreenBufferInfo(handle, out var info); | |||
_defaultColors = (short)(info.Attribute & ~15); | |||
_haveReadDefaultColors = true; | |||
} | |||
} | |||
private static void ResetColor(IntPtr handle) | |||
{ | |||
_ = GetConsoleScreenBufferInfo(handle, out var info); | |||
var otherAttrs = (short)(info.Attribute & ~15); | |||
_ = SetConsoleTextAttribute(handle, (short)(otherAttrs | _defaultColors)); | |||
} | |||
private static void SetColor(ConsoleColor col, IntPtr handle) | |||
{ | |||
_ = GetConsoleScreenBufferInfo(handle, out var info); | |||
var attr = GetAttrForeground(info.Attribute, col); | |||
_ = SetConsoleTextAttribute(handle, attr); | |||
} | |||
private static short GetAttrForeground(int attr, ConsoleColor color) | |||
{ | |||
attr &= ~15; | |||
return (short)(attr | (int)color); | |||
} | |||
private static ConsoleColor GetConsoleColor(IntPtr handle) | |||
{ | |||
_ = GetConsoleScreenBufferInfo(handle, out var info); | |||
return (ConsoleColor)(info.Attribute & 15); | |||
} | |||
// ReSharper disable NotAccessedField.Local | |||
#pragma warning disable 649 | |||
private struct Coordinate | |||
{ | |||
public short X; | |||
public short Y; | |||
} | |||
private struct SmallRect | |||
{ | |||
public short Left; | |||
public short Top; | |||
public short Right; | |||
public short Bottom; | |||
} | |||
private struct ConsoleScreenBufferInfo | |||
{ | |||
public Coordinate Size; | |||
public Coordinate CursorPosition; | |||
public short Attribute; | |||
public SmallRect Window; | |||
public Coordinate MaxWindowSize; | |||
} | |||
#pragma warning restore 649 | |||
// ReSharper restore NotAccessedField.Local | |||
[DllImport("kernel32.dll", EntryPoint = "GetConsoleScreenBufferInfo", SetLastError = true, CharSet = CharSet.Unicode)] | |||
private static extern bool GetConsoleScreenBufferInfo(IntPtr handle, out ConsoleScreenBufferInfo info); | |||
[DllImport("kernel32.dll", EntryPoint = "SetConsoleTextAttribute", SetLastError = true, CharSet = CharSet.Unicode)] | |||
private static extern bool SetConsoleTextAttribute(IntPtr handle, short attribute); | |||
} | |||
} |
@ -1,3 +1,3 @@ | |||
# ![BSIPA](docs/images/banner_dark.svg) [![Build](https://github.com/bsmg/BeatSaber-IPA-Reloaded/workflows/Build/badge.svg)](https://github.com/bsmg/BeatSaber-IPA-Reloaded) | |||
# ![BSIPA](docs/images/banner_dark.svg) [![Build](https://github.com/nike4613/BeatSaber-IPA-Reloaded/workflows/Build/badge.svg)](https://github.com/nike4613/BeatSaber-IPA-Reloaded) | |||
[ALL DOCUMENTATION HAS MOVED HERE](https://bsmg.github.io/BeatSaber-IPA-Reloaded/) | |||
[ALL DOCUMENTATION HAS MOVED HERE](https://nike4613.github.io/BeatSaber-IPA-Reloaded/) |