示例#1
0
        /// <summary>
        ///     Loads a Dragon Quest VII image.
        /// </summary>
        /// <param name="data">The DMP image data</param>
        /// <returns>The image as a texture</returns>
        public static RenderBase.OTexture load(Stream data)
        {
            BinaryReader input    = new BinaryReader(data);
            string       dmpMagic = IOUtils.readString(input, 0, 3);

            if (dmpMagic != "DMP")
            {
                throw new Exception("DMP: Invalid or corrupted file!");
            }

            string format     = IOUtils.readString(input, 4, 4);
            int    width      = input.ReadUInt16();
            int    height     = input.ReadUInt16();
            int    pow2Width  = input.ReadUInt16();
            int    pow2Height = input.ReadUInt16();

            byte[] buffer = new byte[data.Length - 0x10];
            data.Read(buffer, 0, buffer.Length);
            data.Close();

            Bitmap bmp = null;

            switch (format)
            {
            case "8888": bmp = TextureCodec.decode(buffer, pow2Width, pow2Height, RenderBase.OTextureFormat.rgba8); break;
            }

            Bitmap   newBmp = new Bitmap(width, height);
            Graphics gfx    = Graphics.FromImage(newBmp);

            gfx.DrawImage(bmp, new Rectangle(0, 0, width, height), new Rectangle(0, 0, width, height), GraphicsUnit.Pixel);
            gfx.Dispose();

            return(new RenderBase.OTexture(newBmp, "texture"));
        }
