using System.Text; using SabreTools.Data.Extensions; using SabreTools.Data.Models.BSP; using SabreTools.Text.Extensions; namespace SabreTools.Wrappers { public partial class VBSP : IPrintable { #if NETCOREAPP /// public string ExportJSON(bool recursive) => System.Text.Json.JsonSerializer.Serialize(Model, _jsonSerializerOptions); #else /// public string ExportJSON(bool recursive) => Newtonsoft.Json.JsonConvert.SerializeObject(Model, _jsonSerializerOptions); #endif /// public void PrintInformation(StringBuilder builder, bool recursive) { builder.AppendLine("BSP Information:"); builder.AppendLine("-------------------------"); builder.AppendLine(); Print(builder, Model.Header); PrintLumps(builder, Model); } private static void Print(StringBuilder builder, VbspHeader header) { builder.AppendLine(" Header Information:"); builder.AppendLine(" -------------------------"); builder.AppendLine(header.Signature, " Signature"); builder.AppendLine(header.Version, " Version"); builder.AppendLine(header.MapRevision, " Map revision"); builder.AppendLine(); } private static void PrintLumps(StringBuilder builder, VbspFile model) { builder.AppendLine(" Lumps Information:"); builder.AppendLine(" -------------------------"); if (model.Header.Lumps.Length == 0) { builder.AppendLine(" No lumps"); builder.AppendLine(); return; } for (int i = 0; i < model.Header.Lumps.Length; i++) { var lump = model.Header.Lumps[i]; string specialLumpName = ((VbspLumpType)i).FromVbspLumpType(); builder.AppendLine($" Lump {i}{specialLumpName}"); builder.AppendLine(lump.Offset, " Offset"); builder.AppendLine(lump.Length, " Length"); switch ((VbspLumpType)i) { case VbspLumpType.LUMP_ENTITIES: Print(builder, model.Entities); break; case VbspLumpType.LUMP_PLANES: Print(builder, model.PlanesLump); break; case VbspLumpType.LUMP_TEXTURES: Print(builder, model.TexdataLump); break; case VbspLumpType.LUMP_VERTICES: Print(builder, model.VerticesLump); break; case VbspLumpType.LUMP_VISIBILITY: Print(builder, model.VisibilityLump); break; case VbspLumpType.LUMP_NODES: Print(builder, model.NodesLump); break; case VbspLumpType.LUMP_TEXINFO: Print(builder, model.TexinfoLump); break; case VbspLumpType.LUMP_FACES: Print(builder, model.FacesLump); break; case VbspLumpType.LUMP_LIGHTING: Print(builder, model.LightmapLump); break; case VbspLumpType.LUMP_CLIPNODES: Print(builder, model.OcclusionLump); break; case VbspLumpType.LUMP_LEAVES: Print(builder, model.LeavesLump, lump.Version); break; case VbspLumpType.LUMP_MARKSURFACES: Print(builder, model.MarksurfacesLump); break; case VbspLumpType.LUMP_EDGES: Print(builder, model.EdgesLump); break; case VbspLumpType.LUMP_SURFEDGES: Print(builder, model.SurfedgesLump); break; case VbspLumpType.LUMP_MODELS: Print(builder, model.ModelsLump); break; case VbspLumpType.LUMP_WORLDLIGHTS: Print(builder, model.LDRWorldLightsLump); break; case VbspLumpType.LUMP_LEAFFACES: Print(builder, model.LeafFacesLump); break; case VbspLumpType.LUMP_LEAFBRUSHES: Print(builder, model.LeafBrushesLump); break; case VbspLumpType.LUMP_BRUSHES: Print(builder, model.BrushesLump); break; case VbspLumpType.LUMP_BRUSHSIDES: Print(builder, model.BrushsidesLump); break; case VbspLumpType.LUMP_AREAS: // TODO: Support LUMP_AREAS [20] when in Models builder.AppendLine(" Data not parsed..."); break; case VbspLumpType.LUMP_AREAPORTALS: // TODO: Support LUMP_AREAPORTALS [21] when in Models builder.AppendLine(" Data not parsed..."); break; case VbspLumpType.LUMP_PORTALS: // TODO: Support LUMP_PORTALS / LUMP_UNUSED0 / LUMP_PROPCOLLISION [22] when in Models builder.AppendLine(" Data not parsed..."); break; case VbspLumpType.LUMP_CLUSTERS: // TODO: Support LUMP_CLUSTERS / LUMP_UNUSED1 / LUMP_PROPHULLS [23] when in Models builder.AppendLine(" Data not parsed..."); break; case VbspLumpType.LUMP_PORTALVERTS: // TODO: Support LUMP_PORTALVERTS / LUMP_UNUSED2 / LUMP_FAKEENTITIES / LUMP_PROPHULLVERTS [24] when in Models builder.AppendLine(" Data not parsed..."); break; case VbspLumpType.LUMP_CLUSTERPORTALS: // TODO: Support LUMP_CLUSTERPORTALS / LUMP_UNUSED3 / LUMP_PROPTRIS [25] when in Models builder.AppendLine(" Data not parsed..."); break; case VbspLumpType.LUMP_DISPINFO: Print(builder, model.DispInfosLump); break; case VbspLumpType.LUMP_ORIGINALFACES: Print(builder, model.OriginalFacesLump); break; case VbspLumpType.LUMP_PHYSDISP: // TODO: Support LUMP_PHYSDISP [28] when in Models builder.AppendLine(" Data not parsed..."); break; case VbspLumpType.LUMP_PHYSCOLLIDE: Print(builder, model.PhysCollideLump); break; case VbspLumpType.LUMP_VERTNORMALS: // TODO: Support LUMP_VERTNORMALS [30] when in Models builder.AppendLine(" Data not parsed..."); break; case VbspLumpType.LUMP_VERTNORMALINDICES: // TODO: Support LUMP_VERTNORMALINDICES [31] when in Models builder.AppendLine(" Data not parsed..."); break; case VbspLumpType.LUMP_DISP_LIGHTMAP_ALPHAS: // TODO: Support LUMP_DISP_LIGHTMAP_ALPHAS [32] when in Models builder.AppendLine(" Data not parsed..."); break; case VbspLumpType.LUMP_DISP_VERTS: Print(builder, model.DispVertsLump); break; case VbspLumpType.LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS: // TODO: Support LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS [34] when in Models builder.AppendLine(" Data not parsed..."); break; case VbspLumpType.LUMP_GAME_LUMP: Print(builder, model.GameLump); break; case VbspLumpType.LUMP_LEAFWATERDATA: // TODO: Support LUMP_LEAFWATERDATA [36] when in Models builder.AppendLine(" Data not parsed..."); break; case VbspLumpType.LUMP_PRIMITIVES: // TODO: Support LUMP_PRIMITIVES [37] when in Models builder.AppendLine(" Data not parsed..."); break; case VbspLumpType.LUMP_PRIMVERTS: // TODO: Support LUMP_PRIMVERTS [38] when in Models builder.AppendLine(" Data not parsed..."); break; case VbspLumpType.LUMP_PRIMINDICES: // TODO: Support LUMP_PRIMINDICES [39] when in Models builder.AppendLine(" Data not parsed..."); break; case VbspLumpType.LUMP_PAKFILE: Print(builder, model.PakfileLump); break; case VbspLumpType.LUMP_CLIPPORTALVERTS: // TODO: Support LUMP_CLIPPORTALVERTS [41] when in Models builder.AppendLine(" Data not parsed..."); break; case VbspLumpType.LUMP_CUBEMAPS: Print(builder, model.CubemapsLump); break; case VbspLumpType.LUMP_TEXDATA_STRING_DATA: Print(builder, model.TexdataStringData); break; case VbspLumpType.LUMP_TEXDATA_STRING_TABLE: Print(builder, model.TexdataStringTable); break; case VbspLumpType.LUMP_OVERLAYS: Print(builder, model.OverlaysLump); break; case VbspLumpType.LUMP_LEAFMINDISTTOWATER: // TODO: Support LUMP_LEAFMINDISTTOWATER [46] when in Models builder.AppendLine(" Data not parsed..."); break; case VbspLumpType.LUMP_FACE_MACRO_TEXTURE_INFO: // TODO: Support LUMP_FACE_MACRO_TEXTURE_INFO [47] when in Models builder.AppendLine(" Data not parsed..."); break; case VbspLumpType.LUMP_DISP_TRIS: Print(builder, model.DispTrisLump); break; case VbspLumpType.LUMP_PHYSCOLLIDESURFACE: // TODO: Support LUMP_PHYSCOLLIDESURFACE / LUMP_PROP_BLOB [49] when in Models builder.AppendLine(" Data not parsed..."); break; case VbspLumpType.LUMP_WATEROVERLAYS: // TODO: Support LUMP_WATEROVERLAYS [50] when in Models builder.AppendLine(" Data not parsed..."); break; case VbspLumpType.LUMP_LIGHTMAPPAGES: Print(builder, model.HDRAmbientIndexLump); break; case VbspLumpType.LUMP_LIGHTMAPPAGEINFOS: Print(builder, model.LDRAmbientIndexLump); break; case VbspLumpType.LUMP_LIGHTING_HDR: // TODO: Support LUMP_LIGHTING_HDR [53] when in Models builder.AppendLine(" Data not parsed..."); break; case VbspLumpType.LUMP_WORLDLIGHTS_HDR: Print(builder, model.HDRWorldLightsLump); break; case VbspLumpType.LUMP_LEAF_AMBIENT_LIGHTING_HDR: Print(builder, model.HDRAmbientLightingLump); break; case VbspLumpType.LUMP_LEAF_AMBIENT_LIGHTING: Print(builder, model.LDRAmbientLightingLump); break; case VbspLumpType.LUMP_XZIPPAKFILE: // TODO: Support LUMP_XZIPPAKFILE [57] when in Models builder.AppendLine(" Data not parsed..."); break; case VbspLumpType.LUMP_FACES_HDR: // TODO: Support LUMP_FACES_HDR [58] when in Models builder.AppendLine(" Data not parsed..."); break; case VbspLumpType.LUMP_MAP_FLAGS: // TODO: Support LUMP_MAP_FLAGS [59] when in Models builder.AppendLine(" Data not parsed..."); break; case VbspLumpType.LUMP_OVERLAY_FADES: // TODO: Support LUMP_OVERLAY_FADES [60] when in Models builder.AppendLine(" Data not parsed..."); break; case VbspLumpType.LUMP_OVERLAY_SYSTEM_LEVELS: // TODO: Support LUMP_OVERLAY_SYSTEM_LEVELS [61] when in Models builder.AppendLine(" Data not parsed..."); break; case VbspLumpType.LUMP_PHYSLEVEL: // TODO: Support LUMP_PHYSLEVEL [62] when in Models builder.AppendLine(" Data not parsed..."); break; case VbspLumpType.LUMP_DISP_MULTIBLEND: // TODO: Support LUMP_DISP_MULTIBLEND [63] when in Models builder.AppendLine(" Data not parsed..."); break; default: builder.AppendLine($" Unsupported lump type: {(VbspLumpType)i} (0x{i:X4})"); break; } } builder.AppendLine(); } private static void Print(StringBuilder builder, EntitiesLump lump) { if (lump.Entities.Length == 0) { builder.AppendLine(" No data"); return; } for (int i = 0; i < lump.Entities.Length; i++) { // TODO: Implement entity printing //var entity = lump.Entities[i]; builder.AppendLine($" Entity {i}: Not printed yet"); } } private static void Print(StringBuilder builder, PlanesLump lump) { if (lump.Planes.Length == 0) { builder.AppendLine(" No data"); return; } for (int i = 0; i < lump.Planes.Length; i++) { var plane = lump.Planes[i]; builder.AppendLine($" Plane {i}"); builder.AppendLine($" Normal vector: ({plane.NormalVector.X}, {plane.NormalVector.Y}, {plane.NormalVector.Z})"); builder.AppendLine(plane.Distance, " Distance"); builder.AppendLine($" Plane type: {plane.PlaneType} (0x{plane.PlaneType:X})"); } } private static void Print(StringBuilder builder, TexdataLump lump) { if (lump.Texdatas.Length == 0) { builder.AppendLine(" No data"); return; } for (int i = 0; i < lump.Texdatas.Length; i++) { var texdata = lump.Texdatas[i]; builder.AppendLine($" Texture {i}"); builder.AppendLine($" Reflectivity: ({texdata.Reflectivity.X}, {texdata.Reflectivity.Y}, {texdata.Reflectivity.Z})"); builder.AppendLine(texdata.NameStringTableID, " Name string table ID"); builder.AppendLine(texdata.Width, " Width"); builder.AppendLine(texdata.Height, " Height"); builder.AppendLine(texdata.ViewWidth, " View width"); builder.AppendLine(texdata.ViewHeight, " View height"); } } private static void Print(StringBuilder builder, VerticesLump lump) { if (lump.Vertices.Length == 0) { builder.AppendLine(" No data"); return; } for (int i = 0; i < lump.Vertices.Length; i++) { var vertex = lump.Vertices[i]; builder.AppendLine($" Vertex {i}: ({vertex.X}, {vertex.Y}, {vertex.Z})"); } } private static void Print(StringBuilder builder, VisibilityLump lump) { builder.AppendLine(lump.NumClusters, " Cluster count"); builder.AppendLine(" Byte offsets skipped..."); } private static void Print(StringBuilder builder, VbspNodesLump lump) { if (lump.Nodes.Length == 0) { builder.AppendLine(" No data"); return; } for (int i = 0; i < lump.Nodes.Length; i++) { var node = lump.Nodes[i]; builder.AppendLine($" Node {i}"); builder.AppendLine(node.Children, " Children"); builder.AppendLine(node.Mins, " Mins"); builder.AppendLine(node.Maxs, " Maxs"); builder.AppendLine(node.FirstFace, " First face index"); builder.AppendLine(node.FaceCount, " Count of faces"); builder.AppendLine(node.Area, " Area"); builder.AppendLine(node.Padding, " Padding"); } } private static void Print(StringBuilder builder, VbspTexinfoLump lump) { if (lump.Texinfos.Length == 0) { builder.AppendLine(" No data"); return; } for (int i = 0; i < lump.Texinfos.Length; i++) { var texinfo = lump.Texinfos[i]; builder.AppendLine($" Texinfo {i}"); builder.AppendLine($" Texture S-Vector: ({texinfo.TextureSVector.X}, {texinfo.TextureSVector.Y}, {texinfo.TextureSVector.Z})"); builder.AppendLine(texinfo.TextureSShift, " Texture shift in S direction"); builder.AppendLine($" Texture T-Vector: ({texinfo.TextureTVector.X}, {texinfo.TextureTVector.Y}, {texinfo.TextureTVector.Z})"); builder.AppendLine(texinfo.TextureTShift, " Texture shift in T direction"); builder.AppendLine($" Lightmap S-Vector: ({texinfo.LightmapSVector.X}, {texinfo.LightmapSVector.Y}, {texinfo.LightmapSVector.Z})"); builder.AppendLine(texinfo.TextureSShift, " Lightmap shift in S direction"); builder.AppendLine($" Lightmap T-Vector: ({texinfo.LightmapTVector.X}, {texinfo.LightmapTVector.Y}, {texinfo.LightmapTVector.Z})"); builder.AppendLine(texinfo.TextureTShift, " Lightmap shift in T direction"); builder.AppendLine($" Flags: {texinfo.Flags} (0x{texinfo.Flags:X})"); builder.AppendLine(texinfo.TexData, " Pointer to texdata"); } } private static void Print(StringBuilder builder, VbspFacesLump lump) { if (lump.Faces.Length == 0) { builder.AppendLine(" No data"); return; } for (int i = 0; i < lump.Faces.Length; i++) { var face = lump.Faces[i]; builder.AppendLine($" Face {i}"); builder.AppendLine(face.PlaneNum, " Plane number"); builder.AppendLine(face.Side, " Side"); builder.AppendLine(face.OnNode, " On node"); builder.AppendLine(face.FirstEdgeIndex, " First surfedge index"); builder.AppendLine(face.NumberOfEdges, " Surfedge count"); builder.AppendLine(face.TextureInfoIndex, " Texture info index"); builder.AppendLine(face.DisplacementInfoIndex, " Displacement info index"); builder.AppendLine(face.SurfaceFogVolumeID, " Surface fog volume ID"); builder.AppendLine(face.Styles, " Styles"); builder.AppendLine(face.LightmapOffset, " Lightmap offset"); builder.AppendLine(face.Area, " Area"); builder.AppendLine(face.LightmapTextureMinsInLuxels, " Lightmap texture mins in Luxels"); builder.AppendLine(face.LightmapTextureSizeInLuxels, " Lightmap texture size in Luxels"); builder.AppendLine(face.OrigFace, " Original face index"); builder.AppendLine(face.PrimitiveCount, " Primitive count"); builder.AppendLine(face.FirstPrimitiveID, " First primitive ID"); builder.AppendLine(face.SmoothingGroups, " Smoothing groups"); } } private static void Print(StringBuilder builder, PhysCollideLump lump) { if (lump.Models.Length == 0) { builder.AppendLine(" No data"); return; } for (int i = 0; i < lump.Models.Length; i++) { var model = lump.Models[i]; builder.AppendLine($" Model {i}"); builder.AppendLine(model.ModelIndex, " Model index"); builder.AppendLine(model.DataSize, " Data size"); builder.AppendLine(model.KeydataSize, " Keydata size"); builder.AppendLine(model.SolidCount, " Solid count"); if (model.Solids.Length == 0) { builder.AppendLine(" No solids"); } else { for (int j = 0; j < model.Solids.Length; j++) { var solid = model.Solids[j]; builder.AppendLine($" Solid {j}"); builder.AppendLine(solid.Size, " Size"); builder.AppendLine(" Collision data skipped..."); } } builder.AppendLine(" Keydata skipped..."); } } private static void Print(StringBuilder builder, LightmapLump lump) { if (lump.Lightmap.Length == 0) builder.AppendLine(" No data"); else builder.AppendLine(" Lightmap data skipped..."); } private static void Print(StringBuilder builder, OcclusionLump lump) { builder.AppendLine(lump.Count, " Count"); if (lump.Data.Length == 0) { builder.AppendLine(" No occluder data"); } else { for (int i = 0; i < lump.Data.Length; i++) { var data = lump.Data[i]; builder.AppendLine($" Occluder Data {i}"); builder.AppendLine(data.Flags, " Flags"); builder.AppendLine(data.FirstPoly, " First poly"); builder.AppendLine(data.PolyCount, " Poly count"); builder.AppendLine($" Mins: {data.Mins.X}, {data.Mins.Y}, {data.Mins.Z}"); builder.AppendLine($" Maxs: {data.Maxs.X}, {data.Maxs.Y}, {data.Maxs.Z}"); builder.AppendLine(data.Area, " Area"); } } builder.AppendLine(lump.PolyDataCount, " Polydata count"); if (lump.PolyData.Length == 0) { builder.AppendLine(" No occluder polydata"); } else { for (int i = 0; i < lump.PolyData.Length; i++) { var polydata = lump.PolyData[i]; builder.AppendLine($" Occluder Polydata {i}"); builder.AppendLine(polydata.FirstVertexIndex, " First vertex index"); builder.AppendLine(polydata.VertexCount, " Vertex count"); builder.AppendLine(polydata.PlanEnum, " Plan enum"); } } builder.AppendLine(lump.VertexIndexCount, " Vertex index count"); if (lump.VertexIndicies.Length == 0) { builder.AppendLine(" No vertex indicies"); } else { for (int i = 0; i < lump.VertexIndicies.Length; i++) { builder.AppendLine($" Vertex Index {i}: {lump.VertexIndicies[i]}"); } } } private static void Print(StringBuilder builder, VbspLeavesLump lump, uint version) { if (lump.Leaves.Length == 0) { builder.AppendLine(" No data"); return; } for (int i = 0; i < lump.Leaves.Length; i++) { var leaf = lump.Leaves[i]; builder.AppendLine($" Leaf {i}"); builder.AppendLine($" Contents: {leaf.Contents} (0x{leaf.Contents:X})"); builder.AppendLine(leaf.Cluster, " Cluster"); builder.AppendLine(leaf.AreaFlags, " AreaFlags"); builder.AppendLine(leaf.Mins, " Mins"); builder.AppendLine(leaf.Maxs, " Maxs"); builder.AppendLine(leaf.FirstLeafFace, " First leaf face"); builder.AppendLine(leaf.NumLeafFaces, " Leaf faces count"); builder.AppendLine(leaf.FirstLeafBrush, " First leaf brush"); builder.AppendLine(leaf.NumLeafBrushes, " Leaf brushes count"); builder.AppendLine(leaf.LeafWaterDataID, " Leaf water data ID"); if (version == 0) { if (leaf.AmbientLighting.Colors.Length == 0) { builder.AppendLine(" No ambient lighting colors"); } else { for (int j = 0; j < leaf.AmbientLighting.Colors.Length; j++) { var color = leaf.AmbientLighting.Colors[j]; builder.AppendLine($" Ambient Lighting Color {j}"); builder.AppendLine(color.Red, " Red"); builder.AppendLine(color.Green, " Green"); builder.AppendLine(color.Blue, " Blue"); builder.AppendLine(color.Exponent, " Exponent"); } } } else { builder.AppendLine(leaf.Padding, " Padding"); } } } private static void Print(StringBuilder builder, MarksurfacesLump lump) { if (lump.Marksurfaces.Length == 0) { builder.AppendLine(" No data"); return; } for (int i = 0; i < lump.Marksurfaces.Length; i++) { var marksurface = lump.Marksurfaces[i]; builder.AppendLine(marksurface, $" Marksurface {i}"); } } private static void Print(StringBuilder builder, EdgesLump lump) { if (lump.Edges.Length == 0) { builder.AppendLine(" No data"); return; } for (int i = 0; i < lump.Edges.Length; i++) { var edge = lump.Edges[i]; builder.AppendLine($" Edge {i}"); builder.AppendLine(edge.VertexIndices, " Vertex indices"); } } private static void Print(StringBuilder builder, SurfedgesLump lump) { if (lump.Surfedges.Length == 0) { builder.AppendLine(" No data"); return; } for (int i = 0; i < lump.Surfedges.Length; i++) { var surfedge = lump.Surfedges[i]; builder.AppendLine(surfedge, $" Surfedge {i}"); } } private static void Print(StringBuilder builder, VbspModelsLump lump) { if (lump.Models.Length == 0) { builder.AppendLine(" No data"); return; } for (int i = 0; i < lump.Models.Length; i++) { var model = lump.Models[i]; builder.AppendLine($" Model {i}"); builder.AppendLine($" Mins: ({model.Mins.X}, {model.Mins.Y}, {model.Mins.Z})"); builder.AppendLine($" Maxs: ({model.Maxs.X}, {model.Maxs.Y}, {model.Maxs.Z})"); builder.AppendLine($" Origin vector: ({model.OriginVector.X}, {model.OriginVector.Y}, {model.OriginVector.Z})"); builder.AppendLine(model.HeadNode, " Headnode index"); builder.AppendLine(model.FirstFaceIndex, " First face index"); builder.AppendLine(model.FacesCount, " Faces count"); } } private static void Print(StringBuilder builder, WorldLightsLump lump) { if (lump.WorldLights.Length == 0) { builder.AppendLine(" No data"); return; } for (int j = 0; j < lump.WorldLights.Length; j++) { var worldlight = lump.WorldLights[j]; builder.AppendLine($" World Light {j}"); builder.AppendLine($" Origin: ({worldlight.Origin.X}, {worldlight.Origin.Y}, {worldlight.Origin.Z})"); builder.AppendLine($" Intensity: ({worldlight.Intensity.X}, {worldlight.Intensity.Y}, {worldlight.Intensity.Z})"); builder.AppendLine($" Normal: ({worldlight.Normal.X}, {worldlight.Normal.Y}, {worldlight.Normal.Z})"); builder.AppendLine(worldlight.Cluster, " Cluster"); builder.AppendLine($" Emit type: {worldlight.EmitType} (0x{worldlight.EmitType:X})"); builder.AppendLine(worldlight.Style, " Style"); builder.AppendLine(worldlight.StopDot, " Start of penumbra"); builder.AppendLine(worldlight.StopDot2, " End of penumbra"); builder.AppendLine(worldlight.Exponent, " Exponent"); builder.AppendLine(worldlight.Radius, " Radius"); builder.AppendLine(worldlight.ConstantAttn, " Constant attn."); builder.AppendLine(worldlight.LinearAttn, " Linear attn."); builder.AppendLine(worldlight.QuadraticAttn, " Quadratic attn."); builder.AppendLine(worldlight.Flags, " Flags"); builder.AppendLine(worldlight.Texinfo, " Texinfo"); builder.AppendLine(worldlight.Owner, " Owner"); } } private static void Print(StringBuilder builder, LeafFacesLump lump) { if (lump.Map.Length == 0) { builder.AppendLine(" No data"); return; } for (int i = 0; i < lump.Map.Length; i++) { var entry = lump.Map[i]; builder.AppendLine($" Map entry {i}: {entry}"); } } private static void Print(StringBuilder builder, LeafBrushesLump lump) { if (lump.Map.Length == 0) { builder.AppendLine(" No data"); return; } for (int i = 0; i < lump.Map.Length; i++) { var entry = lump.Map[i]; builder.AppendLine($" Map entry {i}: {entry}"); } } private static void Print(StringBuilder builder, BrushesLump lump) { if (lump.Brushes.Length == 0) { builder.AppendLine(" No data"); return; } for (int i = 0; i < lump.Brushes.Length; i++) { var brush = lump.Brushes[i]; builder.AppendLine($" Brush {i}"); builder.AppendLine(brush.FirstSide, " First brushside"); builder.AppendLine(brush.NumSides, " Number of brushsides"); builder.AppendLine($" Contents: {brush.Contents} (0x{brush.Contents:X})"); } } private static void Print(StringBuilder builder, BrushsidesLump lump) { if (lump.Brushsides.Length == 0) { builder.AppendLine(" No data"); return; } for (int i = 0; i < lump.Brushsides.Length; i++) { var brushside = lump.Brushsides[i]; builder.AppendLine($" Brushside {i}"); builder.AppendLine(brushside.PlaneNum, " Plane number"); builder.AppendLine(brushside.TextureInfo, " Texture info"); builder.AppendLine(brushside.DisplacementInfo, " Displacement info"); builder.AppendLine(brushside.Bevel, " Bevel"); } } private static void Print(StringBuilder builder, DispInfosLump lump) { if (lump.Infos.Length == 0) { builder.AppendLine(" No data"); return; } for (int i = 0; i < lump.Infos.Length; i++) { var info = lump.Infos[i]; builder.AppendLine($" Disp Info {i}"); builder.AppendLine($" Start position: ({info.StartPosition.X}, {info.StartPosition.Y}, {info.StartPosition.Z})"); builder.AppendLine(info.DispVertStart, " Index into disp verts"); builder.AppendLine(info.DispTriStart, " Index into disp tris"); builder.AppendLine(info.Power, " Power"); builder.AppendLine(info.MinTess, " Minimum tesselation"); builder.AppendLine(info.SmoothingAngle, " Smoothing angle"); builder.AppendLine(info.Contents, " Contents"); builder.AppendLine(info.MapFace, " Map face"); builder.AppendLine(info.LightmapAlphaStart, " Lightmap alpha start"); builder.AppendLine(info.LightmapSamplePositionStart, " Lightmap sample position start"); builder.AppendLine($" Edge Neighbors:"); if (info.EdgeNeighbors.Length == 0) { builder.AppendLine(" No edge neighbors"); } else { for (int j = 0; j < info.EdgeNeighbors.Length; j++) { var edgeNeighbor = info.EdgeNeighbors[j]; builder.AppendLine($" Edge Neighbor {j}"); if (edgeNeighbor.SubNeighbors.Length == 0) { builder.AppendLine(" No subneighbors"); } else { for (int k = 0; k < edgeNeighbor.SubNeighbors.Length; k++) { var subNeighbor = edgeNeighbor.SubNeighbors[k]; builder.AppendLine($" Subneighbor {k}"); builder.AppendLine(subNeighbor.NeighborIndex, " Neighbor index"); builder.AppendLine(subNeighbor.NeighborOrientation, " Neighbor orientation"); builder.AppendLine(subNeighbor.Span, " Span"); builder.AppendLine(subNeighbor.NeighborSpan, " Neighbor span"); } } } } builder.AppendLine($" Corner Neighbors:"); if (info.CornerNeighbors.Length == 0) { builder.AppendLine(" No corner neighbors"); } else { for (int j = 0; j < info.CornerNeighbors.Length; j++) { var cornerNeighbor = info.CornerNeighbors[j]; builder.AppendLine($" Corner Neighbor {j}"); builder.AppendLine(cornerNeighbor.Neighbors, " Neighbors"); builder.AppendLine(cornerNeighbor.NeighborCount, " Neighbor count"); } } builder.AppendLine(info.AllowedVerts, " Allowed verts"); } } private static void Print(StringBuilder builder, DispVertsLump lump) { if (lump.Verts.Length == 0) { builder.AppendLine(" No data"); return; } for (int i = 0; i < lump.Verts.Length; i++) { var vert = lump.Verts[i]; builder.AppendLine($" Disp Vert {i}"); builder.AppendLine($" Vec: ({vert.Vec.X}, {vert.Vec.Y}, {vert.Vec.Z})"); builder.AppendLine(vert.Dist, " Dist"); builder.AppendLine(vert.Alpha, " Alpha"); } } private static void Print(StringBuilder builder, GameLump lump) { builder.AppendLine(lump.LumpCount, " Lump count"); if (lump.Directories.Length == 0) { builder.AppendLine(" No directories"); return; } for (int i = 0; i < lump.Directories.Length; i++) { var dir = lump.Directories[i]; builder.AppendLine($" Game Lump Directory {i}"); builder.AppendLine(dir.Id, " Id"); builder.AppendLine(dir.Flags, " Flags"); builder.AppendLine(dir.Version, " Version"); builder.AppendLine(dir.FileOffset, " File offset"); builder.AppendLine(dir.FileLength, " File length"); } } private static void Print(StringBuilder builder, PakfileLump lump) { if (lump.Data.Length == 0) { builder.AppendLine(" No data"); return; } builder.AppendLine(" Data skipped..."); } private static void Print(StringBuilder builder, CubemapsLump lump) { if (lump.Cubemaps.Length == 0) { builder.AppendLine(" No data"); return; } for (int i = 0; i < lump.Cubemaps.Length; i++) { var cubemap = lump.Cubemaps[i]; builder.AppendLine($" Cubemap {i}"); builder.AppendLine(cubemap.Origin, " Origin"); builder.AppendLine(cubemap.Size, " Size"); } } private static void Print(StringBuilder builder, TexdataStringData lump) { if (lump.Strings.Length == 0) { builder.AppendLine(" No data"); return; } for (int i = 0; i < lump.Strings.Length; i++) { var str = lump.Strings[i]; builder.AppendLine($" String {i}: {str}"); } } private static void Print(StringBuilder builder, TexdataStringTable lump) { if (lump.Offsets.Length == 0) { builder.AppendLine(" No data"); return; } for (int i = 0; i < lump.Offsets.Length; i++) { var offset = lump.Offsets[i]; builder.AppendLine($" Offset {i}: {offset}"); } } private static void Print(StringBuilder builder, OverlaysLump lump) { if (lump.Overlays.Length == 0) { builder.AppendLine(" No data"); return; } for (int i = 0; i < lump.Overlays.Length; i++) { var overlay = lump.Overlays[i]; builder.AppendLine($" Overlay {i}"); builder.AppendLine(overlay.Id, " Id"); builder.AppendLine(overlay.TexInfo, " Texinfo"); builder.AppendLine(overlay.FaceCountAndRenderOrder, " Face count and render order"); builder.AppendLine(overlay.Ofaces, " Ofaces"); builder.AppendLine(overlay.U, " U"); builder.AppendLine(overlay.V, " V"); if (overlay.UVPoints.Length == 0) { builder.AppendLine(" No UV points"); } else { for (int j = 0; j < overlay.UVPoints.Length; j++) { var point = overlay.UVPoints[j]; builder.AppendLine($" UV Point {j}: ({point.X}, {point.Y}, {point.Z})"); } } builder.AppendLine($" Origin: ({overlay.Origin.X}, {overlay.Origin.Y}, {overlay.Origin.Z})"); builder.AppendLine($" Basis normal: ({overlay.BasisNormal.X}, {overlay.BasisNormal.Y}, {overlay.BasisNormal.Z})"); } } private static void Print(StringBuilder builder, DispTrisLump lump) { if (lump.Tris.Length == 0) { builder.AppendLine(" No data"); return; } for (int i = 0; i < lump.Tris.Length; i++) { var tri = lump.Tris[i]; builder.AppendLine($" Disp Tri {i}"); builder.AppendLine($" Tags: {tri.Tags} (0x{tri.Tags:X})"); } } private static void Print(StringBuilder builder, AmbientIndexLump lump) { if (lump.Indicies.Length == 0) { builder.AppendLine(" No data"); return; } for (int i = 0; i < lump.Indicies.Length; i++) { var index = lump.Indicies[i]; builder.AppendLine($" Index {i}"); builder.AppendLine(index.AmbientSampleCount, " Ambient sample count"); builder.AppendLine(index.FirstAmbientSample, " First ambient sample"); } } private static void Print(StringBuilder builder, AmbientLightingLump lump) { if (lump.Lightings.Length == 0) { builder.AppendLine(" No data"); return; } for (int i = 0; i < lump.Lightings.Length; i++) { var lighting = lump.Lightings[i]; builder.AppendLine($" Lighting {i}"); builder.AppendLine(" Colors array skipped..."); builder.AppendLine(lighting.X, " X"); builder.AppendLine(lighting.Y, " Y"); builder.AppendLine(lighting.Z, " Z"); builder.AppendLine(lighting.Pad, " Pad"); } } } }