├── .gitignore ├── BChartConsts.cs ├── BChartReader.cs ├── BChartUtils.cs ├── BChartWriter.cs ├── BinaryEx ├── BinStream.Read.cs ├── BinStream.Write.cs ├── BinUtils.Read.cs ├── BinUtils.ReadOnlySpanRead.cs ├── BinUtils.ReadOnlySpanRefRead.cs ├── BinUtils.RefRead.cs ├── BinUtils.RefWrite.cs ├── BinUtils.SpanRead.cs ├── BinUtils.SpanRefRead.cs ├── BinUtils.SpanWrite.cs ├── BinUtils.UnsafeRead.cs ├── BinUtils.UnsafeRefRead.cs ├── BinUtils.UnsafeRefWrite.cs ├── BinUtils.UnsafeWrite.cs ├── BinUtils.Write.cs └── BinUtils.cs ├── MSChartParser ├── ChartEditorExtentions │ └── NoteFunctions.cs ├── Engine │ ├── Core │ │ ├── EnumLookupTable.cs │ │ └── EnumX.cs │ └── Logger.cs ├── Game │ └── Misc │ │ ├── SongValidate.cs │ │ └── Utility.cs ├── Globals.cs ├── IO │ ├── Chart │ │ ├── ChartIOHelper.cs │ │ ├── ChartReader.cs │ │ └── ChartWriter.cs │ ├── ExportOptions.cs │ ├── MSCE │ │ └── MsceIOHelper.cs │ └── Midi │ │ ├── ByteSort.cs │ │ ├── MidIOHelper.cs │ │ ├── MidReader.cs │ │ └── MidWriter.cs └── Song │ ├── Chart.cs │ ├── Events │ ├── BPM.cs │ ├── ChartEvent.cs │ ├── ChartObject.cs │ ├── Event.cs │ ├── Note.cs │ ├── Section.cs │ ├── SongObject.cs │ ├── Starpower.cs │ ├── SyncTrack.cs │ └── TimeSignature.cs │ ├── LyricHelper.cs │ ├── Metadata.cs │ ├── Song.cs │ ├── SongConfig.cs │ ├── SongObjectHelper.cs │ └── TickFunctions.cs ├── Program.cs ├── README.md ├── bchart.csproj ├── chartFormat.txt └── license.txt /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | # Visual Studio Code 30 | .vscode/ 31 | # Uncomment if you have tasks that create the project's static files in wwwroot 32 | #wwwroot/ 33 | 34 | # Visual Studio 2017 auto generated files 35 | Generated\ Files/ 36 | 37 | # MSTest test Results 38 | [Tt]est[Rr]esult*/ 39 | [Bb]uild[Ll]og.* 40 | 41 | # NUNIT 42 | *.VisualState.xml 43 | TestResult.xml 44 | 45 | # Build Results of an ATL Project 46 | [Dd]ebugPS/ 47 | [Rr]eleasePS/ 48 | dlldata.c 49 | 50 | # Benchmark Results 51 | BenchmarkDotNet.Artifacts/ 52 | 53 | # .NET Core 54 | project.lock.json 55 | project.fragment.lock.json 56 | artifacts/ 57 | **/Properties/launchSettings.json 58 | 59 | # StyleCop 60 | StyleCopReport.xml 61 | 62 | # Files built by Visual Studio 63 | *_i.c 64 | *_p.c 65 | *_i.h 66 | *.ilk 67 | *.meta 68 | *.obj 69 | *.iobj 70 | *.pch 71 | *.pdb 72 | *.ipdb 73 | *.pgc 74 | *.pgd 75 | *.rsp 76 | *.sbr 77 | *.tlb 78 | *.tli 79 | *.tlh 80 | *.tmp 81 | *.tmp_proj 82 | *.log 83 | *.vspscc 84 | *.vssscc 85 | .builds 86 | *.pidb 87 | *.svclog 88 | *.scc 89 | 90 | # Chutzpah Test files 91 | _Chutzpah* 92 | 93 | # Visual C++ cache files 94 | ipch/ 95 | *.aps 96 | *.ncb 97 | *.opendb 98 | *.opensdf 99 | *.sdf 100 | *.cachefile 101 | *.VC.db 102 | *.VC.VC.opendb 103 | 104 | # Visual Studio profiler 105 | *.psess 106 | *.vsp 107 | *.vspx 108 | *.sap 109 | 110 | # Visual Studio Trace Files 111 | *.e2e 112 | 113 | # TFS 2012 Local Workspace 114 | $tf/ 115 | 116 | # Guidance Automation Toolkit 117 | *.gpState 118 | 119 | # ReSharper is a .NET coding add-in 120 | _ReSharper*/ 121 | *.[Rr]e[Ss]harper 122 | *.DotSettings.user 123 | 124 | # JustCode is a .NET coding add-in 125 | .JustCode 126 | 127 | # TeamCity is a build add-in 128 | _TeamCity* 129 | 130 | # DotCover is a Code Coverage Tool 131 | *.dotCover 132 | 133 | # AxoCover is a Code Coverage Tool 134 | .axoCover/* 135 | !.axoCover/settings.json 136 | 137 | # Visual Studio code coverage results 138 | *.coverage 139 | *.coveragexml 140 | 141 | # NCrunch 142 | _NCrunch_* 143 | .*crunch*.local.xml 144 | nCrunchTemp_* 145 | 146 | # MightyMoose 147 | *.mm.* 148 | AutoTest.Net/ 149 | 150 | # Web workbench (sass) 151 | .sass-cache/ 152 | 153 | # Installshield output folder 154 | [Ee]xpress/ 155 | 156 | # DocProject is a documentation generator add-in 157 | DocProject/buildhelp/ 158 | DocProject/Help/*.HxT 159 | DocProject/Help/*.HxC 160 | DocProject/Help/*.hhc 161 | DocProject/Help/*.hhk 162 | DocProject/Help/*.hhp 163 | DocProject/Help/Html2 164 | DocProject/Help/html 165 | 166 | # Click-Once directory 167 | publish/ 168 | 169 | # Publish Web Output 170 | *.[Pp]ublish.xml 171 | *.azurePubxml 172 | # Note: Comment the next line if you want to checkin your web deploy settings, 173 | # but database connection strings (with potential passwords) will be unencrypted 174 | *.pubxml 175 | *.publishproj 176 | 177 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 178 | # checkin your Azure Web App publish settings, but sensitive information contained 179 | # in these scripts will be unencrypted 180 | PublishScripts/ 181 | 182 | # NuGet Packages 183 | *.nupkg 184 | # The packages folder can be ignored because of Package Restore 185 | **/[Pp]ackages/* 186 | # except build/, which is used as an MSBuild target. 187 | !**/[Pp]ackages/build/ 188 | # Uncomment if necessary however generally it will be regenerated when needed 189 | #!**/[Pp]ackages/repositories.config 190 | # NuGet v3's project.json files produces more ignorable files 191 | *.nuget.props 192 | *.nuget.targets 193 | 194 | # Microsoft Azure Build Output 195 | csx/ 196 | *.build.csdef 197 | 198 | # Microsoft Azure Emulator 199 | ecf/ 200 | rcf/ 201 | 202 | # Windows Store app package directories and files 203 | AppPackages/ 204 | BundleArtifacts/ 205 | Package.StoreAssociation.xml 206 | _pkginfo.txt 207 | *.appx 208 | 209 | # Visual Studio cache files 210 | # files ending in .cache can be ignored 211 | *.[Cc]ache 212 | # but keep track of directories ending in .cache 213 | !*.[Cc]ache/ 214 | 215 | # Others 216 | ClientBin/ 217 | ~$* 218 | *~ 219 | *.dbmdl 220 | *.dbproj.schemaview 221 | *.jfm 222 | *.pfx 223 | *.publishsettings 224 | orleans.codegen.cs 225 | 226 | # Including strong name files can present a security risk 227 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 228 | #*.snk 229 | 230 | # Since there are multiple workflows, uncomment next line to ignore bower_components 231 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 232 | #bower_components/ 233 | 234 | # RIA/Silverlight projects 235 | Generated_Code/ 236 | 237 | # Backup & report files from converting an old project file 238 | # to a newer Visual Studio version. Backup files are not needed, 239 | # because we have git ;-) 240 | _UpgradeReport_Files/ 241 | Backup*/ 242 | UpgradeLog*.XML 243 | UpgradeLog*.htm 244 | ServiceFabricBackup/ 245 | *.rptproj.bak 246 | 247 | # SQL Server files 248 | *.mdf 249 | *.ldf 250 | *.ndf 251 | 252 | # Business Intelligence projects 253 | *.rdl.data 254 | *.bim.layout 255 | *.bim_*.settings 256 | *.rptproj.rsuser 257 | 258 | # Microsoft Fakes 259 | FakesAssemblies/ 260 | 261 | # GhostDoc plugin setting file 262 | *.GhostDoc.xml 263 | 264 | # Node.js Tools for Visual Studio 265 | .ntvs_analysis.dat 266 | node_modules/ 267 | 268 | # Visual Studio 6 build log 269 | *.plg 270 | 271 | # Visual Studio 6 workspace options file 272 | *.opt 273 | 274 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 275 | *.vbw 276 | 277 | # Visual Studio LightSwitch build output 278 | **/*.HTMLClient/GeneratedArtifacts 279 | **/*.DesktopClient/GeneratedArtifacts 280 | **/*.DesktopClient/ModelManifest.xml 281 | **/*.Server/GeneratedArtifacts 282 | **/*.Server/ModelManifest.xml 283 | _Pvt_Extensions 284 | 285 | # Paket dependency manager 286 | .paket/paket.exe 287 | paket-files/ 288 | 289 | # FAKE - F# Make 290 | .fake/ 291 | 292 | # JetBrains Rider 293 | .idea/ 294 | *.sln.iml 295 | 296 | # CodeRush 297 | .cr/ 298 | 299 | # Python Tools for Visual Studio (PTVS) 300 | __pycache__/ 301 | *.pyc 302 | 303 | # Cake - Uncomment if you are using it 304 | # tools/** 305 | # !tools/packages.config 306 | 307 | # Tabs Studio 308 | *.tss 309 | 310 | # Telerik's JustMock configuration file 311 | *.jmconfig 312 | 313 | # BizTalk build output 314 | *.btp.cs 315 | *.btm.cs 316 | *.odx.cs 317 | *.xsd.cs 318 | 319 | # OpenCover UI analysis results 320 | OpenCover/ 321 | 322 | # Azure Stream Analytics local run output 323 | ASALocalRun/ 324 | 325 | # MSBuild Binary and Structured Log 326 | *.binlog 327 | 328 | # NVidia Nsight GPU debugger configuration file 329 | *.nvuser 330 | 331 | # MFractors (Xamarin productivity tool) working folder 332 | .mfractor/ -------------------------------------------------------------------------------- /BChartConsts.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace BChart; 7 | 8 | public static class BChartConsts 9 | { 10 | public const byte EVENT_TEMPO = 0x01; 11 | public const byte EVENT_TIME_SIG = 0x02; 12 | public const byte EVENT_TEXT = 0x03; 13 | public const byte EVENT_SECTION = 0x04; 14 | public const byte EVENT_PHRASE = 0x05; 15 | public const byte EVENT_NOTE = 0x06; 16 | 17 | public const byte PHRASE_STARPOWER = 0x01; 18 | public const byte PHRASE_SOLO = 0x02; 19 | public const byte PHRASE_LYRICS_LINE = 0x03; 20 | 21 | public const byte DIFFICULTY_EASY = 0x00; 22 | public const byte DIFFICULTY_MEDIUM = 0x01; 23 | public const byte DIFFICULTY_HARD = 0x02; 24 | public const byte DIFFICULTY_EXPERT = 0x03; 25 | 26 | public const byte NOTE_UKN = 0xFF; 27 | 28 | public static class GuitarNotes 29 | { 30 | public const byte NOTE_OPEN = 0x00; 31 | public const byte NOTE_GREEN = 0x01; 32 | public const byte NOTE_RED = 0x02; 33 | public const byte NOTE_YELLOW = 0x03; 34 | public const byte NOTE_BLUE = 0x04; 35 | public const byte NOTE_ORANGE = 0x05; 36 | 37 | public const uint NOTE_MOD_TOGGLE_FORCED = 1; 38 | public const uint NOTE_MOD_FORCE_HOPO = 2; 39 | public const uint NOTE_MOD_FORCE_STRUM = 4; 40 | public const uint NOTE_MOD_TAP = 8; 41 | } 42 | 43 | public static class SixFretGuitarNotes 44 | { 45 | public const byte NOTE_OPEN = 0x00; 46 | public const byte NOTE_B1 = 0x01; 47 | public const byte NOTE_B2 = 0x02; 48 | public const byte NOTE_B3 = 0x03; 49 | public const byte NOTE_W1 = 0x04; 50 | public const byte NOTE_W2 = 0x05; 51 | public const byte NOTE_W3 = 0x06; 52 | 53 | public const uint NOTE_MOD_TOGGLE_FORCED = 1; 54 | public const uint NOTE_MOD_FORCE_HOPO = 2; 55 | public const uint NOTE_MOD_FORCE_STRUM = 4; 56 | public const uint NOTE_MOD_TAP = 8; 57 | } 58 | 59 | public static class DrumNotes 60 | { 61 | public const byte NOTE_KICK = 0x00; 62 | public const byte NOTE_RED = 0x01; 63 | public const byte NOTE_YELLOW = 0x02; 64 | public const byte NOTE_BLUE = 0x03; 65 | public const byte NOTE_GREEN = 0x04; 66 | public const byte NOTE_FIVE_LANE_GREEN = 0x05; 67 | 68 | public const uint NOTE_MOD_ACCENT = 1; 69 | public const uint NOTE_MOD_GHOST = 2; 70 | public const uint NOTE_MOD_CYMBAL = 4; 71 | public const uint NOTE_MOD_KICK_2 = 8; 72 | } 73 | 74 | public const byte INSTRUMENT_GUITAR = 0; 75 | public const byte INSTRUMENT_GUITAR_SIX = 1; 76 | public const byte INSTRUMENT_BASS = 2; 77 | public const byte INSTRUMENT_BASS_SIX = 3; 78 | public const byte INSTRUMENT_RHYTHM = 4; 79 | public const byte INSTRUMENT_COOP = 5; 80 | public const byte INSTRUMENT_KEYS = 6; 81 | public const byte INSTRUMENT_DRUMS = 7; 82 | public const byte INSTRUMENT_VOCALS = 8; 83 | public const byte INSTRUMENT_UKN = 0xFF; 84 | 85 | public static uint HeaderChunkName = BChartUtils.GetChunkNameToInt("BCHF"); 86 | public static uint TempoChunkName = BChartUtils.GetChunkNameToInt("SYNC"); 87 | public static uint GlobalEventsChunkName = BChartUtils.GetChunkNameToInt("EVTS"); 88 | public static uint InstrumentChunkName = BChartUtils.GetChunkNameToInt("INST"); 89 | public static uint DifficultyChunkName = BChartUtils.GetChunkNameToInt("DIFF"); 90 | 91 | } -------------------------------------------------------------------------------- /BChartReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Buffers; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Text; 6 | using BinaryEx; 7 | using MoonscraperChartEditor.Song; 8 | using MoonscraperChartEditor.Song.IO; 9 | using static MoonscraperChartEditor.Song.Song; 10 | 11 | namespace BChart; 12 | 13 | public static class BChartReader 14 | { 15 | public static string ReadTextEventData(Span data) 16 | { 17 | return Encoding.UTF8.GetString(data); 18 | } 19 | 20 | public static (byte type, uint length) ReadPhrase(Span data) 21 | { 22 | int pos = 0; 23 | byte type = data.ReadByte(ref pos); 24 | uint length = data.ReadUInt32LE(ref pos); 25 | return (type, length); 26 | } 27 | 28 | public static Note ReadNoteData(Span data, Instrument inst, uint tick) 29 | { 30 | byte baseEventLength = 5; 31 | int pos = 0; 32 | uint modifiers = 0; 33 | byte noteValue = data.ReadByte(ref pos); 34 | uint tickLength = data.ReadUInt32LE(ref pos); 35 | 36 | if (data.Length > baseEventLength) 37 | { 38 | modifiers = data.ReadUInt32LE(ref pos); 39 | } 40 | 41 | Note note = new Note(tick, BChartUtils.BChartToMoonNote(inst, noteValue), tickLength); 42 | BChartUtils.ApplyBChartModToNote(inst, note, modifiers); 43 | return note; 44 | } 45 | 46 | public static BPM ReadTempoData(Span data, uint tickPos) 47 | { 48 | return new BPM(tickPos, (uint)(60000000000.0 / data.ReadUInt32LE(0))); 49 | } 50 | 51 | public static TimeSignature ReadTSData(Span data, uint tickPos) 52 | { 53 | int pos = 0; 54 | var num = data.ReadByte(ref pos); 55 | var den = data.ReadByte(ref pos); 56 | 57 | return new TimeSignature(tickPos, num, den); 58 | } 59 | 60 | 61 | public static byte ReadEventBytes(Span data, ref int pos, out Span eventSpanOut, ref uint tickPos) 62 | { 63 | uint outData; 64 | byte firstByte = data.ReadByte(ref pos); 65 | switch (firstByte) 66 | { 67 | case 254: 68 | outData = data.ReadUInt16LE(ref pos); 69 | break; 70 | case 255: 71 | outData = data.ReadUInt32LE(ref pos); 72 | break; 73 | default: 74 | outData = firstByte; 75 | break; 76 | } 77 | 78 | tickPos += outData; 79 | byte eventType = data.ReadByte(ref pos); 80 | byte eventLength = data.ReadByte(ref pos); 81 | 82 | eventSpanOut = data.Slice(pos, eventLength); 83 | pos += eventLength; 84 | return eventType; 85 | } 86 | 87 | public static (uint version, uint instrumentCount) ReadHeader(Span data, Song song) 88 | { 89 | int pos = 0; 90 | 91 | uint version = data.ReadUInt16LE(ref pos); 92 | uint resolution = data.ReadUInt16LE(ref pos); 93 | uint instrumentCount = data.ReadUInt16LE(ref pos); 94 | 95 | song.resolution = resolution; 96 | 97 | return (version, instrumentCount); 98 | } 99 | 100 | public static void ReadTempoMap(Span data, Song song) 101 | { 102 | int pos = 0; 103 | uint eventCount = data.ReadUInt32LE(ref pos); 104 | uint tickPos = 0; 105 | for (int i = 0; i < eventCount; ++i) 106 | { 107 | byte eventType = ReadEventBytes(data, ref pos, out Span dataSpan, ref tickPos); 108 | 109 | switch (eventType) 110 | { 111 | case BChartConsts.EVENT_TEMPO: 112 | song.Add(ReadTempoData(dataSpan, tickPos), false); 113 | break; 114 | case BChartConsts.EVENT_TIME_SIG: 115 | song.Add(ReadTSData(dataSpan, tickPos), false); 116 | break; 117 | } 118 | } 119 | song.UpdateCache(); 120 | } 121 | 122 | public static void ReadGlobalEvents(Span data, Song song) 123 | { 124 | int pos = 0; 125 | uint eventCount = data.ReadUInt32LE(ref pos); 126 | uint tickPos = 0; 127 | for (int i = 0; i < eventCount; ++i) 128 | { 129 | 130 | byte eventType = ReadEventBytes(data, ref pos, out Span dataSpan, ref tickPos); 131 | 132 | switch (eventType) 133 | { 134 | case BChartConsts.EVENT_SECTION: 135 | { 136 | 137 | string txt = ReadTextEventData(dataSpan); 138 | song.Add(new Section(txt, tickPos), false); 139 | break; 140 | } 141 | case BChartConsts.EVENT_TEXT: 142 | { 143 | string txt = ReadTextEventData(dataSpan); 144 | song.Add(new Event(txt, tickPos), false); 145 | break; 146 | } 147 | } 148 | } 149 | song.UpdateCache(); 150 | } 151 | 152 | public static void ReadDifficulty(Span data, Song song, Instrument inst) 153 | { 154 | int pos = 0; 155 | Difficulty diff = BChartUtils.BChartToMoonDiff(data.ReadByte(ref pos)); 156 | int eventCount = data.ReadInt32LE(ref pos); 157 | List soloEndEvents = new List(); 158 | Console.WriteLine($"{inst} {diff}"); 159 | var chart = song.GetChart(inst, diff); 160 | uint tickPos = 0; 161 | for (int i = 0; i < eventCount; ++i) 162 | { 163 | byte eventType = ReadEventBytes(data, ref pos, out Span dataSpan, ref tickPos); 164 | 165 | 166 | for (int j = 0; j < soloEndEvents.Count; ++j) 167 | { 168 | var end = soloEndEvents[j]; 169 | if (tickPos > end.tick) 170 | { 171 | chart.Add(end, false); 172 | soloEndEvents.RemoveAt(j); 173 | j--; 174 | } 175 | } 176 | 177 | switch (eventType) 178 | { 179 | case BChartConsts.EVENT_NOTE: 180 | Note note = ReadNoteData(dataSpan, inst, tickPos); 181 | chart.Add(note, false); 182 | break; 183 | case BChartConsts.EVENT_PHRASE: 184 | { 185 | (byte phraseType, uint length) = ReadPhrase(dataSpan); 186 | 187 | if (phraseType == BChartConsts.PHRASE_STARPOWER) 188 | { 189 | chart.Add(new Starpower(tickPos, length), false); 190 | } 191 | else if (phraseType == BChartConsts.PHRASE_SOLO) 192 | { 193 | var start = new ChartEvent(tickPos, MidIOHelper.SoloEventText); 194 | var end = new ChartEvent(tickPos + length, MidIOHelper.SoloEndEventText); 195 | soloEndEvents.Add(end); 196 | chart.Add(start, false); 197 | } 198 | break; 199 | } 200 | case BChartConsts.EVENT_TEXT: 201 | string txt = ReadTextEventData(dataSpan); 202 | chart.Add(new ChartEvent(tickPos, txt), false); 203 | break; 204 | default: 205 | // Skip any unknown event types 206 | break; 207 | } 208 | } 209 | chart.UpdateCache(); 210 | } 211 | 212 | public static (Instrument inst, byte count) ReadInstrument(Span data) 213 | { 214 | int pos = 0; 215 | Instrument inst = BChartUtils.BChartToMoonInstrument(data.ReadByte(ref pos)); 216 | byte count = data.ReadByte(ref pos); 217 | 218 | return (inst, count); 219 | } 220 | 221 | public static Song ReadBChart(string path) 222 | { 223 | Song song = new Song(); 224 | string directory = Path.GetDirectoryName(path); 225 | MsceIOHelper.DiscoverAudio(directory, song); 226 | 227 | byte[] fileData = File.ReadAllBytes(path); 228 | Span data = fileData; 229 | int pos = 0; 230 | Instrument currentInstrument = Instrument.Unrecognised; 231 | byte diffCount = 0; 232 | 233 | uint version; 234 | uint instrumentCount; 235 | 236 | while (pos < data.Length) 237 | { 238 | // Console.WriteLine(pos); 239 | var chunkID = data.ReadUInt32LE(ref pos); 240 | var chunkLength = data.ReadInt32LE(ref pos); 241 | var chunkData = data.Slice(pos, chunkLength); 242 | pos += chunkLength; 243 | 244 | if (chunkID == BChartConsts.HeaderChunkName) 245 | { 246 | (version, instrumentCount) = ReadHeader(chunkData, song); 247 | } 248 | else if (chunkID == BChartConsts.TempoChunkName) 249 | { 250 | ReadTempoMap(chunkData, song); 251 | } 252 | else if (chunkID == BChartConsts.GlobalEventsChunkName) 253 | { 254 | ReadGlobalEvents(chunkData, song); 255 | } 256 | else if (chunkID == BChartConsts.InstrumentChunkName) 257 | { 258 | (currentInstrument, diffCount) = ReadInstrument(chunkData); 259 | } 260 | else if (chunkID == BChartConsts.DifficultyChunkName) 261 | { 262 | ReadDifficulty(chunkData, song, currentInstrument); 263 | } 264 | 265 | } 266 | 267 | return song; 268 | } 269 | } 270 | -------------------------------------------------------------------------------- /BChartWriter.cs: -------------------------------------------------------------------------------- 1 | 2 | using System; 3 | using System.IO; 4 | using MoonscraperChartEditor.Song; 5 | using MoonscraperChartEditor.Song.IO; 6 | using BinaryEx; 7 | using System.Collections.Generic; 8 | using static MoonscraperChartEditor.Song.Song; 9 | using System.Text; 10 | using System.Buffers; 11 | 12 | namespace BChart; 13 | 14 | public static class BChartWriter 15 | { 16 | // Event serialization functions 17 | 18 | public static void WriteTextEvent(Stream stream, ChartEvent ev) 19 | { 20 | byte[] bytes = BChartUtils.GetStringAsBytes(ev.eventName, out var outSpan); 21 | 22 | // limit string size to byte max value 23 | if (outSpan.Length > 255) 24 | { 25 | outSpan = outSpan.Slice(0, 255); 26 | } 27 | WriteTicks(stream, ev.tick); 28 | stream.WriteByte(BChartConsts.EVENT_TEXT); 29 | stream.WriteByte((byte)outSpan.Length); 30 | stream.Write(outSpan); 31 | 32 | ArrayPool.Shared.Return(bytes); 33 | } 34 | 35 | public static void WriteTextEvent(Stream stream, Event ev) 36 | { 37 | byte[] bytes = BChartUtils.GetStringAsBytes(ev.title, out var outSpan); 38 | 39 | // limit string size to byte max value 40 | if (outSpan.Length > 255) 41 | { 42 | outSpan = outSpan.Slice(0, 255); 43 | } 44 | WriteTicks(stream, ev.tick); 45 | stream.WriteByte(BChartConsts.EVENT_TEXT); 46 | stream.WriteByte((byte)outSpan.Length); 47 | stream.Write(outSpan); 48 | 49 | ArrayPool.Shared.Return(bytes); 50 | } 51 | 52 | public static void WriteSection(Stream stream, Section section) 53 | { 54 | byte[] bytes = BChartUtils.GetStringAsBytes(section.title, out var outSpan); 55 | 56 | // limit string size to byte max value 57 | if (outSpan.Length > 255) 58 | { 59 | outSpan = outSpan.Slice(0, 255); 60 | } 61 | WriteTicks(stream, section.tick); 62 | stream.WriteByte(BChartConsts.EVENT_SECTION); 63 | stream.WriteByte((byte)outSpan.Length); 64 | stream.Write(outSpan); 65 | 66 | ArrayPool.Shared.Return(bytes); 67 | } 68 | 69 | public static void WritePhrase(Stream stream, byte phraseType, uint tick, uint tickLength) 70 | { 71 | WriteTicks(stream, tick); 72 | stream.WriteByte(BChartConsts.EVENT_PHRASE); 73 | stream.WriteByte(5); 74 | stream.WriteByte(phraseType); 75 | stream.WriteUInt32LE(tickLength); 76 | } 77 | 78 | public static void WriteTimeSignature(Stream stream, TimeSignature ts) 79 | { 80 | WriteTicks(stream, ts.tick); 81 | stream.WriteByte(BChartConsts.EVENT_TIME_SIG); 82 | stream.WriteByte(2); 83 | stream.WriteByte((byte)ts.numerator); 84 | stream.WriteByte((byte)ts.denominator); 85 | } 86 | 87 | const long microsecondsPerMinute = 60000000; 88 | public static void WriteTempo(Stream stream, BPM bpm) 89 | { 90 | uint microSecondsPerQuarter = (uint)(microsecondsPerMinute * 1000 / bpm.value); 91 | WriteTicks(stream, bpm.tick); 92 | stream.WriteByte(BChartConsts.EVENT_TEMPO); 93 | stream.WriteByte(4); 94 | stream.WriteUInt32LE(microSecondsPerQuarter); 95 | } 96 | 97 | 98 | private static uint previousTickPos = 0; 99 | public static void WriteTicks(Stream stream, uint tickPos) 100 | { 101 | if (tickPos < previousTickPos) 102 | { 103 | previousTickPos = 0; 104 | } 105 | uint deltaTicks = tickPos - previousTickPos; 106 | if (deltaTicks > ushort.MaxValue) 107 | { 108 | stream.WriteByte(255); 109 | stream.WriteUInt32LE(deltaTicks); 110 | } 111 | else if (deltaTicks > 253) 112 | { 113 | stream.WriteByte(254); 114 | stream.WriteUInt16LE((ushort)deltaTicks); 115 | } 116 | else 117 | { 118 | stream.WriteByte((byte)deltaTicks); 119 | } 120 | previousTickPos = tickPos; 121 | } 122 | 123 | public static bool WriteNote(Stream stream, Note note, Instrument inst) 124 | { 125 | uint eventLength = 5; // Note event is atleast 6 bytes 126 | uint supplementalDataLength = 4; // Note modifiers supplemental data is 4 bytes 127 | 128 | uint modifiers = BChartUtils.MoonNoteToBChartMod(inst, note); 129 | 130 | byte noteOut = BChartUtils.MoonNoteToBChart(inst, note); 131 | 132 | // don't write out unknown note 133 | if (noteOut == BChartConsts.NOTE_UKN) 134 | { 135 | return false; 136 | } 137 | 138 | byte byteLength = (byte)eventLength; 139 | 140 | if (modifiers > 0) 141 | { 142 | byteLength += (byte)supplementalDataLength; 143 | } 144 | 145 | WriteTicks(stream, note.tick); 146 | stream.WriteByte(BChartConsts.EVENT_NOTE); 147 | stream.WriteByte(byteLength); 148 | stream.WriteByte(noteOut); 149 | stream.WriteUInt32LE(note.length); 150 | 151 | if (modifiers > 0) 152 | { 153 | stream.WriteUInt32LE(modifiers); 154 | } 155 | return true; 156 | } 157 | 158 | public static void WriteChunk(Stream stream, uint chunkId, Action action, Action preAction = null) 159 | { 160 | // serialize chunk data to memory stream 161 | using (var ms = new MemoryStream()) 162 | { 163 | action?.Invoke(ms); 164 | using (var ms2 = new MemoryStream()) 165 | { 166 | preAction?.Invoke(ms2); 167 | stream.WriteUInt32LE(chunkId); 168 | stream.WriteInt32LE((int)(ms.Length + ms2.Length)); 169 | stream.Write(ms2.GetBuffer(), 0, (int)ms2.Length); 170 | stream.Write(ms.GetBuffer(), 0, (int)ms.Length); 171 | } 172 | } 173 | } 174 | 175 | // Chunk serialization functions 176 | public static void WriteHeader(Stream stream, uint instrumentCount, uint resolution) 177 | { 178 | // header 179 | const int headerLength = 6; 180 | stream.WriteUInt32LE(BChartConsts.HeaderChunkName); // BCHF 181 | stream.WriteInt32LE(headerLength); 182 | stream.WriteUInt16LE(1); // version 1 183 | stream.WriteUInt16LE((ushort)resolution); 184 | stream.WriteUInt16LE((ushort)instrumentCount); 185 | } 186 | 187 | public static void WriteTempoMap(Stream stream, IList tempoMap) 188 | { 189 | int savedEvents = 0; 190 | void WriteData(Stream stre) 191 | { 192 | foreach (var tempo in tempoMap) 193 | { 194 | if (tempo is TimeSignature ts) 195 | { 196 | savedEvents++; 197 | WriteTimeSignature(stre, ts); 198 | } 199 | else if (tempo is BPM bpm) 200 | { 201 | savedEvents++; 202 | WriteTempo(stre, bpm); 203 | } 204 | } 205 | } 206 | 207 | void PreDataWrite(Stream stre) 208 | { 209 | // write this to the outstream directly before the data gets written 210 | stre.WriteInt32LE(savedEvents); 211 | } 212 | 213 | WriteChunk(stream, BChartConsts.TempoChunkName, WriteData, PreDataWrite); // SYNC 214 | 215 | } 216 | 217 | public static void WriteGlobalEvents(Stream stream, IList events) 218 | { 219 | int savedEvents = 0; 220 | void WriteData(Stream stre) 221 | { 222 | foreach (var ev in events) 223 | { 224 | if (ev is Section section) 225 | { 226 | savedEvents++; 227 | WriteSection(stre, section); 228 | } 229 | else 230 | { 231 | savedEvents++; 232 | WriteTextEvent(stre, ev); 233 | } 234 | } 235 | } 236 | 237 | void PreDataWrite(Stream stre) 238 | { 239 | // write this to the outstream directly before the data gets written 240 | stre.WriteInt32LE(savedEvents); 241 | } 242 | WriteChunk(stream, BChartConsts.GlobalEventsChunkName, WriteData, PreDataWrite); // EVTS 243 | 244 | } 245 | 246 | public static void WriteInstrument(Stream stream, Instrument inst, Dictionary diffs) 247 | { 248 | void WriteData(Stream stre) 249 | { 250 | stre.WriteByte(BChartUtils.MoonInstrumentToBChart(inst)); 251 | stre.WriteByte((byte)diffs.Count); 252 | // TODO per instrument events track? 253 | } 254 | WriteChunk(stream, BChartConsts.InstrumentChunkName, WriteData); // INST 255 | foreach (var data in diffs) 256 | { 257 | WriteDifficulty(stream, inst, data.Key, data.Value); 258 | } 259 | } 260 | 261 | public static void WriteDifficulty(Stream stream, Instrument inst, Difficulty diff, Chart chart) 262 | { 263 | int savedEvents = 0; 264 | void WriteData(Stream stre) 265 | { 266 | int i = 0; 267 | foreach (var ev in chart.chartObjects) 268 | { 269 | ++i; 270 | switch (ev) 271 | { 272 | case Note note: 273 | if (WriteNote(stre, note, inst)) 274 | { 275 | savedEvents++; 276 | } 277 | break; 278 | case Starpower sp: 279 | savedEvents++; 280 | WritePhrase(stre, BChartConsts.PHRASE_STARPOWER, sp.tick, sp.length); 281 | break; 282 | case ChartEvent chEv: 283 | { 284 | if (chEv.eventName == MidIOHelper.SoloEventText) 285 | { 286 | bool foundMatch = false; 287 | // Find matching soloEnd 288 | for (int j = i; j < chart.chartObjects.Count; ++i) 289 | { 290 | if (chart.chartObjects[i] is ChartEvent chNext) 291 | { 292 | if (chNext.eventName == MidIOHelper.SoloEndEventText) 293 | { 294 | foundMatch = true; 295 | savedEvents++; 296 | WritePhrase(stre, BChartConsts.PHRASE_SOLO, chEv.tick, chNext.tick - chEv.tick); 297 | break; 298 | } 299 | } 300 | } 301 | if (!foundMatch) 302 | { 303 | savedEvents++; 304 | // Make length match until the last chart event 305 | var last = chart.chartObjects[chart.chartObjects.Count - 1]; 306 | WritePhrase(stre, BChartConsts.PHRASE_SOLO, chEv.tick, last.tick - chEv.tick); 307 | } 308 | } 309 | else if (chEv.eventName == MidIOHelper.SoloEndEventText) 310 | { 311 | continue; 312 | } 313 | else 314 | { 315 | savedEvents++; 316 | WriteTextEvent(stre, chEv); 317 | } 318 | } 319 | break; 320 | } 321 | } 322 | } 323 | void PreDataWrite(Stream stre) 324 | { 325 | // write this to the outstream directly before the data gets written 326 | stre.WriteByte(BChartUtils.MoonDiffToBChart(diff)); 327 | stre.WriteInt32LE(savedEvents); 328 | } 329 | WriteChunk(stream, BChartConsts.DifficultyChunkName, WriteData, PreDataWrite); // DIFF 330 | } 331 | 332 | /// 333 | /// Serialize and save instance as BChart file format 334 | /// 335 | /// File path to save to 336 | /// Instance to serialize 337 | public static void WriteToFile(string outputPath, Song song) 338 | { 339 | Dictionary> charts = new Dictionary>(); 340 | var instruments = Enum.GetValues(); 341 | var diffs = Enum.GetValues(); 342 | 343 | foreach (var inst in instruments) 344 | { 345 | foreach (var diff in diffs) 346 | { 347 | try 348 | { 349 | var chart = song.GetChart(inst, diff); 350 | if (chart != null) 351 | { 352 | // Skip any empty tracks 353 | if (chart.note_count == 0) 354 | { 355 | continue; 356 | } 357 | if (!charts.ContainsKey(inst)) 358 | { 359 | charts[inst] = new Dictionary(); 360 | } 361 | charts[inst][diff] = chart; 362 | 363 | } 364 | } 365 | catch 366 | { 367 | } 368 | } 369 | } 370 | if (File.Exists(outputPath)) 371 | { 372 | File.Delete(outputPath); 373 | } 374 | using (var fs = File.OpenWrite(outputPath)) 375 | { 376 | WriteHeader(fs, (uint)charts.Values.Count, (uint)Math.Round(song.resolution)); 377 | WriteTempoMap(fs, song.syncTrack); 378 | WriteGlobalEvents(fs, song.eventsAndSections); 379 | 380 | foreach (var inst in charts) 381 | { 382 | Console.WriteLine($"Writing {inst.Key}"); 383 | WriteInstrument(fs, inst.Key, inst.Value); 384 | } 385 | } 386 | } 387 | 388 | } 389 | -------------------------------------------------------------------------------- /BinaryEx/BinStream.Read.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2022 Matthew Sitton 2 | // BSD License - See LICENSE in the project root for license information. 3 | using System; 4 | using System.Diagnostics; 5 | using System.IO; 6 | using System.Runtime.CompilerServices; 7 | 8 | namespace BinaryEx 9 | { 10 | public static partial class BinStream 11 | { 12 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 13 | public static UInt32 ReadUInt24LE(this Stream data) 14 | { 15 | Debug.Assert(data.CanRead); 16 | 17 | byte[] scratch = EnsureScratch(); 18 | 19 | data.Read(scratch, 0, 3); 20 | 21 | return scratch.ReadUInt24LE(0); 22 | } 23 | 24 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 25 | public static UInt32 ReadUInt24BE(this Stream data) 26 | { 27 | Debug.Assert(data.CanRead); 28 | 29 | byte[] scratch = EnsureScratch(); 30 | 31 | data.Read(scratch, 0, 3); 32 | 33 | return scratch.ReadUInt24BE(0); 34 | } 35 | 36 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 37 | public static Int64 ReadInt64LE(this Stream data) 38 | { 39 | Debug.Assert(data.CanRead); 40 | 41 | byte[] scratch = EnsureScratch(); 42 | 43 | data.Read(scratch, 0, 8); 44 | 45 | return scratch.ReadInt64LE(0); 46 | } 47 | 48 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 49 | public static Int64 ReadInt64BE(this Stream data) 50 | { 51 | Debug.Assert(data.CanRead); 52 | 53 | byte[] scratch = EnsureScratch(); 54 | 55 | data.Read(scratch, 0, 8); 56 | 57 | return scratch.ReadInt64BE(0); 58 | } 59 | 60 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 61 | public static Int32 ReadInt32LE(this Stream data) 62 | { 63 | Debug.Assert(data.CanRead); 64 | 65 | byte[] scratch = EnsureScratch(); 66 | 67 | data.Read(scratch, 0, 4); 68 | 69 | return scratch.ReadInt32LE(0); 70 | } 71 | 72 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 73 | public static Int32 ReadInt32BE(this Stream data) 74 | { 75 | Debug.Assert(data.CanRead); 76 | 77 | byte[] scratch = EnsureScratch(); 78 | 79 | data.Read(scratch, 0, 4); 80 | 81 | return scratch.ReadInt32BE(0); 82 | } 83 | 84 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 85 | public static Int16 ReadInt16LE(this Stream data) 86 | { 87 | Debug.Assert(data.CanRead); 88 | 89 | byte[] scratch = EnsureScratch(); 90 | 91 | data.Read(scratch, 0, 2); 92 | 93 | return scratch.ReadInt16LE(0); 94 | } 95 | 96 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 97 | public static Int16 ReadInt16BE(this Stream data) 98 | { 99 | Debug.Assert(data.CanRead); 100 | 101 | byte[] scratch = EnsureScratch(); 102 | 103 | data.Read(scratch, 0, 2); 104 | 105 | return scratch.ReadInt16BE(0); 106 | } 107 | 108 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 109 | public static sbyte ReadSByte(this Stream data) 110 | { 111 | Debug.Assert(data.CanRead); 112 | 113 | byte[] scratch = EnsureScratch(); 114 | 115 | data.Read(scratch, 0, 1); 116 | 117 | return scratch.ReadSByte(0); 118 | } 119 | 120 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 121 | public static UInt64 ReadUInt64LE(this Stream data) 122 | { 123 | Debug.Assert(data.CanRead); 124 | 125 | byte[] scratch = EnsureScratch(); 126 | 127 | data.Read(scratch, 0, 8); 128 | 129 | return scratch.ReadUInt64LE(0); 130 | } 131 | 132 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 133 | public static UInt64 ReadUInt64BE(this Stream data) 134 | { 135 | Debug.Assert(data.CanRead); 136 | 137 | byte[] scratch = EnsureScratch(); 138 | 139 | data.Read(scratch, 0, 8); 140 | 141 | return scratch.ReadUInt64LE(0); 142 | } 143 | 144 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 145 | public static UInt32 ReadUInt32LE(this Stream data) 146 | { 147 | Debug.Assert(data.CanRead); 148 | 149 | byte[] scratch = EnsureScratch(); 150 | 151 | data.Read(scratch, 0, 4); 152 | 153 | return scratch.ReadUInt32LE(0); 154 | } 155 | 156 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 157 | public static UInt32 ReadUInt32BE(this Stream data) 158 | { 159 | Debug.Assert(data.CanRead); 160 | 161 | byte[] scratch = EnsureScratch(); 162 | 163 | data.Read(scratch, 0, 4); 164 | 165 | return scratch.ReadUInt32BE(0); 166 | } 167 | 168 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 169 | public static UInt16 ReadUInt16LE(this Stream data) 170 | { 171 | Debug.Assert(data.CanRead); 172 | 173 | byte[] scratch = EnsureScratch(); 174 | 175 | data.Read(scratch, 0, 2); 176 | 177 | return scratch.ReadUInt16LE(0); 178 | } 179 | 180 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 181 | public static UInt16 ReadUInt16BE(this Stream data) 182 | { 183 | Debug.Assert(data.CanRead); 184 | 185 | byte[] scratch = EnsureScratch(); 186 | 187 | data.Read(scratch, 0, 2); 188 | 189 | return scratch.ReadUInt16BE(0); 190 | } 191 | 192 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 193 | public static byte ReadByte(this Stream data) 194 | { 195 | Debug.Assert(data.CanRead); 196 | 197 | byte[] scratch = EnsureScratch(); 198 | 199 | data.Read(scratch, 0, 1); 200 | 201 | return scratch.ReadByte(0); 202 | } 203 | } 204 | } -------------------------------------------------------------------------------- /BinaryEx/BinStream.Write.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2022 Matthew Sitton 2 | // BSD License - See LICENSE in the project root for license information. 3 | using System; 4 | using System.Diagnostics; 5 | using System.IO; 6 | using System.Runtime.CompilerServices; 7 | 8 | namespace BinaryEx 9 | { 10 | public static partial class BinStream 11 | { 12 | [ThreadStatic] static byte[] scratchData; 13 | 14 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 15 | private static byte[] EnsureScratch() 16 | { 17 | if (scratchData == null) 18 | { 19 | scratchData = new byte[8]; 20 | } 21 | 22 | return scratchData; 23 | } 24 | 25 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 26 | public static void WriteInt24BE(this Stream data, Int32 value) 27 | { 28 | Debug.Assert(data.CanWrite); 29 | 30 | byte[] scratch = EnsureScratch(); 31 | 32 | scratch.WriteInt24BE(0, value); 33 | data.Write(scratch, 0, 4); 34 | } 35 | 36 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 37 | public static void WriteInt24LE(this Stream data, Int32 value) 38 | { 39 | Debug.Assert(data.CanWrite); 40 | 41 | byte[] scratch = EnsureScratch(); 42 | 43 | scratch.WriteInt24LE(0, value); 44 | data.Write(scratch, 0, 4); 45 | } 46 | 47 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 48 | public static void WriteInt64BE(this Stream data, Int64 value) 49 | { 50 | Debug.Assert(data.CanWrite); 51 | 52 | byte[] scratch = EnsureScratch(); 53 | 54 | scratch.WriteInt64BE(0, value); 55 | data.Write(scratch, 0, 8); 56 | } 57 | 58 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 59 | public static void WriteInt64LE(this Stream data, Int64 value) 60 | { 61 | Debug.Assert(data.CanWrite); 62 | 63 | byte[] scratch = EnsureScratch(); 64 | 65 | scratch.WriteInt64LE(0, value); 66 | data.Write(scratch, 0, 8); 67 | } 68 | 69 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 70 | public static void WriteInt32BE(this Stream data, Int32 value) 71 | { 72 | Debug.Assert(data.CanWrite); 73 | 74 | byte[] scratch = EnsureScratch(); 75 | 76 | scratch.WriteInt32BE(0, value); 77 | data.Write(scratch, 0, 4); 78 | } 79 | 80 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 81 | public static void WriteInt32LE(this Stream data, Int32 value) 82 | { 83 | Debug.Assert(data.CanWrite); 84 | 85 | byte[] scratch = EnsureScratch(); 86 | 87 | scratch.WriteInt32LE(0, value); 88 | data.Write(scratch, 0, 4); 89 | } 90 | 91 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 92 | public static void WriteInt16BE(this Stream data, Int16 value) 93 | { 94 | Debug.Assert(data.CanWrite); 95 | 96 | byte[] scratch = EnsureScratch(); 97 | 98 | scratch.WriteInt16BE(0, value); 99 | data.Write(scratch, 0, 2); 100 | } 101 | 102 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 103 | public static void WriteInt16LE(this Stream data, Int16 value) 104 | { 105 | Debug.Assert(data.CanWrite); 106 | 107 | byte[] scratch = EnsureScratch(); 108 | 109 | scratch.WriteInt16LE(0, value); 110 | data.Write(scratch, 0, 2); 111 | } 112 | 113 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 114 | public static void WriteSByte(this Stream data, sbyte value) 115 | { 116 | Debug.Assert(data.CanWrite); 117 | data.WriteByte((byte)value); 118 | } 119 | 120 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 121 | public static void WriteUInt24BE(this Stream data, UInt32 value) 122 | { 123 | Debug.Assert(data.CanWrite); 124 | 125 | byte[] scratch = EnsureScratch(); 126 | 127 | scratch.WriteUInt24BE(0, value); 128 | data.Write(scratch, 0, 4); 129 | } 130 | 131 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 132 | public static void WriteUInt24LE(this Stream data, UInt32 value) 133 | { 134 | Debug.Assert(data.CanWrite); 135 | 136 | byte[] scratch = EnsureScratch(); 137 | 138 | scratch.WriteUInt24LE(0, value); 139 | data.Write(scratch, 0, 4); 140 | } 141 | 142 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 143 | public static void WriteUInt64BE(this Stream data, UInt64 value) 144 | { 145 | Debug.Assert(data.CanWrite); 146 | 147 | byte[] scratch = EnsureScratch(); 148 | 149 | scratch.WriteUInt64BE(0, value); 150 | data.Write(scratch, 0, 8); 151 | } 152 | 153 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 154 | public static void WriteUInt64LE(this Stream data, UInt64 value) 155 | { 156 | Debug.Assert(data.CanWrite); 157 | 158 | byte[] scratch = EnsureScratch(); 159 | 160 | scratch.WriteUInt64LE(0, value); 161 | data.Write(scratch, 0, 8); 162 | } 163 | 164 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 165 | public static void WriteUInt32BE(this Stream data, UInt32 value) 166 | { 167 | Debug.Assert(data.CanWrite); 168 | 169 | byte[] scratch = EnsureScratch(); 170 | 171 | scratch.WriteUInt32BE(0, value); 172 | data.Write(scratch, 0, 4); 173 | } 174 | 175 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 176 | public static void WriteUInt32LE(this Stream data, UInt32 value) 177 | { 178 | Debug.Assert(data.CanWrite); 179 | 180 | byte[] scratch = EnsureScratch(); 181 | 182 | scratch.WriteUInt32LE(0, value); 183 | data.Write(scratch, 0, 4); 184 | } 185 | 186 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 187 | public static void WriteUInt16BE(this Stream data, UInt16 value) 188 | { 189 | Debug.Assert(data.CanWrite); 190 | 191 | byte[] scratch = EnsureScratch(); 192 | 193 | scratch.WriteUInt16BE(0, value); 194 | data.Write(scratch, 0, 2); 195 | } 196 | 197 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 198 | public static void WriteUInt16LE(this Stream data, UInt16 value) 199 | { 200 | Debug.Assert(data.CanWrite); 201 | 202 | byte[] scratch = EnsureScratch(); 203 | 204 | scratch.WriteUInt16LE(0, value); 205 | data.Write(scratch, 0, 2); 206 | } 207 | 208 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 209 | public static void WriteByte(this Stream data, byte value) 210 | { 211 | Debug.Assert(data.CanWrite); 212 | data.WriteByte(value); 213 | } 214 | } 215 | } -------------------------------------------------------------------------------- /BinaryEx/BinUtils.Read.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2022 Matthew Sitton 2 | // BSD License - See LICENSE in the project root for license information. 3 | using System; 4 | using System.Diagnostics; 5 | using System.Runtime.CompilerServices; 6 | 7 | namespace BinaryEx 8 | { 9 | public static partial class BinUtils 10 | { 11 | 12 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 13 | public static UInt32 ReadUInt24LE(this byte[] buff, int offset) 14 | { 15 | Debug.Assert(buff.Length >= offset + 3); 16 | UInt32 val = 0; 17 | 18 | Unsafe.CopyBlockUnaligned(ref Unsafe.As(ref val), ref buff[offset], 3); 19 | return val; 20 | } 21 | 22 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 23 | public static UInt32 ReadUInt24BE(this byte[] buff, int offset) 24 | { 25 | Debug.Assert(buff.Length >= offset + 3); 26 | 27 | UInt32 val = 0; 28 | unsafe 29 | { 30 | byte* dst = (byte*)Unsafe.AsPointer(ref val) + 1; 31 | byte* start = (byte*)Unsafe.AsPointer(ref buff[offset]); 32 | 33 | Unsafe.CopyBlockUnaligned(dst, start, 3); 34 | } 35 | return SwapEndianess(val); 36 | } 37 | 38 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 39 | public static Int32 ReadInt24LE(this byte[] buff, int offset) 40 | { 41 | Int32 val = (Int32)ReadUInt24LE(buff, offset); 42 | return val - (val >> 23 << 24); 43 | } 44 | 45 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 46 | public static Int32 ReadInt24BE(this byte[] buff, int offset) 47 | { 48 | Int32 val = (Int32)ReadUInt24BE(buff, offset); 49 | return val - (val >> 23 << 24); 50 | } 51 | 52 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 53 | public static Int64 ReadInt64LE(this byte[] buff, int offset) 54 | { 55 | return (Int64)ReadUInt64LE(buff, offset); 56 | } 57 | 58 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 59 | public static Int64 ReadInt64BE(this byte[] buff, int offset) 60 | { 61 | return (Int64)ReadUInt64BE(buff, offset); 62 | } 63 | 64 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 65 | public static Int32 ReadInt32LE(this byte[] buff, int offset) 66 | { 67 | return (Int32)ReadUInt32LE(buff, offset); 68 | } 69 | 70 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 71 | public static Int32 ReadInt32BE(this byte[] buff, int offset) 72 | { 73 | return (Int32)ReadUInt32BE(buff, offset); 74 | } 75 | 76 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 77 | public static Int16 ReadInt16LE(this byte[] buff, int offset) 78 | { 79 | return (Int16)ReadUInt16LE(buff, offset); 80 | } 81 | 82 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 83 | public static Int16 ReadInt16BE(this byte[] buff, int offset) 84 | { 85 | return (Int16)ReadUInt16BE(buff, offset); 86 | } 87 | 88 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 89 | public static sbyte ReadSByte(this byte[] buff, int offset) 90 | { 91 | Debug.Assert(buff.Length >= Unsafe.SizeOf()); 92 | return (sbyte)buff[offset]; 93 | } 94 | 95 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 96 | public static UInt64 ReadUInt64LE(this byte[] buff, int offset) 97 | { 98 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 99 | return Unsafe.As(ref buff[offset]); 100 | } 101 | 102 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 103 | public static UInt64 ReadUInt64BE(this byte[] buff, int offset) 104 | { 105 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 106 | return SwapEndianess(Unsafe.As(ref buff[offset])); 107 | } 108 | 109 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 110 | public static UInt32 ReadUInt32LE(this byte[] buff, int offset) 111 | { 112 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 113 | return Unsafe.As(ref buff[offset]); 114 | } 115 | 116 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 117 | public static UInt32 ReadUInt32BE(this byte[] buff, int offset) 118 | { 119 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 120 | return SwapEndianess(Unsafe.As(ref buff[offset])); 121 | } 122 | 123 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 124 | public static UInt16 ReadUInt16LE(this byte[] buff, int offset) 125 | { 126 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 127 | return Unsafe.As(ref buff[offset]); 128 | } 129 | 130 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 131 | public static UInt16 ReadUInt16BE(this byte[] buff, int offset) 132 | { 133 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 134 | return SwapEndianess(Unsafe.As(ref buff[offset])); 135 | } 136 | 137 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 138 | public static byte ReadByte(this byte[] buff, int offset) 139 | { 140 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 141 | return buff[offset]; 142 | } 143 | 144 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 145 | public static int ReadBytes(this byte[] buff, int offset, byte[] output, UInt32 count) 146 | { 147 | Debug.Assert(buff.Length >= offset + count); 148 | Unsafe.CopyBlockUnaligned(ref output[0], ref buff[offset], count); 149 | return (int)count; 150 | } 151 | 152 | public static int ReadCountLE(this byte[] buff, int offset, T[] output, UInt32 count) where T : unmanaged 153 | { 154 | int outputByteSize = Unsafe.SizeOf() * (int)count; 155 | Debug.Assert(buff.Length >= offset + outputByteSize); 156 | unsafe 157 | { 158 | byte* outStart = (byte*)Unsafe.AsPointer(ref output[0]); 159 | 160 | Unsafe.CopyBlockUnaligned(ref Unsafe.AsRef(outStart), ref buff[offset], (uint)outputByteSize); 161 | return outputByteSize; 162 | } 163 | } 164 | } 165 | } -------------------------------------------------------------------------------- /BinaryEx/BinUtils.ReadOnlySpanRead.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2022 Matthew Sitton 2 | // BSD License - See LICENSE in the project root for license information. 3 | using System; 4 | using System.Diagnostics; 5 | using System.Runtime.CompilerServices; 6 | 7 | namespace BinaryEx 8 | { 9 | public static partial class BinUtils 10 | { 11 | 12 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 13 | public unsafe static UInt32 ReadUInt24LE(this ReadOnlySpan buff, int offset) 14 | { 15 | Debug.Assert(buff.Length >= offset + 3); 16 | 17 | fixed (byte* bp = buff) 18 | { 19 | return ReadUInt24LE(bp, offset); 20 | } 21 | } 22 | 23 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 24 | public unsafe static UInt32 ReadUInt24BE(this ReadOnlySpan buff, int offset) 25 | { 26 | Debug.Assert(buff.Length >= offset + 3); 27 | 28 | fixed (byte* bp = buff) 29 | { 30 | return ReadUInt24BE(bp, offset); 31 | } 32 | } 33 | 34 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 35 | public unsafe static Int32 ReadInt24LE(this ReadOnlySpan buff, int offset) 36 | { 37 | Debug.Assert(buff.Length >= offset + 3); 38 | 39 | fixed (byte* bp = buff) 40 | { 41 | return ReadInt24LE(bp, offset); 42 | } 43 | } 44 | 45 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 46 | public unsafe static Int32 ReadInt24BE(this ReadOnlySpan buff, int offset) 47 | { 48 | Debug.Assert(buff.Length >= offset + 3); 49 | 50 | fixed (byte* bp = buff) 51 | { 52 | return ReadInt24BE(bp, offset); 53 | } 54 | } 55 | 56 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 57 | public unsafe static Int64 ReadInt64LE(this ReadOnlySpan buff, int offset) 58 | { 59 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 60 | 61 | fixed (byte* bp = buff) 62 | { 63 | return ReadInt64LE(bp, offset); 64 | } 65 | } 66 | 67 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 68 | public unsafe static Int64 ReadInt64BE(this ReadOnlySpan buff, int offset) 69 | { 70 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 71 | 72 | fixed (byte* bp = buff) 73 | { 74 | return ReadInt64BE(bp, offset); 75 | } 76 | } 77 | 78 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 79 | public unsafe static Int32 ReadInt32LE(this ReadOnlySpan buff, int offset) 80 | { 81 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 82 | 83 | fixed (byte* bp = buff) 84 | { 85 | return ReadInt32LE(bp, offset); 86 | } 87 | } 88 | 89 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 90 | public unsafe static Int32 ReadInt32BE(this ReadOnlySpan buff, int offset) 91 | { 92 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 93 | 94 | fixed (byte* bp = buff) 95 | { 96 | return ReadInt32BE(bp, offset); 97 | } 98 | } 99 | 100 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 101 | public unsafe static Int16 ReadInt16LE(this ReadOnlySpan buff, int offset) 102 | { 103 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 104 | 105 | fixed (byte* bp = buff) 106 | { 107 | return ReadInt16LE(bp, offset); 108 | } 109 | } 110 | 111 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 112 | public unsafe static Int16 ReadInt16BE(this ReadOnlySpan buff, int offset) 113 | { 114 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 115 | 116 | fixed (byte* bp = buff) 117 | { 118 | return ReadInt16BE(bp, offset); 119 | } 120 | } 121 | 122 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 123 | public unsafe static sbyte ReadSByte(this ReadOnlySpan buff, int offset) 124 | { 125 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 126 | 127 | fixed (byte* bp = buff) 128 | { 129 | return ReadSByte(bp, offset); 130 | }; 131 | } 132 | 133 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 134 | public unsafe static UInt64 ReadUInt64LE(this ReadOnlySpan buff, int offset) 135 | { 136 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 137 | 138 | fixed (byte* bp = buff) 139 | { 140 | return ReadUInt64LE(bp, offset); 141 | }; 142 | } 143 | 144 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 145 | public unsafe static UInt64 ReadUInt64BE(this ReadOnlySpan buff, int offset) 146 | { 147 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 148 | 149 | fixed (byte* bp = buff) 150 | { 151 | return ReadUInt64BE(bp, offset); 152 | }; 153 | } 154 | 155 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 156 | public unsafe static UInt32 ReadUInt32LE(this ReadOnlySpan buff, int offset) 157 | { 158 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 159 | 160 | fixed (byte* bp = buff) 161 | { 162 | return ReadUInt32LE(bp, offset); 163 | }; 164 | } 165 | 166 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 167 | public unsafe static UInt32 ReadUInt32BE(this ReadOnlySpan buff, int offset) 168 | { 169 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 170 | 171 | fixed (byte* bp = buff) 172 | { 173 | return ReadUInt32LE(bp, offset); 174 | }; 175 | } 176 | 177 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 178 | public unsafe static UInt16 ReadUInt16LE(this ReadOnlySpan buff, int offset) 179 | { 180 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 181 | 182 | fixed (byte* bp = buff) 183 | { 184 | return ReadUInt16LE(bp, offset); 185 | }; 186 | } 187 | 188 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 189 | public unsafe static UInt16 ReadUInt16BE(this ReadOnlySpan buff, int offset) 190 | { 191 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 192 | 193 | fixed (byte* bp = buff) 194 | { 195 | return ReadUInt16BE(bp, offset); 196 | }; 197 | } 198 | 199 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 200 | public unsafe static byte ReadByte(this ReadOnlySpan buff, int offset) 201 | { 202 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 203 | 204 | fixed (byte* bp = buff) 205 | { 206 | return ReadByte(bp, offset); 207 | }; 208 | } 209 | 210 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 211 | public unsafe static int ReadBytes(this ReadOnlySpan buff, int offset, byte[] output, UInt32 count) 212 | { 213 | Debug.Assert(buff.Length >= offset + count); 214 | 215 | fixed (byte* bp = buff) 216 | { 217 | return ReadBytes(bp, offset, output, count); 218 | }; 219 | } 220 | 221 | public unsafe static int ReadCountLE(this ReadOnlySpan buff, int offset, T[] output, UInt32 count) where T : unmanaged 222 | { 223 | Debug.Assert(buff.Length >= offset + (Unsafe.SizeOf() * count)); 224 | 225 | fixed (byte* bp = buff) 226 | { 227 | return ReadCountLE(bp, offset, output, count); 228 | }; 229 | } 230 | } 231 | } -------------------------------------------------------------------------------- /BinaryEx/BinUtils.ReadOnlySpanRefRead.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2022 Matthew Sitton 2 | // BSD License - See LICENSE in the project root for license information. 3 | using System; 4 | using System.Diagnostics; 5 | using System.Runtime.CompilerServices; 6 | 7 | namespace BinaryEx 8 | { 9 | public static partial class BinUtils 10 | { 11 | 12 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 13 | public static Int32 ReadInt24LE(this ReadOnlySpan buff, ref int offset) 14 | { 15 | Int32 val = ReadInt24LE(buff, offset); 16 | offset += 3; 17 | return val; 18 | } 19 | 20 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 21 | public static Int32 ReadInt24BE(this ReadOnlySpan buff, ref int offset) 22 | { 23 | Int32 val = ReadInt24BE(buff, offset); 24 | offset += 3; 25 | return val; 26 | } 27 | 28 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 29 | public static UInt32 ReadUInt24LE(this ReadOnlySpan buff, ref int offset) 30 | { 31 | UInt32 val = ReadUInt24LE(buff, offset); 32 | offset += 3; 33 | return val; 34 | } 35 | 36 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 37 | public static UInt32 ReadUInt24BE(this ReadOnlySpan buff, ref int offset) 38 | { 39 | UInt32 val = ReadUInt24BE(buff, offset); 40 | offset += 3; 41 | return val; 42 | } 43 | 44 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 45 | public static Int64 ReadInt64LE(this ReadOnlySpan buff, ref int offset) 46 | { 47 | Int64 val = ReadInt64LE(buff, offset); 48 | offset += 8; 49 | return val; 50 | } 51 | 52 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 53 | public static Int64 ReadInt64BE(this ReadOnlySpan buff, ref int offset) 54 | { 55 | Int64 val = ReadInt64BE(buff, offset); 56 | offset += 8; 57 | return val; 58 | } 59 | 60 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 61 | public static Int32 ReadInt32LE(this ReadOnlySpan buff, ref int offset) 62 | { 63 | Int32 val = ReadInt32LE(buff, offset); 64 | offset += 4; 65 | return val; 66 | } 67 | 68 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 69 | public static Int32 ReadInt32BE(this ReadOnlySpan buff, ref int offset) 70 | { 71 | Int32 val = ReadInt32BE(buff, offset); 72 | offset += 4; 73 | return val; 74 | } 75 | 76 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 77 | public static Int16 ReadInt16LE(this ReadOnlySpan buff, ref int offset) 78 | { 79 | Int16 val = ReadInt16LE(buff, offset); 80 | offset += 2; 81 | return val; 82 | } 83 | 84 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 85 | public static Int16 ReadInt16BE(this ReadOnlySpan buff, ref int offset) 86 | { 87 | Int16 val = ReadInt16BE(buff, offset); 88 | offset += 2; 89 | return val; 90 | } 91 | 92 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 93 | public static sbyte ReadSByte(this ReadOnlySpan buff, ref int offset) 94 | { 95 | sbyte val = ReadSByte(buff, offset); 96 | offset += 1; 97 | return val; 98 | } 99 | 100 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 101 | public static UInt64 ReadUInt64LE(this ReadOnlySpan buff, ref int offset) 102 | { 103 | UInt64 val = ReadUInt64LE(buff, offset); 104 | offset += 8; 105 | return val; 106 | } 107 | 108 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 109 | public static UInt64 ReadUInt64BE(this ReadOnlySpan buff, ref int offset) 110 | { 111 | UInt64 val = ReadUInt64BE(buff, offset); 112 | offset += 8; 113 | return val; 114 | } 115 | 116 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 117 | public static UInt32 ReadUInt32LE(this ReadOnlySpan buff, ref int offset) 118 | { 119 | UInt32 val = ReadUInt32LE(buff, offset); 120 | offset += 4; 121 | return val; 122 | } 123 | 124 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 125 | public static UInt32 ReadUInt32BE(this ReadOnlySpan buff, ref int offset) 126 | { 127 | UInt32 val = ReadUInt32BE(buff, offset); 128 | offset += 4; 129 | return val; 130 | } 131 | 132 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 133 | public static UInt16 ReadUInt16LE(this ReadOnlySpan buff, ref int offset) 134 | { 135 | UInt16 val = ReadUInt16LE(buff, offset); 136 | offset += 2; 137 | return val; 138 | } 139 | 140 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 141 | public static UInt16 ReadUInt16BE(this ReadOnlySpan buff, ref int offset) 142 | { 143 | UInt16 val = ReadUInt16BE(buff, offset); 144 | offset += 2; 145 | return val; 146 | } 147 | 148 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 149 | public static byte ReadByte(this ReadOnlySpan buff, ref int offset) 150 | { 151 | return buff[offset++]; 152 | } 153 | 154 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 155 | public static void ReadBytes(this ReadOnlySpan buff, ref int offset, byte[] output, UInt32 count) 156 | { 157 | offset += ReadBytes(buff, offset, output, count); 158 | } 159 | 160 | public static void ReadCountLE(this ReadOnlySpan buff, ref int offset, T[] output, UInt32 count) where T : unmanaged 161 | { 162 | offset += ReadCountLE(buff, offset, output, count); 163 | } 164 | } 165 | } -------------------------------------------------------------------------------- /BinaryEx/BinUtils.RefRead.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2022 Matthew Sitton 2 | // BSD License - See LICENSE in the project root for license information. 3 | using System; 4 | using System.Diagnostics; 5 | using System.Runtime.CompilerServices; 6 | 7 | namespace BinaryEx 8 | { 9 | public static partial class BinUtils 10 | { 11 | 12 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 13 | public static Int32 ReadInt24LE(this byte[] buff, ref int offset) 14 | { 15 | Int32 val = ReadInt24LE(buff, offset); 16 | offset += 3; 17 | return val; 18 | } 19 | 20 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 21 | public static Int32 ReadInt24BE(this byte[] buff, ref int offset) 22 | { 23 | Int32 val = ReadInt24BE(buff, offset); 24 | offset += 3; 25 | return val; 26 | } 27 | 28 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 29 | public static UInt32 ReadUInt24LE(this byte[] buff, ref int offset) 30 | { 31 | UInt32 val = ReadUInt24LE(buff, offset); 32 | offset += 3; 33 | return val; 34 | } 35 | 36 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 37 | public static UInt32 ReadUInt24BE(this byte[] buff, ref int offset) 38 | { 39 | UInt32 val = ReadUInt24BE(buff, offset); 40 | offset += 3; 41 | return val; 42 | } 43 | 44 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 45 | public static Int64 ReadInt64LE(this byte[] buff, ref int offset) 46 | { 47 | Int64 val = ReadInt64LE(buff, offset); 48 | offset += 8; 49 | return val; 50 | } 51 | 52 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 53 | public static Int64 ReadInt64BE(this byte[] buff, ref int offset) 54 | { 55 | Int64 val = ReadInt64BE(buff, offset); 56 | offset += 8; 57 | return val; 58 | } 59 | 60 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 61 | public static Int32 ReadInt32LE(this byte[] buff, ref int offset) 62 | { 63 | Int32 val = ReadInt32LE(buff, offset); 64 | offset += 4; 65 | return val; 66 | } 67 | 68 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 69 | public static Int32 ReadInt32BE(this byte[] buff, ref int offset) 70 | { 71 | Int32 val = ReadInt32BE(buff, offset); 72 | offset += 4; 73 | return val; 74 | } 75 | 76 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 77 | public static Int16 ReadInt16LE(this byte[] buff, ref int offset) 78 | { 79 | Int16 val = ReadInt16LE(buff, offset); 80 | offset += 2; 81 | return val; 82 | } 83 | 84 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 85 | public static Int16 ReadInt16BE(this byte[] buff, ref int offset) 86 | { 87 | Int16 val = ReadInt16BE(buff, offset); 88 | offset += 2; 89 | return val; 90 | } 91 | 92 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 93 | public static sbyte ReadSByte(this byte[] buff, ref int offset) 94 | { 95 | sbyte val = ReadSByte(buff, offset); 96 | offset += 1; 97 | return val; 98 | } 99 | 100 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 101 | public static UInt64 ReadUInt64LE(this byte[] buff, ref int offset) 102 | { 103 | UInt64 val = ReadUInt64LE(buff, offset); 104 | offset += 8; 105 | return val; 106 | } 107 | 108 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 109 | public static UInt64 ReadUInt64BE(this byte[] buff, ref int offset) 110 | { 111 | UInt64 val = ReadUInt64BE(buff, offset); 112 | offset += 8; 113 | return val; 114 | } 115 | 116 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 117 | public static UInt32 ReadUInt32LE(this byte[] buff, ref int offset) 118 | { 119 | UInt32 val = ReadUInt32LE(buff, offset); 120 | offset += 4; 121 | return val; 122 | } 123 | 124 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 125 | public static UInt32 ReadUInt32BE(this byte[] buff, ref int offset) 126 | { 127 | UInt32 val = ReadUInt32BE(buff, offset); 128 | offset += 4; 129 | return val; 130 | } 131 | 132 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 133 | public static UInt16 ReadUInt16LE(this byte[] buff, ref int offset) 134 | { 135 | UInt16 val = ReadUInt16LE(buff, offset); 136 | offset += 2; 137 | return val; 138 | } 139 | 140 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 141 | public static UInt16 ReadUInt16BE(this byte[] buff, ref int offset) 142 | { 143 | UInt16 val = ReadUInt16BE(buff, offset); 144 | offset += 2; 145 | return val; 146 | } 147 | 148 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 149 | public static byte ReadByte(this byte[] buff, ref int offset) 150 | { 151 | return buff[offset++]; 152 | } 153 | 154 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 155 | public static void ReadBytes(this byte[] data, ref int offset, byte[] output, UInt32 count) 156 | { 157 | offset += ReadBytes(data, offset, output, count); 158 | } 159 | 160 | public static void ReadCountLE(this byte[] data, ref int offset, T[] output, UInt32 count) where T : unmanaged 161 | { 162 | offset += ReadCountLE(data, offset, output, count); 163 | } 164 | } 165 | } -------------------------------------------------------------------------------- /BinaryEx/BinUtils.RefWrite.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2022 Matthew Sitton 2 | // BSD License - See LICENSE in the project root for license information. 3 | using System; 4 | using System.Runtime.CompilerServices; 5 | 6 | namespace BinaryEx 7 | { 8 | public static partial class BinUtils 9 | { 10 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 11 | public static void WriteInt64BE(this byte[] buff, ref int offset, Int64 value) 12 | { 13 | WriteInt64BE(buff, offset, value); 14 | offset += 8; 15 | } 16 | 17 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 18 | public static void WriteInt64LE(this byte[] buff, ref int offset, Int64 value) 19 | { 20 | WriteInt64LE(buff, offset, value); 21 | offset += 8; 22 | } 23 | 24 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 25 | public static void WriteInt32BE(this byte[] buff, ref int offset, Int32 value) 26 | { 27 | WriteInt32BE(buff, offset, value); 28 | offset += 4; 29 | } 30 | 31 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 32 | public static void WriteInt32LE(this byte[] buff, ref int offset, Int32 value) 33 | { 34 | WriteInt32LE(buff, offset, value); 35 | offset += 4; 36 | } 37 | 38 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 39 | public static void WriteInt24BE(this byte[] buff, ref int offset, Int32 value) 40 | { 41 | WriteInt24BE(buff, offset, value); 42 | offset += 3; 43 | } 44 | 45 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 46 | public static void WriteInt24LE(this byte[] buff, ref int offset, Int32 value) 47 | { 48 | WriteInt24LE(buff, offset, value); 49 | offset += 3; 50 | } 51 | 52 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 53 | public static void WriteInt16BE(this byte[] buff, ref int offset, Int16 value) 54 | { 55 | WriteInt16BE(buff, offset, value); 56 | offset += 2; 57 | } 58 | 59 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 60 | public static void WriteInt16LE(this byte[] buff, ref int offset, Int16 value) 61 | { 62 | WriteInt16LE(buff, offset, value); 63 | offset += 2; 64 | } 65 | 66 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 67 | public static void WriteSByte(this byte[] buff, ref int offset, sbyte value) 68 | { 69 | WriteSByte(buff, offset, value); 70 | offset += 1; 71 | } 72 | 73 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 74 | public static void WriteUInt64BE(this byte[] buff, ref int offset, UInt64 value) 75 | { 76 | WriteUInt64BE(buff, offset, value); 77 | offset += 8; 78 | } 79 | 80 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 81 | public static void WriteUInt64LE(this byte[] buff, ref int offset, UInt64 value) 82 | { 83 | WriteUInt64LE(buff, offset, value); 84 | offset += 8; 85 | } 86 | 87 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 88 | public static void WriteUInt32BE(this byte[] buff, ref int offset, UInt32 value) 89 | { 90 | WriteUInt32BE(buff, offset, value); 91 | offset += 4; 92 | } 93 | 94 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 95 | public static void WriteUInt32LE(this byte[] buff, ref int offset, UInt32 value) 96 | { 97 | WriteUInt32LE(buff, offset, value); 98 | offset += 4; 99 | } 100 | 101 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 102 | public static void WriteUInt24BE(this byte[] buff, ref int offset, UInt32 value) 103 | { 104 | WriteUInt24BE(buff, offset, value); 105 | offset += 3; 106 | } 107 | 108 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 109 | public static void WriteUInt24LE(this byte[] buff, ref int offset, UInt32 value) 110 | { 111 | WriteUInt24LE(buff, offset, value); 112 | offset += 3; 113 | } 114 | 115 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 116 | public static void WriteUInt16BE(this byte[] buff, ref int offset, UInt16 value) 117 | { 118 | WriteUInt16BE(buff, offset, value); 119 | offset += 2; 120 | } 121 | 122 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 123 | public static void WriteUInt16LE(this byte[] buff, ref int offset, UInt16 value) 124 | { 125 | WriteUInt16LE(buff, offset, value); 126 | offset += 2; 127 | } 128 | 129 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 130 | public static void WriteByte(this byte[] buff, ref int offset, byte value) 131 | { 132 | WriteByte(buff, offset, value); 133 | offset += 1; 134 | } 135 | 136 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 137 | public static void WriteBytes(this byte[] data, ref int offset, byte[] input, UInt32 count) 138 | { 139 | offset += WriteBytes(data, offset, input, count); 140 | } 141 | 142 | } 143 | } -------------------------------------------------------------------------------- /BinaryEx/BinUtils.SpanRead.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2022 Matthew Sitton 2 | // BSD License - See LICENSE in the project root for license information. 3 | using System; 4 | using System.Diagnostics; 5 | using System.Runtime.CompilerServices; 6 | 7 | namespace BinaryEx 8 | { 9 | public static partial class BinUtils 10 | { 11 | 12 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 13 | public unsafe static UInt32 ReadUInt24LE(this Span buff, int offset) 14 | { 15 | Debug.Assert(buff.Length >= offset + 3); 16 | 17 | fixed (byte* bp = buff) 18 | { 19 | return ReadUInt24LE(bp, offset); 20 | } 21 | } 22 | 23 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 24 | public unsafe static UInt32 ReadUInt24BE(this Span buff, int offset) 25 | { 26 | Debug.Assert(buff.Length >= offset + 3); 27 | 28 | fixed (byte* bp = buff) 29 | { 30 | return ReadUInt24BE(bp, offset); 31 | } 32 | } 33 | 34 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 35 | public unsafe static Int32 ReadInt24LE(this Span buff, int offset) 36 | { 37 | Debug.Assert(buff.Length >= offset + 3); 38 | 39 | fixed (byte* bp = buff) 40 | { 41 | return ReadInt24LE(bp, offset); 42 | } 43 | } 44 | 45 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 46 | public unsafe static Int32 ReadInt24BE(this Span buff, int offset) 47 | { 48 | Debug.Assert(buff.Length >= offset + 3); 49 | 50 | fixed (byte* bp = buff) 51 | { 52 | return ReadInt24BE(bp, offset); 53 | } 54 | } 55 | 56 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 57 | public unsafe static Int64 ReadInt64LE(this Span buff, int offset) 58 | { 59 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 60 | 61 | fixed (byte* bp = buff) 62 | { 63 | return ReadInt64LE(bp, offset); 64 | } 65 | } 66 | 67 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 68 | public unsafe static Int64 ReadInt64BE(this Span buff, int offset) 69 | { 70 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 71 | 72 | fixed (byte* bp = buff) 73 | { 74 | return ReadInt64BE(bp, offset); 75 | } 76 | } 77 | 78 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 79 | public unsafe static Int32 ReadInt32LE(this Span buff, int offset) 80 | { 81 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 82 | 83 | fixed (byte* bp = buff) 84 | { 85 | return ReadInt32LE(bp, offset); 86 | } 87 | } 88 | 89 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 90 | public unsafe static Int32 ReadInt32BE(this Span buff, int offset) 91 | { 92 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 93 | 94 | fixed (byte* bp = buff) 95 | { 96 | return ReadInt32BE(bp, offset); 97 | } 98 | } 99 | 100 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 101 | public unsafe static Int16 ReadInt16LE(this Span buff, int offset) 102 | { 103 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 104 | 105 | fixed (byte* bp = buff) 106 | { 107 | return ReadInt16LE(bp, offset); 108 | } 109 | } 110 | 111 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 112 | public unsafe static Int16 ReadInt16BE(this Span buff, int offset) 113 | { 114 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 115 | 116 | fixed (byte* bp = buff) 117 | { 118 | return ReadInt16BE(bp, offset); 119 | } 120 | } 121 | 122 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 123 | public unsafe static sbyte ReadSByte(this Span buff, int offset) 124 | { 125 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 126 | 127 | fixed (byte* bp = buff) 128 | { 129 | return ReadSByte(bp, offset); 130 | }; 131 | } 132 | 133 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 134 | public unsafe static UInt64 ReadUInt64LE(this Span buff, int offset) 135 | { 136 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 137 | 138 | fixed (byte* bp = buff) 139 | { 140 | return ReadUInt64LE(bp, offset); 141 | }; 142 | } 143 | 144 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 145 | public unsafe static UInt64 ReadUInt64BE(this Span buff, int offset) 146 | { 147 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 148 | 149 | fixed (byte* bp = buff) 150 | { 151 | return ReadUInt64BE(bp, offset); 152 | }; 153 | } 154 | 155 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 156 | public unsafe static UInt32 ReadUInt32LE(this Span buff, int offset) 157 | { 158 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 159 | 160 | fixed (byte* bp = buff) 161 | { 162 | return ReadUInt32LE(bp, offset); 163 | }; 164 | } 165 | 166 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 167 | public unsafe static UInt32 ReadUInt32BE(this Span buff, int offset) 168 | { 169 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 170 | 171 | fixed (byte* bp = buff) 172 | { 173 | return ReadUInt32LE(bp, offset); 174 | }; 175 | } 176 | 177 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 178 | public unsafe static UInt16 ReadUInt16LE(this Span buff, int offset) 179 | { 180 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 181 | 182 | fixed (byte* bp = buff) 183 | { 184 | return ReadUInt16LE(bp, offset); 185 | }; 186 | } 187 | 188 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 189 | public unsafe static UInt16 ReadUInt16BE(this Span buff, int offset) 190 | { 191 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 192 | 193 | fixed (byte* bp = buff) 194 | { 195 | return ReadUInt16BE(bp, offset); 196 | }; 197 | } 198 | 199 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 200 | public unsafe static byte ReadByte(this Span buff, int offset) 201 | { 202 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 203 | 204 | fixed (byte* bp = buff) 205 | { 206 | return ReadByte(bp, offset); 207 | }; 208 | } 209 | 210 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 211 | public unsafe static int ReadBytes(this Span buff, int offset, byte[] output, UInt32 count) 212 | { 213 | Debug.Assert(buff.Length >= offset + count); 214 | 215 | fixed (byte* bp = buff) 216 | { 217 | return ReadBytes(bp, offset, output, count); 218 | }; 219 | } 220 | 221 | public unsafe static int ReadCountLE(this Span buff, int offset, T[] output, UInt32 count) where T : unmanaged 222 | { 223 | Debug.Assert(buff.Length >= offset + (Unsafe.SizeOf() * count)); 224 | 225 | fixed (byte* bp = buff) 226 | { 227 | return ReadCountLE(bp, offset, output, count); 228 | }; 229 | } 230 | } 231 | } -------------------------------------------------------------------------------- /BinaryEx/BinUtils.SpanRefRead.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2022 Matthew Sitton 2 | // BSD License - See LICENSE in the project root for license information. 3 | using System; 4 | using System.Diagnostics; 5 | using System.Runtime.CompilerServices; 6 | 7 | namespace BinaryEx 8 | { 9 | public static partial class BinUtils 10 | { 11 | 12 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 13 | public static Int32 ReadInt24LE(this Span buff, ref int offset) 14 | { 15 | Int32 val = ReadInt24LE(buff, offset); 16 | offset += 3; 17 | return val; 18 | } 19 | 20 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 21 | public static Int32 ReadInt24BE(this Span buff, ref int offset) 22 | { 23 | Int32 val = ReadInt24BE(buff, offset); 24 | offset += 3; 25 | return val; 26 | } 27 | 28 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 29 | public static UInt32 ReadUInt24LE(this Span buff, ref int offset) 30 | { 31 | UInt32 val = ReadUInt24LE(buff, offset); 32 | offset += 3; 33 | return val; 34 | } 35 | 36 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 37 | public static UInt32 ReadUInt24BE(this Span buff, ref int offset) 38 | { 39 | UInt32 val = ReadUInt24BE(buff, offset); 40 | offset += 3; 41 | return val; 42 | } 43 | 44 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 45 | public static Int64 ReadInt64LE(this Span buff, ref int offset) 46 | { 47 | Int64 val = ReadInt64LE(buff, offset); 48 | offset += 8; 49 | return val; 50 | } 51 | 52 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 53 | public static Int64 ReadInt64BE(this Span buff, ref int offset) 54 | { 55 | Int64 val = ReadInt64BE(buff, offset); 56 | offset += 8; 57 | return val; 58 | } 59 | 60 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 61 | public static Int32 ReadInt32LE(this Span buff, ref int offset) 62 | { 63 | Int32 val = ReadInt32LE(buff, offset); 64 | offset += 4; 65 | return val; 66 | } 67 | 68 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 69 | public static Int32 ReadInt32BE(this Span buff, ref int offset) 70 | { 71 | Int32 val = ReadInt32BE(buff, offset); 72 | offset += 4; 73 | return val; 74 | } 75 | 76 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 77 | public static Int16 ReadInt16LE(this Span buff, ref int offset) 78 | { 79 | Int16 val = ReadInt16LE(buff, offset); 80 | offset += 2; 81 | return val; 82 | } 83 | 84 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 85 | public static Int16 ReadInt16BE(this Span buff, ref int offset) 86 | { 87 | Int16 val = ReadInt16BE(buff, offset); 88 | offset += 2; 89 | return val; 90 | } 91 | 92 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 93 | public static sbyte ReadSByte(this Span buff, ref int offset) 94 | { 95 | sbyte val = ReadSByte(buff, offset); 96 | offset += 1; 97 | return val; 98 | } 99 | 100 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 101 | public static UInt64 ReadUInt64LE(this Span buff, ref int offset) 102 | { 103 | UInt64 val = ReadUInt64LE(buff, offset); 104 | offset += 8; 105 | return val; 106 | } 107 | 108 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 109 | public static UInt64 ReadUInt64BE(this Span buff, ref int offset) 110 | { 111 | UInt64 val = ReadUInt64BE(buff, offset); 112 | offset += 8; 113 | return val; 114 | } 115 | 116 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 117 | public static UInt32 ReadUInt32LE(this Span buff, ref int offset) 118 | { 119 | UInt32 val = ReadUInt32LE(buff, offset); 120 | offset += 4; 121 | return val; 122 | } 123 | 124 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 125 | public static UInt32 ReadUInt32BE(this Span buff, ref int offset) 126 | { 127 | UInt32 val = ReadUInt32BE(buff, offset); 128 | offset += 4; 129 | return val; 130 | } 131 | 132 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 133 | public static UInt16 ReadUInt16LE(this Span buff, ref int offset) 134 | { 135 | UInt16 val = ReadUInt16LE(buff, offset); 136 | offset += 2; 137 | return val; 138 | } 139 | 140 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 141 | public static UInt16 ReadUInt16BE(this Span buff, ref int offset) 142 | { 143 | UInt16 val = ReadUInt16BE(buff, offset); 144 | offset += 2; 145 | return val; 146 | } 147 | 148 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 149 | public static byte ReadByte(this Span buff, ref int offset) 150 | { 151 | return buff[offset++]; 152 | } 153 | 154 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 155 | public static void ReadBytes(this Span buff, ref int offset, byte[] output, UInt32 count) 156 | { 157 | offset += ReadBytes(buff, offset, output, count); 158 | } 159 | 160 | public static void ReadCountLE(this Span buff, ref int offset, T[] output, UInt32 count) where T : unmanaged 161 | { 162 | offset += ReadCountLE(buff, offset, output, count); 163 | } 164 | } 165 | } -------------------------------------------------------------------------------- /BinaryEx/BinUtils.SpanWrite.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2022 Matthew Sitton 2 | // BSD License - See LICENSE in the project root for license information. 3 | using System; 4 | using System.Diagnostics; 5 | using System.Runtime.CompilerServices; 6 | 7 | namespace BinaryEx 8 | { 9 | public static partial class BinUtils 10 | { 11 | 12 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 13 | public unsafe static void WriteInt64BE(this Span buff, int offset, Int64 value) 14 | { 15 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 16 | 17 | fixed (byte* bp = buff) 18 | { 19 | WriteInt64BE(bp, offset, value); 20 | } 21 | } 22 | 23 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 24 | public unsafe static void WriteInt64LE(this Span buff, int offset, Int64 value) 25 | { 26 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 27 | 28 | fixed (byte* bp = buff) 29 | { 30 | WriteInt64LE(bp, offset, value); 31 | } 32 | } 33 | 34 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 35 | public unsafe static void WriteInt32BE(this Span buff, int offset, Int32 value) 36 | { 37 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 38 | 39 | fixed (byte* bp = buff) 40 | { 41 | WriteInt32BE(bp, offset, value); 42 | } 43 | } 44 | 45 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 46 | public unsafe static void WriteInt32LE(this Span buff, int offset, Int32 value) 47 | { 48 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 49 | 50 | fixed (byte* bp = buff) 51 | { 52 | WriteInt32LE(bp, offset, value); 53 | } 54 | } 55 | 56 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 57 | public unsafe static void WriteInt24BE(this Span buff, int offset, Int32 value) 58 | { 59 | Debug.Assert(value <= 0x7FFFFF && value >= -0x7FFFFF); 60 | Debug.Assert(buff.Length >= offset + 3); 61 | 62 | fixed (byte* bp = buff) 63 | { 64 | WriteInt24BE(bp, offset, value); 65 | } 66 | } 67 | 68 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 69 | public unsafe static void WriteInt24LE(this Span buff, int offset, Int32 value) 70 | { 71 | Debug.Assert(value <= 0x7FFFFF && value >= -0x7FFFFF); 72 | Debug.Assert(buff.Length >= offset + 3); 73 | 74 | fixed (byte* bp = buff) 75 | { 76 | WriteInt24LE(bp, offset, value); 77 | } 78 | } 79 | 80 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 81 | public unsafe static void WriteInt16BE(this Span buff, int offset, Int16 value) 82 | { 83 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 84 | 85 | fixed (byte* bp = buff) 86 | { 87 | WriteInt16BE(bp, offset, value); 88 | } 89 | } 90 | 91 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 92 | public unsafe static void WriteInt16LE(this Span buff, int offset, Int16 value) 93 | { 94 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 95 | 96 | fixed (byte* bp = buff) 97 | { 98 | WriteInt16LE(bp, offset, value); 99 | } 100 | } 101 | 102 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 103 | public unsafe static void WriteSByte(this Span buff, int offset, sbyte value) 104 | { 105 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 106 | 107 | fixed (byte* bp = buff) 108 | { 109 | WriteSByte(bp, offset, value); 110 | } 111 | } 112 | 113 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 114 | public unsafe static void WriteUInt64BE(this Span buff, int offset, UInt64 value) 115 | { 116 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 117 | 118 | fixed (byte* bp = buff) 119 | { 120 | WriteUInt64BE(bp, offset, value); 121 | } 122 | } 123 | 124 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 125 | public unsafe static void WriteUInt64LE(this Span buff, int offset, UInt64 value) 126 | { 127 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 128 | 129 | fixed (byte* bp = buff) 130 | { 131 | WriteUInt64LE(bp, offset, value); 132 | } 133 | } 134 | 135 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 136 | public unsafe static void WriteUInt32BE(this Span buff, int offset, UInt32 value) 137 | { 138 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 139 | 140 | fixed (byte* bp = buff) 141 | { 142 | WriteUInt32BE(bp, offset, value); 143 | } 144 | } 145 | 146 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 147 | public unsafe static void WriteUInt32LE(this Span buff, int offset, UInt32 value) 148 | { 149 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 150 | 151 | fixed (byte* bp = buff) 152 | { 153 | WriteUInt32LE(bp, offset, value); 154 | } 155 | } 156 | 157 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 158 | public unsafe static void WriteUInt24BE(this Span buff, int offset, UInt32 value) 159 | { 160 | 161 | Debug.Assert(value <= 0xFFFFFF); 162 | Debug.Assert(buff.Length >= offset + 3); 163 | 164 | fixed (byte* bp = buff) 165 | { 166 | WriteUInt24BE(bp, offset, value); 167 | } 168 | } 169 | 170 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 171 | public unsafe static void WriteUInt24LE(this Span buff, int offset, UInt32 value) 172 | { 173 | Debug.Assert(value <= 0xFFFFFF); 174 | Debug.Assert(buff.Length >= offset + 3); 175 | 176 | fixed (byte* bp = buff) 177 | { 178 | WriteUInt24LE(bp, offset, value); 179 | } 180 | } 181 | 182 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 183 | public unsafe static void WriteUInt16BE(this Span buff, int offset, UInt16 value) 184 | { 185 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 186 | 187 | fixed (byte* bp = buff) 188 | { 189 | WriteUInt16BE(bp, offset, value); 190 | } 191 | } 192 | 193 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 194 | public unsafe static void WriteUInt16LE(this Span buff, int offset, UInt16 value) 195 | { 196 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 197 | 198 | fixed (byte* bp = buff) 199 | { 200 | WriteUInt16LE(bp, offset, value); 201 | } 202 | } 203 | 204 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 205 | public unsafe static void WriteByte(this Span buff, int offset, byte value) 206 | { 207 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 208 | 209 | fixed (byte* bp = buff) 210 | { 211 | WriteByte(bp, offset, value); 212 | } 213 | } 214 | 215 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 216 | public unsafe static int WriteBytes(this Span buff, int offset, byte[] input, UInt32 count) 217 | { 218 | Debug.Assert(buff.Length >= offset + count); 219 | 220 | fixed (byte* bp = buff) 221 | { 222 | return WriteBytes(bp, offset, input, count); 223 | } 224 | } 225 | 226 | } 227 | } -------------------------------------------------------------------------------- /BinaryEx/BinUtils.UnsafeRead.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2022 Matthew Sitton 2 | // BSD License - See LICENSE in the project root for license information. 3 | using System; 4 | using System.Diagnostics; 5 | using System.Runtime.CompilerServices; 6 | 7 | namespace BinaryEx 8 | { 9 | public static partial class BinUtils 10 | { 11 | 12 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 13 | public unsafe static UInt32 ReadUInt24LE(byte* buff, int offset) 14 | { 15 | UInt32 val = 0; 16 | Unsafe.CopyBlockUnaligned(ref Unsafe.As(ref val), ref buff[offset], 3); 17 | return val; 18 | } 19 | 20 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 21 | public unsafe static UInt32 ReadUInt24BE(byte* buff, int offset) 22 | { 23 | UInt32 val = 0; 24 | unsafe 25 | { 26 | byte* dst = (byte*)Unsafe.AsPointer(ref val) + 1; 27 | byte* start = (byte*)Unsafe.AsPointer(ref buff[offset]); 28 | 29 | Unsafe.CopyBlockUnaligned(dst, start, 3); 30 | } 31 | return SwapEndianess(val); 32 | } 33 | 34 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 35 | public unsafe static Int32 ReadInt24LE(byte* buff, int offset) 36 | { 37 | Int32 val = (Int32)ReadUInt24LE(buff, offset); 38 | return val - (val >> 23 << 24); 39 | } 40 | 41 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 42 | public unsafe static Int32 ReadInt24BE(byte* buff, int offset) 43 | { 44 | Int32 val = (Int32)ReadUInt24BE(buff, offset); 45 | return val - (val >> 23 << 24); 46 | } 47 | 48 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 49 | public unsafe static Int64 ReadInt64LE(byte* buff, int offset) 50 | { 51 | return (Int64)ReadUInt64LE(buff, offset); 52 | } 53 | 54 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 55 | public unsafe static Int64 ReadInt64BE(byte* buff, int offset) 56 | { 57 | return (Int64)ReadUInt64BE(buff, offset); 58 | } 59 | 60 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 61 | public unsafe static Int32 ReadInt32LE(byte* buff, int offset) 62 | { 63 | return (Int32)ReadUInt32LE(buff, offset); 64 | } 65 | 66 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 67 | public unsafe static Int32 ReadInt32BE(byte* buff, int offset) 68 | { 69 | return (Int32)ReadUInt32BE(buff, offset); 70 | } 71 | 72 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 73 | public unsafe static Int16 ReadInt16LE(byte* buff, int offset) 74 | { 75 | return (Int16)ReadUInt16LE(buff, offset); 76 | } 77 | 78 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 79 | public unsafe static Int16 ReadInt16BE(byte* buff, int offset) 80 | { 81 | return (Int16)ReadUInt16BE(buff, offset); 82 | } 83 | 84 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 85 | public unsafe static sbyte ReadSByte(byte* buff, int offset) 86 | { 87 | return (sbyte)buff[offset]; 88 | } 89 | 90 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 91 | public unsafe static UInt64 ReadUInt64LE(byte* buff, int offset) 92 | { 93 | return Unsafe.As(ref buff[offset]); 94 | } 95 | 96 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 97 | public unsafe static UInt64 ReadUInt64BE(byte* buff, int offset) 98 | { 99 | return SwapEndianess(Unsafe.As(ref buff[offset])); 100 | } 101 | 102 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 103 | public unsafe static UInt32 ReadUInt32LE(byte* buff, int offset) 104 | { 105 | return Unsafe.As(ref buff[offset]); 106 | } 107 | 108 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 109 | public unsafe static UInt32 ReadUInt32BE(byte* buff, int offset) 110 | { 111 | return SwapEndianess(Unsafe.As(ref buff[offset])); 112 | } 113 | 114 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 115 | public unsafe static UInt16 ReadUInt16LE(byte* buff, int offset) 116 | { 117 | return Unsafe.As(ref buff[offset]); 118 | } 119 | 120 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 121 | public unsafe static UInt16 ReadUInt16BE(byte* buff, int offset) 122 | { 123 | return SwapEndianess(Unsafe.As(ref buff[offset])); 124 | } 125 | 126 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 127 | public unsafe static byte ReadByte(byte* buff, int offset) 128 | { 129 | return buff[offset]; 130 | } 131 | 132 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 133 | public unsafe static int ReadBytes(byte* buff, int offset, byte[] output, UInt32 count) 134 | { 135 | Unsafe.CopyBlockUnaligned(ref output[0], ref buff[offset], count); 136 | return (int)count; 137 | } 138 | 139 | public unsafe static int ReadCountLE(byte* buff, int offset, T[] output, UInt32 count) where T : unmanaged 140 | { 141 | unsafe 142 | { 143 | uint outputByteSize = (uint)Unsafe.SizeOf() * count; 144 | 145 | Debug.Assert(outputByteSize >= count); 146 | byte* outStart = (byte*)Unsafe.AsPointer(ref output[0]); 147 | 148 | Unsafe.CopyBlockUnaligned(ref Unsafe.AsRef(outStart), ref buff[offset], outputByteSize); 149 | return (int)outputByteSize; 150 | } 151 | } 152 | } 153 | } -------------------------------------------------------------------------------- /BinaryEx/BinUtils.UnsafeRefRead.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2022 Matthew Sitton 2 | // BSD License - See LICENSE in the project root for license information. 3 | using System; 4 | using System.Diagnostics; 5 | using System.Runtime.CompilerServices; 6 | 7 | namespace BinaryEx 8 | { 9 | public static partial class BinUtils 10 | { 11 | 12 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 13 | public unsafe static Int32 ReadInt24LE(byte* buff, ref int offset) 14 | { 15 | Int32 val = ReadInt24LE(buff, offset); 16 | offset += 3; 17 | return val; 18 | } 19 | 20 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 21 | public unsafe static Int32 ReadInt24BE(byte* buff, ref int offset) 22 | { 23 | Int32 val = ReadInt24BE(buff, offset); 24 | offset += 3; 25 | return val; 26 | } 27 | 28 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 29 | public unsafe static UInt32 ReadUInt24LE(byte* buff, ref int offset) 30 | { 31 | UInt32 val = ReadUInt24LE(buff, offset); 32 | offset += 3; 33 | return val; 34 | } 35 | 36 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 37 | public unsafe static UInt32 ReadUInt24BE(byte* buff, ref int offset) 38 | { 39 | UInt32 val = ReadUInt24BE(buff, offset); 40 | offset += 3; 41 | return val; 42 | } 43 | 44 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 45 | public unsafe static Int64 ReadInt64LE(byte* buff, ref int offset) 46 | { 47 | Int64 val = ReadInt64LE(buff, offset); 48 | offset += 8; 49 | return val; 50 | } 51 | 52 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 53 | public unsafe static Int64 ReadInt64BE(byte* buff, ref int offset) 54 | { 55 | Int64 val = ReadInt64BE(buff, offset); 56 | offset += 8; 57 | return val; 58 | } 59 | 60 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 61 | public unsafe static Int32 ReadInt32LE(byte* buff, ref int offset) 62 | { 63 | Int32 val = ReadInt32LE(buff, offset); 64 | offset += 4; 65 | return val; 66 | } 67 | 68 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 69 | public unsafe static Int32 ReadInt32BE(byte* buff, ref int offset) 70 | { 71 | Int32 val = ReadInt32BE(buff, offset); 72 | offset += 4; 73 | return val; 74 | } 75 | 76 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 77 | public unsafe static Int16 ReadInt16LE(byte* buff, ref int offset) 78 | { 79 | Int16 val = ReadInt16LE(buff, offset); 80 | offset += 2; 81 | return val; 82 | } 83 | 84 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 85 | public unsafe static Int16 ReadInt16BE(byte* buff, ref int offset) 86 | { 87 | Int16 val = ReadInt16BE(buff, offset); 88 | offset += 2; 89 | return val; 90 | } 91 | 92 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 93 | public unsafe static sbyte ReadSByte(byte* buff, ref int offset) 94 | { 95 | sbyte val = ReadSByte(buff, offset); 96 | offset += 1; 97 | return val; 98 | } 99 | 100 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 101 | public unsafe static UInt64 ReadUInt64LE(byte* buff, ref int offset) 102 | { 103 | UInt64 val = ReadUInt64LE(buff, offset); 104 | offset += 8; 105 | return val; 106 | } 107 | 108 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 109 | public unsafe static UInt64 ReadUInt64BE(byte* buff, ref int offset) 110 | { 111 | UInt64 val = ReadUInt64BE(buff, offset); 112 | offset += 8; 113 | return val; 114 | } 115 | 116 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 117 | public unsafe static UInt32 ReadUInt32LE(byte* buff, ref int offset) 118 | { 119 | UInt32 val = ReadUInt32LE(buff, offset); 120 | offset += 4; 121 | return val; 122 | } 123 | 124 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 125 | public unsafe static UInt32 ReadUInt32BE(byte* buff, ref int offset) 126 | { 127 | UInt32 val = ReadUInt32BE(buff, offset); 128 | offset += 4; 129 | return val; 130 | } 131 | 132 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 133 | public unsafe static UInt16 ReadUInt16LE(byte* buff, ref int offset) 134 | { 135 | UInt16 val = ReadUInt16LE(buff, offset); 136 | offset += 2; 137 | return val; 138 | } 139 | 140 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 141 | public unsafe static UInt16 ReadUInt16BE(byte* buff, ref int offset) 142 | { 143 | UInt16 val = ReadUInt16BE(buff, offset); 144 | offset += 2; 145 | return val; 146 | } 147 | 148 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 149 | public unsafe static byte ReadByte(byte* buff, ref int offset) 150 | { 151 | return buff[offset++]; 152 | } 153 | 154 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 155 | public unsafe static void ReadBytes(byte* buff, ref int offset, byte[] output, UInt32 count) 156 | { 157 | offset += ReadBytes(buff, offset, output, count); 158 | } 159 | 160 | public unsafe static void ReadCountLE(byte* buff, ref int offset, T[] output, UInt32 count) where T : unmanaged 161 | { 162 | offset += ReadCountLE(buff, offset, output, count); 163 | } 164 | } 165 | } -------------------------------------------------------------------------------- /BinaryEx/BinUtils.UnsafeRefWrite.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2022 Matthew Sitton 2 | // BSD License - See LICENSE in the project root for license information. 3 | using System; 4 | using System.Runtime.CompilerServices; 5 | 6 | namespace BinaryEx 7 | { 8 | public static partial class BinUtils 9 | { 10 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 11 | public unsafe static void WriteInt64BE(byte* buff, ref int offset, Int64 value) 12 | { 13 | WriteInt64BE(buff, offset, value); 14 | offset += 8; 15 | } 16 | 17 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 18 | public unsafe static void WriteInt64LE(byte* buff, ref int offset, Int64 value) 19 | { 20 | WriteInt64LE(buff, offset, value); 21 | offset += 8; 22 | } 23 | 24 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 25 | public unsafe static void WriteInt32BE(byte* buff, ref int offset, Int32 value) 26 | { 27 | WriteInt32BE(buff, offset, value); 28 | offset += 4; 29 | } 30 | 31 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 32 | public unsafe static void WriteInt32LE(byte* buff, ref int offset, Int32 value) 33 | { 34 | WriteInt32LE(buff, offset, value); 35 | offset += 4; 36 | } 37 | 38 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 39 | public unsafe static void WriteInt24BE(byte* buff, ref int offset, Int32 value) 40 | { 41 | WriteInt24BE(buff, offset, value); 42 | offset += 3; 43 | } 44 | 45 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 46 | public unsafe static void WriteInt24LE(byte* buff, ref int offset, Int32 value) 47 | { 48 | WriteInt24LE(buff, offset, value); 49 | offset += 3; 50 | } 51 | 52 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 53 | public unsafe static void WriteInt16BE(byte* buff, ref int offset, Int16 value) 54 | { 55 | WriteInt16BE(buff, offset, value); 56 | offset += 2; 57 | } 58 | 59 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 60 | public unsafe static void WriteInt16LE(byte* buff, ref int offset, Int16 value) 61 | { 62 | WriteInt16LE(buff, offset, value); 63 | offset += 2; 64 | } 65 | 66 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 67 | public unsafe static void WriteSByte(byte* buff, ref int offset, sbyte value) 68 | { 69 | WriteSByte(buff, offset, value); 70 | offset += 1; 71 | } 72 | 73 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 74 | public unsafe static void WriteUInt64BE(byte* buff, ref int offset, UInt64 value) 75 | { 76 | WriteUInt64BE(buff, offset, value); 77 | offset += 8; 78 | } 79 | 80 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 81 | public unsafe static void WriteUInt64LE(byte* buff, ref int offset, UInt64 value) 82 | { 83 | WriteUInt64LE(buff, offset, value); 84 | offset += 8; 85 | } 86 | 87 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 88 | public unsafe static void WriteUInt32BE(byte* buff, ref int offset, UInt32 value) 89 | { 90 | WriteUInt32BE(buff, offset, value); 91 | offset += 4; 92 | } 93 | 94 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 95 | public unsafe static void WriteUInt32LE(byte* buff, ref int offset, UInt32 value) 96 | { 97 | WriteUInt32LE(buff, offset, value); 98 | offset += 4; 99 | } 100 | 101 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 102 | public unsafe static void WriteUInt24BE(byte* buff, ref int offset, UInt32 value) 103 | { 104 | WriteUInt24BE(buff, offset, value); 105 | offset += 3; 106 | } 107 | 108 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 109 | public unsafe static void WriteUInt24LE(byte* buff, ref int offset, UInt32 value) 110 | { 111 | WriteUInt24LE(buff, offset, value); 112 | offset += 3; 113 | } 114 | 115 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 116 | public unsafe static void WriteUInt16BE(byte* buff, ref int offset, UInt16 value) 117 | { 118 | WriteUInt16BE(buff, offset, value); 119 | offset += 2; 120 | } 121 | 122 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 123 | public unsafe static void WriteUInt16LE(byte* buff, ref int offset, UInt16 value) 124 | { 125 | WriteUInt16LE(buff, offset, value); 126 | offset += 2; 127 | } 128 | 129 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 130 | public unsafe static void WriteByte(byte* buff, ref int offset, byte value) 131 | { 132 | WriteByte(buff, offset, value); 133 | offset += 1; 134 | } 135 | 136 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 137 | public unsafe static void WriteBytes(byte* buff, ref int offset, byte[] input, UInt32 count) 138 | { 139 | offset += WriteBytes(buff, offset, input, count); 140 | } 141 | 142 | } 143 | } -------------------------------------------------------------------------------- /BinaryEx/BinUtils.UnsafeWrite.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2022 Matthew Sitton 2 | // BSD License - See LICENSE in the project root for license information. 3 | using System; 4 | using System.Diagnostics; 5 | using System.Runtime.CompilerServices; 6 | 7 | namespace BinaryEx 8 | { 9 | public static partial class BinUtils 10 | { 11 | 12 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 13 | public unsafe static void WriteInt64BE(byte* buff, int offset, Int64 value) 14 | { 15 | WriteUInt64BE(buff, offset, (UInt64)value); 16 | } 17 | 18 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 19 | public unsafe static void WriteInt64LE(byte* buff, int offset, Int64 value) 20 | { 21 | WriteUInt64LE(buff, offset, (UInt64)value); 22 | } 23 | 24 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 25 | public unsafe static void WriteInt32BE(byte* buff, int offset, Int32 value) 26 | { 27 | WriteUInt32BE(buff, offset, (UInt32)value); 28 | } 29 | 30 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 31 | public unsafe static void WriteInt32LE(byte* buff, int offset, Int32 value) 32 | { 33 | WriteUInt32LE(buff, offset, (UInt32)value); 34 | } 35 | 36 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 37 | public unsafe static void WriteInt24BE(byte* buff, int offset, Int32 value) 38 | { 39 | Debug.Assert(value <= 0x7FFFFF && value >= -0x7FFFFF); 40 | WriteUInt24BE(buff, offset, (UInt32)(value & 0xFFFFFF)); 41 | } 42 | 43 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 44 | public unsafe static void WriteInt24LE(byte* buff, int offset, Int32 value) 45 | { 46 | Debug.Assert(value <= 0x7FFFFF && value >= -0x7FFFFF); 47 | WriteUInt24LE(buff, offset, (UInt32)(value & 0xFFFFFF)); 48 | } 49 | 50 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 51 | public unsafe static void WriteInt16BE(byte* buff, int offset, Int16 value) 52 | { 53 | WriteUInt16BE(buff, offset, (UInt16)value); 54 | } 55 | 56 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 57 | public unsafe static void WriteInt16LE(byte* buff, int offset, Int16 value) 58 | { 59 | WriteUInt16LE(buff, offset, (UInt16)value); 60 | } 61 | 62 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 63 | public unsafe static void WriteSByte(byte* buff, int offset, sbyte value) 64 | { 65 | WriteByte(buff, offset, (byte)value); 66 | } 67 | 68 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 69 | public unsafe static void WriteUInt64BE(byte* buff, int offset, UInt64 value) 70 | { 71 | Unsafe.WriteUnaligned(ref buff[offset], SwapEndianess(value)); 72 | } 73 | 74 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 75 | public unsafe static void WriteUInt64LE(byte* buff, int offset, UInt64 value) 76 | { 77 | Unsafe.WriteUnaligned(ref buff[offset], value); 78 | } 79 | 80 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 81 | public unsafe static void WriteUInt32BE(byte* buff, int offset, UInt32 value) 82 | { 83 | Unsafe.WriteUnaligned(ref buff[offset], SwapEndianess(value)); 84 | } 85 | 86 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 87 | public unsafe static void WriteUInt32LE(byte* buff, int offset, UInt32 value) 88 | { 89 | Unsafe.WriteUnaligned(ref buff[offset], value); 90 | } 91 | 92 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 93 | public unsafe static void WriteUInt24BE(byte* buff, int offset, UInt32 value) 94 | { 95 | Debug.Assert(value <= 0xFFFFFF); 96 | 97 | value = SwapEndianess(value); 98 | 99 | byte* src = (byte*)Unsafe.AsPointer(ref value) + 1; 100 | byte* destStart = buff + offset; 101 | 102 | Unsafe.CopyBlockUnaligned(destStart, src, 3); 103 | } 104 | 105 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 106 | public unsafe static void WriteUInt24LE(byte* buff, int offset, UInt32 value) 107 | { 108 | Unsafe.CopyBlockUnaligned(ref buff[offset], ref Unsafe.As(ref value), 3); 109 | } 110 | 111 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 112 | public unsafe static void WriteUInt16BE(byte* buff, int offset, UInt16 value) 113 | { 114 | Unsafe.WriteUnaligned(ref buff[offset], SwapEndianess(value)); 115 | } 116 | 117 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 118 | public unsafe static void WriteUInt16LE(byte* buff, int offset, UInt16 value) 119 | { 120 | Unsafe.WriteUnaligned(ref buff[offset], value); 121 | } 122 | 123 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 124 | public unsafe static void WriteByte(byte* buff, int offset, byte value) 125 | { 126 | Unsafe.WriteUnaligned(ref buff[offset], value); 127 | } 128 | 129 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 130 | public unsafe static int WriteBytes(byte* buff, int offset, byte[] input, UInt32 count) 131 | { 132 | Unsafe.CopyBlockUnaligned(ref buff[offset], ref input[0], count); 133 | return (int)count; 134 | } 135 | 136 | } 137 | } -------------------------------------------------------------------------------- /BinaryEx/BinUtils.Write.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2022 Matthew Sitton 2 | // BSD License - See LICENSE in the project root for license information. 3 | using System; 4 | using System.Diagnostics; 5 | using System.Runtime.CompilerServices; 6 | 7 | namespace BinaryEx 8 | { 9 | public static partial class BinUtils 10 | { 11 | 12 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 13 | public static void WriteInt64BE(this byte[] buff, int offset, Int64 value) 14 | { 15 | WriteUInt64BE(buff, offset, (UInt64)value); 16 | } 17 | 18 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 19 | public static void WriteInt64LE(this byte[] buff, int offset, Int64 value) 20 | { 21 | WriteUInt64LE(buff, offset, (UInt64)value); 22 | } 23 | 24 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 25 | public static void WriteInt32BE(this byte[] buff, int offset, Int32 value) 26 | { 27 | WriteUInt32BE(buff, offset, (UInt32)value); 28 | } 29 | 30 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 31 | public static void WriteInt32LE(this byte[] buff, int offset, Int32 value) 32 | { 33 | WriteUInt32LE(buff, offset, (UInt32)value); 34 | } 35 | 36 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 37 | public static void WriteInt24BE(this byte[] buff, int offset, Int32 value) 38 | { 39 | Debug.Assert(value <= 0x7FFFFF && value >= -0x7FFFFF); 40 | Debug.Assert(buff.Length >= offset + 3); 41 | WriteUInt24BE(buff, offset, (UInt32)(value & 0xFFFFFF)); 42 | } 43 | 44 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 45 | public static void WriteInt24LE(this byte[] buff, int offset, Int32 value) 46 | { 47 | Debug.Assert(value <= 0x7FFFFF && value >= -0x7FFFFF); 48 | Debug.Assert(buff.Length >= offset + 3); 49 | WriteUInt24LE(buff, offset, (UInt32)(value & 0xFFFFFF)); 50 | } 51 | 52 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 53 | public static void WriteInt16BE(this byte[] buff, int offset, Int16 value) 54 | { 55 | WriteUInt16BE(buff, offset, (UInt16)value); 56 | } 57 | 58 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 59 | public static void WriteInt16LE(this byte[] buff, int offset, Int16 value) 60 | { 61 | WriteUInt16LE(buff, offset, (UInt16)value); 62 | } 63 | 64 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 65 | public static void WriteSByte(this byte[] buff, int offset, sbyte value) 66 | { 67 | WriteByte(buff, offset, (byte)value); 68 | } 69 | 70 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 71 | public static void WriteUInt64BE(this byte[] buff, int offset, UInt64 value) 72 | { 73 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 74 | Unsafe.WriteUnaligned(ref buff[offset], SwapEndianess(value)); 75 | } 76 | 77 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 78 | public static void WriteUInt64LE(this byte[] buff, int offset, UInt64 value) 79 | { 80 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 81 | Unsafe.WriteUnaligned(ref buff[offset], value); 82 | } 83 | 84 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 85 | public static void WriteUInt32BE(this byte[] buff, int offset, UInt32 value) 86 | { 87 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 88 | Unsafe.WriteUnaligned(ref buff[offset], SwapEndianess(value)); 89 | } 90 | 91 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 92 | public static void WriteUInt32LE(this byte[] buff, int offset, UInt32 value) 93 | { 94 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 95 | Unsafe.WriteUnaligned(ref buff[offset], value); 96 | } 97 | 98 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 99 | public static void WriteUInt24BE(this byte[] buff, int offset, UInt32 value) 100 | { 101 | 102 | Debug.Assert(value <= 0xFFFFFF); 103 | Debug.Assert(buff.Length >= offset + 3); 104 | 105 | value = SwapEndianess(value); 106 | 107 | unsafe 108 | { 109 | byte* src = (byte*)Unsafe.AsPointer(ref value) + 1; 110 | byte* destStart = (byte*)Unsafe.AsPointer(ref buff[offset]); 111 | 112 | Unsafe.CopyBlockUnaligned(destStart, src, 3); 113 | } 114 | } 115 | 116 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 117 | public static void WriteUInt24LE(this byte[] buff, int offset, UInt32 value) 118 | { 119 | Debug.Assert(value <= 0xFFFFFF); 120 | Debug.Assert(buff.Length >= offset + 3); 121 | Unsafe.CopyBlockUnaligned(ref buff[offset], ref Unsafe.As(ref value), 3); 122 | } 123 | 124 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 125 | public static void WriteUInt16BE(this byte[] buff, int offset, UInt16 value) 126 | { 127 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 128 | Unsafe.WriteUnaligned(ref buff[offset], SwapEndianess(value)); 129 | } 130 | 131 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 132 | public static void WriteUInt16LE(this byte[] buff, int offset, UInt16 value) 133 | { 134 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 135 | Unsafe.WriteUnaligned(ref buff[offset], value); 136 | } 137 | 138 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 139 | public static void WriteByte(this byte[] buff, int offset, byte value) 140 | { 141 | Debug.Assert(buff.Length >= offset + Unsafe.SizeOf()); 142 | Unsafe.WriteUnaligned(ref buff[offset], value); 143 | } 144 | 145 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 146 | public static int WriteBytes(this byte[] buff, int offset, byte[] input, UInt32 count) 147 | { 148 | Debug.Assert(buff.Length >= offset + count); 149 | Unsafe.CopyBlockUnaligned(ref buff[offset], ref input[0], count); 150 | return (int)count; 151 | } 152 | 153 | } 154 | } -------------------------------------------------------------------------------- /BinaryEx/BinUtils.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2022 Matthew Sitton 2 | // BSD License - See LICENSE in the project root for license information. 3 | using System; 4 | using System.Runtime.CompilerServices; 5 | 6 | namespace BinaryEx 7 | { 8 | public static partial class BinUtils 9 | { 10 | 11 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 12 | public static Int16 SwapEndianess(Int16 val) 13 | { 14 | return (Int16)SwapEndianess((UInt16)val); 15 | } 16 | 17 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 18 | public static Int32 SwapEndianess(Int32 val) 19 | { 20 | return (Int32)SwapEndianess((UInt32)val); 21 | } 22 | 23 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 24 | public static Int64 SwapEndianess(Int64 val) 25 | { 26 | return (Int64)SwapEndianess((UInt64)val); 27 | } 28 | 29 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 30 | public static UInt16 SwapEndianess(UInt16 val) 31 | { 32 | return (UInt16)((val << 8) | (val >> 8)); 33 | } 34 | 35 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 36 | public static UInt32 SwapEndianess(UInt32 val) 37 | { 38 | val = ((val << 8) & 0xFF00FF00) | ((val >> 8) & 0xFF00FF); 39 | return (val << 16) | (val >> 16); 40 | } 41 | 42 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 43 | public static UInt64 SwapEndianess(UInt64 val) 44 | { 45 | return (((UInt64)SwapEndianess((UInt32)val)) << 32) | (UInt64)SwapEndianess((UInt32)(val >> 32)); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /MSChartParser/ChartEditorExtentions/NoteFunctions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016-2020 Alexander Ong 2 | // See LICENSE in project root for license information. 3 | 4 | //#define OPEN_NOTES_BLOCK_EXTENDED_SUSTAINS 5 | 6 | using System; 7 | using System.Collections; 8 | using System.Collections.Generic; 9 | using MoonscraperChartEditor.Song; 10 | 11 | public static class NoteFunctions 12 | { 13 | 14 | public static bool AllowedToBeDoubleKick(Note note, Song.Difficulty difficulty) 15 | { 16 | return note.IsOpenNote() && difficulty == Song.Difficulty.Expert; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /MSChartParser/Engine/Core/EnumLookupTable.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016-2020 Alexander Ong 2 | // See LICENSE in project root for license information. 3 | 4 | using System; 5 | using System.Collections; 6 | using System.Collections.Generic; 7 | 8 | namespace MoonscraperEngine 9 | { 10 | public class EnumLookupTable : IList where EnumType : struct, Enum 11 | { 12 | Value[] table = new Value[EnumX.Count]; 13 | 14 | public Value this[int index] { get => table[index]; set => table[index] = value; } 15 | public Value this[EnumType index] { get => table[EnumX.ToInt(index)]; set => table[EnumX.ToInt(index)] = value; } 16 | 17 | public int Count => table.Length; 18 | 19 | public bool IsReadOnly => table.IsReadOnly; 20 | 21 | public void Add(Value item) 22 | { 23 | ((IList)table).Add(item); 24 | } 25 | 26 | public void Clear() 27 | { 28 | ((IList)table).Clear(); 29 | } 30 | 31 | public bool Contains(Value item) 32 | { 33 | return ((IList)table).Contains(item); 34 | } 35 | 36 | public void CopyTo(Value[] array, int arrayIndex) 37 | { 38 | ((IList)table).CopyTo(array, arrayIndex); 39 | } 40 | 41 | public IEnumerator GetEnumerator() 42 | { 43 | return ((IList)table).GetEnumerator(); 44 | } 45 | 46 | public int IndexOf(Value item) 47 | { 48 | return ((IList)table).IndexOf(item); 49 | } 50 | 51 | public void Insert(int index, Value item) 52 | { 53 | ((IList)table).Insert(index, item); 54 | } 55 | 56 | public bool Remove(Value item) 57 | { 58 | return ((IList)table).Remove(item); 59 | } 60 | 61 | public void RemoveAt(int index) 62 | { 63 | ((IList)table).RemoveAt(index); 64 | } 65 | 66 | IEnumerator IEnumerable.GetEnumerator() 67 | { 68 | return table.GetEnumerator(); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /MSChartParser/Engine/Core/EnumX.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016-2020 Alexander Ong 2 | // See LICENSE in project root for license information. 3 | 4 | using System; 5 | using System.Collections; 6 | using System.Linq.Expressions; 7 | 8 | namespace MoonscraperEngine 9 | { 10 | public static class EnumX where EnumType : struct, Enum 11 | { 12 | public static readonly EnumType[] Values = Enum.GetValues(); 13 | public static int Count => Values.Length; 14 | static readonly string[] StringValues = new string[Count]; 15 | 16 | static EnumX() 17 | { 18 | for (int i = 0; i < Values.Length; ++i) 19 | { 20 | StringValues[i] = Values[i].ToString(); 21 | } 22 | } 23 | 24 | public static int ToInt(EnumType t) 25 | { 26 | return CastTo.From(t); 27 | } 28 | 29 | public static EnumType FromInt(int t) 30 | { 31 | return CastTo.From(t); 32 | } 33 | 34 | public static bool GenericTryParse(string str, out EnumType enumType) 35 | { 36 | for (int i = 0; i < Count; ++i) 37 | { 38 | if (string.Equals(str, StringValues[i])) 39 | { 40 | enumType = Values[i]; 41 | return true; 42 | } 43 | } 44 | 45 | enumType = Values[0]; 46 | 47 | return false; 48 | } 49 | 50 | // https://stackoverflow.com/questions/1189144/c-sharp-non-boxing-conversion-of-generic-enum-to-int 51 | static class CastTo 52 | { 53 | /// 54 | /// Casts to . 55 | /// This does not cause boxing for value types. 56 | /// Useful in generic methods. 57 | /// 58 | /// Source type to cast from. Usually a generic type. 59 | public static T From(S s) 60 | { 61 | return Cache.caster(s); 62 | } 63 | 64 | private static class Cache 65 | { 66 | public static readonly Func caster = Get(); 67 | 68 | private static Func Get() 69 | { 70 | var p = Expression.Parameter(typeof(S)); 71 | var c = Expression.ConvertChecked(p, typeof(T)); 72 | return Expression.Lambda>(c, p).Compile(); 73 | } 74 | } 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /MSChartParser/Engine/Logger.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016-2020 Alexander Ong 2 | // See LICENSE in project root for license information. 3 | 4 | using System; 5 | using System.Collections; 6 | using System.Collections.Generic; 7 | 8 | public static class Logger 9 | { 10 | public static string LogException(System.Exception e, string errorContextMessage) 11 | { 12 | string fullMessage = string.Format("Exception Logged-\nContext: {0}\nMessage: {1} \nStack Trace: {2}", errorContextMessage, e.Message, e.StackTrace.ToString()); 13 | Console.WriteLine(fullMessage); 14 | 15 | return fullMessage; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /MSChartParser/Game/Misc/SongValidate.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016-2020 Alexander Ong 2 | // See LICENSE in project root for license information. 3 | 4 | using System.Text; 5 | using MoonscraperEngine; 6 | using MoonscraperChartEditor.Song; 7 | using System; 8 | 9 | public class SongValidate 10 | { 11 | [System.Flags] 12 | public enum ValidationOptions 13 | { 14 | None = 0, 15 | GuitarHero3 = 1 << 0, 16 | CloneHero = 1 << 1, 17 | } 18 | 19 | public struct ValidationParameters 20 | { 21 | public bool checkMidiIssues; 22 | public float songLength; 23 | } 24 | 25 | static string PrintObjectTime(float seconds) 26 | { 27 | TimeSpan time = TimeSpan.FromSeconds(seconds); 28 | return time.ToString("mm':'ss'.'ff"); 29 | } 30 | 31 | public static string GenerateReport(ValidationOptions validationOptions, Song song, ValidationParameters validationParams, out bool hasErrors) 32 | { 33 | StringBuilder sb = new StringBuilder(); 34 | hasErrors = false; 35 | 36 | sb.AppendFormat("{0}\n", CheckForErrorsMoonscraper(song, validationParams, ref hasErrors)); 37 | 38 | if ((validationOptions & ValidationOptions.GuitarHero3) != 0) 39 | { 40 | sb.AppendFormat("{0}\n", CheckForErrorsGuitarHero3(song, validationParams, ref hasErrors)); 41 | } 42 | 43 | if ((validationOptions & ValidationOptions.CloneHero) != 0) 44 | { 45 | sb.AppendFormat("{0}\n", CheckForErrorsCloneHero(song, validationParams, ref hasErrors)); 46 | } 47 | 48 | return sb.ToString(); 49 | } 50 | 51 | static string CheckForErrorsMoonscraper(Song song, ValidationParameters validationParams, ref bool hasErrors) 52 | { 53 | bool hasErrorsLocal = false; 54 | StringBuilder sb = new StringBuilder(); 55 | sb.AppendLine("Moonscraper validation report: "); 56 | 57 | // Check if any objects have exceeded the max length 58 | { 59 | uint tick = song.TimeToTick(validationParams.songLength, song.resolution); 60 | 61 | // Song objects 62 | { 63 | // Synctrack 64 | { 65 | int index, length; 66 | SongObjectHelper.GetRange(song.syncTrack, tick, uint.MaxValue, out index, out length); 67 | 68 | for (int i = index; i < length; ++i) 69 | { 70 | hasErrorsLocal |= true; 71 | 72 | SyncTrack st = song.syncTrack[i]; 73 | 74 | sb.AppendFormat("\tFound synctrack object beyond the length of the song-\n"); 75 | sb.AppendFormat("\t\tType = {0}, time = {2}, position = {1}\n", st.GetType(), st.tick, PrintObjectTime(st.time)); 76 | } 77 | } 78 | 79 | // Events 80 | { 81 | int index, length; 82 | SongObjectHelper.GetRange(song.eventsAndSections, tick, uint.MaxValue, out index, out length); 83 | 84 | for (int i = index; i < length; ++i) 85 | { 86 | hasErrorsLocal |= true; 87 | 88 | MoonscraperChartEditor.Song.Event eventObject = song.eventsAndSections[i]; 89 | 90 | sb.AppendFormat("\tFound event object beyond the length of the song-\n"); 91 | sb.AppendFormat("\t\tType = {0}, time = {2}, position = {1}\n", eventObject.GetType(), eventObject.tick, PrintObjectTime(eventObject.time)); 92 | } 93 | } 94 | } 95 | 96 | // Chart objects 97 | foreach (Song.Instrument instrument in EnumX.Values) 98 | { 99 | if (instrument == Song.Instrument.Unrecognised) 100 | continue; 101 | 102 | foreach (Song.Difficulty difficulty in EnumX.Values) 103 | { 104 | Chart chart = song.GetChart(instrument, difficulty); 105 | 106 | int index, length; 107 | SongObjectHelper.GetRange(chart.chartObjects, tick, uint.MaxValue, out index, out length); 108 | 109 | for (int i = index; i < length; ++i) 110 | { 111 | hasErrorsLocal |= true; 112 | 113 | ChartObject co = chart.chartObjects[i]; 114 | 115 | sb.AppendFormat("\tFound chart object beyond the length of the song-\n"); 116 | sb.AppendFormat("\t\tType = {0}, time = {2}, position = {1}\n", co.GetType(), co.tick, PrintObjectTime(co.time)); 117 | } 118 | } 119 | } 120 | } 121 | 122 | if (!hasErrorsLocal) 123 | { 124 | sb.AppendLine("\tNo errors detected"); 125 | } 126 | 127 | hasErrors |= hasErrorsLocal; 128 | 129 | return sb.ToString(); 130 | } 131 | 132 | static string CheckForErrorsGuitarHero3(Song song, ValidationParameters validationParams, ref bool hasErrors) 133 | { 134 | const int SECTION_LIMIT = 100; 135 | 136 | StringBuilder sb = new StringBuilder(); 137 | sb.AppendLine("Guitar Hero 3 validation report: "); 138 | 139 | bool hasErrorsLocal = false; 140 | 141 | // Check that we haven't exceeded the section count in GH3 142 | if (song.sections.Count > 100) 143 | { 144 | hasErrorsLocal |= true; 145 | sb.AppendFormat("\tSection count has exceeded limit of {0} sections\n", SECTION_LIMIT); 146 | sb.AppendFormat("\tAffected sections:\n"); 147 | 148 | for (int i = SECTION_LIMIT; i < song.sections.Count; ++i) 149 | { 150 | Section section = song.sections[i]; 151 | sb.AppendFormat("\t\tTime = {2}, Position = {0}, Title = {1}\n", section.tick, section.title, PrintObjectTime(section.time)); 152 | } 153 | } 154 | 155 | if (!hasErrorsLocal) 156 | { 157 | sb.AppendLine("\tNo errors detected"); 158 | } 159 | 160 | hasErrors |= hasErrorsLocal; 161 | 162 | return sb.ToString(); 163 | } 164 | 165 | static string CheckForErrorsCloneHero(Song song, ValidationParameters validationParams, ref bool hasErrors) 166 | { 167 | StringBuilder sb = new StringBuilder(); 168 | sb.AppendLine("Clone Hero validation report: "); 169 | 170 | bool hasErrorsLocal = false; 171 | 172 | // Check that time signature positions line up on the measure from the previous time signature 173 | { 174 | var timeSignatures = song.timeSignatures; 175 | for (int tsIndex = 1; tsIndex < timeSignatures.Count; ++tsIndex) 176 | { 177 | TimeSignature previousTs = timeSignatures[tsIndex - 1]; 178 | TimeSignature.MeasureInfo measureInfo = previousTs.GetMeasureInfo(); 179 | 180 | TimeSignature tsToTest = timeSignatures[tsIndex]; 181 | uint deltaTick = tsToTest.tick - previousTs.tick; 182 | 183 | var measureLine = measureInfo.measureLine; 184 | if (((float)deltaTick % measureLine.tickGap) != 0) // Doesn't line up on a measure 185 | { 186 | hasErrorsLocal |= true; 187 | sb.AppendFormat("\tFound misaligned Time Signature at time {1}, position {0}. Time signatures must be aligned to the measure set by the previous time signature.\n", 188 | tsToTest.tick 189 | , PrintObjectTime(tsToTest.time) 190 | ); 191 | } 192 | } 193 | } 194 | 195 | // If we have no starpower but more than 1 solo section then CH will interpret this as an RB1 style midi, and misinterpret the solo markers as starpower 196 | if (validationParams.checkMidiIssues) 197 | { 198 | foreach (Song.Instrument instrument in EnumX.Values) 199 | { 200 | if (instrument == Song.Instrument.Unrecognised) 201 | continue; 202 | 203 | foreach (Song.Difficulty difficulty in EnumX.Values) 204 | { 205 | Chart chart = song.GetChart(instrument, difficulty); 206 | 207 | if (chart.starPower.Count <= 0) 208 | { 209 | // Check for solo markers 210 | int soloMarkerCount = 0; 211 | foreach (ChartEvent ce in chart.events) 212 | { 213 | if (ce.eventName == MoonscraperChartEditor.Song.IO.MidIOHelper.SoloEventText) 214 | { 215 | ++soloMarkerCount; 216 | 217 | if (soloMarkerCount > 1) 218 | { 219 | hasErrorsLocal |= true; 220 | sb.AppendFormat("\tTrack {0} has no starpower and more than 1 solo section. If exported to the midi format, Clone Hero will interpret this chart as an older style midi, and will misinterpret solo markers as starpower.\n", instrument); 221 | 222 | goto NewInstrument; 223 | } 224 | } 225 | } 226 | } 227 | } 228 | 229 | NewInstrument:; 230 | } 231 | } 232 | 233 | if (!hasErrorsLocal) 234 | { 235 | sb.AppendLine("\tNo errors detected"); 236 | } 237 | 238 | hasErrors |= hasErrorsLocal; 239 | 240 | return sb.ToString(); 241 | } 242 | } 243 | -------------------------------------------------------------------------------- /MSChartParser/Game/Misc/Utility.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016-2020 Alexander Ong 2 | // See LICENSE in project root for license information. 3 | 4 | using System.Collections; 5 | using System; 6 | using System.Runtime.Serialization.Formatters.Binary; 7 | 8 | static class Utility 9 | { 10 | public const int NOTFOUND = -1; 11 | static System.Text.StringBuilder timeFormatter = new System.Text.StringBuilder(32, 32); 12 | static readonly char[] numbers = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; 13 | 14 | public static string timeConvertion(float time) 15 | { 16 | timeFormatter.Remove(0, timeFormatter.Length); 17 | TimeSpan timeSpan = TimeSpan.FromSeconds(time); 18 | 19 | if (timeSpan.Hours > 0) 20 | { 21 | return String.Format(@"{0:hh\:mm\:ss\.ff}", timeSpan); 22 | } 23 | else 24 | { 25 | return String.Format(@"{0:mm\:ss\.ff}", timeSpan); 26 | } 27 | } 28 | 29 | static void AppendDigit(System.Text.StringBuilder timeFormatter, int digit) 30 | { 31 | // Append the first digit 32 | int firstDigit = digit / 10; 33 | if (digit < 10) 34 | timeFormatter.Append('0'); 35 | else 36 | timeFormatter.Append(numbers[firstDigit]); 37 | // Append second digit 38 | timeFormatter.Append(numbers[digit - firstDigit * 10]); 39 | } 40 | 41 | static int millisecondRounding(int value, int roundPlaces) 42 | { 43 | string sVal = value.ToString(); 44 | 45 | if (sVal.Length > 0 && sVal[0] == '-') 46 | ++roundPlaces; 47 | 48 | if (sVal.Length > roundPlaces) 49 | sVal = sVal.Remove(roundPlaces); 50 | 51 | return int.Parse(sVal); 52 | } 53 | 54 | public static bool validateExtension(string filepath, string[] validExtensions) 55 | { 56 | // Need to check extension 57 | string extension = System.IO.Path.GetExtension(filepath); 58 | 59 | foreach (string validExtension in validExtensions) 60 | { 61 | if (extension == validExtension) 62 | return true; 63 | } 64 | return false; 65 | } 66 | 67 | public struct IntVector2 68 | { 69 | public int x, y; 70 | public IntVector2(int x, int y) 71 | { 72 | this.x = x; 73 | this.y = y; 74 | } 75 | } 76 | } 77 | 78 | public static class floatExtension 79 | { 80 | public static float Round(this float sourceFloat, int decimalPlaces) 81 | { 82 | return (float)Math.Round(sourceFloat, decimalPlaces); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /MSChartParser/Globals.cs: -------------------------------------------------------------------------------- 1 | namespace MoonscraperChartEditor 2 | { 3 | public static class Globals 4 | { 5 | public const string TABSPACE = " "; 6 | public static readonly string LINE_ENDING = "\r\n"; 7 | public static readonly string[] validAudioExtensions = { ".ogg", ".wav", ".mp3" }; 8 | 9 | public static bool validateExtension(string filepath, string[] validExtensions) 10 | { 11 | // Need to check extension 12 | string extension = System.IO.Path.GetExtension(filepath); 13 | 14 | foreach (string validExtension in validExtensions) 15 | { 16 | if (extension == validExtension) 17 | return true; 18 | } 19 | return false; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /MSChartParser/IO/Chart/ChartIOHelper.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016-2020 Alexander Ong 2 | // See LICENSE in project root for license information. 3 | 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | using System.Text.RegularExpressions; 7 | using System.Linq; 8 | 9 | namespace MoonscraperChartEditor.Song.IO 10 | { 11 | public static class ChartIOHelper 12 | { 13 | public enum FileSubType 14 | { 15 | Default, 16 | 17 | // Stores space characters found in ChartEvent objects as Japanese full-width spaces. Need to convert this back when loading. 18 | MoonscraperPropriety, 19 | } 20 | 21 | public const string 22 | c_dataBlockSong = "[Song]" 23 | , c_dataBlockSyncTrack = "[SyncTrack]" 24 | , c_dataBlockEvents = "[Events]" 25 | ; 26 | 27 | public const int c_proDrumsOffset = 64; 28 | public const int c_instrumentPlusOffset = 32; 29 | public const int c_starpowerId = 2; 30 | public const int c_starpowerDrumFillId = 64; 31 | 32 | public static readonly Dictionary c_guitarNoteNumLookup = new Dictionary() 33 | { 34 | { 0, (int)Note.GuitarFret.Green }, 35 | { 1, (int)Note.GuitarFret.Red }, 36 | { 2, (int)Note.GuitarFret.Yellow }, 37 | { 3, (int)Note.GuitarFret.Blue }, 38 | { 4, (int)Note.GuitarFret.Orange }, 39 | { 7, (int)Note.GuitarFret.Open }, 40 | }; 41 | 42 | public static readonly Dictionary c_guitarFlagNumLookup = new Dictionary() 43 | { 44 | { 5 , Note.Flags.Forced }, 45 | { 6 , Note.Flags.Tap }, 46 | }; 47 | 48 | public static readonly Dictionary c_drumNoteNumLookup = new Dictionary() 49 | { 50 | { 0, (int)Note.DrumPad.Kick }, 51 | { 1, (int)Note.DrumPad.Red }, 52 | { 2, (int)Note.DrumPad.Yellow }, 53 | { 3, (int)Note.DrumPad.Blue }, 54 | { 4, (int)Note.DrumPad.Orange }, 55 | { 5, (int)Note.DrumPad.Green }, 56 | }; 57 | 58 | public static readonly Dictionary c_drumNoteToSaveNumberLookup = c_drumNoteNumLookup.ToDictionary((i) => i.Value, (i) => i.Key); 59 | 60 | public static readonly Dictionary c_drumFlagNumLookup = new Dictionary() 61 | { 62 | { c_proDrumsOffset + 2, Note.Flags.ProDrums_Cymbal }, // Yellow save num from c_drumNoteNumLookup 63 | { c_proDrumsOffset + 3, Note.Flags.ProDrums_Cymbal }, // Blue save num from c_drumNoteNumLookup 64 | { c_proDrumsOffset + 4, Note.Flags.ProDrums_Cymbal }, // Orange (Green in 4-lane) save num from c_drumNoteNumLookup 65 | { c_instrumentPlusOffset, Note.Flags.InstrumentPlus }, // Double Kick 66 | }; 67 | 68 | // Default flags, mark as cymbal for pro drums automatically. Also used for choosing whether to write flag information or not if it's like this by default in the first place. 69 | public static readonly Dictionary c_drumNoteDefaultFlagsLookup = new Dictionary() 70 | { 71 | { (int)Note.DrumPad.Kick , Note.Flags.None }, 72 | { (int)Note.DrumPad.Red , Note.Flags.None }, 73 | { (int)Note.DrumPad.Yellow , Note.Flags.None }, 74 | { (int)Note.DrumPad.Blue , Note.Flags.None }, 75 | { (int)Note.DrumPad.Orange , Note.Flags.None }, // Orange becomes green during 4-lane 76 | { (int)Note.DrumPad.Green , Note.Flags.None }, 77 | }; 78 | 79 | public static readonly Dictionary c_ghlNoteNumLookup = new Dictionary() 80 | { 81 | { 0, (int)Note.GHLiveGuitarFret.White1 }, 82 | { 1, (int)Note.GHLiveGuitarFret.White2 }, 83 | { 2, (int)Note.GHLiveGuitarFret.White3 }, 84 | { 3, (int)Note.GHLiveGuitarFret.Black1 }, 85 | { 4, (int)Note.GHLiveGuitarFret.Black2 }, 86 | { 8, (int)Note.GHLiveGuitarFret.Black3 }, 87 | { 7, (int)Note.GHLiveGuitarFret.Open }, 88 | }; 89 | 90 | public static readonly Dictionary c_ghlFlagNumLookup = c_guitarFlagNumLookup; 91 | 92 | public static readonly Dictionary c_trackNameToTrackDifficultyLookup = new Dictionary() 93 | { 94 | { "Easy", Song.Difficulty.Easy }, 95 | { "Medium", Song.Difficulty.Medium }, 96 | { "Hard", Song.Difficulty.Hard }, 97 | { "Expert", Song.Difficulty.Expert }, 98 | }; 99 | 100 | public static readonly Dictionary c_instrumentStrToEnumLookup = new Dictionary() 101 | { 102 | { "Single", Song.Instrument.Guitar }, 103 | { "DoubleGuitar", Song.Instrument.GuitarCoop }, 104 | { "DoubleBass", Song.Instrument.Bass }, 105 | { "DoubleRhythm", Song.Instrument.Rhythm }, 106 | { "Drums", Song.Instrument.Drums }, 107 | { "Keyboard", Song.Instrument.Keys }, 108 | { "GHLGuitar", Song.Instrument.GHLiveGuitar }, 109 | { "GHLBass", Song.Instrument.GHLiveBass }, 110 | }; 111 | 112 | public static readonly Dictionary c_instrumentParsingTypeLookup = new Dictionary() 113 | { 114 | // Other instruments default to loading as a guitar type track 115 | { Song.Instrument.Drums, Song.Instrument.Drums }, 116 | { Song.Instrument.GHLiveGuitar , Song.Instrument.GHLiveGuitar }, 117 | { Song.Instrument.GHLiveBass , Song.Instrument.GHLiveBass }, 118 | }; 119 | 120 | public static class MetaData 121 | { 122 | const string QUOTEVALIDATE = @"""[^""\\]*(?:\\.[^""\\]*)*"""; 123 | const string QUOTESEARCH = "\"([^\"]*)\""; 124 | const string FLOATSEARCH = @"[\-\+]?\d+(\.\d+)?"; // US culture only 125 | 126 | public static readonly System.Globalization.CultureInfo c_cultureInfo = new System.Globalization.CultureInfo("en-US"); 127 | 128 | public enum MetadataValueType 129 | { 130 | String, 131 | Float, 132 | Player2, 133 | Difficulty, 134 | Year, 135 | } 136 | 137 | public class MetadataItem 138 | { 139 | string m_key; 140 | Regex m_readerParseRegex; 141 | string m_saveFormat; 142 | 143 | static readonly string c_metaDataSaveFormat = string.Format("{0}{{0}} = \"{{{{0}}}}\"{1}", Globals.TABSPACE, Globals.LINE_ENDING); 144 | static readonly string c_metaDataSaveFormatNoQuote = string.Format("{0}{{0}} = {{{{0}}}}{1}", Globals.TABSPACE, Globals.LINE_ENDING); 145 | 146 | public string key { get { return m_key; } } 147 | public Regex regex { get { return m_readerParseRegex; } } 148 | public string saveFormat { get { return m_saveFormat; } } 149 | 150 | public MetadataItem(string key, MetadataValueType type) 151 | { 152 | m_key = key; 153 | 154 | Regex parseStrRegex = new Regex(key + " = " + QUOTEVALIDATE, RegexOptions.Compiled); 155 | 156 | switch (type) 157 | { 158 | case MetadataValueType.String: 159 | { 160 | m_readerParseRegex = parseStrRegex; 161 | m_saveFormat = string.Format(c_metaDataSaveFormat, key); 162 | break; 163 | } 164 | 165 | case MetadataValueType.Float: 166 | { 167 | m_readerParseRegex = new Regex(key + " = " + FLOATSEARCH, RegexOptions.Compiled); 168 | m_saveFormat = string.Format(c_cultureInfo, c_metaDataSaveFormatNoQuote, key); 169 | break; 170 | } 171 | 172 | case MetadataValueType.Player2: 173 | { 174 | m_readerParseRegex = new Regex(key + @" = \w+", RegexOptions.Compiled); 175 | m_saveFormat = string.Format(c_metaDataSaveFormatNoQuote, key); 176 | break; 177 | } 178 | 179 | case MetadataValueType.Difficulty: 180 | { 181 | m_readerParseRegex = new Regex(key + @" = \d+", RegexOptions.Compiled); 182 | m_saveFormat = string.Format(c_metaDataSaveFormatNoQuote, key); 183 | break; 184 | } 185 | 186 | case MetadataValueType.Year: 187 | { 188 | m_readerParseRegex = parseStrRegex; 189 | m_saveFormat = string.Format("{0}{1} = \", {{0}}\"{2}", Globals.TABSPACE, "Year", Globals.LINE_ENDING); 190 | break; 191 | } 192 | 193 | default: 194 | throw new System.Exception("Unhandled Metadata item type"); 195 | } 196 | } 197 | } 198 | 199 | public readonly static MetadataItem name = new MetadataItem("Name", MetadataValueType.String); 200 | public readonly static MetadataItem artist = new MetadataItem("Artist", MetadataValueType.String); 201 | public readonly static MetadataItem charter = new MetadataItem("Charter", MetadataValueType.String); 202 | public readonly static MetadataItem offset = new MetadataItem("Offset", MetadataValueType.Float); 203 | public readonly static MetadataItem resolution = new MetadataItem("Resolution", MetadataValueType.Float); 204 | public readonly static MetadataItem player2 = new MetadataItem("Player2", MetadataValueType.Player2); 205 | public readonly static MetadataItem difficulty = new MetadataItem("Difficulty", MetadataValueType.Difficulty); 206 | public readonly static MetadataItem length = new MetadataItem("Length", MetadataValueType.Float); 207 | public readonly static MetadataItem previewStart = new MetadataItem("PreviewStart", MetadataValueType.Float); 208 | public readonly static MetadataItem previewEnd = new MetadataItem("PreviewEnd", MetadataValueType.Float); 209 | public readonly static MetadataItem genre = new MetadataItem("Genre", MetadataValueType.String); 210 | public readonly static MetadataItem year = new MetadataItem("Year", MetadataValueType.Year); 211 | public readonly static MetadataItem album = new MetadataItem("Album", MetadataValueType.String); 212 | public readonly static MetadataItem mediaType = new MetadataItem("MediaType", MetadataValueType.String); 213 | public readonly static MetadataItem musicStream = new MetadataItem("MusicStream", MetadataValueType.String); 214 | public readonly static MetadataItem guitarStream = new MetadataItem("GuitarStream", MetadataValueType.String); 215 | public readonly static MetadataItem bassStream = new MetadataItem("BassStream", MetadataValueType.String); 216 | public readonly static MetadataItem rhythmStream = new MetadataItem("RhythmStream", MetadataValueType.String); 217 | public readonly static MetadataItem drumStream = new MetadataItem("DrumStream", MetadataValueType.String); 218 | public readonly static MetadataItem drum2Stream = new MetadataItem("Drum2Stream", MetadataValueType.String); 219 | public readonly static MetadataItem drum3Stream = new MetadataItem("Drum3Stream", MetadataValueType.String); 220 | public readonly static MetadataItem drum4Stream = new MetadataItem("Drum4Stream", MetadataValueType.String); 221 | public readonly static MetadataItem vocalStream = new MetadataItem("VocalStream", MetadataValueType.String); 222 | public readonly static MetadataItem keysStream = new MetadataItem("KeysStream", MetadataValueType.String); 223 | public readonly static MetadataItem crowdStream = new MetadataItem("CrowdStream", MetadataValueType.String); 224 | 225 | public static string ParseAsString(string line) 226 | { 227 | return Regex.Matches(line, QUOTESEARCH)[0].ToString().Trim('"'); 228 | } 229 | 230 | public static float ParseAsFloat(string line) 231 | { 232 | return float.Parse(Regex.Matches(line, FLOATSEARCH)[0].ToString(), c_cultureInfo); // .chart format only allows '.' as decimal seperators. Need to parse correctly under any locale. 233 | } 234 | 235 | public static short ParseAsShort(string line) 236 | { 237 | return short.Parse(Regex.Matches(line, FLOATSEARCH)[0].ToString()); 238 | } 239 | } 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /MSChartParser/IO/ExportOptions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016-2020 Alexander Ong 2 | // See LICENSE in project root for license information. 3 | 4 | namespace MoonscraperChartEditor.Song.IO 5 | { 6 | public struct ExportOptions 7 | { 8 | public struct MidiOptions 9 | { 10 | public enum RBFormat 11 | { 12 | RB2, 13 | RB3, 14 | } 15 | 16 | public Song.Difficulty difficultyToUseGlobalTrackEvents; // Which difficulty to take things like starpower, tap, solo and tom toggle events from 17 | public RBFormat rbFormat; // Changes section name prefix 18 | } 19 | 20 | public bool forced; 21 | public Format format; 22 | public uint tickOffset; 23 | public float targetResolution; 24 | public bool copyDownEmptyDifficulty; 25 | public MidiOptions midiOptions; 26 | public bool isGeneralSave; 27 | public bool substituteCHLyricChars; 28 | 29 | public enum Format 30 | { 31 | Chart, Midi, Msce 32 | } 33 | 34 | public enum Game 35 | { 36 | PhaseShift, RockBand2, RockBand3 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /MSChartParser/IO/MSCE/MsceIOHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using MoonscraperEngine; 6 | 7 | namespace MoonscraperChartEditor.Song.IO 8 | { 9 | // Stores space characters found in ChartEvent objects as Japanese full-width spaces. Need to convert this back when loading. 10 | public class MsceIOHelper 11 | { 12 | public const string FileExtention = ".msce"; 13 | 14 | public static readonly Dictionary LocalEventCharReplacementToMsce = new Dictionary() 15 | { 16 | { ' ', '\u3000' } 17 | }; 18 | 19 | public static readonly Dictionary LyricEventCharReplacementToMsce = new Dictionary() 20 | { 21 | { '\"', '`' } 22 | }; 23 | 24 | public static readonly Dictionary LocalEventCharReplacementFromMsce = LocalEventCharReplacementToMsce.ToDictionary((i) => i.Value, (i) => i.Key); 25 | public static readonly Dictionary LyricEventCharReplacementFromMsce = LyricEventCharReplacementToMsce.ToDictionary((i) => i.Value, (i) => i.Key); 26 | 27 | 28 | 29 | static readonly Dictionary c_audioStreamLocationOverrideDict = new Dictionary() 30 | { 31 | // String list is ordered in priority. If it finds a file names with the first string it'll skip over the rest. 32 | // Otherwise just does a ToString on the AudioInstrument enum 33 | { Song.AudioInstrument.Drum, new string[] { "drums", "drums_1" } }, 34 | }; 35 | 36 | public static void DiscoverAudio(string directory, Song song) 37 | { 38 | 39 | foreach (Song.AudioInstrument audio in EnumX.Values) 40 | { 41 | // First try any specific filenames for the instrument, then try the instrument name 42 | List filenamesToTry = new List(); 43 | 44 | if (c_audioStreamLocationOverrideDict.ContainsKey(audio)) 45 | { 46 | filenamesToTry.AddRange(c_audioStreamLocationOverrideDict[audio]); 47 | } 48 | 49 | filenamesToTry.Add(audio.ToString()); 50 | 51 | // Search for each combination of filenamesToTry + audio extension until we find a file 52 | string audioFilepath = null; 53 | 54 | foreach (string testFilename in filenamesToTry) 55 | { 56 | foreach (string extension in Globals.validAudioExtensions) 57 | { 58 | string testFilepath = Path.Combine(directory, testFilename.ToLower() + extension); 59 | 60 | if (File.Exists(testFilepath)) 61 | { 62 | audioFilepath = testFilepath; 63 | break; 64 | } 65 | } 66 | } 67 | 68 | // If we didn't find a file, assign a default value to the audio path 69 | if (audioFilepath == null) 70 | { 71 | audioFilepath = Path.Combine(directory, audio.ToString().ToLower() + ".ogg"); 72 | } 73 | 74 | // Debug.Log(audioFilepath); 75 | song.SetAudioLocation(audio, audioFilepath); 76 | } 77 | } 78 | 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /MSChartParser/IO/Midi/ByteSort.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016-2020 Alexander Ong 2 | // See LICENSE in project root for license information. 3 | 4 | namespace MoonscraperChartEditor.Song.IO 5 | { 6 | public class SortableBytes 7 | { 8 | public uint tick; 9 | public byte[] bytes; 10 | 11 | public SortableBytes() 12 | { 13 | tick = 0; 14 | bytes = new byte[0]; 15 | } 16 | 17 | public SortableBytes(uint tick, byte[] bytes) 18 | { 19 | this.tick = tick; 20 | this.bytes = bytes; 21 | } 22 | 23 | public static void Sort(SortableBytes[] bytes) 24 | { 25 | MergeSort(bytes, 0, bytes.Length - 1); 26 | } 27 | 28 | public static SortableBytes[] MergeAlreadySorted(SortableBytes[] a, SortableBytes[] b) 29 | { 30 | SortableBytes[] merged = new SortableBytes[a.Length + b.Length]; 31 | int i = 0, j = 0; 32 | 33 | for (int k = 0; k < merged.Length; ++k) 34 | { 35 | SortableBytes selected; 36 | 37 | if (i >= a.Length) 38 | selected = b[j++]; 39 | else if (j >= b.Length) 40 | selected = a[i++]; 41 | else 42 | selected = a[i].tick < b[j].tick ? a[i++] : b[j++]; 43 | 44 | merged[k] = selected; 45 | } 46 | 47 | return merged; 48 | } 49 | 50 | static void MergeSort(SortableBytes[] bytes, int left, int right) 51 | { 52 | int mid; 53 | 54 | if (right > left) 55 | { 56 | mid = (right + left) / 2; 57 | 58 | MergeSort(bytes, left, mid); 59 | MergeSort(bytes, mid + 1, right); 60 | 61 | Merge(bytes, left, (mid + 1), right); 62 | } 63 | 64 | } 65 | 66 | static void Merge(SortableBytes[] bytes, int left, int mid, int right) 67 | { 68 | SortableBytes[] temp = new SortableBytes[bytes.Length]; 69 | int i, eol, num, pos; 70 | 71 | eol = (mid - 1); 72 | pos = left; 73 | num = (right - left + 1); 74 | 75 | while ((left <= eol) && (mid <= right)) 76 | { 77 | if (bytes[left].tick <= bytes[mid].tick) 78 | temp[pos++] = bytes[left++]; 79 | else 80 | temp[pos++] = bytes[mid++]; 81 | } 82 | 83 | while (left <= eol) 84 | temp[pos++] = bytes[left++]; 85 | 86 | while (mid <= right) 87 | temp[pos++] = bytes[mid++]; 88 | 89 | for (i = 0; i < num; i++) 90 | { 91 | bytes[right] = temp[right]; 92 | right--; 93 | } 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /MSChartParser/IO/Midi/MidIOHelper.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016-2020 Alexander Ong 2 | // See LICENSE in project root for license information. 3 | 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | 8 | namespace MoonscraperChartEditor.Song.IO 9 | { 10 | public static class MidIOHelper 11 | { 12 | public const string EVENTS_TRACK = "EVENTS"; // Sections 13 | public const string GUITAR_TRACK = "PART GUITAR"; 14 | public const string GUITAR_COOP_TRACK = "PART GUITAR COOP"; 15 | public const string BASS_TRACK = "PART BASS"; 16 | public const string RHYTHM_TRACK = "PART RHYTHM"; 17 | public const string KEYS_TRACK = "PART KEYS"; 18 | public const string DRUMS_TRACK = "PART DRUMS"; 19 | public const string GHL_GUITAR_TRACK = "PART GUITAR GHL"; 20 | public const string GHL_BASS_TRACK = "PART BASS GHL"; 21 | public const string VOCALS_TRACK = "PART VOCALS"; 22 | 23 | public const string LYRIC_EVENT_PREFIX = LyricHelper.LYRIC_EVENT_PREFIX; 24 | public const byte SOLO_NOTE = 0x67; // 103, http://docs.c3universe.com/rbndocs/index.php?title=Guitar_and_Bass_Authoring#Solo_Sections 25 | public const byte STARPOWER_NOTE = 0x74; // 116, http://docs.c3universe.com/rbndocs/index.php?title=Overdrive_and_Big_Rock_Endings 26 | 27 | // 120 - 124 http://docs.c3universe.com/rbndocs/index.php?title=Drum_Authoring#Drum_Fills 28 | public const byte STARPOWER_DRUM_FILL_0 = 120; 29 | public const byte STARPOWER_DRUM_FILL_1 = 121; 30 | public const byte STARPOWER_DRUM_FILL_2 = 122; 31 | public const byte STARPOWER_DRUM_FILL_3 = 123; 32 | public const byte STARPOWER_DRUM_FILL_4 = 124; 33 | 34 | public const string SoloEventText = "solo"; 35 | public const string SoloEndEventText = "soloend"; 36 | public const int DOUBLE_KICK_NOTE = 95; 37 | public const byte FLAM_MARKER = 0x6d; // 109 38 | 39 | public const int PhraseMarker = 105; // http://docs.c3universe.com/rbndocs/index.php?title=Vocal_Authoring 40 | public const string PhraseStartText = LyricHelper.PhraseStartText; 41 | public const string PhraseEndText = LyricHelper.PhraseEndText; 42 | 43 | public const string Rb2SectionPrefix = "section "; 44 | public const string Rb3SectionPrefix = "prc_"; 45 | 46 | public const byte VELOCITY = 0x64; // 100 47 | public const byte VELOCITY_ACCENT = 0x7f; // 127, fof/ps 48 | public const byte VELOCITY_GHOST = 0x1; // 1, fof/ps 49 | 50 | // http://docs.c3universe.com/rbndocs/index.php?title=Drum_Authoring 51 | public static readonly Dictionary PAD_TO_CYMBAL_LOOKUP = new Dictionary() 52 | { 53 | { Note.DrumPad.Yellow, 110 }, 54 | { Note.DrumPad.Blue, 111 }, 55 | { Note.DrumPad.Orange, 112 }, 56 | }; 57 | 58 | public static readonly Dictionary CYMBAL_TO_PAD_LOOKUP = PAD_TO_CYMBAL_LOOKUP.ToDictionary((i) => i.Value, (i) => i.Key); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /MSChartParser/Song/Chart.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016-2020 Alexander Ong 2 | // See LICENSE in project root for license information. 3 | 4 | //#define TIMING_DEBUG 5 | 6 | using System.Collections.Generic; 7 | using System.Text.RegularExpressions; 8 | using System.Linq; 9 | 10 | namespace MoonscraperChartEditor.Song 11 | { 12 | public class Chart 13 | { 14 | Song _song; 15 | List _chartObjects; 16 | int _note_count; 17 | GameMode _gameMode; 18 | public string name = string.Empty; 19 | 20 | /// 21 | /// Read only list of notes. 22 | /// 23 | public SongObjectCache notes { get; private set; } 24 | /// 25 | /// Read only list of starpower. 26 | /// 27 | public SongObjectCache starPower { get; private set; } 28 | /// 29 | /// Read only list of local events. 30 | /// 31 | public SongObjectCache events { get; private set; } 32 | /// 33 | /// The song this chart is connected to. 34 | /// 35 | public Song song { get { return _song; } } 36 | /// 37 | /// The game mode the chart is designed for 38 | /// 39 | public GameMode gameMode { get { return _gameMode; } } 40 | 41 | /// 42 | /// Read only list containing all chart notes, starpower and events. 43 | /// 44 | public ReadOnlyList chartObjects; 45 | 46 | /// 47 | /// The total amount of notes in the chart, counting chord (notes sharing the same tick position) as a single note. 48 | /// 49 | public int note_count { get { return _note_count; } } 50 | 51 | /// 52 | /// Creates a new chart object. 53 | /// 54 | /// The song to associate this chart with. 55 | /// The name of the chart (easy single, expert double guitar, etc. 56 | public Chart(Song song, GameMode gameMode, string name = "") 57 | { 58 | _song = song; 59 | _chartObjects = new List(); 60 | chartObjects = new ReadOnlyList(_chartObjects); 61 | _gameMode = gameMode; 62 | 63 | notes = new SongObjectCache();// new Note[0]; 64 | starPower = new SongObjectCache();// new Starpower[0]; 65 | events = new SongObjectCache(); // new ChartEvent[0]; 66 | 67 | _note_count = 0; 68 | 69 | this.name = name; 70 | } 71 | 72 | public Chart(Song song, Song.Instrument instrument, string name = "") : this(song, Song.InstumentToChartGameMode(instrument), name) 73 | { 74 | } 75 | 76 | public Chart(Chart chart, Song song) 77 | { 78 | _song = song; 79 | name = chart.name; 80 | _gameMode = chart.gameMode; 81 | 82 | _chartObjects = new List(); 83 | _chartObjects.AddRange(chart._chartObjects); 84 | 85 | chartObjects = new ReadOnlyList(_chartObjects); 86 | 87 | this.name = chart.name; 88 | } 89 | 90 | /// 91 | /// Updates all read-only values and the total note count. 92 | /// 93 | public void UpdateCache() 94 | { 95 | Song.UpdateCacheList(notes, _chartObjects); 96 | Song.UpdateCacheList(starPower, _chartObjects); 97 | Song.UpdateCacheList(events, _chartObjects); 98 | 99 | //notes = _chartObjects.OfType().ToArray(); 100 | //starPower = _chartObjects.OfType().ToArray(); 101 | //events = _chartObjects.OfType().ToArray(); 102 | 103 | _note_count = GetNoteCount(); 104 | } 105 | 106 | int GetNoteCount() 107 | { 108 | if (notes.Count > 0) 109 | { 110 | int count = 1; 111 | 112 | uint previousPos = notes[0].tick; 113 | for (int i = 1; i < notes.Count; ++i) 114 | { 115 | if (notes[i].tick > previousPos) 116 | { 117 | ++count; 118 | previousPos = notes[i].tick; 119 | } 120 | } 121 | 122 | return count; 123 | } 124 | else 125 | return 0; 126 | } 127 | 128 | public void SetCapacity(int size) 129 | { 130 | if (size > _chartObjects.Capacity) 131 | _chartObjects.Capacity = size; 132 | } 133 | 134 | public void Clear() 135 | { 136 | _chartObjects.Clear(); 137 | } 138 | 139 | /// 140 | /// Adds a series of chart objects (note, starpower and/or chart events) into the chart. 141 | /// 142 | /// Items to add. 143 | public void Add(ChartObject[] chartObjects) 144 | { 145 | foreach (ChartObject chartObject in chartObjects) 146 | { 147 | Add(chartObject, false); 148 | } 149 | 150 | UpdateCache(); 151 | } 152 | 153 | /// 154 | /// Adds a chart object (note, starpower and/or chart event) into the chart. 155 | /// 156 | /// The item to add 157 | /// Automatically update all read-only arrays? 158 | /// If set to false, you must manually call the updateArrays() method, but is useful when adding multiple objects as it increases performance dramatically. 159 | public int Add(ChartObject chartObject, bool update = true) 160 | { 161 | chartObject.chart = this; 162 | chartObject.song = this._song; 163 | 164 | int pos = SongObjectHelper.Insert(chartObject, _chartObjects); 165 | 166 | if (update) 167 | UpdateCache(); 168 | 169 | return pos; 170 | } 171 | 172 | /// 173 | /// Removes a series of chart objects (note, starpower and/or chart events) from the chart. 174 | /// 175 | /// Items to add. 176 | public void Remove(ChartObject[] chartObjects) 177 | { 178 | foreach (ChartObject chartObject in chartObjects) 179 | { 180 | Remove(chartObject, false); 181 | } 182 | 183 | UpdateCache(); 184 | } 185 | 186 | /// 187 | /// Removes a chart object (note, starpower and/or chart event) from the chart. 188 | /// 189 | /// Item to add. 190 | /// Automatically update all read-only arrays? 191 | /// If set to false, you must manually call the updateArrays() method, but is useful when removing multiple objects as it increases performance dramatically. 192 | /// Returns whether the removal was successful or not (item may not have been found if false). 193 | public bool Remove(ChartObject chartObject, bool update = true) 194 | { 195 | bool success = SongObjectHelper.Remove(chartObject, _chartObjects); 196 | 197 | if (success) 198 | { 199 | chartObject.chart = null; 200 | chartObject.song = null; 201 | } 202 | 203 | if (update) 204 | UpdateCache(); 205 | 206 | return success; 207 | } 208 | 209 | public enum GameMode 210 | { 211 | Guitar, 212 | Drums, 213 | GHLGuitar, 214 | 215 | Unrecognised, 216 | } 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /MSChartParser/Song/Events/BPM.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016-2020 Alexander Ong 2 | // See LICENSE in project root for license information. 3 | 4 | namespace MoonscraperChartEditor.Song 5 | { 6 | [System.Serializable] 7 | public class BPM : SyncTrack 8 | { 9 | private readonly ID _classID = ID.BPM; 10 | 11 | public override int classID { get { return (int)_classID; } } 12 | 13 | /// 14 | /// Stored as the bpm value * 1000. For example, a bpm of 120.075 would be stored as 120075. 15 | /// 16 | public uint value; 17 | public float displayValue 18 | { 19 | get 20 | { 21 | return (float)value / 1000.0f; 22 | } 23 | } 24 | 25 | public float? anchor = null; 26 | 27 | /// 28 | /// Basic constructor. 29 | /// 30 | /// Tick position. 31 | /// Stored as the bpm value * 1000 to limit it to 3 decimal places. For example, a bpm of 120.075 would be stored as 120075. 32 | public BPM(uint _position = 0, uint _value = 120000, float? _anchor = null) : base(_position) 33 | { 34 | value = _value; 35 | anchor = _anchor; 36 | } 37 | 38 | public BPM(BPM _bpm) : base(_bpm.tick) 39 | { 40 | value = _bpm.value; 41 | anchor = _bpm.anchor; 42 | } 43 | 44 | public float assignedTime = 0; 45 | 46 | public override SongObject Clone() 47 | { 48 | return new BPM(this); 49 | } 50 | 51 | public override bool AllValuesCompare(T songObject) 52 | { 53 | if (this == songObject && songObject as BPM != null && (songObject as BPM).value == value) 54 | return true; 55 | else 56 | return false; 57 | } 58 | 59 | public void CopyFrom(BPM bpm) 60 | { 61 | tick = bpm.tick; 62 | value = bpm.value; 63 | anchor = bpm.anchor; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /MSChartParser/Song/Events/ChartEvent.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016-2020 Alexander Ong 2 | // See LICENSE in project root for license information. 3 | 4 | namespace MoonscraperChartEditor.Song 5 | { 6 | [System.Serializable] 7 | public class ChartEvent : ChartObject 8 | { 9 | private readonly ID _classID = ID.ChartEvent; 10 | 11 | public override int classID { get { return (int)_classID; } } 12 | 13 | public string eventName { get; private set; } 14 | 15 | public ChartEvent(ChartEvent chartEvent) : base(chartEvent.tick) 16 | { 17 | eventName = chartEvent.eventName; 18 | } 19 | 20 | public ChartEvent(uint _position, string _eventName) : base(_position) 21 | { 22 | eventName = _eventName; 23 | } 24 | 25 | public void CopyFrom(ChartEvent chartEvent) 26 | { 27 | tick = chartEvent.tick; 28 | eventName = chartEvent.eventName; 29 | } 30 | 31 | protected override bool Equals(SongObject b) 32 | { 33 | if (b.GetType() == typeof(ChartEvent)) 34 | { 35 | ChartEvent realB = b as ChartEvent; 36 | if (tick == realB.tick && eventName == realB.eventName) 37 | return true; 38 | else 39 | return false; 40 | } 41 | else 42 | return base.Equals(b); 43 | } 44 | 45 | protected override bool LessThan(SongObject b) 46 | { 47 | if (b.GetType() == typeof(ChartEvent)) 48 | { 49 | ChartEvent realB = b as ChartEvent; 50 | if (tick < b.tick) 51 | return true; 52 | else if (tick == b.tick) 53 | { 54 | if (string.Compare(eventName, realB.eventName) < 0) 55 | return true; 56 | } 57 | 58 | return false; 59 | } 60 | else 61 | return base.LessThan(b); 62 | } 63 | 64 | public override SongObject Clone() 65 | { 66 | return new ChartEvent(this); 67 | } 68 | 69 | public override bool AllValuesCompare(T songObject) 70 | { 71 | if (this == songObject && (songObject as ChartEvent).eventName == eventName) 72 | return true; 73 | else 74 | return false; 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /MSChartParser/Song/Events/ChartObject.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016-2020 Alexander Ong 2 | // See LICENSE in project root for license information. 3 | 4 | namespace MoonscraperChartEditor.Song 5 | { 6 | [System.Serializable] 7 | public abstract class ChartObject : SongObject 8 | { 9 | [System.NonSerialized] 10 | public Chart chart; 11 | 12 | public ChartObject(uint position) : base(position) { } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /MSChartParser/Song/Events/Event.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016-2020 Alexander Ong 2 | // See LICENSE in project root for license information. 3 | 4 | namespace MoonscraperChartEditor.Song 5 | { 6 | [System.Serializable] 7 | public class Event : SongObject 8 | { 9 | private readonly ID _classID = ID.Event; 10 | 11 | public override int classID { get { return (int)_classID; } } 12 | 13 | public string title { get; private set; } 14 | 15 | public Event(string _title, uint _position) : base(_position) 16 | { 17 | title = _title; 18 | } 19 | 20 | public Event(Event songEvent) : base(songEvent.tick) 21 | { 22 | CopyFrom(songEvent); 23 | } 24 | 25 | public void CopyFrom(Event songEvent) 26 | { 27 | tick = songEvent.tick; 28 | title = songEvent.title; 29 | } 30 | 31 | public override SongObject Clone() 32 | { 33 | return new Event(this); 34 | } 35 | 36 | public override bool AllValuesCompare(T songObject) 37 | { 38 | if (this == songObject && (songObject as Event).title == title) 39 | return true; 40 | else 41 | return false; 42 | } 43 | 44 | protected override bool Equals(SongObject b) 45 | { 46 | if (base.Equals(b)) 47 | { 48 | Event realB = b as Event; 49 | return realB != null && tick == realB.tick && title == realB.title; 50 | } 51 | 52 | return false; 53 | } 54 | 55 | protected override bool LessThan(SongObject b) 56 | { 57 | if (this.classID == b.classID) 58 | { 59 | Event realB = b as Event; 60 | if (tick < b.tick) 61 | return true; 62 | else if (tick == b.tick) 63 | { 64 | if (string.Compare(title, realB.title) < 0) 65 | return true; 66 | } 67 | 68 | return false; 69 | } 70 | else 71 | return base.LessThan(b); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /MSChartParser/Song/Events/Section.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016-2020 Alexander Ong 2 | // See LICENSE in project root for license information. 3 | 4 | namespace MoonscraperChartEditor.Song 5 | { 6 | [System.Serializable] 7 | public class Section : Event 8 | { 9 | private readonly ID _classID = ID.Section; 10 | 11 | public override int classID { get { return (int)_classID; } } 12 | 13 | public Section(string _title, uint _position) : base(_title, _position) { } 14 | 15 | public Section(Section section) : base(section.title, section.tick) { } 16 | 17 | public override SongObject Clone() 18 | { 19 | return new Section(this); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /MSChartParser/Song/Events/SongObject.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016-2020 Alexander Ong 2 | // See LICENSE in project root for license information. 3 | 4 | namespace MoonscraperChartEditor.Song 5 | { 6 | [System.Serializable] 7 | public abstract class SongObject 8 | { 9 | /// 10 | /// The song this object is connected to. 11 | /// 12 | [System.NonSerialized] 13 | public Song song; 14 | /// 15 | /// The tick position of the object 16 | /// 17 | public uint tick; 18 | 19 | #if APPLICATION_MOONSCRAPER 20 | /// 21 | /// Unity only. 22 | /// 23 | [System.NonSerialized] 24 | public SongObjectController controller; 25 | #endif 26 | 27 | public abstract int classID { get; } 28 | 29 | public SongObject(uint _tick) 30 | { 31 | tick = _tick; 32 | } 33 | 34 | /// 35 | /// Automatically converts the object's tick position into the time it will appear in the song. 36 | /// 37 | public float time 38 | { 39 | get 40 | { 41 | return song.TickToTime(tick, song.resolution); 42 | } 43 | } 44 | 45 | public abstract SongObject Clone(); 46 | 47 | public T CloneAs() where T : SongObject 48 | { 49 | T clone = this.Clone() as T; 50 | //Debug.Assert(clone != null, "Clone As casting type was incorrect"); 51 | return clone; 52 | } 53 | 54 | public abstract bool AllValuesCompare(T songObject) where T : SongObject; 55 | 56 | public static bool operator ==(SongObject a, SongObject b) 57 | { 58 | bool aIsNull = ReferenceEquals(a, null); 59 | bool bIsNull = ReferenceEquals(b, null); 60 | 61 | if (aIsNull || bIsNull) 62 | { 63 | if (aIsNull == bIsNull) 64 | return true; 65 | else 66 | return false; 67 | } 68 | else 69 | return a.Equals(b); 70 | } 71 | 72 | protected virtual bool Equals(SongObject b) 73 | { 74 | return tick == b.tick && classID == b.classID; 75 | } 76 | 77 | public static bool operator !=(SongObject a, SongObject b) 78 | { 79 | return !(a == b); 80 | } 81 | 82 | protected virtual bool LessThan(SongObject b) 83 | { 84 | if (tick < b.tick) 85 | return true; 86 | else if (tick == b.tick && classID < b.classID) 87 | return true; 88 | else 89 | return false; 90 | } 91 | 92 | public static bool operator <(SongObject a, SongObject b) 93 | { 94 | return a.LessThan(b); 95 | } 96 | 97 | public static bool operator >(SongObject a, SongObject b) 98 | { 99 | if (a != b) 100 | return !(a < b); 101 | else 102 | return false; 103 | } 104 | 105 | public static bool operator <=(SongObject a, SongObject b) 106 | { 107 | return (a < b || a == b); 108 | } 109 | 110 | public static bool operator >=(SongObject a, SongObject b) 111 | { 112 | return (a > b || a == b); 113 | } 114 | 115 | public override bool Equals(System.Object obj) 116 | { 117 | return base.Equals(obj); 118 | } 119 | 120 | public override int GetHashCode() 121 | { 122 | return base.GetHashCode(); 123 | } 124 | 125 | /// 126 | /// Allows different classes to be sorted and grouped together in arrays by giving each class a comparable numeric value that is greater or less than other classes. 127 | /// 128 | public enum ID 129 | { 130 | TimeSignature, BPM, Anchor, Event, Section, Note, Starpower, ChartEvent 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /MSChartParser/Song/Events/Starpower.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016-2020 Alexander Ong 2 | // See LICENSE in project root for license information. 3 | 4 | using System; 5 | 6 | namespace MoonscraperChartEditor.Song 7 | { 8 | [System.Serializable] 9 | public class Starpower : ChartObject 10 | { 11 | [Flags] 12 | public enum Flags 13 | { 14 | None = 0, 15 | 16 | // RB Pro Drums 17 | ProDrums_Activation = 1 << 0, 18 | } 19 | 20 | private readonly ID _classID = ID.Starpower; 21 | 22 | public override int classID { get { return (int)_classID; } } 23 | 24 | public uint length; 25 | public Flags flags = Flags.None; 26 | 27 | public Starpower(uint _position, uint _length, Flags _flags = Flags.None) : base(_position) 28 | { 29 | length = _length; 30 | flags = _flags; 31 | } 32 | 33 | public Starpower(Starpower _starpower) : base(_starpower.tick) 34 | { 35 | length = _starpower.length; 36 | flags = _starpower.flags; 37 | } 38 | 39 | public override SongObject Clone() 40 | { 41 | return new Starpower(this); 42 | } 43 | 44 | public override bool AllValuesCompare(T songObject) 45 | { 46 | if (this == songObject && (songObject as Starpower).length == length && (songObject as Starpower).flags == flags) 47 | return true; 48 | else 49 | return false; 50 | } 51 | 52 | public uint GetCappedLengthForPos(uint pos) 53 | { 54 | uint newLength = length; 55 | if (pos > tick) 56 | newLength = pos - tick; 57 | else 58 | newLength = 0; 59 | 60 | Starpower nextSp = null; 61 | if (song != null && chart != null) 62 | { 63 | int arrayPos = SongObjectHelper.FindClosestPosition(this, chart.starPower); 64 | if (arrayPos == SongObjectHelper.NOTFOUND) 65 | return newLength; 66 | 67 | while (arrayPos < chart.starPower.Count - 1 && chart.starPower[arrayPos].tick <= tick) 68 | { 69 | ++arrayPos; 70 | } 71 | 72 | if (chart.starPower[arrayPos].tick > tick) 73 | nextSp = chart.starPower[arrayPos]; 74 | 75 | if (nextSp != null) 76 | { 77 | // Cap sustain length 78 | if (nextSp.tick < tick) 79 | newLength = 0; 80 | else if (pos > nextSp.tick) 81 | // Cap sustain 82 | newLength = nextSp.tick - tick; 83 | } 84 | // else it's the only starpower or it's the last starpower 85 | } 86 | 87 | return newLength; 88 | } 89 | 90 | public void SetLengthByPos(uint pos) 91 | { 92 | length = GetCappedLengthForPos(pos); 93 | } 94 | 95 | public void CopyFrom(Starpower sp) 96 | { 97 | tick = sp.tick; 98 | length = sp.length; 99 | flags = sp.flags; 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /MSChartParser/Song/Events/SyncTrack.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016-2020 Alexander Ong 2 | // See LICENSE in project root for license information. 3 | 4 | namespace MoonscraperChartEditor.Song 5 | { 6 | [System.Serializable] 7 | public abstract class SyncTrack : SongObject 8 | { 9 | public SyncTrack(uint _position) : base(_position) { } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /MSChartParser/Song/Events/TimeSignature.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016-2020 Alexander Ong 2 | // See LICENSE in project root for license information. 3 | 4 | namespace MoonscraperChartEditor.Song 5 | { 6 | [System.Serializable] 7 | public class TimeSignature : SyncTrack 8 | { 9 | private readonly ID _classID = ID.TimeSignature; 10 | 11 | public override int classID { get { return (int)_classID; } } 12 | 13 | public uint numerator; 14 | public uint denominator; 15 | 16 | public uint quarterNotesPerMeasure { get { return numerator; } } 17 | public uint beatsPerMeasure { get { return denominator; } } 18 | 19 | public TimeSignature(uint _position = 0, uint _numerator = 4, uint _denominator = 4) : base(_position) 20 | { 21 | numerator = _numerator; 22 | denominator = _denominator; 23 | } 24 | 25 | public TimeSignature(TimeSignature ts) : base(ts.tick) 26 | { 27 | numerator = ts.numerator; 28 | denominator = ts.denominator; 29 | } 30 | 31 | public override SongObject Clone() 32 | { 33 | return new TimeSignature(this); 34 | } 35 | 36 | public override bool AllValuesCompare(T songObject) 37 | { 38 | if (this == songObject && songObject as TimeSignature != null && (songObject as TimeSignature).numerator == numerator) 39 | return true; 40 | else 41 | return false; 42 | } 43 | 44 | public void CopyFrom(TimeSignature ts) 45 | { 46 | tick = ts.tick; 47 | numerator = ts.numerator; 48 | denominator = ts.denominator; 49 | } 50 | 51 | public struct BeatInfo 52 | { 53 | public uint tickOffset; 54 | public uint tickGap; 55 | public int repetitions; 56 | public uint repetitionCycleOffset; 57 | } 58 | 59 | public struct MeasureInfo 60 | { 61 | public BeatInfo measureLine; 62 | public BeatInfo beatLine; 63 | public BeatInfo quarterBeatLine; 64 | } 65 | 66 | public MeasureInfo GetMeasureInfo() 67 | { 68 | MeasureInfo measureInfo = new MeasureInfo(); 69 | float resolution = song.resolution; 70 | 71 | { 72 | measureInfo.measureLine.tickOffset = 0; 73 | measureInfo.measureLine.repetitions = 1; 74 | measureInfo.measureLine.tickGap = (uint)(resolution * 4.0f / denominator * numerator); 75 | measureInfo.measureLine.repetitionCycleOffset = 0; 76 | } 77 | 78 | { 79 | measureInfo.beatLine.tickGap = measureInfo.measureLine.tickGap / numerator; 80 | measureInfo.beatLine.tickOffset = measureInfo.beatLine.tickGap; 81 | measureInfo.beatLine.repetitions = (int)numerator - 1; 82 | measureInfo.beatLine.repetitionCycleOffset = measureInfo.beatLine.tickOffset; 83 | } 84 | 85 | { 86 | measureInfo.quarterBeatLine.tickGap = measureInfo.beatLine.tickGap; 87 | measureInfo.quarterBeatLine.tickOffset = measureInfo.beatLine.tickGap / 2; 88 | measureInfo.quarterBeatLine.repetitions = (int)numerator; 89 | measureInfo.quarterBeatLine.repetitionCycleOffset = 0; 90 | } 91 | 92 | return measureInfo; 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /MSChartParser/Song/LyricHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using MoonscraperChartEditor.Song; 3 | 4 | public static class LyricHelper 5 | { 6 | public const string LYRIC_EVENT_PREFIX = "lyric "; 7 | public const string PhraseStartText = "phrase_start"; 8 | public const string PhraseEndText = "phrase_end"; 9 | 10 | public static readonly Dictionary CloneHeroCharSubstitutions = new Dictionary() 11 | { 12 | { "\"", "`" }, 13 | 14 | /* 15 | { "-", "=" }, 16 | { " ", "_" }, 17 | { "#", string.Empty }, 18 | { "^", string.Empty }, 19 | { "/", string.Empty }, 20 | { "+", string.Empty }, 21 | { "%", string.Empty }, 22 | */ 23 | }; 24 | 25 | public static bool IsLyric(this Event e) 26 | { 27 | return e.classID == (int)SongObject.ID.Event && IsLyric(e.title); 28 | } 29 | 30 | public static bool IsLyric(string title) 31 | { 32 | return title.StartsWith(LYRIC_EVENT_PREFIX); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /MSChartParser/Song/Metadata.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016-2020 Alexander Ong 2 | // See LICENSE in project root for license information. 3 | 4 | namespace MoonscraperChartEditor.Song 5 | { 6 | public class Metadata 7 | { 8 | string m_name, m_artist, m_charter, m_player2, m_genre, m_mediatype, m_album; 9 | 10 | public string name { get { return m_name; } set { m_name = MakeValidMetadataString(value); } } 11 | public string artist { get { return m_artist; } set { m_artist = MakeValidMetadataString(value); } } 12 | public string charter { get { return m_charter; } set { m_charter = MakeValidMetadataString(value); } } 13 | public string player2 { get { return m_player2; } set { m_player2 = MakeValidMetadataString(value); } } 14 | public string genre { get { return m_genre; } set { m_genre = MakeValidMetadataString(value); } } 15 | public string mediatype { get { return m_mediatype; } set { m_mediatype = MakeValidMetadataString(value); } } 16 | public string album { get { return m_album; } set { m_album = MakeValidMetadataString(value); } } 17 | public string year; 18 | 19 | public int difficulty; 20 | public float previewStart, previewEnd; 21 | 22 | public Metadata() 23 | { 24 | name = artist = charter = album = year = string.Empty; 25 | player2 = "Bass"; 26 | difficulty = 0; 27 | previewStart = previewEnd = 0; 28 | genre = "rock"; 29 | mediatype = "cd"; 30 | } 31 | 32 | public Metadata(Metadata metaData) 33 | { 34 | name = metaData.name; 35 | artist = metaData.artist; 36 | charter = metaData.charter; 37 | album = metaData.album; 38 | year = metaData.year; 39 | player2 = metaData.player2; 40 | difficulty = metaData.difficulty; 41 | previewStart = metaData.previewStart; 42 | previewEnd = metaData.previewEnd; 43 | genre = metaData.genre; 44 | mediatype = metaData.mediatype; 45 | } 46 | 47 | string MakeValidMetadataString(string v) 48 | { 49 | return v.Replace("\"", string.Empty); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /MSChartParser/Song/SongConfig.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016-2020 Alexander Ong 2 | // See LICENSE in project root for license information. 3 | 4 | namespace MoonscraperChartEditor.Song 5 | { 6 | public static class SongConfig 7 | { 8 | public const float STANDARD_BEAT_RESOLUTION = 192.0f; 9 | public const uint FULL_STEP = 768; 10 | public const float RESOLUTIONS_PER_MEASURE = 4.0f; 11 | public const int FORCED_NOTE_TICK_THRESHOLD = 65; 12 | public const int PRO_DRUMS_LANE_COUNT = 4; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /MSChartParser/Song/TickFunctions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016-2020 Alexander Ong 2 | // See LICENSE in project root for license information. 3 | 4 | using System; // import math lib 5 | 6 | namespace MoonscraperChartEditor.Song 7 | { 8 | public static class TickFunctions 9 | { 10 | public const float SECONDS_PER_MINUTE = 60.0f; 11 | 12 | /// 13 | /// Calculates the amount of time elapsed between 2 tick positions. 14 | /// 15 | /// Initial tick position. 16 | /// Final tick position. 17 | /// Ticks per beat, usually provided from the resolution song of a Song class. 18 | /// The beats per minute value. BPMs provided from a BPM object need to be divded by 1000 as it is stored as the value read from a .chart file. 19 | /// 20 | public static double DisToTime(uint tickStart, uint tickEnd, float resolution, float bpm) 21 | { 22 | return (tickEnd - tickStart) / resolution * SECONDS_PER_MINUTE / bpm; 23 | } 24 | 25 | public static double DisToBpm(uint tickStart, uint tickEnd, double deltatime, double resolution) 26 | { 27 | return (tickEnd - tickStart) / resolution * SECONDS_PER_MINUTE / deltatime; 28 | } 29 | 30 | public static uint TimeToDis(float timeStart, float timeEnd, float resolution, float bpm) 31 | { 32 | return (uint)Math.Round((timeEnd - timeStart) * bpm / SECONDS_PER_MINUTE * resolution); 33 | } 34 | 35 | public static uint TickScaling(uint tick, float originalResolution, float outputResolution) 36 | { 37 | tick = (uint)Math.Round(tick * outputResolution / originalResolution); 38 | return tick; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Program.cs: -------------------------------------------------------------------------------- 1 |  2 | 3 | using System; 4 | using System.Diagnostics; 5 | using System.IO; 6 | using BChart; 7 | using MoonscraperChartEditor.Song; 8 | using MoonscraperChartEditor.Song.IO; 9 | 10 | public static class Program 11 | { 12 | public static Song LoadSong(string chartPath) 13 | { 14 | var ext = Path.GetExtension(chartPath); 15 | Song song = null; 16 | if (ext.Equals(".mid", StringComparison.OrdinalIgnoreCase)) 17 | { 18 | MidReader.CallbackState callBackState = default; 19 | song = MidReader.ReadMidi(chartPath, ref callBackState); 20 | } 21 | else if (ext.Equals(".chart", StringComparison.OrdinalIgnoreCase)) 22 | { 23 | song = ChartReader.ReadChart(chartPath); 24 | } 25 | return song; 26 | } 27 | 28 | public static void Main(string[] args) 29 | { 30 | var chartPath = args[0]; 31 | var outputFolder = Path.GetDirectoryName(chartPath); 32 | var outputFile = Path.Combine(outputFolder, "notes.bch"); 33 | var outputMidi = Path.Combine(outputFolder, "notes.mid"); 34 | var outputChart = Path.Combine(outputFolder, "notes2.chart"); 35 | 36 | Stopwatch sw = new Stopwatch(); 37 | sw.Start(); 38 | Song song = LoadSong(chartPath); 39 | sw.Stop(); 40 | Console.WriteLine($"MSCP: Load took {sw.Elapsed.Milliseconds} ms"); 41 | sw.Restart(); 42 | // MidWriter.WriteToFile(outputMidi, song, song.defaultExportOptions); 43 | // sw.Stop(); 44 | // Console.WriteLine($"midi: Took {sw.Elapsed.Milliseconds} ms"); 45 | // sw.Restart(); 46 | // ChartWriter.ErrorReport errorReport; 47 | // new ChartWriter(outputChart).Write(song, song.defaultExportOptions, out errorReport); 48 | // sw.Stop(); 49 | // Console.WriteLine($"chart: Took {sw.Elapsed.Milliseconds} ms"); 50 | // sw.Restart(); 51 | BChartWriter.WriteToFile(outputFile, song); 52 | sw.Stop(); 53 | Console.WriteLine($"bch: Save took {sw.Elapsed.Milliseconds} ms"); 54 | sw.Restart(); 55 | var newSong = BChartReader.ReadBChart(outputFile); 56 | sw.Stop(); 57 | Console.WriteLine($"bch: Load took {sw.Elapsed.Milliseconds} ms"); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bchart 2 | Experimental binary chart format 3 | 4 | WIP New binary chart file format for plastic instrument games 5 | -------------------------------------------------------------------------------- /bchart.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net6.0-windows 6 | true 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /chartFormat.txt: -------------------------------------------------------------------------------- 1 | bchart format: 2 | 3 | deltaTickEncoding: 4 | if tickDelta > 65535 0xFFFF uint16 max 5 | write byte - 255 6 | write uint32 - deltaTick value 7 | else if tickDelta > 253: 8 | write byte - 254 9 | write uint16 - deltaTick value 10 | else 11 | byte - deltaTick value 12 | 13 | deltaTickEncoding decoding: 14 | byte firstByte = data.ReadByte(ref pos); 15 | switch (firstByte) 16 | case 254: 17 | return data.ReadUInt16LE(ref pos); 18 | case 255: 19 | return data.ReadUInt32LE(ref pos); 20 | default: 21 | return firstByte; 22 | 23 | event format: 24 | [deltaTickEncoding tickPos] [uint8 eventType] [uint8 eventByteLength] [eventData] 25 | event types: 26 | [Tempo 0x01] 27 | [0x01] [uint8 length 4] [uint32 tempo] 28 | [TimeSig 0x2] 29 | [0x02] [uint8 length 2] [byte numerator] [byte denominator] 30 | [TextEvent 0x3] 31 | [0x03] [uint8 length len] [utf8 characters] 32 | [Section 0x4] 33 | [0x04] [uint8 length len] [utf8 characters] 34 | [Phrase 0x5] 35 | [0x5] [uint8 length 2] [byte phraseType] [uint32 tickLength] 36 | [Note 0x06] 37 | [0x6] [uint8 9] [byte noteNumber] [uint32 tickLength] [uint32 modifiersBitMask] 38 | [Note 0x06] 39 | [0x6] [uint8 5] [byte noteNumber] [uint32 tickLength] // No modifiers note event 40 | header: 41 | uint32/byte[4] - BCHF 0x46484342 / 0x42 0x43 0x48 0x46 42 | int32 - byteLength 43 | uint16 - Version number 44 | uint16 - Resolution 45 | uint16 - instrumentCount 46 | 47 | tempoMap: 48 | uint32/byte[4] - SYNC 0x434E5953 / 0x53 0x59 0x4E 0x43 49 | int32 - byteLength 50 | int32 - eventCount 51 | [TimeSig 0x2] 52 | [Tempo 0x01] 53 | 54 | GlobalEvents: 55 | uint32/byte[4] - EVTS 0x53545645 / 0x45 0x56 0x54 0x53 56 | int32 - byteLength 57 | int32 - eventCount 58 | [Section 0x4] 59 | [TextEvent 0x3] 60 | 61 | Instrument: 62 | uint32/byte[4] - INST 0x54534E49 / 0x49 0x4E 0x53 0x54 63 | int32 - byteLength 64 | byte - instrumentID 65 | byte - diffCount 66 | InstrumentDiffEvents 67 | 68 | InstrumentDiffEvents: - Required to come after an instrument chunk 69 | uint32/byte[4] - DIFF 0x46464944 / 0x44 0x49 0x46 0x46 70 | int32 - byteLength 71 | byte - difficultyID 72 | int32 - eventCount 73 | [deltaTickEncoding tickPos] [uint32 eventByteLength] [event data] 74 | 75 | AnimationEvents: Not yet fully defined but reserved for future use! - Required to come after an instrument chunk 76 | uint32/byte[4] - ANIM 77 | int32 - byteLength 78 | int32 - eventCount 79 | [deltaTickEncoding tickPos] [uint32 eventByteLength] [event data] 80 | 81 | Instrument Note IDs: 82 | drums: 83 | Kick: 0x00 84 | Red: 0x01 85 | Yellow: 0x02 86 | Blue: 0x03 87 | Green: 0x04 88 | FiveLaneGreen: 0x05 89 | 90 | 5-fret: 91 | Open: 0x00 92 | Green: 0x01 93 | Red: 0x02 94 | Yellow: 0x03 95 | Blue: 0x04 96 | Orange: 0x05 97 | 98 | 6-fret: 99 | Open: 0x00 100 | B1: 0x01 101 | B2: 0x02 102 | B3: 0x03 103 | W1: 0x04 104 | W2: 0x05 105 | W3: 0x06 106 | 107 | Modifiers: 108 | Guitar: 109 | NOTE_MOD_TOGGLE_FORCED: 1 110 | NOTE_MOD_FORCE_HOPO: 2 111 | NOTE_MOD_FORCE_STRUM: 4 112 | NOTE_MOD_TAP: 8 113 | 114 | SixFretGuitar: 115 | NOTE_MOD_TOGGLE_FORCED: 1 116 | NOTE_MOD_FORCE_HOPO: 2 117 | NOTE_MOD_FORCE_STRUM: 4 118 | NOTE_MOD_TAP: 8 119 | 120 | Drum: 121 | NOTE_MOD_ACCENT: 1 122 | NOTE_MOD_GHOST: 2 123 | NOTE_MOD_CYMBAL: 4 124 | NOTE_MOD_KICK_2: 8 125 | 126 | Difficulties: 127 | EASY: 0x00 128 | MEDIUM: 0x01 129 | HARD: 0x02 130 | EXPERT: 0x03 131 | 132 | Phrases: 133 | STARPOWER: 0x01 134 | SOLO: 0x02 135 | LYRICS_LINE: 0x03 136 | 137 | Events: 138 | TEMPO: 0x01 139 | TIME_SIG: 0x02 140 | TEXT: 0x03 141 | SECTION: 0x04 142 | PHRASE: 0x05 143 | NOTE: 0x06 144 | 145 | Instruments: 146 | GUITAR: 0 147 | GUITAR_SIX: 1 148 | BASS: 2 149 | BASS_SIX: 3 150 | RHYTHM: 4 151 | COOP: 5 152 | KEYS: 6 153 | DRUMS: 7 154 | VOCALS: 8 155 | 156 | Other considerations: 157 | Animation data, venue, characters, camera 158 | Modchart events 159 | 160 | TODO - Add major and minor version number minor version number is non-breaking changes, major is breaking changes -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | 2 | BSD 3-Clause License 3 | 4 | Copyright (c) 2019-2022, Matthew Sitton 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without modification, 8 | are permitted provided that the following conditions are met: 9 | 10 | 1. Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 13 | 2. Redistributions in binary form must reproduce the above copyright notice, 14 | this list of conditions and the following disclaimer in the documentation and/or 15 | other materials provided with the distribution. 16 | 17 | 3. Neither the name of the copyright holder nor the names of its contributors 18 | may be used to endorse or promote products derived from this software without 19 | specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 22 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 25 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 28 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | --------------------------------------------------------------------------------- 33 | Moonscraper code license: 34 | 35 | BSD 3-Clause License 36 | 37 | Copyright (c) 2016-2021, Alexander Ong 38 | All rights reserved. 39 | 40 | Redistribution and use in source and binary forms, with or without modification, 41 | are permitted provided that the following conditions are met: 42 | 43 | 1. Redistributions of source code must retain the above copyright notice, this 44 | list of conditions and the following disclaimer. 45 | 46 | 2. Redistributions in binary form must reproduce the above copyright notice, 47 | this list of conditions and the following disclaimer in the documentation and/or 48 | other materials provided with the distribution. 49 | 50 | 3. Neither the name of the copyright holder nor the names of its contributors 51 | may be used to endorse or promote products derived from this software without 52 | specific prior written permission. 53 | 54 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 55 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 56 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 57 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 58 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 59 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 60 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 61 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 62 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 63 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------------