/// <summary> /// Initializes a new instance of the <see cref="DAT"/> class. /// </summary> /// <param name="s">The s.</param> public DAT(Stream s) { Chunks.Clear(); ByteCover.Clear(); ByteCoverName.Clear(); AllVertices.Clear(); AllNormals.Clear(); AllTriangles.Clear(); vismapid.Clear(); br = new BinaryReader(s); while (s.Position < s.Length) { long offset = s.Position; CHUNK c = new CHUNK(s); uint d1 = (c.data.Length >= 4) ? BitConverter.ToUInt32(c.data, 0) : 0; uint d2 = (c.data.Length >= 8) ? BitConverter.ToUInt32(c.data, 4) : 0; ////Console.WriteLine("chunk: {0,8:x8} : {1}:{2,2:x2} {3,8:x8} bytes / {4,8:x8} {5,8:x8}", offset, c.FourCC, c.Type, c.Length, d1, d2); //File.WriteAllBytes(string.Format("{0,4:x4}{1}.enc", Chunks.Count, c.FourCC), c.data); CHUNK.Decrypt(c.data); string filename = string.Format("{0,4:x4}-{1}-{2,2:x2}.dec", Chunks.Count, c.FourCC, c.Type); #if WRITE_CHUNKS_TO_DISK if (!File.Exists(filename)) { File.WriteAllBytes(filename, c.data); } #endif c.Number = Chunks.Count; Chunks.Add(c); } s.Dispose(); br.Close(); br.Dispose(); }
/// <summary> /// Parse2s the e. /// </summary> /// <param name="c">The c.</param> public void Parse2E(CHUNK c) { int hcount = c.data[0x20]; //if (maxcount != 0 && maxcount != 1) //Console.WriteLine("{0,4:x4}", c.Number); if (hcount == 0) { return; // whatever } //if (c.data[0x60] != 'm') //Console.WriteLine("{0,4:x4}", c.Number); // type at [0x60..0x67] is "warp " or "model " or " " int numpoints = BitConverter.ToInt32(c.data, 0x70); for (int i = 0; i < numpoints; i++) { } }
/// <summary> /// Parse20s the specified c. /// </summary> /// <param name="c">The c.</param> public void Parse20(CHUNK c) { //Debugger.Break(); var filename = string.Format("{0,4:x4}-{1}-{2,2:x2}.dec", c.Number, c.FourCC, c.Type); var filenamepng = string.Format("{0,4:x4}-{1}-{2,2:x2}.png", c.Number, c.FourCC, c.Type); #if false var psi = new ProcessStartInfo("dxt3.exe", "\"" + filename + "\""); var p = Process.Start(psi); p.WaitForExit(); if (!Directory.Exists("out")) { Directory.CreateDirectory("out"); } if (File.Exists("out.png")) { File.Move("out.png", Path.Combine("out", filenamepng)); } #endif }
/// <summary> /// Parse1s the c. /// </summary> /// <param name="c">The c.</param> public void Parse1C(CHUNK c) { // map int meshoffset = BitConverter.ToInt32(c.data, 8); // stores info about the collision mesh int gridwidth = 20 * c.data[0x0c]; // stored div 10 int gridheight = 20 * c.data[0x0d]; int bucketwidth = c.data[0x0e]; // almost always 40? int bucketheight = c.data[0x0f]; // stored mul 10; so 40 = 4 yalms; probably baked in so gridwidth*bucketwidth = mapwidth int quadtreeoffset = BitConverter.ToInt32(c.data, 0x10); int objectsoffset = 0x20; int objectsendoffset = BitConverter.ToInt32(c.data, 0x14); int objectscount = (objectsendoffset - objectsoffset) / 0x64; int shortnameoffset = BitConverter.ToInt32(c.data, 0x18); int shortnamescount = (meshoffset - shortnameoffset) / 0x4c; AddByteCover("header", 0, 0x20); // collision mesh // - use the grid for fast location-based queries, or // - use the list directly if you want to enumerate all the meshes (grid is also fine // for this, just skip over the null pointers) // - dont use the mesh data directly because it stores grid-local coordinates and also // lacks transformations int meshmodelcount = BitConverter.ToInt32(c.data, meshoffset + 0x00); int meshmodeldata = BitConverter.ToInt32(c.data, meshoffset + 0x04); // raw mesh data, don't use this, it is local object data and doesn't have transformations applied int meshgridbucketlistscount = BitConverter.ToInt32(c.data, meshoffset + 0x08); int meshgridbucketlists = BitConverter.ToInt32(c.data, meshoffset + 0x0c); // each bucket has a list of {meshdata,transformation} and the list is zero terminated int gridoffset = BitConverter.ToInt32(c.data, meshoffset + 0x10); // whole map is divided into a grid; each bucket points to its list entry ////Console.WriteLine("mesh: {0} {1} {2} {3} {4}", meshmodelcount.ToString("x8"), meshmodeldata.ToString("x8"), meshgridbucketlistscount.ToString("x8"), meshgridbucketlists.ToString("x8"), gridoffset.ToString("x8")); int mapidlistoffset = BitConverter.ToInt32(c.data, meshoffset + 0x14); int mapidlistcount = BitConverter.ToInt32(c.data, meshoffset + 0x18); AddByteCover("meshheader", meshoffset, 0x20); //Console.WriteLine("meshes:"); int j = meshmodeldata; Console.WriteLine(j); for (int i = 0; i < meshmodelcount; i++) { ////Console.Write("{0,4:x4} |", i); int retj = ParseMesh(c.data, j); AddByteCover("meshes", j, retj - j); j = retj; } // this loop populates global AllVertices AllNormals AllTriangles (sorry for statics; if you plan on using this for more than one map at a time, rewrite to be less staticy) //Console.WriteLine("grid:"); try { int offs = 0; int entryoffs = 0; for (int y = 0; y < gridheight; y++) { for (int x = 0; x < gridwidth; x++) { offs = (y * gridwidth + x) * 4; entryoffs = BitConverter.ToInt32(c.data, gridoffset + offs); if (entryoffs != 0 && entryoffs ! < 0) { ParseGridEntry(c.data, entryoffs, x, y); } AddByteCover("grid", gridoffset + offs, 4); } } } catch (Exception ex) { Char.Logger.AddDebugText(Char.Tc.rtbDebug, ex.ToString()); } #if WRITE_COLLISION_MESH_TO_DISK // write out the collision mesh in some format using (var sw = new StreamWriter(string.Format(@"Map Collision obj\{0}.obj", FileName))) { foreach (var v in AllVertices) { sw.WriteLine("v {0} {1} {2}", v.x, v.y, -v.z); } foreach (var n in AllNormals) { sw.WriteLine("vn {0} {1} {2}", n.nx, n.ny, -n.nz); } foreach (var t in AllTriangles) { sw.WriteLine("f {0}//{1} {2}//{3} {4}//{5}", 1 + t.iv0, 1 + t.in0, 1 + t.iv1, 1 + t.in0, 1 + t.iv2, 1 + t.in0 ); } } #endif //Console.WriteLine("shortnames:"); for (int i = 0; i < shortnamescount; i++) { string name = Encoding.ASCII.GetString(c.data, shortnameoffset + i * 0x4c, 0x20); ////Console.Write("{0,4:x4} | {1}", i, name); AddByteCover("shortnames", shortnameoffset + i * 0x4c, 0x4c); } //Console.WriteLine(); //Console.WriteLine("objects:"); for (int i = 0; i < objectscount; i++) { //Console.Write("{0,4:x4} |", i); ParseObject(c.data, objectsoffset + i * 0x64); AddByteCover("objects", objectsoffset + i * 0x64, 0x64); } //Console.WriteLine("vislist:"); for (int i = 0; i < mapidlistcount; i++) { //Console.Write("{0,4:x4} |", i); int mapid = ParseMapIDStruct(c.data, mapidlistoffset + 0xc0 * i); vismapid.Add(i, mapid); AddByteCover("vislist", mapidlistoffset + 0xc0 * i, 0xc0); } // quadtree does multiple things: // - given a position, traverse down to some bucket // - each bucket tells you what things are visible, which improves speed of game by limiting objects/polygons drawn // - also tells you the 2D sub map id for a given 3D point (to pull up the proper 2D image when you open up /map) // quadtree volumes have some transformations that I haven't bothered to apply // quadtree stores entire vertical column (i.e. it is not really an octree) //Console.WriteLine("quadtree:"); BBT root = ParseQuadTree(c.data, quadtreeoffset, 1); #if false // query position to mapid(s) BBT found = root.find(-30, 0, 380); foreach (int node in found.matchids) { Console.WriteLine("{0}", node); } foreach (int mapid in found.matches) { Console.WriteLine("{0}", mapid); } #endif AddByteCover("EOF", c.data.Length, 1); PrintByteCover(); }
/// <summary> /// Parse36s the specified c. /// </summary> /// <param name="c">The c.</param> public void Parse36(CHUNK c) { // if .zoneid same as current zone, region is probably a "zone line" // - if srcordst is 0, the volume zones out // - if srcordst is 1, the volume is the random region where you zone in (but your // rotation will come from zone out) var l = new Subregion(); int count = BitConverter.ToInt32(c.data, 0x30); for (int i = 0; i < count; i++) { var o = new Subregion(); o.x = BitConverter.ToSingle(c.data, 0x40 + i * 0x40 + 0x00); o.y = BitConverter.ToSingle(c.data, 0x40 + i * 0x40 + 0x04); o.z = BitConverter.ToSingle(c.data, 0x40 + i * 0x40 + 0x08); o.rx = BitConverter.ToSingle(c.data, 0x40 + i * 0x40 + 0x0c); o.ry = BitConverter.ToSingle(c.data, 0x40 + i * 0x40 + 0x10); o.rz = BitConverter.ToSingle(c.data, 0x40 + i * 0x40 + 0x14); o.vx = BitConverter.ToSingle(c.data, 0x40 + i * 0x40 + 0x18); o.vy = BitConverter.ToSingle(c.data, 0x40 + i * 0x40 + 0x1c); o.vz = BitConverter.ToSingle(c.data, 0x40 + i * 0x40 + 0x20); byte[] namebytes = new byte[8]; Array.Copy(c.data, 0x40 + i * 0x40 + 0x24, namebytes, 0, 8); int len = 0; while (len < namebytes.Length && namebytes[len] != 0) { len++; } len = 8; o.name = Encoding.ASCII.GetString(namebytes, 0, len); o.zoneid = BitConverter.ToInt32(c.data, 0x40 + i * 0x40 + 0x2c); o.srcordst = BitConverter.ToInt32(c.data, 0x40 + i * 0x40 + 0x30); #if true /*Console.WriteLine("{0,8} {1} {2} : {3,8},{4,8},{5,8} : {6,8},{7,8},{8,8} : {9,8},{10,8},{11,8}", * o.name, o.srcordst, o.zoneid.ToString("x4"), * o.x.ToString("N2"), o.y.ToString("N2"), o.z.ToString("N2"), * o.rx.ToString("N2"), o.ry.ToString("N2"), o.rz.ToString("N2"), * o.vx.ToString("N2"), o.vy.ToString("N2"), o.vz.ToString("N2") * );*/ #endif if (true || o.srcordst == 0) { float vx = o.vx / 2; float vy = o.vy / 2; float vz = o.vz / 2; float ry = -o.ry; //Console.WriteLine("{0} {1} {2} {3} {4} {5} 0.5", o.x + rotx(-vx, vz, ry), o.z + roty(-vx, vz, ry), o.y, o.x + rotx(vx, vz, ry), o.z + roty(vx, vz, ry), o.y); //Console.WriteLine("{0} {1} {2} {3} {4} {5} 0.5", o.x + rotx(vx, vz, ry), o.z + roty(vx, vz, ry), o.y, o.x + rotx(vx, -vz, ry), o.z + roty(vx, -vz, ry), o.y); //Console.WriteLine("{0} {1} {2} {3} {4} {5} 0.5", o.x + rotx(vx, -vz, ry), o.z + roty(vx, -vz, ry), o.y, o.x + rotx(-vx, -vz, ry), o.z + roty(-vx, -vz, ry), o.y); //Console.WriteLine("{0} {1} {2} {3} {4} {5} 0.5", o.x + rotx(-vx, -vz, ry), o.z + roty(-vx, -vz, ry), o.y, o.x + rotx(-vx, vz, ry), o.z + roty(-vx, vz, ry), o.y); ////Console.WriteLine("{0} {1} {2} {3} {4} {5} 0.5", o.x + rotx(-vx, -vz, ry), o.z + roty(-vx, -vz, ry), o.y, o.x + rotx(vx, vz, ry), o.z + roty(vx, vz, ry), o.y); ////Console.WriteLine("{0} {1} {2} {3} {4} {5} 0.5", o.x + rotx(vx, -vz, ry), o.z + roty(vx, -vz, ry), o.y, o.x + rotx(-vx, -vz, ry), o.z + roty(-vx, -vz, ry), o.y); } l = o; } }