示例#2
0
        private void save()
        {
            using (FileStream data = new FileStream(currentFile, FileMode.Open))
            {
                BinaryReader input  = new BinaryReader(data);
                BinaryWriter output = new BinaryWriter(data);

                for (int i = 0; i < bch.mips[mipSel].textures.Count; i++)
                {
                    loadedTexture tex = bch.mips[mipSel].textures[i];

                    if (tex.modified)
                    {
                        byte[] buffer = align(TextureCodec.encode(tex.texture.texture, tex.type));
                        int    diff   = buffer.Length - tex.length;
                        replaceData(data, tex.offset, returnSize(tex.type, tex.texture.texture.Width, tex.texture.texture.Height), buffer);

                        tex.modified = false;
                        updateTexture(i, tex);
                    }
                }
            }

            MessageBox.Show("Done!", "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }
示例#3
0
        public static RenderBase.OTexture loadTexture(string fileName)
        {
            if (File.Exists(fileName))
            {
                Serialization.SERI tex = Serialization.getSERI(fileName);

                int    width           = tex.getIntegerParameter("w");
                int    height          = tex.getIntegerParameter("h");
                int    mipmap          = tex.getIntegerParameter("mipmap");
                int    format          = tex.getIntegerParameter("format");
                string textureName     = tex.getStringParameter("tex");
                string fullTextureName = Path.Combine(Path.GetDirectoryName(fileName), textureName);

                if (File.Exists(fullTextureName))
                {
                    RenderBase.OTextureFormat fmt = RenderBase.OTextureFormat.dontCare;
                    switch (format)
                    {
                    case 0: fmt = RenderBase.OTextureFormat.l4; break;

                    case 1: fmt = RenderBase.OTextureFormat.l8; break;

                    case 7: fmt = RenderBase.OTextureFormat.rgb565; break;

                    case 8: fmt = RenderBase.OTextureFormat.rgba5551; break;

                    case 9: fmt = RenderBase.OTextureFormat.rgba4; break;

                    case 0xa: fmt = RenderBase.OTextureFormat.rgba8; break;

                    case 0xb: fmt = RenderBase.OTextureFormat.rgb8; break;

                    case 0xc: fmt = RenderBase.OTextureFormat.etc1; break;

                    case 0xd: fmt = RenderBase.OTextureFormat.etc1a4; break;

                    default: Debug.WriteLine("NLP Model: Unknown Texture format 0x" + format.ToString("X8")); break;
                    }

                    string name   = Path.GetFileNameWithoutExtension(textureName);
                    byte[] buffer = File.ReadAllBytes(fullTextureName);
                    return(new RenderBase.OTexture(TextureCodec.decode(buffer, width, height, fmt), name));
                }
            }

            return(null);
        }
        // This REQUIRES a backing CGFX file and doesn't contain enough data to regenerate one from scratch
        public SimplifiedModel(CGFX cgfx)
        {
            this.cgfx = cgfx;

            // Models are always the first entry per CGFX standard
            var models = cgfx.Data.Entries[0]?.Entries;

            // Textures are the second
            var textures = cgfx.Data.Entries[1]?.Entries.Select(e => e.EntryObject).Cast <DICTObjTexture>().ToList();

            if (textures != null && textures.Count > 0)
            {
                Textures = new SMTexture[textures.Count];

                for (var t = 0; t < textures.Count; t++)
                {
                    Bitmap textureBitmap = null;
                    var    name          = textures[t].Name;
                    var    textureData   = textures[t];

                    if (textureData != null)
                    {
                        var textureRGBA = TextureCodec.ConvertTextureToRGBA(new Utility(null, null, Endianness.Little), textureData.TextureCGFXData, textureData.TextureFormat, (int)textureData.Width, (int)textureData.Height);

                        textureBitmap = new Bitmap((int)textureData.Width, (int)textureData.Height, PixelFormat.Format32bppArgb);
                        var imgData = textureBitmap.LockBits(new Rectangle(0, 0, textureBitmap.Width, textureBitmap.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
                        Marshal.Copy(textureRGBA, 0, imgData.Scan0, textureRGBA.Length);
                        textureBitmap.UnlockBits(imgData);
                        textureBitmap.RotateFlip(RotateFlipType.RotateNoneFlipY);
                    }

                    var smTexture = new SMTexture(name, textureBitmap);
                    Textures[t] = smTexture;
                }
            }

            if (models != null && models.Count > 0)
            {
                // This probably isn't a difficult problem to work around, but it's out of my scope at this time
                if (models.Count != 1)
                {
                    throw new InvalidOperationException("File contains more than one model; only supporting one for now.");
                }

                var model = (DICTObjModel)models.First().EntryObject;

                // NOTE: Currently NOT committing the skeleton back, we're just keeping it for model software
                var bones = model.Skeleton?.Bones.Entries.Select(e => e.EntryObject).Cast <DICTObjBone>().ToList() ?? new List <DICTObjBone>();

                Bones = bones.Select(b => new SMBone
                {
                    Name           = b.Name,
                    ParentName     = b.Parent?.Name,
                    Rotation       = b.Rotation,
                    Translation    = b.Translation,
                    Scale          = b.Scale,
                    LocalTransform = b.LocalTransform
                }).ToArray();

                Meshes = new SMMesh[model.Meshes.Length];

                for (var m = 0; m < model.Meshes.Length; m++)
                {
                    var mesh  = model.Meshes[m];
                    var shape = model.Shapes[mesh.ShapeIndex];

                    // There might be some clever way of handling multiple vertex buffers
                    // (if it actually happens) but I'm not worried about it. Only looking
                    // for a single one that is VertexBufferInterleaved.
                    var vertexBuffersInterleaved = shape.VertexBuffers.Where(vb => vb is VertexBufferInterleaved);
                    if (vertexBuffersInterleaved.Count() != 1)
                    {
                        throw new InvalidOperationException("Unsupported count of VertexBuffers in VertexBufferInterleaved format");
                    }

                    // Only expecting / supporting 1 SubMesh entry
                    if (shape.SubMeshes.Count != 1)
                    {
                        throw new InvalidOperationException("Unsupported amount of SubMeshes");
                    }

                    var subMesh = shape.SubMeshes[0];

                    // The BoneReferences in the SubMesh are what the vertex's local index references.
                    var boneReferences = subMesh.BoneReferences;

                    // These aren't "faces" in the geometrical sense, but rather a header of sorts
                    if (subMesh.Faces.Count != 1)
                    {
                        throw new InvalidOperationException("Unsupported amount of Faces");
                    }

                    var faceHeader = subMesh.Faces[0];

                    // Again, just one FaceDescriptor...
                    if (faceHeader.FaceDescriptors.Count != 1)
                    {
                        throw new InvalidOperationException("Unsupported amount of FaceDescriptors");
                    }

                    var faceDescriptor = faceHeader.FaceDescriptors[0];

                    // We're also only supporting triangles at this point; the model format probably
                    // allows for more groups of geometry, but again, out of my scope
                    if (faceDescriptor.PrimitiveMode != FaceDescriptor.PICAPrimitiveMode.Triangles)
                    {
                        throw new InvalidOperationException("Only supporting triangles format");
                    }

                    // Vertices are stored (in GPU-compatible form)
                    var vertexBuffer      = (VertexBufferInterleaved)vertexBuffersInterleaved.Single();
                    var vertexBufferIndex = shape.VertexBuffers.IndexOf(vertexBuffer);
                    var attributes        = vertexBuffer.Attributes.Select(a => VertexBufferCodec.PICAAttribute.GetPICAAttribute(a)).ToList();

                    // The following are the only VertexAttributes we are supporting at this time
                    var supportedAttributes = new List <VertexBuffer.PICAAttributeName>
                    {
                        VertexBuffer.PICAAttributeName.Position,
                        VertexBuffer.PICAAttributeName.Normal,
                        VertexBuffer.PICAAttributeName.TexCoord0,
                        VertexBuffer.PICAAttributeName.BoneIndex,
                        VertexBuffer.PICAAttributeName.BoneWeight,

                        // Caution: Vertex color may not be supported by all model editors!
                        VertexBuffer.PICAAttributeName.Color
                    };

                    // Check if any unsupported attributes are in use
                    var unsupportedAttributes = attributes.Where(a => !supportedAttributes.Contains(a.Name)).Select(a => a.Name);
                    if (unsupportedAttributes.Any())
                    {
                        throw new InvalidOperationException($"This model is using the following unsupported attributes: {string.Join(", ", unsupportedAttributes)}");
                    }

                    var nativeVertices = VertexBufferCodec.GetVertices(shape, vertexBufferIndex);

                    // Convert to the simplified vertices
                    var boneIndexCount  = GetElementsOfAttribute(attributes, VertexBuffer.PICAAttributeName.BoneIndex);  // How many bone indices are actually used
                    var boneWeightCount = GetElementsOfAttribute(attributes, VertexBuffer.PICAAttributeName.BoneWeight); // How many bone weights are actually used

                    // FIXME? There seems to be, on occasion, a bone relationship that points to
                    // the entire mesh but not assigned to any of the vertices. So basically the
                    // vertices are recorded as having no bone indices but in fact are all dependent
                    // upon associating with bone index 0 (?) This will force it to use at least
                    // one bone index even if zero is specified for this case.
                    if (boneReferences != null && boneReferences.Count > 0)
                    {
                        boneIndexCount = Math.Max(boneIndexCount, 1);
                    }

                    var vertices = nativeVertices.Select(v => new SMVertex
                    {
                        Position    = new Vector3(v.Position.X, v.Position.Y, v.Position.Z),
                        Normal      = new Vector3(v.Normal.X, v.Normal.Y, v.Normal.Z),
                        TexCoord    = new Vector3(v.TexCoord0.X, v.TexCoord0.Y, v.TexCoord0.Z),
                        BoneIndices = (new[] { v.Indices.b0, v.Indices.b1, v.Indices.b2, v.Indices.b3 }).Take(boneIndexCount).ToArray(),
                        Weights     = (new[] { v.Weights.w0, v.Weights.w1, v.Weights.w2, v.Weights.w3 }).Take(boneWeightCount).ToArray(),
                        Color       = v.Color // Caution! Not all 3D model editors may support vertex color!
                    }).ToList();

                    // The vertices use relative bone indices based on the SubMesh definitions,
                    // which we're going to make absolute now
                    for (var v = 0; v < vertices.Count; v++)
                    {
                        var vertex = vertices[v];

                        for (var i = 0; i < vertex.BoneIndices.Length; i++)
                        {
                            vertex.BoneIndices[i] = boneReferences[vertex.BoneIndices[i]].Index;

                            // Also, if no bone weights are available, assign a weight of 1 to the first bone.
                            // This won't be stored ultimately as the PICA attributes won't specify it.
                            if (vertex.Weights.Length == 0)
                            {
                                vertex.Weights = new float[] { 1.0f };
                            }
                        }
                    }

                    // Deconstruct into triangle faces
                    var triangles = new List <SMTriangle>();
                    var indices   = faceDescriptor.Indices;
                    for (var i = 0; i < indices.Count; i += 3)
                    {
                        triangles.Add(new SMTriangle
                        {
                            v1 = indices[i + 0],
                            v2 = indices[i + 1],
                            v3 = indices[i + 2]
                        });
                    }

                    // Finally, assign material, if available (mostly for the model editor's benefit)
                    var material  = model.ModelMaterials.Entries.Select(e => e.EntryObject).Cast <DICTObjModelMaterial>().ToList()[mesh.MaterialId];
                    var texture   = material.TextureMappers.First().TextureReference;
                    var name      = texture.ReferenceName;
                    var smTexture = (Textures != null) ? Textures.Where(t => t.Name == name).SingleOrDefault() : null;
                    if (smTexture == null)
                    {
                        smTexture = new SMTexture(name, null);
                    }

                    Meshes[m] = new SMMesh(vertices, triangles, shape, vertexBufferIndex, smTexture);
                }
            }
        }
示例#5
0
        // Imports the RGBA texture data converting it to CGFX and storing it.
        // NOTE: "utility" is just used for endianness, it's not reading/writing a CGFX file
        public void SetTexture(Utility utility, byte[] data, bool safetyCheck = true, uint?width = null, uint?height = null, Format?format = null)
        {
            // TODO: I wonder how feasible it is to use a different size or format...
            // (e.g., in particular, "upgrading" a grayscale texture to full color ...)
            // Unknown whether that might cause a game to crash if it's unexpected or too large.

            if (safetyCheck &&
                (
                    (width.HasValue && width.Value != Width) ||
                    (height.HasValue && height.Value != Height) ||
                    (format.HasValue && format != TextureFormat)
                )
                )
            {
                throw new InvalidOperationException("ChunkDICTTexture SetTexture: The texture you are trying to import does not match the original width/height/format; if you're SURE about this, set safetyCheck = false");
            }


            // NOTE!! Actually changing the texture format is untested and may cause problems / crashes...
            if (width.HasValue)
            {
                // WARNING: This is a GUESS
                if (Width == Unknown3)
                {
                    Unknown3 = Width;
                }
                else
                {
                    throw new NotImplementedException($"ChunkDICTTexture SetTexture: Assumption that 'Unknown3' relates to Width is WRONG (Width={Width}, Unknown3={Unknown3})");
                }

                Width = width.Value;
            }

            if (height.HasValue)
            {
                // WARNING: This is a GUESS
                if (Height == Unknown2)
                {
                    Unknown2 = Height;
                }
                else
                {
                    throw new NotImplementedException($"ChunkDICTTexture SetTexture: Assumption that 'Unknown2' relates to Height is WRONG (Height={Height}, Unknown2={Unknown2})");
                }

                Height = height.Value;
            }

            if (format.HasValue)
            {
                TextureFormat = format.Value;
            }

            // TODO -- BPP??!! (and Unknown1?)

            // TODO -- changing format doesn't update BitsPerPixel yet, not sure how critical, will need generic way to get it
            // ... really need to figure out how it correlates to the texture format ...
            // SEE ALSO: Unknown1, which appears to correlate to BPP "somehow" (but not necessarily identical)

            var newCGFXData = TextureCodec.ConvertTextureToCGFX(utility, data, TextureFormat, (int)Width, (int)Height);

            if (safetyCheck && newCGFXData.Length != TextureCGFXData.Length)
            {
                throw new InvalidOperationException("ChunkDICTTexture SetTexture: The texture data came back in a different raw data size than the original!");
            }

            TextureCGFXData = newCGFXData;
        }
示例#6
0
 // Converts the CGFX texture data to RGBA for exporting
 // NOTE: "utility" is just used for endianness, it's not reading/writing a CGFX file
 public byte[] GetTextureRGBA(Utility utility)
 {
     return(TextureCodec.ConvertTextureToRGBA(utility, TextureCGFXData, TextureFormat, (int)Width, (int)Height));
 }
示例#7
0
        /// <summary>
        ///     Loads a Fantasy Life ZTEX texture from a Stream.
        /// </summary>
        /// <param name="data">The Stream with the data</param>
        /// <returns>The list of textures</returns>
        public static List <RenderBase.OTexture> load(Stream data)
        {
            List <RenderBase.OTexture> textures = new List <RenderBase.OTexture>();

            BinaryReader input = new BinaryReader(data);

            string ztexMagic    = IOUtils.readString(input, 0, 4);
            ushort textureCount = input.ReadUInt16();

            input.ReadUInt16();
            input.ReadUInt32();

            List <textureEntry> entries = new List <textureEntry>();

            for (int i = 0; i < textureCount; i++)
            {
                textureEntry entry = new textureEntry();

                entry.name = IOUtils.readString(input, (uint)(0xc + (i * 0x58)));
                data.Seek(0xc + (i * 0x58) + 0x40, SeekOrigin.Begin);

                input.ReadUInt32();
                entry.offset = input.ReadUInt32();
                input.ReadUInt32();
                entry.length = input.ReadUInt32();
                entry.width  = input.ReadUInt16();
                entry.height = input.ReadUInt16();
                input.ReadByte();
                entry.format = input.ReadByte();
                input.ReadUInt16();

                entries.Add(entry);
            }

            foreach (textureEntry entry in entries)
            {
                data.Seek(entry.offset, SeekOrigin.Begin);
                byte[] buffer = new byte[entry.length];
                data.Read(buffer, 0, buffer.Length);

                Bitmap bmp = null;
                switch (entry.format)
                {
                case 1: bmp = TextureCodec.decode(buffer, entry.width, entry.height, RenderBase.OTextureFormat.rgb565); break;

                case 5: bmp = TextureCodec.decode(buffer, entry.width, entry.height, RenderBase.OTextureFormat.rgba4); break;

                case 9: bmp = TextureCodec.decode(buffer, entry.width, entry.height, RenderBase.OTextureFormat.rgba8); break;

                case 0x18: bmp = TextureCodec.decode(buffer, entry.width, entry.height, RenderBase.OTextureFormat.etc1); break;

                case 0x19: bmp = TextureCodec.decode(buffer, entry.width, entry.height, RenderBase.OTextureFormat.etc1a4); break;
                }

                textures.Add(new RenderBase.OTexture(bmp, entry.name));
            }

            data.Close();

            return(textures);
        }
示例#8
0
        private loadedTexture loadPKM(FileStream data, BinaryReader input)
        {
            loadedTexture tex;

            tex.modified = false;
            long descAddress2 = data.Position;

            data.Seek(descAddress2 + 0x18, SeekOrigin.Begin);
            int texLength = input.ReadInt32();

            data.Seek(descAddress2 + 0x28, SeekOrigin.Begin);
            string textureName = IOUtils.readStringWithLength(input, 0x40);

            data.Seek(descAddress2 + 0x68, SeekOrigin.Begin);
            ushort width      = input.ReadUInt16();
            ushort height     = input.ReadUInt16();
            ushort texFormat  = input.ReadUInt16();
            ushort texMipMaps = input.ReadUInt16();

            data.Seek(0x10, SeekOrigin.Current);
            tex.offset = (uint)data.Position;
            byte[] texBuffer = input.ReadBytes(texLength);

            RenderBase.OTextureFormat fmt = RenderBase.OTextureFormat.dontCare;

            switch (texFormat)
            {
            case 0x2: fmt = RenderBase.OTextureFormat.rgb565; break;

            case 0x3: fmt = RenderBase.OTextureFormat.rgb8; break;

            case 0x4: fmt = RenderBase.OTextureFormat.rgba8; break;

            case 0x17: fmt = RenderBase.OTextureFormat.rgba5551; break;

            case 0x23: fmt = RenderBase.OTextureFormat.la8; break;

            case 0x24: fmt = RenderBase.OTextureFormat.hilo8; break;

            case 0x25: fmt = RenderBase.OTextureFormat.l8; break;

            case 0x26: fmt = RenderBase.OTextureFormat.a8; break;

            case 0x27: fmt = RenderBase.OTextureFormat.la4; break;

            case 0x28: fmt = RenderBase.OTextureFormat.l4; break;

            case 0x29: fmt = RenderBase.OTextureFormat.a4; break;

            case 0x2a: fmt = RenderBase.OTextureFormat.etc1; break;

            case 0x2b: fmt = RenderBase.OTextureFormat.etc1a4; break;
            }

            Bitmap texture = TextureCodec.decode(texBuffer, width, height, fmt);

            tex.texture              = new RenderBase.OTexture(texture, textureName);
            tex.type                 = fmt;
            tex.gpuCommandsOffset    = 0;
            tex.gpuCommandsWordCount = 0;
            tex.length               = texLength;
            return(tex);
        }
示例#9
0
        private bool open(string fileName)
        {
            using (FileStream data = new FileStream(fileName, FileMode.Open))
            {
                BinaryReader input = new BinaryReader(data);

                if (peek(input) == 0x00010000)
                {
                    currentFile = fileName;
                    bch         = new loadedBCH();
                    bch.isBCH   = false;
                    bch.mips.Add(new MIPlayer());
                    packPNK(data, input, bch);
                }
                if (peek(input) == 0x15041213)
                {
                    currentFile = fileName;
                    bch         = new loadedBCH();
                    bch.isBCH   = false;
                    bch.mips.Add(new MIPlayer());
                    bch.mips[0].textures.Add(loadPKM(data, input));
                }
                string magic2b = getMagic(input, 2);
                if (magic2b == "PC" || magic2b == "CM")
                {
                    bch         = new loadedBCH();
                    bch.isBCH   = false;
                    currentFile = fileName;
                    data.Seek(2, SeekOrigin.Current);
                    ushort numEntrys = input.ReadUInt16();
                    for (int i = 0; i < (int)numEntrys; i++)
                    {
                        data.Seek(4 + (i * 4), SeekOrigin.Begin);
                        uint offset = input.ReadUInt32();
                        uint end    = input.ReadUInt32();
                        uint lenth  = end - offset;
                        long rtn    = data.Position;
                        data.Seek(offset, SeekOrigin.Begin);
                        if (magic2b == "CM" & i == 0)
                        {
                            packPNK(data, input, bch);
                        }
                        if (lenth > 4)
                        {
                            if (magic2b == "PC")
                            {
                                if (peek(input) == 0x15041213)
                                {
                                    bch.mips[0].textures.Add(loadPKM(data, input));
                                }
                            }
                        }
                    }
                }
                string magic = IOUtils.readString(input, 0);
                if (magic == "BCH")
                {
                    currentFile = fileName;
                    data.Seek(4, SeekOrigin.Current);
                    byte   backwardCompatibility = input.ReadByte();
                    byte   forwardCompatibility  = input.ReadByte();
                    ushort version = input.ReadUInt16();

                    uint mainHeaderOffset      = input.ReadUInt32();
                    uint stringTableOffset     = input.ReadUInt32();
                    uint gpuCommandsOffset     = input.ReadUInt32();
                    uint dataOffset            = input.ReadUInt32();
                    uint dataExtendedOffset    = backwardCompatibility > 0x20 ? input.ReadUInt32() : 0;
                    uint relocationTableOffset = input.ReadUInt32();

                    uint mainHeaderLength      = input.ReadUInt32();
                    uint stringTableLength     = input.ReadUInt32();
                    uint gpuCommandsLength     = input.ReadUInt32();
                    uint dataLength            = input.ReadUInt32();
                    uint dataExtendedLength    = backwardCompatibility > 0x20 ? input.ReadUInt32() : 0;
                    uint relocationTableLength = input.ReadUInt32();

                    data.Seek(mainHeaderOffset, SeekOrigin.Begin);
                    uint modelsPointerTableOffset  = input.ReadUInt32() + mainHeaderOffset;
                    uint modelsPointerTableEntries = input.ReadUInt32();

                    data.Seek(mainHeaderOffset + 0x24, SeekOrigin.Begin);
                    uint texturesPointerTableOffset  = input.ReadUInt32() + mainHeaderOffset;
                    uint texturesPointerTableEntries = input.ReadUInt32();

                    bch       = new loadedBCH();
                    bch.isBCH = true;
                    for (int i = 0; i < 6; i++)
                    {
                        bch.mips.Add(new MIPlayer());
                    }
                    MipSelect.Enabled = true;

                    //Textures
                    for (int index = 0; index < texturesPointerTableEntries; index++)
                    {
                        data.Seek(texturesPointerTableOffset + (index * 4), SeekOrigin.Begin);
                        data.Seek(input.ReadUInt32() + mainHeaderOffset, SeekOrigin.Begin);

                        loadedTexture tex;
                        tex.modified             = false;
                        tex.gpuCommandsOffset    = input.ReadUInt32() + gpuCommandsOffset;
                        tex.gpuCommandsWordCount = input.ReadUInt32();
                        data.Seek(0x14, SeekOrigin.Current);
                        uint   textureNameOffset = input.ReadUInt32();
                        string textureName       = IOUtils.readString(input, textureNameOffset + stringTableOffset);

                        data.Seek(tex.gpuCommandsOffset, SeekOrigin.Begin);
                        PICACommandReader textureCommands = new PICACommandReader(data, tex.gpuCommandsWordCount);

                        tex.offset = textureCommands.getTexUnit0Address() + dataOffset;
                        RenderBase.OTextureFormat fmt = textureCommands.getTexUnit0Format();
                        Size textureSize = textureCommands.getTexUnit0Size();
                        tex.type = fmt;

                        int OGW = textureSize.Width;
                        int OGH = textureSize.Height;
                        for (int i = 0; i < 6; i++)
                        {
                            textureSize.Width  = OGW / Convert.ToInt32(Math.Pow(2, i));
                            textureSize.Height = OGH / Convert.ToInt32(Math.Pow(2, i));
                            tex.length         = returnSize(fmt, textureSize.Width, textureSize.Height);

                            if (textureSize.Height >= 8 & textureSize.Width >= 8)
                            {
                                data.Seek(tex.offset, SeekOrigin.Begin);
                                byte[] buffer = new byte[tex.length];

                                //data.Seek(tex.length + returnSize(fmt, textureSize.Width / 2, textureSize.Height / 2), SeekOrigin.Current);
                                tex.offset = (uint)data.Position;
                                input.Read(buffer, 0, tex.length);
                                Bitmap texture = TextureCodec.decode(
                                    buffer,
                                    textureSize.Width,
                                    textureSize.Height,
                                    fmt);

                                tex.texture = new RenderBase.OTexture(texture, textureName);

                                bch.mips[i].textures.Add(tex);
                                tex.offset = (uint)data.Position;
                            }
                        }
                    }

                    bch.mainHeaderOffset      = mainHeaderOffset;
                    bch.gpuCommandsOffset     = gpuCommandsOffset;
                    bch.dataOffset            = dataOffset;
                    bch.relocationTableOffset = relocationTableOffset;
                    bch.relocationTableLength = relocationTableLength;
                }
                else if (magic == "CTPK\u0001")
                {
                    currentFile = fileName;
                    data.Seek(4, SeekOrigin.Current);
                    ushort ver                  = input.ReadUInt16();
                    ushort numTexture           = input.ReadUInt16();
                    uint   TextureSectionOffset = input.ReadUInt32();
                    uint   TextureSectionSize   = input.ReadUInt32();
                    uint   HashSectionOffset    = input.ReadUInt32();
                    uint   TextureInfoSection   = input.ReadUInt32();

                    bch       = new loadedBCH();
                    bch.isBCH = false;
                    bch.mips.Add(new MIPlayer());
                    for (int i = 0; i < numTexture; i++)
                    {
                        data.Seek(0x20 * (i + 1), SeekOrigin.Begin);
                        loadedTexture tex;
                        tex.modified          = false;
                        tex.gpuCommandsOffset = (uint)(0x20 * (i + 1));
                        uint   textureNameOffset = input.ReadUInt32();
                        string textureName       = IOUtils.readString(input, textureNameOffset);
                        tex.length = input.ReadInt32();
                        tex.offset = input.ReadUInt32() + TextureSectionOffset;
                        tex.type   = (RenderBase.OTextureFormat)input.ReadUInt32();
                        ushort Width  = input.ReadUInt16();
                        ushort Height = input.ReadUInt16();
                        data.Seek(tex.offset, SeekOrigin.Begin);
                        byte[] buffer = new byte[tex.length];

                        input.Read(buffer, 0, buffer.Length);
                        Bitmap texture = TextureCodec.decode(
                            buffer,
                            Width,
                            Height,
                            tex.type);

                        tex.texture = new RenderBase.OTexture(texture, textureName);

                        tex.gpuCommandsWordCount = 0;

                        bch.mips[0].textures.Add(tex);
                    }
                }
            }

            updateTexturesList();
            return(true);
        }
示例#10
0
        /// <summary>
        ///     Loads a Binary Citra Layout Image from a Stream.
        /// </summary>
        /// <param name="data">The Stream with the data</param>
        /// <returns>The image as a texture</returns>
        public static RenderBase.OTexture load(Stream data)
        {
            BinaryReader input = new BinaryReader(data);

            data.Seek(-0x28, SeekOrigin.End);

            //Note: Stella Glow uses Little Endian BFLIMs, so please don't check the magic ;)
            string climMagic        = IOUtils.readStringWithLength(input, 4);
            ushort endian           = input.ReadUInt16();
            uint   climHeaderLength = input.ReadUInt32();

            input.ReadUInt16();
            uint fileLength = input.ReadUInt32();

            input.ReadUInt32();

            string imagMagic        = IOUtils.readStringWithLength(input, 4);
            uint   imagHeaderLength = input.ReadUInt32();
            ushort width            = input.ReadUInt16();
            ushort height           = input.ReadUInt16();
            uint   format           = input.ReadUInt32();
            uint   length           = input.ReadUInt32();

            if (climMagic == "FLIM")
            {
                format = (format >> 16) & 0xf;
            }

            data.Seek(-(length + 0x28), SeekOrigin.End);
            byte[] buffer = new byte[length];
            data.Read(buffer, 0, buffer.Length);
            data.Close();

            int pow2Width  = (int)(Math.Pow(2, Math.Ceiling(Math.Log(width) / Math.Log(2))));
            int pow2Height = (int)(Math.Pow(2, Math.Ceiling(Math.Log(height) / Math.Log(2))));

            Bitmap bmp = null;

            switch (format)
            {
            case 0: bmp = TextureCodec.decode(buffer, pow2Width, pow2Height, RenderBase.OTextureFormat.l8); break;

            case 1: bmp = TextureCodec.decode(buffer, pow2Width, pow2Height, RenderBase.OTextureFormat.a8); break;

            case 2: bmp = TextureCodec.decode(buffer, pow2Width, pow2Height, RenderBase.OTextureFormat.la4); break;

            case 3: bmp = TextureCodec.decode(buffer, pow2Width, pow2Height, RenderBase.OTextureFormat.la8); break;

            case 4: bmp = TextureCodec.decode(buffer, pow2Width, pow2Height, RenderBase.OTextureFormat.hilo8); break;

            case 5: bmp = TextureCodec.decode(buffer, pow2Width, pow2Height, RenderBase.OTextureFormat.rgb565); break;

            case 6: bmp = TextureCodec.decode(buffer, pow2Width, pow2Height, RenderBase.OTextureFormat.rgb8); break;

            case 7: bmp = TextureCodec.decode(buffer, pow2Width, pow2Height, RenderBase.OTextureFormat.rgba5551); break;

            case 8: bmp = TextureCodec.decode(buffer, pow2Width, pow2Height, RenderBase.OTextureFormat.rgba4); break;

            case 9: bmp = TextureCodec.decode(buffer, pow2Width, pow2Height, RenderBase.OTextureFormat.rgba8); break;

            case 0xa: bmp = TextureCodec.decode(buffer, pow2Width, pow2Height, RenderBase.OTextureFormat.etc1); break;

            case 0xb: bmp = TextureCodec.decode(buffer, pow2Width, pow2Height, RenderBase.OTextureFormat.etc1a4); break;

            case 0xc: bmp = TextureCodec.decode(buffer, pow2Width, pow2Height, RenderBase.OTextureFormat.l4); break;
            }

            Bitmap   newBmp = new Bitmap(width, height);
            Graphics gfx    = Graphics.FromImage(newBmp);

            gfx.DrawImage(bmp, new Rectangle(0, 0, width, height), new Rectangle(0, 0, width, height), GraphicsUnit.Pixel);
            gfx.Dispose();

            return(new RenderBase.OTexture(newBmp, "texture"));
        }
示例#11
0
        /// <summary>
        ///     Loads a Game freak texture.
        /// </summary>
        /// <param name="data">The texture data</param>
        /// <returns>The image as a texture</returns>
        public static Ohana3DS_Transfigured.Ohana.RenderBase.OTexture load(Stream data, bool keepOpen = false)
        {
            BinaryReader input       = new BinaryReader(data);
            long         descAddress = data.Position;

            data.Seek(8, SeekOrigin.Current);
            if (Ohana3DS_Transfigured.Ohana.IOUtils.readStringWithLength(input, 7) != "texture")
            {
                return(null);
            }

            data.Seek(descAddress + 0x18, SeekOrigin.Begin);
            int texLength = input.ReadInt32();

            data.Seek(descAddress + 0x28, SeekOrigin.Begin);
            string texName = IOUtils.readStringWithLength(input, 0x40);

            data.Seek(descAddress + 0x68, SeekOrigin.Begin);
            ushort width      = input.ReadUInt16();
            ushort height     = input.ReadUInt16();
            ushort texFormat  = input.ReadUInt16();
            ushort texMipMaps = input.ReadUInt16();

            data.Seek(0x10, SeekOrigin.Current);
            byte[] texBuffer = input.ReadBytes(texLength);

            RenderBase.OTextureFormat fmt = RenderBase.OTextureFormat.dontCare;

            switch (texFormat)
            {
            case 0x2: fmt = RenderBase.OTextureFormat.rgb565; break;

            case 0x3: fmt = RenderBase.OTextureFormat.rgb8; break;

            case 0x4: fmt = RenderBase.OTextureFormat.rgba8; break;

            case 0x16: fmt = RenderBase.OTextureFormat.rgba4; break;

            case 0x17: fmt = RenderBase.OTextureFormat.rgba5551; break;

            case 0x23: fmt = RenderBase.OTextureFormat.la8; break;

            case 0x24: fmt = RenderBase.OTextureFormat.hilo8; break;

            case 0x25: fmt = RenderBase.OTextureFormat.l8; break;

            case 0x26: fmt = RenderBase.OTextureFormat.a8; break;

            case 0x27: fmt = RenderBase.OTextureFormat.la4; break;

            case 0x28: fmt = RenderBase.OTextureFormat.l4; break;

            case 0x29: fmt = RenderBase.OTextureFormat.a4; break;

            case 0x2a: fmt = RenderBase.OTextureFormat.etc1; break;

            case 0x2b: fmt = RenderBase.OTextureFormat.etc1a4; break;

            default: Debug.WriteLine("Unk tex fmt " + texFormat.ToString("X4") + " @ " + texName); break;
            }

            Bitmap tex = TextureCodec.decode(texBuffer, width, height, fmt);

            if (!keepOpen)
            {
                data.Close();
            }

            return(new RenderBase.OTexture(tex, texName));
        }
示例#12
0
        private void save()
        {
            using (FileStream data = new FileStream(currentFile, FileMode.Open))
            {
                BinaryReader input  = new BinaryReader(data);
                BinaryWriter output = new BinaryWriter(data);

                for (int i = 0; i < bch.textures.Count; i++)
                {
                    loadedTexture tex = bch.textures[i];

                    if (tex.modified)
                    {
                        byte[] buffer = align(TextureCodec.encode(tex.texture.texture, RenderBase.OTextureFormat.rgba8));
                        int    diff   = buffer.Length - tex.length;

                        replaceData(data, tex.offset, tex.length, buffer);

                        //Update offsets of next textures
                        tex.length   = buffer.Length;
                        tex.modified = false;
                        updateTexture(i, tex);
                        for (int j = i; j < bch.textures.Count; j++)
                        {
                            loadedTexture next = bch.textures[j];
                            next.offset = (uint)(next.offset + diff);
                            updateTexture(j, next);
                        }

                        //Update all addresses poiting after the replaced data
                        bch.relocationTableOffset = (uint)(bch.relocationTableOffset + diff);
                        for (int index = 0; index < bch.relocationTableLength; index += 4)
                        {
                            data.Seek(bch.relocationTableOffset + index, SeekOrigin.Begin);
                            uint value  = input.ReadUInt32();
                            uint offset = value & 0x1ffffff;
                            byte flags  = (byte)(value >> 25);

                            if ((flags & 0x20) > 0 || flags == 7 || flags == 0xc)
                            {
                                if ((flags & 0x20) > 0)
                                {
                                    data.Seek((offset * 4) + bch.gpuCommandsOffset, SeekOrigin.Begin);
                                }
                                else
                                {
                                    data.Seek((offset * 4) + bch.mainHeaderOffset, SeekOrigin.Begin);
                                }

                                uint address = input.ReadUInt32();
                                if (address + bch.dataOffset > tex.offset)
                                {
                                    address = (uint)(address + diff);
                                    data.Seek(-4, SeekOrigin.Current);
                                    output.Write(address);
                                }
                            }
                        }

                        uint newSize = (uint)((tex.texture.texture.Width << 16) | tex.texture.texture.Height);

                        //Update texture format
                        data.Seek(tex.gpuCommandsOffset, SeekOrigin.Begin);
                        for (int index = 0; index < tex.gpuCommandsWordCount * 3; index++)
                        {
                            uint command = input.ReadUInt32();

                            switch (command)
                            {
                            case 0xf008e:
                            case 0xf0096:
                            case 0xf009e:
                                replaceCommand(data, output, 0);     //Set texture format to 0 = RGBA8888
                                break;

                            case 0xf0082:
                            case 0xf0092:
                            case 0xf009a:
                                replaceCommand(data, output, newSize);     //Set new texture size
                                break;
                            }
                        }

                        //Update material texture format
                        foreach (loadedMaterial mat in bch.materials)
                        {
                            data.Seek(mat.gpuCommandsOffset, SeekOrigin.Begin);
                            for (int index = 0; index < mat.gpuCommandsWordCount; index++)
                            {
                                uint command = input.ReadUInt32();

                                switch (command)
                                {
                                case 0xf008e: if (mat.texture0 == tex.texture.name || mat.texture0 == "")
                                    {
                                        replaceCommand(data, output, 0);
                                    }
                                    break;

                                case 0xf0096: if (mat.texture1 == tex.texture.name || mat.texture1 == "")
                                    {
                                        replaceCommand(data, output, 0);
                                    }
                                    break;

                                case 0xf009e: if (mat.texture2 == tex.texture.name || mat.texture2 == "")
                                    {
                                        replaceCommand(data, output, 0);
                                    }
                                    break;
                                }
                            }
                        }

                        //Patch up BCH header for new offsets and lengths
                        data.Seek(4, SeekOrigin.Begin);
                        byte backwardCompatibility = input.ReadByte();
                        byte forwardCompatibility  = input.ReadByte();

                        //Update Data Extended and Relocation Table offsets
                        data.Seek(18, SeekOrigin.Current);
                        if (backwardCompatibility > 0x20)
                        {
                            updateAddress(data, input, output, diff);
                        }
                        updateAddress(data, input, output, diff);

                        //Update data length
                        data.Seek(12, SeekOrigin.Current);
                        updateAddress(data, input, output, diff);
                    }
                }
            }

            MessageBox.Show("Done!", "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }
示例#13
0
        private bool open(string fileName)
        {
            using (FileStream data = new FileStream(fileName, FileMode.Open))
            {
                BinaryReader input = new BinaryReader(data);

                string magic = IOUtils.readString(input, 0);
                if (magic != "BCH")
                {
                    return(false);
                }
                currentFile = fileName;
                data.Seek(4, SeekOrigin.Current);
                byte   backwardCompatibility = input.ReadByte();
                byte   forwardCompatibility  = input.ReadByte();
                ushort version = input.ReadUInt16();

                uint mainHeaderOffset      = input.ReadUInt32();
                uint stringTableOffset     = input.ReadUInt32();
                uint gpuCommandsOffset     = input.ReadUInt32();
                uint dataOffset            = input.ReadUInt32();
                uint dataExtendedOffset    = backwardCompatibility > 0x20 ? input.ReadUInt32() : 0;
                uint relocationTableOffset = input.ReadUInt32();

                uint mainHeaderLength      = input.ReadUInt32();
                uint stringTableLength     = input.ReadUInt32();
                uint gpuCommandsLength     = input.ReadUInt32();
                uint dataLength            = input.ReadUInt32();
                uint dataExtendedLength    = backwardCompatibility > 0x20 ? input.ReadUInt32() : 0;
                uint relocationTableLength = input.ReadUInt32();

                data.Seek(mainHeaderOffset, SeekOrigin.Begin);
                uint modelsPointerTableOffset  = input.ReadUInt32() + mainHeaderOffset;
                uint modelsPointerTableEntries = input.ReadUInt32();

                data.Seek(mainHeaderOffset + 0x24, SeekOrigin.Begin);
                uint texturesPointerTableOffset  = input.ReadUInt32() + mainHeaderOffset;
                uint texturesPointerTableEntries = input.ReadUInt32();

                bch = new loadedBCH();

                //Textures
                for (int index = 0; index < texturesPointerTableEntries; index++)
                {
                    data.Seek(texturesPointerTableOffset + (index * 4), SeekOrigin.Begin);
                    data.Seek(input.ReadUInt32() + mainHeaderOffset, SeekOrigin.Begin);

                    loadedTexture tex;
                    tex.modified             = false;
                    tex.gpuCommandsOffset    = input.ReadUInt32() + gpuCommandsOffset;
                    tex.gpuCommandsWordCount = input.ReadUInt32();
                    data.Seek(0x14, SeekOrigin.Current);
                    uint   textureNameOffset = input.ReadUInt32();
                    string textureName       = IOUtils.readString(input, textureNameOffset + stringTableOffset);

                    data.Seek(tex.gpuCommandsOffset, SeekOrigin.Begin);
                    PICACommandReader textureCommands = new PICACommandReader(data, tex.gpuCommandsWordCount);

                    tex.offset = textureCommands.getTexUnit0Address() + dataOffset;
                    RenderBase.OTextureFormat fmt = textureCommands.getTexUnit0Format();
                    Size textureSize = textureCommands.getTexUnit0Size();
                    switch (fmt)
                    {
                    case RenderBase.OTextureFormat.rgba8: tex.length = (textureSize.Width * textureSize.Height) * 4; break;

                    case RenderBase.OTextureFormat.rgb8: tex.length = (textureSize.Width * textureSize.Height) * 3; break;

                    case RenderBase.OTextureFormat.rgba5551: tex.length = (textureSize.Width * textureSize.Height) * 2; break;

                    case RenderBase.OTextureFormat.rgb565: tex.length = (textureSize.Width * textureSize.Height) * 2; break;

                    case RenderBase.OTextureFormat.rgba4: tex.length = (textureSize.Width * textureSize.Height) * 2; break;

                    case RenderBase.OTextureFormat.la8: tex.length = (textureSize.Width * textureSize.Height) * 2; break;

                    case RenderBase.OTextureFormat.hilo8: tex.length = (textureSize.Width * textureSize.Height) * 2; break;

                    case RenderBase.OTextureFormat.l8: tex.length = textureSize.Width * textureSize.Height; break;

                    case RenderBase.OTextureFormat.a8: tex.length = textureSize.Width * textureSize.Height; break;

                    case RenderBase.OTextureFormat.la4: tex.length = textureSize.Width * textureSize.Height; break;

                    case RenderBase.OTextureFormat.l4: tex.length = (textureSize.Width * textureSize.Height) >> 1; break;

                    case RenderBase.OTextureFormat.a4: tex.length = (textureSize.Width * textureSize.Height) >> 1; break;

                    case RenderBase.OTextureFormat.etc1: tex.length = (textureSize.Width * textureSize.Height) >> 1; break;

                    case RenderBase.OTextureFormat.etc1a4: tex.length = textureSize.Width * textureSize.Height; break;

                    default: throw new Exception("OBCHTextureReplacer: Invalid texture format on BCH!");
                    }

                    while ((tex.length & 0x7f) > 0)
                    {
                        tex.length++;
                    }

                    data.Seek(tex.offset, SeekOrigin.Begin);
                    byte[] buffer = new byte[textureSize.Width * textureSize.Height * 4];
                    input.Read(buffer, 0, buffer.Length);
                    Bitmap texture = TextureCodec.decode(
                        buffer,
                        textureSize.Width,
                        textureSize.Height,
                        fmt);

                    tex.texture = new RenderBase.OTexture(texture, textureName);

                    bch.textures.Add(tex);
                }

                //Materials
                for (int mdlIndex = 0; mdlIndex < modelsPointerTableEntries; mdlIndex++)
                {
                    data.Seek(modelsPointerTableOffset + (mdlIndex * 4), SeekOrigin.Begin);
                    data.Seek(input.ReadUInt32() + mainHeaderOffset, SeekOrigin.Begin);
                    data.Seek(0x34, SeekOrigin.Current);

                    uint materialsTableOffset  = input.ReadUInt32() + mainHeaderOffset;
                    uint materialsTableEntries = input.ReadUInt32();

                    for (int index = 0; index < materialsTableEntries; index++)
                    {
                        if (backwardCompatibility < 0x21)
                        {
                            data.Seek(materialsTableOffset + (index * 0x58), SeekOrigin.Begin);
                        }
                        else
                        {
                            data.Seek(materialsTableOffset + (index * 0x2c), SeekOrigin.Begin);
                        }

                        loadedMaterial mat;

                        data.Seek(0x10, SeekOrigin.Current);
                        mat.gpuCommandsOffset    = input.ReadUInt32() + gpuCommandsOffset;
                        mat.gpuCommandsWordCount = input.ReadUInt32();

                        if (backwardCompatibility < 0x21)
                        {
                            data.Seek(0x30, SeekOrigin.Current);
                        }
                        else
                        {
                            data.Seek(4, SeekOrigin.Current);
                        }

                        uint texture0Offset = input.ReadUInt32() + stringTableOffset;
                        uint texture1Offset = input.ReadUInt32() + stringTableOffset;
                        uint texture2Offset = input.ReadUInt32() + stringTableOffset;

                        mat.texture0 = IOUtils.readString(input, texture0Offset);
                        mat.texture1 = IOUtils.readString(input, texture1Offset);
                        mat.texture2 = IOUtils.readString(input, texture2Offset);

                        bch.materials.Add(mat);
                    }
                }

                bch.mainHeaderOffset      = mainHeaderOffset;
                bch.gpuCommandsOffset     = gpuCommandsOffset;
                bch.dataOffset            = dataOffset;
                bch.relocationTableOffset = relocationTableOffset;
                bch.relocationTableLength = relocationTableLength;
            }

            updateTexturesList();
            return(true);
        }