예제 #1
0
        /// <summary>
        /// Decodes the specified level of the encoded texture to an array of RGBA8 pixels.
        /// </summary>
        /// <param name="level">The level of the texture to decode.</param>
        /// <param name="desiredStride">Desired stride (number of bytes per scanline) of the result.</param>
        /// <returns>An array with the RGBA8 data of the level (with no extra row padding).</returns>
        public byte[] DecodeLevelToRGBA8(int level, int desiredStride)
        {
            if (level < 0 || level >= LevelCount)
            {
                throw new ArgumentOutOfRangeException("level");
            }
            if (desiredStride < 0)
            {
                throw new ArgumentOutOfRangeException("desiredStride");
            }

            int levelWidth = WidthOfLevel(level), levelHeight = HeightOfLevel(level);

            if (desiredStride < levelWidth * 4)
            {
                throw new ArgumentOutOfRangeException("desiredStride", "Stride is too small to contain a row of data.");
            }

            // Decode texture as RGBA8 (GxTextureDecode format)
            byte[] decodedData = new byte[levelHeight * desiredStride];
            GxTextureFormatCodec.GetCodec(format).DecodeTexture(decodedData, 0,
                                                                levelWidth, levelHeight, desiredStride,
                                                                encodedLevelData[level], 0, null, 0);
            return(decodedData);
        }
        public Texture2D ReadTexture(BinaryReader reader, TEXDescriptor desc, string saveFilePath, string textureName)
        {
            // Verify if index is valid (some entries can be nulled out)
            // We check for 0 specifically because garbage can be store in the
            // first few entries of the file.
            // TPL can have null entries so this is actually correct to the format
            if (desc.isNullEntry != 0)
            {
                return(null);
            }

            // We use Try as GxTextureFormatCodec.GetCodec() can return an error if the type is invalid
            try
            {
                GxTextureFormatCodec codec = GxTextureFormatCodec.GetCodec((GxTextureFormat)desc.format);
                reader.BaseStream.Position = desc.dataPtr;
                byte[] texRaw  = reader.GetBytes(codec.CalcTextureSize(desc.width, desc.height));
                byte[] texRGBA = new byte[4 * desc.width * desc.height]; // RGBA (4 bytes) * w * h
                codec.DecodeTexture(texRGBA, 0, desc.width, desc.height, desc.width * 4, texRaw, 0, null, 0);

                // Reconstruct Texture using Unity's format
                Texture2D texture = new Texture2D(desc.width, desc.height);
                for (int y = 0; y < desc.height; y++)
                {
                    for (int x = 0; x < desc.width; x++)
                    {
                        // Invert Y because LibGXTexture returns array upside-down?
                        // ei 'x, (desc.width - y)' instead of 'x, y'
                        texture.SetPixel(x, (desc.width - y), new Color32(
                                             texRGBA[(y * desc.width + x) * 4 + 0],
                                             texRGBA[(y * desc.width + x) * 4 + 1],
                                             texRGBA[(y * desc.width + x) * 4 + 2],
                                             texRGBA[(y * desc.width + x) * 4 + 3]));
                    }
                }

                string assetPath  = string.Format("{0}/tex_{1}.png", saveFilePath, textureName).PathToUnityPath();
                byte[] imageBytes = texture.EncodeToPNG();
                DestroyImmediate(texture);

                using (BinaryWriter writer = new BinaryWriter(File.Create(assetPath, imageBytes.Length)))
                {
                    writer.Write(imageBytes);
                }

                AssetDatabase.ImportAsset(assetPath, ImportAssetOptions.ForceUpdate);
                AssetDatabase.Refresh(ImportAssetOptions.Default);
                Texture2D tex = (Texture2D)AssetDatabase.LoadAssetAtPath(assetPath, typeof(Texture2D));
                return(tex);
            }
            catch
            {
                Debug.LogErrorFormat("GxTextureFormatCodec.GetCodec() failed to find format [0x{0}]", desc.format.ToString("X2"));
                return(null);
            }
        }
