bsp

package module
v1.0.0-b1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 1, 2024 License: Unlicense Imports: 8 Imported by: 22

README

GoDoc Go report card GolangCI CircleCI codecov

Bsp

The most comprehensive library for reading and writing Source Engine .bsp map files.

Features:
  • Read support for (most) non-xbox360 BSPs (v20,21). v19 support limited, may work
  • Freely modify and resize any Lump data
  • Limited write support, mostly untested
Not all lumps are currently supported, but can be freely read and modified, as they are treated as []byte

The following lumps currently have a full implementation for v20 & v21 BSPs (tested against CS:S & CS:GO):

0: Entdata
1: Planes
2: Texdata
3: Vertexes
4: Visibility
5: Nodes
6: Texinfo
7: Faces
8: Lighting
9: Occlusion
10: Leafs
11: FaceId
12: Edges
13: Surfedges
14: Models
15: WorldLight
16: Leaffaces
17: LeafBrushes
18: Brushes
19: Brushsides
20: Areas
21: AreaPortals
26: DispInfo
27: OriginalFaces
28: PhysDisp
30: VertNormals
31: VertNormalIndices
33: DispVerts
34: DispLightmapSamplePosition
35: Game lump (partial: sprp only)
36: LeafWaterData
37: Primitives
38: PrimVerts
39: PrimIndices
40: Pakfile
41: ClipPortalVerts
42: Cubemaps
43: Texdatastringdata
44: Texdatastringtable
45: Overlays
46: LeafMinDistToWater
47: FaceMacroTextureInfo
48: DispTris
51: LeafAmbientIndexHDR
52: LeafAmbientIndex
53: WorldLightHDR
54: WorldLightHDR
55: LeafAmbientLightingHDR
56: LeafAmbientLighting
58: FacesHDR
59: MapFlags
60: OverlayFades

Lumps not listed here are parsed and available as []byte format.

Note: Some lumps in some BSP versions have data with unidentified purpose. These fields are available as byte arrays. Please submit an issue or a PR if you can help fill in any of these fields.

Usage

Minimal example of obtaining entdata from a BSP. The following will print the entdata lump (entdata is a single json-like string) of a specified .bsp to console.

package main

import (
  "github.com/galaco/bsp"
  "github.com/galaco/bsp/lump"
  "log"
  "os"
)

func main() {
  f, err := os.Open("de_dust2.bsp")
  if err != nil {
    log.Fatal(err)
  }
  defer f.Close()

  // Create a new bsp reader
  reader := bsp.NewReader()

  // Read buffer
  file, err := reader.Read(f)
  if err != nil {
    log.Fatal(err)
  }

  entdata := (file.Lumps[bsp.LumpEntities]).(*lump.Entdata)
  log.Println(entdata.Contents())
}

There are more usage examples available in the examples/ directory.

Exporting BSPs

This library supports writing BSPs. It aims to preserve identical binaries where possible, but this is not guaranteed due to wide-ranging difference in format across games (and even within the same game!). For example:

  • Counterstrike: Source
    • de_dust2 Lump 59 (MapFlags) has 0 flags set, a the 4byte lump is written. Format is BSP v20.
    • de_nuke Lump 59 (MapFlags) has 0 flags set, but the lump is not written. Format is BSP v20.

There are plenty of other scenarios where this can occur, and in a way that we cannot guess with certainty what the expected behaviour should be. By default, this library assumes that structures that contain > 0 bytes are written, but this behaviour can be overridden (see examples).

Overriding lumps.

Lumps can be overridden by using the NewReaderWithOptions function, and passing a custom LumpResolver. A LumpResolver is responsible for return a new instance of whatever lump implementation you wish to use for a particular Lump index and BSP version. See examples for more details.

Lump 35: Game Lump

The game lump has special rules, because of the unusual use of absolute file offsets. If you wish to override the game lump with your own implementation, then you must implement the lump.GameGeneric interface on your custom lump definition.

Real World examples

Contributing

All contributions welcome, in particular any maps that are found to be incompatible.

Documentation

Index

Constants

