major update commit

This commit is contained in:
Michael Becker 2025-03-20 18:51:08 -04:00
parent 0654b86aa9
commit aef4eb4f37
100 changed files with 4278 additions and 82 deletions

6
.gitmodules vendored
View File

@ -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

@ -0,0 +1 @@
Subproject commit 57316e3557c1c3a566d1a24bd60675c02d1f8744

View File

@ -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}

View File

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../../../../web-framework-dotnet/src/lib/MBS.Web/MBS.Web.csproj" />
<ProjectReference Include="../../lib/MBS.Editor.Web/MBS.Editor.Web.csproj" />
</ItemGroup>
</Project>

View File

@ -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<string> 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();
}
}

View File

@ -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

View File

@ -3,8 +3,8 @@
<ItemGroup>
<ProjectReference Include="..\..\lib\MBS.Editor.Core\MBS.Editor.Core.csproj" />
<ProjectReference Include="..\..\lib\MBS.Editor.UserInterface\MBS.Editor.UserInterface.csproj" />
<ProjectReference Include="..\..\..\..\framework-dotnet\framework-dotnet\src\lib\MBS.Desktop\MBS.Desktop.csproj" />
<ProjectReference Include="..\..\..\..\framework-dotnet\framework-dotnet\src\lib\MBS.Core\MBS.Core.csproj" />
<ProjectReference Include="..\..\..\..\desktop-framework-dotnet\desktop-framework-dotnet\src\lib\MBS.Desktop\MBS.Desktop.csproj" />
</ItemGroup>
<PropertyGroup>

View File

@ -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

View File

@ -1,22 +1,73 @@
//
// DataFormat.cs - translates ObjectModel to serialized data in a particular format
//
// Author:
// Michael Becker <alcexhim@gmail.com>
//
// 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 <http://www.gnu.org/licenses/>.
namespace MBS.Editor.Core;
public abstract class DataFormat
{
protected virtual void BeforeLoadInternal(Stack<ObjectModel> objectModels)
{
}
protected virtual void AfterLoadInternal(Stack<ObjectModel> objectModels)
{
}
public void Load(ObjectModel objectModel, Stream stream)
{
LoadInternal(objectModel, stream);
Stack<ObjectModel> stack = new Stack<ObjectModel>();
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<ObjectModel> objectModels)
{
}
protected virtual void AfterSaveInternal(Stack<ObjectModel> objectModels)
{
}
public void Save(ObjectModel objectModel, Stream stream)
{
SaveInternal(objectModel, stream);
Stack<ObjectModel> stack = new Stack<ObjectModel>();
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<T>() where T : DataFormat, new()
{
T objectModel = new T();

View File

@ -0,0 +1,142 @@
// Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
//
// 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 <https://www.gnu.org/licenses/>.
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;
/// <summary>
/// Gets or sets the endianness of multibyte values. For IFF and AIFF, set to
/// <see cref="Endianness.BigEndian" />. For RIFF, set to <see cref="Endianness.LittleEndian" />.
/// </summary>
/// <value></value>
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<ChunkedObjectModel>(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<ChunkedObjectModel>(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;
}
}

View File

@ -0,0 +1,45 @@
// Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
//
// 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 <https://www.gnu.org/licenses/>.
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<string> GroupChunkNames { get; } = new List<string>();
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;
}

View File

@ -0,0 +1,27 @@
// Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
//
// 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 <https://www.gnu.org/licenses/>.
namespace MBS.Editor.Core.DataFormats.FileSystem.IFF;
public class RIFFDataFormat : IFFDataFormat
{
public RIFFDataFormat()
{
GroupChunkNames.Add("RIFF");
Endianness = IO.Endianness.LittleEndian;
}
}

View File

@ -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();
}
}

View File

@ -0,0 +1,9 @@
namespace MBS.Editor.Core;
public enum INIDataFormatToken
{
None = 0,
Comment,
String,
Group
}

View File