예제 #3
0
        private void updateTextureCount()
        {
            int.TryParse(texX.Text, out int x);
            int.TryParse(texY.Text, out int y);
            int texSize = x * y * (GxTextureFormatCodec.GetCodec(currentFormat).BitsPerPixel / 8);

            if (texSize != 0)
            {
                int finalCount = fileSize / texSize;
                texCount.Text = finalCount.ToString();
            }
        }
예제 #4
0
        /// <summary>
        /// Create or replace the specified texture level from the specified image data.
        /// New texture levels must be created in order.
        /// </summary>
        /// <param name="level">The level of the texture to create or replace.</param>
        /// <param name="newImageDataStride">The stride (number of bytes per row) of the new image data.</param>
        /// <param name="newImageData">The new image data for the level.</param>
        public void DefineLevelData(int level, int newImageDataStride, byte[] newImageData)
        {
            if (level > LevelCount)             // We allow to either replace an existing level or to generate the next level
            {
                throw new ArgumentOutOfRangeException("level");
            }
            if (newImageDataStride < 0)
            {
                throw new ArgumentOutOfRangeException("newImageDataStride");
            }
            if (newImageData == null)
            {
                throw new ArgumentNullException("newImageData");
            }

            // Check that this texture level can be defined (size is not too small)
            // This checks that width and height can be divided evenly by 2^level
            if ((width & ((1 << level) - 1)) != 0 ||
                (height & ((1 << level) - 1)) != 0)
            {
                throw new ArgumentOutOfRangeException("level", "Level is too low for the image dimensions.");
            }

            int levelWidth  = width >> level;
            int levelHeight = height >> level;

            if (newImageDataStride < levelWidth * 4)
            {
                throw new ArgumentOutOfRangeException("newImageDataStride", "Stride is too small to contain a row of data.");
            }

            // Adding a new mipmap?
            if (level == LevelCount)
            {
                encodedLevelData.Add(new byte[CalculateSizeOfLevel(level)]);
            }

            // Encode
            GxTextureFormatCodec.GetCodec(format).EncodeTexture(
                newImageData, 0, levelWidth, levelHeight, newImageDataStride,
                encodedLevelData[level], 0, null, 0);
        }
예제 #5
0
        /// <summary>
        /// Calculates the size of a level of the texture.
        /// </summary>
        /// <param name="level">The level of the texture.</param>
        /// <param name="replicateCmprBug">true to replicate the F-Zero GX CMPR encoding bug.</param>
        /// <returns>The size of the encoded data in the specified level.</returns>
        private int CalculateSizeOfLevel(int level, bool replicateCmprBug = false)
        {
            // Here we allow also to specify the "next" level for easier implementation
            // of the methods that encode the new texture
            if (level < 0 || level > LevelCount)
            {
                throw new ArgumentOutOfRangeException("level");
            }

            int levelWidth = width >> level, levelHeight = height >> level;

            // Hack: CMPR sizes are not calculated correctly, replicate the bug
            if (replicateCmprBug && format == GxTextureFormat.CMPR)
            {
                int w     = PaddingUtils.Align(levelWidth, 4);  // Align to 4 (should really be 8)
                int h     = PaddingUtils.Align(levelHeight, 4); // Align to 4 (should really be 8)
                int sz    = (w * h * 4) / 8;                    // CMPR is 4 bits per pixel
                int szpad = PaddingUtils.Align(sz, 32);         // Align to 32 (this should normally not be needed)
                return(szpad);
            }

            return(GxTextureFormatCodec.GetCodec(format).CalcTextureSize(levelWidth, levelHeight));
        }