View Source
const (
	ContentsEmpty = 0 // No contents

	ContentsSolid       = 0x1 // an eye is never valid in a solid
	ContentsWindow      = 0x2 // translucent, but not watery (glass)
	ContentsAux         = 0x4
	ContentsGrate       = 0x8 // alpha-tested "grate" textures.  Bullets/sight pass through, but solids don't
	ContentsSlime       = 0x10
	ContentsWater       = 0x20
	ContentsBlockLOS    = 0x40 // block AI line of sight
	ContentsOpaque      = 0x80 // things that cannot be seen through (may be non-solid though)
	LastVisibleContents = 0x80

	AllVisibleContents = LastVisibleContents | (LastVisibleContents - 1)

	ContentsTestFogVolume = 0x100
	ContentsUnused        = 0x200

	// ContentsUnused6 is unused.
	// NOTE: If it's visible, grab from the top + update LastVisibleContents
	// if not visible, then grab from the bottom.
	ContentsUnused6 = 0x400

	ContentsTeam1 = 0x800  // per team contents used to differentiate collisions
	ContentsTeam2 = 0x1000 // between players and objects on different teams

	// ContentsIgnoreNodrawOpaque ignores ContentsOpaque on surfaces that have SurfNodraw.
	ContentsIgnoreNodrawOpaque = 0x2000

	// ContentsMoveable hits entities which are MOVETYPE_PUSH (doors, plats, etc.)
	ContentsMoveable = 0x4000

	ContentsAreaPortal = 0x8000

	ContentsPlayerClip  = 0x10000
	ContentsMonsterClip = 0x20000

	ContentsCurrent0    = 0x40000
	ContentsCurrent90   = 0x80000
	ContentsCurrent180  = 0x100000
	ContentsCurrent270  = 0x200000
	ContentsCurrentUp   = 0x400000
	ContentsCurrentDown = 0x800000

	ContentsOrigin = 0x1000000 // removed before bsping an entity

	ContentsMonster     = 0x2000000 // should never be on a brush, only in game
	ContentsDebris      = 0x4000000
	ContentsDetail      = 0x8000000  // brushes to be added after vis leafs
	ContentsTranslucent = 0x10000000 // auto set if any surface has trans
	ContentsLadder      = 0x20000000
	ContentsHitbox      = 0x40000000 // use accurate hitboxes on trace

	SurfLight    = 0x0001 // value will hold the light strength
	SurfSky2D    = 0x0002 // don't draw, indicates we should skylight + draw 2d sky but not draw the 3D skybox
	SurfSky      = 0x0004 // don't draw, but add to skybox
	SurfWarp     = 0x0008 // turbulent water warp
	SurfTrans    = 0x0010
	SurfNoPortal = 0x0020 // the surface can not have a portal placed on it
	SurfTrigger  = 0x0040 // Valve FIXME: This is an xbox hack to work around elimination of trigger surfaces, which breaks occluders
	SurfNodraw   = 0x0080 // don't bother referencing the texture

	SurfHint = 0x0100 // make a primary bsp splitter

	SurfSkip      = 0x0200 // completely ignore, allowing non-closed brushes
	SurfNoLight   = 0x0400 // Don't calculate light
	SurfBumpLight = 0x0800 // calculate three lightmaps for the surface for bumpmapping
	SurfNoShadows = 0x1000 // Don't receive shadows
	SurfNoDecals  = 0x2000 // Don't receive decals
	SurfNoChop    = 0x4000 // Don't subdivide patches on this surface
	SurfHitbox    = 0x8000 // surface is part of a hitbox

	// MaskAll is everything.
	MaskAll = 0xFFFFFFFF

	// MaskSolid is everything that is normally solid.
	MaskSolid = ContentsSolid | ContentsMoveable | ContentsWindow | ContentsMonster | ContentsGrate

	// MaskPlayerSolid is everything that blocks player movement.
	MaskPlayerSolid = ContentsSolid | ContentsMoveable | ContentsPlayerClip | ContentsWindow | ContentsMonster | ContentsGrate

	// MaskNPCSolid blocks npc movement.
	MaskNPCSolid = ContentsSolid | ContentsMoveable | ContentsMonsterClip | ContentsWindow | ContentsMonster | ContentsGrate

	// MaskWater is water physics in these contents.
	MaskWater = ContentsWater | ContentsMoveable | ContentsSlime

	// MaskOpaque is everything that blocks lighting.
	MaskOpaque = ContentsSolid | ContentsMoveable | ContentsOpaque

	// MaskOpaqueAndNPCs is everything that blocks lighting, but with monsters added.
	MaskOpaqueAndNPCs = MaskOpaque | ContentsMonster

	// MaskBlockLOS is everything that blocks line of sight for AI.
	MaskBlockLOS = ContentsSolid | ContentsMoveable | ContentsBlockLOS

	// MaskBlockLOSAndNPCs is everything that blocks line of sight for AI plus NPCs.
	MaskBlockLOSAndNPCs = MaskBlockLOS | ContentsMonster

	// MaskVisible is everything that blocks line of sight for players.
	MaskVisible = MaskOpaque | ContentsIgnoreNodrawOpaque

	// MaskVisibleAndNPCs is everything that blocks line of sight for players, but with monsters added.
	MaskVisibleAndNPCs = MaskOpaqueAndNPCs | ContentsIgnoreNodrawOpaque

	// MaskShot is bullets see these as solid.
	MaskShot = ContentsSolid | ContentsMoveable | ContentsMonster | ContentsWindow | ContentsDebris | ContentsHitbox

	// MaskShotHull is non-raycasted weapons see this as solid (includes grates).
	MaskShotHull = ContentsSolid | ContentsMoveable | ContentsMonster | ContentsWindow | ContentsDetail | ContentsGrate

	// MaskShotPortal hits solids (not grates) and passes through everything else.
	MaskShotPortal = ContentsSolid | ContentsMoveable | ContentsWindow | ContentsMonster

	// MaskSolidBrushOnly is everything normally solid, except monsters (world+brush only).
	MaskSolidBrushOnly = ContentsSolid | ContentsMoveable | ContentsWindow | ContentsGrate

	// MaskPlayerSolidBrushOnly is everything normally solid for player movement, except monsters (world+brush only).
	MaskPlayerSolidBrushOnly = ContentsSolid | ContentsMoveable | ContentsWindow | ContentsPlayerClip | ContentsGrate

	// MaskNPCSolidBrushOnly is everything normally solid for npc movement, except monsters (world+brush only).
	MaskNPCSolidBrushOnly = ContentsSolid | ContentsMoveable | ContentsWindow | ContentsMonsterClip | ContentsGrate

	// MaskNPCWorldStatic is just the world, used for route rebuilding.
	MaskNPCWorldStatic = ContentsSolid | ContentsWindow | ContentsMonsterClip | ContentsGrate

	// MaskSplitAreaPortal are things that can split areaportals.
	MaskSplitAreaPortal = ContentsWater | ContentsSlime

	// MaskCurrent is moving water.
	// UNDONE: This is untested.
	MaskCurrent = ContentsCurrent0 | ContentsCurrent90 | ContentsCurrent180 | ContentsCurrent270 | ContentsCurrentUp | ContentsCurrentDown

	// MaskDeadSolid is everything that blocks corpse movement.
	// UNDONE: Not used yet / may be deleted.
	MaskDeadSolid = ContentsSolid | ContentsPlayerClip | ContentsWindow | ContentsGrate
)
View Source
const (
	// LumpEntities is Entity keyvalue data stored as string
	LumpEntities = LumpId(0)
	// LumpPlanes is bsp planes
	LumpPlanes = LumpId(1)
	// LumpTexData is texture data used by bsp faces
	LumpTexData = LumpId(2)
	// LumpVertexes is vertex data
	LumpVertexes = LumpId(3)
	// LumpVisibility is vvis calculated visibility pvs & pas information
	LumpVisibility = LumpId(4)
	// LumpNodes is bsp node tree entries
	LumpNodes = LumpId(5)
	// LumpTexInfo is face texture information
	LumpTexInfo = LumpId(6)
	// LumpFaces is  bsp faces
	LumpFaces = LumpId(7)
	// LumpLighting
	LumpLighting = LumpId(8)
	// LumpOcclusion
	LumpOcclusion = LumpId(9)
	// LumpLeafs
	LumpLeafs = LumpId(10)
	// LumpFaceIds is contents is normally stripped out by compiler
	LumpFaceIds = LumpId(11)
	// LumpEdges is face edges. v1->v2, vertex order may be reversed
	LumpEdges = LumpId(12)
	// LumpSurfEdges
	LumpSurfEdges = LumpId(13)
	// LumpModels is models are root bsp nodes. m[0] = worldspawn. m[0+n] are brush entity data
	LumpModels = LumpId(14)
	// LumpWorldLights
	LumpWorldLights = LumpId(15)
	// LumpLeafFaces is faces that separate leaves.
	LumpLeafFaces = LumpId(16)
	// LumpLeafBrushes is brushes that define a leaf volume
	LumpLeafBrushes = LumpId(17)
	// LumpBrushes
	LumpBrushes = LumpId(18)
	// LumpBrushSides
	LumpBrushSides = LumpId(19)
	// LumpAreas
	LumpAreas = LumpId(20)
	// LumpAreaPortals
	LumpAreaPortals = LumpId(21)
	// LumpPortals
	LumpPortals = LumpId(22)
	// LumpUnused0
	LumpUnused0 = LumpId(22)
	// LumpPropCollision
	LumpPropCollision = LumpId(22)
	// LumpClusters
	LumpClusters = LumpId(23)
	// LumpUnused1
	LumpUnused1 = LumpId(23)
	// LumpPropHulls
	LumpPropHulls = LumpId(23)
	// LumpPortalVerts
	LumpPortalVerts = LumpId(24)
	// LumpUnused2
	LumpUnused2 = LumpId(24)
	// LumpPropHullVerts
	LumpPropHullVerts = LumpId(24)
	// LumpClusterPortals
	LumpClusterPortals = LumpId(25)
	// LumpUnused3
	LumpUnused3 = LumpId(25)
	// LumpPropTris
	LumpPropTris = LumpId(25)
	// LumpDispInfo
	LumpDispInfo = LumpId(26)
	// LumpOriginalFaces
	LumpOriginalFaces = LumpId(27)
	// LumpPhysDisp
	LumpPhysDisp = LumpId(28)
	// LumpPhysCollide
	LumpPhysCollide = LumpId(29)
	// LumpVertNormals
	LumpVertNormals = LumpId(30)
	// LumpVertNormalIndices
	LumpVertNormalIndices = LumpId(31)
	// LumpDispLightmapAlphas is contents is normally stripped out
	LumpDispLightmapAlphas = LumpId(32)
	// LumpDispVerts
	LumpDispVerts = LumpId(33)
	// LumpDispLightmapSamplePositions
	LumpDispLightmapSamplePositions = LumpId(34)
	// LumpGame is game specific data. includes staticprop data
	LumpGame = LumpId(35)
	// LumpLeafWaterData
	LumpLeafWaterData = LumpId(36)
	// LumpPrimitives
	LumpPrimitives = LumpId(37)
	// LumpPrimVerts
	LumpPrimVerts = LumpId(38)
	// LumpPrimIndices
	LumpPrimIndices = LumpId(39)
	// LumpPakfile is uncompressed zip of packed custom content
	LumpPakfile = LumpId(40)
	// LumpClipPortalVerts
	LumpClipPortalVerts = LumpId(41)
	// LumpCubemaps
	LumpCubemaps = LumpId(42)
	// LumpTexDataStringData is raw string data of material paths
	LumpTexDataStringData = LumpId(43)
	// LumpTexDataStringTable references entries in the string data lump
	LumpTexDataStringTable = LumpId(44)
	// LumpOverlays
	LumpOverlays = LumpId(45)
	// LumpLeafMinDistToWater
	LumpLeafMinDistToWater = LumpId(46)
	// LumpFaceMacroTextureInfo
	LumpFaceMacroTextureInfo = LumpId(47)
	// LumpDispTris
	LumpDispTris = LumpId(48)
	// LumpPhysCollideSurface is deprecated
	LumpPhysCollideSurface = LumpId(49)
	// LumpPropBlob
	LumpPropBlob = LumpId(49)
	// LumpWaterOverlays
	LumpWaterOverlays = LumpId(50)
	// LumpLightmapPages
	LumpLightmapPages = LumpId(51)
	// LumpLeafAmbientIndexHDR
	LumpLeafAmbientIndexHDR = LumpId(51)
	// LumpLightmapPagesInfo
	LumpLightmapPagesInfo = LumpId(52)
	// LumpLeafAmbientIndex
	LumpLeafAmbientIndex = LumpId(52)
	// LumpLightingHDR
	LumpLightingHDR = LumpId(53)
	// LumpWorldLightsHDR
	LumpWorldLightsHDR = LumpId(54)
	// LumpLeafAmbientLightingHDR
	LumpLeafAmbientLightingHDR = LumpId(55)
	// LumpLeafAmbientLighting
	LumpLeafAmbientLighting = LumpId(56)
	// LumpXZipPakfile is xbox specific.
	// Deprecated: Valve says it's deprecated.
	LumpXZipPakfile = LumpId(57)
	// LumpFacesHDR
	LumpFacesHDR = LumpId(58)
	// LumpMapFlags
	LumpMapFlags = LumpId(59)
	// LumpOverlayFades
	LumpOverlayFades = LumpId(60)
	// LumpOverlaySystemLevels
	LumpOverlaySystemLevels = LumpId(61)
	// LumpPhysLevel
	LumpPhysLevel = LumpId(62)
	// LumpDispMultiBlend
	LumpDispMultiBlend = LumpId(63)
)

