@ -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.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/) |