Merge main into 'SkinPackConversion'

This commit is contained in:
miku-666
2023-04-23 20:19:07 +02:00
33 changed files with 709 additions and 954 deletions

View File

@@ -10,7 +10,7 @@ using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using OMI.Formats.Pck;
using PckStudio.Classes.Conversion.Legacy.JsonDefinitions;
using PckStudio.Classes.Extentions;
using PckStudio.Extensions;
using PckStudio.Conversion.Common.JsonDefinitions;
namespace PckStudio.Conversion.Legacy

View File

@@ -0,0 +1,13 @@
namespace PckStudio.Extensions
{
public enum BlendMode
{
Add,
Subtract,
Multiply,
Average,
DescendingOrder,
AscendingOrder,
Screen
}
}

View File

@@ -1,8 +1,8 @@
using System.Collections.Generic;
namespace PckStudio.Classes.Extentions
namespace PckStudio.Extensions
{
internal static class EnumerableExtentions
internal static class EnumerableExtensions
{
public static IEnumerable<(int index, T type)>enumerate<T>(this IEnumerable<T> array)
{

View File

@@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
namespace PckStudio.Extensions
{
public struct GraphicsConfig
{
public GraphicsConfig()
{
CompositingQuality = CompositingQuality.Default;
InterpolationMode = InterpolationMode.Default;
SmoothingMode = SmoothingMode.Default;
PixelOffsetMode = PixelOffsetMode.Default;
CompositingMode = CompositingMode.SourceOver;
}
public CompositingMode CompositingMode { get; set; }
public CompositingQuality CompositingQuality { get; set; }
public InterpolationMode InterpolationMode { get; set; }
public SmoothingMode SmoothingMode { get; set; }
public PixelOffsetMode PixelOffsetMode { get; set; }
}
internal static class GraphicsExtensions
{
public static void ApplyConfig(this Graphics graphics, GraphicsConfig config)
{
graphics.CompositingMode = config.CompositingMode;
graphics.CompositingQuality = config.CompositingQuality;
graphics.InterpolationMode = config.InterpolationMode;
graphics.SmoothingMode = config.SmoothingMode;
graphics.PixelOffsetMode = config.PixelOffsetMode;
}
}
}

View File

@@ -0,0 +1,241 @@
using System;
using System.Drawing;
using System.Diagnostics;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Linq;
namespace PckStudio.Extensions
{
public enum ImageLayoutDirection
{
Horizontal,
Vertical
}
internal static class ImageExtensions
{
private struct ImageLayoutInfo
{
/// <summary>
/// Size of sub section of the image
/// </summary>
public readonly Size SectionSize;
public readonly Point SectionPoint;
public readonly Rectangle SectionArea;
public ImageLayoutInfo(int width, int height, int index, ImageLayoutDirection layoutDirection)
{
bool horizontal = layoutDirection == ImageLayoutDirection.Horizontal;
SectionSize = horizontal ? new Size(width, width) : new Size(height, height);
SectionPoint = horizontal ? new Point(0, index * width) : new Point(index * height, 0);
SectionArea = new Rectangle(SectionPoint, SectionSize);
}
}
public static Image GetArea(this Image source, Rectangle area)
{
Image tileImage = new Bitmap(area.Width, area.Height);
using (Graphics gfx = Graphics.FromImage(tileImage))
{
gfx.SmoothingMode = SmoothingMode.None;
gfx.InterpolationMode = InterpolationMode.NearestNeighbor;
gfx.PixelOffsetMode = PixelOffsetMode.HighQuality;
gfx.DrawImage(source, new Rectangle(Point.Empty, area.Size), area, GraphicsUnit.Pixel);
}
return tileImage;
}
public static IEnumerable<Image> CreateImageList(this Image source, Size size)
{
int rowCount = source.Width / size.Width;
int columnCount = source.Height / size.Height;
Debug.WriteLine($"{source.Width} {source.Height} {size} {columnCount} {rowCount}");
for (int i = 0; i < columnCount * rowCount; i++)
{
int row = Math.DivRem(i, rowCount, out int column);
Rectangle tileArea = new Rectangle(new Point(column * size.Height, row * size.Width), size);
yield return source.GetArea(tileArea);
}
yield break;
}
public static IEnumerable<Image> CreateImageList(this Image source, int scalar)
{
return CreateImageList(source, new Size(scalar, scalar));
}
public static IEnumerable<Image> CreateImageList(this Image source, ImageLayoutDirection layoutDirection)
{
for (int i = 0; i < source.Height / source.Width; i++)
{
ImageLayoutInfo locationInfo = new ImageLayoutInfo(source.Width, source.Height, i, layoutDirection);
yield return source.GetArea(locationInfo.SectionArea);
}
yield break;
}
public static Image ImageFromImageArray(Image[] sources, ImageLayoutDirection layoutDirection)
{
Size imageSize = CalculateImageSize(sources, layoutDirection);
var result = new Bitmap(imageSize.Width, imageSize.Height);
using (var graphic = Graphics.FromImage(result))
{
foreach (var (i, texture) in sources.enumerate())
{
var info = new ImageLayoutInfo(imageSize.Width, imageSize.Height, i, layoutDirection);
graphic.DrawImage(texture, info.SectionPoint);
};
}
return result;
}
private static Size CalculateImageSize(Image[] sources, ImageLayoutDirection layoutDirection)
{
var horizontal = layoutDirection == ImageLayoutDirection.Horizontal;
int width = sources[0].Width;
int height = sources[0].Height;
if (!sources.All(img => img.Width.Equals(width) && img.Height.Equals(height)))
throw new InvalidOperationException("Images must have the same width and height.");
if (horizontal)
width *= sources.Length;
else
height *= sources.Length;
return new Size(width, height);
}
public static Image ResizeImage(this Image image, int width, int height, GraphicsConfig graphicsConfig)
{
var destRect = new Rectangle(0, 0, width, height);
var destImage = new Bitmap(width, height);
destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);
using (var graphics = Graphics.FromImage(destImage))
{
graphics.ApplyConfig(graphicsConfig);
using (var wrapMode = new ImageAttributes())
{
wrapMode.SetWrapMode(WrapMode.TileFlipXY);
graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode);
}
}
return destImage;
}
public static Image Fill(this Image image, Color color)
{
using (var g = Graphics.FromImage(image))
{
using (SolidBrush brush = new SolidBrush(color))
{
g.FillRectangle(brush, new Rectangle(Point.Empty, image.Size));
}
}
return image;
}
public static Image Blend(this Image image, Color foregroundColor, BlendMode mode)
{
if (image is not Bitmap baseImage)
return image;
BitmapData baseImageData = baseImage.LockBits(new Rectangle(Point.Empty, baseImage.Size),
ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
byte[] baseImageBuffer = new byte[baseImageData.Stride * baseImageData.Height];
Marshal.Copy(baseImageData.Scan0, baseImageBuffer, 0, baseImageBuffer.Length);
float overlayR = foregroundColor.R / 255f;
float overlayG = foregroundColor.G / 255f;
float overlayB = foregroundColor.B / 255f;
for (int k = 0; k < baseImageBuffer.Length; k += 4)
{
baseImageBuffer[k + 0] = CalculateColorComponentBlendValue(baseImageBuffer[k + 0] / 255f, overlayR, mode);
baseImageBuffer[k + 1] = CalculateColorComponentBlendValue(baseImageBuffer[k + 1] / 255f, overlayG, mode);
baseImageBuffer[k + 2] = CalculateColorComponentBlendValue(baseImageBuffer[k + 2] / 255f, overlayB, mode);
}
Bitmap bitmapResult = new Bitmap(baseImage.Width, baseImage.Height, PixelFormat.Format32bppArgb);
BitmapData resultImageData = bitmapResult.LockBits(new Rectangle(Point.Empty, bitmapResult.Size),
ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
Marshal.Copy(baseImageBuffer, 0, resultImageData.Scan0, baseImageBuffer.Length);
bitmapResult.UnlockBits(resultImageData);
baseImage.UnlockBits(baseImageData);
return bitmapResult;
}
public static Image Blend(this Image image, Image overlay, BlendMode mode)
{
if (image is not Bitmap baseImage || overlay is not Bitmap overlayImage ||
image.Width != overlay.Width || image.Height != overlay.Height)
return image;
BitmapData baseImageData = baseImage.LockBits(new Rectangle(Point.Empty, baseImage.Size),
ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
byte[] baseImageBuffer = new byte[baseImageData.Stride * baseImageData.Height];
Marshal.Copy(baseImageData.Scan0, baseImageBuffer, 0, baseImageBuffer.Length);
BitmapData overlayImageData = overlayImage.LockBits(new Rectangle(Point.Empty, overlayImage.Size),
ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
byte[] overlayImageBuffer = new byte[overlayImageData.Stride * overlayImageData.Height];
Marshal.Copy(overlayImageData.Scan0, overlayImageBuffer, 0, overlayImageBuffer.Length);
for (int k = 0; k < baseImageBuffer.Length && k < overlayImageBuffer.Length; k += 4)
{
baseImageBuffer[k + 0] = CalculateColorComponentBlendValue(baseImageBuffer[k + 0] / 255f, overlayImageBuffer[k + 0] / 255f, mode);
baseImageBuffer[k + 1] = CalculateColorComponentBlendValue(baseImageBuffer[k + 1] / 255f, overlayImageBuffer[k + 1] / 255f, mode);
baseImageBuffer[k + 2] = CalculateColorComponentBlendValue(baseImageBuffer[k + 2] / 255f, overlayImageBuffer[k + 2] / 255f, mode);
}
Bitmap bitmapResult = new Bitmap(baseImage.Width, baseImage.Height, PixelFormat.Format32bppArgb);
BitmapData resultImageData = bitmapResult.LockBits(new Rectangle(Point.Empty, bitmapResult.Size),
ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
Marshal.Copy(baseImageBuffer, 0, resultImageData.Scan0, baseImageBuffer.Length);
bitmapResult.UnlockBits(resultImageData);
baseImage.UnlockBits(baseImageData);
overlayImage.UnlockBits(overlayImageData);
return bitmapResult;
}
private static T Clamp<T>(T value, T min, T max) where T : IComparable<T>
{
if (value.CompareTo(min) < 0) return min;
else if (value.CompareTo(max) > 0) return max;
else return value;
}
private static byte CalculateColorComponentBlendValue(float source, float overlay, BlendMode blendType)
{
source = Clamp(source, 0.0f, 1.0f);
overlay = Clamp(overlay, 0.0f, 1.0f);
float resultValue = blendType switch
{
BlendMode.Add => source + overlay,
BlendMode.Subtract => source - overlay,
BlendMode.Multiply => source * overlay,
BlendMode.Average => (source + overlay) / 2.0f,
BlendMode.AscendingOrder => source > overlay ? overlay : source,
BlendMode.DescendingOrder => source < overlay ? overlay : source,
BlendMode.Screen => 1f - (1f - source)*(1f - overlay),
_ => 0.0f
};
return (byte)Clamp(resultValue * 255, 0, 255);
}
}
}

View File

@@ -1,8 +1,8 @@
using System.Collections.Generic;
namespace PckStudio.Forms.Utilities
namespace PckStudio.Extensions
{
public static class ListUtils
public static class ListExtensions
{
public static IList<T> Swap<T>(this IList<T> list, int index1, int index2)
{

View File

@@ -1,143 +0,0 @@
using System.Collections.Generic;
using System.Drawing.Drawing2D;
using System.Drawing;
using System.Diagnostics;
using System;
using System.Drawing.Imaging;
namespace PckStudio.Classes.Extentions
{
internal static class ImageExtentions
{
public enum ImageLayoutDirection
{
Horizontal,
Vertical
}
private struct ImageLayoutInfo
{
/// <summary>
/// Size of sub section of the image
/// </summary>
public Size SectionSize;
public Point SectionPoint;
public ImageLayoutInfo(int width, int height, int index, ImageLayoutDirection layoutDirection)
{
bool horizontal = layoutDirection == ImageLayoutDirection.Horizontal;
SectionSize = horizontal ? new Size(width, width) : new Size(height, height);
SectionPoint = horizontal ? new Point(0, index * width) : new Point(index * height, 0);
}
}
private static Image GetTileImage(Image source, Rectangle area, Size size, GraphicsUnit unit = GraphicsUnit.Pixel)
{
Image tileImage = new Bitmap(size.Width, size.Height);
using (Graphics gfx = Graphics.FromImage(tileImage))
{
gfx.SmoothingMode = SmoothingMode.None;
gfx.InterpolationMode = InterpolationMode.NearestNeighbor;
gfx.PixelOffsetMode = PixelOffsetMode.HighQuality;
gfx.DrawImage(source, new Rectangle(Point.Empty, size), area, unit);
}
return tileImage;
}
public static IEnumerable<Image> CreateImageList(this Image source, Size size)
{
int img_row_count = source.Width / size.Width;
int img_column_count = source.Height / size.Height;
Debug.WriteLine($"{source.Width} {source.Height} {size} {img_column_count} {img_row_count}");
for (int i = 0; i < img_column_count * img_row_count; i++)
{
int row = i / img_row_count;
int column = i % img_row_count;
Rectangle tileArea = new Rectangle(new Point(column * size.Height, row * size.Width), size);
yield return GetTileImage(source, tileArea, size);
}
yield break;
}
public static IEnumerable<Image> CreateImageList(this Image source, int scalar)
{
return CreateImageList(source, new Size(scalar, scalar));
}
public static IEnumerable<Image> CreateImageList(this Image source, ImageLayoutDirection layoutDirection)
{
for (int i = 0; i < source.Height / source.Width; i++)
{
ImageLayoutInfo locationInfo = new ImageLayoutInfo(source.Width, source.Height, i, layoutDirection);
Rectangle tileArea = new Rectangle(locationInfo.SectionPoint, locationInfo.SectionSize);
yield return GetTileImage(source, tileArea, locationInfo.SectionSize);
}
yield break;
}
public static Image ImageFromImageArray(Image[] sources, ImageLayoutDirection layoutDirection)
{
Size imageSize = CalculateImageSize(sources, layoutDirection);
var result = new Bitmap(imageSize.Width, imageSize.Height);
using (var graphic = Graphics.FromImage(result))
{
foreach (var (i, texture) in sources.enumerate())
{
var info = new ImageLayoutInfo(imageSize.Width, imageSize.Height, i, layoutDirection);
graphic.DrawImage(texture, info.SectionPoint);
};
}
return result;
}
private static Size CalculateImageSize(Image[] sources, ImageLayoutDirection layoutDirection)
{
var horizontal = layoutDirection == ImageLayoutDirection.Horizontal;
// TODO: Validate all source images to be the same size.
int width = sources[0].Width;
int heigh = sources[0].Height;
if (horizontal)
width *= sources.Length;
else
heigh *= sources.Length;
return new Size(width, heigh);
}
public struct GraphicsConfig
{
public CompositingMode CompositingMode { get; set; }
public CompositingQuality CompositingQuality { get; set; }
public InterpolationMode InterpolationMode { get; set; }
public SmoothingMode SmoothingMode {get; set; }
public PixelOffsetMode PixelOffsetMode { get; set; }
}
public static Image ResizeImage(this Image image, int width, int height, GraphicsConfig graphicsConfig)
{
var destRect = new Rectangle(0, 0, width, height);
var destImage = new Bitmap(width, height);
destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);
using (var graphics = Graphics.FromImage(destImage))
{
graphics.CompositingMode = graphicsConfig.CompositingMode;
graphics.CompositingQuality = graphicsConfig.CompositingQuality;
graphics.InterpolationMode = graphicsConfig.InterpolationMode;
graphics.SmoothingMode = graphicsConfig.SmoothingMode;
graphics.PixelOffsetMode = graphicsConfig.PixelOffsetMode;
using (var wrapMode = new ImageAttributes())
{
wrapMode.SetWrapMode(WrapMode.TileFlipXY);
graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode);
}
}
return destImage;
}
}
}

View File

@@ -1,91 +0,0 @@
using System;
using System.Drawing;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using PckStudio.Models;
namespace PckStudio.Classes
{
class CSM
{
//Part Name
//Part Parent(HEAD, BODY, LEG0, LEG1, ARM0, ARM1)
//Part Name
//Position-X
//Position-Y
//Position-Z
//Size-X
//Size-Y
//Size-Z
//UV-Y
//UV-X
public static List<string> CSMBlock = new List<string>();
public static void TryParse(string CSM, MinecraftModelView modelView)
{
try
{
int i = 0;
string[] CSMLines = CSM.Split(new[] { "\n", "\r\n" }, StringSplitOptions.None);
foreach (string line in CSMLines)
{
if (i > 10)
{
GetModelPartFromCSM(CSMBlock, modelView);
CSMBlock.Clear();
i = 0;
}
CSMBlock.Add(line + "\n");
i++;
}
modelView.Invalidate();
}
catch { }
}
public static void GetModelPartFromCSM(List<string> CSM, MinecraftModelView modelView)
{
string PartName = CSM[0];
string PartParent = CSM[1];
string PartName2 = CSM[2];
int PositionX = int.Parse(CSM[3]);
int PositionY = int.Parse(CSM[4]);
int PositionZ = int.Parse(CSM[5]);
int SizeX = int.Parse(CSM[6]);
int SizeY = int.Parse(CSM[7]);
int SizeZ = int.Parse(CSM[8]);
int UVY = int.Parse(CSM[9]);
int UVX = int.Parse(CSM[10]);
//RenderBox
System.Drawing.Image source = Textures[0].Source;
Object3D object3D = new Box(source, new Rectangle(8, 0, 0x10, 8), new Rectangle(0, 8, 0x20, 8), new Point3D(0f, 0f, 0f), Effects.None);
Object3D object3D2 = new Box(source, new Rectangle(0x28, 0, 0x10, 8), new Rectangle(0x20, 8, 0x20, 8), new Point3D(0f, 0f, 0f), Effects.None);
Object3D object3D3 = new Box(source, new Rectangle(0x2C, 0x10, 8, 4), new Rectangle(0x28, 0x14, 0x20, 0xC), new Point3D(0f, 4f, 0f), Effects.FlipHorizontally);
//RenderGroup
Object3DGroup object3DGroup = new Object3DGroup();
object3D2.Scale = 1.16f;
object3DGroup.RotationOrder = RotationOrders.XY;
object3DGroup.MinDegrees1 = -80f;
object3DGroup.MaxDegrees1 = 80f;
object3DGroup.MinDegrees2 = -57f;
object3DGroup.MaxDegrees2 = 57f;
object3DGroup.Add(object3D);
object3DGroup.Add(object3D2);
object3DGroup.Position = new Point3D(0f, 8f, 0f);
object3DGroup.Origin = new Point3D(0f, -4f, 0f);
object3DGroup.RotationOrder = RotationOrders.XY;
modelView.AddDynamic(object3DGroup);
}
public static Texture[] Textures = new Texture[] { new Texture(Bitmap.FromFile(Environment.CurrentDirectory + "\\default.png")) };
}
}

View File

@@ -0,0 +1,94 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OMI.Workers;
using PckStudio.Classes._3ds;
using OMI;
namespace PckStudio.Classes.IO._3DST
{
internal class _3DSTextureReader : IDataFormatReader<Image>, IDataFormatReader
{
public Image FromFile(string filename)
{
if (File.Exists(filename))
{
Image img = null;
using (var fs = File.OpenRead(filename))
{
img = FromStream(fs);
}
return img;
}
throw new FileNotFoundException(filename);
}
public Image FromStream(Stream stream)
{
Image img = null;
using (var reader = new EndiannessAwareBinaryReader(stream, Encoding.ASCII, leaveOpen: true, Endianness.LittleEndian))
{
if (reader.ReadString(4) == "3DST")
{
const int offset = 32;
reader.ReadInt32(); // unknown value
_3DSTextureFormat format = reader.ReadInt32() switch
{
0 => _3DSTextureFormat.argb8,
1 => _3DSTextureFormat.rgb8,
2 => _3DSTextureFormat.rgba5551,
3 => _3DSTextureFormat.rgb8,
4 => _3DSTextureFormat.rgba4,
9 => _3DSTextureFormat.la4,
_ => _3DSTextureFormat.dontCare,
};
int width = reader.ReadInt32();
int height = reader.ReadInt32();
int bufferSize = CalcBufferSize(format, width, height);
stream.Seek(offset, SeekOrigin.Begin);
byte[] buffer = new byte[bufferSize];
reader.Read(buffer, 0, bufferSize);
img = TextureCodec.Decode(buffer, width, height, format);
img.RotateFlip(RotateFlipType.RotateNoneFlipY);
}
}
return img;
}
private static int CalcBufferSize(_3DSTextureFormat textureFormat, int width, int height)
{
switch (textureFormat)
{
case _3DSTextureFormat.argb8:
return width * height * 4;
case _3DSTextureFormat.rgb8:
return width * height * 3;
case _3DSTextureFormat.rgba5551:
case _3DSTextureFormat.rgb565:
case _3DSTextureFormat.rgba4:
case _3DSTextureFormat.la8:
case _3DSTextureFormat.hilo8:
return width * height * 2;
case _3DSTextureFormat.l8:
case _3DSTextureFormat.a8:
case _3DSTextureFormat.la4:
case _3DSTextureFormat.etc1a4:
return width * height;
case _3DSTextureFormat.l4:
case _3DSTextureFormat.a4:
case _3DSTextureFormat.etc1:
return width * height >> 1;
default:
throw new InvalidDataException("Invalid texture format on BCH!");
}
}
object IDataFormatReader.FromFile(string filename) => FromFile(filename);
object IDataFormatReader.FromStream(Stream stream) => FromStream(stream);
}
}

View File

@@ -0,0 +1,47 @@
using System;
using System.Drawing;
using System.IO;
using System.Text;
using OMI;
using OMI.Workers;
using PckStudio.Classes._3ds;
namespace PckStudio.Classes.IO._3DST
{
internal class _3DSTextureWriter : IDataFormatWriter
{
private Image _image;
private _3DSTextureFormat _format;
public _3DSTextureWriter(Image image, _3DSTextureFormat format = _3DSTextureFormat.argb8)
{
_image = image;
_format = format;
}
public void WriteToFile(string filename)
{
using(var fs = File.OpenWrite(filename))
{
WriteToStream(fs);
}
}
public void WriteToStream(Stream stream)
{
using (var writer = new EndiannessAwareBinaryWriter(stream, Encoding.ASCII, leaveOpen: true, Endianness.LittleEndian))
{
writer.WriteString("3DST"); // 0
writer.Write(2); // 4 unknown
writer.Write((int)_format); // 8
writer.Write(_image.Width); // 12
writer.Write(_image.Height); // 16
writer.Write(0); // 20
writer.Write(0); // 24
writer.Write(0); // 28
_image.RotateFlip(RotateFlipType.RotateNoneFlipY);
byte[] buffer = TextureCodec.Encode(new Bitmap(_image), _format);
stream.Write(buffer, 0, buffer.Length);
}
}
}
}

View File

@@ -2,14 +2,35 @@
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using PckStudio.Classes._3ds.Utils;
namespace PckStudio.Classes._3ds
{
/// <summary>
/// Format of the texture used on the PICA200.
/// </summary>
public enum _3DSTextureFormat
{
argb8 = 0,
rgb8 = 1,
rgba5551 = 2,
rgb565 = 3,
rgba4 = 4,
la8 = 5,
hilo8 = 6,
l8 = 7,
a8 = 8,
la4 = 9,
l4 = 0xa,
a4 = 0xb,
etc1 = 0xc,
etc1a4 = 0xd,
dontCare
}
class TextureCodec
{
private static int[] tileOrder = { 0, 1, 8, 9, 2, 3, 10, 11, 16, 17, 24, 25, 18, 19, 26, 27, 4, 5, 12, 13, 6, 7, 14, 15, 20, 21, 28, 29, 22, 23, 30, 31, 32, 33, 40, 41, 34, 35, 42, 43, 48, 49, 56, 57, 50, 51, 58, 59, 36, 37, 44, 45, 38, 39, 46, 47, 52, 53, 60, 61, 54, 55, 62, 63 };
private static int[,] etc1LUT = { { 2, 8, -2, -8 }, { 5, 17, -5, -17 }, { 9, 29, -9, -29 }, { 13, 42, -13, -42 }, { 18, 60, -18, -60 }, { 24, 80, -24, -80 }, { 33, 106, -33, -106 }, { 47, 183, -47, -183 } };
private static readonly int[] tileOrder = { 0, 1, 8, 9, 2, 3, 10, 11, 16, 17, 24, 25, 18, 19, 26, 27, 4, 5, 12, 13, 6, 7, 14, 15, 20, 21, 28, 29, 22, 23, 30, 31, 32, 33, 40, 41, 34, 35, 42, 43, 48, 49, 56, 57, 50, 51, 58, 59, 36, 37, 44, 45, 38, 39, 46, 47, 52, 53, 60, 61, 54, 55, 62, 63 };
private static readonly int[,] etc1LUT = { { 2, 8, -2, -8 }, { 5, 17, -5, -17 }, { 9, 29, -9, -29 }, { 13, 42, -13, -42 }, { 18, 60, -18, -60 }, { 24, 80, -24, -80 }, { 33, 106, -33, -106 }, { 47, 183, -47, -183 } };
private static class TextureConverter
{

View File

@@ -1,57 +1,66 @@
using System.IO;
using System.Text;
using OMI;
using OMI.Workers;
using PckStudio.Classes.FileTypes;
namespace PckStudio.Classes.IO.CSMB
{
internal class CSMBFileReader : StreamDataReader<CSMBFile>
internal class CSMBFileReader : IDataFormatReader<CSMBFile>, IDataFormatReader
{
public static CSMBFile Read(Stream stream)
{
return new CSMBFileReader().ReadFromStream(stream);
}
private CSMBFileReader() : base(false)
private CSMBFileReader()
{ }
protected override CSMBFile ReadFromStream(Stream stream)
public CSMBFile FromFile(string filename)
{
CSMBFile BinFile = new CSMBFile();
ReadInt(stream);
int NumOfParts = ReadInt(stream);
for(int i = 0; i < NumOfParts; i++)
{
CSMBPart part = new CSMBPart();
part.Name = ReadString(stream);
part.Parent = (CSMBParentPart)ReadInt(stream);
part.posX = ReadFloat(stream);
part.posY = ReadFloat(stream);
part.posZ = ReadFloat(stream);
part.sizeX = ReadFloat(stream);
part.sizeY = ReadFloat(stream);
part.sizeZ = ReadFloat(stream);
part.uvX = ReadInt(stream);
part.uvY = ReadInt(stream);
part.MirrorTexture = ReadBool(stream);
part.HideWArmour = ReadBool(stream);
part.Inflation = ReadFloat(stream);
BinFile.Parts.Add(part);
}
int NumOfOffsets = ReadInt(stream);
for (int i = 0; i < NumOfOffsets; i++)
{
CSMBOffset offset = new CSMBOffset();
offset.offsetPart = (CSMBOffsetPart)ReadInt(stream);
offset.VerticalOffset = ReadFloat(stream);
BinFile.Offsets.Add(offset);
}
return BinFile;
throw new System.NotImplementedException();
}
private string ReadString(Stream stream)
public CSMBFile FromStream(Stream stream)
{
short strlen = ReadShort(stream);
return ReadString(stream, strlen, Encoding.ASCII);
CSMBFile csmbFile = new CSMBFile();
using (var reader = new EndiannessAwareBinaryReader(stream, Encoding.ASCII, leaveOpen: true, Endianness.LittleEndian))
{
reader.ReadInt32();
int numOfParts = reader.ReadInt32();
for (int i = 0; i < numOfParts; i++)
{
CSMBPart part = new CSMBPart();
part.Name = ReadString(reader);
part.Parent = (CSMBParentPart)reader.ReadInt32();
part.posX = reader.ReadSingle();
part.posY = reader.ReadSingle();
part.posZ = reader.ReadSingle();
part.sizeX = reader.ReadSingle();
part.sizeY = reader.ReadSingle();
part.sizeZ = reader.ReadSingle();
part.uvX = reader.ReadInt32();
part.uvY = reader.ReadInt32();
part.MirrorTexture = reader.ReadBoolean();
part.HideWArmour = reader.ReadBoolean();
part.Inflation = reader.ReadSingle();
csmbFile.Parts.Add(part);
}
int numOfOffsets = reader.ReadInt32();
for (int i = 0; i < numOfOffsets; i++)
{
CSMBOffset offset = new CSMBOffset();
offset.offsetPart = (CSMBOffsetPart)reader.ReadInt32();
offset.VerticalOffset = reader.ReadSingle();
csmbFile.Offsets.Add(offset);
}
}
return csmbFile;
}
private string ReadString(EndiannessAwareBinaryReader reader)
{
ushort strlen = reader.ReadUInt16();
return reader.ReadString(strlen);
}
object IDataFormatReader.FromStream(Stream stream) => FromStream(stream);
object IDataFormatReader.FromFile(string filename) => FromFile(filename);
}
}

View File

@@ -1,54 +1,58 @@
using System.IO;
using System.Text;
using PckStudio.Classes.FileTypes;
using OMI.Workers;
using OMI;
namespace PckStudio.Classes.IO.CSMB
{
internal class CSMBFileWriter : StreamDataWriter
internal class CSMBFileWriter : IDataFormatWriter
{
CSMBFile _CSMB;
public static void Write(Stream stream, CSMBFile file)
{
new CSMBFileWriter(file).WriteToStream(stream);
}
public CSMBFileWriter(CSMBFile csmb) : base(false)
public CSMBFileWriter(CSMBFile csmb)
{
_CSMB = csmb;
}
protected override void WriteToStream(Stream stream)
public void WriteToFile(string filename)
{
WriteInt(stream, 0);
WriteInt(stream, _CSMB.Parts.Count);
foreach(CSMBPart part in _CSMB.Parts)
using(var fs = File.OpenWrite(filename))
{
WriteString(stream, part.Name);
WriteInt(stream, (int)part.Parent);
WriteFloat(stream, part.posX);
WriteFloat(stream, part.posY);
WriteFloat(stream, part.posZ);
WriteFloat(stream, part.sizeX);
WriteFloat(stream, part.sizeY);
WriteFloat(stream, part.sizeZ);
WriteInt(stream, part.uvX);
WriteInt(stream, part.uvY);
WriteBool(stream, part.MirrorTexture);
WriteBool(stream, part.HideWArmour);
WriteFloat(stream, part.Inflation);
}
WriteInt(stream, _CSMB.Offsets.Count);
foreach (CSMBOffset offset in _CSMB.Offsets)
{
WriteInt(stream, (int)offset.offsetPart);
WriteFloat(stream, offset.VerticalOffset);
WriteToStream(fs);
}
}
private void WriteString(Stream stream, string s)
public void WriteToStream(Stream stream)
{
WriteShort(stream, (short)s.Length);
WriteString(stream, s, Encoding.ASCII);
using (var writer = new EndiannessAwareBinaryWriter(stream, Encoding.ASCII, leaveOpen: true, Endianness.LittleEndian))
{
writer.Write(0);
writer.Write(_CSMB.Parts.Count);
foreach (CSMBPart part in _CSMB.Parts)
{
writer.Write((short)part.Name.Length);
writer.WriteString(part.Name);
writer.Write((int)part.Parent);
writer.Write(part.posX);
writer.Write(part.posY);
writer.Write(part.posZ);
writer.Write(part.sizeX);
writer.Write(part.sizeY);
writer.Write(part.sizeZ);
writer.Write(part.uvX);
writer.Write(part.uvY);
writer.Write(part.MirrorTexture);
writer.Write(part.HideWArmour);
writer.Write(part.Inflation);
}
writer.Write(_CSMB.Offsets.Count);
foreach (CSMBOffset offset in _CSMB.Offsets)
{
writer.Write((int)offset.offsetPart);
writer.Write(offset.VerticalOffset);
}
}
}
}
}

View File

@@ -1,6 +1,9 @@
using PckStudio.Classes.FileTypes;
using OMI;
using OMI.Workers;
using PckStudio.Classes.FileTypes;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
@@ -14,55 +17,73 @@ namespace PckStudio.Classes.IO.PCK
{ }
}
internal class PCKAudioFileReader : StreamDataReader<PCKAudioFile>
internal class PCKAudioFileReader : IDataFormatReader<PCKAudioFile>, IDataFormatReader
{
private PCKAudioFile _file;
private Endianness _endianness;
private List<string> LUT = new List<string>();
private List<PCKAudioFile.AudioCategory.EAudioType> _OriginalAudioTypeOrder = new List<PCKAudioFile.AudioCategory.EAudioType>();
public static PCKAudioFile Read(Stream stream, bool isLittleEndian)
public PCKAudioFileReader(Endianness endianness)
{
return new PCKAudioFileReader(isLittleEndian).ReadFromStream(stream);
_endianness = endianness;
}
private PCKAudioFileReader(bool isLittleEndian) : base(isLittleEndian)
public PCKAudioFile FromFile(string filename)
{
if(File.Exists(filename))
{
PCKAudioFile file;
using(var fs = File.OpenRead(filename))
{
file = FromStream(fs);
}
return file;
}
throw new FileNotFoundException(filename);
}
protected override PCKAudioFile ReadFromStream(Stream stream)
public PCKAudioFile FromStream(Stream stream)
{
int pck_type = ReadInt(stream);
if (pck_type > 0xf00000) // 03 00 00 00 == true
throw new OverflowException(nameof(pck_type));
if (pck_type > 1)
throw new InvalidAudioPckException(nameof(pck_type));
_file = new PCKAudioFile();
ReadLookUpTable(stream);
ReadCategories(stream);
ReadCategorySongs(stream);
using (var reader = new EndiannessAwareBinaryReader(stream,
_endianness == Endianness.BigEndian
? Encoding.BigEndianUnicode
: Encoding.Unicode,
leaveOpen: true, _endianness))
{
int pck_type = reader.ReadInt32();
if (pck_type > 0xf00000) // 03 00 00 00 == true
throw new OverflowException(nameof(pck_type));
if (pck_type > 1)
throw new InvalidAudioPckException(nameof(pck_type));
_file = new PCKAudioFile();
ReadLookUpTable(reader);
ReadCategories(reader);
ReadCategorySongs(reader);
}
return _file;
}
private void ReadLookUpTable(Stream stream)
private void ReadLookUpTable(EndiannessAwareBinaryReader reader)
{
int count = ReadInt(stream);
int count = reader.ReadInt32();
LUT = new List<string>(count);
for (int i = 0; i < count; i++)
{
int index = ReadInt(stream);
LUT.Insert(index, ReadString(stream));
int index = reader.ReadInt32();
string value = ReadString(reader);
LUT.Insert(index, value);
}
}
private void ReadCategories(Stream stream)
private void ReadCategories(EndiannessAwareBinaryReader reader)
{
int categoryEntryCount = ReadInt(stream);
int categoryEntryCount = reader.ReadInt32();
for (; 0 < categoryEntryCount; categoryEntryCount--)
{
var parameterType = (PCKAudioFile.AudioCategory.EAudioParameterType)ReadInt(stream);
var audioType = (PCKAudioFile.AudioCategory.EAudioType)ReadInt(stream);
string name = ReadString(stream);
var parameterType = (PCKAudioFile.AudioCategory.EAudioParameterType)reader.ReadInt32();
var audioType = (PCKAudioFile.AudioCategory.EAudioType)reader.ReadInt32();
string name = ReadString(reader);
// AddCategory puts the file's categories out of order and causes some songs to be put in the wrong categories
// This is my simple fix for the issue.
_OriginalAudioTypeOrder.Add(audioType);
@@ -70,17 +91,17 @@ namespace PckStudio.Classes.IO.PCK
}
}
private void ReadCategorySongs(Stream stream)
private void ReadCategorySongs(EndiannessAwareBinaryReader reader)
{
List<string> credits = new List<string>();
List<string> creditIds = new List<string>();
foreach (var c in _OriginalAudioTypeOrder)
{
int audioCount = ReadInt(stream);
int audioCount = reader.ReadInt32();
for (; 0 < audioCount; audioCount--)
{
string key = LUT[ReadInt(stream)];
string value = ReadString(stream);
string key = LUT[reader.ReadInt32()];
string value = ReadString(reader);
switch (key)
{
case "CUENAME":
@@ -104,12 +125,16 @@ namespace PckStudio.Classes.IO.PCK
}
}
private string ReadString(Stream stream)
private string ReadString(EndiannessAwareBinaryReader reader)
{
int len = ReadInt(stream);
string s = ReadString(stream, len, IsUsingLittleEndian ? Encoding.Unicode : Encoding.BigEndianUnicode);
ReadInt(stream); // padding
int len = reader.ReadInt32();
string s = reader.ReadString(len);
reader.ReadInt32(); // padding
return s;
}
object IDataFormatReader.FromStream(Stream stream) => FromStream(stream);
object IDataFormatReader.FromFile(string filename) => FromFile(filename);
}
}

View File

@@ -1,14 +1,17 @@
using PckStudio.Classes.FileTypes;
using OMI;
using OMI.Workers;
using PckStudio.Classes.FileTypes;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace PckStudio.Classes.IO.PCK
{
internal class PCKAudioFileWriter : StreamDataWriter
internal class PCKAudioFileWriter : IDataFormatWriter
{
private PCKAudioFile _file;
private Endianness _endianness;
private static readonly List<string> LUT = new List<string>
{
"CUENAME",
@@ -16,71 +19,82 @@ namespace PckStudio.Classes.IO.PCK
"CREDITID"
};
public static void Write(Stream stream, PCKAudioFile file, bool isLittleEndian)
{
new PCKAudioFileWriter(file, isLittleEndian).WriteToStream(stream);
}
private PCKAudioFileWriter(PCKAudioFile file, bool isLittleEndian) : base(isLittleEndian)
public PCKAudioFileWriter(PCKAudioFile file, Endianness endianness)
{
_file = file;
_endianness = endianness;
}
protected override void WriteToStream(Stream stream)
public void WriteToFile(string filename)
{
WriteInt(stream, _file.type);
WriteLookUpTable(stream);
WriteCategories(stream);
WriteCategorySongs(stream);
using(var fs = File.OpenWrite(filename))
{
WriteToStream(fs);
}
}
private void WriteString(Stream stream, string s)
public void WriteToStream(Stream stream)
{
WriteInt(stream, s.Length);
WriteString(stream, s, IsUsingLittleEndian ? Encoding.Unicode : Encoding.BigEndianUnicode);
WriteInt(stream, 0); // padding
using (var writer = new EndiannessAwareBinaryWriter(stream,
_endianness == Endianness.BigEndian
? Encoding.BigEndianUnicode
: Encoding.Unicode,
leaveOpen: true, _endianness))
{
writer.Write(_file.type);
WriteLookUpTable(writer);
WriteCategories(writer);
WriteCategorySongs(writer);
}
}
private void WriteLookUpTable(Stream stream)
private void WriteString(EndiannessAwareBinaryWriter writer, string s)
{
WriteInt(stream, 3);
writer.Write(s.Length);
writer.WriteString(s);
writer.Write(0); // padding
}
private void WriteLookUpTable(EndiannessAwareBinaryWriter writer)
{
writer.Write(3);
for (int i = 0; i < 3; i++)
{
WriteInt(stream, i);
WriteString(stream, LUT[i]);
writer.Write(i);
WriteString(writer, LUT[i]);
}
}
private void WriteCategories(Stream stream)
private void WriteCategories(EndiannessAwareBinaryWriter writer)
{
WriteInt(stream, _file.Categories.Length);
writer.Write(_file.Categories.Length);
foreach (var category in _file.Categories)
{
WriteInt(stream, (int)category.parameterType);
WriteInt(stream, (int)category.audioType);
WriteString(stream, category.Name);
writer.Write((int)category.parameterType);
writer.Write((int)category.audioType);
WriteString(writer, category.Name);
}
}
private void WriteCategorySongs(Stream stream)
private void WriteCategorySongs(EndiannessAwareBinaryWriter writer)
{
bool addCredit = true;
foreach (var category in _file.Categories)
{
WriteInt(stream, category.SongNames.Count + (addCredit ? _file.Credits.Count * 2 : 0));
writer.Write(category.SongNames.Count + (addCredit ? _file.Credits.Count * 2 : 0));
foreach (var name in category.SongNames)
{
WriteInt(stream, LUT.IndexOf("CUENAME"));
WriteString(stream, name);
writer.Write(LUT.IndexOf("CUENAME"));
WriteString(writer, name);
}
if (addCredit)
{
foreach (var credit in _file.Credits)
{
WriteInt(stream, LUT.IndexOf("CREDIT"));
WriteString(stream, credit.Value);
WriteInt(stream, LUT.IndexOf("CREDITID"));
WriteString(stream, credit.Key);
writer.Write(LUT.IndexOf("CREDIT"));
WriteString(writer, credit.Value);
writer.Write(LUT.IndexOf("CREDITID"));
WriteString(writer, credit.Key);
}
}
addCredit = false;

View File

@@ -1,92 +0,0 @@
/* Copyright (c) 2022-present miku-666
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1.The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
using PckStudio.Classes.FileTypes;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PckStudio.Classes.IO
{
public abstract class StreamDataReader<T>
{
private static bool useLittleEndian;
protected static bool IsUsingLittleEndian => useLittleEndian;
protected abstract T ReadFromStream(Stream stream);
protected StreamDataReader(bool useLittleEndian)
{
StreamDataReader<T>.useLittleEndian = useLittleEndian;
}
protected static string ReadString(Stream stream, int length, Encoding encoding)
{
byte[] buffer = ReadBytes(stream, length << Convert.ToInt32(encoding is UnicodeEncoding));
return encoding.GetString(buffer).TrimEnd('\0');
}
protected static byte[] ReadBytes(Stream stream, int count)
{
byte[] buffer = new byte[count];
stream.Read(buffer, 0, count);
return buffer;
}
protected static bool ReadBool(Stream stream)
{
return stream.ReadByte() != 0;
}
protected static ushort ReadUShort(Stream stream) => (ushort)ReadShort(stream);
protected static short ReadShort(Stream stream)
{
byte[] bytes = ReadBytes(stream, 2);
if (BitConverter.IsLittleEndian && !useLittleEndian)
Array.Reverse(bytes);
return BitConverter.ToInt16(bytes, 0);
}
protected static uint ReadUInt(Stream stream) => (uint)ReadInt(stream);
protected static int ReadInt(Stream stream)
{
byte[] buffer = ReadBytes(stream, 4);
if (BitConverter.IsLittleEndian && !useLittleEndian)
Array.Reverse(buffer);
return BitConverter.ToInt32(buffer, 0);
}
protected static ulong ReadULong(Stream stream) => (ulong)ReadLong(stream);
protected static long ReadLong(Stream stream)
{
byte[] buffer = ReadBytes(stream, 8);
if (BitConverter.IsLittleEndian && !useLittleEndian)
Array.Reverse(buffer);
return BitConverter.ToInt64(buffer, 0);
}
protected static float ReadFloat(Stream stream)
{
byte[] buffer = ReadBytes(stream, 4);
if (BitConverter.IsLittleEndian && !useLittleEndian)
Array.Reverse(buffer);
return BitConverter.ToSingle(buffer, 0);
}
}
}

View File

@@ -1,89 +0,0 @@
/* Copyright (c) 2022-present miku-666
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1.The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PckStudio.Classes.IO
{
public abstract class StreamDataWriter
{
private static bool useLittleEndian;
protected static bool IsUsingLittleEndian => useLittleEndian;
protected StreamDataWriter(bool littleEndian)
{
useLittleEndian = littleEndian;
}
protected abstract void WriteToStream(Stream stream);
protected static void WriteBool(Stream stream, bool state)
{
stream.WriteByte((byte)(state ? 1 : 0));
}
protected static void WriteUShort(Stream stream, ushort value) => WriteShort(stream, (short)value);
protected static void WriteShort(Stream stream, short value)
{
byte[] bytes = BitConverter.GetBytes(value);
if (BitConverter.IsLittleEndian && !useLittleEndian)
Array.Reverse(bytes);
WriteBytes(stream, bytes, 2);
}
protected static void WriteUInt(Stream stream, uint value) => WriteInt(stream, (int)value);
protected static void WriteInt(Stream stream, int value)
{
byte[] buffer = BitConverter.GetBytes(value);
if (BitConverter.IsLittleEndian && !useLittleEndian)
Array.Reverse(buffer);
WriteBytes(stream, buffer, 4);
}
protected static void WriteULong(Stream stream, ulong value) => WriteLong(stream, (long)value);
protected static void WriteLong(Stream stream, long value)
{
byte[] buffer = BitConverter.GetBytes(value);
if (BitConverter.IsLittleEndian && !useLittleEndian)
Array.Reverse(buffer);
WriteBytes(stream, buffer, 8);
}
protected static void WriteString(Stream stream, string s, Encoding encoding)
=> WriteBytes(stream, encoding.GetBytes(s));
protected static void WriteBytes(Stream stream, byte[] bytes) => WriteBytes(stream, bytes, bytes.Length);
protected static void WriteBytes(Stream stream, byte[] bytes, int count)
{
stream.Write(bytes, 0, count);
}
protected static void WriteFloat(Stream stream, float value)
{
byte[] buffer = BitConverter.GetBytes(value);
if (BitConverter.IsLittleEndian && !useLittleEndian)
Array.Reverse(buffer);
WriteBytes(stream, buffer, 4);
}
}
}

View File

@@ -1,131 +0,0 @@
using System;
using System.Drawing;
using System.IO;
using System.Text;
namespace PckStudio.Classes._3ds.Utils
{
/// <summary>
/// Format of the texture used on the PICA200.
/// </summary>
public enum _3DSTextureFormat
{
argb8 = 0,
rgb8 = 1,
rgba5551 = 2,
rgb565 = 3,
rgba4 = 4,
la8 = 5,
hilo8 = 6,
l8 = 7,
a8 = 8,
la4 = 9,
l4 = 0xa,
a4 = 0xb,
etc1 = 0xc,
etc1a4 = 0xd,
dontCare
}
internal class _3DSUtil
{
private static string ReadString(Stream stream, int len)
{
byte[] buffer = new byte[len];
stream.Read(buffer, 0, len);
return Encoding.ASCII.GetString(buffer);
}
private static int ReadInt32(Stream stream)
{
byte[] buffer = new byte[4];
stream.Read(buffer, 0, 4);
return BitConverter.ToInt32(buffer, 0);
}
private static void WriteString(Stream stream, string s)
{
byte[] buffer = Encoding.ASCII.GetBytes(s);
stream.Write(buffer, 0, buffer.Length);
}
private static void WriteInt32(Stream stream, int value)
{
byte[] buffer = BitConverter.GetBytes(value);
stream.Write(buffer, 0, 4);
}
public static int CalcBufferSize(_3DSTextureFormat textureFormat, int width, int height)
{
switch (textureFormat)
{
case _3DSTextureFormat.argb8:
return width * height * 4;
case _3DSTextureFormat.rgb8:
return width * height * 3;
case _3DSTextureFormat.rgba5551:
case _3DSTextureFormat.rgb565:
case _3DSTextureFormat.rgba4:
case _3DSTextureFormat.la8:
case _3DSTextureFormat.hilo8:
return width * height * 2;
case _3DSTextureFormat.l8:
case _3DSTextureFormat.a8:
case _3DSTextureFormat.la4:
case _3DSTextureFormat.etc1a4:
return width * height;
case _3DSTextureFormat.l4:
case _3DSTextureFormat.a4:
case _3DSTextureFormat.etc1:
return width * height >> 1;
default:
throw new InvalidDataException("Invalid texture format on BCH!");
}
}
public static Image GetImageFrom3DST(Stream stream)
{
if (ReadString(stream, 4) == "3DST")
{
const int offset = 32;
stream.Seek(8L, SeekOrigin.Begin);
_3DSTextureFormat format = ReadInt32(stream) switch
{
0 => _3DSTextureFormat.argb8,
1 => _3DSTextureFormat.rgb8,
2 => _3DSTextureFormat.rgba5551,
3 => _3DSTextureFormat.rgb8,
4 => _3DSTextureFormat.rgba4,
9 => _3DSTextureFormat.la4,
_ => _3DSTextureFormat.dontCare,
};
int width = ReadInt32(stream);
int height = ReadInt32(stream);
int bufferSize = CalcBufferSize(format, width, height);
stream.Seek(offset, SeekOrigin.Begin);
byte[] buffer = new byte[bufferSize];
stream.Read(buffer, 0, bufferSize);
var img = TextureCodec.Decode(buffer, width, height, format);
img.RotateFlip(RotateFlipType.RotateNoneFlipY);
return img;
}
return null;
}
public static void SetImageTo3DST(Stream stream, Image source, _3DSTextureFormat format = _3DSTextureFormat.argb8)
{
// TODO: fix Encoding
WriteString(stream, "3DST"); // 0
WriteInt32(stream, 2); // 4 unknown
WriteInt32(stream, (int)format); // 8
WriteInt32(stream, source.Width); // 12
WriteInt32(stream, source.Height); // 16
WriteInt32(stream, 0); // 20
WriteInt32(stream, 0); // 24
WriteInt32(stream, 0); // 28
source.RotateFlip(RotateFlipType.RotateNoneFlipY);
byte[] buffer = TextureCodec.Encode(new Bitmap(source), format);
stream.Write(buffer, 0, buffer.Length);
}
}
}

View File

@@ -1,98 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace PckStudio.Classes.Utils.ModelConversion
{
public class BedrockJSONtoCSM
{
public string JSONtoCSM(string JsonString)
{
dynamic jsonDe = JsonConvert.DeserializeObject<dynamic>(JsonString);
string NewJSON = JsonConvert.SerializeObject(jsonDe["minecraft:geometry"]);
JObject[] NewJObject = JsonConvert.DeserializeObject<JObject[]>(NewJSON);
string CSMData = "";
foreach (JBone bone in NewJObject[0].bones)
{
int i = 0;
string PARENT = bone.name;
foreach (JCube Cube in bone.cubes)
{
string name = PARENT + " " + i;
float PosXModifier = 0;
float PosYModifier = 0;
float PosZModifier = 0;
switch (PARENT)
{
case "ARM0":
PosXModifier = 5;
PosYModifier = 22;
break;
case "ARM1":
PosXModifier = -5;
PosYModifier = 22;
break;
case "LEG0":
PosXModifier = 1.9f;
PosYModifier = 12;
break;
case "LEG1":
PosXModifier = -1.9f;
PosYModifier = 12;
break;
case "BODY":
PosYModifier = 24;
break;
case "HEAD":
PosYModifier = 24;
break;
}
float PosX = Cube.origin[0] + PosXModifier;
float PosY = Cube.origin[1] + PosYModifier;
float PosZ = Cube.origin[2] + PosZModifier;
float SizeX = Cube.size[0];
float SizeY = Cube.size[1];
float SizeZ = Cube.size[2];
float UvX = Cube.uv[0];
float UvY = Cube.uv[1];
CSMData += name + "\n" + PARENT + "\n" + name + "\n" + PosX + "\n" + PosY + "\n" + PosZ + "\n" + SizeX + "\n" + SizeY + "\n" + SizeZ + "\n" + UvX + "\n" + UvY + "\n";
i++;
}
}
return CSMData;
}
}
internal class WholeJSON
{
public string format_version = "1.12.0";
public Dictionary<string, object> entries = new Dictionary<string, object>();
}
internal class JObject
{
public Dictionary<string, object> description = new Dictionary<string, object>();
public JBone[] bones = { };
}
internal class JBone
{
public string name = "";
public int[] pivot = {0, 0, 0};
public JCube[] cubes = { };
}
internal class JCube
{
public float[] origin = new float[3];
public float[] size = new float [3];
public float[] uv = new float[2];
}
}

View File

@@ -1,105 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace PckStudio.Classes.Utils.ModelConversion
{
public class CSMtoBedrockJSON
{
public string CSMtoJSON(string CSMString)
{
List<string> CSMLIST = new List<string>();
CSMLIST.AddRange(CSMString.Split(new[] { "\n", "\r\n" }, StringSplitOptions.RemoveEmptyEntries));
List<List<string>> CSMS = SplitToSublists(CSMLIST, 11);
JObject jobj = new JObject();
List<JBone> bones = new List<JBone>();
Dictionary<string, List<JCube>> CubeList = new Dictionary<string, List<JCube>>
{
{"HEAD", new List<JCube>()},
{"BODY", new List<JCube>()},
{"ARM0", new List<JCube>()},
{"ARM1", new List<JCube>()},
{"LEG0", new List<JCube>()},
{"LEG1", new List<JCube>()}
};
foreach (List<string> CSMItem in CSMS)
{
JCube NewCube = new JCube();
float PosXModifier = 0;
float PosYModifier = 0;
float PosZModifier = 0;
switch (CSMItem[1])
{
case "ARM0":
PosXModifier = -5;
PosYModifier = -22;
break;
case "ARM1":
PosXModifier = 5;
PosYModifier = -22;
break;
case "LEG0":
PosXModifier = -1.9f;
PosYModifier = -12;
break;
case "LEG1":
PosXModifier = 1.9f;
PosYModifier = -12;
break;
case "BODY":
PosYModifier = -24;
break;
case "HEAD":
PosYModifier = -24;
break;
}
NewCube.origin[0] = float.Parse(CSMItem[3]) + PosXModifier;
NewCube.origin[1] = float.Parse(CSMItem[4]) + PosYModifier;
NewCube.origin[2] = float.Parse(CSMItem[5]) + PosZModifier;
NewCube.size[0] = float.Parse(CSMItem[6]);
NewCube.size[1] = float.Parse(CSMItem[7]);
NewCube.size[2] = float.Parse(CSMItem[8]);
NewCube.uv[0] = float.Parse(CSMItem[9]);
NewCube.uv[1] = float.Parse(CSMItem[10]);
CubeList[CSMItem[1]].Add(NewCube);
}
foreach (KeyValuePair<string, List<JCube>> bone in CubeList)
{
JBone jb = new JBone();
jb.name = bone.Key;
jb.cubes = bone.Value.ToArray();
bones.Add(jb);
}
jobj.bones = bones.ToArray();
jobj.description.Add("identifier", "geometry.steve");
jobj.description.Add("texture_width", 64);
jobj.description.Add("texture_height", 64);
jobj.description.Add("visible_bounds_width", 2);
jobj.description.Add("visible_bounds_height", 3.5f);
jobj.description.Add("visible_bounds_offset", new float[] { 0, 1.25f, 0 });
WholeJSON WJ = new WholeJSON();
WJ.entries.Add("format_version", "1.12.0");
WJ.entries.Add("minecraft:geometry", new JObject[] { jobj });
string JSONDATA = JsonConvert.SerializeObject(WJ.entries, Formatting.Indented);
return JSONDATA;
}
public List<List<string>> SplitToSublists(List<string> source, int size)
{
return source
.Select((x, i) => new { Index = i, Value = x })
.GroupBy(x => x.Index / size)
.Select(x => x.Select(v => v.Value).ToList())
.ToList();
}
}
}

View File

@@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using PckStudio.Classes.Extentions;
using PckStudio.Extensions;
using System.Text;
@@ -140,7 +140,7 @@ namespace PckStudio.Forms.Editor
var textures = isClockOrCompass ? linearImages : frameTextures;
return ImageExtentions.ImageFromImageArray(textures.ToArray(), ImageExtentions.ImageLayoutDirection.Vertical);
return ImageExtensions.ImageFromImageArray(textures.ToArray(), ImageLayoutDirection.Vertical);
}
}
}

View File

@@ -3,15 +3,13 @@ using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using PckStudio.Classes.FileTypes;
using PckStudio.Forms.Additional_Popups.Animation;
using PckStudio.Forms.Utilities;
using PckStudio.Classes.Extentions;
using PckStudio.Extensions;
using OMI.Formats.Pck;
namespace PckStudio.Forms.Editor
@@ -51,7 +49,7 @@ namespace PckStudio.Forms.Editor
using MemoryStream textureMem = new MemoryStream(animationFile.Data);
var texture = new Bitmap(textureMem);
var frameTextures = texture.CreateImageList(ImageExtentions.ImageLayoutDirection.Horizontal);
var frameTextures = texture.CreateImageList(ImageLayoutDirection.Horizontal);
currentAnimation = animationFile.Properties.HasProperty("ANIM")
? new Animation(frameTextures, animationFile.Properties.GetPropertyValue("ANIM"))
@@ -296,7 +294,7 @@ namespace PckStudio.Forms.Editor
return;
}
using MemoryStream textureMem = new MemoryStream(File.ReadAllBytes(textureFile));
var textures = Image.FromStream(textureMem).CreateImageList(ImageExtentions.ImageLayoutDirection.Horizontal);
var textures = Image.FromStream(textureMem).CreateImageList(ImageLayoutDirection.Horizontal);
var new_animation = new Animation(textures);
try
{

View File

@@ -64,7 +64,8 @@ namespace PckStudio.Forms.Editor
audioPCK = file;
using (var stream = new MemoryStream(file.Data))
{
audioFile = PCKAudioFileReader.Read(stream, isLittleEndian);
var reader = new PCKAudioFileReader(isLittleEndian ? OMI.Endianness.LittleEndian : OMI.Endianness.BigEndian);
audioFile = reader.FromStream(stream);
}
SetUpTree();
@@ -396,7 +397,8 @@ namespace PckStudio.Forms.Editor
using (var stream = new MemoryStream())
{
PCKAudioFileWriter.Write(stream, audioFile, _isLittleEndian);
var writer = new PCKAudioFileWriter(audioFile, _isLittleEndian ? OMI.Endianness.LittleEndian : OMI.Endianness.BigEndian);
writer.WriteToStream(stream);
audioPCK.SetData(stream.ToArray());
}
DialogResult = DialogResult.OK;

View File

@@ -5,10 +5,10 @@ using System.Windows.Forms;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using PckStudio.Classes.Utils;
using PckStudio.Classes._3ds.Utils;
using OMI.Formats.Languages;
using OMI.Formats.Pck;
using PckStudio.Forms.Editor;
using PckStudio.Classes.IO._3DST;
namespace PckStudio
{
@@ -347,7 +347,8 @@ namespace PckStudio
{
using (var fs = File.OpenRead(ofdd.FileName))
{
CheckImage(_3DSUtil.GetImageFrom3DST(fs));
var reader = new _3DSTextureReader();
CheckImage(reader.FromStream(fs));
textSkinName.Text = Path.GetFileNameWithoutExtension(ofdd.FileName);
}
return;

View File

@@ -1,12 +1,9 @@
using Newtonsoft.Json.Linq;
using System.Drawing;
using System.Linq;
using System.IO;
using PckStudio.Properties;
using System.Drawing.Imaging;
using PckStudio.Classes.Extentions;
using OMI.Formats.Pck;
using PckStudio.Extensions;
namespace PckStudio.Forms.Utilities
{

View File

@@ -4,11 +4,9 @@ using System.Linq;
using System.IO;
using PckStudio.Properties;
using PckStudio.Classes.Extentions;
using PckStudio.Extensions;
using OMI.Formats.Behaviour;
using OMI.Workers.Behaviour;
using OMI.Formats.Pck;
using System;
namespace PckStudio.Forms.Utilities
{

View File

@@ -4,7 +4,7 @@ using System.Linq;
using System.IO;
using PckStudio.Properties;
using PckStudio.Classes.Extentions;
using PckStudio.Extensions;
using OMI.Formats.Pck;
using OMI.Formats.Material;
using OMI.Workers.Material;

View File

@@ -4,7 +4,7 @@ using System.Linq;
using System.IO;
using PckStudio.Properties;
using PckStudio.Classes.Extentions;
using PckStudio.Extensions;
using OMI.Formats.Model;
using OMI.Formats.Pck;
using OMI.Workers.Model;

View File

@@ -19,7 +19,7 @@ using PckStudio.Classes.FileTypes;
using PckStudio.Classes.IO.PCK;
using OMI.Formats.Pck;
using OMI.Workers.Pck;
using PckStudio.Classes.Extentions;
using PckStudio.Extensions;
namespace PckStudio.Forms
{
@@ -1027,7 +1027,7 @@ namespace PckStudio.Forms
{
var ms = new MemoryStream(skinTexture.Data);
Bitmap saveSkin = new Bitmap(Image.FromStream(ms));
var config = new ImageExtentions.GraphicsConfig()
var config = new GraphicsConfig()
{
CompositingMode = CompositingMode.SourceCopy,
CompositingQuality = CompositingQuality.HighQuality,

View File

@@ -19,7 +19,6 @@ using PckStudio.Properties;
using PckStudio.Classes.FileTypes;
using PckStudio.Classes.Utils;
using PckStudio.Classes.Utils.ARC;
using PckStudio.Classes._3ds.Utils;
using PckStudio.Forms;
using PckStudio.Forms.Utilities;
using PckStudio.Forms.Editor;
@@ -27,6 +26,7 @@ using PckStudio.Forms.Additional_Popups.Animation;
using PckStudio.Forms.Additional_Popups;
using PckStudio.Classes.Misc;
using PckStudio.Classes.IO.PCK;
using PckStudio.Classes.IO._3DST;
using PckStudio.Conversion.Bedrock;
namespace PckStudio
@@ -840,12 +840,15 @@ namespace PckStudio
audioPck.AddCategory(PCKAudioFile.AudioCategory.EAudioType.Overworld);
audioPck.AddCategory(PCKAudioFile.AudioCategory.EAudioType.Nether);
audioPck.AddCategory(PCKAudioFile.AudioCategory.EAudioType.End);
PckFile.FileData pckFileData = currentPCK.CreateNewFile("audio.pck", PckFile.FileData.FileType.AudioFile);
using (var stream = new MemoryStream())
{
PCKAudioFileWriter.Write(stream, audioPck, isLittle);
pckFileData.SetData(stream.ToArray());
}
PckFile.FileData pckFileData = currentPCK.CreateNewFile("audio.pck", PckFile.FileData.FileType.AudioFile, () =>
{
using (var stream = new MemoryStream())
{
var writer = new PCKAudioFileWriter(audioPck, isLittle ? OMI.Endianness.LittleEndian : OMI.Endianness.BigEndian);
writer.WriteToStream(stream);
return stream.ToArray();
}
});
return pckFileData;
}
@@ -2078,11 +2081,11 @@ namespace PckStudio
saveFileDialog.DefaultExt = ".3dst";
if (saveFileDialog.ShowDialog() == DialogResult.OK)
{
using (var fs = saveFileDialog.OpenFile())
using (var ms = new MemoryStream(file.Data))
{
using var ms = new MemoryStream(file.Data);
Image img = Image.FromStream(ms);
_3DSUtil.SetImageTo3DST(fs, img);
var writer = new _3DSTextureWriter(img);
writer.WriteToFile(saveFileDialog.FileName);
}
}
}

View File

@@ -170,6 +170,9 @@
<Compile Include="Classes\API\PCKCenter\PCKCollections.cs" />
<Compile Include="Classes\API\PCKCenter\PCKCollectionsLocal.cs" />
<Compile Include="Classes\API\PCKCenter\SaveLocalJSON.cs" />
<Compile Include="Classes\Extensions\BlendMode.cs" />
<Compile Include="Classes\Extensions\EnumerableExtensions.cs" />
<Compile Include="Classes\Extensions\GraphicsExtensions.cs" />
<Compile Include="Classes\Conversion\Bedrock\BedrockSkinExporter.cs" />
<Compile Include="Classes\Conversion\Bedrock\FileExportContext.cs" />
<Compile Include="Classes\Conversion\Bedrock\IExportContext.cs" />
@@ -185,11 +188,11 @@
<Compile Include="Classes\Conversion\Bedrock\ZipExportContext.cs" />
<Compile Include="Classes\Conversion\Legacy\JsonDefinitions\BlockbenchBedrockModel.cs" />
<Compile Include="Classes\Conversion\Legacy\LegacySkinExporter.cs" />
<Compile Include="Classes\Extentions\EnumerableExtentions.cs" />
<Compile Include="Classes\FileTypes\CSMBFile.cs" />
<Compile Include="Classes\FileTypes\PCKAudioFile.cs" />
<Compile Include="Classes\FileTypes\Binka.cs" />
<Compile Include="Classes\FileTypes\CSM.cs" />
<Compile Include="Classes\IO\3DST\3DSTextureReader.cs" />
<Compile Include="Classes\IO\3DST\3DSTextureWriter.cs" />
<Compile Include="Classes\IO\CSMB\CSMBFileReader.cs" />
<Compile Include="Classes\IO\CSMB\CSMBFileWriter.cs" />
<Compile Include="Classes\IO\PCK\PCKAudioFileReader.cs" />
@@ -198,12 +201,9 @@
<Compile Include="Classes\IO\Sounds\Sounds.cs" />
<Compile Include="Classes\Misc\FTPClient.cs" />
<Compile Include="Classes\Models\DefaultModels\Steve64x64Model.cs" />
<Compile Include="Classes\Utils\3DS\3DSUtil.cs" />
<Compile Include="Classes\Utils\ARC\ARCUtil.cs" />
<Compile Include="Classes\Extentions\ImageExtentions.cs" />
<Compile Include="Classes\Extensions\ImageExtensions.cs" />
<Compile Include="Classes\Utils\SkinANIM.cs" />
<Compile Include="Classes\IO\StreamDataReader.cs" />
<Compile Include="Classes\IO\StreamDataWriter.cs" />
<Compile Include="Classes\Models\DefaultModels\Steve64x32Model.cs" />
<Compile Include="Classes\Models\DefaultModels\ModelBase.cs" />
<Compile Include="Classes\Models\DefaultModels\Texture.cs" />
@@ -432,7 +432,7 @@
<Compile Include="Forms\Utilities\installWiiU.Designer.cs">
<DependentUpon>installWiiU.cs</DependentUpon>
</Compile>
<Compile Include="Classes\Utils\ListUtils.cs" />
<Compile Include="Classes\Extensions\ListExtensions.cs" />
<Compile Include="Forms\Utilities\PCK Manager.cs">
<SubType>Form</SubType>
</Compile>
@@ -480,7 +480,7 @@
<DependentUpon>CreditsForm.cs</DependentUpon>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Classes\Utils\3DS\TextureCodec.cs" />
<Compile Include="Classes\IO\3DST\TextureCodec.cs" />
<EmbeddedResource Include="Forms\Additional-Popups\EntityForms\AddEntry.resx">
<DependentUpon>AddEntry.cs</DependentUpon>
</EmbeddedResource>

View File

@@ -1,8 +1,8 @@
# PCK Studio
Modify .PCK archives as you please!
Modify .PCK archives as you please!<br>
**If you have any issues or would like to suggested a feature, we encourage you to open a new [issue](https://github.com/PhoenixARC/-PCK-Studio/issues) on the github.**
## Features:
#### If you'd like to suggested a feature to be added open an [issue](https://github.com/PhoenixARC/-PCK-Studio/issues) on the github.
* Open, Edit and Save .PCK archives (this means models, animations, entire custom packs, etc.)
* Add / Remove / Replace skins, audios, textures, animations and much more.
* Edit localization data to make your mods support every language supported by minecraft itself!
@@ -23,8 +23,8 @@ Modify .PCK archives as you please!
### Setup:
```shell
$ git clone https://github.com/PhoenixARC/-PCK-Studio.git
$ cd "-PCK-Studio"
$ git clone --recursive https://github.com/PhoenixARC/-PCK-Studio.git PCK-Studio
$ cd PCK-Studio
```
## How to Build:
@@ -44,21 +44,19 @@ $ cd "-PCK-Studio"
```
### A quick note:
* Forms will not load in viewer until the solution is build _at least_ once
## Active Developers:
> [PhoenixARC](https://github.com/PhoenixARC)<br>
> [MattNL](https://github.com/MattN-L)<br>
> [Miku-666](https://github.com/NessieHax)<br>
> [EternalModz](https://github.com/EternalModz)<br>
## Active Dev Team:
* [PhoenixARC](https://github.com/PhoenixARC)
* [MattNL](https://github.com/MattN-L)
* [Miku-666](https://github.com/NessieHax)
* [EternalModz](https://github.com/EternalModz)
## Legacy PCK Studio Devs and contributors:
* [Nobledez](https://github.com/Nobledez)
* [XxModZxXWiiPlaza](https://github.com/XxModZxXWiiPlaza)
* [SlothWiiPlaza](https://github.com/Kashiiera)
* [jam1garner](https://github.com/jam1garner)
## Previous Developers and Contributors:
> [Nobledez](https://github.com/Nobledez)<br>
> [jam1garner](https://github.com/jam1garner)<br>
> [XxModZxXWiiPlaza](https://github.com/XxModZxXWiiPlaza)<br>
> [SlothWiiPlaza](https://github.com/Kashiiera)<br>
## Other Credits
* [yaboiFoxx](https://github.com/yaboiFoxx) for Improved UI concept

2
Vendor/OMI-Lib vendored