@ -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;
}
}
/// <summary>
/// Closes the current stream and releases any resources (such as sockets and file handles) associated with the current stream.
/// </summary>
public void Close()
{
BaseStream.Close();
}
/// <summary>
/// Aligns the <see cref="Reader" /> to the specified number of bytes. If the current
/// position of the <see cref="Reader" /> 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.
/// </summary>
/// <param name="alignTo">The number of bytes on which to align the <see cref="Reader"/>.</param>
/// <param name="extraPadding">Any additional padding bytes that should be included after aligning to the specified boundary.</param>
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)
{

View File

@ -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;
}
}
}
/// <summary>
/// Closes the current stream and releases any resources (such as sockets and file handles) associated with the current stream.
/// </summary>
public void Close()
{
BaseStream.Close();
}
}

View File

@ -32,5 +32,14 @@ public class ObjectModel
}
return objectModel;
}
public static T CastOrThrow<T>(ObjectModel? objectModel) where T : ObjectModel
{
T om = objectModel as T;
if (om == null)
{
throw new ObjectModelNotSupportedException(typeof(T), objectModel?.GetType());
}
return om;
}
}

View File

@ -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<string, string> __c1(Type expected, Type actual)
{
Dictionary<string, string> dict = new Dictionary<string, string>();
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;
}
}

View File

@ -0,0 +1,63 @@
// Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
//
// 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 <https://www.gnu.org/licenses/>.
namespace MBS.Editor.Core.ObjectModels.Chunked;
public abstract class Chunk
{
public class ChunkCollection
: System.Collections.ObjectModel.Collection<Chunk>
{
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; }
}

View File

@ -0,0 +1,23 @@
// Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
//
// 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 <https://www.gnu.org/licenses/>.
namespace MBS.Editor.Core.ObjectModels.Chunked;
public class ChunkedObjectModel : ObjectModel, IChunkContainer
{
public Chunk.ChunkCollection Chunks { get; } = new Chunk.ChunkCollection();
}

View File

@ -0,0 +1,29 @@
// Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
//
// 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 <https://www.gnu.org/licenses/>.
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;
}

View File

@ -0,0 +1,29 @@
// Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
//
// 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 <https://www.gnu.org/licenses/>.
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();
}

View File

@ -0,0 +1,23 @@
// Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
//
// 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 <https://www.gnu.org/licenses/>.
namespace MBS.Editor.Core.ObjectModels.Chunked;
public interface IChunkContainer
{
Chunk.ChunkCollection Chunks { get; }
}

View File

@ -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;
}
}

View File

@ -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");
}

View File

@ -178,4 +178,14 @@ public class FileSystemItemCollection
}
return list.ToArray();
}
public T? GetChild<T>(string v) where T : FileSystemItem
{
FileSystemItem? item = this[v];
if (item is T)
{
return (T)item;
}
return null;
}
}

View File

@ -0,0 +1,9 @@
namespace MBS.Editor.Core;
public interface IPropertyListContainer
{
PropertyListItem.PropertyListItemCollection Items { get; }
IPropertyListContainer? Parent { get; }
}

View File

@ -0,0 +1,6 @@
namespace MBS.Editor.Core;
public class PropertyListComment : PropertyListItem
{
public string Value { get; set; } = "";
}

View File

@ -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);
}
}

View File

@ -0,0 +1,58 @@
namespace MBS.Editor.Core;
public abstract class PropertyListItem
{
public class PropertyListItemCollection
: System.Collections.ObjectModel.Collection<PropertyListItem>
{
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;
}
}
}

View File

@ -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);
}
}

View File

@ -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<T>()
{
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<T>(def.DataType, Value);
}
}
}
}
return default(T);
}
public PropertyListProperty(string name, object value)
{
Name = name;
Value = value;
}
}

View File

@ -0,0 +1,27 @@
namespace MBS.Editor.Core.ObjectModels.PropertyList.Schema;
public class PropertyValueConverter
{
public class PropertyValueConverterCollection
: System.Collections.ObjectModel.Collection<PropertyValueConverter>
{
}
}
public class PropertyValueConverter<TInput, TOutput> : PropertyValueConverter
{
private Func<TInput, TOutput> __convertTo = null;
private Func<TOutput, TInput> __convertFrom = null;
public PropertyValueConverter(Func<TInput, TOutput> convertTo, Func<TOutput, TInput> convertFrom)
{
__convertTo = convertTo;
__convertFrom = convertFrom;
}
public TOutput? ConvertTo(TInput value)
{
return default;
}
}

View File

