diff --git a/plugins/Nintendo/plugin_nintendo/Images/3dst.cs b/plugins/Nintendo/plugin_nintendo/Images/3dst.cs new file mode 100644 index 00000000..68925977 --- /dev/null +++ b/plugins/Nintendo/plugin_nintendo/Images/3dst.cs @@ -0,0 +1,54 @@ +using System.Drawing; +using System.IO; +using Kanvas.Swizzle; +using Komponent.IO; +using Kontract.Models.Image; + +namespace plugin_nintendo.Images +{ + class _3dst + { + private static readonly int HeaderSize = Tools.MeasureType(typeof(_3dstHeader)); + + private _3dstHeader _header; + + public ImageInfo Load(Stream input) + { + using var br = new BinaryReaderX(input); + + // Read header + _header = br.ReadType<_3dstHeader>(); + + // Read image data + br.BaseStream.Position = 0x80; + var imgData = br.ReadBytes((int)(input.Length - 0x80)); + + // Create image info + var imageInfo = new ImageInfo(imgData, _header.format, new Size(_header.width, _header.height)); + imageInfo.RemapPixels.With(context => new CtrSwizzle(context)); + + return imageInfo; + } + + public void Save(Stream output, ImageInfo imageInfo) + { + using var bw = new BinaryWriterX(output); + + // Calculate offsets + var dataOffset = 0x80; + + // Write image data + output.Position = dataOffset; + bw.Write(imageInfo.ImageData); + + // Update header + _header.format = (short)imageInfo.ImageFormat; + _header.width = (short)imageInfo.ImageSize.Width; + _header.height = (short)imageInfo.ImageSize.Height; + + // Write header + output.Position = 0; + bw.WriteType(_header); + } + } +} diff --git a/plugins/Nintendo/plugin_nintendo/Images/3dstPlugin.cs b/plugins/Nintendo/plugin_nintendo/Images/3dstPlugin.cs new file mode 100644 index 00000000..270e935e --- /dev/null +++ b/plugins/Nintendo/plugin_nintendo/Images/3dstPlugin.cs @@ -0,0 +1,40 @@ +using System; +using System.Threading.Tasks; +using Komponent.IO; +using Kontract.Interfaces.FileSystem; +using Kontract.Interfaces.Managers; +using Kontract.Interfaces.Plugins.Identifier; +using Kontract.Interfaces.Plugins.State; +using Kontract.Models; +using Kontract.Models.Context; +using Kontract.Models.IO; + +namespace plugin_nintendo.Images +{ + public class _3dstPlugin : IFilePlugin, IIdentifyFiles + { + public Guid PluginId => Guid.Parse("1fa3a56c-864c-4618-9dfc-22734b91d4c5"); + public PluginType PluginType => PluginType.Image; + public string[] FileExtensions => new[] { "*.3dst" }; + public PluginMetadata Metadata { get; } + + public _3dstPlugin() + { + Metadata = new PluginMetadata("3DST", "DaniElectra", "The image format used for textures and thumbnails on Nintendo Anime Channel."); + } + + public async Task IdentifyAsync(IFileSystem fileSystem, UPath filePath, IdentifyContext identifyContext) + { + var fileStream = await fileSystem.OpenFileAsync(filePath); + + using var br = new BinaryReaderX(fileStream); + var magic = br.ReadString(7); + return magic == "texture"; + } + + public IPluginState CreatePluginState(IBaseFileManager fileManager) + { + return new _3dstState(); + } + } +} diff --git a/plugins/Nintendo/plugin_nintendo/Images/3dstState.cs b/plugins/Nintendo/plugin_nintendo/Images/3dstState.cs new file mode 100644 index 00000000..932ee546 --- /dev/null +++ b/plugins/Nintendo/plugin_nintendo/Images/3dstState.cs @@ -0,0 +1,50 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Kanvas; +using Kontract.Interfaces.FileSystem; +using Kontract.Interfaces.Plugins.State; +using Kontract.Kanvas; +using Kontract.Models.Context; +using Kontract.Models.Image; +using Kontract.Models.IO; + +namespace plugin_nintendo.Images +{ + class _3dstState : IImageState, ILoadFiles, ISaveFiles + { + private _3dst _3dst; + + public EncodingDefinition EncodingDefinition { get; } + public IList Images { get; private set; } + + public bool ContentChanged => IsContentChanged(); + + public _3dstState() + { + _3dst = new _3dst(); + + EncodingDefinition = _3dstSupport.GetEncodingDefinition(); + } + + public async Task Load(IFileSystem fileSystem, UPath filePath, LoadContext loadContext) + { + var fileStream = await fileSystem.OpenFileAsync(filePath); + Images = new List { new KanvasImage(EncodingDefinition, _3dst.Load(fileStream)) }; + } + + public Task Save(IFileSystem fileSystem, UPath savePath, SaveContext saveContext) + { + var fileStream = fileSystem.OpenFile(savePath, FileMode.Create, FileAccess.Write); + _3dst.Save(fileStream, Images[0].ImageInfo); + + return Task.CompletedTask; + } + + private bool IsContentChanged() + { + return Images.Any(x => x.ContentChanged); + } + } +} diff --git a/plugins/Nintendo/plugin_nintendo/Images/3dstSupport.cs b/plugins/Nintendo/plugin_nintendo/Images/3dstSupport.cs new file mode 100644 index 00000000..3f243300 --- /dev/null +++ b/plugins/Nintendo/plugin_nintendo/Images/3dstSupport.cs @@ -0,0 +1,42 @@ +using System.Collections.Generic; +using Kanvas; +using Komponent.IO.Attributes; +using Kontract.Kanvas; +using Kontract.Models.Image; + +namespace plugin_nintendo.Images +{ + class _3dstHeader + { + [FixedLength(7)] + public string magic; + public int zero1; + public int zero2; + public byte zero3; + public short width; + public short height; + public short format; + } + + class _3dstSupport + { + private static readonly IDictionary Formats = new Dictionary + { + [0] = ImageFormats.Rgba8888(), + [1] = ImageFormats.Rgb888(), + [2] = ImageFormats.A8(), + [4] = ImageFormats.Etc1(true), + [5] = ImageFormats.Rgba5551(), + [6] = ImageFormats.Rgb565(), + [7] = ImageFormats.Rgba4444() + }; + + public static EncodingDefinition GetEncodingDefinition() + { + var definition = new EncodingDefinition(); + definition.AddColorEncodings(Formats); + + return definition; + } + } +}