private int SizeOf2MeshData(GcGame game) { bool isVtx16Bit = (SectionFlags & (uint)GcmfSectionFlags._16Bit) != 0 && !GcmfVersionDetails.Is16BitEffectiveModelIgnored(game); return(PaddingUtils.Align(Meshes.Sum(mesh => mesh.SizeOfIndexedData(isVtx16Bit)), 0x20)); }
private int SizeOfHeaderEntries(GxGame game) { if (game == GxGame.SuperMonkeyBallDX) { return(PaddingUtils.Align((8 + (4 + 4 + 2 + 2 + 2 + 2) * Count), 0x20)); } else { return(PaddingUtils.Align(4 + (4 + 4 + 2 + 2 + 2 + 2) * Count, 0x20)); } }
private void GenerateNodes(ArcFileSystemEntry currentEntry, List <U8Node> nodes, List <ArcFileSystemEntry> entries, ref int currentNodeId, int parentNodeId, ref int currentStringTableOffset, ref int currentDataOffset) { // Add the node and the entry to the enumeration U8Node node = new U8Node(); nodes.Add(node); entries.Add(currentEntry); int thisNodeId = currentNodeId; currentNodeId++; // Reserve space for the name in the string table node.NameOffset = currentStringTableOffset; currentStringTableOffset += currentEntry.Name.Length + 1; if (currentEntry is ArcFileSystemDirectory) { ArcFileSystemDirectory currentDirectory = (ArcFileSystemDirectory)currentEntry; foreach (ArcFileSystemEntry subEntry in currentDirectory.Entries) { GenerateNodes(subEntry, nodes, entries, ref currentNodeId, thisNodeId, ref currentStringTableOffset, ref currentDataOffset); } node.Type = U8Node.TYPE_DIRECTORY; node.DataOffset = parentNodeId; node.Size = currentNodeId; } else if (currentEntry is ArcFileSystemFile) { ArcFileSystemFile currentFile = (ArcFileSystemFile)currentEntry; node.Type = U8Node.TYPE_FILE; node.DataOffset = currentDataOffset; node.Size = currentFile.Data.Length; currentDataOffset = PaddingUtils.Align(currentDataOffset + currentFile.Data.Length, 0x20); } }
/// <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)); }
/// Gets the size of the texture when written to a binary stream. internal int SizeOfTextureData(GcGame game) { int size = 0; for (int level = 0; level < LevelCount; level++) { // Hack: CMPR sizes are not calculated correctly as explained in the doc., replicate the bug if (TplVersionDetails.HasCmprSizeBug(game) && format == GcTextureFormat.CMPR) { int levelWidth = width >> level, levelHeight = height >> level; 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) size += szpad; } else { size += CalculateSizeOfLevel(level); } } return(size); }
private int SizeOfHeader() { return(PaddingUtils.Align(SizeOfHeaderEntries() + SizeOfHeaderNameTable(), 0x20)); }
private int SizeOfHeaderEntries() { return(PaddingUtils.Align(4 + (4 + 4 + 2 + 2 + 2 + 2) * Count, 0x20)); }
internal int SizeOfNonIndexed(bool is16Bit) { return(PaddingUtils.Align(1 + Items.Sum(strip => strip.SizeOfNonIndexed(is16Bit)), 0x20)); }
private int SizeOf2Type8Unknown2() { return(PaddingUtils.Align(2 * Type8Unknown2.Count, 0x20)); }
private int SizeOfHeader() { return(PaddingUtils.Align(0x40 + Materials.Sum(mat => mat.SizeOf()) + TransformMatrices.Sum(tmtx => tmtx.SizeOf()), 0x20)); }
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); } } }
private int SizeOf2MeshData() { return(PaddingUtils.Align(Meshes.Sum(mesh => mesh.SizeOfIndexedData()), 0x20)); }
internal void Load(EndianBinaryReader input, GxGame 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 transformMatrixCount = (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"); } // Load materials for (int i = 0; i < numMaterials; i++) { GcmfMaterial mat = new GcmfMaterial(); mat.Load(input, i); Materials.Add(mat); } if ((SectionFlags & (uint)~(GcmfSectionFlags._16Bit | GcmfSectionFlags.StitchingModel | GcmfSectionFlags.SkinModel | GcmfSectionFlags.EffectiveModel)) != 0) { throw new InvalidGmaFileException("Unknown GCMF section flags."); } if ((SectionFlags & (uint)GcmfSectionFlags.StitchingModel) != 0 || (SectionFlags & (uint)GcmfSectionFlags.SkinModel) != 0) { for (int i = 0; i < transformMatrixCount; i++) { GcmfTransformMatrix tmtx = new GcmfTransformMatrix(); tmtx.Load(input); TransformMatrices.Add(tmtx); } } else { if (transformMatrixCount != 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 for (int i = 0; i < numVertices; i++) { GcmfVertex vtx = new GcmfVertex(); vtx.LoadIndexed(input); VertexPool.Add(vtx); } // Just because it probably won't be there doesn't mean we shouldn't allow it // and some stages have the things that are not allowed /*if ((game == GxGame.SuperMonkeyBall || game == GxGame.SuperMonkeyBallDX) && (SectionFlags & (uint)GcmfSectionFlags.EffectiveModel) != 0) * { * // SMB doesn't have have any 0x08 section flags, so it's unknown how this field may work in that case * if (offsetPartType8Unknown1 != 0) * throw new InvalidGmaFileException("Gcmf [PreSectionHdr] offsetPartType8Unknown1 is not zero on SMB."); * } * else * {*/ 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); } } // Just because it probably won't be there doesn't mean we shouldn't allow it // and some stages have the things that are not allowed /*if ((game == GxGame.SuperMonkeyBall || game == GxGame.SuperMonkeyBallDX) && (SectionFlags & (uint)GcmfSectionFlags.EffectiveModel) != 0) * { * // SMB doesn't have have any 0x08 section flags, so it's unknown how this field may work in that case * if (offsetPartType8Unknown2 != 0) * throw new InvalidGmaFileException("Gcmf [PreSectionHdr] offsetPartType8Unknown2 is not zero on SMB."); * } * else * {*/ 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) { // TODO figure out a better way to calculate this int numEntriesPart3 = Type8Unknown1.Max(u1 => u1.unk18) + 1; for (int i = 0; i < numEntriesPart3; i++) { Type8Unknown2.Add(input.ReadUInt16()); } if (PaddingUtils.Align(Convert.ToInt32(input.BaseStream.Position), 0x20) != sectionBaseOffset + offsetPartMeshData) { throw new InvalidGmaFileException("Gcmf [PreSectionHdr] offsetPart4 doesn't match expected value."); } input.BaseStream.Position = sectionBaseOffset + offsetPartMeshData; } if (Convert.ToInt32(input.BaseStream.Position) != sectionBaseOffset + offsetPartMeshData) { throw new InvalidGmaFileException("Gcmf [PreSectionHdr] offsetPartMeshData doesn't match expected value."); } // Load the mesh data itself (the indexed triangle strips) for (int i = 0; i < numLayer1Meshes + numLayer2Meshes; i++) { Meshes[i].LoadIndexedData(input, 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); } } }
/// <summary> /// Save the contents of this ARC container to an output stream. /// </summary> /// <param name="outputStream">The stream to which the ARC container will be written.</param> public void Save(Stream outputStream) { EndianBinaryWriter outputBinaryStream = new EndianBinaryWriter(EndianBitConverter.Big, outputStream); // Generate node structures. Assign identifiers, string table offsets, data offsets // Note that here the data offsets will be assigned relative to the data zone and not as absolute offsets // This will be fixed later, when we're able to calculate the file layout List <U8Node> nodes = new List <U8Node>(); List <ArcFileSystemEntry> entries = new List <ArcFileSystemEntry>(); int currentNodeId = 0; int currentStringTableOffset = 0; int currentDataOffset = 0; GenerateNodes(Root, nodes, entries, ref currentNodeId, 0, ref currentStringTableOffset, ref currentDataOffset); // Compute offsets of different parts of the file int headerSize = 0x20; int nodesSize = nodes.Count * 12; int stringTableSize = currentStringTableOffset; int rootNodeOffset = 0x20; int stringTableOffset = rootNodeOffset + nodesSize; int nodesAndStringTableSize = nodesSize + stringTableSize; int dataBeginOffset = PaddingUtils.Align(headerSize + nodesSize + stringTableSize, 0x20); foreach (U8Node node in nodes) // Convert data offsets from relative to absolute { if (node.Type == U8Node.TYPE_FILE) { node.DataOffset += dataBeginOffset; } } // Write header outputBinaryStream.Write(ArcMagic); outputBinaryStream.Write(rootNodeOffset); outputBinaryStream.Write(nodesAndStringTableSize); outputBinaryStream.Write(dataBeginOffset); for (int i = 0; i < 0x10; i++) { outputBinaryStream.Write((byte)0xCC); } // Write nodes foreach (U8Node node in nodes) { node.Write(outputBinaryStream); } // Write string table foreach (ArcFileSystemEntry entry in entries) { outputBinaryStream.WriteAsciiString(entry.Name); } // Write file data foreach (ArcFileSystemEntry entry in entries) { if (entry is ArcFileSystemFile) { outputBinaryStream.Align(0x20); outputBinaryStream.Write(((ArcFileSystemFile)entry).Data); } } }