Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement 3DST (Nintendo Anime Channel) support #262

Open
wants to merge 5 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions plugins/Nintendo/plugin_nintendo/Images/3dst.cs
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
40 changes: 40 additions & 0 deletions plugins/Nintendo/plugin_nintendo/Images/3dstPlugin.cs
Original file line number Diff line number Diff line change
@@ -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<bool> 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();
}
}
}
50 changes: 50 additions & 0 deletions plugins/Nintendo/plugin_nintendo/Images/3dstState.cs
Original file line number Diff line number Diff line change
@@ -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<IKanvasImage> 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<IKanvasImage> { 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);
}
}
}
42 changes: 42 additions & 0 deletions plugins/Nintendo/plugin_nintendo/Images/3dstSupport.cs
Original file line number Diff line number Diff line change
@@ -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<int, IColorEncoding> Formats = new Dictionary<int, IColorEncoding>
{
[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;
}
}
}