Lump Identifiers

Variables

This section is empty.

Functions

This section is empty.

Types

type Bsp

type Bsp struct {
	Header Header   `json:"header"`
	Lumps  [64]Lump `json:"lumps"`
}

Bsp is the root .bsp filetype container. Consists of a 1036byte header and 64 lump blocks.

func (*Bsp) CRC32 added in v0.3.0

func (bsp *Bsp) CRC32() (uint32, error)
type Header struct {
	Id       int32          `json:"id"`
	Version  int32          `json:"version"`
	Lumps    [64]HeaderLump `json:"lumps"`
	Revision int32          `json:"revision"`
}

Header is the Bsp header. Contains format and lump layout data. Do not trust lump information between import and export

type HeaderLump

type HeaderLump struct {
	// Offset is the offset into the file (in bytes).
	Offset int32 `json:"offset"`
	// Length is the lump length (in bytes).
	Length int32 `json:"length"`
	// Version is the lump version.
	Version int32 `json:"version"`
	// ID is lump ident code.
	// If the lump is compressed then treat as an integer that represents the decompressed size.
	ID [4]byte `json:"id"`
}

HeaderLump contains layout information for a given lump, stored in the Header.

type Lump

type Lump interface {
	// FromBytes imports a []byte to a defined lump structure(s).
	FromBytes([]byte) error
	// ToBytes exports lump structure back to []byte.
	ToBytes() ([]byte, error)
	// SetVersion sets bsp version of lump.
	SetVersion(version int32)
}