예제 #6
0
파일: Tpl.cs 프로젝트: TheBombSquad/GxUtils
        private void Load(EndianBinaryReader input, GxGame game, GeneratedTextureHeader?newHeader)
        {
            if (game == GxGame.SuperMonkeyBallDX)
            {
                input.ReadInt32();
            }

            // If there is a header, use the value from that header, otherwise use the generated header value
            int numTextures = (newHeader == null) ? (numTextures = input.ReadInt32()) : (numTextures = newHeader.Value.textureCount);

            // Load texture definition headers
            TextureHeader[] texHdr = new TextureHeader[numTextures];
            if (newHeader == null)
            {
                for (int i = 0; i < numTextures; i++)
                {
                    texHdr[i].FormatRaw  = input.ReadInt32();
                    texHdr[i].Offset     = input.ReadInt32();
                    texHdr[i].Width      = Convert.ToInt32(input.ReadUInt16());
                    texHdr[i].Height     = Convert.ToInt32(input.ReadUInt16());
                    texHdr[i].LevelCount = Convert.ToInt32(input.ReadUInt16());
                    UInt16 check = input.ReadUInt16();
                    if ((game != GxGame.SuperMonkeyBallDX && check != 0x1234) || (game == GxGame.SuperMonkeyBallDX && check != 0x3412))
                    {
                        throw new InvalidTplFileException("Invalid texture header (Field @0x0E).");
                    }
                }
            }

            // Creates a new texture header from provided texture header characteristics
            else
            {
                // The length of the header after this operation's completion
                int initialOffset = newHeader.Value.textureCount * 16;
                // Size of a texture based on how many bytes per pixel
                int texSize = newHeader.Value.textureHeight * newHeader.Value.textureWidth * GxTextureFormatCodec.GetCodec(newHeader.Value.textureFormat).BitsPerPixel / 8;
                // Maximum possible offset for the TPL
                int maxOffset = initialOffset + (texSize) * numTextures;
                // Iteration variable for referencing the index of the texture header
                int iteration;
                for (int iterateOffset = initialOffset; iterateOffset < maxOffset; iterateOffset += texSize)
                {
                    iteration = (iterateOffset - initialOffset) / texSize;
                    texHdr[iteration].FormatRaw  = (int)newHeader.Value.textureFormat;
                    texHdr[iteration].Width      = newHeader.Value.textureWidth;
                    texHdr[iteration].Height     = newHeader.Value.textureHeight;
                    texHdr[iteration].Offset     = iterateOffset;
                    texHdr[iteration].LevelCount = newHeader.Value.textureMipmapCount;
                }
            }

            // Load textures data
            for (int i = 0; i < numTextures; i++)
            {
                TplTexture tex = new TplTexture();

                if (texHdr[i].Offset != 0 && texHdr[i].Width != 0 &&
                    texHdr[i].Height != 0 && texHdr[i].LevelCount != 0) // Texture with defined levels
                {
                    if (game != GxGame.SuperMonkeyBallDX && !Enum.IsDefined(typeof(GxTextureFormat), texHdr[i].FormatRaw))
                    {
                        throw new InvalidTplFileException("Invalid texture header (invalid format.");
                    }
                    if (game == GxGame.SuperMonkeyBallDX)
                    {
                        input.BaseStream.Position = texHdr[i].Offset;
                        int formatRaw = input.ReadInt32();
                        switch (formatRaw)
                        {
                        case 0x0C:
                            texHdr[i].FormatRaw = 0x0E;
                            break;

                        case 0x1A:
                            texHdr[i].FormatRaw = 0x01;
                            break;

                        case 0x0E:
                            texHdr[i].FormatRaw = 0x05;
                            break;

                        default:
                            int x = 5;
                            break;
                        }
                        texHdr[i].Width      = input.ReadInt32();
                        texHdr[i].Height     = input.ReadInt32();
                        texHdr[i].LevelCount = input.ReadInt32();
                        // Compressed?
                        input.ReadInt32();
                        // If uncompressed, data length?
                        input.ReadInt32();
                        // Some other length (for compressed)?
                        input.ReadInt32();
                        // Zero?
                        input.ReadInt32();

                        if (!Enum.IsDefined(typeof(GxTextureFormat), texHdr[i].FormatRaw))
                        {
                            throw new InvalidTplFileException("Invalid texture header (invalid format.");
                        }
                    }
                    else
                    {
                        if (newHeader == null)
                        {
                            input.BaseStream.Position = texHdr[i].Offset;
                        }

                        else
                        {
                            input.BaseStream.Position = texHdr[i].Offset - (newHeader.Value.textureCount * 16);
                        }
                    }
                    tex.LoadTextureData(input, game, (GxTextureFormat)texHdr[i].FormatRaw,
                                        texHdr[i].Width, texHdr[i].Height, texHdr[i].LevelCount);
                }
                else if (texHdr[i].Offset == 0 && texHdr[i].Width == 0 &&
                         texHdr[i].Height == 0 && texHdr[i].LevelCount == 0) // Texture with no defined levels
                {
                    tex.DefineEmptyTexture(texHdr[i].FormatRaw);
                }
                else
                {
                    throw new InvalidTplFileException("Invalid texture header (invalid combination of fields).");
                }

                Add(tex);
            }
        }
