diff --git a/.gitmodules b/.gitmodules
index 479b021..3dc70c7 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,9 @@
[submodule "framework-dotnet"]
path = framework-dotnet
url = git@gitea.azcona-becker.net:alcetech/framework-dotnet
+[submodule "desktop-framework-dotnet"]
+ path = desktop-framework-dotnet
+ url = git@gitea.azcona-becker.net:alcetech/desktop-framework-dotnet
+[submodule "web-framework-dotnet"]
+ path = web-framework-dotnet
+ url = git@gitea.azcona-becker.net:alcetech/web-framework-dotnet
diff --git a/desktop-framework-dotnet b/desktop-framework-dotnet
new file mode 160000
index 0000000..57316e3
--- /dev/null
+++ b/desktop-framework-dotnet
@@ -0,0 +1 @@
+Subproject commit 57316e3557c1c3a566d1a24bd60675c02d1f8744
diff --git a/editor-dotnet.sln b/editor-dotnet.sln
index 499bead..a0cc482 100644
--- a/editor-dotnet.sln
+++ b/editor-dotnet.sln
@@ -14,8 +14,6 @@ EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MBS.Editor.UserInterface", "editor-dotnet\src\lib\MBS.Editor.UserInterface\MBS.Editor.UserInterface.csproj", "{C4316562-555A-4A79-9D71-15737976DF8B}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "app", "app", "{4ED8C38B-47EF-4368-9965-CF627465B45A}"
- ProjectSection(SolutionItems) = preProject
- EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MBS.Editor", "editor-dotnet\src\app\MBS.Editor\MBS.Editor.csproj", "{A936C411-0184-43F8-A343-0DE8C3B7B42E}"
EndProject
@@ -27,8 +25,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{BDC147D8-4D9
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "lib", "lib", "{80A728D5-7C00-4B59-A37E-321C54CC554F}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MBS.Desktop", "framework-dotnet\framework-dotnet\src\lib\MBS.Desktop\MBS.Desktop.csproj", "{4F2B8AF8-E1A4-4114-B4DA-4789A3A21143}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MBS.Core", "framework-dotnet\framework-dotnet\src\lib\MBS.Core\MBS.Core.csproj", "{7565CFB4-9761-4064-B18F-5E2644730BC0}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "plugins", "plugins", "{451AD529-16B4-4049-9D0C-0C79B3DDFA52}"
@@ -45,6 +41,46 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MBS.Editor.Core.Tests", "ed
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MBS.Editor.Plugins.CRI.Tests", "editor-dotnet\src\tests\MBS.Editor.Plugins.CRI.Tests\MBS.Editor.Plugins.CRI.Tests.csproj", "{2747FFC9-55AA-4A76-B0E9-D8A839E94E47}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "desktop-framework-dotnet", "desktop-framework-dotnet", "{1BEBE6E9-D723-4A76-8210-D470DEB8C9C7}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "desktop-framework-dotnet", "desktop-framework-dotnet", "{68FC9B7D-A168-47CE-9106-A2501F5E4814}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{9C1544A0-92FF-46F3-A8F3-93A6EB599EDD}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "lib", "lib", "{418A36B7-1856-4945-AB9D-D66956A6A549}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MBS.Desktop", "desktop-framework-dotnet\desktop-framework-dotnet\src\lib\MBS.Desktop\MBS.Desktop.csproj", "{11862DE3-B214-42C5-9CFE-72FFE7F29F09}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "engines", "engines", "{C91C4319-D6B5-448C-BC28-3F5F65EC4EB7}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MBS.Desktop.Engines.GTK3", "desktop-framework-dotnet\desktop-framework-dotnet\src\engines\MBS.Desktop.Engines.GTK3\MBS.Desktop.Engines.GTK3.csproj", "{A738CB41-831D-4BB3-A7F5-87DB19FAD87B}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MBS.Editor.Plugins.Mekada", "editor-dotnet\src\plugins\MBS.Editor.Plugins.Mekada\MBS.Editor.Plugins.Mekada.csproj", "{9D1F7A94-B165-4581-B68D-4E2CF9F83D30}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MBS.Editor.Plugins.Mekada.Tests", "editor-dotnet\src\tests\MBS.Editor.Plugins.Mekada.Tests\MBS.Editor.Plugins.Mekada.Tests.csproj", "{55865BF2-E3C3-4823-A295-C85F6D98901F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MBS.Editor.Testing", "editor-dotnet\src\lib\MBS.Editor.Testing\MBS.Editor.Testing.csproj", "{93A3D49C-A6EC-4F19-A410-F222DE1C9358}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MBS.Editor.Plugins.Kronosaur", "editor-dotnet\src\plugins\MBS.Editor.Plugins.Kronosaur\MBS.Editor.Plugins.Kronosaur.csproj", "{5D627852-158F-460D-9E5F-56F80BDB9A0B}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{D5423E32-5CD5-4207-8B56-235D470E7BBB}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MBS.Core.Tests", "framework-dotnet\framework-dotnet\src\tests\MBS.Core.Tests\MBS.Core.Tests.csproj", "{13F3D325-9EB8-4479-BB3F-1D663F924A8D}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MBS.Editor.Plugins.Multimedia.Tests", "editor-dotnet\src\tests\MBS.Editor.Plugins.Multimedia.Tests\MBS.Editor.Plugins.Multimedia.Tests.csproj", "{BDE1CD42-771E-4902-93DB-5B17C616C172}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MBS.Editor.Web.Server", "editor-dotnet\src\app\MBS.Editor.Web.Server\MBS.Editor.Web.Server.csproj", "{A74A8939-9E5A-4811-9D03-7B53E164A979}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "web-framework-dotnet", "web-framework-dotnet", "{B24A3EC9-CAC1-4162-A71F-4E085E828DAD}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{5A57B76D-C7E1-48A4-AB10-23E804590287}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "lib", "lib", "{91E8E5EA-8098-4929-9E87-6C2D55F35A02}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MBS.Web", "web-framework-dotnet\src\lib\MBS.Web\MBS.Web.csproj", "{09CD7984-6CB9-42BC-9A9E-31FA1C21697F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MBS.Editor.Web", "editor-dotnet\src\lib\MBS.Editor.Web\MBS.Editor.Web.csproj", "{C40F7294-C2FD-4642-A35E-6AEE7DE9F2A1}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -63,10 +99,6 @@ Global
{A936C411-0184-43F8-A343-0DE8C3B7B42E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A936C411-0184-43F8-A343-0DE8C3B7B42E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A936C411-0184-43F8-A343-0DE8C3B7B42E}.Release|Any CPU.Build.0 = Release|Any CPU
- {4F2B8AF8-E1A4-4114-B4DA-4789A3A21143}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {4F2B8AF8-E1A4-4114-B4DA-4789A3A21143}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {4F2B8AF8-E1A4-4114-B4DA-4789A3A21143}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {4F2B8AF8-E1A4-4114-B4DA-4789A3A21143}.Release|Any CPU.Build.0 = Release|Any CPU
{7565CFB4-9761-4064-B18F-5E2644730BC0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7565CFB4-9761-4064-B18F-5E2644730BC0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7565CFB4-9761-4064-B18F-5E2644730BC0}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -91,13 +123,55 @@ Global
{2747FFC9-55AA-4A76-B0E9-D8A839E94E47}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2747FFC9-55AA-4A76-B0E9-D8A839E94E47}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2747FFC9-55AA-4A76-B0E9-D8A839E94E47}.Release|Any CPU.Build.0 = Release|Any CPU
+ {11862DE3-B214-42C5-9CFE-72FFE7F29F09}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {11862DE3-B214-42C5-9CFE-72FFE7F29F09}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {11862DE3-B214-42C5-9CFE-72FFE7F29F09}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {11862DE3-B214-42C5-9CFE-72FFE7F29F09}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A738CB41-831D-4BB3-A7F5-87DB19FAD87B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A738CB41-831D-4BB3-A7F5-87DB19FAD87B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A738CB41-831D-4BB3-A7F5-87DB19FAD87B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A738CB41-831D-4BB3-A7F5-87DB19FAD87B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9D1F7A94-B165-4581-B68D-4E2CF9F83D30}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9D1F7A94-B165-4581-B68D-4E2CF9F83D30}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9D1F7A94-B165-4581-B68D-4E2CF9F83D30}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9D1F7A94-B165-4581-B68D-4E2CF9F83D30}.Release|Any CPU.Build.0 = Release|Any CPU
+ {55865BF2-E3C3-4823-A295-C85F6D98901F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {55865BF2-E3C3-4823-A295-C85F6D98901F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {55865BF2-E3C3-4823-A295-C85F6D98901F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {55865BF2-E3C3-4823-A295-C85F6D98901F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {93A3D49C-A6EC-4F19-A410-F222DE1C9358}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {93A3D49C-A6EC-4F19-A410-F222DE1C9358}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {93A3D49C-A6EC-4F19-A410-F222DE1C9358}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {93A3D49C-A6EC-4F19-A410-F222DE1C9358}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5D627852-158F-460D-9E5F-56F80BDB9A0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5D627852-158F-460D-9E5F-56F80BDB9A0B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5D627852-158F-460D-9E5F-56F80BDB9A0B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5D627852-158F-460D-9E5F-56F80BDB9A0B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {13F3D325-9EB8-4479-BB3F-1D663F924A8D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {13F3D325-9EB8-4479-BB3F-1D663F924A8D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {13F3D325-9EB8-4479-BB3F-1D663F924A8D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {13F3D325-9EB8-4479-BB3F-1D663F924A8D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {BDE1CD42-771E-4902-93DB-5B17C616C172}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BDE1CD42-771E-4902-93DB-5B17C616C172}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {BDE1CD42-771E-4902-93DB-5B17C616C172}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BDE1CD42-771E-4902-93DB-5B17C616C172}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A74A8939-9E5A-4811-9D03-7B53E164A979}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A74A8939-9E5A-4811-9D03-7B53E164A979}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A74A8939-9E5A-4811-9D03-7B53E164A979}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A74A8939-9E5A-4811-9D03-7B53E164A979}.Release|Any CPU.Build.0 = Release|Any CPU
+ {09CD7984-6CB9-42BC-9A9E-31FA1C21697F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {09CD7984-6CB9-42BC-9A9E-31FA1C21697F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {09CD7984-6CB9-42BC-9A9E-31FA1C21697F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {09CD7984-6CB9-42BC-9A9E-31FA1C21697F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C40F7294-C2FD-4642-A35E-6AEE7DE9F2A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C40F7294-C2FD-4642-A35E-6AEE7DE9F2A1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C40F7294-C2FD-4642-A35E-6AEE7DE9F2A1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C40F7294-C2FD-4642-A35E-6AEE7DE9F2A1}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
- {74AD8C3F-B0B8-472F-A847-1FFFB1667B34} = {F05001E1-6FFB-48AE-BF7F-7F39A24D3B70}
- {451AD529-16B4-4049-9D0C-0C79B3DDFA52} = {F05001E1-6FFB-48AE-BF7F-7F39A24D3B70}
{F05001E1-6FFB-48AE-BF7F-7F39A24D3B70} = {75210F45-D690-4A61-9CD8-96B09E5DAAC5}
{C86F60F9-BBC1-4554-A3B0-D553F9C157A8} = {F05001E1-6FFB-48AE-BF7F-7F39A24D3B70}
{8FFB417A-2CDC-429F-ABE0-19B3015530D3} = {C86F60F9-BBC1-4554-A3B0-D553F9C157A8}
@@ -107,13 +181,32 @@ Global
{B9747AFE-160D-4807-B989-B3F0ACCA3634} = {CC86007D-8193-4EAA-932D-A96B5F09847E}
{BDC147D8-4D97-4663-9408-BC822E1E0B3C} = {B9747AFE-160D-4807-B989-B3F0ACCA3634}
{80A728D5-7C00-4B59-A37E-321C54CC554F} = {BDC147D8-4D97-4663-9408-BC822E1E0B3C}
- {4F2B8AF8-E1A4-4114-B4DA-4789A3A21143} = {80A728D5-7C00-4B59-A37E-321C54CC554F}
{7565CFB4-9761-4064-B18F-5E2644730BC0} = {80A728D5-7C00-4B59-A37E-321C54CC554F}
+ {451AD529-16B4-4049-9D0C-0C79B3DDFA52} = {F05001E1-6FFB-48AE-BF7F-7F39A24D3B70}
{5978938E-19F6-42AE-B588-7719A65ABCA7} = {451AD529-16B4-4049-9D0C-0C79B3DDFA52}
{CDA151F8-5BA7-47DB-883D-CBC2DD94F0DF} = {4ED8C38B-47EF-4368-9965-CF627465B45A}
{78B11A3E-1371-48D8-9B8E-AE6ED2380A50} = {451AD529-16B4-4049-9D0C-0C79B3DDFA52}
+ {74AD8C3F-B0B8-472F-A847-1FFFB1667B34} = {F05001E1-6FFB-48AE-BF7F-7F39A24D3B70}
{7A349FC6-BCE7-465D-ADBC-7A21242E2C78} = {74AD8C3F-B0B8-472F-A847-1FFFB1667B34}
{2747FFC9-55AA-4A76-B0E9-D8A839E94E47} = {74AD8C3F-B0B8-472F-A847-1FFFB1667B34}
+ {68FC9B7D-A168-47CE-9106-A2501F5E4814} = {1BEBE6E9-D723-4A76-8210-D470DEB8C9C7}
+ {9C1544A0-92FF-46F3-A8F3-93A6EB599EDD} = {68FC9B7D-A168-47CE-9106-A2501F5E4814}
+ {418A36B7-1856-4945-AB9D-D66956A6A549} = {9C1544A0-92FF-46F3-A8F3-93A6EB599EDD}
+ {11862DE3-B214-42C5-9CFE-72FFE7F29F09} = {418A36B7-1856-4945-AB9D-D66956A6A549}
+ {C91C4319-D6B5-448C-BC28-3F5F65EC4EB7} = {9C1544A0-92FF-46F3-A8F3-93A6EB599EDD}
+ {A738CB41-831D-4BB3-A7F5-87DB19FAD87B} = {C91C4319-D6B5-448C-BC28-3F5F65EC4EB7}
+ {9D1F7A94-B165-4581-B68D-4E2CF9F83D30} = {451AD529-16B4-4049-9D0C-0C79B3DDFA52}
+ {55865BF2-E3C3-4823-A295-C85F6D98901F} = {74AD8C3F-B0B8-472F-A847-1FFFB1667B34}
+ {93A3D49C-A6EC-4F19-A410-F222DE1C9358} = {C86F60F9-BBC1-4554-A3B0-D553F9C157A8}
+ {5D627852-158F-460D-9E5F-56F80BDB9A0B} = {451AD529-16B4-4049-9D0C-0C79B3DDFA52}
+ {D5423E32-5CD5-4207-8B56-235D470E7BBB} = {BDC147D8-4D97-4663-9408-BC822E1E0B3C}
+ {13F3D325-9EB8-4479-BB3F-1D663F924A8D} = {D5423E32-5CD5-4207-8B56-235D470E7BBB}
+ {BDE1CD42-771E-4902-93DB-5B17C616C172} = {74AD8C3F-B0B8-472F-A847-1FFFB1667B34}
+ {A74A8939-9E5A-4811-9D03-7B53E164A979} = {4ED8C38B-47EF-4368-9965-CF627465B45A}
+ {5A57B76D-C7E1-48A4-AB10-23E804590287} = {B24A3EC9-CAC1-4162-A71F-4E085E828DAD}
+ {91E8E5EA-8098-4929-9E87-6C2D55F35A02} = {5A57B76D-C7E1-48A4-AB10-23E804590287}
+ {09CD7984-6CB9-42BC-9A9E-31FA1C21697F} = {91E8E5EA-8098-4929-9E87-6C2D55F35A02}
+ {C40F7294-C2FD-4642-A35E-6AEE7DE9F2A1} = {C86F60F9-BBC1-4554-A3B0-D553F9C157A8}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {4D0B64EB-14E9-4013-AA33-33716704909B}
diff --git a/editor-dotnet/src/app/MBS.Editor.Web.Server/MBS.Editor.Web.Server.csproj b/editor-dotnet/src/app/MBS.Editor.Web.Server/MBS.Editor.Web.Server.csproj
new file mode 100644
index 0000000..7ebf669
--- /dev/null
+++ b/editor-dotnet/src/app/MBS.Editor.Web.Server/MBS.Editor.Web.Server.csproj
@@ -0,0 +1,15 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
diff --git a/editor-dotnet/src/app/MBS.Editor.Web.Server/Program.cs b/editor-dotnet/src/app/MBS.Editor.Web.Server/Program.cs
new file mode 100644
index 0000000..6154355
--- /dev/null
+++ b/editor-dotnet/src/app/MBS.Editor.Web.Server/Program.cs
@@ -0,0 +1,102 @@
+using System.Text.Json.Nodes;
+using System.Xml;
+using MBS.Core;
+using MBS.Editor.Web.Pages;
+using MBS.Web;
+
+public class Program : WebApplication
+{
+ protected override int DefaultPort => 54920;
+
+ public Program()
+ {
+ Commands.Add(new Command("File", "_File", new CommandItem[]
+ {
+ new CommandReferenceCommandItem("FileOpen")
+ }));
+ Commands.Add(new Command("FileOpen", "_Open"));
+ }
+
+ protected override WebServer CreateWebServer()
+ {
+ WebServer ws = base.CreateWebServer();
+ ws.Routes.Add(new WebRoute("/", new WebHandler(delegate(WebContext ctx)
+ {
+ ctx.Response.ContentType = "application/xhtml+xml";
+
+ MainPage page = new MainPage();
+ page.Title = "MBS Editor";
+
+ MBS.Web.UI.WebControls.CommandBar toolbar = new MBS.Web.UI.WebControls.CommandBar();
+ toolbar.Items.Add(new CommandReferenceCommandItem("FileOpen"));
+ page.Controls.Add(toolbar);
+
+ XmlWriter sw = XmlWriter.Create(ctx.Response.Stream);
+ page.Render(sw);
+ sw.Close();
+ })));
+ ws.Routes.Add(new WebRoute("/api/local/browse", new WebHandler(delegate(WebContext ctx)
+ {
+ StreamWriter sw = new StreamWriter(ctx.Response.Stream);
+
+ string relativePath = "";
+ if (ctx.Request.Query.ContainsKey("path"))
+ {
+ List oPath = ctx.Request.Query["path"];
+ if (oPath.Count > 0)
+ {
+ relativePath = oPath[0];
+ }
+ }
+
+ JsonObject json = new JsonObject();
+
+ string homeDirectory = "/home/beckermj/Documents";
+ string absolutePath = String.Join("/", new string[] { homeDirectory, relativePath });
+ if (!System.IO.Directory.Exists(absolutePath))
+ {
+ json.Add("result", "failure");
+ json.Add("message", "path not found");
+
+ sw.Write(json.ToJsonString());
+ sw.Close();
+ return;
+ }
+
+ json.Add("result", "success");
+
+ JsonArray ary = new JsonArray();
+ string[] files = System.IO.Directory.GetFiles(absolutePath, "*", SearchOption.TopDirectoryOnly);
+ string[] folders = System.IO.Directory.GetDirectories(absolutePath, "*", SearchOption.TopDirectoryOnly);
+ foreach (string file in folders)
+ {
+ JsonObject o = new JsonObject();
+ string title = System.IO.Path.GetFileName(file);
+ o.Add("title", title);
+ o.Add("type", "folder");
+ o.Add("path", file);
+ ary.Add(o);
+ }
+ foreach (string file in files)
+ {
+ JsonObject o = new JsonObject();
+ string title = System.IO.Path.GetFileName(file);
+ o.Add("title", title);
+ o.Add("type", "file");
+ o.Add("path", file);
+ ary.Add(o);
+ }
+
+ json.Add("items", ary);
+
+ sw.Write(json.ToJsonString());
+ sw.Close();
+ })));
+ return ws;
+ }
+
+ public static void Main(string[] args)
+ {
+ (new Program()).Start();
+ }
+}
\ No newline at end of file
diff --git a/editor-dotnet/src/app/MBS.Editor.Web.Server/TODO.txt b/editor-dotnet/src/app/MBS.Editor.Web.Server/TODO.txt
new file mode 100644
index 0000000..5232bbe
--- /dev/null
+++ b/editor-dotnet/src/app/MBS.Editor.Web.Server/TODO.txt
@@ -0,0 +1,12 @@
+This is MBS Editor Web Server.
+
+example URL:
+
+Edit existing registered Document:
+https://localhost:54920/doc/61b30ad3c7754ae2bc7abf949ee83289/edit
+
+Create document and register it:
+https://localhost:54920/doc/new
+
+- redirects to /doc/.../edit when document is created
+
diff --git a/editor-dotnet/src/app/MBS.Editor/MBS.Editor.csproj b/editor-dotnet/src/app/MBS.Editor/MBS.Editor.csproj
index c84f015..76d4962 100644
--- a/editor-dotnet/src/app/MBS.Editor/MBS.Editor.csproj
+++ b/editor-dotnet/src/app/MBS.Editor/MBS.Editor.csproj
@@ -3,8 +3,8 @@
-
+
diff --git a/editor-dotnet/src/install-engines.sh b/editor-dotnet/src/install-engines.sh
new file mode 100755
index 0000000..9617e51
--- /dev/null
+++ b/editor-dotnet/src/install-engines.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+CONFIDENCE=Debug
+NET_VERSION=net8.0
+
+if [ ! -d app/MBS.Editor/bin/$CONFIDENCE/$NET_VERSION/engines ]; then
+
+ mkdir app/MBS.Editor/bin/$CONFIDENCE/$NET_VERSION/engines
+
+fi
+
+for dir in ../../desktop-framework-dotnet/desktop-framework-dotnet/src/engines/* ; do
+
+ echo "Building $dir"
+
+ pushd $dir
+ dotnet build
+ popd
+
+ echo "Copying $dir"
+ cp $dir/bin/$CONFIDENCE/$NET_VERSION/*.dll app/MBS.Editor/bin/$CONFIDENCE/$NET_VERSION/engines
+
+done
diff --git a/editor-dotnet/src/lib/MBS.Editor.Core/DataFormat.cs b/editor-dotnet/src/lib/MBS.Editor.Core/DataFormat.cs
index 566e0ea..24b2df8 100644
--- a/editor-dotnet/src/lib/MBS.Editor.Core/DataFormat.cs
+++ b/editor-dotnet/src/lib/MBS.Editor.Core/DataFormat.cs
@@ -1,22 +1,73 @@
+//
+// DataFormat.cs - translates ObjectModel to serialized data in a particular format
+//
+// Author:
+// Michael Becker
+//
+// Copyright (c) 2011-2024 Mike Becker's Software
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
namespace MBS.Editor.Core;
public abstract class DataFormat
{
+ protected virtual void BeforeLoadInternal(Stack objectModels)
+ {
+ }
+ protected virtual void AfterLoadInternal(Stack objectModels)
+ {
+ }
+
public void Load(ObjectModel objectModel, Stream stream)
{
- LoadInternal(objectModel, stream);
+ Stack stack = new Stack();
+ stack.Push(objectModel);
+ BeforeLoadInternal(stack);
+
+ ObjectModel omb = stack.Pop();
+
+ LoadInternal(omb, stream);
+
+ stack.Push(omb);
+ AfterLoadInternal(stack);
}
protected abstract void LoadInternal(ObjectModel objectModel, Stream stream);
+ protected virtual void BeforeSaveInternal(Stack objectModels)
+ {
+ }
+ protected virtual void AfterSaveInternal(Stack objectModels)
+ {
+ }
+
public void Save(ObjectModel objectModel, Stream stream)
{
- SaveInternal(objectModel, stream);
+ Stack stack = new Stack();
+ stack.Push(objectModel);
+ BeforeSaveInternal(stack);
+
+ ObjectModel omb = stack.Pop();
+
+ SaveInternal(omb, stream);
+
+ stack.Push(omb);
+ AfterSaveInternal(stack);
}
protected abstract void SaveInternal(ObjectModel objectModel, Stream stream);
-
-
public static T FromType() where T : DataFormat, new()
{
T objectModel = new T();
diff --git a/editor-dotnet/src/lib/MBS.Editor.Core/DataFormats/Chunked/ChunkedDataFormat.cs b/editor-dotnet/src/lib/MBS.Editor.Core/DataFormats/Chunked/ChunkedDataFormat.cs
new file mode 100644
index 0000000..281cf7b
--- /dev/null
+++ b/editor-dotnet/src/lib/MBS.Editor.Core/DataFormats/Chunked/ChunkedDataFormat.cs
@@ -0,0 +1,142 @@
+// Copyright (C) 2024 Michael Becker
+//
+// This file is part of editor-dotnet.
+//
+// editor-dotnet is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// editor-dotnet is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with editor-dotnet. If not, see .
+
+using MBS.Core;
+using MBS.Editor.Core.IO;
+using MBS.Editor.Core.ObjectModels.Chunked;
+using MBS.Editor.Core.ObjectModels.FileSystem.FileSources;
+
+namespace MBS.Editor.Core.DataFormats.FileSystem.Chunked;
+
+public class ChunkedDataFormat : DataFormat
+{
+ protected virtual int KeyLength { get; } = 4;
+
+ ///
+ /// Gets or sets the endianness of multibyte values. For IFF and AIFF, set to
+ /// . For RIFF, set to .
+ ///
+ ///
+ public Endianness Endianness { get; set; } = Endianness.LittleEndian;
+
+ protected virtual int ChunkAlignment { get; } = 0;
+ protected virtual bool IsGroupChunk(string name)
+ {
+ return false;
+ }
+
+ private void ReadChunk(Reader reader, IChunkContainer parent)
+ {
+ string chunkName = reader.ReadFixedLengthString(4);
+ int chunkLength = reader.ReadInt32();
+
+ if (IsGroupChunk(chunkName))
+ {
+ string typeName = reader.ReadFixedLengthString(4);
+
+ // is group chunk
+ GroupChunk folder = new GroupChunk(chunkName, typeName);
+
+ while (!reader.BaseStream.EndOfStream())
+ {
+ ReadChunk(reader, folder);
+ }
+
+ parent.Chunks.Add(folder);
+ }
+ else
+ {
+ long offset = reader.BaseStream.Position;
+
+ DataChunk file = new DataChunk(chunkName);
+ file.Source = new EmbeddedFileSource(reader.BaseStream, offset, chunkLength);
+ parent.Chunks.Add(file);
+
+ // CMAP=color map, 768 bytes, 3x256 colors, rgb
+
+ reader.BaseStream.Seek(chunkLength, SeekOrigin.Current);
+ reader.Align(ChunkAlignment);
+ }
+ }
+
+ protected override void LoadInternal(ObjectModel objectModel, Stream stream)
+ {
+ ChunkedObjectModel chunked = ObjectModel.CastOrThrow(objectModel);
+
+ Reader reader = new Reader(stream);
+ reader.Endianness = Endianness;
+ while (!reader.BaseStream.EndOfStream())
+ {
+ ReadChunk(reader, chunked);
+ }
+ }
+ protected override void SaveInternal(ObjectModel objectModel, Stream stream)
+ {
+ ChunkedObjectModel chunked = ObjectModel.CastOrThrow(objectModel);
+
+ Writer writer = new Writer(stream);
+ writer.Endianness = Endianness;
+ foreach (Chunk chunk in chunked.Chunks)
+ {
+ WriteChunk(writer, chunk);
+ }
+ }
+
+ private void WriteChunk(Writer writer, Chunk chunk)
+ {
+ writer.WriteFixedLengthString(chunk.Name, KeyLength);
+ writer.WriteInt32(CalculateChunkSize(chunk));
+
+ if (chunk is GroupChunk g)
+ {
+ writer.WriteFixedLengthString(g.TypeName, KeyLength);
+ foreach (Chunk chunk2 in g.Chunks)
+ {
+ WriteChunk(writer, chunk2);
+ }
+ }
+ else if (chunk is DataChunk d)
+ {
+ if (d.Source != null)
+ {
+ writer.WriteBytes(d.Source.GetData());
+ }
+ }
+ }
+
+ private int CalculateChunkSize(Chunk chunk)
+ {
+ int sz = 0;
+ if (chunk is GroupChunk g)
+ {
+ sz += 4;
+ foreach (Chunk c in g.Chunks)
+ {
+ sz += CalculateChunkSize(c);
+ }
+ }
+ else if (chunk is DataChunk d)
+ {
+ sz = 0;
+ if (d.Source != null)
+ {
+ sz = (int)d.Source.Length;
+ }
+ }
+ return sz;
+ }
+}
diff --git a/editor-dotnet/src/lib/MBS.Editor.Core/DataFormats/FileSystem/IFF/IFFDataFormat.cs b/editor-dotnet/src/lib/MBS.Editor.Core/DataFormats/FileSystem/IFF/IFFDataFormat.cs
new file mode 100644
index 0000000..05a249a
--- /dev/null
+++ b/editor-dotnet/src/lib/MBS.Editor.Core/DataFormats/FileSystem/IFF/IFFDataFormat.cs
@@ -0,0 +1,45 @@
+// Copyright (C) 2024 Michael Becker
+//
+// This file is part of editor-dotnet.
+//
+// editor-dotnet is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// editor-dotnet is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with editor-dotnet. If not, see .
+
+
+using MBS.Core;
+using MBS.Editor.Core.DataFormats.FileSystem.Chunked;
+using MBS.Editor.Core.IO;
+using MBS.Editor.Core.ObjectModels.Chunked;
+using MBS.Editor.Core.ObjectModels.FileSystem;
+using MBS.Editor.Core.ObjectModels.FileSystem.FileSources;
+
+namespace MBS.Editor.Core.DataFormats.FileSystem.IFF;
+
+public class IFFDataFormat : ChunkedDataFormat
+{
+ public List GroupChunkNames { get; } = new List();
+ public IFFDataFormat()
+ {
+ Endianness = Endianness.BigEndian;
+ GroupChunkNames.Add("FORM");
+ GroupChunkNames.Add("LIST");
+ GroupChunkNames.Add("CAT");
+ }
+
+ protected override bool IsGroupChunk(string name)
+ {
+ return GroupChunkNames.Contains(name);
+ }
+
+ protected override int ChunkAlignment => 2;
+}
\ No newline at end of file
diff --git a/editor-dotnet/src/lib/MBS.Editor.Core/DataFormats/FileSystem/IFF/RIFFDataFormat.cs b/editor-dotnet/src/lib/MBS.Editor.Core/DataFormats/FileSystem/IFF/RIFFDataFormat.cs
new file mode 100644
index 0000000..d040eab
--- /dev/null
+++ b/editor-dotnet/src/lib/MBS.Editor.Core/DataFormats/FileSystem/IFF/RIFFDataFormat.cs
@@ -0,0 +1,27 @@
+// Copyright (C) 2024 Michael Becker
+//
+// This file is part of editor-dotnet.
+//
+// editor-dotnet is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// editor-dotnet is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with editor-dotnet. If not, see .
+
+namespace MBS.Editor.Core.DataFormats.FileSystem.IFF;
+
+public class RIFFDataFormat : IFFDataFormat
+{
+ public RIFFDataFormat()
+ {
+ GroupChunkNames.Add("RIFF");
+ Endianness = IO.Endianness.LittleEndian;
+ }
+}
\ No newline at end of file
diff --git a/editor-dotnet/src/lib/MBS.Editor.Core/DataFormats/PropertyList/INI/INIDataFormat.cs b/editor-dotnet/src/lib/MBS.Editor.Core/DataFormats/PropertyList/INI/INIDataFormat.cs
new file mode 100644
index 0000000..d66b347
--- /dev/null
+++ b/editor-dotnet/src/lib/MBS.Editor.Core/DataFormats/PropertyList/INI/INIDataFormat.cs
@@ -0,0 +1,205 @@
+
+using System.Formats.Asn1;
+using System.Runtime.InteropServices.Marshalling;
+using System.Text;
+using System.Text.RegularExpressions;
+using MBS.Core.Settings;
+
+namespace MBS.Editor.Core;
+
+public class INIDataFormat : DataFormat
+{
+ public char CommentPrefix { get; set; } = ';';
+
+ private static DataFormatMetadata? _dfr = null;
+ public static DataFormatMetadata Metadata
+ {
+ get
+ {
+ if (_dfr == null)
+ {
+ _dfr = new DataFormatMetadata();
+
+ SettingsGroup sg = new SettingsGroup("General", new Setting[]
+ {
+ new TextSetting("CommentPrefix", "Comment Prefix", ";")
+ });
+ _dfr.ExportSettings.SettingsGroups.Add(sg);
+ }
+ return _dfr;
+ }
+ }
+
+ protected override void LoadInternal(ObjectModel objectModel, Stream stream)
+ {
+ PropertyListObjectModel? plom = objectModel as PropertyListObjectModel;
+ if (plom == null)
+ throw new ObjectModelNotSupportedException(typeof(PropertyListObjectModel), objectModel?.GetType());
+
+ IPropertyListContainer currentGroup = plom;
+ INIDataFormatToken insideToken = INIDataFormatToken.None;
+ StringBuilder currentString = new StringBuilder();
+ bool escaped = false;
+
+ string currentPropertyName = "";
+ string currentGroupName = "";
+
+ StreamReader sr = new StreamReader(stream);
+ while (!sr.EndOfStream)
+ {
+ int val = sr.Read();
+ if (val == -1)
+ break;
+
+ char c = (char)val;
+ if (insideToken == INIDataFormatToken.Group)
+ {
+ if (c == ']')
+ {
+ insideToken = INIDataFormatToken.None;
+ }
+ else
+ {
+ currentGroupName += c;
+ }
+ continue;
+ }
+
+ if (c == CommentPrefix)
+ {
+ if (insideToken == INIDataFormatToken.None)
+ {
+ insideToken = INIDataFormatToken.Comment;
+ }
+ else
+ {
+ currentString.Append(c);
+ }
+ }
+ else if (c == '"')
+ {
+ if (insideToken == INIDataFormatToken.None)
+ {
+ insideToken = INIDataFormatToken.String;
+ }
+ else if (insideToken == INIDataFormatToken.String && !escaped)
+ {
+ insideToken = INIDataFormatToken.None;
+ }
+ else
+ {
+ currentString.Append(c);
+ }
+ }
+ else if (c == '\\')
+ {
+ if (insideToken == INIDataFormatToken.None || insideToken == INIDataFormatToken.Comment)
+ {
+ currentString.Append(c);
+ }
+ else if (insideToken == INIDataFormatToken.String)
+ {
+ if (!escaped)
+ {
+ escaped = true;
+ }
+ else
+ {
+ currentString.Append(c);
+ }
+ }
+ }
+ else if (c == '\n')
+ {
+ if (currentGroupName != "")
+ {
+ PropertyListGroup group = new PropertyListGroup(currentGroupName);
+ currentGroupName = "";
+
+ plom.Items.Add(group);
+ currentGroup = group;
+ }
+ else
+ {
+ PropertyListProperty property = new PropertyListProperty(currentPropertyName, currentString.ToString());
+ currentString.Clear();
+
+ currentGroup.Items.Add(property);
+ }
+ }
+ else if (c == '=')
+ {
+ if (insideToken == INIDataFormatToken.None)
+ {
+ currentPropertyName = currentString.ToString();
+ currentString.Clear();
+ }
+ else
+ {
+ currentString.Append(c);
+ }
+ }
+ else if (c == '[')
+ {
+ if (insideToken == INIDataFormatToken.None)
+ {
+ insideToken = INIDataFormatToken.Group;
+ continue;
+ }
+ else
+ {
+ currentString.Append(c);
+ }
+ }
+ else if (c == ']')
+ {
+ if (insideToken != INIDataFormatToken.None)
+ {
+ currentString.Append(c);
+ }
+ }
+ else
+ {
+ currentString.Append(c);
+ }
+ }
+ }
+
+ private void WriteItem(StreamWriter sw, PropertyListItem item)
+ {
+ if (item is PropertyListProperty p)
+ {
+ sw.WriteLine(String.Format("{0}={1}", p.Name, p.Value));
+ }
+ else if (item is PropertyListGroup g)
+ {
+ sw.WriteLine(String.Format("[{0}]", g.Name));
+ foreach (PropertyListItem item2 in g.Items)
+ {
+ WriteItem(sw, item2);
+ }
+ }
+ else if (item is PropertyListComment c)
+ {
+ string[] lines = c.Value.Split(System.Environment.NewLine);
+ foreach (string line in lines)
+ {
+ sw.WriteLine(String.Format("{0} {1}", CommentPrefix, line));
+ }
+ }
+ }
+
+ protected override void SaveInternal(ObjectModel objectModel, Stream stream)
+ {
+ PropertyListObjectModel? plom = objectModel as PropertyListObjectModel;
+ if (plom == null)
+ throw new ObjectModelNotSupportedException(typeof(PropertyListObjectModel), objectModel?.GetType());
+
+ StreamWriter sw = new StreamWriter(stream);
+ foreach (PropertyListItem item in plom.Items)
+ {
+ WriteItem(sw, item);
+ }
+ sw.Flush();
+ }
+}
diff --git a/editor-dotnet/src/lib/MBS.Editor.Core/DataFormats/PropertyList/INI/INIDataFormatToken.cs b/editor-dotnet/src/lib/MBS.Editor.Core/DataFormats/PropertyList/INI/INIDataFormatToken.cs
new file mode 100644
index 0000000..845dd01
--- /dev/null
+++ b/editor-dotnet/src/lib/MBS.Editor.Core/DataFormats/PropertyList/INI/INIDataFormatToken.cs
@@ -0,0 +1,9 @@
+namespace MBS.Editor.Core;
+
+public enum INIDataFormatToken
+{
+ None = 0,
+ Comment,
+ String,
+ Group
+}
diff --git a/editor-dotnet/src/lib/MBS.Editor.Core/IO/Reader.cs b/editor-dotnet/src/lib/MBS.Editor.Core/IO/Reader.cs
index 305afe3..f925c44 100644
--- a/editor-dotnet/src/lib/MBS.Editor.Core/IO/Reader.cs
+++ b/editor-dotnet/src/lib/MBS.Editor.Core/IO/Reader.cs
@@ -609,7 +609,7 @@ public class Reader : ReaderWriterBase
public int ReadInt24()
{
byte[] buffer = ReadBytes((uint)3);
- byte[] _buffer = new byte[3];
+ byte[] _buffer = new byte[4];
if (base.Endianness == Endianness.LittleEndian)
{
_buffer[0] = buffer[0];
@@ -1798,31 +1798,6 @@ public class Reader : ReaderWriterBase
return line;
}
}
-
- ///
- /// Closes the current stream and releases any resources (such as sockets and file handles) associated with the current stream.
- ///
- public void Close()
- {
- BaseStream.Close();
- }
-
- ///
- /// Aligns the to the specified number of bytes. If the current
- /// position of the is not a multiple of the specified number of bytes,
- /// the position will be increased by the amount of bytes necessary to bring it to the
- /// aligned position.
- ///
- /// The number of bytes on which to align the .
- /// Any additional padding bytes that should be included after aligning to the specified boundary.
- public void Align(int alignTo, int extraPadding = 0)
- {
- long paddingCount = ((alignTo - (BaseStream.Position % alignTo)) % alignTo);
- paddingCount += extraPadding;
-
- if (BaseStream.Position + paddingCount < BaseStream.Length)
- BaseStream.Position += paddingCount;
- }
public string ReadStringUntilAny(char[] anyOf)
{
diff --git a/editor-dotnet/src/lib/MBS.Editor.Core/IO/ReaderWriterBase.cs b/editor-dotnet/src/lib/MBS.Editor.Core/IO/ReaderWriterBase.cs
index 74aa39d..7ee3cc5 100644
--- a/editor-dotnet/src/lib/MBS.Editor.Core/IO/ReaderWriterBase.cs
+++ b/editor-dotnet/src/lib/MBS.Editor.Core/IO/ReaderWriterBase.cs
@@ -66,14 +66,25 @@ public class ReaderWriterBase
long paddingCount = ((alignTo - (_st.Position % alignTo)) % alignTo);
paddingCount += extraPadding;
- if (_st.Position == _st.Length)
+ if (paddingCount > 0)
{
- byte[] buffer = new byte[paddingCount];
- _st.Write(buffer, 0, buffer.Length);
- }
- else
- {
- _st.Position += paddingCount;
+ if (_st.Position == _st.Length)
+ {
+ byte[] buffer = new byte[paddingCount];
+ _st.Write(buffer, 0, buffer.Length);
+ }
+ else
+ {
+ _st.Position += paddingCount;
+ }
}
}
+
+ ///
+ /// Closes the current stream and releases any resources (such as sockets and file handles) associated with the current stream.
+ ///
+ public void Close()
+ {
+ BaseStream.Close();
+ }
}
diff --git a/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModel.cs b/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModel.cs
index c342e97..35bf2eb 100644
--- a/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModel.cs
+++ b/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModel.cs
@@ -32,5 +32,14 @@ public class ObjectModel
}
return objectModel;
}
-
+
+ public static T CastOrThrow(ObjectModel? objectModel) where T : ObjectModel
+ {
+ T om = objectModel as T;
+ if (om == null)
+ {
+ throw new ObjectModelNotSupportedException(typeof(T), objectModel?.GetType());
+ }
+ return om;
+ }
}
diff --git a/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModelNotSupportedException.cs b/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModelNotSupportedException.cs
index 80e9a03..a1334e7 100644
--- a/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModelNotSupportedException.cs
+++ b/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModelNotSupportedException.cs
@@ -1,3 +1,5 @@
+using MBS.Core;
+
namespace MBS.Editor.Core;
public class ObjectModelNotSupportedException : Exception
@@ -29,9 +31,19 @@ public class ObjectModelNotSupportedException : Exception
public Type? ExpectedObjectModelType { get; }
public Type? ActualObjectModelType { get; }
- public ObjectModelNotSupportedException(string? message, Type expectedObjectModelType, Type actualObjectModelType)
+ private static Dictionary __c1(Type expected, Type actual)
{
+ Dictionary dict = new Dictionary();
+ dict["expected"] = expected.FullName;
+ dict["actual"] = actual.FullName;
+ return dict;
+ }
+ public ObjectModelNotSupportedException(Type expectedObjectModelType, Type? actualObjectModelType) : this("The object model is not supported (expected $(expected), got $(actual))", expectedObjectModelType, actualObjectModelType) { }
+ public ObjectModelNotSupportedException(string? message, Type expectedObjectModelType, Type? actualObjectModelType) : base(StringExtensions.Format(message, __c1(expectedObjectModelType, actualObjectModelType)))
+ {
+ ExpectedObjectModelType = expectedObjectModelType;
+ ActualObjectModelType = actualObjectModelType;
}
}
\ No newline at end of file
diff --git a/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/Chunked/Chunk.cs b/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/Chunked/Chunk.cs
new file mode 100644
index 0000000..1808ca2
--- /dev/null
+++ b/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/Chunked/Chunk.cs
@@ -0,0 +1,63 @@
+// Copyright (C) 2024 Michael Becker
+//
+// This file is part of editor-dotnet.
+//
+// editor-dotnet is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// editor-dotnet is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with editor-dotnet. If not, see .
+
+
+namespace MBS.Editor.Core.ObjectModels.Chunked;
+
+public abstract class Chunk
+{
+ public class ChunkCollection
+ : System.Collections.ObjectModel.Collection
+ {
+
+ public Chunk? this[string name]
+ {
+ get
+ {
+ foreach (Chunk item in this)
+ {
+ if (item.Name == name)
+ return item;
+ }
+ return null;
+ }
+ }
+
+ public Chunk? this[string name, string typeName]
+ {
+ get
+ {
+ foreach (Chunk item in this)
+ {
+ if (item is GroupChunk g)
+ {
+ if (g.Name == name && g.TypeName == typeName)
+ return g;
+ }
+ }
+ return null;
+ }
+ }
+ }
+
+ public Chunk(string name)
+ {
+ Name = name;
+ }
+ public string Name { get; set; }
+
+}
\ No newline at end of file
diff --git a/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/Chunked/ChunkedObjectModel.cs b/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/Chunked/ChunkedObjectModel.cs
new file mode 100644
index 0000000..3dff116
--- /dev/null
+++ b/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/Chunked/ChunkedObjectModel.cs
@@ -0,0 +1,23 @@
+// Copyright (C) 2024 Michael Becker
+//
+// This file is part of editor-dotnet.
+//
+// editor-dotnet is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// editor-dotnet is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with editor-dotnet. If not, see .
+
+namespace MBS.Editor.Core.ObjectModels.Chunked;
+
+public class ChunkedObjectModel : ObjectModel, IChunkContainer
+{
+ public Chunk.ChunkCollection Chunks { get; } = new Chunk.ChunkCollection();
+}
\ No newline at end of file
diff --git a/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/Chunked/DataChunk.cs b/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/Chunked/DataChunk.cs
new file mode 100644
index 0000000..a97ffd7
--- /dev/null
+++ b/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/Chunked/DataChunk.cs
@@ -0,0 +1,29 @@
+// Copyright (C) 2024 Michael Becker
+//
+// This file is part of editor-dotnet.
+//
+// editor-dotnet is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// editor-dotnet is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with editor-dotnet. If not, see .
+
+
+using MBS.Editor.Core.ObjectModels.FileSystem;
+
+namespace MBS.Editor.Core.ObjectModels.Chunked;
+
+public class DataChunk : Chunk
+{
+ public DataChunk(string name) : base(name)
+ {
+ }
+ public FileSource? Source { get; set; } = null;
+}
\ No newline at end of file
diff --git a/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/Chunked/GroupChunk.cs b/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/Chunked/GroupChunk.cs
new file mode 100644
index 0000000..c0b67e0
--- /dev/null
+++ b/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/Chunked/GroupChunk.cs
@@ -0,0 +1,29 @@
+// Copyright (C) 2024 Michael Becker
+//
+// This file is part of editor-dotnet.
+//
+// editor-dotnet is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// editor-dotnet is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with editor-dotnet. If not, see .
+
+namespace MBS.Editor.Core.ObjectModels.Chunked;
+
+public class GroupChunk : Chunk, IChunkContainer
+{
+ public GroupChunk(string name, string typeName) : base(name)
+ {
+ TypeName = typeName;
+ }
+ public string TypeName { get; set; }
+
+ public Chunk.ChunkCollection Chunks { get; } = new Chunk.ChunkCollection();
+}
diff --git a/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/Chunked/IChunkContainer.cs b/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/Chunked/IChunkContainer.cs
new file mode 100644
index 0000000..4e5aa4d
--- /dev/null
+++ b/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/Chunked/IChunkContainer.cs
@@ -0,0 +1,23 @@
+// Copyright (C) 2024 Michael Becker
+//
+// This file is part of editor-dotnet.
+//
+// editor-dotnet is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// editor-dotnet is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with editor-dotnet. If not, see .
+
+namespace MBS.Editor.Core.ObjectModels.Chunked;
+
+public interface IChunkContainer
+{
+ Chunk.ChunkCollection Chunks { get; }
+}
\ No newline at end of file
diff --git a/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/FileSystem/FileSource.cs b/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/FileSystem/FileSource.cs
index 34cd688..c9da54b 100644
--- a/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/FileSystem/FileSource.cs
+++ b/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/FileSystem/FileSource.cs
@@ -1,3 +1,4 @@
+
namespace MBS.Editor.Core.ObjectModels.FileSystem;
public abstract class FileSource
@@ -22,4 +23,11 @@ public abstract class FileSource
*/
return msInput.ToArray();
}
+
+ public Stream GetStream()
+ {
+ byte[] data = GetData();
+ MemoryStream msInput = new MemoryStream(data);
+ return msInput;
+ }
}
\ No newline at end of file
diff --git a/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/FileSystem/FileSources/EmbeddedFileSource.cs b/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/FileSystem/FileSources/EmbeddedFileSource.cs
index 9327a10..c4a08db 100644
--- a/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/FileSystem/FileSources/EmbeddedFileSource.cs
+++ b/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/FileSystem/FileSources/EmbeddedFileSource.cs
@@ -13,7 +13,7 @@ public class EmbeddedFileSource : FileSource
}
protected override byte[] GetDataInternal(long offset, long length)
{
- if (Offset + offset + length >= Stream.Length)
+ if (Offset + offset + length > Stream.Length)
{
throw new ArgumentOutOfRangeException("embedded file offset + requested offset + requested length extends past the actual length of the underlying stream");
}
diff --git a/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/FileSystem/FileSystemItemCollection.cs b/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/FileSystem/FileSystemItemCollection.cs
index 38cfef2..39c4c41 100644
--- a/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/FileSystem/FileSystemItemCollection.cs
+++ b/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/FileSystem/FileSystemItemCollection.cs
@@ -178,4 +178,14 @@ public class FileSystemItemCollection
}
return list.ToArray();
}
+
+ public T? GetChild(string v) where T : FileSystemItem
+ {
+ FileSystemItem? item = this[v];
+ if (item is T)
+ {
+ return (T)item;
+ }
+ return null;
+ }
}
diff --git a/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/PropertyList/IPropertyListContainer.cs b/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/PropertyList/IPropertyListContainer.cs
new file mode 100644
index 0000000..932fe29
--- /dev/null
+++ b/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/PropertyList/IPropertyListContainer.cs
@@ -0,0 +1,9 @@
+namespace MBS.Editor.Core;
+
+public interface IPropertyListContainer
+{
+
+ PropertyListItem.PropertyListItemCollection Items { get; }
+ IPropertyListContainer? Parent { get; }
+
+}
diff --git a/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/PropertyList/PropertyListComment.cs b/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/PropertyList/PropertyListComment.cs
new file mode 100644
index 0000000..06c20d3
--- /dev/null
+++ b/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/PropertyList/PropertyListComment.cs
@@ -0,0 +1,6 @@
+namespace MBS.Editor.Core;
+
+public class PropertyListComment : PropertyListItem
+{
+ public string Value { get; set; } = "";
+}
diff --git a/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/PropertyList/PropertyListGroup.cs b/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/PropertyList/PropertyListGroup.cs
new file mode 100644
index 0000000..2fceee0
--- /dev/null
+++ b/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/PropertyList/PropertyListGroup.cs
@@ -0,0 +1,14 @@
+namespace MBS.Editor.Core;
+
+public class PropertyListGroup : PropertyListItem, IPropertyListContainer
+{
+
+ public PropertyListItem.PropertyListItemCollection Items { get; }
+
+ public PropertyListGroup(string name)
+ {
+ Name = name;
+ Items = new PropertyListItemCollection(this);
+ }
+
+}
diff --git a/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/PropertyList/PropertyListItem.cs b/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/PropertyList/PropertyListItem.cs
new file mode 100644
index 0000000..c62338e
--- /dev/null
+++ b/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/PropertyList/PropertyListItem.cs
@@ -0,0 +1,58 @@
+namespace MBS.Editor.Core;
+
+public abstract class PropertyListItem
+{
+
+ public class PropertyListItemCollection
+ : System.Collections.ObjectModel.Collection
+ {
+ private IPropertyListContainer _Parent = null;
+
+ public PropertyListItemCollection(IPropertyListContainer parent)
+ {
+ _Parent = parent;
+ }
+
+ protected override void ClearItems()
+ {
+ foreach (PropertyListItem item in this)
+ {
+ item.Parent = null;
+ }
+ base.ClearItems();
+ }
+ protected override void InsertItem(int index, PropertyListItem item)
+ {
+ base.InsertItem(index, item);
+ item.Parent = _Parent;
+ }
+ protected override void RemoveItem(int index)
+ {
+ this[index].Parent = null;
+ base.RemoveItem(index);
+ }
+
+ }
+
+ public string Name { get; set; } = "";
+ public IPropertyListContainer? Parent { get; internal set; }
+
+ public PropertyListObjectModel? ParentObjectModel
+
+ {
+ get
+ {
+ IPropertyListContainer? parent = Parent;
+ while (parent != null)
+ {
+ if (parent is PropertyListObjectModel)
+ {
+ return (parent as PropertyListObjectModel);
+ }
+ parent = parent.Parent;
+ }
+ return null;
+ }
+ }
+
+}
diff --git a/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/PropertyList/PropertyListObjectModel.cs b/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/PropertyList/PropertyListObjectModel.cs
new file mode 100644
index 0000000..d0f7f43
--- /dev/null
+++ b/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/PropertyList/PropertyListObjectModel.cs
@@ -0,0 +1,18 @@
+using MBS.Editor.Core.ObjectModels.PropertyList.Schema;
+
+namespace MBS.Editor.Core;
+
+public class PropertyListObjectModel : ObjectModel, IPropertyListContainer
+{
+
+ public PropertyListItem.PropertyListItemCollection Items { get; }
+ public PropertyListSchema? Schema { get; set; } = null;
+
+ public IPropertyListContainer? Parent => null;
+
+ public PropertyListObjectModel()
+ {
+ Items = new PropertyListItem.PropertyListItemCollection(this);
+ }
+
+}
diff --git a/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/PropertyList/PropertyListProperty.cs b/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/PropertyList/PropertyListProperty.cs
new file mode 100644
index 0000000..4e2ae02
--- /dev/null
+++ b/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/PropertyList/PropertyListProperty.cs
@@ -0,0 +1,41 @@
+using MBS.Editor.Core.ObjectModels.PropertyList.Schema;
+
+namespace MBS.Editor.Core;
+
+public class PropertyListProperty : PropertyListItem
+{
+
+ private object? _Value = null;
+ public object? Value
+ {
+ get { return _Value; }
+ set { _Value = value; }
+ }
+
+ public T? GetValueAs()
+ {
+ PropertyListObjectModel? plom = ParentObjectModel;
+ if (plom != null)
+ {
+ PropertyListSchema? schema = plom.Schema;
+ if (schema != null)
+ {
+ foreach (PropertyDefinition def in schema.PropertyDefinitions)
+ {
+ if (def.PropertyName == Name)
+ {
+ //return def.ConvertTo(def.DataType, Value);
+ }
+ }
+ }
+ }
+ return default(T);
+ }
+
+ public PropertyListProperty(string name, object value)
+ {
+ Name = name;
+ Value = value;
+ }
+
+}
diff --git a/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/PropertyList/Schema/PropertyDataTypeDefinition.cs b/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/PropertyList/Schema/PropertyDataTypeDefinition.cs
new file mode 100644
index 0000000..4c0d881
--- /dev/null
+++ b/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/PropertyList/Schema/PropertyDataTypeDefinition.cs
@@ -0,0 +1,27 @@
+namespace MBS.Editor.Core.ObjectModels.PropertyList.Schema;
+
+public class PropertyValueConverter
+{
+ public class PropertyValueConverterCollection
+ : System.Collections.ObjectModel.Collection
+
+ {
+
+ }
+}
+public class PropertyValueConverter : PropertyValueConverter
+{
+ private Func __convertTo = null;
+ private Func __convertFrom = null;
+ public PropertyValueConverter(Func convertTo, Func convertFrom)
+ {
+ __convertTo = convertTo;
+ __convertFrom = convertFrom;
+ }
+
+ public TOutput? ConvertTo(TInput value)
+ {
+ return default;
+ }
+
+}
\ No newline at end of file
diff --git a/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/PropertyList/Schema/PropertyDefinition.cs b/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/PropertyList/Schema/PropertyDefinition.cs
new file mode 100644
index 0000000..c1e0bfc
--- /dev/null
+++ b/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/PropertyList/Schema/PropertyDefinition.cs
@@ -0,0 +1,41 @@
+
+using System.Diagnostics.Contracts;
+using System.Text.Json.Serialization;
+using MBS.Core.Collections.Generic;
+
+namespace MBS.Editor.Core.ObjectModels.PropertyList.Schema;
+
+public class PropertyDefinition
+{
+ public class PropertyDefinitionCollection
+ : System.Collections.ObjectModel.Collection
+ {
+
+ }
+
+ public string PropertyName { get; }
+ public Type DataType { get; }
+ public PropertyValueConverter.PropertyValueConverterCollection Converters { get; } = new PropertyValueConverter.PropertyValueConverterCollection();
+
+ public PropertyDefinition(string propertyName, string displayTitle, Type dataType, PropertyValueConverter[]? valueConverters = null)
+ {
+ PropertyName = propertyName;
+ DataType = dataType;
+ if (valueConverters != null)
+ {
+ Converters.AddRange(valueConverters);
+ }
+ }
+
+ public TTo ConvertTo(TFrom value)
+ {
+ foreach (PropertyValueConverter converter in Converters)
+ {
+ if (converter is PropertyValueConverter cvt)
+ {
+ return cvt.ConvertTo(value);
+ }
+ }
+ throw new NotSupportedException();
+ }
+}
\ No newline at end of file
diff --git a/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/PropertyList/Schema/PropertyListSchema.cs b/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/PropertyList/Schema/PropertyListSchema.cs
new file mode 100644
index 0000000..01422a9
--- /dev/null
+++ b/editor-dotnet/src/lib/MBS.Editor.Core/ObjectModels/PropertyList/Schema/PropertyListSchema.cs
@@ -0,0 +1,14 @@
+using MBS.Core.Collections.Generic;
+
+namespace MBS.Editor.Core.ObjectModels.PropertyList.Schema;
+
+public class PropertyListSchema
+{
+ public PropertyDefinition.PropertyDefinitionCollection PropertyDefinitions { get; } = new PropertyDefinition.PropertyDefinitionCollection();
+
+ public PropertyListSchema(PropertyDefinition[] propertyDefinitions)
+ {
+ PropertyDefinitions.AddRange(propertyDefinitions);
+ }
+
+}
\ No newline at end of file
diff --git a/editor-dotnet/src/lib/MBS.Editor.Testing/EmbeddedResourceTest.cs b/editor-dotnet/src/lib/MBS.Editor.Testing/EmbeddedResourceTest.cs
new file mode 100644
index 0000000..b7c91ea
--- /dev/null
+++ b/editor-dotnet/src/lib/MBS.Editor.Testing/EmbeddedResourceTest.cs
@@ -0,0 +1,65 @@
+using NUnit.Framework;
+
+namespace MBS.Editor.Testing;
+
+public class EmbeddedResourceTest
+{
+ private System.Collections.Generic.Dictionary testDataStreams = new System.Collections.Generic.Dictionary();
+
+ [SetUp]
+ public void SetUp()
+ {
+ InitializeResources();
+ }
+
+ protected void InitializeResources()
+ {
+ MBS.Core.Reflection.ManifestResourceStream[] strms = MBS.Core.Reflection.ManifestResourceStream.GetManifestResourceStreamsForAssembly(this.GetType().Assembly);
+ for (int i = 0; i < strms.Length; i++)
+ {
+ testDataStreams[strms[i].Name] = strms[i].Stream;
+ }
+ }
+
+ protected Stream? CreateResourceStream(string name)
+ {
+ if (TryCreateResourceStream(name, out Stream? st))
+ {
+ return st;
+ }
+ return null;
+ }
+
+ protected bool TryCreateResourceStream(string name, out System.IO.Stream? st)
+ {
+ st = null;
+ if (testDataStreams.ContainsKey(name))
+ {
+ System.IO.Stream TEST_STREAM = testDataStreams[name];
+ st = TEST_STREAM;
+ return true;
+ }
+ else
+ {
+ Console.Error.WriteLine("test data stream not found: '{0}'", name);
+ }
+ return false;
+ }
+
+ protected void SampleStreamTest(string streamName)
+ {
+ if (TryCreateResourceStream(streamName, out System.IO.Stream? st))
+ {
+ SampleStreamTest(st);
+ }
+ else
+ {
+ Assert.Ignore();
+ }
+ }
+
+ protected void SampleStreamTest(Stream st)
+ {
+ }
+
+}
diff --git a/editor-dotnet/src/lib/MBS.Editor.Testing/MBS.Editor.Testing.csproj b/editor-dotnet/src/lib/MBS.Editor.Testing/MBS.Editor.Testing.csproj
new file mode 100644
index 0000000..e5eafa1
--- /dev/null
+++ b/editor-dotnet/src/lib/MBS.Editor.Testing/MBS.Editor.Testing.csproj
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+ net8.0
+ enable
+ enable
+ false
+ false
+
+
+
diff --git a/editor-dotnet/src/lib/MBS.Editor.UserInterface/EditorApplication.cs b/editor-dotnet/src/lib/MBS.Editor.UserInterface/EditorApplication.cs
index f9f2537..033e05e 100644
--- a/editor-dotnet/src/lib/MBS.Editor.UserInterface/EditorApplication.cs
+++ b/editor-dotnet/src/lib/MBS.Editor.UserInterface/EditorApplication.cs
@@ -1,14 +1,38 @@
namespace MBS.Editor.UserInterface;
+using System;
+using MBS.Core;
using MBS.Desktop;
+using MBS.Desktop.Controls;
public class EditorApplication : DesktopApplication
{
+ protected override void OnStartup(EventArgs e)
+ {
+ base.OnStartup(e);
- protected override int StartInternal()
- {
- return 0;
- }
+ Console.WriteLine("editor: OnStartup");
+
+ this.Commands.Add(new Command("FileExit", "E_xit"));
+ this.Commands.Add(new Command("File", "_File", new CommandItem[]
+ {
+ new CommandReferenceCommandItem("FileExit")
+ }));
+
+ this.AttachCommandEventHandler("FileExit", delegate (object sender, CommandEventArgs e)
+ {
+ this.Stop();
+ });
+ }
+
+ protected override void OnActivated(ApplicationActivatedEventArgs e)
+ {
+ base.OnActivated(e);
+
+ Console.WriteLine("editor: OnActivated");
+
+ EditorWindow window = new EditorWindow();
+ window.Show();
+ }
-
}
diff --git a/editor-dotnet/src/lib/MBS.Editor.UserInterface/EditorWindow.cs b/editor-dotnet/src/lib/MBS.Editor.UserInterface/EditorWindow.cs
new file mode 100644
index 0000000..3d56548
--- /dev/null
+++ b/editor-dotnet/src/lib/MBS.Editor.UserInterface/EditorWindow.cs
@@ -0,0 +1,20 @@
+namespace MBS.Editor.UserInterface;
+
+using System;
+using MBS.Core;
+using MBS.Desktop;
+using MBS.Desktop.Controls;
+
+// [ContainerLayout(typeof(EditorWindow), "MBS.Editor.UserInterface.EditorWindow.glade")]
+public class EditorWindow : MainWindow
+{
+
+ public EditorWindow()
+ {
+ Title = "MBS Editor";
+ DefaultSize = new Core.Drawing.Dimension2D(1000, 700);
+
+ Controls.Add(new Label("Strong Bad!") { UseMarkup = true });
+ }
+
+}
diff --git a/editor-dotnet/src/lib/MBS.Editor.UserInterface/MBS.Editor.UserInterface.csproj b/editor-dotnet/src/lib/MBS.Editor.UserInterface/MBS.Editor.UserInterface.csproj
index dd82f9d..4e87b11 100644
--- a/editor-dotnet/src/lib/MBS.Editor.UserInterface/MBS.Editor.UserInterface.csproj
+++ b/editor-dotnet/src/lib/MBS.Editor.UserInterface/MBS.Editor.UserInterface.csproj
@@ -2,7 +2,7 @@
-
+
diff --git a/editor-dotnet/src/lib/MBS.Editor.Web/Controls/PageHeader.cs b/editor-dotnet/src/lib/MBS.Editor.Web/Controls/PageHeader.cs
new file mode 100644
index 0000000..3ee6c62
--- /dev/null
+++ b/editor-dotnet/src/lib/MBS.Editor.Web/Controls/PageHeader.cs
@@ -0,0 +1,31 @@
+// Copyright (C) 2024 Michael Becker
+//
+// This file is part of editor-dotnet.
+//
+// editor-dotnet is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// editor-dotnet is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with editor-dotnet. If not, see .
+
+
+using MBS.Web.UI;
+
+namespace MBS.Editor.Web.Controls;
+
+public class PageHeader : WebControl
+{
+ protected override string TagName => "div";
+ protected override IEnumerable GetStyleClasses()
+ {
+ return new string[] { "uwt-page-header" };
+ }
+
+}
diff --git a/editor-dotnet/src/lib/MBS.Editor.Web/MBS.Editor.Web.csproj b/editor-dotnet/src/lib/MBS.Editor.Web/MBS.Editor.Web.csproj
new file mode 100644
index 0000000..c987729
--- /dev/null
+++ b/editor-dotnet/src/lib/MBS.Editor.Web/MBS.Editor.Web.csproj
@@ -0,0 +1,14 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
diff --git a/editor-dotnet/src/lib/MBS.Editor.Web/Pages/MainPage.cs b/editor-dotnet/src/lib/MBS.Editor.Web/Pages/MainPage.cs
new file mode 100644
index 0000000..b726c65
--- /dev/null
+++ b/editor-dotnet/src/lib/MBS.Editor.Web/Pages/MainPage.cs
@@ -0,0 +1,123 @@
+// Copyright (C) 2024 Michael Becker
+//
+// This file is part of editor-dotnet.
+//
+// editor-dotnet is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// editor-dotnet is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with editor-dotnet. If not, see .
+
+using MBS.Web;
+using MBS.Web.UI;
+
+using MBS.Editor.Web.Controls;
+using MBS.Web.UI.HtmlControls;
+using System.Text;
+using MBS.Core.Drawing;
+using MBS.Web.UI.WebControls;
+using MBS.Core;
+
+namespace MBS.Editor.Web.Pages;
+
+public class MainPage : WebPage
+{
+ private class WebStyleSheet2
+ {
+
+
+ }
+
+
+ public MainPage()
+ {
+ Dictionary> dict = new Dictionary>();
+ /*
+ WebStyleSheet2 wss = new WebStyleSheet2();
+
+ WebStyleSheetSelector selector = WebStyleSheetSelector.Parse("div.uwt-page-header");
+
+ wss.Selectors.Add("div", "uwt-page-header");
+ */
+
+ dict["div.uwt-page-header"] = new Dictionary();
+ dict["div.uwt-page-header"]["border-bottom"] = new object[] { Measurement.Parse("1px"), "solid", Color.FromString("#E0E0E0") };
+
+ StyleSheets.Add(WebStyleSheet.FromContent("text/css", FormatCss(dict)));
+ }
+
+ protected override IEnumerable GetHeaderControls()
+ {
+ return new Control[]
+ {
+
+
+ };
+ }
+
+ private string FormatCss(Dictionary> dict)
+ {
+ StringBuilder sb = new StringBuilder();
+ foreach (KeyValuePair> kvp in dict)
+ {
+ sb.Append(kvp.Key);
+ sb.Append(" { ");
+ foreach (KeyValuePair kvp2 in kvp.Value)
+ {
+ sb.Append(kvp2.Key);
+ sb.Append(": ");
+
+ if (kvp2.Value is Color)
+ {
+ sb.Append(((Color)kvp2.Value).ToHexadecimalHTML());
+ }
+ else if (kvp2.Value is string)
+ {
+ sb.Append("\"");
+ sb.Append(kvp2.Value);
+ sb.Append("\"");
+ }
+ else if (kvp2.Value is object[])
+ {
+ object[] objs = (object[])kvp2.Value;
+ for (int i = 0; i < objs.Length; i++)
+ {
+ sb.Append(objs[i]);
+ if (i < objs.Length - 1)
+ {
+ sb.Append(' ');
+ }
+ }
+ }
+ else
+ {
+ sb.Append(kvp2.Value);
+ }
+ }
+ sb.Append(" } ");
+ }
+ return sb.ToString();
+ }
+
+ protected override IEnumerable GetBodyControls()
+ {
+ CommandBar cb = new CommandBar();
+ cb.ClientId = "mainToolbar";
+ cb.Items.Add(new CommandReferenceCommandItem("FileOpen"));
+
+ return new Control[]
+ {
+ new PageHeader(),
+ cb
+ };
+ }
+
+
+}
\ No newline at end of file
diff --git a/editor-dotnet/src/plugins/MBS.Editor.Plugins.Kronosaur/Class1.cs b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Kronosaur/Class1.cs
new file mode 100644
index 0000000..63247f8
--- /dev/null
+++ b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Kronosaur/Class1.cs
@@ -0,0 +1,6 @@
+namespace MBS.Editor.Plugins.Kronosaur;
+
+public class Class1
+{
+
+}
diff --git a/editor-dotnet/src/plugins/MBS.Editor.Plugins.Kronosaur/MBS.Editor.Plugins.Kronosaur.csproj b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Kronosaur/MBS.Editor.Plugins.Kronosaur.csproj
new file mode 100644
index 0000000..fa71b7a
--- /dev/null
+++ b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Kronosaur/MBS.Editor.Plugins.Kronosaur.csproj
@@ -0,0 +1,9 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
diff --git a/editor-dotnet/src/plugins/MBS.Editor.Plugins.Mekada/DataFormats/Mechanic/MEKDataFormat.cs b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Mekada/DataFormats/Mechanic/MEKDataFormat.cs
new file mode 100644
index 0000000..1b354d2
--- /dev/null
+++ b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Mekada/DataFormats/Mechanic/MEKDataFormat.cs
@@ -0,0 +1,70 @@
+using MBS.Editor.Core;
+using MBS.Editor.Core.ObjectModels.PropertyList.Schema;
+using MBS.Editor.Plugins.Mekada.DataFormats.PropertyList;
+
+namespace MBS.Editor.Plugins.Mekada.DataFormats.Mechanic;
+
+public class MEKDataFormat : MEKBaseDataFormat
+{
+ protected override void BeforeLoadInternal(Stack objectModels)
+ {
+ base.BeforeLoadInternal(objectModels);
+
+ PropertyListObjectModel plom = new PropertyListObjectModel();
+ plom.Schema = new PropertyListSchema(new PropertyDefinition[]
+ {
+ new PropertyDefinition("1403", "Money", typeof(float)),
+ new PropertyDefinition("1405", "Total Time Played", typeof(int), new PropertyValueConverter[]
+ {
+ new PropertyValueConverter(delegate (int value)
+ {
+ TimeSpan ts = new TimeSpan(0, 0, value);
+ return ts;
+ },
+ delegate (TimeSpan value)
+ {
+ int seconds = (value.Hours * 3600) + (value.Minutes * 60) + (value.Seconds);
+ return seconds;
+ })
+ })
+ });
+ objectModels.Push(plom);
+ }
+ protected override void AfterLoadInternal(Stack objectModels)
+ {
+ base.AfterLoadInternal(objectModels);
+
+ PropertyListObjectModel? plom = objectModels.Pop() as PropertyListObjectModel;
+ if (plom == null)
+ throw new ObjectModelNotSupportedException(typeof(PropertyListObjectModel), plom?.GetType());
+
+ Console.WriteLine("{ ");
+ foreach (PropertyListItem item in plom.Items)
+ {
+ RenderItem(item);
+ }
+ Console.WriteLine(" }");
+
+ }
+
+ private void RenderItem(PropertyListItem item)
+ {
+ if (item is PropertyListGroup grp)
+ {
+ Console.WriteLine("\"" + grp.Name + "\": { ");
+ foreach (PropertyListItem item2 in grp.Items)
+ {
+ RenderItem(item2);
+ if (grp.Items.IndexOf(item2) < grp.Items.Count - 1)
+ {
+ Console.Write(", ");
+ }
+ }
+ Console.WriteLine(" }");
+ }
+ else if (item is PropertyListProperty prop)
+ {
+ Console.Write("\"" + item.Name + "\": \"" + prop.Value.ToString() + "\"");
+ }
+ }
+}
diff --git a/editor-dotnet/src/plugins/MBS.Editor.Plugins.Mekada/DataFormats/PropertyList/MEKBaseDataFormat.cs b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Mekada/DataFormats/PropertyList/MEKBaseDataFormat.cs
new file mode 100644
index 0000000..387bffb
--- /dev/null
+++ b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Mekada/DataFormats/PropertyList/MEKBaseDataFormat.cs
@@ -0,0 +1,92 @@
+using System.Text.RegularExpressions;
+using MBS.Editor.Core;
+using MBS.Editor.Core.IO;
+
+namespace MBS.Editor.Plugins.Mekada.DataFormats.PropertyList;
+
+public class MEKBaseDataFormat : DataFormat
+{
+ protected override void LoadInternal(ObjectModel objectModel, Stream stream)
+ {
+ PropertyListObjectModel? plom = objectModel as PropertyListObjectModel;
+ if (plom == null)
+ throw new ObjectModelNotSupportedException(typeof(PropertyListObjectModel), objectModel?.GetType());
+
+ Reader r = new Reader(stream);
+
+ while (true)
+ {
+ PropertyListItem item = ReadItem(r, plom);
+ if (item == null)
+ break;
+
+ plom.Items.Add(item);
+ }
+ }
+
+ private PropertyListItem? ReadItem(Reader r, IPropertyListContainer parent)
+ {
+ long start = r.BaseStream.Position;
+
+ ushort index = r.ReadUInt16();
+ MEKPropertyType type = (MEKPropertyType) r.ReadUInt16();
+ int length = r.ReadInt32();
+ if (index == 0 && type == MEKPropertyType.None && length == 0)
+ return null;
+
+ if (type == MEKPropertyType.Group)
+ {
+ PropertyListGroup group = new PropertyListGroup(index.ToString());
+
+ while (r.BaseStream.Position < length + start)
+ {
+ PropertyListItem? item = ReadItem(r, group);
+ if (item != null)
+ {
+ group.Items.Add(item);
+ }
+ }
+
+ return group;
+ }
+ else if (type == MEKPropertyType.Float)
+ {
+ if (length == 4)
+ {
+ float value = r.ReadSingle();
+ return new PropertyListProperty(index.ToString(), value);
+ }
+ else
+ {
+ throw new NotImplementedException();
+ }
+ }
+ else if (type == MEKPropertyType.Int32)
+ {
+ if (length == 4)
+ {
+ int value = r.ReadInt32();
+ return new PropertyListProperty(index.ToString(), value);
+ }
+ else
+ {
+ throw new NotImplementedException();
+ }
+ }
+ else if (type == MEKPropertyType.String)
+ {
+ string value = r.ReadFixedLengthString(length);
+ return new PropertyListProperty(index.ToString(), value);
+ }
+ else
+ {
+ byte[] data = r.ReadBytes(length);
+ return new PropertyListProperty(index.ToString(), data);
+ }
+ }
+
+ protected override void SaveInternal(ObjectModel objectModel, Stream stream)
+ {
+ throw new NotImplementedException();
+ }
+}
diff --git a/editor-dotnet/src/plugins/MBS.Editor.Plugins.Mekada/DataFormats/PropertyList/MEKPropertyType.cs b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Mekada/DataFormats/PropertyList/MEKPropertyType.cs
new file mode 100644
index 0000000..7e2e7f0
--- /dev/null
+++ b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Mekada/DataFormats/PropertyList/MEKPropertyType.cs
@@ -0,0 +1,10 @@
+namespace MBS.Editor.Plugins.Mekada;
+
+public enum MEKPropertyType : ushort
+{
+ None = 0,
+ Int32 = 8192,
+ Float = 16384,
+ String = 24576,
+ Group = 32768
+}
diff --git a/editor-dotnet/src/plugins/MBS.Editor.Plugins.Mekada/MBS.Editor.Plugins.Mekada.csproj b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Mekada/MBS.Editor.Plugins.Mekada.csproj
new file mode 100644
index 0000000..d7fe524
--- /dev/null
+++ b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Mekada/MBS.Editor.Plugins.Mekada.csproj
@@ -0,0 +1,14 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
diff --git a/editor-dotnet/src/plugins/MBS.Editor.Plugins.Mekada/ObjectModels/Mechanic/MechanicObjectModel.cs b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Mekada/ObjectModels/Mechanic/MechanicObjectModel.cs
new file mode 100644
index 0000000..bc5692e
--- /dev/null
+++ b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Mekada/ObjectModels/Mechanic/MechanicObjectModel.cs
@@ -0,0 +1,11 @@
+using MBS.Editor.Core;
+
+namespace MBS.Editor.Plugins.Mekada;
+
+public class MechanicObjectModel : ObjectModel
+{
+ public string Name { get; set; } = "";
+
+ public float Money { get; set; } = 0.0f;
+
+}
diff --git a/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/DataFormats/Audio/Synthesized/MIDI/MIDICommand.cs b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/DataFormats/Audio/Synthesized/MIDI/MIDICommand.cs
new file mode 100644
index 0000000..4f64d69
--- /dev/null
+++ b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/DataFormats/Audio/Synthesized/MIDI/MIDICommand.cs
@@ -0,0 +1,143 @@
+//
+// MIDICommand.cs - represents a command in a MIDI synthesized audio file
+//
+// Author:
+// Michael Becker
+//
+// Copyright (c) 2019-2020 Mike Becker's Software
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+namespace MBS.Editor.Plugins.Multimedia.DataFormats.Audio.Synthesized.MIDI;
+
+///
+/// Represents a command in a MIDI synthesized audio file.
+///
+public class MIDICommand
+{
+ ///
+ /// Gets or sets the type of the to send.
+ ///
+ /// The type of the to send.
+ public MIDICommandType CommandType
+ {
+ get
+ {
+ MIDICommandType result;
+ switch (Command)
+ {
+ case 0:
+ {
+ result = MIDICommandType.None;
+ return result;
+ }
+ case 8:
+ {
+ result = MIDICommandType.NoteOff;
+ return result;
+ }
+ case 9:
+ {
+ result = MIDICommandType.NoteOn;
+ return result;
+ }
+ case 10:
+ {
+ result = MIDICommandType.KeyAfterTouch;
+ return result;
+ }
+ case 11:
+ {
+ result = MIDICommandType.ControlChange;
+ return result;
+ }
+ case 12:
+ {
+ result = MIDICommandType.ProgramChange;
+ return result;
+ }
+ case 13:
+ {
+ result = MIDICommandType.ChannelAfterTouch;
+ return result;
+ }
+ case 14:
+ {
+ result = MIDICommandType.PitchWheelChange;
+ return result;
+ }
+ }
+ result = MIDICommandType.Unknown;
+ return result;
+ }
+ set
+ {
+ switch (value)
+ {
+ case MIDICommandType.None:
+ {
+ Command = 0;
+ return;
+ }
+ case MIDICommandType.NoteOff:
+ {
+ Command = 8;
+ return;
+ }
+ case MIDICommandType.NoteOn:
+ {
+ Command = 9;
+ return;
+ }
+ case MIDICommandType.KeyAfterTouch:
+ {
+ Command = 10;
+ return;
+ }
+ case MIDICommandType.ControlChange:
+ {
+ Command = 11;
+ return;
+ }
+ case MIDICommandType.ProgramChange:
+ {
+ Command = 12;
+ return;
+ }
+ case MIDICommandType.ChannelAfterTouch:
+ {
+ Command = 13;
+ return;
+ }
+ case MIDICommandType.PitchWheelChange:
+ {
+ Command = 14;
+ return;
+ }
+ }
+ Command = 0;
+ }
+ }
+
+ ///
+ /// Gets or sets the channel on which to send this .
+ ///
+ /// The channel on which to send this .
+ public byte Channel { get; set; } = 0;
+ ///
+ /// Gets or sets the value of the to send.
+ ///
+ /// The value of the to send.
+ public byte Command { get; set; } = 0;
+}
diff --git a/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/DataFormats/Audio/Synthesized/MIDI/MIDICommandType.cs b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/DataFormats/Audio/Synthesized/MIDI/MIDICommandType.cs
new file mode 100644
index 0000000..7f3f2b9
--- /dev/null
+++ b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/DataFormats/Audio/Synthesized/MIDI/MIDICommandType.cs
@@ -0,0 +1,38 @@
+//
+// MIDICommandType.cs - indicates the type of MIDI command
+//
+// Author:
+// Michael Becker
+//
+// Copyright (c) 2019-2020 Mike Becker's Software
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+namespace MBS.Editor.Plugins.Multimedia.DataFormats.Audio.Synthesized.MIDI;
+
+///
+/// Indicates the type of MIDI command.
+///
+public enum MIDICommandType
+{
+ Unknown = -1,
+ None,
+ NoteOff = 8,
+ NoteOn,
+ KeyAfterTouch,
+ ControlChange,
+ ProgramChange,
+ ChannelAfterTouch,
+ PitchWheelChange
+}
diff --git a/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/DataFormats/Audio/Synthesized/MIDI/MIDIDataFormat.cs b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/DataFormats/Audio/Synthesized/MIDI/MIDIDataFormat.cs
new file mode 100644
index 0000000..6d24af3
--- /dev/null
+++ b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/DataFormats/Audio/Synthesized/MIDI/MIDIDataFormat.cs
@@ -0,0 +1,353 @@
+// Copyright (C) 2024 Michael Becker
+//
+// This file is part of editor-dotnet.
+//
+// editor-dotnet is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// editor-dotnet is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with editor-dotnet. If not, see .
+
+using System.Globalization;
+using System.Reflection;
+using MBS.Core;
+using MBS.Editor.Core;
+using MBS.Editor.Core.DataFormats.FileSystem.Chunked;
+using MBS.Editor.Core.IO;
+using MBS.Editor.Core.ObjectModels.Chunked;
+using MBS.Editor.Core.ObjectModels.FileSystem;
+using MBS.Editor.Core.ObjectModels.FileSystem.FileSources;
+using MBS.Editor.Plugins.Multimedia.ObjectModels.Audio;
+using MBS.Editor.Plugins.Multimedia.ObjectModels.Audio.Synthesized;
+
+namespace MBS.Editor.Plugins.Multimedia.DataFormats.Audio.Synthesized.MIDI;
+
+public class MIDIDataFormat : ChunkedDataFormat
+{
+ public MIDIDataFormat()
+ {
+ Endianness = Endianness.BigEndian;
+ }
+
+ protected override void BeforeLoadInternal(Stack objectModels)
+ {
+ base.BeforeLoadInternal(objectModels);
+ objectModels.Push(new ChunkedObjectModel());
+ }
+ protected override void AfterLoadInternal(Stack objectModels)
+ {
+ base.AfterLoadInternal(objectModels);
+
+ ChunkedObjectModel chunked = ObjectModel.CastOrThrow(objectModels.Pop());
+ SynthesizedAudioObjectModel audio = ObjectModel.CastOrThrow(objectModels.Pop());
+
+ System.Collections.Generic.Dictionary notesForNoteNumber = new System.Collections.Generic.Dictionary();
+ foreach (Chunk c in chunked.Chunks)
+ {
+ if (c.Name == "MThd")
+ {
+ Stream s = ((DataChunk)c).Source.GetStream();
+ Reader r = new Reader(s);
+ r.Endianness = Endianness.BigEndian;
+
+ ushort format = r.ReadUInt16();
+ ushort trackCount = r.ReadUInt16();
+ ushort tickdiv = r.ReadUInt16();
+ }
+ else if (c.Name == "MTrk")
+ {
+ int ofs = 0;
+ Stream s = null;
+ try
+ {
+ s = ((DataChunk)c).Source.GetStream();
+ }
+ catch (Exception ex)
+ {
+ return;
+ }
+
+ Reader r = new Reader(s);
+ r.Endianness = Endianness.BigEndian;
+
+ SynthesizedAudioTrack track = new SynthesizedAudioTrack();
+
+ while (!r.BaseStream.EndOfStream())
+ {
+ int deltaTime = r.ReadVariableLengthInt32();
+ MIDIEventType commandAndChannel = (MIDIEventType) r.ReadByte();
+ byte channel = (byte)((byte)commandAndChannel & (byte)MIDIEventType.MIDIChannelMask);
+ MIDIEventType command = (MIDIEventType)((byte)commandAndChannel >> 4);
+
+ if (commandAndChannel == MIDIEventType.Meta)
+ {
+ // this is special cased because it does not include a channel number
+ MIDIMetaEventType metaEventType = (MIDIMetaEventType)r.ReadByte();
+ int length = r.ReadVariableLengthInt32();
+ switch (metaEventType)
+ {
+ case MIDIMetaEventType.Text:
+ {
+ string text = r.ReadFixedLengthString(length).TrimNull();
+ track.Commands.Add(new SynthesizedAudioCommandText(text));
+ break;
+ }
+ case MIDIMetaEventType.CopyrightNotice:
+ {
+ break;
+ }
+ case MIDIMetaEventType.SequenceName:
+ {
+ string text = r.ReadFixedLengthString(length).TrimNull();
+ track.Name = text;
+ break;
+ }
+ case MIDIMetaEventType.ProgramName:
+ {
+ string text = r.ReadFixedLengthString(length).TrimNull();
+ break;
+ }
+ case MIDIMetaEventType.DeviceName:
+ {
+ string text = r.ReadFixedLengthString(length).TrimNull();
+ break;
+ }
+ case MIDIMetaEventType.EndOfTrack:
+ {
+ audio.Tracks.Add(track);
+ break;
+ }
+ case MIDIMetaEventType.TimeSignature:
+ {
+ byte numerator = r.ReadByte();
+ byte denominatorPower = r.ReadByte();
+ byte denominator = (byte)Math.Pow(2.0, denominatorPower);
+ byte ticksPerMetronomeClick = r.ReadByte();
+ byte numberOf32ndNotesPerQuarterNote = r.ReadByte();
+ track.Commands.Add(new SynthesizedAudioCommandTimeSignature(numerator, denominator, ticksPerMetronomeClick, numberOf32ndNotesPerQuarterNote));
+ break;
+ }
+ case MIDIMetaEventType.SetTempo:
+ {
+ int tempo = (int)r.ReadInt24();
+ track.Commands.Add(new SynthesizedAudioCommandTempo((double)tempo));
+ break;
+ }
+ case MIDIMetaEventType.KeySignature:
+ {
+ byte sf = r.ReadByte(); // -7 = 7 flats, -1 = 1 flat, 0 = none (key of C), 1 =
+ bool minorKey = r.ReadBoolean();
+ break;
+ }
+ default:
+ {
+ Console.WriteLine("ue: MIDI: warning: meta event type {0} ({1}) [{2} bytes] unhandled", metaEventType, (byte)metaEventType, length);
+ r.BaseStream.Seek(length, SeekOrigin.Current);
+ break;
+ }
+ }
+ }
+ else
+ {
+ switch (command)
+ {
+ case MIDIEventType.ProgramChange:
+ {
+ byte programNumber = r.ReadByte();
+ break;
+ }
+ case MIDIEventType.NoteOn:
+ {
+ byte noteNumber = r.ReadByte();
+ byte velocity = r.ReadByte();
+
+ if (!notesForNoteNumber.ContainsKey(noteNumber))
+ {
+ notesForNoteNumber[noteNumber] = new SynthesizedAudioCommandNote();
+ }
+ SynthesizedAudioCommandNote note = notesForNoteNumber[noteNumber];
+ note.Frequency = noteNumber;
+ note.Position = deltaTime + ofs;
+ note.Intensity = velocity;
+ ofs += deltaTime;
+ track.Commands.Add(note);
+ break;
+ }
+ case MIDIEventType.NoteOff:
+ {
+ byte noteNumber = r.ReadByte();
+ byte velocity = r.ReadByte();
+
+ if (notesForNoteNumber.ContainsKey(noteNumber))
+ {
+ notesForNoteNumber[noteNumber].Length = deltaTime;
+ notesForNoteNumber.Remove(noteNumber);
+ }
+ break;
+ }
+ case MIDIEventType.ControlChange:
+ {
+ byte controllerNumber = r.ReadByte();
+ byte value = r.ReadByte();
+ // track.Commands.Add(new SynthesizedAudioCommandControlChange(controllerNumber, value));
+ break;
+ }
+ case MIDIEventType.PolyphonicKeyPressureAftertouch:
+ {
+ byte noteNumber = r.ReadByte();
+ byte pressure = r.ReadByte();
+ // track.Commands.Add(new SynthesizedAudioCommandPolyphonicKeyPressureAftertouch(controllerNumber, value));
+ break;
+ }
+ case MIDIEventType.ChannelPressureAftertouch:
+ {
+ byte pressure = r.ReadByte();
+ // track.Commands.Add(new SynthesizedAudioCommandChannelPressureAftertouch(controllerNumber, value));
+ break;
+ }
+ case MIDIEventType.PitchWheelChange:
+ {
+ short value = r.ReadInt16();
+ // track.Commands.Add(new SynthesizedAudioCommandPitchWheel(value));
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private void WriteMIDIMetaEvent(Writer w, int deltaTime, MIDIMetaEventType metaType)
+ {
+ WriteMIDIMetaEvent(w, deltaTime, metaType, (Action?)null);
+ }
+ private void WriteMIDIMetaEvent(Writer w, int deltaTime, MIDIMetaEventType metaType, string data)
+ {
+ WriteMIDIMetaEvent(w, deltaTime, metaType, delegate (Writer w2)
+ {
+ w2.WriteFixedLengthString(data);
+ });
+ }
+ private void WriteMIDIMetaEvent(Writer w, int deltaTime, MIDIMetaEventType metaType, Action? dataFunc = null)
+ {
+ byte[] data;
+ if (dataFunc == null)
+ {
+ data = new byte[0];
+ }
+ else
+ {
+ MemoryStream ms = new MemoryStream();
+ Writer w2 = new Writer(ms);
+
+ dataFunc(w2);
+
+ ms.Close();
+ data = ms.ToArray();
+ }
+
+ w.WriteVariableLengthInt32(deltaTime);
+ w.WriteByte((byte)MIDIEventType.Meta);
+ w.WriteByte((byte)metaType);
+ w.WriteVariableLengthInt32(data.Length);
+ w.WriteBytes(data);
+ }
+
+ protected override void BeforeSaveInternal(Stack objectModels)
+ {
+ base.BeforeSaveInternal(objectModels);
+
+ SynthesizedAudioObjectModel audio = ObjectModel.CastOrThrow(objectModels.Pop());
+
+ ChunkedObjectModel chunked = new ChunkedObjectModel();
+
+ DataChunk MThd = new DataChunk("MThd");
+ chunked.Chunks.Add(MThd);
+
+ foreach (SynthesizedAudioTrack track in audio.Tracks)
+ {
+ DataChunk MTrk = new DataChunk("MTrk");
+
+ MemoryStream ms = new MemoryStream();
+ Writer w = new Writer(ms);
+
+ if (track.Name != null)
+ {
+ WriteMIDIMetaEvent(w, 0, MIDIMetaEventType.SequenceName, track.Name);
+ }
+
+ foreach (SynthesizedAudioCommand command in track.Commands)
+ {
+ WriteCommand(w, command);
+ }
+
+
+ WriteMIDIMetaEvent(w, 0, MIDIMetaEventType.EndOfTrack);
+
+ ms.Close();
+
+ MTrk.Source = new ByteArrayFileSource(ms.ToArray());
+ chunked.Chunks.Add(MTrk);
+ }
+
+ objectModels.Push(chunked);
+ }
+
+ private void WriteCommand(Writer w, SynthesizedAudioCommand command)
+ {
+ if (command is SynthesizedAudioCommandNote note)
+ {
+ WriteMIDIEvent(w, 0, MIDIEventType.NoteOn, delegate(Writer w)
+ {
+ byte noteNumber = (byte)(note.Frequency);
+ byte velocity = 0;
+ w.WriteByte(noteNumber);
+ w.WriteByte(velocity);
+ });
+ }
+ else if (command is SynthesizedAudioCommandTempo tempo)
+ {
+ WriteMIDIMetaEvent(w, 0, MIDIMetaEventType.SetTempo, delegate (Writer w)
+ {
+ w.WriteInt24((int)(tempo.Tempo));
+ });
+ }
+ else if (command is SynthesizedAudioCommandTimeSignature ts)
+ {
+ WriteMIDIMetaEvent(w, 0, MIDIMetaEventType.TimeSignature, delegate (Writer w)
+ {
+ byte numerator = (byte)ts.Numerator;
+ w.WriteByte(numerator);
+
+ byte denominatorPower = (byte)Math.Log2((byte)ts.Denominator);
+ w.WriteByte(denominatorPower);
+
+ byte ticksPerMetronomeClick = (byte)ts.TicksPerMetronomeClick;
+ w.WriteByte(ticksPerMetronomeClick);
+
+ byte numberOf32ndNotesPerQuarterNote = (byte)ts.NumberOf32ndNotesPerQuarterNote;
+ w.WriteByte(numberOf32ndNotesPerQuarterNote);
+ });
+ }
+ }
+
+ private void WriteMIDIEvent(Writer w, int deltaTime, MIDIEventType eventType, Action func)
+ {
+ w.WriteVariableLengthInt32(deltaTime);
+
+ byte channel = 0;
+ MIDIEventType type = MIDIEventType.NoteOn;
+ byte commandAndChannel = (byte)type;
+ w.WriteByte(commandAndChannel);
+
+ func(w);
+ }
+}
\ No newline at end of file
diff --git a/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/DataFormats/Audio/Synthesized/MIDI/MIDIEventType.cs b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/DataFormats/Audio/Synthesized/MIDI/MIDIEventType.cs
new file mode 100644
index 0000000..d33e7d4
--- /dev/null
+++ b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/DataFormats/Audio/Synthesized/MIDI/MIDIEventType.cs
@@ -0,0 +1,46 @@
+//
+// MIDIEventType.cs - indicates the type of MIDI event
+//
+// Author:
+// Michael Becker
+//
+// Copyright (c) 2019 Mike Becker's Software
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+namespace MBS.Editor.Plugins.Multimedia.DataFormats.Audio.Synthesized.MIDI;
+
+///
+/// Indicates the type of MIDI event.
+///
+public enum MIDIEventType : byte
+{
+ None,
+
+ MIDIChannelMask = 0x0F,
+ MIDIEventMask = 0xF0,
+
+ NoteOff = 0x8,
+ NoteOn = 0x9,
+ PolyphonicKeyPressureAftertouch = 0xA,
+ ControlChange = 0xB,
+ ProgramChange = 0xC,
+ ChannelPressureAftertouch = 0xD,
+ PitchWheelChange = 0xE,
+
+
+ SysEx = 0xF0,
+ Escape = 0xF7,
+ Meta = 0xFF
+}
diff --git a/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/DataFormats/Audio/Synthesized/MIDI/MIDIFileFormatType.cs b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/DataFormats/Audio/Synthesized/MIDI/MIDIFileFormatType.cs
new file mode 100644
index 0000000..9e0d5c9
--- /dev/null
+++ b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/DataFormats/Audio/Synthesized/MIDI/MIDIFileFormatType.cs
@@ -0,0 +1,46 @@
+//
+// MIDIFileFormatType.cs - indicates the type of MIDI file format
+//
+// Author:
+// Michael Becker
+//
+// Copyright (c) 2019-2020 Mike Becker's Software
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+namespace MBS.Editor.Plugins.Multimedia.DataFormats.Audio.Synthesized.MIDI;
+
+///
+/// Indicates the type of MIDI file format.
+///
+public enum MIDIFileFormatType : short
+{
+ ///
+ /// Single track (format 0). Consists of a header-chunk and a single track-chunk. The single track chunk will contain all the
+ /// note and tempo information.
+ ///
+ SingleTrack = 0,
+ ///
+ /// Simultaneous multi-track (format 1). Consists of a header-chunk and one or more track-chunks, with all tracks being played
+ /// simultaneously. The first track of a Format 1 file is special, and is also known as the 'Tempo Map'. It should contain all
+ /// meta-events of the types Time Signature, and Set Tempo. The meta-events Sequence/Track Name, Sequence Number, Marker, and
+ /// SMTPE Offset. should also be on the first track of a Format 1 file.
+ ///
+ SimultaneousMultitrack = 1,
+ ///
+ /// Independent multi-track (format 2). Consists of a header-chunk and one or more track-chunks, where each track represents an
+ /// independent sequence.
+ ///
+ IndependentMultitrack = 2
+}
diff --git a/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/DataFormats/Audio/Synthesized/MIDI/MIDIMetaEventType.cs b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/DataFormats/Audio/Synthesized/MIDI/MIDIMetaEventType.cs
new file mode 100644
index 0000000..ee2eb1c
--- /dev/null
+++ b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/DataFormats/Audio/Synthesized/MIDI/MIDIMetaEventType.cs
@@ -0,0 +1,46 @@
+//
+// MIDIMetaEventType.cs - indicates the type of MIDI meta event
+//
+// Author:
+// Michael Becker
+//
+// Copyright (c) 2019 Mike Becker's Software
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+namespace MBS.Editor.Plugins.Multimedia.DataFormats.Audio.Synthesized.MIDI;
+
+///
+/// Indicates the type of MIDI meta event.
+///
+public enum MIDIMetaEventType : byte
+{
+ SequenceNumber = 0x00,
+ Text = 0x01,
+ CopyrightNotice = 0x02,
+ SequenceName = 0x03,
+ InstrumentName = 0x04,
+ Lyric = 0x05,
+ Marker = 0x06,
+ CuePoint = 0x07,
+ ProgramName = 0x08,
+ DeviceName = 0x09,
+ ChannelPrefix = 0x20,
+ EndOfTrack = 0x2F,
+ SetTempo = 0x51,
+ SMPTEOffset = 0x54,
+ TimeSignature = 0x58,
+ KeySignature = 0x59,
+ SequencerSpecific = 0x7F
+}
diff --git a/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/DataFormats/Audio/Synthesized/RMI/RMIDataFormat.cs b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/DataFormats/Audio/Synthesized/RMI/RMIDataFormat.cs
new file mode 100644
index 0000000..b67fba8
--- /dev/null
+++ b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/DataFormats/Audio/Synthesized/RMI/RMIDataFormat.cs
@@ -0,0 +1,114 @@
+// Copyright (C) 2024 Michael Becker
+//
+// This file is part of editor-dotnet.
+//
+// editor-dotnet is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// editor-dotnet is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with editor-dotnet. If not, see .
+
+
+using MBS.Core;
+using MBS.Editor.Core;
+using MBS.Editor.Core.DataFormats.FileSystem.IFF;
+using MBS.Editor.Core.ObjectModels.Chunked;
+using MBS.Editor.Core.ObjectModels.FileSystem;
+using MBS.Editor.Core.ObjectModels.FileSystem.FileSources;
+using MBS.Editor.Plugins.Multimedia.DataFormats.Audio.Synthesized.MIDI;
+using MBS.Editor.Plugins.Multimedia.ObjectModels.Audio.Synthesized;
+
+namespace MBS.Editor.Plugins.Multimedia.DataFormats.Audio.Synthesized.RMI;
+
+public class RMIDataFormat : RIFFDataFormat
+{
+
+ protected override void BeforeLoadInternal(Stack objectModels)
+ {
+ base.BeforeLoadInternal(objectModels);
+ objectModels.Push(new ChunkedObjectModel());
+ }
+ // f8ffdcb7-e2ec-40b1-aa8a-beeb5076c1c9
+ protected override void AfterLoadInternal(Stack objectModels)
+ {
+ base.AfterLoadInternal(objectModels);
+
+ ChunkedObjectModel chunked = ObjectModel.CastOrThrow(objectModels.Pop());
+ SynthesizedAudioObjectModel audio = ObjectModel.CastOrThrow(objectModels.Pop());
+
+ if (chunked.Chunks.Count == 0)
+ {
+ throw new InvalidDataFormatException("RIFF-MIDI document does not contain any RIFF chunks");
+ }
+
+ GroupChunk? root = chunked.Chunks["RIFF", "RMID"] as GroupChunk;
+ if (root == null)
+ {
+ throw new InvalidDataFormatException("RIFF-MIDI document does not contain an RIFF-RMID group chunk");
+ }
+
+ DataChunk? filData = root.Chunks["data"] as DataChunk;
+ if (filData != null)
+ {
+ byte[]? data = filData.Source?.GetData();
+ if (data != null)
+ {
+ MIDIDataFormat mid = new MIDIDataFormat();
+ Document.Load(audio, mid, filData.Source?.GetStream());
+ }
+ }
+
+ GroupChunk info = (GroupChunk)root.Chunks["LIST", "INFO"];
+ if (info != null)
+ {
+ DataChunk? filIART = info.Chunks["IART"] as DataChunk;
+ if (filIART != null)
+ {
+ byte[]? data = filIART.Source?.GetData();
+ if (data != null)
+ {
+ string artist = System.Text.Encoding.UTF8.GetString(data).TrimNull();
+ audio.Metadata.Artist = artist;
+ }
+ }
+ DataChunk? filICOP = info.Chunks["ICOP"] as DataChunk;
+ if (filICOP != null)
+ {
+ byte[]? data = filICOP.Source?.GetData();
+ if (data != null)
+ {
+ string copyright = System.Text.Encoding.UTF8.GetString(data).TrimNull();
+ audio.Metadata.Copyright = copyright;
+ }
+ }
+ }
+ }
+
+ protected override void BeforeSaveInternal(Stack objectModels)
+ {
+ base.BeforeSaveInternal(objectModels);
+
+ SynthesizedAudioObjectModel audio = ObjectModel.CastOrThrow(objectModels.Pop());
+ ChunkedObjectModel chunked = new ChunkedObjectModel();
+
+ GroupChunk rmid = new GroupChunk("RIFF", "RMID");
+
+ MemoryStream ms = new MemoryStream();
+ MIDIDataFormat mid = new MIDIDataFormat();
+ Document.Save(audio, mid, ms);
+
+ DataChunk data = new DataChunk("data");
+ data.Source = new StreamFileSource(ms);
+ rmid.Chunks.Add(data);
+
+ chunked.Chunks.Add(rmid);
+ objectModels.Push(chunked);
+ }
+}
\ No newline at end of file
diff --git a/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/DataFormats/Picture/ILBM/ILBMCompression.cs b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/DataFormats/Picture/ILBM/ILBMCompression.cs
new file mode 100644
index 0000000..2abd25c
--- /dev/null
+++ b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/DataFormats/Picture/ILBM/ILBMCompression.cs
@@ -0,0 +1,27 @@
+// Copyright (C) 2024 Michael Becker
+//
+// This file is part of editor-dotnet.
+//
+// editor-dotnet is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// editor-dotnet is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with editor-dotnet. If not, see .
+
+namespace MBS.Editor.Plugins.Multimedia.DataFormats.Picture.ILBM;
+
+///
+/// Choice of compression algorithm applied to the rows of all source and mask planes.
+///
+public enum ILBMCompression : byte
+{
+ None = 0x00,
+ ByteRun1 = 0x01
+}
\ No newline at end of file
diff --git a/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/DataFormats/Picture/ILBM/ILBMDataFormat.cs b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/DataFormats/Picture/ILBM/ILBMDataFormat.cs
new file mode 100644
index 0000000..9787549
--- /dev/null
+++ b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/DataFormats/Picture/ILBM/ILBMDataFormat.cs
@@ -0,0 +1,164 @@
+// Copyright (C) 2024 Michael Becker
+//
+// This file is part of editor-dotnet.
+//
+// editor-dotnet is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// editor-dotnet is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with editor-dotnet. If not, see .
+
+using MBS.Core.Drawing;
+using MBS.Editor.Core;
+using MBS.Editor.Core.DataFormats.FileSystem.IFF;
+using MBS.Editor.Core.IO;
+using MBS.Editor.Core.ObjectModels.Chunked;
+using MBS.Editor.Core.ObjectModels.FileSystem;
+using MBS.Editor.Plugins.Multimedia.ObjectModels.Palette;
+using MBS.Editor.Plugins.Multimedia.ObjectModels.Picture;
+
+namespace MBS.Editor.Plugins.Multimedia.DataFormats.Picture.ILBM;
+
+public class ILBMDataFormat : IFFDataFormat
+{
+ public ILBMMasking Masking { get; set; } = ILBMMasking.None;
+ public ILBMCompression Compression { get; set; } = ILBMCompression.None;
+ protected override void BeforeLoadInternal(Stack objectModels)
+ {
+ base.BeforeLoadInternal(objectModels);
+ objectModels.Push(new ChunkedObjectModel());
+ }
+ protected override void AfterLoadInternal(Stack objectModels)
+ {
+ base.AfterLoadInternal(objectModels);
+
+ ObjectModel om = objectModels.Pop();
+ ChunkedObjectModel? fsom = om as ChunkedObjectModel;
+ if (fsom == null)
+ throw new ObjectModelNotSupportedException(typeof(ChunkedObjectModel), om.GetType());
+
+ om = objectModels.Pop();
+ PictureObjectModel? pic = om as PictureObjectModel;
+ if (pic == null)
+ throw new ObjectModelNotSupportedException(typeof(PictureObjectModel), om.GetType());
+
+
+ GroupChunk? fldFORM = fsom.Chunks[0] as GroupChunk;
+ if (fldFORM?.Name != "FORM" || fldFORM?.TypeName != "ILBM")
+ throw new InvalidDataFormatException();
+
+ #region BMHD
+ {
+ DataChunk? filBMHD = fldFORM.Chunks[0] as DataChunk;
+ if (filBMHD?.Name != "BMHD" || filBMHD?.Source == null)
+ throw new InvalidDataFormatException();
+
+ MemoryStream ms = new MemoryStream(filBMHD.Source.GetData());
+ Reader reader = new Reader(ms);
+ reader.Endianness = Endianness.BigEndian;
+
+ // thanks http://etwright.org/lwsdk/docs/filefmts/ilbm.html
+
+ // raster width & height in pixels
+ ushort width = reader.ReadUInt16();
+ ushort height = reader.ReadUInt16();
+ pic.Width = width;
+ pic.Height = height;
+
+ // pixel position for this image
+ ushort x = reader.ReadUInt16();
+ ushort y = reader.ReadUInt16();
+
+ byte nPlanes = reader.ReadByte();
+ ILBMMasking masking = (ILBMMasking)reader.ReadByte();
+ ILBMCompression compression = (ILBMCompression)reader.ReadByte();
+ byte pad1 = reader.ReadByte(); // should be 0
+ ushort transparentColor = reader.ReadUInt16();
+ byte xAspect = reader.ReadByte();
+ byte yAspect = reader.ReadByte();
+ ushort pageWidth = reader.ReadUInt16();
+ ushort pageHeight = reader.ReadUInt16();
+ }
+ #endregion
+ #region CMAP
+ {
+ DataChunk? filCMAP = fldFORM.Chunks["CMAP"] as DataChunk;
+ if (filCMAP != null)
+ {
+ byte[]? data = filCMAP.Source?.GetData();
+ if (data != null)
+ {
+ pic.Palette = new PaletteObjectModel();
+ for (int i = 0; i < data.Length; i += 3)
+ {
+ byte r = data[i];
+ byte g = data[i + 1];
+ byte b = data[i + 2];
+
+ Color color = Color.FromRGBAByte(r, g, b);
+ pic.Palette.Entries.Add(color);
+ }
+ }
+ }
+ }
+ #endregion
+
+ #region BODY
+ {
+
+ System.IO.MemoryStream ms = new MemoryStream();
+ Writer w = new Writer(ms);
+
+ DataChunk? filBODY = fldFORM.Chunks["BODY"] as DataChunk;
+ if (filBODY != null)
+ {
+ byte[]? data = filBODY.Source?.GetData();
+ if (data != null)
+ {
+ for (int i = 0; i < data.Length; i += 3)
+ {
+ sbyte n = (sbyte) data[i];
+ if (n >= 0 && n <= 127)
+ {
+ int count = n + 1;
+ for (int j = 0; j < count; j++)
+ {
+ byte next = data[i + 1];
+ w.WriteByte(next);
+ i++;
+ }
+ }
+ else if (n == -128)
+ {
+ // nop
+ }
+ else
+ {
+ int count = -n;
+ byte next = data[i + 1];
+ for (int j = 0; j < count; j++)
+ {
+ w.WriteByte(next);
+ }
+ i++;
+ }
+ }
+ }
+ }
+ }
+ #endregion
+ }
+
+ protected override void BeforeSaveInternal(Stack objectModels)
+ {
+ base.BeforeSaveInternal(objectModels);
+ }
+
+}
\ No newline at end of file
diff --git a/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/DataFormats/Picture/ILBM/ILBMMasking.cs b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/DataFormats/Picture/ILBM/ILBMMasking.cs
new file mode 100644
index 0000000..cfbdb41
--- /dev/null
+++ b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/DataFormats/Picture/ILBM/ILBMMasking.cs
@@ -0,0 +1,46 @@
+// Copyright (C) 2024 Michael Becker
+//
+// This file is part of editor-dotnet.
+//
+// editor-dotnet is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// editor-dotnet is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with editor-dotnet. If not, see .
+
+namespace MBS.Editor.Plugins.Multimedia.DataFormats.Picture.ILBM;
+
+///
+/// Choice of masking technique.
+///
+public enum ILBMMasking : byte
+{
+ ///
+ /// Designates an opaque rectangular image.
+ ///
+ None = 0x00,
+ ///
+ /// Mask plane is interleaved with the bitplanes in the BODY chunk.
+ ///
+ Mask = 0x01,
+ ///
+ /// Pixels in the source planes matching transparentColor are to be considered "transparent."
+ ///
+ ///
+ /// transparentColor isn't a "color number" since it's matched with numbers formed by the source bitmap rather than the possibly
+ /// deeper destination bitmap. Note that having a transparent color implies ignoring one of the color registers.
+ ///
+ TransparentColor = 0x02,
+ ///
+ /// Indicates the reader may construct a mask by lassoing the image as in MacPaint. To do this, put a 1 pixel border of
+ /// transparentColor around the image rectangle. Then do a seed fill from this border. Filled pixels are to be transparent.
+ ///
+ Lasso = 0x03
+}
\ No newline at end of file
diff --git a/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/ObjectModels/Audio/AudioMetadata.cs b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/ObjectModels/Audio/AudioMetadata.cs
new file mode 100644
index 0000000..27e70eb
--- /dev/null
+++ b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/ObjectModels/Audio/AudioMetadata.cs
@@ -0,0 +1,26 @@
+// Copyright (C) 2024 Michael Becker
+//
+// This file is part of editor-dotnet.
+//
+// editor-dotnet is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// editor-dotnet is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with editor-dotnet. If not, see .
+
+namespace MBS.Editor.Plugins.Multimedia.ObjectModels.Audio;
+
+public class AudioMetadata
+{
+ public string Title { get; set; } = "";
+ public string Artist { get; set; } = "";
+ public string Copyright { get; set; } = "";
+
+}
\ No newline at end of file
diff --git a/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/ObjectModels/Audio/AudioObjectModelBase.cs b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/ObjectModels/Audio/AudioObjectModelBase.cs
new file mode 100644
index 0000000..dabade1
--- /dev/null
+++ b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/ObjectModels/Audio/AudioObjectModelBase.cs
@@ -0,0 +1,26 @@
+// Copyright (C) 2024 Michael Becker
+//
+// This file is part of editor-dotnet.
+//
+// editor-dotnet is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// editor-dotnet is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with editor-dotnet. If not, see .
+
+using MBS.Editor.Core;
+
+namespace MBS.Editor.Plugins.Multimedia.ObjectModels.Audio;
+
+public abstract class AudioObjectModelBase : ObjectModel
+{
+ public AudioMetadata Metadata { get; set; } = new AudioMetadata();
+
+}
\ No newline at end of file
diff --git a/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/ObjectModels/Audio/Synthesized/SynthesizedAudioCommand.cs b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/ObjectModels/Audio/Synthesized/SynthesizedAudioCommand.cs
new file mode 100644
index 0000000..bfd3352
--- /dev/null
+++ b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/ObjectModels/Audio/Synthesized/SynthesizedAudioCommand.cs
@@ -0,0 +1,114 @@
+//
+// SynthesizedAudioCommand.cs - represents a command (e.g. note, control change) in a synthesized audio file
+//
+// Author:
+// Michael Becker
+//
+// Copyright (c) 2011-2020 Mike Becker's Software
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+using System;
+using System.Collections.ObjectModel;
+using MBS.Core;
+
+namespace MBS.Editor.Plugins.Multimedia.ObjectModels.Audio.Synthesized;
+
+///
+/// Represents a command (e.g. note, control change) in a synthesized audio file.
+///
+public class SynthesizedAudioCommand : ICloneable
+{
+ public class SynthesizedAudioCommandCollection : Collection
+ {
+ public SynthesizedAudioCommandRest Add(double length)
+ {
+ SynthesizedAudioCommandRest rest = new SynthesizedAudioCommandRest();
+ rest.Length = length;
+
+ base.Add(rest);
+ return rest;
+ }
+ public SynthesizedAudioCommandNote Add(SynthesizedAudioPredefinedNote note, double length, int octave, float volume)
+ {
+ SynthesizedAudioCommandNote command = new SynthesizedAudioCommandNote();
+ command.Length = length;
+ command.Frequency = SynthesizedAudioPredefinedNoteConverter.GetFrequency(note, octave);
+
+ base.Add(command);
+ return command;
+ }
+
+ public event EventHandler ItemsChanged;
+ protected virtual void OnItemsChanged(EventArgs e)
+ {
+ ItemsChanged?.Invoke(this, e);
+ }
+ protected override void InsertItem(int index, SynthesizedAudioCommand item)
+ {
+ base.InsertItem(index, item);
+ OnItemsChanged(EventArgs.Empty);
+ }
+
+ protected override void RemoveItem(int index)
+ {
+ base.RemoveItem(index);
+ OnItemsChanged(EventArgs.Empty);
+ }
+ protected override void ClearItems()
+ {
+ base.ClearItems();
+ OnItemsChanged(EventArgs.Empty);
+ }
+ protected override void SetItem(int index, SynthesizedAudioCommand item)
+ {
+ base.SetItem(index, item);
+ OnItemsChanged(EventArgs.Empty);
+ }
+
+ public T NextOfType(T item, SeekDirection direction = SeekDirection.Forward) where T : SynthesizedAudioCommand
+ {
+ int index = IndexOf(item);
+ if (index < 0) index = 0;
+ if (index > Count - 1) index = 0;
+
+ if (direction == SeekDirection.Backward)
+ {
+ if (index < 0) index = Count - 1;
+ if (index > Count - 1) index = Count - 1;
+
+ for (int i = index - 1; i >= 0; i--)
+ {
+ if (this[i] is T)
+ return (T)this[i];
+ }
+ return (T)this[Count - 1];
+ }
+ else
+ {
+ for (int i = index + 1; i < Count; i++)
+ {
+ if (this[i] is T)
+ return (T)this[i];
+ }
+ return (T)this[0];
+ }
+ }
+ }
+
+ public virtual object Clone()
+ {
+ return base.MemberwiseClone();
+ }
+}
diff --git a/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/ObjectModels/Audio/Synthesized/SynthesizedAudioCommandNote.cs b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/ObjectModels/Audio/Synthesized/SynthesizedAudioCommandNote.cs
new file mode 100644
index 0000000..1e4e8eb
--- /dev/null
+++ b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/ObjectModels/Audio/Synthesized/SynthesizedAudioCommandNote.cs
@@ -0,0 +1,82 @@
+//
+// SynthesizedAudioCommandNote.cs - represents a note command in a synthesized audio file
+//
+// Author:
+// Michael Becker
+//
+// Copyright (c) 2011-2020 Mike Becker's Software
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+using System;
+
+namespace MBS.Editor.Plugins.Multimedia.ObjectModels.Audio.Synthesized;
+
+///
+/// Represents a note command in a synthesized audio file.
+///
+public class SynthesizedAudioCommandNote : SynthesizedAudioCommand
+{
+ public bool Protected { get; set; } = false;
+ public int Position { get; set; } = 0;
+ public double Length { get; set; } = 0;
+ public string Phoneme { get; set; } = null;
+ public string Lyric { get; set; } = null;
+ public double Frequency { get; set; } = 0;
+ public int PreUtterance { get; set; } = 0;
+ public int VoiceOverlap { get; set; } = 0;
+ public int Intensity { get; set; } = 0;
+ public int Modulation { get; set; } = 0;
+ public int PBType { get; set; } = 0;
+ public double[] Pitches { get; set; } = new double[0];
+ public string[] Envelope { get; set; } = new string[0];
+ public double[] VBR { get; set; } = new double[0];
+ public int Accent { get; set; } = 50;
+ public int PitchBendDepth { get; set; } = 8;
+ public int PitchBendLength { get; set; } = 0;
+ public int Decay { get; set; } = 50;
+ public bool PortamentoFalling { get; set; } = false;
+ public int Opening { get; set; } = 127;
+ public bool PortamentoRising { get; set; } = false;
+ public int VibratoLength { get; set; } = 0;
+ public SynthesizedAudioVibratoType VibratoType { get; set; } = SynthesizedAudioVibratoType.None;
+
+ public override object Clone()
+ {
+ return new SynthesizedAudioCommandNote
+ {
+ Envelope = this.Envelope.Clone() as string[],
+ Intensity = this.Intensity,
+ Length = this.Length,
+ Lyric = this.Lyric,
+ Modulation = this.Modulation,
+ PBType = this.PBType,
+ Phoneme = this.Phoneme,
+ Pitches = this.Pitches.Clone() as double[],
+ PortamentoFalling = this.PortamentoFalling,
+ PortamentoRising = this.PortamentoRising,
+ PreUtterance = this.PreUtterance,
+ Protected = this.Protected,
+ Position = this.Position,
+ Frequency = this.Frequency,
+ VBR = this.VBR.Clone() as double[],
+ VoiceOverlap = this.VoiceOverlap
+ };
+ }
+
+ public override string ToString()
+ {
+ return String.Format("{0} [{1}] <{2}, {3}>", Lyric, Phoneme, Position, Length);
+ }
+}
diff --git a/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/ObjectModels/Audio/Synthesized/SynthesizedAudioCommandRest.cs b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/ObjectModels/Audio/Synthesized/SynthesizedAudioCommandRest.cs
new file mode 100644
index 0000000..62c46ed
--- /dev/null
+++ b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/ObjectModels/Audio/Synthesized/SynthesizedAudioCommandRest.cs
@@ -0,0 +1,29 @@
+//
+// SynthesizedAudioCommandRest.cs - represents a rest in a synthesized audio file
+//
+// Author:
+// Michael Becker
+//
+// Copyright (c) 2011-2020 Mike Becker's Software
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+namespace MBS.Editor.Plugins.Multimedia.ObjectModels.Audio.Synthesized;
+
+///
+/// Represents a rest in a synthesized audio file.
+///
+public class SynthesizedAudioCommandRest : SynthesizedAudioCommandNote
+{
+}
diff --git a/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/ObjectModels/Audio/Synthesized/SynthesizedAudioCommandTempo.cs b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/ObjectModels/Audio/Synthesized/SynthesizedAudioCommandTempo.cs
new file mode 100644
index 0000000..b1e25bc
--- /dev/null
+++ b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/ObjectModels/Audio/Synthesized/SynthesizedAudioCommandTempo.cs
@@ -0,0 +1,49 @@
+//
+// SynthesizedAudioCommandTempo.cs - represents a tempo change command in a synthesized audio file
+//
+// Author:
+// Michael Becker
+//
+// Copyright (c) 2011-2020 Mike Becker's Software
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+namespace MBS.Editor.Plugins.Multimedia.ObjectModels.Audio.Synthesized;
+
+///
+/// Represents a tempo change command in a synthesized audio file.
+///
+public class SynthesizedAudioCommandTempo : SynthesizedAudioCommand
+{
+ public double Tempo { get; set; } = 0.0;
+
+ public SynthesizedAudioCommandTempo()
+ {
+ }
+ public SynthesizedAudioCommandTempo(double tempo)
+ {
+ this.Tempo = tempo;
+ }
+ public override string ToString()
+ {
+ return "MM = " + this.Tempo.ToString() + " BPM";
+ }
+ public override object Clone()
+ {
+ return new SynthesizedAudioCommandTempo
+ {
+ Tempo = this.Tempo
+ };
+ }
+}
diff --git a/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/ObjectModels/Audio/Synthesized/SynthesizedAudioCommandText.cs b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/ObjectModels/Audio/Synthesized/SynthesizedAudioCommandText.cs
new file mode 100644
index 0000000..2e1841c
--- /dev/null
+++ b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/ObjectModels/Audio/Synthesized/SynthesizedAudioCommandText.cs
@@ -0,0 +1,48 @@
+//
+// SynthesizedAudioCommandText.cs - represents a text command in a synthesized audio file
+//
+// Author:
+// Michael Becker
+//
+// Copyright (c) 2011-2020 Mike Becker's Software
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+namespace MBS.Editor.Plugins.Multimedia.ObjectModels.Audio.Synthesized;
+
+///
+/// Represents a text command in a synthesized audio file.
+///
+public class SynthesizedAudioCommandText : SynthesizedAudioCommand
+{
+ public string Text { get; set; } = string.Empty;
+ public SynthesizedAudioCommandText()
+ {
+ }
+ public SynthesizedAudioCommandText(string text)
+ {
+ this.Text = text;
+ }
+ public override string ToString()
+ {
+ return "\"" + this.Text + "\"";
+ }
+ public override object Clone()
+ {
+ return new SynthesizedAudioCommandText
+ {
+ Text = this.Text.Clone() as string
+ };
+ }
+}
diff --git a/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/ObjectModels/Audio/Synthesized/SynthesizedAudioCommandTimeSignature.cs b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/ObjectModels/Audio/Synthesized/SynthesizedAudioCommandTimeSignature.cs
new file mode 100644
index 0000000..b5d255a
--- /dev/null
+++ b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/ObjectModels/Audio/Synthesized/SynthesizedAudioCommandTimeSignature.cs
@@ -0,0 +1,66 @@
+//
+// SynthesizedAudioCommandTimeSignature.cs - represents a time signature change command in a synthesized audio file
+//
+// Author:
+// Michael Becker
+//
+// Copyright (c) 2011-2020 Mike Becker's Software
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+namespace MBS.Editor.Plugins.Multimedia.ObjectModels.Audio.Synthesized;
+
+///
+/// Represents a time signature change command in a synthesized audio file.
+///
+public class SynthesizedAudioCommandTimeSignature : SynthesizedAudioCommand
+{
+ public byte Numerator { get; set; } = 0;
+ public byte Denominator { get; set; } = 0;
+ public byte TicksPerMetronomeClick { get; set; } = 0;
+ public byte NumberOf32ndNotesPerQuarterNote { get; set; } = 0;
+
+ public SynthesizedAudioCommandTimeSignature()
+ {
+ }
+ public SynthesizedAudioCommandTimeSignature(byte numerator, byte denominator, byte ticksPerMetronomeClick, byte numberOf32ndNotesPerQuarterNote)
+ {
+ this.Numerator = numerator;
+ this.Denominator = denominator;
+ this.TicksPerMetronomeClick = ticksPerMetronomeClick;
+ this.NumberOf32ndNotesPerQuarterNote = numberOf32ndNotesPerQuarterNote;
+ }
+ public override string ToString()
+ {
+ return string.Concat(new string[]
+ {
+ "TS = ",
+ this.Numerator.ToString(),
+ "/",
+ this.Denominator.ToString(),
+ "; ♪ = ",
+ this.NumberOf32ndNotesPerQuarterNote.ToString()
+ });
+ }
+ public override object Clone()
+ {
+ return new SynthesizedAudioCommandTimeSignature
+ {
+ Denominator = this.Denominator,
+ NumberOf32ndNotesPerQuarterNote = this.NumberOf32ndNotesPerQuarterNote,
+ Numerator = this.Numerator,
+ TicksPerMetronomeClick = this.TicksPerMetronomeClick
+ };
+ }
+}
diff --git a/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/ObjectModels/Audio/Synthesized/SynthesizedAudioObjectModel.cs b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/ObjectModels/Audio/Synthesized/SynthesizedAudioObjectModel.cs
new file mode 100644
index 0000000..f43ec15
--- /dev/null
+++ b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/ObjectModels/Audio/Synthesized/SynthesizedAudioObjectModel.cs
@@ -0,0 +1,109 @@
+//
+// SynthesizedAudioObjectModel.cs - provides an ObjectModel for manipulating synthesized audio files (e.g. MIDI, VSQ, etc.)
+//
+// Author:
+// Michael Becker
+//
+// Copyright (c) 2011-2020 Mike Becker's Software
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+using System.Collections.Generic;
+using MBS.Editor.Core;
+// using MBS.Editor.Plugins.Multimedia.ObjectModels.Audio.Voicebank;
+
+namespace MBS.Editor.Plugins.Multimedia.ObjectModels.Audio.Synthesized;
+
+///
+/// Provides an for manipulating synthesized audio files (e.g. MIDI, VSQ, etc.).
+///
+public class SynthesizedAudioObjectModel : AudioObjectModelBase
+{
+ /*
+ private static ObjectModelReference _omr = null;
+ protected override ObjectModelReference MakeReferenceInternal()
+ {
+ if (_omr == null)
+ {
+ _omr = base.MakeReferenceInternal();
+ _omr.Path = new string[] { "Multimedia", "Audio", "Synthesized Audio" };
+ }
+ return _omr;
+ }
+ */
+
+ public short ChannelCount { get; set; } = 2;
+
+ public string Name { get; set; } = string.Empty;
+
+ public double Tempo { get; set; } = 120.0;
+
+ public SynthesizedAudioTrack.SynthesizedAudioTrackCollection Tracks { get; } = new SynthesizedAudioTrack.SynthesizedAudioTrackCollection();
+ // public VoicebankObjectModel.VoicebankObjectModelCollection Voices { get; } = new VoicebankObjectModel.VoicebankObjectModelCollection();
+
+ /*
+ private CriteriaObject[] _CriteriaObjects = null;
+
+ private CriteriaProperty PROPERTY_LYRIC = new CriteriaProperty("Lyric", typeof(string));
+ protected override CriteriaObject[] GetCriteriaObjectsInternal()
+ {
+ if (_CriteriaObjects == null)
+ {
+ _CriteriaObjects = new CriteriaObject[]
+ {
+ new CriteriaObject("Note", new CriteriaProperty[]
+ {
+ PROPERTY_LYRIC
+ })
+ };
+ }
+ return _CriteriaObjects;
+ }
+ protected override CriteriaResult[] FindInternal(CriteriaQuery query)
+ {
+ List list = new List();
+ for (int i = 0; i < Tracks.Count; i++)
+ {
+ for (int j = 0; j < Tracks[i].Commands.Count; j++)
+ {
+ if (Tracks[i].Commands[j] is SynthesizedAudioCommandNote)
+ {
+ if (query.Check(PROPERTY_LYRIC, (Tracks[i].Commands[j] as SynthesizedAudioCommandNote).Lyric))
+ {
+ list.Add(new CriteriaResult(Tracks[i].Commands[j]));
+ }
+ }
+ }
+ }
+ return list.ToArray();
+ }
+ */
+
+ public override void CopyTo(ObjectModel destination)
+ {
+ SynthesizedAudioObjectModel clone = destination as SynthesizedAudioObjectModel;
+ clone.Name = (this.Name.Clone() as string);
+ clone.Tempo = this.Tempo;
+ foreach (SynthesizedAudioTrack track in this.Tracks)
+ {
+ clone.Tracks.Add(track.Clone() as SynthesizedAudioTrack);
+ }
+ }
+ public override void Clear()
+ {
+ this.Name = string.Empty;
+ this.Tempo = 120.0;
+ this.Tracks.Clear();
+ }
+}
diff --git a/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/ObjectModels/Audio/Synthesized/SynthesizedAudioPredefinedNote.cs b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/ObjectModels/Audio/Synthesized/SynthesizedAudioPredefinedNote.cs
new file mode 100644
index 0000000..5b268cd
--- /dev/null
+++ b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/ObjectModels/Audio/Synthesized/SynthesizedAudioPredefinedNote.cs
@@ -0,0 +1,98 @@
+//
+// SynthesizedAudioPredefinedNote.cs - defines various commonly-used note pitches for synthesized audio files
+//
+// Author:
+// Michael Becker
+//
+// Copyright (c) 2011-2020 Mike Becker's Software
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+using System;
+
+namespace MBS.Editor.Plugins.Multimedia.ObjectModels.Audio.Synthesized;
+
+///
+/// Defines various commonly-used note pitches for synthesized audio files.
+///
+public enum SynthesizedAudioPredefinedNote
+{
+ A = 0,
+ ASharp,
+ BFlat = ASharp,
+ B,
+ C,
+ CSharp,
+ DFlat = CSharp,
+ D,
+ DSharp,
+ EFlat = DSharp,
+ E,
+ F,
+ FSharp,
+ GFlat = FSharp,
+ G,
+ GSharp,
+ AFlat = GSharp
+}
+///
+/// Converts to and from values and their associated frequencies.
+///
+public static class SynthesizedAudioPredefinedNoteConverter
+{
+ public static double GetFrequency(SynthesizedAudioPredefinedNote note, int octave)
+ {
+ return GetFrequency((int)note, octave);
+ }
+ public static double GetFrequency(int note, int octave)
+ {
+ return GetFrequency(note + ((octave - 4) * 12));
+ }
+ public static double GetFrequency(int note)
+ {
+ /*
+ C5 = the C an octave above middle C. This is 3 half steps above A4 and so the frequency is
+ f3 = 440 * (1.059463..)^3 = 523.3 Hz
+ If your calculator does not have the ability to raise to powers, then use the fact that
+ (1.059463..)^3 = (1.059463..)*(1.059463..)*(1.059463..)
+ That is, you multiply it by itself 3 times.
+
+ Middle C is 9 half steps below A4 and the frequency is:
+ f -9 = 440 * (1.059463..)^(-9) = 261.6 Hz
+ If you don't have powers on your calculator, remember that the negative sign on the power means you divide instead of multiply. For this example, you divide by (1.059463..) 9 times.
+ */
+
+ double d = Math.Pow(2, (double)1 / 12);
+ int halfSteps = note;
+ double value = 440 * Math.Pow(d, halfSteps);
+ return value;
+ }
+
+ public static int GetNote(double frequency)
+ {
+ double d = Math.Pow(2, (double)1 / 12);
+ double halfSteps = Math.Pow(frequency / 440, (1 / d));
+ int note = (int)Math.Round((halfSteps - 1) * 12) + 1;
+ return note;
+ }
+
+ public static double ChangeFrequency(double oldFrequency, int detuneBy)
+ {
+ if (detuneBy == 0) return oldFrequency;
+
+ int note = GetNote(oldFrequency);
+ double freq = GetFrequency(note + detuneBy);
+ return freq;
+ }
+}
diff --git a/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/ObjectModels/Audio/Synthesized/SynthesizedAudioStylePlugin.cs b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/ObjectModels/Audio/Synthesized/SynthesizedAudioStylePlugin.cs
new file mode 100644
index 0000000..219b54a
--- /dev/null
+++ b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/ObjectModels/Audio/Synthesized/SynthesizedAudioStylePlugin.cs
@@ -0,0 +1,63 @@
+//
+// SynthesizedAudioStylePlugin.cs
+//
+// Author:
+// Michael Becker
+//
+// Copyright (c) 2011-2020 Mike Becker's Software
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+using System;
+namespace MBS.Editor.Plugins.Multimedia.ObjectModels.Audio.Synthesized;
+
+public class SynthesizedAudioStylePlugin
+{
+ private Guid mvarID = Guid.Empty;
+ private string mvarName = string.Empty;
+ private Version mvarVersion = new Version(1, 0);
+ public Guid ID
+ {
+ get
+ {
+ return this.mvarID;
+ }
+ set
+ {
+ this.mvarID = value;
+ }
+ }
+ public string Name
+ {
+ get
+ {
+ return this.mvarName;
+ }
+ set
+ {
+ this.mvarName = value;
+ }
+ }
+ public Version Version
+ {
+ get
+ {
+ return this.mvarVersion;
+ }
+ set
+ {
+ this.mvarVersion = value;
+ }
+ }
+}
diff --git a/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/ObjectModels/Audio/Synthesized/SynthesizedAudioTrack.cs b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/ObjectModels/Audio/Synthesized/SynthesizedAudioTrack.cs
new file mode 100644
index 0000000..5693d9a
--- /dev/null
+++ b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/ObjectModels/Audio/Synthesized/SynthesizedAudioTrack.cs
@@ -0,0 +1,101 @@
+//
+// SynthesizedAudioTrack.cs - represents a track in a synthesized audio file
+//
+// Author:
+// Michael Becker
+//
+// Copyright (c) 2011-2020 Mike Becker's Software
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+
+using MBS.Core.Drawing;
+
+// using MBS.Editor.Plugins.Multimedia.ObjectModels.Audio.Voicebank;
+
+namespace MBS.Editor.Plugins.Multimedia.ObjectModels.Audio.Synthesized;
+
+///
+/// Represents a track in a synthesized audio file.
+///
+public class SynthesizedAudioTrack : ICloneable
+{
+ public class SynthesizedAudioTrackCollection : Collection
+ {
+ private Dictionary tracksByID = new Dictionary();
+ public SynthesizedAudioTrack this[string ID]
+ {
+ get
+ {
+ SynthesizedAudioTrack result;
+ if (this.tracksByID.ContainsKey(ID))
+ {
+ result = this.tracksByID[ID];
+ }
+ else
+ {
+ result = null;
+ }
+ return result;
+ }
+ }
+
+ protected override void InsertItem(int index, SynthesizedAudioTrack item)
+ {
+ base.InsertItem(index, item);
+
+ if (!string.IsNullOrEmpty(item.ID))
+ tracksByID[item.ID] = item;
+ }
+ protected override void RemoveItem(int index)
+ {
+ if (!string.IsNullOrEmpty(this[index].ID))
+ tracksByID.Remove(this[index].ID);
+
+ base.RemoveItem(index);
+ }
+ protected override void ClearItems()
+ {
+ base.ClearItems();
+ tracksByID.Clear();
+ }
+ }
+
+ public string ID { get; set; } = string.Empty;
+ public string Name { get; set; } = string.Empty;
+ public string Comment { get; set; } = string.Empty;
+ public SynthesizedAudioCommand.SynthesizedAudioCommandCollection Commands { get; } = new SynthesizedAudioCommand.SynthesizedAudioCommandCollection();
+ // public VoicebankObjectModel Synthesizer { get; set; } = null;
+ public bool IsMuted { get; set; } = false;
+ public bool IsSolo { get; set; } = false;
+ public byte Panpot { get; set; } = 64;
+ public byte Volume { get; set; } = 0;
+ public object Clone()
+ {
+ SynthesizedAudioTrack clone = new SynthesizedAudioTrack();
+ foreach (SynthesizedAudioCommand command in this.Commands)
+ {
+ clone.Commands.Add(command.Clone() as SynthesizedAudioCommand);
+ }
+ clone.ID = (this.ID.Clone() as string);
+ clone.Name = (this.Name.Clone() as string);
+ return clone;
+ }
+
+ public double Tempo { get; set; } = 120;
+ public Color Color { get; set; } = Color.Empty;
+}
diff --git a/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/ObjectModels/Audio/Synthesized/SynthesizedAudioVibratoType.cs b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/ObjectModels/Audio/Synthesized/SynthesizedAudioVibratoType.cs
new file mode 100644
index 0000000..987d672
--- /dev/null
+++ b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/ObjectModels/Audio/Synthesized/SynthesizedAudioVibratoType.cs
@@ -0,0 +1,30 @@
+//
+// SynthesizedAudioVibratoType.cs - indicates the type of vibrato applied to a note in a synthesized audio file
+//
+// Author:
+// Michael Becker
+//
+// Copyright (c) 2011-2020 Mike Becker's Software
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+namespace MBS.Editor.Plugins.Multimedia.ObjectModels.Audio.Synthesized;
+
+///
+/// Indicates the type of vibrato applied to a note in a synthesized audio file.
+///
+public enum SynthesizedAudioVibratoType
+{
+ None
+}
diff --git a/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/ObjectModels/Palette/PaletteEntry.cs b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/ObjectModels/Palette/PaletteEntry.cs
new file mode 100644
index 0000000..a4f999f
--- /dev/null
+++ b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/ObjectModels/Palette/PaletteEntry.cs
@@ -0,0 +1,71 @@
+//
+// PaletteEntry.cs - represents a color entry in a palette
+//
+// Author:
+// Michael Becker
+//
+// Copyright (c) 2011-2020 Mike Becker's Software
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+using System;
+using MBS.Core.Drawing;
+
+namespace MBS.Editor.Plugins.Multimedia.ObjectModels.Palette
+{
+ ///
+ /// Represents a color entry in a palette.
+ ///
+ public class PaletteEntry : ICloneable
+ {
+ public class PaletteEntryCollection
+ : System.Collections.ObjectModel.Collection
+ {
+ public PaletteEntry Add(Color color, string colorName = "")
+ {
+ PaletteEntry entry = new PaletteEntry();
+ entry.Name = colorName;
+ entry.Color = color;
+
+ Add(entry);
+ return entry;
+ }
+ }
+
+ public PaletteEntry(Color color = default(Color), string name = "")
+ {
+ Name = name;
+ Color = color;
+ }
+
+ ///
+ /// Gets or sets the name of this .
+ ///
+ /// The name of this .
+ public string Name { get; set; } = String.Empty;
+ ///
+ /// Gets or sets the color of this .
+ ///
+ /// The color of this .
+ public Color Color { get; set; } = Color.Empty;
+
+ public object Clone()
+ {
+ PaletteEntry clone = new PaletteEntry();
+ clone.Name = Name;
+ clone.Color = Color;
+ return clone;
+ }
+ }
+}
diff --git a/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/ObjectModels/Palette/PaletteObjectModel.cs b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/ObjectModels/Palette/PaletteObjectModel.cs
new file mode 100644
index 0000000..7a7a7a6
--- /dev/null
+++ b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/ObjectModels/Palette/PaletteObjectModel.cs
@@ -0,0 +1,85 @@
+//
+// PaletteObjectModel.cs - provides an ObjectModel for manipulating color palettes
+//
+// Author:
+// Michael Becker
+//
+// Copyright (c) 2011-2020 Mike Becker's Software
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+using System;
+using MBS.Editor.Core;
+
+namespace MBS.Editor.Plugins.Multimedia.ObjectModels.Palette
+{
+ ///
+ /// Provides an for manipulating color palettes.
+ ///
+ public class PaletteObjectModel : ObjectModel
+ {
+ /*
+ private static ObjectModelReference _omr = null;
+ protected override ObjectModelReference MakeReferenceInternal()
+ {
+ if (_omr == null)
+ {
+ _omr = new ObjectModelReference(GetType(), new Guid("{c852b1d7-d034-43b4-b850-00e583e47fef}"));
+ _omr.Path = new string[] { "Multimedia", "Color palette" };
+ }
+ return _omr;
+ }
+ */
+
+ public PaletteObjectModel()
+ {
+ }
+ public PaletteObjectModel(PaletteEntry[] entries)
+ {
+ if (entries != null)
+ {
+ for (int i = 0; i < entries.Length; i++)
+ {
+ Entries.Add(entries[i]);
+ }
+ }
+ }
+
+ public override void Clear()
+ {
+ Entries.Clear();
+ }
+
+ public override void CopyTo(ObjectModel where)
+ {
+ PaletteObjectModel clone = (where as PaletteObjectModel);
+ clone.Name = (Name.Clone() as string);
+ for (int i = 0; i < Entries.Count; i++)
+ {
+ clone.Entries.Add(Entries[i].Clone() as PaletteEntry);
+ }
+ }
+
+ ///
+ /// Gets or sets the name of this palette.
+ ///
+ /// The name of this palette.
+ public string Name { get; set; } = String.Empty;
+ ///
+ /// Gets a collection of instances representing the color entries in this palette.
+ ///
+ /// The color entries in this palette.
+ public PaletteEntry.PaletteEntryCollection Entries { get; } = new PaletteEntry.PaletteEntryCollection();
+ }
+}
diff --git a/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/ObjectModels/Picture/PictureObjectModel.cs b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/ObjectModels/Picture/PictureObjectModel.cs
new file mode 100644
index 0000000..3af1a35
--- /dev/null
+++ b/editor-dotnet/src/plugins/MBS.Editor.Plugins.Multimedia/ObjectModels/Picture/PictureObjectModel.cs
@@ -0,0 +1,55 @@
+//
+// Copyright (C) 2024 Michael Becker
+//
+// This file is part of editor-dotnet.
+//
+// editor-dotnet is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// editor-dotnet is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with editor-dotnet. If not, see .
+//
+
+using System.Numerics;
+using MBS.Core.Drawing;
+using MBS.Editor.Core;
+using MBS.Editor.Plugins.Multimedia.ObjectModels.Palette;
+
+namespace MBS.Editor.Plugins.Multimedia.ObjectModels.Picture;
+
+public class PictureObjectModel : ObjectModel
+{
+ private Dictionary pixels = new Dictionary();
+
+ public PaletteObjectModel? Palette { get; set; } = null;
+
+ public int Width { get; set; }
+ public int Height { get; set; }
+
+ public IEnumerable> GetPixels()
+ {
+ return pixels;
+ }
+
+ public Color GetPixel(int x, int y)
+ {
+ Vector2D key = new Vector2D(x, y);
+ if (pixels.ContainsKey(key))
+ {
+ return pixels[key];
+ }
+ return Color.Empty;
+ }
+
+ public void SetPixel(int x, int y, Color color)
+ {
+ pixels[new Vector2D(x, y)] = color;
+ }
+}
\ No newline at end of file
diff --git a/editor-dotnet/src/tests/MBS.Editor.Core.Tests/DataFormats/FileSystem/IFF/IFFDataFormatTests.cs b/editor-dotnet/src/tests/MBS.Editor.Core.Tests/DataFormats/FileSystem/IFF/IFFDataFormatTests.cs
new file mode 100644
index 0000000..80104ec
--- /dev/null
+++ b/editor-dotnet/src/tests/MBS.Editor.Core.Tests/DataFormats/FileSystem/IFF/IFFDataFormatTests.cs
@@ -0,0 +1,49 @@
+// Copyright (C) 2024 Michael Becker
+//
+// This file is part of editor-dotnet.
+//
+// editor-dotnet is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// editor-dotnet is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with editor-dotnet. If not, see .
+
+using MBS.Editor.Core.DataFormats.FileSystem.IFF;
+using MBS.Editor.Core.ObjectModels.Chunked;
+using MBS.Editor.Core.ObjectModels.FileSystem;
+using MBS.Editor.Testing;
+
+namespace MBS.Editor.Core.Tests.DataFormats.FileSystem.IFF;
+
+[TestFixture]
+public class IFFDataFormatTests : EmbeddedResourceTest
+{
+
+ [Test]
+ public void CheckVenusTotalSize()
+ {
+ if (TryCreateResourceStream("MBS.Editor.Core.Tests.Resources.TestData.DataFormats.FileSystem.IFF.VENUS.IFF", out Stream? st))
+ {
+ if (st == null)
+ return;
+
+ ChunkedObjectModel fsom = new ChunkedObjectModel();
+
+
+ IFFDataFormat iff = new IFFDataFormat();
+ Document.Load(fsom, iff, st);
+
+ Assert.That(fsom.Chunks.Count, Is.EqualTo(1));
+ Assert.That((fsom.Chunks[0] as GroupChunk).Chunks.Count, Is.EqualTo(3));
+ }
+ }
+
+
+}
\ No newline at end of file
diff --git a/editor-dotnet/src/tests/MBS.Editor.Core.Tests/MBS.Editor.Core.Tests.csproj b/editor-dotnet/src/tests/MBS.Editor.Core.Tests/MBS.Editor.Core.Tests.csproj
index 050e4ca..92af4a8 100644
--- a/editor-dotnet/src/tests/MBS.Editor.Core.Tests/MBS.Editor.Core.Tests.csproj
+++ b/editor-dotnet/src/tests/MBS.Editor.Core.Tests/MBS.Editor.Core.Tests.csproj
@@ -6,6 +6,10 @@
false
true
+
+
+
+
@@ -15,5 +19,6 @@
+
\ No newline at end of file
diff --git a/editor-dotnet/src/tests/MBS.Editor.Core.Tests/ObjectModels/FileSystem/FileSystemItemCollectionTests.cs b/editor-dotnet/src/tests/MBS.Editor.Core.Tests/ObjectModels/FileSystem/FileSystemItemCollectionTests.cs
index de922a8..5f68bd9 100644
--- a/editor-dotnet/src/tests/MBS.Editor.Core.Tests/ObjectModels/FileSystem/FileSystemItemCollectionTests.cs
+++ b/editor-dotnet/src/tests/MBS.Editor.Core.Tests/ObjectModels/FileSystem/FileSystemItemCollectionTests.cs
@@ -5,7 +5,7 @@ namespace MBS.Editor.Core.Tests.ObjectModels.FileSystem;
public class FileSystemItemCollectionTests
{
- [ Test()]
+ [Test()]
public static void AddFolderWithEmptyNameTest()
{
// the reason this test exists
diff --git a/editor-dotnet/src/tests/MBS.Editor.Core.Tests/ObjectModels/PropertyList/INIDataFormatTests.cs b/editor-dotnet/src/tests/MBS.Editor.Core.Tests/ObjectModels/PropertyList/INIDataFormatTests.cs
new file mode 100644
index 0000000..087f57b
--- /dev/null
+++ b/editor-dotnet/src/tests/MBS.Editor.Core.Tests/ObjectModels/PropertyList/INIDataFormatTests.cs
@@ -0,0 +1,80 @@
+namespace MBS.Editor.Core.Tests.ObjectModels.PropertyList;
+
+using System.Text;
+using MBS.Editor.Core.ObjectModels.PropertyList;
+using MBS.Editor.Testing;
+
+public class INIDataFormatTests : EmbeddedResourceTest
+{
+ private PropertyListObjectModel CreateSimplePropertyListWithSections()
+ {
+ PropertyListObjectModel plom = new PropertyListObjectModel();
+
+ PropertyListProperty prop = new PropertyListProperty("Test 1", 3127);
+ plom.Items.Add(prop);
+
+ prop = new PropertyListProperty("String Property", "This is a Test");
+ plom.Items.Add(prop);
+
+ PropertyListGroup group = new PropertyListGroup("Section 2");
+ group.Items.Add(new PropertyListProperty("Property 2", 923.1072));
+ plom.Items.Add(group);
+
+ return plom;
+ }
+
+ [Test()]
+ public void INIDataFormatTest()
+ {
+ PropertyListObjectModel plom = CreateSimplePropertyListWithSections();
+ INIDataFormat ini = new INIDataFormat();
+
+ int old_count = plom.Items.Count;
+
+ MemoryStream ms = new MemoryStream();
+ Document.Save(plom, ini, ms);
+ ms.Flush();
+
+ byte[] data = ms.ToArray();
+ ms = new MemoryStream(data);
+
+ plom = new PropertyListObjectModel();
+ ini = new INIDataFormat();
+
+ Document.Load(plom, ini, ms);
+
+ Assert.That(plom.Items.Count, Is.EqualTo(old_count));
+ }
+
+ [Test()]
+ public void ReallyBadINIDataFormatTest()
+ {
+ if (TryCreateResourceStream("Resources.TestData.ObjectModels.PropertyList.INI.CompleteTest.ini", out Stream? st))
+ {
+ if (st == null)
+ return;
+
+ PropertyListObjectModel plom = new PropertyListObjectModel();
+ INIDataFormat ini = new INIDataFormat();
+ Document.Load(plom, ini, st);
+
+ Assert.That(plom.Items.Count == 4); // comments are included by default
+ Assert.That(plom.Items[0], Is.InstanceOf());
+
+ Assert.That(plom.Items[1], Is.InstanceOf());
+
+ PropertyListGroup? g = plom.Items[1] as PropertyListGroup;
+ if (g != null)
+ {
+ Assert.That(g.Items.Count, Is.EqualTo(3));
+ Assert.That(g.Items[0].Name, Is.EqualTo("Property1"));
+
+ PropertyListProperty? prop = g.Items[1] as PropertyListProperty;
+ Assert.That(prop, Is.Not.Null);
+ Assert.That(prop.Value, Is.Not.Null);
+ Assert.That(prop.Value, Is.EqualTo("This is a property with quotes."));
+ }
+ }
+ }
+
+}
diff --git a/editor-dotnet/src/tests/MBS.Editor.Core.Tests/Resources/TestData/DataFormats/FileSystem/IFF/VENUS.IFF b/editor-dotnet/src/tests/MBS.Editor.Core.Tests/Resources/TestData/DataFormats/FileSystem/IFF/VENUS.IFF
new file mode 100644
index 0000000..443778e
Binary files /dev/null and b/editor-dotnet/src/tests/MBS.Editor.Core.Tests/Resources/TestData/DataFormats/FileSystem/IFF/VENUS.IFF differ
diff --git a/editor-dotnet/src/tests/MBS.Editor.Core.Tests/Resources/TestData/ObjectModels/PropertyList/INI/CompleteTest.ini b/editor-dotnet/src/tests/MBS.Editor.Core.Tests/Resources/TestData/ObjectModels/PropertyList/INI/CompleteTest.ini
new file mode 100644
index 0000000..a96d07c
--- /dev/null
+++ b/editor-dotnet/src/tests/MBS.Editor.Core.Tests/Resources/TestData/ObjectModels/PropertyList/INI/CompleteTest.ini
@@ -0,0 +1,16 @@
+; this is a really bad INI file which should be able
+; to be completely parsed by MBS Editor
+
+[Section1]
+Property1=Value2
+PropertyWithQuotes="This is a property with quotes."
+PropertyWithoutQuotes=Quotes really do not matter in INI files.
+
+[Section with a space in its name]
+; this should work, and this comment should be included
+; in the PropertyListObjectModel
+Property Key with Space=Another unquoted value
+What happens when=there are multiple equal=values it should be=just the first one
+
+[This is [probably a [really bad] Idea]]
+Version=1.0.50726.42
diff --git a/editor-dotnet/src/tests/MBS.Editor.Plugins.CRI.Tests/DataFormats/CPK/CPKDataFormatTests.cs b/editor-dotnet/src/tests/MBS.Editor.Plugins.CRI.Tests/DataFormats/CPK/CPKDataFormatTests.cs
index a2678c9..03b310e 100644
--- a/editor-dotnet/src/tests/MBS.Editor.Plugins.CRI.Tests/DataFormats/CPK/CPKDataFormatTests.cs
+++ b/editor-dotnet/src/tests/MBS.Editor.Plugins.CRI.Tests/DataFormats/CPK/CPKDataFormatTests.cs
@@ -5,22 +5,17 @@ using MBS.Editor.Core;
using MBS.Editor.Core.ObjectModels.FileSystem;
using MBS.Editor.Core.ObjectModels.FileSystem.FileSources;
using MBS.Editor.Plugins.CRI.DataFormats.FileSystem.CPK;
+using MBS.Editor.Testing;
using NUnit.Framework.Internal;
namespace MBS.Editor.Plugins.CRI.Tests.DataFormats.CPK;
-public class CPKDataFormatTests
+public class CPKDataFormatTests : EmbeddedResourceTest
{
- private System.Collections.Generic.Dictionary testDataStreams = new System.Collections.Generic.Dictionary();
-
[SetUp]
public void Setup()
{
- MBS.Core.Reflection.ManifestResourceStream[] strms = MBS.Core.Reflection.ManifestResourceStream.GetManifestResourceStreamsForAssembly(System.Reflection.Assembly.GetExecutingAssembly());
- for (int i = 0; i < strms.Length; i++)
- {
- testDataStreams[strms[i].Name] = strms[i].Stream;
- }
+ InitializeResources();
}
[Test(Description = "Ensures the CPKDataFormat can store exactly one file, uncompressed, with no filename masking.")]
@@ -57,20 +52,6 @@ public class CPKDataFormatTests
Assert.That(System_dat.Source.GetData(2, 1)[0], Is.EqualTo(0xBA));
}
-
- private void SampleStreamTest(string streamName)
- {
- if (testDataStreams.ContainsKey(streamName))
- {
- System.IO.Stream TEST_STREAM = testDataStreams[streamName];
- SampleStreamTest(TEST_STREAM);
- }
- else
- {
- Console.Error.WriteLine("test data stream not found: '" + streamName + "'");
- Assert.Ignore();
- }
- }
private void SampleStreamTest(System.IO.Stream stream)
{
stream.Seek(0, System.IO.SeekOrigin.Begin);
diff --git a/editor-dotnet/src/tests/MBS.Editor.Plugins.CRI.Tests/DataFormats/Database/UTF/UTFDataFormatTests.cs b/editor-dotnet/src/tests/MBS.Editor.Plugins.CRI.Tests/DataFormats/Database/UTF/UTFDataFormatTests.cs
new file mode 100644
index 0000000..27416c1
--- /dev/null
+++ b/editor-dotnet/src/tests/MBS.Editor.Plugins.CRI.Tests/DataFormats/Database/UTF/UTFDataFormatTests.cs
@@ -0,0 +1,8 @@
+using System;
+
+namespace MBS.Editor.Plugins.CRI.Tests.DataFormats.Database.UTF;
+
+public class UTFDataFormatTests
+{
+
+}
diff --git a/editor-dotnet/src/tests/MBS.Editor.Plugins.CRI.Tests/MBS.Editor.Plugins.CRI.Tests.csproj b/editor-dotnet/src/tests/MBS.Editor.Plugins.CRI.Tests/MBS.Editor.Plugins.CRI.Tests.csproj
index cd028c0..8a81931 100644
--- a/editor-dotnet/src/tests/MBS.Editor.Plugins.CRI.Tests/MBS.Editor.Plugins.CRI.Tests.csproj
+++ b/editor-dotnet/src/tests/MBS.Editor.Plugins.CRI.Tests/MBS.Editor.Plugins.CRI.Tests.csproj
@@ -16,6 +16,7 @@
+
diff --git a/editor-dotnet/src/tests/MBS.Editor.Plugins.Mekada.Tests/DataFormats/MEK/MEKDataFormatTests.cs b/editor-dotnet/src/tests/MBS.Editor.Plugins.Mekada.Tests/DataFormats/MEK/MEKDataFormatTests.cs
new file mode 100644
index 0000000..c28e646
--- /dev/null
+++ b/editor-dotnet/src/tests/MBS.Editor.Plugins.Mekada.Tests/DataFormats/MEK/MEKDataFormatTests.cs
@@ -0,0 +1,59 @@
+using MBS.Editor.Core;
+using MBS.Editor.Plugins.Mekada.DataFormats.Mechanic;
+using MBS.Editor.Testing;
+
+namespace MBS.Editor.Plugins.Mekada.Tests;
+
+public class MEKDataFormatTests : EmbeddedResourceTest
+{
+ [SetUp]
+ public void Setup()
+ {
+ InitializeResources();
+ }
+
+ [Test]
+ public void Test1()
+ {
+ if (TryCreateResourceStream("MBS.Editor.Plugins.Mekada.Tests.Resources.TestData.Mechanics.0.mek", out Stream? st))
+ {
+ MechanicObjectModel mekom = new MechanicObjectModel();
+ MEKDataFormat mekdf = new MEKDataFormat();
+ Document.Load(mekom, mekdf, st);
+ }
+ else
+ {
+ Assert.Ignore();
+ }
+ }
+
+ [Test]
+ public void Test2()
+ {
+ if (TryCreateResourceStream("MBS.Editor.Plugins.Mekada.Tests.Resources.TestData.Mechanics.1.mek", out Stream? st))
+ {
+ MechanicObjectModel mekom = new MechanicObjectModel();
+ MEKDataFormat mekdf = new MEKDataFormat();
+ Document.Load(mekom, mekdf, st);
+ }
+ else
+ {
+ Assert.Ignore();
+ }
+ }
+
+ [Test]
+ public void WithCarInLot()
+ {
+ if (TryCreateResourceStream("MBS.Editor.Plugins.Mekada.Tests.Resources.TestData.Mechanics.WithCarInLot.mek", out Stream? st))
+ {
+ MechanicObjectModel mekom = new MechanicObjectModel();
+ MEKDataFormat mekdf = new MEKDataFormat();
+ Document.Load(mekom, mekdf, st);
+ }
+ else
+ {
+ Assert.Ignore();
+ }
+ }
+}
\ No newline at end of file
diff --git a/editor-dotnet/src/tests/MBS.Editor.Plugins.Mekada.Tests/GlobalUsings.cs b/editor-dotnet/src/tests/MBS.Editor.Plugins.Mekada.Tests/GlobalUsings.cs
new file mode 100644
index 0000000..cefced4
--- /dev/null
+++ b/editor-dotnet/src/tests/MBS.Editor.Plugins.Mekada.Tests/GlobalUsings.cs
@@ -0,0 +1 @@
+global using NUnit.Framework;
\ No newline at end of file
diff --git a/editor-dotnet/src/tests/MBS.Editor.Plugins.Mekada.Tests/MBS.Editor.Plugins.Mekada.Tests.csproj b/editor-dotnet/src/tests/MBS.Editor.Plugins.Mekada.Tests/MBS.Editor.Plugins.Mekada.Tests.csproj
new file mode 100644
index 0000000..98421d3
--- /dev/null
+++ b/editor-dotnet/src/tests/MBS.Editor.Plugins.Mekada.Tests/MBS.Editor.Plugins.Mekada.Tests.csproj
@@ -0,0 +1,39 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+ false
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/editor-dotnet/src/tests/MBS.Editor.Plugins.Mekada.Tests/Resources/TestData/Mechanics/0.mek b/editor-dotnet/src/tests/MBS.Editor.Plugins.Mekada.Tests/Resources/TestData/Mechanics/0.mek
new file mode 100644
index 0000000..7930b12
Binary files /dev/null and b/editor-dotnet/src/tests/MBS.Editor.Plugins.Mekada.Tests/Resources/TestData/Mechanics/0.mek differ
diff --git a/editor-dotnet/src/tests/MBS.Editor.Plugins.Mekada.Tests/Resources/TestData/Mechanics/1.mek b/editor-dotnet/src/tests/MBS.Editor.Plugins.Mekada.Tests/Resources/TestData/Mechanics/1.mek
new file mode 100644
index 0000000..6aee92c
Binary files /dev/null and b/editor-dotnet/src/tests/MBS.Editor.Plugins.Mekada.Tests/Resources/TestData/Mechanics/1.mek differ
diff --git a/editor-dotnet/src/tests/MBS.Editor.Plugins.Mekada.Tests/Resources/TestData/Mechanics/WithCarInLot.mek b/editor-dotnet/src/tests/MBS.Editor.Plugins.Mekada.Tests/Resources/TestData/Mechanics/WithCarInLot.mek
new file mode 100644
index 0000000..222a921
Binary files /dev/null and b/editor-dotnet/src/tests/MBS.Editor.Plugins.Mekada.Tests/Resources/TestData/Mechanics/WithCarInLot.mek differ
diff --git a/editor-dotnet/src/tests/MBS.Editor.Plugins.Multimedia.Tests/DataFormats/Audio/Synthesized/RMI/RMIDataFormatTests.cs b/editor-dotnet/src/tests/MBS.Editor.Plugins.Multimedia.Tests/DataFormats/Audio/Synthesized/RMI/RMIDataFormatTests.cs
new file mode 100644
index 0000000..ef609ef
--- /dev/null
+++ b/editor-dotnet/src/tests/MBS.Editor.Plugins.Multimedia.Tests/DataFormats/Audio/Synthesized/RMI/RMIDataFormatTests.cs
@@ -0,0 +1,117 @@
+// Copyright (C) 2024 Michael Becker
+//
+// This file is part of editor-dotnet.
+//
+// editor-dotnet is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// editor-dotnet is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with editor-dotnet. If not, see .
+
+using MBS.Editor.Core;
+using MBS.Editor.Plugins.Multimedia.DataFormats.Audio.Synthesized.RMI;
+using MBS.Editor.Plugins.Multimedia.ObjectModels.Audio.Synthesized;
+using MBS.Editor.Testing;
+
+namespace MBS.Editor.Plugins.Multimedia.Tests.DataFormats.Audio.Synthesized.RMI;
+
+public class RMIDataFormatTests : EmbeddedResourceTest
+{
+ [Test]
+ public void LoadMetadataTest()
+ {
+ if (TryCreateResourceStream("MBS.Editor.Plugins.Multimedia.Tests.Resources.TestData.DataFormats.Audio.Synthesized.RMI.Mountain.rmi", out Stream? st) && st != null)
+ {
+ SynthesizedAudioObjectModel audio = new SynthesizedAudioObjectModel();
+ RMIDataFormat rmi = new RMIDataFormat();
+
+ Document.Load(audio, rmi, st);
+
+ Assert.That(audio.Metadata.Artist, Is.EqualTo("Edvard Grieg"));
+ Assert.That(audio.Metadata.Copyright, Is.EqualTo("1995 Midisoft Corporation"));
+ }
+ else
+ {
+ Assert.Ignore();
+ }
+ }
+
+ [Test]
+ public void LoadTrackMetadataTest()
+ {
+ if (TryCreateResourceStream("MBS.Editor.Plugins.Multimedia.Tests.Resources.TestData.DataFormats.Audio.Synthesized.RMI.Mountain.rmi", out Stream? st) && st != null)
+ {
+ SynthesizedAudioObjectModel audio = new SynthesizedAudioObjectModel();
+ RMIDataFormat rmi = new RMIDataFormat();
+
+ Document.Load(audio, rmi, st);
+
+ Assert.That(audio.Tracks.Count, Is.EqualTo(12));
+ Assert.That(audio.Tracks[1].Name, Is.EqualTo("High Strings"));
+ }
+ else
+ {
+ Assert.Ignore();
+ }
+ }
+
+ [Test]
+ public void SaveTrackMetadataTest()
+ {
+ if (TryCreateResourceStream("MBS.Editor.Plugins.Multimedia.Tests.Resources.TestData.DataFormats.Audio.Synthesized.RMI.Mountain.rmi", out Stream? st) && st != null)
+ {
+ SynthesizedAudioObjectModel audio = new SynthesizedAudioObjectModel();
+ RMIDataFormat rmi = new RMIDataFormat();
+
+ Document.Load(audio, rmi, st);
+
+ MemoryStream ms = new MemoryStream();
+ Document.Save(audio, rmi, ms);
+
+ audio = new SynthesizedAudioObjectModel();
+ ms.Seek(0, SeekOrigin.Begin);
+ Document.Load(audio, rmi, ms);
+
+ Assert.That(audio.Tracks.Count, Is.EqualTo(12));
+ Assert.That(audio.Tracks[1].Name, Is.EqualTo("High Strings"));
+ }
+ else
+ {
+ Assert.Ignore();
+ }
+ }
+
+ [Test]
+ public void SaveTrackCommandsTest()
+ {
+ if (TryCreateResourceStream("MBS.Editor.Plugins.Multimedia.Tests.Resources.TestData.DataFormats.Audio.Synthesized.RMI.Mountain.rmi", out Stream? st) && st != null)
+ {
+ SynthesizedAudioObjectModel audio = new SynthesizedAudioObjectModel();
+ RMIDataFormat rmi = new RMIDataFormat();
+
+ Document.Load(audio, rmi, st);
+
+ MemoryStream ms = new MemoryStream();
+ Document.Save(audio, rmi, ms);
+
+ int cc = audio.Tracks[1].Commands.Count;
+
+ audio = new SynthesizedAudioObjectModel();
+ ms.Seek(0, SeekOrigin.Begin);
+ Document.Load(audio, rmi, ms);
+
+ Assert.That(audio.Tracks[1].Commands.Count, Is.EqualTo(cc));
+ }
+ else
+ {
+ Assert.Ignore();
+ }
+ }
+}
\ No newline at end of file
diff --git a/editor-dotnet/src/tests/MBS.Editor.Plugins.Multimedia.Tests/DataFormats/Picture/ILBM/ILBMDataFormatTests.cs b/editor-dotnet/src/tests/MBS.Editor.Plugins.Multimedia.Tests/DataFormats/Picture/ILBM/ILBMDataFormatTests.cs
new file mode 100644
index 0000000..f47eb3b
--- /dev/null
+++ b/editor-dotnet/src/tests/MBS.Editor.Plugins.Multimedia.Tests/DataFormats/Picture/ILBM/ILBMDataFormatTests.cs
@@ -0,0 +1,62 @@
+using MBS.Core.Drawing;
+using MBS.Editor.Core;
+using MBS.Editor.Plugins.Multimedia.DataFormats.Picture.ILBM;
+using MBS.Editor.Plugins.Multimedia.ObjectModels.Picture;
+using MBS.Editor.Testing;
+
+namespace MBS.Editor.Plugins.Multimedia.Tests.DataFormats.Picture.ILBM;
+
+public class ILBMDataFormatTests : EmbeddedResourceTest
+{
+ [SetUp]
+ public void Setup()
+ {
+ }
+
+ [Test]
+ public void Venus_Size_EqualTo_300x150()
+ {
+ if (!TryCreateResourceStream("MBS.Editor.Plugins.Multimedia.Tests.Resources.TestData.DataFormats.Picture.ILBM.VENUS.IFF", out Stream? st))
+ {
+ Assert.Ignore();
+ return;
+ }
+ if (st == null)
+ {
+ Assert.Ignore();
+ return;
+ }
+
+ PictureObjectModel pic = new PictureObjectModel();
+
+ ILBMDataFormat iff = new ILBMDataFormat();
+ Document.Load(pic, iff, st);
+
+ Assert.That(pic.Width, Is.EqualTo(300));
+ Assert.That(pic.Height, Is.EqualTo(150));
+ }
+
+ [Test]
+ public void Venus_Palettte_Contains_ColorMap()
+ {
+ if (!TryCreateResourceStream("MBS.Editor.Plugins.Multimedia.Tests.Resources.TestData.DataFormats.Picture.ILBM.VENUS.IFF", out Stream? st))
+ {
+ Assert.Ignore();
+ return;
+ }
+ if (st == null)
+ {
+ Assert.Ignore();
+ return;
+ }
+
+ PictureObjectModel pic = new PictureObjectModel();
+
+ ILBMDataFormat iff = new ILBMDataFormat();
+ Document.Load(pic, iff, st);
+
+ Assert.That(pic.Palette, Is.Not.Null);
+ Assert.That(pic.Palette.Entries.Count, Is.EqualTo(256));
+ Assert.That(pic.Palette.Entries[1].Color, Is.EqualTo(Color.FromString("#FFAF17")));
+ }
+}
\ No newline at end of file
diff --git a/editor-dotnet/src/tests/MBS.Editor.Plugins.Multimedia.Tests/GlobalUsings.cs b/editor-dotnet/src/tests/MBS.Editor.Plugins.Multimedia.Tests/GlobalUsings.cs
new file mode 100644
index 0000000..cefced4
--- /dev/null
+++ b/editor-dotnet/src/tests/MBS.Editor.Plugins.Multimedia.Tests/GlobalUsings.cs
@@ -0,0 +1 @@
+global using NUnit.Framework;
\ No newline at end of file
diff --git a/editor-dotnet/src/tests/MBS.Editor.Plugins.Multimedia.Tests/MBS.Editor.Plugins.Multimedia.Tests.csproj b/editor-dotnet/src/tests/MBS.Editor.Plugins.Multimedia.Tests/MBS.Editor.Plugins.Multimedia.Tests.csproj
new file mode 100644
index 0000000..3f65521
--- /dev/null
+++ b/editor-dotnet/src/tests/MBS.Editor.Plugins.Multimedia.Tests/MBS.Editor.Plugins.Multimedia.Tests.csproj
@@ -0,0 +1,42 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+ false
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/editor-dotnet/src/tests/MBS.Editor.Plugins.Multimedia.Tests/Resources/TestData/DataFormats/Audio/Synthesized/RMI/Mountain.rmi b/editor-dotnet/src/tests/MBS.Editor.Plugins.Multimedia.Tests/Resources/TestData/DataFormats/Audio/Synthesized/RMI/Mountain.rmi
new file mode 100644
index 0000000..da79d91
Binary files /dev/null and b/editor-dotnet/src/tests/MBS.Editor.Plugins.Multimedia.Tests/Resources/TestData/DataFormats/Audio/Synthesized/RMI/Mountain.rmi differ
diff --git a/editor-dotnet/src/tests/MBS.Editor.Plugins.Multimedia.Tests/Resources/TestData/DataFormats/Picture/ILBM/BLK.IFF b/editor-dotnet/src/tests/MBS.Editor.Plugins.Multimedia.Tests/Resources/TestData/DataFormats/Picture/ILBM/BLK.IFF
new file mode 100644
index 0000000..1bddef9
Binary files /dev/null and b/editor-dotnet/src/tests/MBS.Editor.Plugins.Multimedia.Tests/Resources/TestData/DataFormats/Picture/ILBM/BLK.IFF differ
diff --git a/editor-dotnet/src/tests/MBS.Editor.Plugins.Multimedia.Tests/Resources/TestData/DataFormats/Picture/ILBM/VENUS.IFF b/editor-dotnet/src/tests/MBS.Editor.Plugins.Multimedia.Tests/Resources/TestData/DataFormats/Picture/ILBM/VENUS.IFF
new file mode 100644
index 0000000..443778e
Binary files /dev/null and b/editor-dotnet/src/tests/MBS.Editor.Plugins.Multimedia.Tests/Resources/TestData/DataFormats/Picture/ILBM/VENUS.IFF differ
diff --git a/framework-dotnet b/framework-dotnet
index a24ae26..223c34c 160000
--- a/framework-dotnet
+++ b/framework-dotnet
@@ -1 +1 @@
-Subproject commit a24ae26d5775be4387a58f61a3dc26057e0c1171
+Subproject commit 223c34c734ee60b4f4738b48181afb7221e18c39
diff --git a/web-framework-dotnet b/web-framework-dotnet
new file mode 160000
index 0000000..732daae
--- /dev/null
+++ b/web-framework-dotnet
@@ -0,0 +1 @@
+Subproject commit 732daae00ebeb6bb31454bd954bdd8174ef1fd0e