Exemple #1
0
        internal void Save(EndianBinaryWriter output, GcGame game)
        {
            int headerSize = SizeOfHeader();

            // In the GCMF file, the triangle meshes are classified in two layers:
            // The "opaque" layer and the "translucid" layer.
            // We store both kinds of layers in the same vector for easier manipulation
            // (setting the layer property on the triangle mesh itself), which the user
            // may order freely, but now that we're writting the GCMF again, we need to
            // reclassify the meshes in both layers.
            GcmfMesh[] layer1Meshes        = Meshes.Where(mesh => mesh.Layer == GcmfMesh.MeshLayer.Layer1).ToArray();
            GcmfMesh[] layer2Meshes        = Meshes.Where(mesh => mesh.Layer == GcmfMesh.MeshLayer.Layer2).ToArray();
            GcmfMesh[] meshesSortedByLayer = layer1Meshes.Union(layer2Meshes).ToArray();

            // Write GCMF header
            output.Write(GcmfMagic);
            output.Write(SectionFlags);
            output.Write(BoundingSphereCenter.X);
            output.Write(BoundingSphereCenter.Y);
            output.Write(BoundingSphereCenter.Z);
            output.Write(BoundingSphereRadius);
            output.Write(Convert.ToUInt16(Materials.Count));
            output.Write(Convert.ToUInt16(layer1Meshes.Length));
            output.Write(Convert.ToUInt16(layer2Meshes.Length));
            output.Write(Convert.ToByte(TransformMatrices.Count));
            output.Write((byte)0);
            output.Write(headerSize);
            output.Write((uint)0);
            output.Write(TransformMatrixDefaultIdxs);
            output.Write((uint)0);
            output.Write((uint)0);
            output.Write((uint)0);
            output.Write((uint)0);

            for (int i = 0; i < Materials.Count; i++)
            {
                Materials[i].Save(output, i);
            }

            foreach (GcmfTransformMatrix tmtx in TransformMatrices)
            {
                tmtx.Save(output);
            }

            output.Align(0x20);

            if ((SectionFlags & (uint)GcmfSectionFlags.SkinModel) != 0 || (SectionFlags & (uint)GcmfSectionFlags.EffectiveModel) != 0)
            {
                int offsetPartVertexPool    = SizeOf2Header() + SizeOf2MeshHeaders();
                int offsetPartType8Unknown1 = offsetPartVertexPool + SizeOf2VertexPool(game);
                int offsetPartType8Unknown2 = offsetPartType8Unknown1 + (((SectionFlags & (uint)GcmfSectionFlags.SkinModel) != 0) ? SizeOf2Type8Unknown1() : 0);
                int offsetPartMeshData      = offsetPartType8Unknown2 + (((SectionFlags & (uint)GcmfSectionFlags.SkinModel) != 0) ? SizeOf2Type8Unknown2() : 0);
                if (!GcmfVersionDetails.IsGcmfSkinModelSupported(game))
                {
                    // Those are zero on SMB1
                    offsetPartType8Unknown1 = 0;
                    offsetPartType8Unknown2 = 0;
                }

                output.Write(VertexPool.Count);
                output.Write(offsetPartType8Unknown1);
                output.Write(offsetPartVertexPool);
                output.Write(offsetPartMeshData);
                output.Write(offsetPartType8Unknown2);
                output.Write((uint)0);
                output.Write((uint)0);
                output.Write((uint)0);

                // Write the mesh headers
                foreach (GcmfMesh mesh in meshesSortedByLayer)
                {
                    mesh.SaveHeader(output, true, false);
                }

                // Write the vertex pool
                bool isVtx16Bit = (SectionFlags & (uint)GcmfSectionFlags._16Bit) != 0 &&
                                  !GcmfVersionDetails.Is16BitEffectiveModelIgnored(game);

                foreach (GcmfVertex vtx in VertexPool)
                {
                    vtx.SaveIndexed(output, isVtx16Bit);
                }

                if ((SectionFlags & (uint)GcmfSectionFlags.SkinModel) != 0)
                {
                    foreach (GcmfType8Unknown1 unk1 in Type8Unknown1)
                    {
                        unk1.Save(output);
                    }

                    foreach (ushort unk2 in Type8Unknown2)
                    {
                        output.Write(unk2);
                    }

                    output.Align(0x20);
                }

                // Write the section data itself (the indexed triangle strips)
                Dictionary <GcmfVertex, int> vertexPoolIndexes = new Dictionary <GcmfVertex, int>();
                for (int i = 0; i < VertexPool.Count; i++)
                {
                    vertexPoolIndexes.Add(VertexPool[i], i);
                }

                foreach (GcmfMesh mesh in meshesSortedByLayer)
                {
                    mesh.SaveIndexedData(output, isVtx16Bit, vertexPoolIndexes);
                }

                output.Align(0x20);
            }
            else
            {
                bool is16Bit = ((SectionFlags & (uint)GcmfSectionFlags._16Bit) != 0);
                foreach (GcmfMesh mesh in meshesSortedByLayer)
                {
                    mesh.SaveHeader(output, false, is16Bit);
                    mesh.SaveNonIndexedData(output, is16Bit);
                }
            }
        }