@ -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<PropertyDefinition>
{
}
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, TTo>(TFrom value)
{
foreach (PropertyValueConverter converter in Converters)
{
if (converter is PropertyValueConverter<TFrom, TTo> cvt)
{
return cvt.ConvertTo(value);
}
}
throw new NotSupportedException();
}
}

View File

@ -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);
}
}

View File

@ -0,0 +1,65 @@
using NUnit.Framework;
namespace MBS.Editor.Testing;
public class EmbeddedResourceTest
{
private System.Collections.Generic.Dictionary<string, System.IO.Stream> testDataStreams = new System.Collections.Generic.Dictionary<string, System.IO.Stream>();
[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)
{
}
}

View File

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<PackageReference Include="NUnit" Version="3.13.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\framework-dotnet\framework-dotnet\src\lib\MBS.Core\MBS.Core.csproj" />
<ProjectReference Include="..\MBS.Editor.Core\MBS.Editor.Core.csproj" />
</ItemGroup>
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>false</IsTestProject>
</PropertyGroup>
</Project>

View File

@ -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();
}
}

View File

@ -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("<b>Strong Bad!</b>") { UseMarkup = true });
}
}

View File

@ -2,7 +2,7 @@
<ItemGroup>
<ProjectReference Include="..\..\..\..\framework-dotnet\framework-dotnet\src\lib\MBS.Core\MBS.Core.csproj" />
<ProjectReference Include="..\..\..\..\framework-dotnet\framework-dotnet\src\lib\MBS.Desktop\MBS.Desktop.csproj" />
<ProjectReference Include="..\..\..\..\desktop-framework-dotnet\desktop-framework-dotnet\src\lib\MBS.Desktop\MBS.Desktop.csproj" />
</ItemGroup>
<PropertyGroup>

View File

@ -0,0 +1,31 @@
// Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
//
// 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 <https://www.gnu.org/licenses/>.
using MBS.Web.UI;
namespace MBS.Editor.Web.Controls;
public class PageHeader : WebControl
{
protected override string TagName => "div";
protected override IEnumerable<string> GetStyleClasses()
{
return new string[] { "uwt-page-header" };
}
}

