using System.Text; using SabreTools.Data.Extensions; using SabreTools.Data.Models.BSP; using SabreTools.Text.Extensions; namespace SabreTools.Wrappers { public partial class BSP : 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, BspHeader? header) { builder.AppendLine(" Header Information:"); builder.AppendLine(" -------------------------"); if (header is null) { builder.AppendLine(" No header"); builder.AppendLine(); return; } builder.AppendLine(header.Version, " Version"); builder.AppendLine(); } private static void PrintLumps(StringBuilder builder, BspFile? model) { builder.AppendLine(" Lumps Information:"); builder.AppendLine(" -------------------------"); if (model?.Header?.Lumps is null || 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 = ((BspLumpType)i).FromBspLumpType(); builder.AppendLine($" Lump {i}{specialLumpName}"); builder.AppendLine(lump.Offset, " Offset"); builder.AppendLine(lump.Length, " Length"); switch ((BspLumpType)i) { case BspLumpType.LUMP_ENTITIES: Print(builder, model.Entities); break; case BspLumpType.LUMP_PLANES: Print(builder, model.PlanesLump); break; case BspLumpType.LUMP_TEXTURES: Print(builder, model.TextureLump); break; case BspLumpType.LUMP_VERTICES: Print(builder, model.VerticesLump); break; case BspLumpType.LUMP_VISIBILITY: Print(builder, model.VisibilityLump); break; case BspLumpType.LUMP_NODES: Print(builder, model.NodesLump); break; case BspLumpType.LUMP_TEXINFO: Print(builder, model.TexinfoLump); break; case BspLumpType.LUMP_FACES: Print(builder, model.FacesLump); break; case BspLumpType.LUMP_LIGHTING: Print(builder, model.LightmapLump); break; case BspLumpType.LUMP_CLIPNODES: Print(builder, model.ClipnodesLump); break; case BspLumpType.LUMP_LEAVES: Print(builder, model.LeavesLump); break; case BspLumpType.LUMP_MARKSURFACES: Print(builder, model.MarksurfacesLump); break; case BspLumpType.LUMP_EDGES: Print(builder, model.EdgesLump); break; case BspLumpType.LUMP_SURFEDGES: Print(builder, model.SurfedgesLump); break; case BspLumpType.LUMP_MODELS: Print(builder, model.ModelsLump); break; default: builder.AppendLine($" Unsupported lump type: {(BspLumpType)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++) { var entity = lump.Entities[i]; builder.AppendLine($" Entity {i}:"); if (entity.Attributes.Count == 0) { builder.AppendLine(" No attributes"); continue; } for (int j = 0; j < entity.Attributes.Count; j++) { var kvp = entity.Attributes[j]; builder.AppendLine($" Attribute {j}: {kvp.Key}={kvp.Value}"); } } } 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, TextureLump lump) { builder.AppendLine(" Texture Header:"); builder.AppendLine(lump.Header.MipTextureCount, " MipTexture count"); builder.AppendLine(lump.Header.Offsets, " Offsets"); builder.AppendLine(" Textures:"); if (lump.Textures.Length == 0) { builder.AppendLine(" No texture data"); return; } for (int i = 0; i < lump.Textures.Length; i++) { var texture = lump.Textures[i]; builder.AppendLine($" Texture {i}"); builder.AppendLine(texture.Name, " Name"); builder.AppendLine(texture.Width, " Width"); builder.AppendLine(texture.Height, " Height"); builder.AppendLine(texture.Offsets, " Offsets"); } } 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, BspNodesLump 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"); } } private static void Print(StringBuilder builder, BspTexinfoLump 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($" S-Vector: ({texinfo.SVector?.X}, {texinfo.SVector?.Y}, {texinfo.SVector?.Z})"); builder.AppendLine(texinfo.TextureSShift, " Texture shift in S direction"); builder.AppendLine($" T-Vector: ({texinfo.TVector?.X}, {texinfo.TVector?.Y}, {texinfo.TVector?.Z})"); builder.AppendLine(texinfo.TextureTShift, " Texture shift in T direction"); builder.AppendLine(texinfo.MiptexIndex, " Miptex index"); builder.AppendLine($" Flags: {texinfo.Flags} (0x{texinfo.Flags:X})"); } } private static void Print(StringBuilder builder, BspFacesLump 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.PlaneIndex, " Plane index"); builder.AppendLine(face.PlaneSideCount, " Plane side count"); builder.AppendLine(face.FirstEdgeIndex, " First surfedge index"); builder.AppendLine(face.NumberOfEdges, " Surfedge count"); builder.AppendLine(face.TextureInfoIndex, " Texture info index"); builder.AppendLine(face.LightingStyles, " Lighting styles"); builder.AppendLine(face.LightmapOffset, " Lightmap offset"); } } 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, ClipnodesLump lump) { if (lump.Clipnodes.Length == 0) { builder.AppendLine(" No data"); return; } for (int i = 0; i < lump.Clipnodes.Length; i++) { var clipnode = lump.Clipnodes[i]; builder.AppendLine($" Clipnode {i}"); builder.AppendLine(clipnode.PlaneIndex, " Plane index"); builder.AppendLine(clipnode.ChildrenIndices, " Children indices"); } } private static void Print(StringBuilder builder, BspLeavesLump lump) { 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.VisOffset, " Visibility offset"); builder.AppendLine(leaf.Mins, " Mins"); builder.AppendLine(leaf.Maxs, " Maxs"); builder.AppendLine(leaf.FirstMarkSurfaceIndex, " First marksurface index"); builder.AppendLine(leaf.MarkSurfacesCount, " Marksurfaces count"); builder.AppendLine(leaf.AmbientLevels, " Ambient sound levels"); } } 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, BspModelsLump lump) { if (lump.Models.Length == 0) { builder.AppendLine(" No data"); return; } for (int i = 0; i < lump.Models.Length; i++) { var bmodel = lump.Models[i]; builder.AppendLine($" Model {i}"); builder.AppendLine($" Mins: {bmodel.Mins?.X}, {bmodel.Mins?.Y}, {bmodel.Mins?.Z}"); builder.AppendLine($" Maxs: {bmodel.Maxs?.X}, {bmodel.Maxs?.Y}, {bmodel.Maxs?.Z}"); builder.AppendLine($" Origin vector: {bmodel.OriginVector?.X}, {bmodel.OriginVector?.Y}, {bmodel.OriginVector?.Z}"); builder.AppendLine(bmodel.HeadnodesIndex, " Headnodes index"); builder.AppendLine(bmodel.VisLeafsCount, " ??? (VisLeafsCount)"); builder.AppendLine(bmodel.FirstFaceIndex, " First face index"); builder.AppendLine(bmodel.FacesCount, " Faces count"); } } } }