Exemple #2
0
        internal void Load(EndianBinaryReader input, GcGame game)
        {
            int baseOffset = Convert.ToInt32(input.BaseStream.Position);

            // Load GCMF header
            if (input.ReadUInt32() != GcmfMagic)
            {
                throw new InvalidGmaFileException("Expected Gcmf[0x00] == GcmfMagic.");
            }
            SectionFlags         = input.ReadUInt32();
            BoundingSphereCenter = new Vector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle());
            BoundingSphereRadius = input.ReadSingle();
            int numMaterials         = (int)input.ReadUInt16();
            int numLayer1Meshes      = (int)input.ReadUInt16();
            int numLayer2Meshes      = (int)input.ReadUInt16();
            int numTransformMatrices = (int)input.ReadByte();

            if (input.ReadByte() != 0)
            {
                throw new InvalidGmaFileException("Expected Gcmf[0x1F] == 0");
            }
            int headerSize = input.ReadInt32();

            if (input.ReadUInt32() != 0)
            {
                throw new InvalidGmaFileException("Expected Gcmf[0x24] == 0");
            }
            input.Read(TransformMatrixDefaultIdxs, 0, 8);
            if (input.ReadUInt32() != 0)
            {
                throw new InvalidGmaFileException("Expected Gcmf[0x30] == 0");
            }
            if (input.ReadUInt32() != 0)
            {
                throw new InvalidGmaFileException("Expected Gcmf[0x34] == 0");
            }
            if (input.ReadUInt32() != 0)
            {
                throw new InvalidGmaFileException("Expected Gcmf[0x38] == 0");
            }
            if (input.ReadUInt32() != 0)
            {
                throw new InvalidGmaFileException("Expected Gcmf[0x3C] == 0");
            }

            // Check that the combination of flags is correct
            if ((SectionFlags & (uint)~(GcmfSectionFlags._16Bit |
                                        GcmfSectionFlags.StitchingModel |
                                        GcmfSectionFlags.SkinModel |
                                        GcmfSectionFlags.EffectiveModel)) != 0)
            {
                throw new InvalidGmaFileException("Unknown GCMF section flags.");
            }

            if ((SectionFlags & (uint)GcmfSectionFlags.SkinModel) != 0 && !GcmfVersionDetails.IsGcmfSkinModelSupported(game))
            {
                throw new InvalidGmaFileException("GCMF with skin model flag is not supported in this game.");
            }

            // Load materials
            for (int i = 0; i < numMaterials; i++)
            {
                GcmfMaterial mat = new GcmfMaterial();
                mat.Load(input, i);
                Materials.Add(mat);
            }

            // Load transform matrices
            if ((SectionFlags & (uint)GcmfSectionFlags.StitchingModel) != 0 ||
                (SectionFlags & (uint)GcmfSectionFlags.SkinModel) != 0)
            {
                for (int i = 0; i < numTransformMatrices; i++)
                {
                    GcmfTransformMatrix tmtx = new GcmfTransformMatrix();
                    tmtx.Load(input);
                    TransformMatrices.Add(tmtx);
                }
            }
            else
            {
                if (numTransformMatrices != 0)
                {
                    throw new InvalidGmaFileException("GcmfSection: No transform matrices expected, but transformMatrixCount != 0?");
                }
            }

            if (PaddingUtils.Align(Convert.ToInt32(input.BaseStream.Position), 0x20) != baseOffset + headerSize)
            {
                throw new InvalidGmaFileException("Gcmf [End Header Offset] mismatch.");
            }

            input.BaseStream.Position = baseOffset + headerSize;

            if ((SectionFlags & (uint)GcmfSectionFlags.SkinModel) != 0 ||
                (SectionFlags & (uint)GcmfSectionFlags.EffectiveModel) != 0)
            {
                int sectionBaseOffset = Convert.ToInt32(input.BaseStream.Position);

                int numVertices             = input.ReadInt32();
                int offsetPartType8Unknown1 = input.ReadInt32();
                int offsetPartVertexPool    = input.ReadInt32();
                int offsetPartMeshData      = input.ReadInt32();
                int offsetPartType8Unknown2 = input.ReadInt32();
                if (input.ReadUInt32() != 0)
                {
                    throw new InvalidGmaFileException("Gcmf[PreSectionHdr-0x14]");
                }
                if (input.ReadUInt32() != 0)
                {
                    throw new InvalidGmaFileException("Gcmf[PreSectionHdr-0x18]");
                }
                if (input.ReadUInt32() != 0)
                {
                    throw new InvalidGmaFileException("Gcmf[PreSectionHdr-0x1C]");
                }

                // Load the mesh headers
                List <GcmfMesh.HeaderSectionInfo> meshHeaderSectionInfos = new List <GcmfMesh.HeaderSectionInfo>();
                for (int i = 0; i < numLayer1Meshes + numLayer2Meshes; i++)
                {
                    GcmfMesh mesh = new GcmfMesh();
                    meshHeaderSectionInfos.Add(mesh.LoadHeader(input,
                                                               (i < numLayer1Meshes) ? GcmfMesh.MeshLayer.Layer1 : GcmfMesh.MeshLayer.Layer2));
                    Meshes.Add(mesh);
                }

                if (Convert.ToInt32(input.BaseStream.Position) != sectionBaseOffset + offsetPartVertexPool)
                {
                    throw new InvalidGmaFileException("Gcmf [PreSectionHdr] offsetPartVertexPool doesn't match expected value.");
                }

                // Load the vertex pool, i.e. the vertices referenced from the indexed triangle strips
                // On SMB1, the 16 bit flag is ignored in effective models
                bool isVtx16Bit = (SectionFlags & (uint)GcmfSectionFlags._16Bit) != 0 &&
                                  !GcmfVersionDetails.Is16BitEffectiveModelIgnored(game);

                for (int i = 0; i < numVertices; i++)
                {
                    GcmfVertex vtx = new GcmfVertex();
                    vtx.LoadIndexed(input, isVtx16Bit);
                    VertexPool.Add(vtx);
                }

                // Load type 8 unknown 1 / unknown 2
                if (GcmfVersionDetails.IsGcmfSkinModelSupported(game))
                {
                    if (Convert.ToInt32(input.BaseStream.Position) != sectionBaseOffset + offsetPartType8Unknown1)
                    {
                        throw new InvalidGmaFileException("Gcmf [PreSectionHdr] offsetPartType8Unknown1 doesn't match expected value.");
                    }

                    if ((SectionFlags & (uint)GcmfSectionFlags.SkinModel) != 0)
                    {
                        for (int i = 0; i < (offsetPartType8Unknown2 - offsetPartType8Unknown1) / 0x20; i++)
                        {
                            GcmfType8Unknown1 unk1 = new GcmfType8Unknown1();
                            unk1.Load(input);
                            Type8Unknown1.Add(unk1);
                        }
                    }

                    if (Convert.ToInt32(input.BaseStream.Position) != sectionBaseOffset + offsetPartType8Unknown2)
                    {
                        throw new InvalidGmaFileException("Gcmf [PreSectionHdr] offsetPartType8Unknown2 doesn't match expected value.");
                    }

                    if ((SectionFlags & (uint)GcmfSectionFlags.SkinModel) != 0)
                    {
                        for (int i = 0; i < numTransformMatrices; i++)
                        {
                            Type8Unknown2.Add(input.ReadUInt16());
                        }

                        if (PaddingUtils.Align(Convert.ToInt32(input.BaseStream.Position), 0x20) != sectionBaseOffset + offsetPartMeshData)
                        {
                            throw new InvalidGmaFileException("Gcmf [PreSectionHdr] offsetPartMeshData doesn't match expected value.");
                        }

                        input.BaseStream.Position = sectionBaseOffset + offsetPartMeshData;
                    }
                }
                else
                {
                    // We already made sure that the section flags are not skin model if it's not supported,
                    // so if we're here, the flag is set to effective model.

                    // In this case, the offsets of those sections must be zero

                    // SMB doesn't have have any 0x08 section flags, so those offsets are zero
                    if (offsetPartType8Unknown1 != 0)
                    {
                        throw new InvalidGmaFileException("Gcmf [PreSectionHdr] offsetPartType8Unknown1 is not zero on SMB.");
                    }

                    if (offsetPartType8Unknown2 != 0)
                    {
                        throw new InvalidGmaFileException("Gcmf [PreSectionHdr] offsetPartType8Unknown2 is not zero on SMB.");
                    }
                }

                // Load the mesh data itself (the indexed triangle strips)
                if (Convert.ToInt32(input.BaseStream.Position) != sectionBaseOffset + offsetPartMeshData)
                {
                    throw new InvalidGmaFileException("Gcmf [PreSectionHdr] offsetPartMeshData doesn't match expected value.");
                }

                for (int i = 0; i < numLayer1Meshes + numLayer2Meshes; i++)
                {
                    Meshes[i].LoadIndexedData(input, isVtx16Bit, VertexPool, meshHeaderSectionInfos[i]);
                }

                // Make sure that all the vertices in the vertex pool got their
                // vertex flags set when we read the indexed triangle strips,
                // that is, that they were referenced at least once.
                // Otherwise, we would have semi-initialized vertices in the vertex pool!
                if (VertexPool.Any(vtx => !vtx.IsIndexedVertexInitialized()))
                {
                    throw new InvalidGmaFileException("Not all vertex flags of all vertices in the vertex pool got initialized!");
                }
            }
            else
            {
                for (int i = 0; i < numLayer1Meshes + numLayer2Meshes; i++)
                {
                    GcmfMesh mesh = new GcmfMesh();
                    GcmfMesh.HeaderSectionInfo headerSectionInfo = mesh.LoadHeader(input,
                                                                                   (i < numLayer1Meshes) ? GcmfMesh.MeshLayer.Layer1 : GcmfMesh.MeshLayer.Layer2);
                    mesh.LoadNonIndexedData(input, headerSectionInfo, (SectionFlags & (uint)GcmfSectionFlags._16Bit) != 0);
                    Meshes.Add(mesh);
                }
            }
        }