Lump interface.

func LumpResolverByBSPVersion

func LumpResolverByBSPVersion(id LumpId, header Header) (l Lump, err error)

LumpResolverByBSPVersion returns an empty bsp lump for the specified bsp version and lump id If a version is not 19,20,21 then a generic lump that holds raw bytes only ([]byte) is returned.

type LumpId added in v0.2.0

type LumpId int

LumpId is the lump reference used by Source

type LumpResolver

type LumpResolver func(id LumpId, header Header) (Lump, error)

LumpResolver Return an instance of a Lump for a given offset.

type Reader

type Reader struct {
	// contains filtered or unexported fields
}

Reader is a Bsp File reader.

func NewReader

func NewReader() *Reader

NewReader creates a new Bsp reader with default config.

func NewReaderWithConfig

func NewReaderWithConfig(config ReaderConfig) *Reader

NewReaderWithConfig creates a new Bsp reader.

func (*Reader) Read

func (r *Reader) Read(stream io.Reader) (*Bsp, error)

Read reads from an io.Reader into a structured Bsp.

type ReaderConfig

type ReaderConfig struct {
	// LumpResolver is used to produce a new instance of whatever lump is presented by a given BSP version.
	// If you have unsupported lumps, then overwrite this parameter with a custom implementation.
	LumpResolver LumpResolver
}

ReaderConfig offers configurable parameters for reading BSP.

func DefaultReaderConfig

func DefaultReaderConfig() ReaderConfig

DefaultReaderConfig returns the default config for a reader.

type Writer

type Writer struct{}

Writer is a Bsp export writer.

func NewWriter

func NewWriter() *Writer

NewWriter Returns a new bsp writer instance.

func (*Writer) Write

func (w *Writer) Write(bsp *Bsp) ([]byte, error)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL