using CommonLang.ByteOrder; using CommonLang.IO; using CommonUI_Unity3D.Impl; using System; using System.IO; using UnityEngine; namespace CommonUI_Unity3D.Src.M3Z { ////////////////////////////////////////////////////////////////////////// // M3Z ////////////////////////////////////////////////////////////////////////// public enum M3ZType { M3Z_TYPE_PNG = 0x00474e50, M3Z_TYPE_JPG = 0x0047504a, M3Z_TYPE_BMP = 0x00504d42, M3Z_TYPE_PVR4 = 0x34525650, M3Z_TYPE_PVR2 = 0x32525650, M3Z_TYPE_PVR1 = 0x31525650, M3Z_TYPE_PVRA = 0x41525650, M3Z_TYPE_PKM1 = 0x314d4b50, M3Z_TYPE_PKMA = 0x414d4b50, M3Z_TYPE_ATC_E = 0x45435441, M3Z_TYPE_ATC_I = 0x49435441, M3Z_TYPE_ATC_RGB = 0x33435441, M3Z_TYPE_ETC1 = 0x31435445, M3Z_TYPE_ETCA = 0x41435445, M3Z_TYPE_RGBA8 = 0x41424752, M3Z_TYPE_A8 = 0x00003841, M3Z_TYPE_DXT1 = 0x31545844, M3Z_TYPE_DXT3 = 0x33545844, M3Z_TYPE_DXT5 = 0x35545844, } public class M3ZHeader { public const uint M3Z_HEADER = 0x5a33464d; /**文件头*/ public uint header { get; private set; } /**文件尺寸*/ public uint version { get; private set; } /**原始图片宽*/ public int srcWidth { get; private set; } /**原始图片高*/ public int srcHeight { get; private set; } /**原始图片是否包含半透明*/ public bool srcHasAlpha { get; private set; } /**附加数据*/ public string extUTFData { get; private set; } /**纹理数量*/ public int trunkCount { get; private set; } /**纹理块*/ public M3ZTrunk[] trunks { get; private set; } public M3ZHeader(Stream data) { this.header = LittleEdian.GetU32(data); if (header != M3Z_HEADER) { throw new Exception("Invalid M3Z data"); } this.version = LittleEdian.GetU32(data); const uint v_0100 = 0x00000100; if (version == v_0100) { srcWidth = LittleEdian.GetS32(data); srcHeight = LittleEdian.GetS32(data); srcHasAlpha = LittleEdian.GetBool(data); extUTFData = LittleEdian.GetUTF(data); trunkCount = LittleEdian.GetS32(data); trunks = new M3ZTrunk[trunkCount]; for (int i = 0; i < trunkCount; i++) { trunks[i] = new M3ZTrunk(data, version); } } else { // 君王2老M3Z文件格式代码 srcWidth = LittleEdian.GetS32(data); srcHeight = LittleEdian.GetS32(data); srcHasAlpha = LittleEdian.GetBool(data); trunkCount = LittleEdian.GetS32(data); trunks = new M3ZTrunk[trunkCount]; for (int i = 0; i < trunkCount; i++) { trunks[i] = new M3ZTrunk(data, version); } } } } public class M3ZTrunk { /// /// 类型 /// public M3ZType type { get; private set; } public uint flags { get; private set; } /// /// 是否包含半透明 /// public bool hasAlpha { get; private set; } /// /// 二的冥宽 /// public int pixelW { get; private set; } /// /// 二的冥高 /// public int pixelH { get; private set; } /// /// 实际像素点宽 /// public int realPixelW { get; private set; } /// /// 实际像素点高 /// public int realPixelH { get; private set; } /// /// 扩展数据 /// public string extUTFData { get; private set; } /// /// 数据段尺寸 /// public int fileSize { get; private set; } public ICompressTextureData texData { get; private set; } public M3ZTrunk(Stream data, uint version) { const uint v_0100 = 0x00000100; if (version == v_0100) { this.type = (M3ZType)LittleEdian.GetU32(data); this.hasAlpha = LittleEdian.GetBool(data); this.pixelW = LittleEdian.GetS32(data); this.pixelH = LittleEdian.GetS32(data); this.realPixelW = LittleEdian.GetS32(data); this.realPixelH = LittleEdian.GetS32(data); this.extUTFData = LittleEdian.GetUTF(data); this.fileSize = LittleEdian.GetS32(data); } else { this.type = (M3ZType)LittleEdian.GetU32(data); this.flags = LittleEdian.GetU32(data); this.hasAlpha = LittleEdian.GetBool(data); this.pixelW = LittleEdian.GetS32(data); this.pixelH = LittleEdian.GetS32(data); this.realPixelW = pixelW; this.realPixelH = pixelH; this.fileSize = LittleEdian.GetS32(data); } switch (type) { case M3ZType.M3Z_TYPE_PVR2: case M3ZType.M3Z_TYPE_PVR4: case M3ZType.M3Z_TYPE_PVR1: case M3ZType.M3Z_TYPE_PVRA: this.texData = new PVRTexHeader(); break; case M3ZType.M3Z_TYPE_ETC1: case M3ZType.M3Z_TYPE_ETCA: this.texData = new KTXTexHeader(); break; case M3ZType.M3Z_TYPE_PKM1: case M3ZType.M3Z_TYPE_PKMA: this.texData = new PKMTexHeader(); break; default: this.texData = new UnknowTexHeader(); break; } if (texData != null) { texData.Decode(this, data); } } public Texture2D LoadRawTextureData() { if (texData != null) { return texData.LoadRawTextureData(this); } return null; } public static TextureFormat GetTextureFormat(M3ZType mtype) { switch (mtype) { case M3ZType.M3Z_TYPE_PNG: case M3ZType.M3Z_TYPE_JPG: case M3ZType.M3Z_TYPE_BMP: return TextureFormat.RGBA32; case M3ZType.M3Z_TYPE_PVR2: return TextureFormat.PVRTC_RGBA2; case M3ZType.M3Z_TYPE_PVR4: case M3ZType.M3Z_TYPE_PVR1: case M3ZType.M3Z_TYPE_PVRA: return TextureFormat.PVRTC_RGBA4; case M3ZType.M3Z_TYPE_PKM1: case M3ZType.M3Z_TYPE_PKMA: return TextureFormat.ETC_RGB4; case M3ZType.M3Z_TYPE_ATC_E: case M3ZType.M3Z_TYPE_ATC_I: case M3ZType.M3Z_TYPE_ATC_RGB: return TextureFormat.ATC_RGB4; case M3ZType.M3Z_TYPE_ETC1: case M3ZType.M3Z_TYPE_ETCA: return TextureFormat.ETC_RGB4; case M3ZType.M3Z_TYPE_RGBA8: return TextureFormat.RGBA32; case M3ZType.M3Z_TYPE_A8: return TextureFormat.Alpha8; case M3ZType.M3Z_TYPE_DXT1: return TextureFormat.DXT1; case M3ZType.M3Z_TYPE_DXT3: return TextureFormat.DXT5; case M3ZType.M3Z_TYPE_DXT5: return TextureFormat.DXT5; } return TextureFormat.RGBA32; } } public interface ICompressTextureData { byte[] RawData { get; } void Decode(M3ZTrunk trunk, Stream stream); void Encode(Stream stream); Texture2D LoadRawTextureData(M3ZTrunk trunk); } public struct UnknowTexHeader : ICompressTextureData { public byte[] rawData; public byte[] RawData { get { return rawData; } } public void Decode(M3ZTrunk trunk, Stream stream) { this.rawData = new byte[trunk.fileSize]; IOUtil.ReadToEnd(stream, rawData, 0, rawData.Length); } public void Encode(Stream stream) { IOUtil.WriteToEnd(stream, rawData, 0, rawData.Length); } public Texture2D LoadRawTextureData(M3ZTrunk trunk) { Texture2D tex = new Texture2D(trunk.pixelW, trunk.pixelH, M3ZTrunk.GetTextureFormat(trunk.type), false, true); tex.LoadRawTextureData(rawData); tex.Apply(false, true); return tex; } } public struct PVRTexHeader : ICompressTextureData { public const int PVR_HEADER_SIZE = 52; public uint headerLength; public uint height; public uint width; public uint numMipmaps; public uint flags; public uint dataLength; public uint bpp; public uint bitmaskRed; public uint bitmaskGreen; public uint bitmaskBlue; public uint bitmaskAlpha; public uint pvrTag; public uint numSurfs; public byte[] rawData; public byte[] RawData { get { return rawData; } } public void Decode(M3ZTrunk trunk, Stream stream) { this.headerLength = LittleEdian.GetU32(stream); this.height = LittleEdian.GetU32(stream); this.width = LittleEdian.GetU32(stream); this.numMipmaps = LittleEdian.GetU32(stream); this.flags = LittleEdian.GetU32(stream); this.dataLength = LittleEdian.GetU32(stream); this.bpp = LittleEdian.GetU32(stream); this.bitmaskRed = LittleEdian.GetU32(stream); this.bitmaskGreen = LittleEdian.GetU32(stream); this.bitmaskBlue = LittleEdian.GetU32(stream); this.bitmaskAlpha = LittleEdian.GetU32(stream); this.pvrTag = LittleEdian.GetU32(stream); this.numSurfs = LittleEdian.GetU32(stream); this.rawData = new byte[dataLength]; IOUtil.ReadToEnd(stream, rawData, 0, rawData.Length); } public void Encode(Stream stream) { LittleEdian.PutU32(stream, this.headerLength); LittleEdian.PutU32(stream, this.height); LittleEdian.PutU32(stream, this.width); LittleEdian.PutU32(stream, this.numMipmaps); LittleEdian.PutU32(stream, this.flags); LittleEdian.PutU32(stream, this.dataLength); LittleEdian.PutU32(stream, this.bpp); LittleEdian.PutU32(stream, this.bitmaskRed); LittleEdian.PutU32(stream, this.bitmaskGreen); LittleEdian.PutU32(stream, this.bitmaskBlue); LittleEdian.PutU32(stream, this.bitmaskAlpha); LittleEdian.PutU32(stream, this.pvrTag); LittleEdian.PutU32(stream, this.numSurfs); IOUtil.WriteToEnd(stream, rawData, 0, rawData.Length); } public Texture2D LoadRawTextureData(M3ZTrunk trunk) { Texture2D tex = new Texture2D(trunk.pixelW, trunk.pixelH, M3ZTrunk.GetTextureFormat(trunk.type), false, true); tex.LoadRawTextureData(rawData); tex.Apply(false, true); return tex; } } public struct PKMTexHeader : ICompressTextureData { public const int PKM_HEADER_SIZE = 16; public byte[] MagicNumber; // 4 byte magic number: "PKM " public byte[] Version; // 2 byte version "10" public byte[] DataType; // 2 byte data type: 0 (ETC1_RGB_NO_MIPMAPS) public ushort ExtWidth; // 16 bit big endian extended width public ushort ExtHeight; // 16 bit big endian extended height public ushort SrcWidth; // 16 bit big endian original width public ushort SrcHeight; // 16 bit big endian original height public byte[] rawData; public byte[] RawData { get { return rawData; } } public void Decode(M3ZTrunk trunk, Stream stream) { this.MagicNumber = IOUtil.ReadExpect(stream, 4); this.Version = IOUtil.ReadExpect(stream, 2); this.DataType = IOUtil.ReadExpect(stream, 2); this.ExtWidth = BigEdian.GetU16(stream); this.ExtHeight = BigEdian.GetU16(stream); this.SrcWidth = BigEdian.GetU16(stream); this.SrcHeight = BigEdian.GetU16(stream); this.rawData = new byte[trunk.fileSize - PKM_HEADER_SIZE]; IOUtil.ReadToEnd(stream, rawData, 0, rawData.Length); } public void Encode(Stream stream) { IOUtil.WriteToEnd(stream, this.MagicNumber, 0, 4); IOUtil.WriteToEnd(stream, this.Version, 0, 2); IOUtil.WriteToEnd(stream, this.DataType, 0, 2); BigEdian.PutU16(stream, this.ExtWidth); BigEdian.PutU16(stream, this.ExtHeight); BigEdian.PutU16(stream, this.SrcWidth); BigEdian.PutU16(stream, this.SrcHeight); IOUtil.WriteToEnd(stream, rawData, 0, rawData.Length); } public Texture2D LoadRawTextureData(M3ZTrunk trunk) { Texture2D tex = new Texture2D(trunk.pixelW, trunk.pixelH, M3ZTrunk.GetTextureFormat(trunk.type), false, true); tex.LoadRawTextureData(rawData); tex.Apply(false, true); return tex; } } public struct KTXTexHeader : ICompressTextureData { public const int KTX_HEADER_SIZE = 52 + 12; public static byte[] KTX_IDENTIFIER_REF = { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A }; public byte[] identifier; public uint endianness; public uint glType; public uint glTypeSize; public uint glFormat; public uint glInternalFormat; public uint glBaseInternalFormat; public uint pixelWidth; public uint pixelHeight; public uint pixelDepth; public uint numberOfArrayElements; public uint numberOfFaces; public uint numberOfMipmapLevels; public uint bytesOfKeyValueData; public byte[] rawData; public byte[] RawData { get { return rawData; } } public void Decode(M3ZTrunk trunk, Stream stream) { this.identifier = IOUtil.ReadExpect(stream, 12); this.endianness = LittleEdian.GetU32(stream); this.glType = LittleEdian.GetU32(stream); this.glTypeSize = LittleEdian.GetU32(stream); this.glFormat = LittleEdian.GetU32(stream); this.glInternalFormat = LittleEdian.GetU32(stream); this.glBaseInternalFormat = LittleEdian.GetU32(stream); this.pixelWidth = LittleEdian.GetU32(stream); this.pixelHeight = LittleEdian.GetU32(stream); this.pixelDepth = LittleEdian.GetU32(stream); this.numberOfArrayElements = LittleEdian.GetU32(stream); this.numberOfFaces = LittleEdian.GetU32(stream); this.numberOfMipmapLevels = LittleEdian.GetU32(stream); this.bytesOfKeyValueData = LittleEdian.GetU32(stream); stream.Position += this.bytesOfKeyValueData; uint imageSize = LittleEdian.GetU32(stream); this.rawData = new byte[imageSize]; IOUtil.ReadToEnd(stream, rawData, 0, rawData.Length); } public void Encode(Stream stream) { IOUtil.WriteToEnd(stream, this.identifier, 0, 12); LittleEdian.PutU32(stream, endianness); LittleEdian.PutU32(stream, glType); LittleEdian.PutU32(stream, glTypeSize); LittleEdian.PutU32(stream, glFormat); LittleEdian.PutU32(stream, glInternalFormat); LittleEdian.PutU32(stream, glBaseInternalFormat); LittleEdian.PutU32(stream, pixelWidth); LittleEdian.PutU32(stream, pixelHeight); LittleEdian.PutU32(stream, pixelDepth); LittleEdian.PutU32(stream, numberOfArrayElements); LittleEdian.PutU32(stream, numberOfFaces); LittleEdian.PutU32(stream, numberOfMipmapLevels); LittleEdian.PutU32(stream, bytesOfKeyValueData); LittleEdian.PutU32(stream, (uint)rawData.Length); IOUtil.WriteToEnd(stream, rawData, 0, rawData.Length); } public Texture2D LoadRawTextureData(M3ZTrunk trunk) { Texture2D tex = new Texture2D(trunk.pixelW, trunk.pixelH, M3ZTrunk.GetTextureFormat(trunk.type), false, true); tex.LoadRawTextureData(rawData); tex.Apply(false, true); return tex; } } public static class G3ZStream { public static Stream DecompressToStream(byte[] data) { int pos = 0; int Head = (int)LittleEdian.GetU32(data, ref pos); int SrcSize = (int)LittleEdian.GetU32(data, ref pos); byte[] dst = new byte[SrcSize]; UnityDriver.Platform.NativeDecompressMemory( new ArraySegment(data, pos, data.Length - pos), new ArraySegment(dst, 0, dst.Length)); return new MemoryStream(dst); } } }