### Compile MajSimai from Source Source: https://github.com/lingfeng-bbben/majsimai/blob/master/README.md Commands to restore and publish the library using the .NET CLI. ```bash git clone https://github.com/LingFeng-bbben/MajSimai.git cd MajSimai dotnet restore # Feel free to change the target framework # If you want to generate nuget package, please remove /p:GeneratePackageOnBuild=false dotnet publish -c release /p:PublishAot=false /p:GeneratePackageOnBuild=false -f netstandard2.1 ``` ```bash dotnet publish -c release /p:PublishAot=true -f net9.0 -r linux-x64 ``` -------------------------------- ### C++ Usage of MajSimai Native Library Source: https://context7.com/lingfeng-bbben/majsimai/llms.txt Demonstrates how to use the MajSimai native library in C++. Includes parsing Simai content and handling results. Ensure the library is compiled with AOT for native interop. ```cpp // C++ usage of MajSimai native library #include #include struct MajSimaiParseResult { int32_t code = -1; int32_t errorAtLine = -1; int32_t errorAtColumn = -1; int32_t errorMsgLen = 0; int32_t errorContentLen = 0; void* simaiFile = NULL; char* errorMsgAnsi = NULL; char* errorContentAnsi = NULL; }; // Function declarations extern "C" void __cdecl MajSimai_Parse(char*, int32_t, void*); extern "C" bool __cdecl MajSimai_Free(MajSimaiParseResult*); extern "C" bool __cdecl MajSimai_FreeHGlobal(void*); int main() { const std::string FUMEN = R"(&title=Native Test &artist=C++ Developer &first=0 &des=Designer &lv_5=10 &inote_5= (180){8}1,2,3,4,5,6,7,8, 1b,2b,3b,4b, 1h[8:4],2h[8:4], 1-3[8:2],4-6[8:2], )"; MajSimaiParseResult result = MajSimaiParseResult(); // Parse the simai content MajSimai_Parse(const_cast(FUMEN.data()), FUMEN.length(), &result); if (result.code == 0) { std::cout << "Parse successful!" << std::endl; // Access result.simaiFile for parsed data... } else { std::cout << "Parse failed at line " << result.errorAtLine << ", column " << result.errorAtColumn << std::endl; if (result.errorMsgAnsi) { std::cout << "Error: " << result.errorMsgAnsi << std::endl; } } // Free allocated memory MajSimai_Free(&result); return 0; } // Build command for native library: // dotnet publish -c release /p:PublishAot=true -f net9.0 -r linux-x64 ``` -------------------------------- ### Manage Custom SimaiCommand Metadata Source: https://context7.com/lingfeng-bbben/majsimai/llms.txt Access, create, and parse custom key-value pair commands that extend standard Simai metadata. ```csharp using MajSimai; // Parse custom commands from file string simaiContent = @"&title=Test Song &artist=Test Artist &first=0 &customField=customValue &genre=Pop &version=1.0 &inote_5=(120){4}1,2,3,4,"; using var stream = new MemoryStream(Encoding.UTF8.GetBytes(simaiContent)); SimaiFile file = SimaiParser.Parse(stream); // Access custom commands foreach (var cmd in file.Commands) { Console.WriteLine($"Command: &{cmd.Prefix}={cmd.Value}"); // Convert to KeyValuePair implicitly KeyValuePair kvp = cmd; Console.WriteLine($"As KVP: {kvp.Key} -> {kvp.Value}"); } // Create new commands var newCommand = new SimaiCommand("customTag", "customValue"); file.Commands.Add(newCommand); // Parse command from string if (SimaiCommand.TryParse("&myCommand=myValue".AsSpan(), out var parsedCmd)) { Console.WriteLine($"Parsed: {parsedCmd.Prefix} = {parsedCmd.Value}"); } // Command equality comparison var cmd1 = new SimaiCommand("test", "value"); var cmd2 = new SimaiCommand("test", "value"); Console.WriteLine($"Commands equal: {cmd1 == cmd2}"); // true ``` -------------------------------- ### Parse Complete Simai File Asynchronously Source: https://context7.com/lingfeng-bbben/majsimai/llms.txt Reads a full Simai file from a stream, computes an MD5 hash, and returns a SimaiFile object. Supports custom character encodings for non-UTF-8 files. ```csharp using MajSimai; using System.Text; // Parse from file stream with default UTF-8 encoding var filePath = "/path/to/maidata.txt"; using var fileStream = File.OpenRead(filePath); SimaiFile simaiFile = await SimaiParser.ParseAsync(fileStream); // Access parsed data Console.WriteLine($"Title: {simaiFile.Title}"); Console.WriteLine($"Artist: {simaiFile.Artist}"); Console.WriteLine($"Offset: {simaiFile.Offset}s"); Console.WriteLine($"Hash: {simaiFile.Hash}"); // Iterate through all 7 difficulty charts for (int i = 0; i < simaiFile.Charts.Length; i++) { var chart = simaiFile.Charts[i]; if (!chart.IsEmpty) { Console.WriteLine($"Chart {i + 1}: Level {chart.Level}, Designer: {chart.Designer}"); Console.WriteLine($" Note count: {chart.NoteTimings.Length}"); } } // Access custom commands foreach (var cmd in simaiFile.Commands) { Console.WriteLine($"Custom command: &{cmd.Prefix}={cmd.Value}"); } // Parse with specific encoding (e.g., Shift-JIS for Japanese files) using var fileStream2 = File.OpenRead(filePath); simaiFile = await SimaiParser.ParseAsync(fileStream2, Encoding.GetEncoding("shift_jis")); ``` -------------------------------- ### Use MajSimai Native Exports in C++ Source: https://github.com/lingfeng-bbben/majsimai/blob/master/README.md Define the exported structures and functions to interact with the MajSimai native library. ```C++ struct MajSimaiParseResult { int32_t code = -1; int32_t errorAtLine = -1; int32_t errorAtColumn = -1; int32_t errorMsgLen = 0; int32_t errorContentLen = 0; void* simaiFile = NULL; char* errorMsgAnsi = NULL; char* errorContentAnsi = NULL; }; extern "C" void __cdecl MajSimai_Parse(char*, int32_t, void*); extern "C" bool __cdecl MajSimai_Free(MajSimaiParseResult*); extern "C" bool __cdecl MajSimai_FreeHGlobal(void*); int main() { const string FUMEN = R"(&title=Test &artist=NULL &first=0 &des=1 &lv_5=114514 &inote_5= (197){8}1,2,3,4,5,6,7,8,)"; MajSimaiParseResult result = MajSimaiParseResult(); MajSimai_Parse(FUMEN.data(), FUMEN.length(), &result); MajSimai_Free(&result); } ``` -------------------------------- ### SimaiNote Data Structure Analysis Source: https://context7.com/lingfeng-bbben/majsimai/llms.txt Examine the SimaiNote class to understand individual note properties like type, position, timing, and flags for special behaviors. This includes details for Hold and Slide notes. ```csharp using MajSimai; string fumen = "(150){4}1bx$,2h[4:2],3-5[4:1]b,B1f,C2h[4:1],"; SimaiChart chart = SimaiParser.ParseChart(fumen); foreach (var timing in chart.NoteTimings) { foreach (var note in timing.Notes) { Console.WriteLine($"Note Type: {note.Type}"); Console.WriteLine($" Position: {note.StartPosition} (1-8 for buttons, with TouchArea for touch)"); Console.WriteLine($" Touch Area: '{note.TouchArea}'"); // Note flags Console.WriteLine($" IsBreak: {note.IsBreak}"); Console.WriteLine($" IsEx: {note.IsEx}"); Console.WriteLine($" IsForceStar: {note.IsForceStar}"); Console.WriteLine($" IsFakeRotate: {note.IsFakeRotate}"); Console.WriteLine($" IsMine: {note.IsMine}"); Console.WriteLine($" IsHanabi: {note.IsHanabi}"); // Hold/Slide specific if (note.Type == SimaiNoteType.Hold || note.Type == SimaiNoteType.TouchHold) { Console.WriteLine($" Hold Time: {note.HoldTime}s"); } if (note.Type == SimaiNoteType.Slide) { Console.WriteLine($" Slide Start Time: {note.SlideStartTime}s"); Console.WriteLine($" Slide Duration: {note.SlideTime}s"); Console.WriteLine($" IsSlideBreak: {note.IsSlideBreak}"); Console.WriteLine($" IsSlideNoHead: {note.IsSlideNoHead}"); Console.WriteLine($" IsMineSlide: {note.IsMineSlide}"); } Console.WriteLine($" Raw Content: {note.RawContent}"); Console.WriteLine(); } } ``` -------------------------------- ### Analyze SimaiTimingPoint and Note Groups Source: https://context7.com/lingfeng-bbben/majsimai/llms.txt Iterate through timing points and note groups within a parsed chart to access BPM, scroll speed, and raw text positioning. ```csharp using MajSimai; string fumen = "(120){4}1/2/3,4/5,{8}1,2,3,4,"; SimaiChart chart = SimaiParser.ParseChart(fumen); foreach (var tp in chart.NoteTimings) { Console.WriteLine($"Timing: {tp.Timing:F4}s"); Console.WriteLine($" BPM: {tp.Bpm}"); Console.WriteLine($" HSpeed: {tp.HSpeed}x"); Console.WriteLine($" Raw Content: '{tp.RawContent}'"); Console.WriteLine($" Text Position: Line {tp.RawTextPositionY}, Column {tp.RawTextPositionX}"); Console.WriteLine($" Notes in group: {tp.Notes.Length}"); Console.WriteLine($" Is Empty: {tp.IsEmpty}"); foreach (var note in tp.Notes) { Console.WriteLine($" - {note.Type} at {note.StartPosition}"); } Console.WriteLine(); } // Access comma timings (beat markers without notes) Console.WriteLine("Beat markers:"); foreach (var ct in chart.CommaTimings) { Console.WriteLine($" Beat at {ct.Timing:F4}s (BPM: {ct.Bpm})"); } ``` -------------------------------- ### Parse Simai Charts and Metadata in C# Source: https://github.com/lingfeng-bbben/majsimai/blob/master/README.md Use SimaiParser to load charts from file streams or raw strings. Supports optional encoding specification. ```C# // Specify the chart file var filePath = "/path/to/your/chart.txt"; using var fileStream = File.OpenRead(filePath); // Parse into SimaiFile var simaiFile = await SimaiParser.ParseAsync(fileStream); // With special encoding simaiFile = await SimaiParser.ParseAsync(fileStream, Encoding.ASCII); // Or parse only metadata var simaiMetadata = await SimaiParser.ParseMetadataAsync(fileStream); // With special encoding simaiMetadata = await SimaiParser.ParseMetadataAsync(fileStream, Encoding.ASCII); // Or parse from the chart text you provide var fumen = "(197){8}1,1,1,1,1,1,1,1,2b,3h,4-1[8:1],"; var simaiChart = await SimaiParser.ParseChartAsync(fumen); ``` -------------------------------- ### Handle InvalidSimaiMarkupException Source: https://context7.com/lingfeng-bbben/majsimai/llms.txt Catch parsing and syntax errors to retrieve specific line and column information for debugging chart markup. ```csharp using MajSimai; try { // Invalid chart - missing closing parenthesis for BPM string invalidFumen = "(120{4}1,2,3,4,"; SimaiChart chart = SimaiParser.ParseChart(invalidFumen); } catch (InvalidSimaiMarkupException ex) { Console.WriteLine($"Parse error at Line {ex.Line}, Column {ex.Column}"); Console.WriteLine($"Content: '{ex.Content}'"); Console.WriteLine($"Message: {ex.Message}"); } try { // Invalid metadata markup string invalidMetadata = @"&title=Test &missingEquals &artist=Test"; using var stream = new MemoryStream(Encoding.UTF8.GetBytes(invalidMetadata)); SimaiParser.ParseMetadata(stream); } catch (InvalidSimaiMarkupException ex) { Console.WriteLine($"Metadata error at line {ex.Line}: {ex.Message}"); } // InvalidSimaiSyntaxException for syntax-specific errors (inherits from InvalidSimaiMarkupException) try { string badBpm = "(notanumber){4}1,2,3,4,"; SimaiParser.ParseChart(badBpm); } catch (InvalidSimaiSyntaxException ex) { Console.WriteLine($"Syntax error: {ex.Message}"); Console.WriteLine($"At: Line {ex.Line}, Column {ex.Column}"); } ``` -------------------------------- ### Parse Complete Simai File Synchronously Source: https://context7.com/lingfeng-bbben/majsimai/llms.txt Synchronously reads and parses a Simai file from a stream, suitable for environments where asynchronous operations are not required. ```csharp using MajSimai; var filePath = "/path/to/maidata.txt"; using var fileStream = File.OpenRead(filePath); // Synchronous parsing with UTF-8 encoding (default) SimaiFile simaiFile = SimaiParser.Parse(fileStream); // Synchronous parsing with custom encoding using var fileStream2 = File.OpenRead(filePath); simaiFile = SimaiParser.Parse(fileStream2, Encoding.ASCII); // Access the parsed file Console.WriteLine($"Parsed: {simaiFile.Title} by {simaiFile.Artist}"); Console.WriteLine($"Available charts: {simaiFile.Charts.Count(c => !c.IsEmpty)}"); ``` -------------------------------- ### SimaiParser.ParseMetadataAsync - Parse Only Metadata Source: https://context7.com/lingfeng-bbben/majsimai/llms.txt Asynchronously parses only the metadata from a simai file, excluding the note charts. This method is faster when only song information is required. It also provides access to raw fumen strings and custom commands. ```APIDOC ## PUT /api/users/{userId} ### Description Updates an existing user account's information. ### Method PUT ### Endpoint /api/users/{userId} ### Parameters #### Path Parameters - **userId** (string) - Required - The unique identifier of the user to update. #### Query Parameters None #### Request Body - **email** (string) - Optional - The new email address for the account. - **username** (string) - Optional - The new username for the account. ### Request Example ```json { "email": "john.doe.updated@example.com" } ``` ### Response #### Success Response (200 OK) - **message** (string) - A confirmation message indicating the user was updated. #### Response Example ```json { "message": "User account updated successfully." } ``` #### Error Response (404 Not Found) - **error** (string) - A message indicating that the user was not found. #### Error Response Example ```json { "error": "User with ID a1b2c3d4-e5f6-7890-1234-567890abcdef not found." } ``` #### Error Response (400 Bad Request) - **error** (string) - A message describing the error (e.g., "Invalid input data."). #### Error Response Example ```json { "error": "Invalid input data." } ``` ``` -------------------------------- ### SimaiParser.ParseAsync - Parse Complete Simai File from Stream Source: https://context7.com/lingfeng-bbben/majsimai/llms.txt Asynchronously reads a simai file from a stream, parses it into a SimaiFile object, and computes an MD5 hash for integrity verification. Supports custom encodings. ```APIDOC ## POST /api/users ### Description This endpoint allows for the creation of new user accounts. ### Method POST ### Endpoint /api/users ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **username** (string) - Required - The desired username for the new account. - **email** (string) - Required - The email address for the account. - **password** (string) - Required - The password for the account. ### Request Example ```json { "username": "johndoe", "email": "john.doe@example.com", "password": "securepassword123" } ``` ### Response #### Success Response (201 Created) - **userId** (string) - The unique identifier for the newly created user. - **message** (string) - A confirmation message. #### Response Example ```json { "userId": "a1b2c3d4-e5f6-7890-1234-567890abcdef", "message": "User account created successfully." } ``` #### Error Response (400 Bad Request) - **error** (string) - A message describing the error (e.g., "Username already exists."). #### Error Response Example ```json { "error": "Username already exists." } ``` ``` -------------------------------- ### Parse Simai Metadata Only Source: https://context7.com/lingfeng-bbben/majsimai/llms.txt Extracts only metadata from a Simai file, skipping the parsing of note charts for improved performance when only song information is needed. ```csharp using MajSimai; var filePath = "/path/to/maidata.txt"; using var fileStream = File.OpenRead(filePath); // Parse metadata only (notes are not parsed) SimaiMetadata metadata = await SimaiParser.ParseMetadataAsync(fileStream); Console.WriteLine($"Title: {metadata.Title}"); Console.WriteLine($"Artist: {metadata.Artist}"); Console.WriteLine($"Offset: {metadata.Offset}"); Console.WriteLine($"Hash: {metadata.Hash}"); // Access difficulty levels (7 slots for different difficulties) for (int i = 0; i < metadata.Levels.Length; i++) { var level = metadata.Levels[i]; var designer = metadata.Designers[i]; if (!string.IsNullOrEmpty(level)) { Console.WriteLine($"Difficulty {i + 1}: Level {level}, Designer: {designer}"); } } // Access raw fumen strings (unparsed chart data) for (int i = 0; i < metadata.Fumens.Length; i++) { var fumen = metadata.Fumens[i]; if (!string.IsNullOrEmpty(fumen)) { Console.WriteLine($"Fumen {i + 1} length: {fumen.Length} chars"); } } // Access custom commands foreach (var cmd in metadata.Commands) { Console.WriteLine($"Command: &{cmd.Prefix}={cmd.Value}"); } ``` -------------------------------- ### Deparse SimaiFile to Text Source: https://context7.com/lingfeng-bbben/majsimai/llms.txt Convert a modified SimaiFile object back into its text representation or stream it directly to a file with optional encoding. ```csharp using MajSimai; // Parse an existing file using var fileStream = File.OpenRead("/path/to/maidata.txt"); SimaiFile simaiFile = await SimaiParser.ParseAsync(fileStream); // Modify metadata simaiFile.Title = "Modified Title"; simaiFile.Artist = "Modified Artist"; simaiFile.Offset = 0.5f; // Convert back to string string output = SimaiParser.Deparse(simaiFile); Console.WriteLine(output); // Write to file using var outputStream = File.Create("/path/to/output/maidata.txt"); SimaiParser.Deparse(simaiFile, outputStream); // Async version with custom encoding using var outputStream2 = File.Create("/path/to/output/maidata_sjis.txt"); await SimaiParser.DeparseAsync(simaiFile, outputStream2, Encoding.GetEncoding("shift_jis")); ``` -------------------------------- ### Parse Chart String Asynchronously Source: https://context7.com/lingfeng-bbben/majsimai/llms.txt Use SimaiParser.ParseChartAsync to process raw chart strings directly into a SimaiChart object. This is useful for individual chart data. It supports parsing with level and designer metadata. ```csharp using MajSimai; // Parse a simple chart string string fumen = "(197){8}1,2,3,4,5,6,7,8,"; SimaiChart chart = await SimaiParser.ParseChartAsync(fumen); Console.WriteLine($"Parsed {chart.NoteTimings.Length} timing points"); // Parse with level and designer metadata string complexFumen = @"(120){4}1,2,3,4, {8}1/2,3/4,5/6,7/8, 1h[4:1],2h[4:2], 1-3[4:1],4-6[4:1],"; chart = await SimaiParser.ParseChartAsync("12+", "ChartDesigner", complexFumen.AsMemory()); Console.WriteLine($"Level: {chart.Level}"); Console.WriteLine($"Designer: {chart.Designer}"); Console.WriteLine($"Raw fumen: {chart.Fumen}"); Console.WriteLine($"Is empty: {chart.IsEmpty}"); // Iterate through note timings foreach (var timing in chart.NoteTimings) { Console.WriteLine($"Time: {timing.Timing:F3}s, BPM: {timing.Bpm}, Notes: {timing.Notes.Length}"); foreach (var note in timing.Notes) { Console.WriteLine($" - {note.Type} at position {note.StartPosition}"); } } // Access comma timings (beat markers) Console.WriteLine($"Total beats: {chart.CommaTimings.Length}"); ``` -------------------------------- ### Parse Chart String Synchronously Source: https://context7.com/lingfeng-bbben/majsimai/llms.txt Use SimaiParser.ParseChart for synchronous parsing of chart strings, offering control over level and designer metadata. It utilizes ReadOnlySpan for efficient memory handling. ```csharp using MajSimai; // Basic chart with BPM (120) and beat division {4} string fumen = "(120){4}1,2,3,4,1b,2b,3b,4b,"; SimaiChart chart = SimaiParser.ParseChart(fumen); // Chart with various note types string advancedFumen = @"(180){8} 1,1b,1x,1bx, 1h[8:4], 1-3[8:2],1-5[8:2], B1,B1f,B1h[8:4], 1/2/3, `1`2`3`4,"; chart = SimaiParser.ParseChart("13", "TestDesigner", advancedFumen); // Analyze note types var tapCount = 0; var holdCount = 0; var slideCount = 0; var touchCount = 0; foreach (var timing in chart.NoteTimings) { foreach (var note in timing.Notes) { switch (note.Type) { case SimaiNoteType.Tap: tapCount++; break; case SimaiNoteType.Hold: holdCount++; break; case SimaiNoteType.Slide: slideCount++; break; case SimaiNoteType.Touch: touchCount++; break; case SimaiNoteType.TouchHold: touchCount++; break; } } } Console.WriteLine($"Taps: {tapCount}, Holds: {holdCount}, Slides: {slideCount}, Touches: {touchCount}"); ``` -------------------------------- ### SimaiParser.Parse - Synchronous Parse from Stream Source: https://context7.com/lingfeng-bbben/majsimai/llms.txt Synchronously reads and parses a simai file from a stream. This method is suitable for environments where asynchronous operations are not required or preferred. Supports custom encodings. ```APIDOC ## GET /api/users/{userId} ### Description Retrieves the details of a specific user account. ### Method GET ### Endpoint /api/users/{userId} ### Parameters #### Path Parameters - **userId** (string) - Required - The unique identifier of the user to retrieve. #### Query Parameters None #### Request Body None ### Request Example ```http GET /api/users/a1b2c3d4-e5f6-7890-1234-567890abcdef HTTP/1.1 Host: example.com Accept: application/json ``` ### Response #### Success Response (200 OK) - **userId** (string) - The unique identifier for the user. - **username** (string) - The username of the user. - **email** (string) - The email address of the user. - **createdAt** (string) - The timestamp when the user account was created (ISO 8601 format). #### Response Example ```json { "userId": "a1b2c3d4-e5f6-7890-1234-567890abcdef", "username": "johndoe", "email": "john.doe@example.com", "createdAt": "2023-10-27T10:00:00Z" } ``` #### Error Response (404 Not Found) - **error** (string) - A message indicating that the user was not found. #### Error Response Example ```json { "error": "User with ID a1b2c3d4-e5f6-7890-1234-567890abcdef not found." } ``` ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.