### Create New Osu! Skin Mixes with SkinMixerMachine Source: https://context7.com/rednir/osuskinmixer/llms.txt Demonstrates the usage of the SkinMixerMachine class to create a new osu! skin by selecting and combining elements from multiple source skins. It covers configuring skin options, setting specific values for elements like cursors and sounds, initializing the machine, setting the new skin name, and running the mixing process. The output is a new OsuSkin object representing the combined skin. ```csharp using OsuSkinMixer.Utils; using OsuSkinMixer.Models; using System.Threading; // Configure skin options (typically from UI) SkinOption[] options = SkinOption.Default; // Get default option hierarchy // Set values for specific options (example: use cursor from Skin A, sounds from Skin B) var flatOptions = SkinOption.Flatten(options); foreach (var option in flatOptions) { if (option is SkinFileOption fileOption && fileOption.IncludeFileName == "cursor") { option.Value = new SkinOptionValue(skinA); // Use cursor from skinA } else if (option is SkinFileOption soundOption && soundOption.IsAudio) { option.Value = new SkinOptionValue(skinB); // Use sounds from skinB } } // Create the skin mixer machine SkinMixerMachine machine = new SkinMixerMachine { SkinOptions = options, ProgressChanged = progress => Console.WriteLine($"Progress: {progress}%"), StatusChanged = status => Console.WriteLine($"Status: {status}") }; // Set the new skin name (creates working directory) machine.SetNewSkin("My Mixed Skin"); // Run the mixing process CancellationTokenSource cts = new CancellationTokenSource(); try { machine.Run(cts.Token); // Access the created skin OsuSkin newSkin = machine.NewSkin; Console.WriteLine($"Created skin: {newSkin.Name} at {newSkin.Directory.FullName}"); } catch (OperationCanceledException) { Console.WriteLine("Skin creation was cancelled"); } ``` -------------------------------- ### SkinOption Hierarchy Management in C# Source: https://context7.com/rednir/osuskinmixer/llms.txt Demonstrates how to access, flatten, filter, and manipulate the default SkinOption hierarchy in C#. It covers retrieving the default hierarchy, iterating through all options, filtering by type (file, INI property, INI section), and setting values for SkinFileOptions. It also shows how to create a custom SkinFileOption and retrieve parent options. ```csharp using OsuSkinMixer.Models; // Get the default skin option hierarchy SkinOption[] defaultOptions = SkinOption.Default; // The hierarchy includes: // - Interface (Main menu, Song select, Mod icons, Results screen, Pause screen, In-game) // - Gameplay (osu!, taiko, catch, mania specific elements) // - Cursor (Head, Trail) // - Hitsounds (Normal, Soft, Drum, Spinner, Nightcore, Taiko-specific) // - Menu Sounds (Interface, In-game) // Flatten the hierarchy for iteration IEnumerable allOptions = SkinOption.Flatten(defaultOptions); // Filter by option type var fileOptions = allOptions.OfType(); var iniPropertyOptions = allOptions.OfType(); var iniSectionOptions = allOptions.OfType(); // Work with file options foreach (SkinFileOption option in fileOptions) { Console.WriteLine($"File: {option.IncludeFileName}"); Console.WriteLine($" Audio: {option.IsAudio}"); Console.WriteLine($" Animatable: {option.IsAnimatable}"); Console.WriteLine($" Allowed suffixes: {string.Join(", ", option.AllowedSuffixes ?? Array.Empty())}"); // Set the value to use a specific skin option.Value = new SkinOptionValue(selectedSkin); // Or use default skin option.Value = new SkinOptionValue(SkinOptionValueType.DefaultSkin); // Or make it blank/transparent option.Value = new SkinOptionValue(SkinOptionValueType.Blank); // Or keep unchanged (for skin modifier) option.Value = new SkinOptionValue(SkinOptionValueType.Unchanged); } // Create custom file option SkinFileOption customOption = new SkinFileOption( fileName: "custom-element", isAudio: false, displayName: "Custom Element", isAnimatable: true, allowedSuffixes: new[] { "0", "1", "2", "3" } ); // Get parent options for a child IEnumerable parents = SkinOption.GetParents(childOption, defaultOptions); ``` -------------------------------- ### Settings - Application Configuration API Source: https://context7.com/rednir/osuskinmixer/llms.txt Manages application settings, osu! folder configuration, logging, and update checking. ```APIDOC ## Settings - Application Configuration ### Description Manages application settings, osu! folder configuration, logging, and update checking. ### Methods - **InitialiseSettingsFile()**: Initializes the application settings file if it doesn't exist. - **TryCreateLockFile()**: Attempts to create a lock file to ensure a single instance of the application is running. Returns true if successful, false otherwise. - **TrySetOsuFolder(string osuPath, out string error)**: Attempts to set the osu! folder path. Returns true on success, false with an error message. - **Save()**: Saves the current application settings. - **Log(string message)**: Logs a custom message. - **StartLoggingToFile()**: Enables debug file logging. - **StopLoggingToFile()**: Stops debug file logging, saves the log, and opens the logs folder. - **PushException(Exception ex)**: Pushes an exception to be handled by the UI. - **PushToast(string message)**: Pushes a toast notification message. - **GetLatestReleaseOrNullAsync()**: Asynchronously retrieves the latest release information from GitHub. - **DownloadInstallerAsync(GithubRelease release)**: Asynchronously downloads the installer for a given GitHub release. ### Properties - **SkinsFolderPath**: Gets the configured path for the skins folder. - **SongsFolderPath**: Gets the configured path for the songs folder. - **HiddenSkinsFolderPath**: Gets the configured path for the hidden skins folder. - **Content**: Gets or sets the `SettingsContent` object containing application settings. - **VERSION**: A string representing the current application version. ### Events - **ExceptionPushed**: Event triggered when an exception is pushed. - **ToastPushed**: Event triggered when a toast message is pushed. ### Example Usage ```csharp // Initialize settings Settings.InitialiseSettingsFile(); // Set osu! folder if (Settings.TrySetOsuFolder("/path/to/osu!", out string error)) { Console.WriteLine($"Skins folder: {Settings.SkinsFolderPath}"); } // Access and modify settings SettingsContent content = Settings.Content; content.AutoUpdate = true; Settings.Save(); // Logging Settings.Log("Custom log message"); Settings.StartLoggingToFile(); Settings.StopLoggingToFile(); // Error handling Settings.ExceptionPushed += ex => ShowErrorDialog(ex.Message); Settings.PushException(new Exception("Something went wrong")); // Check for updates GithubRelease release = await Settings.GetLatestReleaseOrNullAsync(); if (release != null) { await Settings.DownloadInstallerAsync(release); } ``` ``` -------------------------------- ### Configure Application Settings Source: https://context7.com/rednir/osuskinmixer/llms.txt The Settings class manages global application configuration, including osu! directory paths, logging, and update checking. It ensures single-instance execution via lock files. ```csharp using OsuSkinMixer.Statics; Settings.InitialiseSettingsFile(); if (Settings.TrySetOsuFolder("/path/to/osu!", out string error)) { Settings.Content.AutoUpdate = true; Settings.Save(); } GithubRelease release = await Settings.GetLatestReleaseOrNullAsync(); if (release != null) { await Settings.DownloadInstallerAsync(release); } ``` -------------------------------- ### OsuSkinIni: Parsing and Generating osu! Skin Configuration Source: https://context7.com/rednir/osuskinmixer/llms.txt The OsuSkinIni class is responsible for parsing existing osu! skin.ini files and generating new ones. It supports all standard sections and properties, allowing for direct modification of values, addition of new sections, and removal of properties. The class can also create new skin.ini files with default settings. ```csharp using OsuSkinMixer.Models; using System.IO; // Parse existing skin.ini file string iniContent = File.ReadAllText("/path/to/skin.ini"); OsuSkinIni skinIni = new OsuSkinIni(iniContent); // Create new skin.ini with defaults OsuSkinIni newIni = new OsuSkinIni( name: "My Custom Skin", author: "PlayerName", version: "latest" ); // Access sections and properties string skinName = skinIni.TryGetPropertyValue("General", "Name"); string cursorExpand = skinIni.TryGetPropertyValue("General", "CursorExpand"); string combo1Color = skinIni.TryGetPropertyValue("Colours", "Combo1"); // Modify sections directly OsuSkinIniSection colorsSection = skinIni.Sections.Find(s => s.Name == "Colours"); if (colorsSection != null) { colorsSection["Combo1"] = "255,128,0"; // Set combo color colorsSection["SliderBorder"] = "255,255,255"; colorsSection.Remove("Combo5"); // Remove unused color } // Add new section OsuSkinIniSection maniaSection = new OsuSkinIniSection("Mania"); maniaSection.Add("Keys", "4"); maniaSection.Add("ColumnWidth", "30,30,30,30"); skinIni.Sections.Add(maniaSection); // Generate skin.ini file content string output = skinIni.ToString(); // Output: "// skin.ini generated by osu! skin mixer v3.2\n\n[General]\nName: My Custom Skin..." File.WriteAllText("/path/to/new/skin.ini", output); ``` -------------------------------- ### Modify Existing Osu! Skins with SkinModifierMachine Source: https://context7.com/rednir/osuskinmixer/llms.txt Illustrates how to use the SkinModifierMachine class to modify existing osu! skins. This includes replacing specific elements (like hitcircles), making elements blank (like miss indicators), and applying advanced transformations such as smooth cursor trails. It also shows how to configure combo color overrides and progress callbacks. The process supports undo functionality. ```csharp using OsuSkinMixer.Utils; using OsuSkinMixer.Models; using System.Threading; using System.Drawing; // Get skins to modify OsuSkin[] skinsToModify = new[] { existingSkin1, existingSkin2 }; // Configure modification options SkinOption[] options = SkinOption.Default; var flatOptions = SkinOption.Flatten(options); // Set specific elements to replace or make blank foreach (var option in flatOptions) { if (option is SkinFileOption fileOption) { if (fileOption.IncludeFileName == "hitcircle") { // Replace hitcircle with one from another skin option.Value = new SkinOptionValue(sourceSkin); } else if (fileOption.IncludeFileName == "hit0") { // Make miss indicator transparent/blank option.Value = new SkinOptionValue(SkinOptionValueType.Blank); } else { // Keep unchanged option.Value = new SkinOptionValue(SkinOptionValueType.Unchanged); } } } // Configure combo color overrides per skin Dictionary comboColorOverrides = new() { ["MySkin"] = new[] { new Color(1, 0.5f, 0), // Orange new Color(0, 0.8f, 0.2f), // Green new Color(0.2f, 0.5f, 1) // Blue } }; // Create and configure the modifier machine SkinModifierMachine machine = new SkinModifierMachine { SkinOptions = options, SkinsToModify = skinsToModify, SkinComboColourOverrides = comboColorOverrides, SkinCursorColourOverrideImageDirs = new Dictionary(), // Advanced transformations SmoothTrail = true, // Convert cursor trail to smooth mode Instafade = false, // Instant-fading hitcircles DisableInterfaceAnimations = false, // Progress callbacks ProgressChanged = p => Console.WriteLine($"Progress: {p}%"), StatusChanged = s => Console.WriteLine($"Status: {s}") }; // Run modification (supports undo via Operation class) CancellationTokenSource cts = new CancellationTokenSource(); machine.Run(cts.Token); // Note: Cancellation is disabled after 80% progress (UNCANCELLABLE_AFTER) ``` -------------------------------- ### OsuSkin: Representing and Accessing osu! Skin Elements Source: https://context7.com/rednir/osuskinmixer/llms.txt The OsuSkin class provides methods to load, access, and manage osu! skin elements such as textures, audio, and configuration. It handles texture caching, fallback to default elements, and supports loading various audio formats and sprite frames. It also manages credits files and allows clearing the texture cache. ```csharp using OsuSkinMixer.Models; using System.IO; using Godot; // Create a skin from an existing directory DirectoryInfo skinDir = new DirectoryInfo("/path/to/osu/Skins/MySkin"); OsuSkin skin = new OsuSkin(skinDir, hidden: false); // Access skin properties string skinName = skin.Name; // "MySkin" string author = skin.SkinIni?.TryGetPropertyValue("General", "Author"); int elementCount = skin.ElementCount; // Count of PNG/WAV/OGG/MP3 files Color[] comboColors = skin.ComboColors; // Array of combo colors from skin.ini // Load textures (with automatic fallback to default skin) Texture2D hitcircle = skin.GetTexture("hitcircle", "png"); Texture2D hitcircle2x = skin.Get2XTexture("hitcircle", "png"); // Prefers @2x version // Try to get texture with success indicator bool found = skin.TryGetTexture("cursor", out Texture2D cursor, "png"); bool found2x = skin.TryGet2XTexture("cursor", out Texture2D cursor2x, "png"); // Load audio streams (supports .wav, .ogg, .mp3) AudioStream hitSound = skin.GetAudioStream("normal-hitnormal"); AudioStream comboBreak = skin.GetAudioStream("combobreak"); // Load animated sprite frames SpriteFrames frames = new SpriteFrames(); skin.AddSpriteFramesAnimation(frames, "menu-back", use2x: true); // Access and write credits file OsuSkinCredits credits = skin.Credits; string creditsPath = skin.WriteCreditsFile(); // Clear texture cache (call after skin modification) skin.ClearCache(); ``` -------------------------------- ### POST /SkinMixerMachine Source: https://context7.com/rednir/osuskinmixer/llms.txt Creates a new skin by combining elements from multiple source skins based on a provided hierarchy of skin options. ```APIDOC ## POST /SkinMixerMachine ### Description Creates a new skin by combining elements from multiple source skins. Users can define specific source skins for individual elements like cursors or audio files. ### Method POST ### Endpoint /SkinMixerMachine ### Parameters #### Request Body - **SkinOptions** (Array) - Required - The hierarchy of skin elements to be mixed. - **NewSkinName** (String) - Required - The name of the resulting mixed skin. - **ProgressChanged** (Callback) - Optional - Function to track percentage progress. - **StatusChanged** (Callback) - Optional - Function to track status updates. ### Request Example { "NewSkinName": "My Mixed Skin", "SkinOptions": [ ... ] } ### Response #### Success Response (200) - **NewSkin** (Object) - The created skin object containing name and directory details. #### Response Example { "Name": "My Mixed Skin", "Directory": "/path/to/skins/My Mixed Skin" } ``` -------------------------------- ### Manage Skin Collection with OsuData Source: https://context7.com/rednir/osuskinmixer/llms.txt The OsuData class provides a static interface to manage osu! skins. It handles events for skin lifecycle changes, file system monitoring, and UI navigation requests. ```csharp using OsuSkinMixer.Statics; using OsuSkinMixer.Models; OsuData.AllSkinsLoaded += () => Console.WriteLine("All skins loaded"); OsuData.SkinAdded += skin => Console.WriteLine($"Skin added: {skin.Name}"); if (OsuData.TryLoadSkins()) { foreach (OsuSkin skin in OsuData.Skins) { Console.WriteLine($"{skin.Name} - Hidden: {skin.Hidden}"); } } OsuData.SweepPaused = true; // Perform bulk operations OsuData.SweepPaused = false; ``` -------------------------------- ### Execute Undoable Operations Source: https://context7.com/rednir/osuskinmixer/llms.txt The Operation class provides a wrapper for skin-related tasks that require undo functionality. It maintains history and allows for asynchronous execution with optional file system monitoring pauses. ```csharp using OsuSkinMixer.Models; Operation operation = new Operation( type: OperationType.SkinMixer, targetSkin: newSkin, action: () => { /* Logic */ }, undoAction: () => { /* Undo logic */ } ); await operation.RunOperation(pauseSweep: true); if (operation.CanUndo) { operation.UndoOperation(); } ``` -------------------------------- ### OsuData - Skin Collection Management API Source: https://context7.com/rednir/osuskinmixer/llms.txt Manages the user's skin collection, providing events and methods for loading, adding, removing, and querying skins. ```APIDOC ## OsuData - Skin Collection Management ### Description Manages the user's skin collection, providing events for skin changes and methods to load, add, remove, and query skins. ### Events - **AllSkinsLoaded**: Triggered when all skins have been loaded. - **SkinAdded**: Triggered when a new skin is added. - **SkinModified**: Triggered when an existing skin is modified. - **SkinRemoved**: Triggered when a skin is removed. - **SkinConflictDetected**: Triggered when a skin conflict is detected between visible and hidden folders. - **SkinInfoRequested**: Triggered when skin information needs to be displayed. - **SkinModifyRequested**: Triggered when a skin modification screen needs to be shown. ### Methods - **TryLoadSkins()**: Attempts to load all skins from the configured osu! folder. Returns true on success, false otherwise. - **AddSkin(OsuSkin skin)**: Adds a new skin to the collection. - **RemoveSkin(OsuSkin skin)**: Removes a skin from the collection. - **InvokeSkinModified(OsuSkin skin)**: Invokes the skin modification event for a given skin. - **RequestSkinInfo(OsuSkin[] skins)**: Requests the display of skin information for the specified skins. - **RequestSkinModify(OsuSkin[] skins)**: Requests the display of the skin modification screen for the specified skins. ### Properties - **Skins**: An array of all loaded `OsuSkin` objects, sorted by name. - **SweepPaused**: A boolean value to pause automatic folder monitoring during bulk operations. ### Example Usage ```csharp // Subscribe to events OsuData.AllSkinsLoaded += () => Console.WriteLine("All skins loaded"); OsuData.SkinAdded += skin => Console.WriteLine($"Skin added: {skin.Name}"); // Load skins OsuData.TryLoadSkins(); // Access skins OsuSkin[] allSkins = OsuData.Skins; // Manage skins OsuData.AddSkin(newSkin); OsuData.RemoveSkin(oldSkin); // Request UI navigation OsuData.RequestSkinInfo(new[] { selectedSkin }); ``` ``` -------------------------------- ### Operation - Undoable Operations with History API Source: https://context7.com/rednir/osuskinmixer/llms.txt Wraps skin operations with undo support and maintains operation history. ```APIDOC ## Operation - Undoable Operations with History ### Description Wraps skin operations with undo support and maintains operation history for the history popup. ### Methods - **RunOperation(bool pauseSweep)**: Runs the operation asynchronously. If `pauseSweep` is true, it pauses automatic folder monitoring during the operation. - **UndoOperation()**: Undoes the operation if `CanUndo` is true. ### Properties - **type**: The type of operation (e.g., `OperationType.SkinMixer`). - **targetSkin**: The `OsuSkin` object targeted by the operation. - **action**: A delegate representing the main logic of the operation. - **undoAction**: A delegate representing the logic to undo the operation. - **Started**: A boolean indicating if the operation has started. - **CanUndo**: A boolean indicating if the operation can be undone. - **Description**: A string describing the operation. - **TimeStarted**: The timestamp when the operation started. ### Example Usage ```csharp // Create an operation Operation operation = new Operation( type: OperationType.SkinMixer, targetSkin: newSkin, action: () => { /* main logic */ }, undoAction: () => { /* undo logic */ } ); // Run the operation await operation.RunOperation(pauseSweep: true); // Check if undoable and undo if (operation.CanUndo) { operation.UndoOperation(); } // Access operation history List history = Settings.Content.Operations; foreach (var op in history.Where(o => o.CanUndo)) { Console.WriteLine($"{op.TimeStarted}: {op.Description}"); } ``` ``` -------------------------------- ### POST /SkinModifierMachine Source: https://context7.com/rednir/osuskinmixer/llms.txt Modifies existing skins by replacing elements, overriding combo colors, and applying advanced graphical transformations. ```APIDOC ## POST /SkinModifierMachine ### Description Modifies one or more existing skins. Supports element replacement, blanking out specific files, and applying transformations like smooth trails or instafade. ### Method POST ### Endpoint /SkinModifierMachine ### Parameters #### Request Body - **SkinsToModify** (Array) - Required - List of existing skin objects to be modified. - **SkinOptions** (Array) - Required - Configuration for element replacements. - **SkinComboColourOverrides** (Dictionary) - Optional - Color overrides per skin. - **SmoothTrail** (Boolean) - Optional - Enables smooth cursor trails. - **Instafade** (Boolean) - Optional - Enables instant-fading hitcircles. ### Request Example { "SkinsToModify": ["skin1", "skin2"], "SmoothTrail": true, "Instafade": false } ### Response #### Success Response (200) - **Status** (String) - Completion status of the modification process. #### Response Example { "Status": "Success" } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.