예제 #7
0
        public bool ReadTextureFromTPL(BinaryReader reader, int index, out Texture2D texture, string name, bool saveToDisk)
        {
            // If index is invalid
            if (index > numDescriptors)
            {
                throw new System.IndexOutOfRangeException(string.Format("Index must be less than or equal to {0}!", numDescriptors));
            }
            else if (index < 0)
            {
                throw new System.IndexOutOfRangeException("Index must be greater than 0!");
            }

            // Load Texture Descriptor at index
            TEXDescriptor desc = descriptorArray[index];

            // Verify if index is valid (some entries can be nulled out)
            // We check for 0 specifically because garbage can be store in the
            // first few entries of the file.
            if (desc.isNullEntry != 0)
            {
                texture = null;
                return(false);
            }

            // We use Try as GxTextureFormatCodec.GetCodec() can return an error if the type is invalid
            try
            {
                GxTextureFormatCodec codec = GxTextureFormatCodec.GetCodec((GxTextureFormat)desc.format);
                reader.BaseStream.Position = desc.dataPtr;
                byte[] texRaw  = reader.GetBytes(codec.CalcTextureSize(desc.width, desc.height));
                byte[] texRGBA = new byte[4 * desc.width * desc.height]; // RGBA (4 bytes) * w * h
                codec.DecodeTexture(texRGBA, 0, desc.width, desc.height, desc.width * 4, texRaw, 0, null, 0);

                // Reconstruct Texture using Unity's format
                texture = new Texture2D(desc.width, desc.height);
                for (int y = 0; y < desc.height; y++)
                {
                    for (int x = 0; x < desc.width; x++)
                    {
                        // Invert Y because LibGXTexture return array upside-down
                        // ei 'x, (desc.width - y)' instead of 'x, y'
                        texture.SetPixel(x, (desc.width - y), new Color32(
                                             texRGBA[(y * desc.width + x) * 4 + 0],
                                             texRGBA[(y * desc.width + x) * 4 + 1],
                                             texRGBA[(y * desc.width + x) * 4 + 2],
                                             texRGBA[(y * desc.width + x) * 4 + 3]));
                    }
                }

                if (saveToDisk)
                {
                    Debug.LogFormat("Saved {0} to path {1}", name, PersistantDataPath(""));
                    SaveBytes(string.Format("{0}.png", name), texture.EncodeToPNG());
                }

                return(true);
            }
            catch
            {
                Debug.LogErrorFormat("GxTextureFormatCodec.GetCodec() failed to find format [{0}]", desc.format.ToString("X"));
                texture = null;
                return(false);
            }
        }