/// <summary> /// Read single animation from file<para/> /// Чтение одной анимации из файла /// </summary> Animation ReadAnimation(BinaryReader f) { Animation a = new Animation(); Header h; // Reading animation name // Чтение имени анимации h = ReadHeader(f, HeaderType.Name); a.Name = f.ReadVCString(h.Size.AlignToOctet()); System.Diagnostics.Debug.WriteLine(a.Name); // Reading objects // Чтение объектов h = ReadHeader(f, HeaderType.Objects); h = ReadHeader(f, HeaderType.Collection); int objectCount = f.ReadInt32(); f.BaseStream.Position += 4; a.Bones = new Bone[objectCount]; for (int i = 0; i < objectCount; i++) { a.Bones[i] = ReadBone(f); } return a; }
/// <summary> /// Constructor, opens file handle and reads filesystem<para/> /// Конструктор, открывает хендл файла и читает список /// </summary> /// <param name="path">Path to IMG file<para/>Путь к IMG-файлу</param> public ArchiveFile(string path) { // Opening file handle // Открытие ссылки if (!File.Exists(path)) { throw new FileNotFoundException("[ArchiveFile] IMG not found: " + Path.GetFileName(path), path); } stream = new BinaryReader(new FileStream(path, FileMode.Open, FileAccess.Read)); // Pick dictionary // Поиск словаря string dict = Path.ChangeExtension(path, ".dir"); if (!File.Exists(dict)) { throw new FileNotFoundException("[ArchiveFile] IMG dictionary not found: " + Path.GetFileName(dict), dict); } // Reading files // Чтение словаря entries = new Dictionary<string, Entry>(); BinaryReader f = new BinaryReader(new FileStream(dict, FileMode.Open, FileAccess.Read)); int fileCount = (int)(f.BaseStream.Length / EntrySize); for (int i = 0; i < fileCount; i++) { // Reading entry // Чтение файла int offset = f.ReadInt32(); int size = f.ReadInt32(); string name = f.ReadVCString(24); Entry e = new Entry(name, offset, size, stream); // Adding to array // Добавляем в список if (entries.ContainsKey(name.ToLower())) { Dev.Console.Log("[ArchiveFile] Duplicate entry: "+name); entries[name.ToLower()] = e; }else{ entries.Add(name.ToLower(), e); } } f.Close(); Dev.Console.Log("[ArchiveFile] Mounted "+Path.GetFileName(path)+" ("+fileCount+" entries)"); }
/// <summary> /// Read collision file from file<para/> /// Чтение файлов коллизий из файла /// </summary> /// <param name="filename">File name<para/>Имя файла</param> public CollisionFile(string filename) { if (!File.Exists(filename)) { throw new FileNotFoundException("[CollisionFile] File not found: "+Path.GetFileName(filename), filename); } // Opening reader // Открытие ридера Collisions = new List<Group>(); BinaryReader f = new BinaryReader(new FileStream(filename, FileMode.Open, FileAccess.Read), Encoding.ASCII); // Read all the models // Чтение всех моделей while (f.BaseStream.Position<f.BaseStream.Length-1) { // Reading header // Чтение заголовка string header = new string(f.ReadChars(4)); if (header != "COLL") { throw new Exception("[CollisionFile] Unknown collision format: "+header); } f.BaseStream.Position += 4; // Creating new group // Создание новой группы Group c = new Group(); // Reading collision head // Чтение оглавления файла c.Name = f.ReadVCString(22).ToLower(); c.ID = f.ReadUInt16(); // Reading bounds // Чтение габаритов c.BoundsRadius = f.ReadSingle(); c.BoundsCenter = f.ReadVCVector(); c.BoundsMin = f.ReadVCVector(); c.BoundsMax = f.ReadVCVector(); // Reading objects // Чтение объектов int numSpheres = f.ReadInt32(); if (numSpheres>0) { // Spheres // Сферы c.Spheres = new Sphere[numSpheres]; for (int i = 0; i < numSpheres; i++) { Sphere s = new Sphere(); s.Radius = f.ReadSingle(); s.Center = f.ReadVCVector(); s.ReadParams(f); c.Spheres[i] = s; } } // Skip unknown index // Пропуск неизвестного числа f.BaseStream.Position += 4; int numBoxes = f.ReadInt32(); if (numBoxes>0) { // Boxes // Боксы c.Boxes = new Box[numBoxes]; for (int i = 0; i < numBoxes; i++) { Box b = new Box(); b.Min = f.ReadVCVector(); b.Max = f.ReadVCVector(); b.ReadParams(f); c.Boxes[i] = b; } } int numVerts = f.ReadInt32(); if (numVerts>0) { // Vertices and triangles // Вершины и треугольники c.Vertices = new Vector3[numVerts]; for (int i = 0; i < numVerts; i++) { c.Vertices[i] = f.ReadVCVector(); } // Reading trimeshes // Чтение тримешей int indexCount = f.ReadInt32(); List<int>[] indices = new List<int>[SurfaceMaterialsCount]; int meshCount = 0; for (int i = 0; i < indexCount; i++) { // Reading single tri // Чтение треугольника int v0 = f.ReadInt32(); int v1 = f.ReadInt32(); int v2 = f.ReadInt32(); int mat = f.ReadByte(); f.BaseStream.Position += 3; // Determining surf // Поиск поверхности if (indices[mat]==null) { indices[mat] = new List<int>(); meshCount++; } indices[mat].AddRange(new int[]{ v0, v1, v2 }); } // Storing trimeshes // Сохранение тримешей c.Meshes = new Trimesh[meshCount]; int p = 0; for (int i = 0; i < indices.Length; i++) { if (indices[i]!=null) { c.Meshes[p] = new Trimesh() { Flags = 0, Material = (byte)i, Indices = indices[i].ToArray() }; p++; } } }else{ // Skip indices - they are empty // Пропуск индексов - они пустые f.BaseStream.Position += 4; } // Store mesh // Сохранение меша if (c.Spheres != null || c.Boxes != null || c.Meshes !=null) { Collisions.Add(c); } // Give prior to main thread // Отдаём предпочтение основному потоку System.Threading.Thread.Sleep(0); } // Closing reader // Закрытие потока f.Close(); }
/// <summary> /// Take single image from stream<para/> /// Получение текстуры из потока /// </summary> /// <param name="f">Stream<para/>Поток</param> /// <returns>Texture entry<para/>Запись текстуры</returns> Entry ReadTexture(BinaryReader f) { Entry tx = new Entry(); // Texture platform flag // Платформа текстуры uint platform = f.ReadUInt32(); if (platform != 8) { throw new Exception("[TextureFile] Specified platform does not match VC: " + platform); } // Filtering flags // Флаги фильтрации tx.Filtering = (FilterMode)f.ReadByte(); byte wrapd = f.ReadByte(); tx.AddressU = (AddressMode)(wrapd & 0x0F); tx.AddressV = (AddressMode)(wrapd >> 4); f.BaseStream.Position += 2; // Texture and mask names // Имена текстуры и её маски tx.Name = f.ReadVCString(32).ToLower(); tx.Mask = f.ReadVCString(32).ToLower(); // Raster format // Формат растра tx.Flags = f.ReadUInt32(); // Does alpha present // Есть ли альфаканал uint hasAlpha = f.ReadUInt32(); // Texture dimensions // Размеры текстуры int texW = f.ReadUInt16(); int texH = f.ReadUInt16(); // Bit depth // Глубина цвета int depth = f.ReadByte(); // Mipmap number // Количество MIP-уровней int mipcount = f.ReadByte(); // Raster type (must be 4) // Тип растра (должен быть 4) int type = f.ReadByte(); // DXT compression // DXT-сжатие tx.ScanCompression = (Compression)f.ReadByte(); // Special palette // Палитра byte[] palette = null; List<Scan> scans = new List<Scan>(); // Reading palette, if needed // Чтение палитры если требуется if ((tx.Flags & (int)RasterFlags.Palette8) > 0 || (tx.Flags & (int)RasterFlags.Palette4) > 0) { int paletteSize = 1024; if ((tx.Flags & (int)RasterFlags.Palette4) > 0) { paletteSize = 64; } palette = f.ReadBytes(paletteSize); } // Reading data for all mipmaps // Чтение MIP-уровней for (int mip = 0; mip < mipcount; mip++) { // Size of data (may be 0) // Размер данных (может быть равен 0) int dataSize = (int)f.ReadUInt32(); if (dataSize == 0) { // Computing size // Вычисление размера if ((tx.Flags & (int)RasterFlags.Palette8) > 0 || (tx.Flags & (int)RasterFlags.Palette4) > 0) { // Format is paletted // Формат задан палитрой dataSize = texW * texH; if ((tx.Flags & (int)RasterFlags.Palette4) > 0) { dataSize /= 2; } } else if (tx.ScanCompression != Compression.Uncompressed) { // Format is compressed // Кадры подвержены сжатию int ttw = texW; int tth = texH; if (ttw < 4) ttw = 4; if (tth < 4) tth = 4; if (tx.ScanCompression == Compression.DXT3) { dataSize = (ttw / 4) * (tth / 4) * 16; } else { dataSize = (ttw / 4) * (tth / 4) * 8; } } } // Reading raw data, corresponding to size // Чтение данных byte[] data = f.ReadBytes(dataSize); Scan scan = new Scan(); scan.Size = new Vector2(texW, texH); if ((tx.Flags & (int)RasterFlags.Palette8) > 0) { // Decoding paletted textures // Раскодирование текстур с палитрой byte[] output = new byte[texW * texH * 4]; for (int i = 0; i < data.Length; i++) { Array.Copy(palette, data[i] * 4, output, i * 4, 4); } scan.Data = output; } else { // Generic textures, may be compressed // Обычные текстуры, возмножно сжатые scan.Data = data; } scans.Add(scan); texW /= 2; texH /= 2; } tx.Scans = scans.ToArray(); return tx; }
/// <summary> /// Read single bone from animation file<para/> /// Чтение одной кости из файла /// </summary> Bone ReadBone(BinaryReader f) { Bone b = new Bone(); Header h; // Reading header // Чтение заголовка h = ReadHeader(f, HeaderType.Animation); h = ReadHeader(f, HeaderType.AnimationData); // Reading bone // Чтение кости b.Name = f.ReadVCString(28); b.FrameСount = f.ReadInt32(); // Skip unknown info // Пропуск неизвестной информации if (h.Size == 48) { f.BaseStream.Position += 4; } b.LastFrame = f.ReadInt32(); b.NextSibling = f.ReadInt32(); b.PrevSibling = f.ReadInt32(); b.Frames = ReadFrames(f, b.FrameСount); return b; }
/// <summary> /// Reading material data<para/> /// Чтение материалов /// </summary> /// <param name="g">Surface<para/>Поверхность</param> /// <param name="f">BinaryReader</param> /// <param name="numMaterials">Materials count<para/>Количество материалов</param> void ReadMaterials(Geometry g, BinaryReader f, int numMaterials) { g.Materials = new Material[numMaterials]; for (int mt = 0; mt < numMaterials; mt++) { // Material header ChunkHeader h = ReadHeader(f); if (h.Type != ChunkType.Material) { throw new Exception("[ModelFile] Unexpected chunk: " + h.Type); } h = ReadHeader(f); if (h.Type != ChunkType.Struct) { throw new Exception("[ModelFile] Unexpected chunk: " + h.Type); } Material m = new Material(); // Reading material flags // Чтение флагов m.Flags = f.ReadInt32(); // Material tint color // Цвет материала m.Color = f.ReadBytes(4); if (m.Color[3]<254f) { m.HasAlpha = true; } // Skip some data // Пропуск данных f.BaseStream.Position += 4; // Reading surface props // Чтение параметров поверхности int texHave = f.ReadInt32(); m.Props = new float[3]; for (int pg = 0; pg < 3; pg++) { m.Props[pg] = f.ReadSingle(); } // Reading texture data // Чтение данных текстуры if (texHave > 0) { h = ReadHeader(f); int texEnd = (int)f.BaseStream.Position + (int)h.Size; if (h.Type != ChunkType.Texture) { throw new Exception("[ModelFile] Unexpected chunk: " + h.Type); } h = ReadHeader(f); if (h.Type != ChunkType.Struct) { throw new Exception("[ModelFile] Unexpected chunk: " + h.Type); } m.Textures = new Texture[1]; Texture t = new Texture(); m.Textures[0] = t; t.Filter = (TextureFile.FilterMode)f.ReadByte(); byte wrapd = f.ReadByte(); t.AddressU = (TextureFile.AddressMode)(wrapd & 0x0F); t.AddressV = (TextureFile.AddressMode)(wrapd >> 4); f.BaseStream.Position += 2; h = ReadHeader(f); if (h.Type != ChunkType.String) { throw new Exception("[ModelFile] Unexpected chunk: " + h.Type); } t.Name = f.ReadVCString((int)h.Size).ToLower(); h = ReadHeader(f); if (h.Type != ChunkType.String) { throw new Exception("[ModelFile] Unexpected chunk: " + h.Type); } t.MaskName = f.ReadVCString((int)h.Size).ToLower(); if (t.MaskName != "") { m.HasAlpha = true; } h = ReadHeader(f); if (h.Type != ChunkType.Extension) { throw new Exception("[ModelFile] Unexpected chunk: " + h.Type); } f.BaseStream.Position += h.Size; } else { if (g.HasAlpha) { m.HasAlpha = true; } } h = ReadHeader(f); if (h.Type != ChunkType.Extension) { throw new Exception("[ModelFile] Unexpected chunk: " + h.Type); } if (h.Size > 0) { int ops = (int)(f.BaseStream.Position + h.Size); while (true) { if (f.BaseStream.Position >= ops) { break; } h = ReadHeader(f); switch (h.Type) { default: f.BaseStream.Position += h.Size; break; } } } g.Materials[mt] = m; } }
/// <summary> /// Reading frame data<para/> /// Чтение кадров анимации /// </summary> /// <param name="f">Stream<para/>Поток</param> /// <param name="numFrames">Frame count<para/>Количество кадров</param> void ReadFrames(BinaryReader f, int numFrames) { Frames = new Frame[numFrames]; // Reading struct part // Чтение структуры for (int i = 0; i < numFrames; i++) { Frame fr = new Frame(); // Frame rotation // Поворот кадра fr.Rotation = new float[9]; for (int r = 0; r < 9; r++) { fr.Rotation[r] = f.ReadSingle(); } // Frame position // Позиция кадра fr.Position = new float[3]; for (int p = 0; p < 3; p++) { fr.Position[p] = f.ReadSingle(); } // Parent index // Индекс родителя fr.Parent = f.ReadInt32(); // Skip data // Пропуск данных f.BaseStream.Position += 4; Frames[i] = fr; } // Reading extension // Чтение расширения for (int i = 0; i < numFrames; i++) { ChunkHeader h = ReadHeader(f); if (h.Type != ChunkType.Extension) { throw new Exception("[ModelFile] Unexpected chunk: " + h.Type); } if (h.Size > 0) { int endps = (int)(f.BaseStream.Position + h.Size); while (true) { if (f.BaseStream.Position >= endps) { break; } h = ReadHeader(f); switch (h.Type) { case ChunkType.Frame: Frames[i].Name = f.ReadVCString((int)h.Size); break; default: f.BaseStream.Position += h.Size; break; } } } } }