View File

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../../../../framework-dotnet/framework-dotnet/src/lib/MBS.Core/MBS.Core.csproj" />
<ProjectReference Include="../../../../web-framework-dotnet/src/lib/MBS.Web/MBS.Web.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,123 @@
// Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
//
// 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 <https://www.gnu.org/licenses/>.
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<string, Dictionary<string, object>> dict = new Dictionary<string, Dictionary<string, object>>();
/*
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<string, object>();
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<Control> GetHeaderControls()
{
return new Control[]
{
};
}
private string FormatCss(Dictionary<string, Dictionary<string, object>> dict)
{
StringBuilder sb = new StringBuilder();
foreach (KeyValuePair<string, Dictionary<string, object>> kvp in dict)
{
sb.Append(kvp.Key);
sb.Append(" { ");
foreach (KeyValuePair<string, object> 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<Control> GetBodyControls()
{
CommandBar cb = new CommandBar();
cb.ClientId = "mainToolbar";
cb.Items.Add(new CommandReferenceCommandItem("FileOpen"));
return new Control[]
{
new PageHeader(),
cb
};
}
}

View File

@ -0,0 +1,6 @@
namespace MBS.Editor.Plugins.Kronosaur;
public class Class1
{
}

View File

@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@ -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<ObjectModel> 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<int, TimeSpan>(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<ObjectModel> 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() + "\"");
}
}
}

View File

@ -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();
}
}

View File

@ -0,0 +1,10 @@
namespace MBS.Editor.Plugins.Mekada;
public enum MEKPropertyType : ushort
{
None = 0,
Int32 = 8192,
Float = 16384,
String = 24576,
Group = 32768
}

View File

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\lib\MBS.Editor.Core\MBS.Editor.Core.csproj" />
<ProjectReference Include="..\..\..\..\framework-dotnet\framework-dotnet\src\lib\MBS.Core\MBS.Core.csproj" />
</ItemGroup>
</Project>

View File

@ -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;
}

View File

@ -0,0 +1,143 @@
//
// MIDICommand.cs - represents a command in a MIDI synthesized audio file
//
// Author:
// Michael Becker <alcexhim@gmail.com>
//
// 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 <http://www.gnu.org/licenses/>.
namespace MBS.Editor.Plugins.Multimedia.DataFormats.Audio.Synthesized.MIDI;
/// <summary>
/// Represents a command in a MIDI synthesized audio file.
/// </summary>
public class MIDICommand
{
/// <summary>
/// Gets or sets the type of the <see cref="MIDICommand" /> to send.
/// </summary>
/// <value>The type of the <see cref="MIDICommand" /> to send.</value>
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;
}
}
/// <summary>
/// Gets or sets the channel on which to send this <see cref="MIDICommand" />.
/// </summary>
/// <value>The channel on which to send this <see cref="MIDICommand" />.</value>
public byte Channel { get; set; } = 0;
/// <summary>
/// Gets or sets the value of the <see cref="MIDICommand" /> to send.
/// </summary>
/// <value>The value of the <see cref="MIDICommand" /> to send.</value>
public byte Command { get; set; } = 0;
}

View File

@ -0,0 +1,38 @@
//
// MIDICommandType.cs - indicates the type of MIDI command
//
// Author:
// Michael Becker <alcexhim@gmail.com>
//
// 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 <http://www.gnu.org/licenses/>.
namespace MBS.Editor.Plugins.Multimedia.DataFormats.Audio.Synthesized.MIDI;
/// <summary>
/// Indicates the type of MIDI command.
/// </summary>
public enum MIDICommandType
{
Unknown = -1,
None,
NoteOff = 8,
NoteOn,
KeyAfterTouch,
ControlChange,
ProgramChange,
ChannelAfterTouch,
PitchWheelChange
}

View File

@ -0,0 +1,353 @@
// Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
//
// 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 <https://www.gnu.org/licenses/>.
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<ObjectModel> objectModels)
{
base.BeforeLoadInternal(objectModels);
objectModels.Push(new ChunkedObjectModel());
}
protected override void AfterLoadInternal(Stack<ObjectModel> objectModels)
{
base.AfterLoadInternal(objectModels);
ChunkedObjectModel chunked = ObjectModel.CastOrThrow<ChunkedObjectModel>(objectModels.Pop());
SynthesizedAudioObjectModel audio = ObjectModel.CastOrThrow<SynthesizedAudioObjectModel>(objectModels.Pop());
System.Collections.Generic.Dictionary<int, SynthesizedAudioCommandNote> notesForNoteNumber = new System.Collections.Generic.Dictionary<int, SynthesizedAudioCommandNote>();
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<Writer>?)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<Writer>? 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<ObjectModel> objectModels)
{
base.BeforeSaveInternal(objectModels);
SynthesizedAudioObjectModel audio = ObjectModel.CastOrThrow<SynthesizedAudioObjectModel>(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<Writer> func)
{
w.WriteVariableLengthInt32(deltaTime);
byte channel = 0;
MIDIEventType type = MIDIEventType.NoteOn;
byte commandAndChannel = (byte)type;
w.WriteByte(commandAndChannel);
func(w);
}
}

View File

@ -0,0 +1,46 @@
//
// MIDIEventType.cs - indicates the type of MIDI event
//
// Author:
// Michael Becker <alcexhim@gmail.com>
//
// 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 <http://www.gnu.org/licenses/>.
namespace MBS.Editor.Plugins.Multimedia.DataFormats.Audio.Synthesized.MIDI;
/// <summary>
/// Indicates the type of MIDI event.
/// </summary>
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
}

View File

@ -0,0 +1,46 @@
//
// MIDIFileFormatType.cs - indicates the type of MIDI file format
//
// Author:
// Michael Becker <alcexhim@gmail.com>
//
// 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 <http://www.gnu.org/licenses/>.
namespace MBS.Editor.Plugins.Multimedia.DataFormats.Audio.Synthesized.MIDI;
/// <summary>
/// Indicates the type of MIDI file format.
/// </summary>
public enum MIDIFileFormatType : short
{
/// <summary>
/// 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.
/// </summary>
SingleTrack = 0,
/// <summary>
/// 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.
/// </summary>
SimultaneousMultitrack = 1,
/// <summary>
/// Independent multi-track (format 2). Consists of a header-chunk and one or more track-chunks, where each track represents an
/// independent sequence.
/// </summary>
IndependentMultitrack = 2
}

View File

@ -0,0 +1,46 @@
//
// MIDIMetaEventType.cs - indicates the type of MIDI meta event
//
// Author:
// Michael Becker <alcexhim@gmail.com>
//
// 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 <http://www.gnu.org/licenses/>.
namespace MBS.Editor.Plugins.Multimedia.DataFormats.Audio.Synthesized.MIDI;
/// <summary>
/// Indicates the type of MIDI meta event.
/// </summary>
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
}

View File

@ -0,0 +1,114 @@
// Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
//
// 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 <https://www.gnu.org/licenses/>.
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<ObjectModel> objectModels)
{
base.BeforeLoadInternal(objectModels);
objectModels.Push(new ChunkedObjectModel());
}
// f8ffdcb7-e2ec-40b1-aa8a-beeb5076c1c9
protected override void AfterLoadInternal(Stack<ObjectModel> objectModels)
{
base.AfterLoadInternal(objectModels);
ChunkedObjectModel chunked = ObjectModel.CastOrThrow<ChunkedObjectModel>(objectModels.Pop());
SynthesizedAudioObjectModel audio = ObjectModel.CastOrThrow<SynthesizedAudioObjectModel>(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<ObjectModel> objectModels)
{
base.BeforeSaveInternal(objectModels);
SynthesizedAudioObjectModel audio = ObjectModel.CastOrThrow<SynthesizedAudioObjectModel>(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);
}
}

View File

@ -0,0 +1,27 @@
// Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
//
// 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 <https://www.gnu.org/licenses/>.
namespace MBS.Editor.Plugins.Multimedia.DataFormats.Picture.ILBM;
/// <summary>
/// Choice of compression algorithm applied to the rows of all source and mask planes.
/// </summary>
public enum ILBMCompression : byte
{
None = 0x00,
ByteRun1 = 0x01
}

View File

@ -0,0 +1,164 @@
// Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
//
// 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 <https://www.gnu.org/licenses/>.
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<ObjectModel> objectModels)
{
base.BeforeLoadInternal(objectModels);
objectModels.Push(new ChunkedObjectModel());
}
protected override void AfterLoadInternal(Stack<ObjectModel> 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<ObjectModel> objectModels)
{
base.BeforeSaveInternal(objectModels);
}
}

View File

@ -0,0 +1,46 @@
// Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
//
// 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 <https://www.gnu.org/licenses/>.
namespace MBS.Editor.Plugins.Multimedia.DataFormats.Picture.ILBM;
/// <summary>
/// Choice of masking technique.
/// </summary>
public enum ILBMMasking : byte
{
/// <summary>
/// Designates an opaque rectangular image.
/// </summary>
None = 0x00,
/// <summary>
/// Mask plane is interleaved with the bitplanes in the BODY chunk.
/// </summary>
Mask = 0x01,
/// <summary>
/// Pixels in the source planes matching transparentColor are to be considered "transparent."
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
TransparentColor = 0x02,
/// <summary>
/// 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.
/// </summary>
Lasso = 0x03
}

View File

@ -0,0 +1,26 @@
// Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
//
// 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 <https://www.gnu.org/licenses/>.
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; } = "";
}

View File

@ -0,0 +1,26 @@
// Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
//
// 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 <https://www.gnu.org/licenses/>.
using MBS.Editor.Core;
namespace MBS.Editor.Plugins.Multimedia.ObjectModels.Audio;
public abstract class AudioObjectModelBase : ObjectModel
{
public AudioMetadata Metadata { get; set; } = new AudioMetadata();
}

View File

@ -0,0 +1,114 @@
//
// SynthesizedAudioCommand.cs - represents a command (e.g. note, control change) in a synthesized audio file
//
// Author:
// Michael Becker <alcexhim@gmail.com>
//
// 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 <http://www.gnu.org/licenses/>.
using System;
using System.Collections.ObjectModel;
using MBS.Core;
namespace MBS.Editor.Plugins.Multimedia.ObjectModels.Audio.Synthesized;
/// <summary>
/// Represents a command (e.g. note, control change) in a synthesized audio file.
/// </summary>
public class SynthesizedAudioCommand : ICloneable
{
public class SynthesizedAudioCommandCollection : Collection<SynthesizedAudioCommand>
{
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>(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();
}
}

View File

@ -0,0 +1,82 @@
//
// SynthesizedAudioCommandNote.cs - represents a note command in a synthesized audio file
//
// Author:
// Michael Becker <alcexhim@gmail.com>
//
// 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 <http://www.gnu.org/licenses/>.
using System;
namespace MBS.Editor.Plugins.Multimedia.ObjectModels.Audio.Synthesized;
/// <summary>
/// Represents a note command in a synthesized audio file.
/// </summary>
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);
}
}

View File

@ -0,0 +1,29 @@
//
// SynthesizedAudioCommandRest.cs - represents a rest in a synthesized audio file
//
// Author:
// Michael Becker <alcexhim@gmail.com>
//
// 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 <http://www.gnu.org/licenses/>.
namespace MBS.Editor.Plugins.Multimedia.ObjectModels.Audio.Synthesized;
/// <summary>
/// Represents a rest in a synthesized audio file.
/// </summary>
public class SynthesizedAudioCommandRest : SynthesizedAudioCommandNote
{
}

View File

@ -0,0 +1,49 @@
//
// SynthesizedAudioCommandTempo.cs - represents a tempo change command in a synthesized audio file
//
// Author:
// Michael Becker <alcexhim@gmail.com>
//
// 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 <http://www.gnu.org/licenses/>.
namespace MBS.Editor.Plugins.Multimedia.ObjectModels.Audio.Synthesized;
/// <summary>
/// Represents a tempo change command in a synthesized audio file.
/// </summary>
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
};
}
}

View File

@ -0,0 +1,48 @@
//
// SynthesizedAudioCommandText.cs - represents a text command in a synthesized audio file
//
// Author:
// Michael Becker <alcexhim@gmail.com>
//
// 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 <http://www.gnu.org/licenses/>.
namespace MBS.Editor.Plugins.Multimedia.ObjectModels.Audio.Synthesized;
/// <summary>
/// Represents a text command in a synthesized audio file.
/// </summary>
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
};
}
}

View File

@ -0,0 +1,66 @@
//
// SynthesizedAudioCommandTimeSignature.cs - represents a time signature change command in a synthesized audio file
//
// Author:
// Michael Becker <alcexhim@gmail.com>
//
// 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 <http://www.gnu.org/licenses/>.
namespace MBS.Editor.Plugins.Multimedia.ObjectModels.Audio.Synthesized;
/// <summary>
/// Represents a time signature change command in a synthesized audio file.
/// </summary>
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
};
}
}

View File

@ -0,0 +1,109 @@
//
// SynthesizedAudioObjectModel.cs - provides an ObjectModel for manipulating synthesized audio files (e.g. MIDI, VSQ, etc.)
//
// Author:
// Michael Becker <alcexhim@gmail.com>
//
// 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 <http://www.gnu.org/licenses/>.
using System.Collections.Generic;
using MBS.Editor.Core;
// using MBS.Editor.Plugins.Multimedia.ObjectModels.Audio.Voicebank;
namespace MBS.Editor.Plugins.Multimedia.ObjectModels.Audio.Synthesized;
/// <summary>
/// Provides an <see cref="ObjectModel" /> for manipulating synthesized audio files (e.g. MIDI, VSQ, etc.).
/// </summary>
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<CriteriaResult> list = new List<CriteriaResult>();
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();
}
}

View File

@ -0,0 +1,98 @@
//
// SynthesizedAudioPredefinedNote.cs - defines various commonly-used note pitches for synthesized audio files
//
// Author:
// Michael Becker <alcexhim@gmail.com>
//
// 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 <http://www.gnu.org/licenses/>.
using System;
namespace MBS.Editor.Plugins.Multimedia.ObjectModels.Audio.Synthesized;
/// <summary>
/// Defines various commonly-used note pitches for synthesized audio files.
/// </summary>
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
}
/// <summary>
/// Converts to and from <see cref="SynthesizedAudioPredefinedNote" /> values and their associated frequencies.
/// </summary>
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;
}
}

View File

@ -0,0 +1,63 @@
//
// SynthesizedAudioStylePlugin.cs
//
// Author:
// Michael Becker <alcexhim@gmail.com>
//
// 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 <http://www.gnu.org/licenses/>.
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;
}
}
}

View File

@ -0,0 +1,101 @@
//
// SynthesizedAudioTrack.cs - represents a track in a synthesized audio file
//
// Author:
// Michael Becker <alcexhim@gmail.com>
//
// 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 <http://www.gnu.org/licenses/>.
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;
/// <summary>
/// Represents a track in a synthesized audio file.
/// </summary>
public class SynthesizedAudioTrack : ICloneable
{
public class SynthesizedAudioTrackCollection : Collection<SynthesizedAudioTrack>
{
private Dictionary<string, SynthesizedAudioTrack> tracksByID = new Dictionary<string, SynthesizedAudioTrack>();
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;
}

View File

@ -0,0 +1,30 @@
//
// SynthesizedAudioVibratoType.cs - indicates the type of vibrato applied to a note in a synthesized audio file
//
// Author:
// Michael Becker <alcexhim@gmail.com>
//
// 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 <http://www.gnu.org/licenses/>.
namespace MBS.Editor.Plugins.Multimedia.ObjectModels.Audio.Synthesized;
/// <summary>
/// Indicates the type of vibrato applied to a note in a synthesized audio file.
/// </summary>
public enum SynthesizedAudioVibratoType
{
None
}

View File

@ -0,0 +1,71 @@
//
// PaletteEntry.cs - represents a color entry in a palette
//
// Author:
// Michael Becker <alcexhim@gmail.com>
//
// 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 <http://www.gnu.org/licenses/>.
using System;
using MBS.Core.Drawing;
namespace MBS.Editor.Plugins.Multimedia.ObjectModels.Palette
{
/// <summary>
/// Represents a color entry in a palette.
/// </summary>
public class PaletteEntry : ICloneable
{
public class PaletteEntryCollection
: System.Collections.ObjectModel.Collection<PaletteEntry>
{
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;
}
/// <summary>
/// Gets or sets the name of this <see cref="PaletteEntry" />.
/// </summary>
/// <value>The name of this <see cref="PaletteEntry" />.</value>
public string Name { get; set; } = String.Empty;
/// <summary>
/// Gets or sets the color of this <see cref="PaletteEntry" />.
/// </summary>
/// <value>The color of this <see cref="PaletteEntry" />.</value>
public Color Color { get; set; } = Color.Empty;
public object Clone()
{
PaletteEntry clone = new PaletteEntry();
clone.Name = Name;
clone.Color = Color;
return clone;
}
}
}

View File

@ -0,0 +1,85 @@
//
// PaletteObjectModel.cs - provides an ObjectModel for manipulating color palettes
//
// Author:
// Michael Becker <alcexhim@gmail.com>
//
// 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 <http://www.gnu.org/licenses/>.
using System;
using MBS.Editor.Core;
namespace MBS.Editor.Plugins.Multimedia.ObjectModels.Palette
{
/// <summary>
/// Provides an <see cref="ObjectModel" /> for manipulating color palettes.
/// </summary>
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);
}
}
/// <summary>
/// Gets or sets the name of this palette.
/// </summary>
/// <value>The name of this palette.</value>
public string Name { get; set; } = String.Empty;
/// <summary>
/// Gets a collection of <see cref="PaletteEntry" /> instances representing the color entries in this palette.
/// </summary>
/// <value>The color entries in this palette.</value>
public PaletteEntry.PaletteEntryCollection Entries { get; } = new PaletteEntry.PaletteEntryCollection();
}
}

View File

@ -0,0 +1,55 @@
//
// Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
//
// 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 <https://www.gnu.org/licenses/>.
//
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<Vector2D, Color> pixels = new Dictionary<Vector2D, Color>();
public PaletteObjectModel? Palette { get; set; } = null;
public int Width { get; set; }
public int Height { get; set; }
public IEnumerable<KeyValuePair<Vector2D, Color>> 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;
}
}

View File

@ -0,0 +1,49 @@
// Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
//
// 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 <https://www.gnu.org/licenses/>.
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));
}
}
}

View File

@ -6,6 +6,10 @@
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="Resources/TestData/DataFormats/FileSystem/IFF/VENUS.IFF" />
<EmbeddedResource Include="Resources/TestData/ObjectModels/PropertyList/INI/CompleteTest.ini" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0" />
<PackageReference Include="NUnit" Version="3.13.3" />
@ -15,5 +19,6 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\lib\MBS.Editor.Core\MBS.Editor.Core.csproj" />
<ProjectReference Include="..\..\lib\MBS.Editor.Testing\MBS.Editor.Testing.csproj" />
</ItemGroup>
</Project>

View File

@ -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

View File

@ -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<PropertyListComment>());
Assert.That(plom.Items[1], Is.InstanceOf<PropertyListGroup>());
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."));
}
}
}
}

View File

@ -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

View File

@ -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<string, System.IO.Stream> testDataStreams = new System.Collections.Generic.Dictionary<string, System.IO.Stream>();
[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);

View File

@ -0,0 +1,8 @@
using System;
namespace MBS.Editor.Plugins.CRI.Tests.DataFormats.Database.UTF;
public class UTFDataFormatTests
{
}

View File

@ -16,6 +16,7 @@
<ItemGroup>
<ProjectReference Include="..\..\lib\MBS.Editor.Core\MBS.Editor.Core.csproj" />
<ProjectReference Include="..\..\plugins\MBS.Editor.Plugins.CRI\MBS.Editor.Plugins.CRI.csproj" />
<ProjectReference Include="..\..\lib\MBS.Editor.Testing\MBS.Editor.Testing.csproj" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\TestData\sample_data_uncompressed_unmasked.cpk" />

View File

@ -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();
}
}
}

View File

@ -0,0 +1 @@
global using NUnit.Framework;

View File

@ -0,0 +1,39 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0" />
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
<PackageReference Include="NUnit.Analyzers" Version="3.6.1" />
<PackageReference Include="coverlet.collector" Version="6.0.0" />
</ItemGroup>
<ItemGroup>
<Folder Include="Resources/" />
<Folder Include="Resources/TestData/" />
<Folder Include="Resources/TestData/Mechanics/" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources/TestData/Mechanics/0.mek" />
<EmbeddedResource Include="Resources/TestData/Mechanics/1.mek" />
<EmbeddedResource Include="Resources/TestData/Mechanics/WithCarInLot.mek" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\framework-dotnet\framework-dotnet\src\lib\MBS.Core\MBS.Core.csproj" />
<ProjectReference Include="..\..\lib\MBS.Editor.Core\MBS.Editor.Core.csproj" />
<ProjectReference Include="..\..\plugins\MBS.Editor.Plugins.Mekada\MBS.Editor.Plugins.Mekada.csproj" />
<ProjectReference Include="..\..\lib\MBS.Editor.Testing\MBS.Editor.Testing.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,117 @@
// Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
//
// 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 <https://www.gnu.org/licenses/>.
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();
}
}
}

View File

@ -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")));
}
}

View File

@ -0,0 +1 @@
global using NUnit.Framework;

View File

@ -0,0 +1,42 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0" />
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
<PackageReference Include="NUnit.Analyzers" Version="3.6.1" />
<PackageReference Include="coverlet.collector" Version="6.0.0" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources/TestData/DataFormats/Picture/ILBM/VENUS.IFF" />
<EmbeddedResource Include="Resources/TestData/DataFormats/Picture/ILBM/BLK.IFF" />
<EmbeddedResource Include="Resources/TestData/DataFormats/Audio/Synthesized/RMI/Mountain.rmi" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../../../../framework-dotnet/framework-dotnet/src/lib/MBS.Core/MBS.Core.csproj" />
<ProjectReference Include="../../lib/MBS.Editor.Core/MBS.Editor.Core.csproj" />
<ProjectReference Include="../../lib/MBS.Editor.Testing/MBS.Editor.Testing.csproj" />
<ProjectReference Include="../../plugins/MBS.Editor.Plugins.Multimedia/MBS.Editor.Plugins.Multimedia.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Resources/" />
<Folder Include="Resources/TestData/" />
<Folder Include="Resources/TestData/DataFormats/" />
<Folder Include="Resources/TestData/DataFormats/Picture/" />
<Folder Include="Resources/TestData/DataFormats/Picture/ILBM/" />
</ItemGroup>
</Project>

@ -1 +1 @@
Subproject commit a24ae26d5775be4387a58f61a3dc26057e0c1171
Subproject commit 223c34c734ee60b4f4738b48181afb7221e18c39

1
web-framework-dotnet Submodule

@ -0,0 +1 @@
Subproject commit 732daae00ebeb6bb31454bd954bdd8174ef1fd0e