#region References using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; using System.IO; #endregion //using System.Windows.Media.Imaging; namespace Ultima { public sealed class AnimationEdit { private static FileIndex m_FileIndex = new FileIndex("Anim.idx", "Anim.mul", 6); private static FileIndex m_FileIndex2 = new FileIndex("Anim2.idx", "Anim2.mul", -1); private static FileIndex m_FileIndex3 = new FileIndex("Anim3.idx", "Anim3.mul", -1); private static FileIndex m_FileIndex4 = new FileIndex("Anim4.idx", "Anim4.mul", -1); private static FileIndex m_FileIndex5 = new FileIndex("Anim5.idx", "Anim5.mul", -1); private static AnimIdx[] animcache; private static readonly AnimIdx[] animcache2; private static readonly AnimIdx[] animcache3; private static readonly AnimIdx[] animcache4; private static readonly AnimIdx[] animcache5; static AnimationEdit() { if (m_FileIndex.IdxLength > 0) { animcache = new AnimIdx[m_FileIndex.IdxLength / 12]; } if (m_FileIndex2.IdxLength > 0) { animcache2 = new AnimIdx[m_FileIndex2.IdxLength / 12]; } if (m_FileIndex3.IdxLength > 0) { animcache3 = new AnimIdx[m_FileIndex3.IdxLength / 12]; } if (m_FileIndex4.IdxLength > 0) { animcache4 = new AnimIdx[m_FileIndex4.IdxLength / 12]; } if (m_FileIndex5.IdxLength > 0) { animcache5 = new AnimIdx[m_FileIndex5.IdxLength / 12]; } } /// /// Rereads AnimX files /// public static void Reload() { m_FileIndex = new FileIndex("Anim.idx", "Anim.mul", 6); m_FileIndex2 = new FileIndex("Anim2.idx", "Anim2.mul", -1); m_FileIndex3 = new FileIndex("Anim3.idx", "Anim3.mul", -1); m_FileIndex4 = new FileIndex("Anim4.idx", "Anim4.mul", -1); m_FileIndex5 = new FileIndex("Anim5.idx", "Anim5.mul", -1); if (m_FileIndex.IdxLength > 0) { animcache = new AnimIdx[m_FileIndex.IdxLength / 12]; } if (m_FileIndex2.IdxLength > 0) { animcache = new AnimIdx[m_FileIndex2.IdxLength / 12]; } if (m_FileIndex3.IdxLength > 0) { animcache = new AnimIdx[m_FileIndex3.IdxLength / 12]; } if (m_FileIndex4.IdxLength > 0) { animcache = new AnimIdx[m_FileIndex4.IdxLength / 12]; } if (m_FileIndex5.IdxLength > 0) { animcache = new AnimIdx[m_FileIndex5.IdxLength / 12]; } } private static void GetFileIndex( int body, int fileType, int action, int direction, out FileIndex fileIndex, out int index) { switch (fileType) { default: case 1: fileIndex = m_FileIndex; if (body < 200) { index = body * 110; } else if (body < 400) { index = 22000 + ((body - 200) * 65); } else { index = 35000 + ((body - 400) * 175); } break; case 2: fileIndex = m_FileIndex2; if (body < 200) { index = body * 110; } else { index = 22000 + ((body - 200) * 65); } break; case 3: fileIndex = m_FileIndex3; if (body < 300) { index = body * 65; } else if (body < 400) { index = 33000 + ((body - 300) * 110); } else { index = 35000 + ((body - 400) * 175); } break; case 4: fileIndex = m_FileIndex4; if (body < 200) { index = body * 110; } else if (body < 400) { index = 22000 + ((body - 200) * 65); } else { index = 35000 + ((body - 400) * 175); } break; case 5: fileIndex = m_FileIndex5; if ((body < 200) && (body != 34)) // looks strange, though it works. { index = body * 110; } else if (body < 400) { index = 22000 + ((body - 200) * 65); } else { index = 35000 + ((body - 400) * 175); } break; } index += action * 5; if (direction <= 4) { index += direction; } else { index += direction - (direction - 4) * 2; } } private static AnimIdx[] GetCache(int filetype) { switch (filetype) { case 1: return animcache; case 2: return animcache2; case 3: return animcache3; case 4: return animcache4; case 5: return animcache5; default: return animcache; } } public static AnimIdx GetAnimation(int filetype, int body, int action, int dir) { AnimIdx[] cache = GetCache(filetype); FileIndex fileIndex; int index; GetFileIndex(body, filetype, action, dir, out fileIndex, out index); if (cache != null) { if (cache[index] != null) { return cache[index]; } } return cache[index] = new AnimIdx(index, fileIndex, filetype); } public static bool IsActionDefinied(int filetype, int body, int action) { AnimIdx[] cache = GetCache(filetype); FileIndex fileIndex; int index; GetFileIndex(body, filetype, action, 0, out fileIndex, out index); if (cache != null) { if (cache[index] != null) { if ((cache[index].Frames != null) && (cache[index].Frames.Count > 0)) { return true; } else { return false; } } } int AnimCount = Animations.GetAnimLength(body, filetype); if (AnimCount < action) { return false; } int length, extra; bool patched; bool valid = fileIndex.Valid(index, out length, out extra, out patched); if ((!valid) || (length < 1)) { return false; } return true; } public static void LoadFromVD(int filetype, int body, BinaryReader bin) { AnimIdx[] cache = GetCache(filetype); FileIndex fileIndex; int index; GetFileIndex(body, filetype, 0, 0, out fileIndex, out index); int animlength = Animations.GetAnimLength(body, filetype) * 5; var entries = new Entry3D[animlength]; for (int i = 0; i < animlength; ++i) { entries[i].lookup = bin.ReadInt32(); entries[i].length = bin.ReadInt32(); entries[i].extra = bin.ReadInt32(); } foreach (Entry3D entry in entries) { if ((entry.lookup > 0) && (entry.lookup < bin.BaseStream.Length) && (entry.length > 0)) { bin.BaseStream.Seek(entry.lookup, SeekOrigin.Begin); cache[index] = new AnimIdx(bin, entry.extra); } ++index; } } public static void ExportToVD(int filetype, int body, string file) { AnimIdx[] cache = GetCache(filetype); FileIndex fileIndex; int index; GetFileIndex(body, filetype, 0, 0, out fileIndex, out index); using (var fs = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Write)) { using (var bin = new BinaryWriter(fs)) { bin.Write((short)6); int animlength = Animations.GetAnimLength(body, filetype); int currtype = animlength == 22 ? 0 : animlength == 13 ? 1 : 2; bin.Write((short)currtype); long indexpos = bin.BaseStream.Position; long animpos = bin.BaseStream.Position + 12 * animlength * 5; for (int i = index; i < index + animlength * 5; i++) { AnimIdx anim; if (cache != null) { if (cache[i] != null) { anim = cache[i]; } else { anim = cache[i] = new AnimIdx(i, fileIndex, filetype); } } else { anim = cache[i] = new AnimIdx(i, fileIndex, filetype); } if (anim == null) { bin.BaseStream.Seek(indexpos, SeekOrigin.Begin); bin.Write(-1); bin.Write(-1); bin.Write(-1); indexpos = bin.BaseStream.Position; } else { anim.ExportToVD(bin, ref indexpos, ref animpos); } } } } } public static void Save(int filetype, string path) { string filename; AnimIdx[] cache; FileIndex fileindex; switch (filetype) { case 1: filename = "anim"; cache = animcache; fileindex = m_FileIndex; break; case 2: filename = "anim2"; cache = animcache2; fileindex = m_FileIndex2; break; case 3: filename = "anim3"; cache = animcache3; fileindex = m_FileIndex3; break; case 4: filename = "anim4"; cache = animcache4; fileindex = m_FileIndex4; break; case 5: filename = "anim5"; cache = animcache5; fileindex = m_FileIndex5; break; default: filename = "anim"; cache = animcache; fileindex = m_FileIndex; break; } string idx = Path.Combine(path, filename + ".idx"); string mul = Path.Combine(path, filename + ".mul"); using ( FileStream fsidx = new FileStream(idx, FileMode.Create, FileAccess.Write, FileShare.Write), fsmul = new FileStream(mul, FileMode.Create, FileAccess.Write, FileShare.Write)) { using (BinaryWriter binidx = new BinaryWriter(fsidx), binmul = new BinaryWriter(fsmul)) { for (int idxc = 0; idxc < cache.Length; ++idxc) { AnimIdx anim; if (cache != null) { if (cache[idxc] != null) { anim = cache[idxc]; } else { anim = cache[idxc] = new AnimIdx(idxc, fileindex, filetype); } } else { anim = cache[idxc] = new AnimIdx(idxc, fileindex, filetype); } if (anim == null) { binidx.Write(-1); binidx.Write(-1); binidx.Write(-1); } else { anim.Save(binmul, binidx); } } } } } } public sealed class AnimIdx { public int idxextra; public ushort[] Palette { get; private set; } public List Frames { get; private set; } public AnimIdx(int index, FileIndex fileIndex, int filetype) { Palette = new ushort[0x100]; int length, extra; bool patched; Stream stream = fileIndex.Seek(index, out length, out extra, out patched); if ((stream == null) || (length < 1)) { return; } idxextra = extra; using (var bin = new BinaryReader(stream)) { for (int i = 0; i < 0x100; ++i) { Palette[i] = (ushort)(bin.ReadUInt16() ^ 0x8000); } var start = (int)bin.BaseStream.Position; int frameCount = bin.ReadInt32(); var lookups = new int[frameCount]; for (int i = 0; i < frameCount; ++i) { lookups[i] = start + bin.ReadInt32(); } Frames = new List(); for (int i = 0; i < frameCount; ++i) { stream.Seek(lookups[i], SeekOrigin.Begin); Frames.Add(new FrameEdit(bin)); } } stream.Close(); } public AnimIdx(BinaryReader bin, int extra) { Palette = new ushort[0x100]; idxextra = extra; for (int i = 0; i < 0x100; ++i) { Palette[i] = (ushort)(bin.ReadUInt16() ^ 0x8000); } var start = (int)bin.BaseStream.Position; int frameCount = bin.ReadInt32(); var lookups = new int[frameCount]; for (int i = 0; i < frameCount; ++i) { lookups[i] = start + bin.ReadInt32(); } Frames = new List(); for (int i = 0; i < frameCount; ++i) { bin.BaseStream.Seek(lookups[i], SeekOrigin.Begin); Frames.Add(new FrameEdit(bin)); } } public unsafe Bitmap[] GetFrames() { if ((Frames == null) || (Frames.Count == 0)) { return null; } var bits = new Bitmap[Frames.Count]; for (int i = 0; i < bits.Length; ++i) { FrameEdit frame = Frames[i]; int width = frame.width; int height = frame.height; if (height == 0 || width == 0) { continue; } var bmp = new Bitmap(width, height, Settings.PixelFormat); BitmapData bd = bmp.LockBits( new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, Settings.PixelFormat); var line = (ushort*)bd.Scan0; int delta = bd.Stride >> 1; int xBase = frame.Center.X - 0x200; int yBase = frame.Center.Y + height - 0x200; line += xBase; line += yBase * delta; for (int j = 0; j < frame.RawData.Length; ++j) { FrameEdit.Raw raw = frame.RawData[j]; ushort* cur = line + (((raw.offy) * delta) + ((raw.offx) & 0x3FF)); ushort* end = cur + (raw.run); int ii = 0; while (cur < end) { *cur++ = Palette[raw.data[ii++]]; } } bmp.UnlockBits(bd); bits[i] = bmp; } return bits; } public void AddFrame(Bitmap bit) { if (Frames == null) { Frames = new List(); } Frames.Add(new FrameEdit(bit, Palette, 0, 0)); } public void ReplaceFrame(Bitmap bit, int index) { if ((Frames == null) || (Frames.Count == 0)) { return; } if (index > Frames.Count) { return; } Frames[index] = new FrameEdit(bit, Palette, (Frames[index]).Center.X, (Frames[index]).Center.Y); } public void RemoveFrame(int index) { if (Frames == null) { return; } if (index > Frames.Count) { return; } Frames.RemoveAt(index); } public void ClearFrames() { if (Frames == null) { return; } Frames.Clear(); } #if false //Soulblighter Modification public void GetGifPalette(Bitmap bit) { using (MemoryStream imageStreamSource = new MemoryStream()) { System.Drawing.ImageConverter ic = new System.Drawing.ImageConverter(); byte[] btImage = (byte[])ic.ConvertTo(bit, typeof(byte[])); imageStreamSource.Write(btImage, 0, btImage.Length); GifBitmapDecoder decoder = new GifBitmapDecoder(imageStreamSource, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default); BitmapPalette pal = decoder.Palette; int i; for (i = 0; i < 0x100; i++) { this.Palette[i] = 0; } try { i = 0; while (i < 0x100)//&& i < pal.Colors.Count) { int Red = pal.Colors[i].R / 8; int Green = pal.Colors[i].G / 8; int Blue = pal.Colors[i].B / 8; int contaFinal = (((0x400 * Red) + (0x20 * Green)) + Blue) + 0x8000; if (contaFinal == 0x8000) contaFinal = 0x8001; this.Palette[i] = (ushort)contaFinal; i++; } } catch (System.IndexOutOfRangeException) { } catch (System.ArgumentOutOfRangeException) { } for (i = 0; i < 0x100; i++) { if (this.Palette[i] < 0x8000) this.Palette[i] = 0x8000; } } } #endif public unsafe void GetImagePalette(Bitmap bit) { int count = 0; var bmp = new Bitmap(bit); BitmapData bd = bmp.LockBits( new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, Settings.PixelFormat); var line = (ushort*)bd.Scan0; int delta = bd.Stride >> 1; ushort* cur = line; int i = 0; while (i < 0x100) { Palette[i] = 0; i++; } int y = 0; while (y < bmp.Height) { cur = line; for (int x = 0; x < bmp.Width; x++) { ushort c = cur[x]; if (c != 0) { bool found = false; i = 0; while (i < Palette.Length) { if (Palette[i] == c) { found = true; break; } i++; } if (!found) { Palette[count++] = c; } if (count >= 0x100) { break; } } } for (i = 0; i < 0x100; i++) { if (Palette[i] < 0x8000) { Palette[i] = 0x8000; } } if (count >= 0x100) { break; } y++; line += delta; } } public void PaletteConversor(int seletor) { int i; for (i = 0; i < 0x100; i++) { int BlueTemp = (Palette[i] - 0x8000) / 0x20; BlueTemp *= 0x20; BlueTemp = (Palette[i] - 0x8000) - BlueTemp; int GreenTemp = (Palette[i] - 0x8000) / 0x400; GreenTemp *= 0x400; GreenTemp = ((Palette[i] - 0x8000) - GreenTemp) - BlueTemp; GreenTemp /= 0x20; int RedTemp = (Palette[i] - 0x8000) / 0x400; int contaFinal = 0; switch (seletor) { case 1: contaFinal = (((0x400 * RedTemp) + (0x20 * GreenTemp)) + BlueTemp) + 0x8000; break; case 2: contaFinal = (((0x400 * RedTemp) + (0x20 * BlueTemp)) + GreenTemp) + 0x8000; break; case 3: contaFinal = (((0x400 * GreenTemp) + (0x20 * RedTemp)) + BlueTemp) + 0x8000; break; case 4: contaFinal = (((0x400 * GreenTemp) + (0x20 * BlueTemp)) + RedTemp) + 0x8000; break; case 5: contaFinal = (((0x400 * BlueTemp) + (0x20 * GreenTemp)) + RedTemp) + 0x8000; break; case 6: contaFinal = (((0x400 * BlueTemp) + (0x20 * RedTemp)) + GreenTemp) + 0x8000; break; } if (contaFinal == 0x8000) { contaFinal = 0x8001; } Palette[i] = (ushort)contaFinal; } for (i = 0; i < 0x100; i++) { if (Palette[i] < 0x8000) { Palette[i] = 0x8000; } } } public void PaletteReductor(int Redp, int Greenp, int Bluep) { int i; Redp /= 8; Greenp /= 8; Bluep /= 8; for (i = 0; i < 0x100; i++) { int BlueTemp = (Palette[i] - 0x8000) / 0x20; BlueTemp *= 0x20; BlueTemp = (Palette[i] - 0x8000) - BlueTemp; int GreenTemp = (Palette[i] - 0x8000) / 0x400; GreenTemp *= 0x400; GreenTemp = ((Palette[i] - 0x8000) - GreenTemp) - BlueTemp; GreenTemp /= 0x20; int RedTemp = (Palette[i] - 0x8000) / 0x400; RedTemp += Redp; GreenTemp += Greenp; BlueTemp += Bluep; if (RedTemp < 0) { RedTemp = 0; } if (RedTemp > 0x1f) { RedTemp = 0x1f; } if (GreenTemp < 0) { GreenTemp = 0; } if (GreenTemp > 0x1f) { GreenTemp = 0x1f; } if (BlueTemp < 0) { BlueTemp = 0; } if (BlueTemp > 0x1f) { BlueTemp = 0x1f; } int contaFinal = (((0x400 * RedTemp) + (0x20 * GreenTemp)) + BlueTemp) + 0x8000; if (contaFinal == 0x8000) { contaFinal = 0x8001; } Palette[i] = (ushort)contaFinal; } for (i = 0; i < 0x100; i++) { if (Palette[i] < 0x8000) { Palette[i] = 0x8000; } } } //End of Soulblighter Modification public unsafe void ExportPalette(string filename, int type) { switch (type) { case 0: using (var Tex = new StreamWriter(new FileStream(filename, FileMode.Create, FileAccess.ReadWrite))) { for (int i = 0; i < 0x100; ++i) { Tex.WriteLine(Palette[i]); } } break; case 1: { var bmp = new Bitmap(0x100, 20, Settings.PixelFormat); BitmapData bd = bmp.LockBits( new Rectangle(0, 0, 0x100, 20), ImageLockMode.WriteOnly, Settings.PixelFormat); var line = (ushort*)bd.Scan0; int delta = bd.Stride >> 1; for (int y = 0; y < bd.Height; ++y, line += delta) { ushort* cur = line; for (int i = 0; i < 0x100; ++i) { *cur++ = Palette[i]; } } bmp.UnlockBits(bd); var b = new Bitmap(bmp); b.Save(filename, ImageFormat.Bmp); b.Dispose(); bmp.Dispose(); break; } case 2: { var bmp = new Bitmap(0x100, 20, Settings.PixelFormat); BitmapData bd = bmp.LockBits( new Rectangle(0, 0, 0x100, 20), ImageLockMode.WriteOnly, Settings.PixelFormat); var line = (ushort*)bd.Scan0; int delta = bd.Stride >> 1; for (int y = 0; y < bd.Height; ++y, line += delta) { ushort* cur = line; for (int i = 0; i < 0x100; ++i) { *cur++ = Palette[i]; } } bmp.UnlockBits(bd); var b = new Bitmap(bmp); b.Save(filename, ImageFormat.Tiff); b.Dispose(); bmp.Dispose(); break; } } } public void ReplacePalette(ushort[] palette) { Palette = palette; } public void Save(BinaryWriter bin, BinaryWriter idx) { if ((Frames == null) || (Frames.Count == 0)) { idx.Write(-1); idx.Write(-1); idx.Write(-1); return; } long start = bin.BaseStream.Position; idx.Write((int)start); for (int i = 0; i < 0x100; ++i) { bin.Write((ushort)(Palette[i] ^ 0x8000)); } long startpos = bin.BaseStream.Position; bin.Write(Frames.Count); long seek = bin.BaseStream.Position; long curr = bin.BaseStream.Position + 4 * Frames.Count; foreach (FrameEdit frame in Frames) { bin.BaseStream.Seek(seek, SeekOrigin.Begin); bin.Write((int)(curr - startpos)); seek = bin.BaseStream.Position; bin.BaseStream.Seek(curr, SeekOrigin.Begin); frame.Save(bin); curr = bin.BaseStream.Position; } start = bin.BaseStream.Position - start; idx.Write((int)start); idx.Write(idxextra); } public void ExportToVD(BinaryWriter bin, ref long indexpos, ref long animpos) { bin.BaseStream.Seek(indexpos, SeekOrigin.Begin); if ((Frames == null) || (Frames.Count == 0)) { bin.Write(-1); bin.Write(-1); bin.Write(-1); indexpos = bin.BaseStream.Position; return; } bin.Write((int)animpos); indexpos = bin.BaseStream.Position; bin.BaseStream.Seek(animpos, SeekOrigin.Begin); for (int i = 0; i < 0x100; ++i) { bin.Write((ushort)(Palette[i] ^ 0x8000)); } long startpos = (int)bin.BaseStream.Position; bin.Write(Frames.Count); long seek = (int)bin.BaseStream.Position; long curr = bin.BaseStream.Position + 4 * Frames.Count; foreach (FrameEdit frame in Frames) { bin.BaseStream.Seek(seek, SeekOrigin.Begin); bin.Write((int)(curr - startpos)); seek = bin.BaseStream.Position; bin.BaseStream.Seek(curr, SeekOrigin.Begin); frame.Save(bin); curr = bin.BaseStream.Position; } long length = bin.BaseStream.Position - animpos; animpos = bin.BaseStream.Position; bin.BaseStream.Seek(indexpos, SeekOrigin.Begin); bin.Write((int)length); bin.Write(idxextra); indexpos = bin.BaseStream.Position; } } public sealed class FrameEdit { private const int DoubleXor = (0x200 << 22) | (0x200 << 12); public struct Raw { public int run; public int offx; public int offy; public byte[] data; } public Raw[] RawData { get; private set; } public Point Center { get; set; } public int width; public int height; public FrameEdit(BinaryReader bin) { int xCenter = bin.ReadInt16(); int yCenter = bin.ReadInt16(); width = bin.ReadUInt16(); height = bin.ReadUInt16(); if (height == 0 || width == 0) { return; } int header; var tmp = new List(); while ((header = bin.ReadInt32()) != 0x7FFF7FFF) { var raw = new Raw(); header ^= DoubleXor; raw.run = (header & 0xFFF); raw.offy = ((header >> 12) & 0x3FF); raw.offx = ((header >> 22) & 0x3FF); int i = 0; raw.data = new byte[raw.run]; while (i < raw.run) { raw.data[i++] = bin.ReadByte(); } tmp.Add(raw); } RawData = tmp.ToArray(); Center = new Point(xCenter, yCenter); } public unsafe FrameEdit(Bitmap bit, ushort[] palette, int centerx, int centery) { Center = new Point(centerx, centery); width = bit.Width; height = bit.Height; BitmapData bd = bit.LockBits( new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, Settings.PixelFormat); var line = (ushort*)bd.Scan0; int delta = bd.Stride >> 1; var tmp = new List(); int X = 0; for (int Y = 0; Y < bit.Height; ++Y, line += delta) { ushort* cur = line; int i = 0; int j = 0; X = 0; while (i < bit.Width) { i = X; for (i = X; i <= bit.Width; ++i) { //first pixel set if (i < bit.Width) { if (cur[i] != 0) { break; } } } if (i < bit.Width) { for (j = (i + 1); j < bit.Width; ++j) { //next non set pixel if (cur[j] == 0) { break; } } var raw = new Raw(); raw.run = j - i; raw.offx = j - raw.run - centerx; raw.offx += 512; raw.offy = Y - centery - bit.Height; raw.offy += 512; int r = 0; raw.data = new byte[raw.run]; while (r < raw.run) { ushort col = (cur[r + i]); raw.data[r++] = GetPaletteIndex(palette, col); } tmp.Add(raw); X = j + 1; i = X; } } } RawData = tmp.ToArray(); bit.UnlockBits(bd); } public void ChangeCenter(int x, int y) { for (int i = 0; i < RawData.Length; i++) { RawData[i].offx += Center.X; RawData[i].offx -= x; RawData[i].offy += Center.Y; RawData[i].offy -= y; } Center = new Point(x, y); } private static byte GetPaletteIndex(ushort[] palette, ushort col) { for (int i = 0; i < palette.Length; i++) { if (palette[i] == col) { return (byte)i; } } return 0; } public void Save(BinaryWriter bin) { bin.Write((short)Center.X); bin.Write((short)Center.Y); bin.Write((ushort)width); bin.Write((ushort)height); if (RawData != null) { for (int j = 0; j < RawData.Length; j++) { int newHeader = RawData[j].run | (RawData[j].offy << 12) | (RawData[j].offx << 22); newHeader ^= DoubleXor; bin.Write(newHeader); foreach (byte b in RawData[j].data) { bin.Write(b); } } } bin.Write(0x7FFF7FFF); } } }