diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 3eebfe0..0000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,8 +0,0 @@ -version: 2 -jobs: - build: - docker: - - image: circleci/golang:1.17 - steps: - - checkout - - run: go test -v ./... diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..cd59860 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,22 @@ +name: Test and coverage +on: + push: + branches: + - '*' + pull_request: + branches: [main] +jobs: + test: + name: Run tests + runs-on: 'ubuntu-latest' + steps: + - name: Checkout code + uses: actions/checkout@v2 + with: + fetch-depth: 2 + - name: Install Go 1.19.0 + uses: actions/setup-go@v2 + with: + go-version: '1.19.0' + - name: Run coverage + run: go test ./... -race -coverpkg=./libs/... -coverprofile=coverage.out -covermode=atomic diff --git a/README.md b/README.md index 19a1ade..67b35d1 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,11 @@ [![GoDoc](https://godoc.org/github.com/Galaco/bsp?status.svg)](https://godoc.org/github.com/Galaco/bsp) [![Go report card](https://goreportcard.com/badge/github.com/galaco/bsp)](https://goreportcard.com/badge/github.com/galaco/bsp) [![GolangCI](https://golangci.com/badges/github.com/galaco/bsp.svg)](https://golangci.com) -[![Build Status](https://travis-ci.com/Galaco/bsp.svg?branch=master)](https://travis-ci.com/Galaco/bsp) [![CircleCI](https://circleci.com/gh/Galaco/bsp/tree/master.svg?style=svg)](https://circleci.com/gh/Galaco/bsp/tree/master) [![codecov](https://codecov.io/gh/Galaco/bsp/branch/master/graph/badge.svg)](https://codecov.io/gh/Galaco/bsp) # Bsp -Go library for handling Source Engine .bsp map files. +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 @@ -72,7 +71,11 @@ The following lumps currently have a full implementation for v20 & v21 BSPs (tes 60: OverlayFades ``` -##### This library may reorganise lump order during the first export. This is intentional to handle lump resizing, but will change your checksum if you export without changes. +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 @@ -83,30 +86,60 @@ 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/lumps" - "log" - "os" + "github.com/galaco/bsp" + "github.com/galaco/bsp/lump" + "log" + "os" ) func main() { - f,_ := os.Open("de_dust2.bsp") - - // Create a new bsp reader - reader := bsp.NewReader(f) - - // Read buffer - file,err := reader.Read() - if err != nil { - log.Fatal(err) - } - f.Close() - - lump := file.Lump(bsp.LumpEntities).(*lumps.Entities) - log.Println(lump.GetData()) + 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 * Proof of concept BSP viewer: [https://github.com/Galaco/kero](https://github.com/Galaco/kero) * Insert game_text newline placeholder characters (avoids Hammer crash) as a compile step: [https://github.com/Galaco/CS-GO-game_text-newline-inserter/tree/golang](https://github.com/Galaco/CS-GO-game_text-newline-inserter/tree/golang) @@ -114,4 +147,4 @@ func main() { # Contributing -All contributions welcome. Known unsupported games/maps are especially useful. +All contributions welcome, in particular any maps that are found to be incompatible. diff --git a/ar_baggage.bsp.gz b/ar_baggage.bsp.gz deleted file mode 100644 index a9bf9dd..0000000 Binary files a/ar_baggage.bsp.gz and /dev/null differ diff --git a/bsp.go b/bsp.go index 693c115..847b305 100644 --- a/bsp.go +++ b/bsp.go @@ -1,47 +1,40 @@ package bsp -import "github.com/galaco/bsp/lumps" - // Bsp is the root .bsp filetype container. // Consists of a 1036byte header and 64 lump blocks. type Bsp struct { - header Header - lumps [64]Lump + Header Header `json:"header"` + Lumps [64]Lump `json:"lumps"` } // Header is the Bsp header. Contains format and lump layout data. // Do not trust lump information between import and export type Header struct { - Id int32 - Version int32 - Lumps [64]HeaderLump - Revision int32 + Id int32 `json:"id"` + Version int32 `json:"version"` + Lumps [64]HeaderLump `json:"lumps"` + Revision int32 `json:"revision"` } // HeaderLump contains layout information for a given lump, stored in the Header. type HeaderLump struct { - Offset int32 - Length int32 - Version int32 - Id [4]byte -} - -// Header gets the header for a bsp. -func (bsp *Bsp) Header() *Header { - return &bsp.header -} - -// Lump gets the lump for a given id. -func (bsp *Bsp) Lump(index LumpId) lumps.ILump { - return bsp.RawLump(index).Contents() -} - -// RawLump gets the lump for a given id. -func (bsp *Bsp) RawLump(index LumpId) *Lump { - return &bsp.lumps[int(index)] + // 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"` } -// SetLump sets the lump data for a given id. -func (bsp *Bsp) SetLump(index LumpId, lump Lump) { - bsp.lumps[int(index)] = lump +// Lump interface. +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) } diff --git a/bspFlags.go b/bspFlags.go index d4c6018..b155eb1 100644 --- a/bspFlags.go +++ b/bspFlags.go @@ -1,143 +1,147 @@ package bsp const ( - CONTENTS_EMPTY = 0 // No contents + ContentsEmpty = 0 // No contents - CONTENTS_SOLID = 0x1 // an eye is never valid in a solid - CONTENTS_WINDOW = 0x2 // translucent, but not watery (glass) - CONTENTS_AUX = 0x4 - CONTENTS_GRATE = 0x8 // alpha-tested "grate" textures. Bullets/sight pass through, but solids don't - CONTENTS_SLIME = 0x10 - CONTENTS_WATER = 0x20 - CONTENTS_BLOCKLOS = 0x40 // block AI line of sight - CONTENTS_OPAQUE = 0x80 // things that cannot be seen through (may be non-solid though) - LAST_VISIBLE_CONTENTS = 0x80 + 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 - ALL_VISIBLE_CONTENTS = LAST_VISIBLE_CONTENTS | (LAST_VISIBLE_CONTENTS - 1) + AllVisibleContents = LastVisibleContents | (LastVisibleContents - 1) - CONTENTS_TESTFOGVOLUME = 0x100 - CONTENTS_UNUSED = 0x200 + ContentsTestFogVolume = 0x100 + ContentsUnused = 0x200 - // unused - // NOTE: If it's visible, grab from the top + update LAST_VISIBLE_CONTENTS + // ContentsUnused6 is unused. + // NOTE: If it's visible, grab from the top + update LastVisibleContents // if not visible, then grab from the bottom. - CONTENTS_UNUSED6 = 0x400 + ContentsUnused6 = 0x400 - CONTENTS_TEAM1 = 0x800 // per team contents used to differentiate collisions - CONTENTS_TEAM2 = 0x1000 // between players and objects on different teams + ContentsTeam1 = 0x800 // per team contents used to differentiate collisions + ContentsTeam2 = 0x1000 // between players and objects on different teams - // ignore CONTENTS_OPAQUE on surfaces that have SURF_NODRAW - CONTENTS_IGNORE_NODRAW_OPAQUE = 0x2000 + // ContentsIgnoreNodrawOpaque ignores ContentsOpaque on surfaces that have SurfNodraw. + ContentsIgnoreNodrawOpaque = 0x2000 - // hits entities which are MOVETYPE_PUSH (doors, plats, etc.) - CONTENTS_MOVEABLE = 0x4000 + // ContentsMoveable hits entities which are MOVETYPE_PUSH (doors, plats, etc.) + ContentsMoveable = 0x4000 - // remaining contents are non-visible, and don't eat brushes - CONTENTS_AREAPORTAL = 0x8000 + // Remaining contents are non-visible, and don't eat brushes. - CONTENTS_PLAYERCLIP = 0x10000 - CONTENTS_MONSTERCLIP = 0x20000 + ContentsAreaPortal = 0x8000 - // currents can be added to any other contents, and may be mixed - CONTENTS_CURRENT_0 = 0x40000 - CONTENTS_CURRENT_90 = 0x80000 - CONTENTS_CURRENT_180 = 0x100000 - CONTENTS_CURRENT_270 = 0x200000 - CONTENTS_CURRENT_UP = 0x400000 - CONTENTS_CURRENT_DOWN = 0x800000 + ContentsPlayerClip = 0x10000 + ContentsMonsterClip = 0x20000 - CONTENTS_ORIGIN = 0x1000000 // removed before bsping an entity + // Currents can be added to any other contents, and may be mixed. - CONTENTS_MONSTER = 0x2000000 // should never be on a brush, only in game - CONTENTS_DEBRIS = 0x4000000 - CONTENTS_DETAIL = 0x8000000 // brushes to be added after vis leafs - CONTENTS_TRANSLUCENT = 0x10000000 // auto set if any surface has trans - CONTENTS_LADDER = 0x20000000 - CONTENTS_HITBOX = 0x40000000 // use accurate hitboxes on trace + ContentsCurrent0 = 0x40000 + ContentsCurrent90 = 0x80000 + ContentsCurrent180 = 0x100000 + ContentsCurrent270 = 0x200000 + ContentsCurrentUp = 0x400000 + ContentsCurrentDown = 0x800000 - // NOTE: These are stored in a short in the engine now. Don't use more than 16 bits - SURF_LIGHT = 0x0001 // value will hold the light strength - SURF_SKY2D = 0x0002 // don't draw, indicates we should skylight + draw 2d sky but not draw the 3D skybox - SURF_SKY = 0x0004 // don't draw, but add to skybox - SURF_WARP = 0x0008 // turbulent water warp - SURF_TRANS = 0x0010 - SURF_NOPORTAL = 0x0020 // the surface can not have a portal placed on it - SURF_TRIGGER = 0x0040 // FIXME: This is an xbox hack to work around elimination of trigger surfaces, which breaks occluders - SURF_NODRAW = 0x0080 // don't bother referencing the texture + ContentsOrigin = 0x1000000 // removed before bsping an entity - SURF_HINT = 0x0100 // make a primary bsp splitter + 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 - SURF_SKIP = 0x0200 // completely ignore, allowing non-closed brushes - SURF_NOLIGHT = 0x0400 // Don't calculate light - SURF_BUMPLIGHT = 0x0800 // calculate three lightmaps for the surface for bumpmapping - SURF_NOSHADOWS = 0x1000 // Don't receive shadows - SURF_NODECALS = 0x2000 // Don't receive decals - SURF_NOCHOP = 0x4000 // Don't subdivide patches on this surface - SURF_HITBOX = 0x8000 // surface is part of a hitbox + // NOTE: These are stored in a short in the engine now. Don't use more than 16 bits. - // ----------------------------------------------------- - // spatial content masks - used for spatial queries (traceline,etc.) - // ----------------------------------------------------- - MASK_ALL = (0xFFFFFFFF) + 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 - // everything that is normally solid - MASK_SOLID = (CONTENTS_SOLID | CONTENTS_MOVEABLE | CONTENTS_WINDOW | CONTENTS_MONSTER | CONTENTS_GRATE) + SurfHint = 0x0100 // make a primary bsp splitter - // everything that blocks player movement - MASK_PLAYERSOLID = (CONTENTS_SOLID | CONTENTS_MOVEABLE | CONTENTS_PLAYERCLIP | CONTENTS_WINDOW | CONTENTS_MONSTER | CONTENTS_GRATE) + 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 - // blocks npc movement - MASK_NPCSOLID = (CONTENTS_SOLID | CONTENTS_MOVEABLE | CONTENTS_MONSTERCLIP | CONTENTS_WINDOW | CONTENTS_MONSTER | CONTENTS_GRATE) + // Spacial content masks - used for spacial queries (traceline,etc.). - // water physics in these contents - MASK_WATER = (CONTENTS_WATER | CONTENTS_MOVEABLE | CONTENTS_SLIME) + // MaskAll is everything. + MaskAll = 0xFFFFFFFF - // everything that blocks lighting - MASK_OPAQUE = (CONTENTS_SOLID | CONTENTS_MOVEABLE | CONTENTS_OPAQUE) + // MaskSolid is everything that is normally solid. + MaskSolid = ContentsSolid | ContentsMoveable | ContentsWindow | ContentsMonster | ContentsGrate - // everything that blocks lighting, but with monsters added. - MASK_OPAQUE_AND_NPCS = (MASK_OPAQUE | CONTENTS_MONSTER) + // MaskPlayerSolid is everything that blocks player movement. + MaskPlayerSolid = ContentsSolid | ContentsMoveable | ContentsPlayerClip | ContentsWindow | ContentsMonster | ContentsGrate - // everything that blocks line of sight for AI - MASK_BLOCKLOS = (CONTENTS_SOLID | CONTENTS_MOVEABLE | CONTENTS_BLOCKLOS) + // MaskNPCSolid blocks npc movement. + MaskNPCSolid = ContentsSolid | ContentsMoveable | ContentsMonsterClip | ContentsWindow | ContentsMonster | ContentsGrate - // everything that blocks line of sight for AI plus NPCs - MASK_BLOCKLOS_AND_NPCS = (MASK_BLOCKLOS | CONTENTS_MONSTER) + // MaskWater is water physics in these contents. + MaskWater = ContentsWater | ContentsMoveable | ContentsSlime - // everything that blocks line of sight for players - MASK_VISIBLE = (MASK_OPAQUE | CONTENTS_IGNORE_NODRAW_OPAQUE) + // MaskOpaque is everything that blocks lighting. + MaskOpaque = ContentsSolid | ContentsMoveable | ContentsOpaque - // everything that blocks line of sight for players, but with monsters added. - MASK_VISIBLE_AND_NPCS = (MASK_OPAQUE_AND_NPCS | CONTENTS_IGNORE_NODRAW_OPAQUE) + // MaskOpaqueAndNPCs is everything that blocks lighting, but with monsters added. + MaskOpaqueAndNPCs = MaskOpaque | ContentsMonster - // bullets see these as solid - MASK_SHOT = (CONTENTS_SOLID | CONTENTS_MOVEABLE | CONTENTS_MONSTER | CONTENTS_WINDOW | CONTENTS_DEBRIS | CONTENTS_HITBOX) + // MaskBlockLOS is everything that blocks line of sight for AI. + MaskBlockLOS = ContentsSolid | ContentsMoveable | ContentsBlockLOS - // non-raycasted weapons see this as solid (includes grates) - MASK_SHOT_HULL = (CONTENTS_SOLID | CONTENTS_MOVEABLE | CONTENTS_MONSTER | CONTENTS_WINDOW | CONTENTS_DEBRIS | CONTENTS_GRATE) + // MaskBlockLOSAndNPCs is everything that blocks line of sight for AI plus NPCs. + MaskBlockLOSAndNPCs = MaskBlockLOS | ContentsMonster - // hits solids (not grates) and passes through everything else - MASK_SHOT_PORTAL = (CONTENTS_SOLID | CONTENTS_MOVEABLE | CONTENTS_WINDOW | CONTENTS_MONSTER) + // MaskVisible is everything that blocks line of sight for players. + MaskVisible = MaskOpaque | ContentsIgnoreNodrawOpaque - // everything normally solid, except monsters (world+brush only) - MASK_SOLID_BRUSHONLY = (CONTENTS_SOLID | CONTENTS_MOVEABLE | CONTENTS_WINDOW | CONTENTS_GRATE) + // MaskVisibleAndNPCs is everything that blocks line of sight for players, but with monsters added. + MaskVisibleAndNPCs = MaskOpaqueAndNPCs | ContentsIgnoreNodrawOpaque - // everything normally solid for player movement, except monsters (world+brush only) - MASK_PLAYERSOLID_BRUSHONLY = (CONTENTS_SOLID | CONTENTS_MOVEABLE | CONTENTS_WINDOW | CONTENTS_PLAYERCLIP | CONTENTS_GRATE) + // MaskShot is bullets see these as solid. + MaskShot = ContentsSolid | ContentsMoveable | ContentsMonster | ContentsWindow | ContentsDebris | ContentsHitbox - // everything normally solid for npc movement, except monsters (world+brush only) - MASK_NPCSOLID_BRUSHONLY = (CONTENTS_SOLID | CONTENTS_MOVEABLE | CONTENTS_WINDOW | CONTENTS_MONSTERCLIP | CONTENTS_GRATE) + // MaskShotHull is non-raycasted weapons see this as solid (includes grates). + MaskShotHull = ContentsSolid | ContentsMoveable | ContentsMonster | ContentsWindow | ContentsDetail | ContentsGrate - // just the world, used for route rebuilding - MASK_NPCWORLDSTATIC = (CONTENTS_SOLID | CONTENTS_WINDOW | CONTENTS_MONSTERCLIP | CONTENTS_GRATE) + // MaskShotPortal hits solids (not grates) and passes through everything else. + MaskShotPortal = ContentsSolid | ContentsMoveable | ContentsWindow | ContentsMonster - // These are things that can split areaportals - MASK_SPLITAREAPORTAL = (CONTENTS_WATER | CONTENTS_SLIME) + // MaskSolidBrushOnly is everything normally solid, except monsters (world+brush only). + MaskSolidBrushOnly = ContentsSolid | ContentsMoveable | ContentsWindow | ContentsGrate - // UNDONE: This is untested, any moving water - MASK_CURRENT = (CONTENTS_CURRENT_0 | CONTENTS_CURRENT_90 | CONTENTS_CURRENT_180 | CONTENTS_CURRENT_270 | CONTENTS_CURRENT_UP | CONTENTS_CURRENT_DOWN) + // MaskPlayerSolidBrushOnly is everything normally solid for player movement, except monsters (world+brush only). + MaskPlayerSolidBrushOnly = ContentsSolid | ContentsMoveable | ContentsWindow | ContentsPlayerClip | ContentsGrate - // everything that blocks corpse movement - // UNDONE: Not used yet / may be deleted - MASK_DEADSOLID = (CONTENTS_SOLID | CONTENTS_PLAYERCLIP | CONTENTS_WINDOW | CONTENTS_GRATE) + // 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 ) diff --git a/bsp_test.go b/bsp_test.go index 55bf64d..a1c2f03 100644 --- a/bsp_test.go +++ b/bsp_test.go @@ -2,81 +2,87 @@ package bsp import ( "bytes" - "github.com/galaco/bsp/lumps" + "compress/gzip" + "io" "log" + "os" "testing" + + "github.com/google/go-cmp/cmp" ) // Test that resultant lump data matches expected. -func TestLumpExports(t *testing.T) { - t.Skip() - file, err := ReadFromFile("maps/v20/de_dust2.bsp") - if err != nil { - t.Error(err) +func Test_ExportedLumpBytesAreCorrect(t *testing.T) { + testCases := []struct { + name string + filePath string + }{ + { + name: "de_dust2", + filePath: "testdata/v20/de_dust2.bsp.gz", + }, + { + name: "ar_baggage", + filePath: "testdata/v21/ar_baggage.bsp.gz", + }, } - // Verify lump lengths - lumpIndex := 0 - for lumpIndex < 64 { - lump := file.Lump(LumpId(lumpIndex)) - rawLump := file.RawLump(LumpId(lumpIndex)) - lumpBytes, err := lump.Marshall() - if err != nil { - t.Error(err) - } - if len(lumpBytes) != int(file.Header().Lumps[lumpIndex].Length) { - t.Errorf("Lump: %d length mismatch. Got: %dbytes, expected: %dbytes", lumpIndex, len(lumpBytes), file.header.Lumps[lumpIndex].Length) - } else { - log.Printf("Index: %d, Expected: %d, Actual: %d\n", lumpIndex, len(rawLump.RawContents()), len(lumpBytes)) - if !bytes.Equal(lumpBytes, rawLump.RawContents()) { - t.Errorf("Lump: %d data mismatch", lumpIndex) + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + binaryFile, err := os.Open(tc.filePath) + if err != nil { + t.Fatal(err) + } + defer func(binaryFile *os.File) { + if err := binaryFile.Close(); err != nil { + t.Error(err) + } + }(binaryFile) + binarygzr, err := gzip.NewReader(binaryFile) + if err != nil { + t.Fatal(err) + } + binaryData, err := io.ReadAll(binarygzr) + if err != nil { + t.Fatal(err) } - } - - lumpIndex += 1 - } -} - -func TestBsp_Header(t *testing.T) { - sut := new(Bsp) - header := new(Header) - header.Id = 564 - - sut.header = *header - - if sut.Header().Id != header.Id { - t.Error("unexpected header struct returned") - } -} - -func TestBsp_Lump(t *testing.T) { - sut := new(Bsp) - l := new(lumps.Generic) - sut.lumps[0].data = l - - if sut.Lump(0) != l { - t.Error("unexpected lump returned") - } -} - -func TestBsp_RawLump(t *testing.T) { - sut := new(Bsp) - l := new(lumps.Generic) - sut.lumps[0].data = l - if sut.RawLump(0).data != l { - t.Error("unexpected lump returned") - } -} + testFile, err := os.Open(tc.filePath) + if err != nil { + t.Fatal(err) + } + defer func(testFile *os.File) { + if err := testFile.Close(); err != nil { + t.Error(err) + } + }(testFile) + testFilegzr, err := gzip.NewReader(testFile) + if err != nil { + t.Fatal(err) + } + testBSP, err := NewReaderWithConfig(ReaderConfig{ + LumpResolver: LumpResolverByBSPVersion, + }).Read(testFilegzr) + if err != nil { + t.Fatal(err) + } -func TestBsp_SetLump(t *testing.T) { - sut := new(Bsp) - ld := new(lumps.Generic) - l := new(Lump) - l.data = ld - sut.SetLump(0, *l) + // Verify lump lengths. + for lumpIndex := range testBSP.Header.Lumps { + actual, err := testBSP.Lumps[lumpIndex].ToBytes() + if err != nil { + t.Error(err) + } + if len(actual) != int(testBSP.Header.Lumps[lumpIndex].Length) { + t.Errorf("Lump: %d length mismatch. Got: %dbytes, expected: %dbytes", lumpIndex, len(actual), testBSP.Header.Lumps[lumpIndex].Length) + } - if sut.RawLump(0).data != ld { - t.Error("unexpected lump returned") + expected := binaryData[testBSP.Header.Lumps[lumpIndex].Offset : testBSP.Header.Lumps[lumpIndex].Offset+testBSP.Header.Lumps[lumpIndex].Length] + if !bytes.Equal(actual, expected) { + t.Errorf("Lump: %d data mismatch", lumpIndex) + log.Println(cmp.Diff(expected, actual)) + } + } + }) } } diff --git a/crc.go b/crc.go index 7a1fb95..4376059 100644 --- a/crc.go +++ b/crc.go @@ -16,7 +16,7 @@ func (bsp *Bsp) CRC32() (uint32, error) { } sort.Slice(lumpList, func(i, j int) bool { - return bsp.header.Lumps[lumpList[i]].Offset < bsp.header.Lumps[lumpList[j]].Offset + return bsp.Header.Lumps[lumpList[i]].Offset < bsp.Header.Lumps[lumpList[j]].Offset }) for i := 0; i < lumpCount; i++ { @@ -25,13 +25,16 @@ func (bsp *Bsp) CRC32() (uint32, error) { continue } - _, err := crc.Write(bsp.RawLump(l).raw) + raw, err := bsp.Lumps[l].ToBytes() if err != nil { return 0, err } + if _, err := crc.Write(raw); err != nil { + return 0, err + } } - // see CRC32_Final + // see CRC32_Final. res := crc.Sum32() ^ 0xFFFFFFFF return res, nil diff --git a/crc_test.go b/crc_test.go index fa31181..591b410 100644 --- a/crc_test.go +++ b/crc_test.go @@ -9,29 +9,47 @@ import ( ) func TestBsp_Crc(t *testing.T) { - f, err := os.Open("ar_baggage.bsp.gz") - if err != nil { - t.Error(err) + testCases := []struct { + name string + filePath string + expected uint32 + }{ + { + name: "de_dust2", + filePath: "testdata/v20/de_dust2.bsp.gz", + expected: 3380635791, + }, + { + name: "ar_baggage", + filePath: "testdata/v21/ar_baggage.bsp.gz", + expected: 2836609078, + }, } - - gzR, err := gzip.NewReader(f) - if err != nil { - t.Error(err) - } - - bspF, err := bsp.ReadFromStream(gzR) - if err != nil { - t.Error(err) - } - - res, err := bspF.CRC32() - if err != nil { - t.Error("unexpected error:", err) - } - - const expected = 2836609078 - - if res != expected { - t.Errorf("CRC incorrect, expected %d got %d", expected, res) + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + f, err := os.Open(tc.filePath) + if err != nil { + t.Error(err) + } + + gzR, err := gzip.NewReader(f) + if err != nil { + t.Error(err) + } + + bspF, err := bsp.NewReader().Read(gzR) + if err != nil { + t.Error(err) + } + + res, err := bspF.CRC32() + if err != nil { + t.Error("unexpected error:", err) + } + + if res != tc.expected { + t.Errorf("CRC incorrect, expected %d got %d", tc.expected, res) + } + }) } } diff --git a/examples/exportignoreemptymapflags/main.go b/examples/exportignoreemptymapflags/main.go new file mode 100644 index 0000000..b260466 --- /dev/null +++ b/examples/exportignoreemptymapflags/main.go @@ -0,0 +1,69 @@ +package main + +import ( + "bytes" + "compress/gzip" + "fmt" + "github.com/galaco/bsp" + "github.com/galaco/bsp/lump" + "log" + "os" +) + +type CustomMapFlags struct { + lump.MapFlags +} + +func (mapFlags *CustomMapFlags) ToBytes() ([]byte, error) { + // If our flags are 0, return an empty lump. + // CS:S de_nuke works this way, but de_dust2 does not. + if mapFlags.MapFlags.Data.LevelFlags == 0 { + return make([]byte, 0), nil + } + return mapFlags.MapFlags.ToBytes() +} + +func customLumpResolver(id bsp.LumpId, header bsp.Header) (l bsp.Lump, err error) { + if id == bsp.LumpMapFlags { + return &CustomMapFlags{}, nil + } + return bsp.LumpResolverByBSPVersion(id, header) +} + +func main() { + f, err := os.Open("testdata/v20/de_dust2.bsp.gz") + if err != nil { + panic(err) + } + defer f.Close() + binarygzr, err := gzip.NewReader(f) + if err != nil { + panic(err) + } + + reader := bsp.NewReaderWithConfig(bsp.ReaderConfig{ + LumpResolver: customLumpResolver, + }) + file, err := reader.Read(binarygzr) + if err != nil { + panic(err) + } + log.Println(fmt.Sprintf("Old MapFlags Length: %d", file.Header.Lumps[bsp.LumpMapFlags].Length)) + + // Make a modification to a lump. + file.Lumps[bsp.LumpMapFlags].(*CustomMapFlags).Data.LevelFlags = 0 + + writer := bsp.NewWriter() + output, err := writer.Write(file) + if err != nil { + panic(err) + } + + buf := bytes.NewBuffer(output) + reader2 := bsp.NewReader() + newFile, err := reader2.Read(buf) + if err != nil { + panic(err) + } + log.Println(fmt.Sprintf("New MapFlags Length: %d", newFile.Header.Lumps[bsp.LumpMapFlags].Length)) +} diff --git a/examples/printentdata/main.go b/examples/printentdata/main.go new file mode 100644 index 0000000..dc37603 --- /dev/null +++ b/examples/printentdata/main.go @@ -0,0 +1,35 @@ +package main + +import ( + "compress/gzip" + "github.com/galaco/bsp" + "github.com/galaco/bsp/lump" + "log" + "os" +) + +func main() { + f, err := os.Open("testdata/v20/de_dust2.bsp.gz") + if err != nil { + log.Fatal(err) + } + defer f.Close() + binarygzr, err := gzip.NewReader(f) + if err != nil { + log.Fatal(err) + } + + reader := bsp.NewReader() + file, err := reader.Read(binarygzr) + if err != nil { + log.Fatal(err) + } + + entdata := file.Lumps[bsp.LumpEntities].(*lump.EntData) + l := entdata.Contents() + if err != nil { + panic(err) + } + _ = l + log.Println(entdata.Contents()) +} diff --git a/examples/readbsp/main.go b/examples/readbsp/main.go new file mode 100644 index 0000000..28295e8 --- /dev/null +++ b/examples/readbsp/main.go @@ -0,0 +1,27 @@ +package main + +import ( + "compress/gzip" + "github.com/galaco/bsp" + "log" + "os" +) + +func main() { + f, err := os.Open("testdata/v20/de_dust2.bsp.gz") + if err != nil { + log.Fatal(err) + } + defer f.Close() + binarygzr, err := gzip.NewReader(f) + if err != nil { + log.Fatal(err) + } + + reader := bsp.NewReader() + file, err := reader.Read(binarygzr) + if err != nil { + log.Fatal(err) + } + _ = file +} diff --git a/examples/writebsp/main.go b/examples/writebsp/main.go new file mode 100644 index 0000000..181b7ec --- /dev/null +++ b/examples/writebsp/main.go @@ -0,0 +1,35 @@ +package main + +import ( + "compress/gzip" + "github.com/galaco/bsp" + "github.com/galaco/bsp/lump" + "os" +) + +func main() { + f, err := os.Open("testdata/v20/de_dust2.bsp.gz") + if err != nil { + panic(err) + } + defer f.Close() + binarygzr, err := gzip.NewReader(f) + if err != nil { + panic(err) + } + + reader := bsp.NewReader() + file, err := reader.Read(binarygzr) + if err != nil { + panic(err) + } + + // Make a modification to a lump. + file.Lumps[bsp.LumpMapFlags].(*lump.MapFlags).Data.LevelFlags &= 8 + + writer := bsp.NewWriter() + _, err = writer.Write(file) + if err != nil { + panic(err) + } +} diff --git a/go.mod b/go.mod index 6caa7a0..5a66ad1 100644 --- a/go.mod +++ b/go.mod @@ -2,6 +2,9 @@ module github.com/galaco/bsp go 1.19 -require github.com/go-gl/mathgl v1.0.0 +require github.com/go-gl/mathgl v1.1.0 -require golang.org/x/image v0.5.0 // indirect +require ( + github.com/google/go-cmp v0.5.9 // indirect + golang.org/x/image v0.12.0 // indirect +) diff --git a/go.sum b/go.sum index 4c92501..715f615 100644 --- a/go.sum +++ b/go.sum @@ -1,29 +1,42 @@ github.com/go-gl/mathgl v1.0.0 h1:t9DznWJlXxxjeeKLIdovCOVJQk/GzDEL7h/h+Ro2B68= github.com/go-gl/mathgl v1.0.0/go.mod h1:yhpkQzEiH9yPyxDUGzkmgScbaBVlhC06qodikEM0ZwQ= +github.com/go-gl/mathgl v1.1.0 h1:0lzZ+rntPX3/oGrDzYGdowSLC2ky8Osirvf5uAwfIEA= +github.com/go-gl/mathgl v1.1.0/go.mod h1:yhpkQzEiH9yPyxDUGzkmgScbaBVlhC06qodikEM0ZwQ= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/image v0.0.0-20190321063152-3fc05d484e9f/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.5.0 h1:5JMiNunQeQw++mMOz48/ISeNu3Iweh/JaZU8ZLqHRrI= golang.org/x/image v0.5.0/go.mod h1:FVC7BI/5Ym8R25iw5OLsgshdUBbT1h5jZTpA+mvAdZ4= +golang.org/x/image v0.12.0 h1:w13vZbU4o5rKOFFR8y7M+c4A5jXDC0uXTdHYRP8X2DQ= +golang.org/x/image v0.12.0/go.mod h1:Lu90jvHG7GfemOIcldsh9A2hS01ocl6oNO7ype5mEnk= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/internal/versions/v19.go b/internal/versions/v19.go deleted file mode 100644 index b8cdfc9..0000000 --- a/internal/versions/v19.go +++ /dev/null @@ -1,10 +0,0 @@ -package versions - -import ( - "github.com/galaco/bsp/lumps" -) - -// Getv19Lump returns the corresponding v19 lump for provided index -func Getv19Lump(index int) (lumps.ILump, error) { - return Getv20Lump(index) -} diff --git a/internal/versions/v19_test.go b/internal/versions/v19_test.go deleted file mode 100644 index 5af9f1e..0000000 --- a/internal/versions/v19_test.go +++ /dev/null @@ -1,16 +0,0 @@ -package versions - -import ( - "github.com/galaco/bsp/lumps" - "reflect" - "testing" -) - -func TestGetv19Lump(t *testing.T) { - l, _ := Getv19Lump(4) - if reflect.TypeOf(l) != reflect.TypeOf(&lumps.Visibility{}) { - t.Errorf("Lump type mismatch. Got: %s, expected: %s, ", - reflect.TypeOf(l), - reflect.TypeOf(lumps.Visibility{})) - } -} diff --git a/internal/versions/v20.go b/internal/versions/v20.go deleted file mode 100644 index 4bd19ff..0000000 --- a/internal/versions/v20.go +++ /dev/null @@ -1,146 +0,0 @@ -package versions - -import ( - "errors" - "github.com/galaco/bsp/lumps" -) - -// Getv20Lump returns the corresponding v20 lump for provided index -func Getv20Lump(index int) (lumps.ILump, error) { - var ret lumps.ILump - var err error - switch index { - case 0: - ret = new(lumps.EntData) - case 1: - ret = new(lumps.Planes) - case 2: - ret = new(lumps.TexData) - case 3: - ret = new(lumps.Vertex) - case 4: - ret = new(lumps.Visibility) - case 5: - ret = new(lumps.Node) - case 6: - ret = new(lumps.TexInfo) - case 7: - ret = new(lumps.Face) - case 8: - ret = new(lumps.Lighting) - case 9: - ret = new(lumps.Occlusion) - case 10: - ret = new(lumps.Leaf) - case 11: - ret = new(lumps.FaceId) - case 12: - ret = new(lumps.Edge) - case 13: - ret = new(lumps.Surfedge) - case 14: - ret = new(lumps.Model) - case 15: - ret = new(lumps.WorldLight) - case 16: - ret = new(lumps.LeafFace) - case 17: - ret = new(lumps.LeafBrush) - case 18: - ret = new(lumps.Brush) - case 19: - ret = new(lumps.BrushSide) - case 20: - ret = new(lumps.Area) - case 21: - ret = new(lumps.AreaPortal) - case 22: - ret = new(lumps.Unimplemented) //portals | unused0 | propcollision - case 23: - ret = new(lumps.Unimplemented) //clusters | unused1 | prophulls - case 24: - ret = new(lumps.Unimplemented) //portalverts | unused2 | prophullverts - case 25: - ret = new(lumps.Unimplemented) //clusterportals | unused3 | proptris - case 26: - ret = new(lumps.DispInfo) - case 27: - ret = new(lumps.Face) - case 28: - ret = new(lumps.PhysDisp) - case 29: - ret = new(lumps.Unimplemented) //physcollide - IN PROGRESS - case 30: - ret = new(lumps.VertNormal) - case 31: - ret = new(lumps.VertNormalIndice) - case 32: - ret = new(lumps.Unimplemented) //disp lightmap alphas - IS STRIPPED ANYWAY? - case 33: - ret = new(lumps.DispVert) - case 34: - ret = new(lumps.DispLightmapSamplePosition) - case 35: - ret = new(lumps.Game) - case 36: - ret = new(lumps.LeafWaterData) - case 37: - ret = new(lumps.Unimplemented) //primitives FIXME - Appears to be 4bytes unaccounted for at end of lump? - case 38: - ret = new(lumps.PrimVert) - case 39: - ret = new(lumps.PrimIndice) - case 40: - ret = new(lumps.Pakfile) //pakfile - case 41: - ret = new(lumps.ClipPortalVerts) - case 42: - ret = new(lumps.Cubemap) - case 43: - ret = new(lumps.TexDataStringData) - case 44: - ret = new(lumps.TexDataStringTable) - case 45: - ret = new(lumps.Overlay) - case 46: - ret = new(lumps.LeafMinDistToWater) - case 47: - ret = new(lumps.FaceMacroTextureInfo) - case 48: - ret = new(lumps.DispTris) - case 49: - ret = new(lumps.Unimplemented) //physcollidesurface | prop blob - case 50: - ret = new(lumps.Unimplemented) //wateroverlays - case 51: - ret = new(lumps.LeafAmbientIndexHDR) - case 52: - ret = new(lumps.LeafAmbientIndex) - case 53: - ret = new(lumps.Lighting) //lighting hdr - case 54: - ret = new(lumps.WorldLightHDR) //worldlights hdr - case 55: - ret = new(lumps.LeafAmbientLightingHDR) - case 56: - ret = new(lumps.LeafAmbientLighting) //leaf ambient lighting - case 57: - ret = new(lumps.Unimplemented) //xzippakfile - case 58: - ret = new(lumps.FaceHDR) - case 59: - ret = new(lumps.MapFlags) - case 60: - ret = new(lumps.OverlayFade) - case 61: - ret = new(lumps.Unimplemented) //overlay system levels - case 62: - ret = new(lumps.Unimplemented) //physlevel - case 63: - ret = new(lumps.Unimplemented) //disp multiblend - default: - err = errors.New("invalid lump id") - } - - return ret, err -} diff --git a/internal/versions/v20_test.go b/internal/versions/v20_test.go deleted file mode 100644 index 4a332bd..0000000 --- a/internal/versions/v20_test.go +++ /dev/null @@ -1,21 +0,0 @@ -package versions - -import ( - "github.com/galaco/bsp/lumps" - "reflect" - "testing" -) - -func TestGetv20Lump(t *testing.T) { - l, _ := Getv20Lump(4) - if reflect.TypeOf(l) != reflect.TypeOf(&lumps.Visibility{}) { - t.Errorf("Lump type mismatch. Got: %s, expected: %s, ", - reflect.TypeOf(l), - reflect.TypeOf(lumps.Visibility{})) - } - - _, err := Getv20Lump(65) - if err == nil { - t.Error("invalid lump index provided, but not error returned") - } -} diff --git a/internal/versions/v21.go b/internal/versions/v21.go deleted file mode 100644 index 178737d..0000000 --- a/internal/versions/v21.go +++ /dev/null @@ -1,10 +0,0 @@ -package versions - -import ( - "github.com/galaco/bsp/lumps" -) - -// Getv21Lump returns the corresponding v21 lump for provided index -func Getv21Lump(index int) (lumps.ILump, error) { - return Getv20Lump(index) -} diff --git a/internal/versions/v21_test.go b/internal/versions/v21_test.go deleted file mode 100644 index 48fe64f..0000000 --- a/internal/versions/v21_test.go +++ /dev/null @@ -1,16 +0,0 @@ -package versions - -import ( - "github.com/galaco/bsp/lumps" - "reflect" - "testing" -) - -func TestGetv21Lump(t *testing.T) { - l, _ := Getv21Lump(4) - if reflect.TypeOf(l) != reflect.TypeOf(&lumps.Visibility{}) { - t.Errorf("Lump type mismatch. Got: %s, expected: %s, ", - reflect.TypeOf(l), - reflect.TypeOf(lumps.Visibility{})) - } -} diff --git a/internal/versions/versions.go b/internal/versions/versions.go deleted file mode 100644 index ccbad56..0000000 --- a/internal/versions/versions.go +++ /dev/null @@ -1,19 +0,0 @@ -package versions - -import "github.com/galaco/bsp/lumps" - -// GetLumpForVersion 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. -func GetLumpForVersion(bspVersion int, lumpId int) (lumps.ILump, error) { - switch bspVersion { - case 19: - return Getv19Lump(lumpId) - case 20: - return Getv20Lump(lumpId) - case 21: - return Getv21Lump(lumpId) - default: - return new(lumps.Unimplemented), nil - } -} diff --git a/internal/versions/versions_test.go b/internal/versions/versions_test.go deleted file mode 100644 index 1fbed32..0000000 --- a/internal/versions/versions_test.go +++ /dev/null @@ -1,41 +0,0 @@ -package versions - -import ( - "github.com/galaco/bsp/lumps" - "reflect" - "testing" -) - -func TestGetLumpForVersion(t *testing.T) { - l, err := GetLumpForVersion(987, 1) - if err != nil { - t.Error(err) - } - if reflect.TypeOf(l) != reflect.TypeOf(&lumps.Unimplemented{}) { - t.Error("Lump type mismatch.") - } - - l, err = GetLumpForVersion(19, 4) - if err != nil { - t.Error(err) - } - if reflect.TypeOf(l) != reflect.TypeOf(&lumps.Visibility{}) { - t.Error("Lump type mismatch.") - } - - l, err = GetLumpForVersion(20, 4) - if err != nil { - t.Error(err) - } - if reflect.TypeOf(l) != reflect.TypeOf(&lumps.Visibility{}) { - t.Error("Lump type mismatch.") - } - - l, err = GetLumpForVersion(21, 4) - if err != nil { - t.Error(err) - } - if reflect.TypeOf(l) != reflect.TypeOf(&lumps.Visibility{}) { - t.Error("Lump type mismatch.") - } -} diff --git a/lump.go b/lump.go deleted file mode 100644 index 94905a3..0000000 --- a/lump.go +++ /dev/null @@ -1,74 +0,0 @@ -package bsp - -import ( - "fmt" - "github.com/galaco/bsp/internal/versions" - "github.com/galaco/bsp/lumps" -) - -// Lump is a container for a lump. Also includes metadata about the lump. -// N.B. Some information mirrors the header's lump descriptor, but header information should not be trusted after -// import completion. -type Lump struct { - raw []byte - data lumps.ILump - length int32 - id LumpId - loaded bool -} - -// SetId sets lump identifier -// Id is the lump type id (not the id for the order the lumps are stored) -func (l *Lump) SetId(index LumpId) { - l.id = index -} - -// Contents Get the contents of a lump. -// NOTE: Will need to be cast to the relevant lumps -func (l *Lump) Contents() lumps.ILump { - if !l.loaded { - if l.data.Unmarshall(l.raw) != nil { - return nil - } - l.loaded = true - } - return l.data -} - -// SetContents Set content type of a lump. -func (l *Lump) SetContents(data lumps.ILump) { - l.data = data - l.loaded = false -} - -// RawContents Get the raw []byte contents of a lump. -// N.B. This is the raw imported value. To get the raw value of a modified lump, use Contents().Marshall() -func (l *Lump) RawContents() []byte { - return l.raw -} - -// SetRawContents Set raw []byte contents of a lump. -func (l *Lump) SetRawContents(raw []byte) { - l.raw = raw -} - -// Length Get length of a lump in bytes. -func (l *Lump) Length() int32 { - return l.length -} - -// getReferenceLumpByIndex Return an instance of a Lump for a given offset. -func getReferenceLumpByIndex(index int, version int32) (lumps.ILump, error) { - if index < 0 || index > 63 { - return nil, fmt.Errorf("invalid lump id: %d provided", index) - } - - l, err := versions.GetLumpForVersion(int(version), index) - if err != nil { - return nil, err - } - - l.SetVersion(version) - - return l, nil -} diff --git a/lump/area.go b/lump/area.go new file mode 100644 index 0000000..2963b18 --- /dev/null +++ b/lump/area.go @@ -0,0 +1,32 @@ +package lump + +import ( + primitives "github.com/galaco/bsp/lump/primitive/area" +) + +// Area is Lump 20: Areas +type Area struct { + Metadata + Data []primitives.Area `json:"data"` +} + +// FromBytes imports this lump from raw byte data. +func (lump *Area) FromBytes(raw []byte) error { + data, err := unmarshallBasicLump[primitives.Area](raw) + if err != nil { + return err + } + + lump.Data = data + return nil +} + +// Contents returns internal format structure Data +func (lump *Area) Contents() []primitives.Area { + return lump.Data +} + +// ToBytes converts this lump back to raw byte Data +func (lump *Area) ToBytes() ([]byte, error) { + return marshallBasicLump(lump.Data) +} diff --git a/lump/areaportal.go b/lump/areaportal.go new file mode 100644 index 0000000..c03fa42 --- /dev/null +++ b/lump/areaportal.go @@ -0,0 +1,32 @@ +package lump + +import ( + primitives "github.com/galaco/bsp/lump/primitive/areaportal" +) + +// AreaPortal is Lump 21: Areaportals +type AreaPortal struct { + Metadata + Data []primitives.AreaPortal `json:"data"` +} + +// FromBytes imports this lump from raw byte data. +func (lump *AreaPortal) FromBytes(raw []byte) error { + data, err := unmarshallBasicLump[primitives.AreaPortal](raw) + if err != nil { + return err + } + + lump.Data = data + return nil +} + +// Contents returns internal format structure Data +func (lump *AreaPortal) Contents() []primitives.AreaPortal { + return lump.Data +} + +// ToBytes converts this lump back to raw byte Data +func (lump *AreaPortal) ToBytes() ([]byte, error) { + return marshallBasicLump(lump.Data) +} diff --git a/lump/brush.go b/lump/brush.go new file mode 100644 index 0000000..15e2577 --- /dev/null +++ b/lump/brush.go @@ -0,0 +1,32 @@ +package lump + +import ( + primitives "github.com/galaco/bsp/lump/primitive/brush" +) + +// Brush is Lump 18: Brush +type Brush struct { + Metadata + Data []primitives.Brush `json:"data"` +} + +// FromBytes imports this lump from raw byte data. +func (lump *Brush) FromBytes(raw []byte) error { + data, err := unmarshallBasicLump[primitives.Brush](raw) + if err != nil { + return err + } + + lump.Data = data + return nil +} + +// Contents returns internal format structure Data +func (lump *Brush) Contents() []primitives.Brush { + return lump.Data +} + +// ToBytes converts this lump back to raw byte Data +func (lump *Brush) ToBytes() ([]byte, error) { + return marshallBasicLump(lump.Data) +} diff --git a/lump/brushside.go b/lump/brushside.go new file mode 100644 index 0000000..c067d90 --- /dev/null +++ b/lump/brushside.go @@ -0,0 +1,32 @@ +package lump + +import ( + primitives "github.com/galaco/bsp/lump/primitive/brushside" +) + +// BrushSide is Lump 19: BrushSide +type BrushSide struct { + Metadata + Data []primitives.BrushSide `json:"data"` // MAX_MAP_BRUSHSIDES = 65536 +} + +// FromBytes imports this lump from raw byte data. +func (lump *BrushSide) FromBytes(raw []byte) error { + data, err := unmarshallBasicLump[primitives.BrushSide](raw) + if err != nil { + return err + } + + lump.Data = data + return nil +} + +// Contents returns internal format structure Data +func (lump *BrushSide) Contents() []primitives.BrushSide { + return lump.Data +} + +// ToBytes converts this lump back to raw byte Data +func (lump *BrushSide) ToBytes() ([]byte, error) { + return marshallBasicLump(lump.Data) +} diff --git a/lump/clipportalverts.go b/lump/clipportalverts.go new file mode 100644 index 0000000..4bb14e3 --- /dev/null +++ b/lump/clipportalverts.go @@ -0,0 +1,32 @@ +package lump + +import ( + "github.com/go-gl/mathgl/mgl32" +) + +// ClipPortalVerts is Lump 41: ClipPortalVerts +type ClipPortalVerts struct { + Metadata + Data []mgl32.Vec3 `json:"data"` +} + +// FromBytes imports this lump from raw byte Data +func (lump *ClipPortalVerts) FromBytes(raw []byte) error { + data, err := unmarshallBasicLump[mgl32.Vec3](raw) + if err != nil { + return err + } + + lump.Data = data + return nil +} + +// Contents returns internal format structure Data +func (lump *ClipPortalVerts) Contents() []mgl32.Vec3 { + return lump.Data +} + +// ToBytes converts this lump back to raw byte Data +func (lump *ClipPortalVerts) ToBytes() ([]byte, error) { + return marshallBasicLump(lump.Data) +} diff --git a/lumps/clipportalverts_test.go b/lump/clipportalverts_test.go similarity index 82% rename from lumps/clipportalverts_test.go rename to lump/clipportalverts_test.go index 212fce0..b96f149 100644 --- a/lumps/clipportalverts_test.go +++ b/lump/clipportalverts_test.go @@ -1,10 +1,11 @@ -package lumps +package lump import ( "bytes" "encoding/binary" - "github.com/go-gl/mathgl/mgl32" "testing" + + "github.com/go-gl/mathgl/mgl32" ) func TestClipPortalVerts_GetData(t *testing.T) { @@ -18,11 +19,11 @@ func TestClipPortalVerts_GetData(t *testing.T) { if err != nil { t.Error(err) } - err = sut.Unmarshall(buf.Bytes()) + err = sut.FromBytes(buf.Bytes()) if err != nil { t.Error(err) } - for idx, b := range sut.GetData() { + for idx, b := range sut.Contents() { if data[idx] != b { t.Error("mismatched between expected and actual unmarshalled bytes") } @@ -40,16 +41,16 @@ func TestClipPortalVerts_Marshall(t *testing.T) { if err != nil { t.Error(err) } - err = sut.Unmarshall(buf.Bytes()) + err = sut.FromBytes(buf.Bytes()) if err != nil { t.Error(err) } - for idx, b := range sut.data { + for idx, b := range sut.Data { if data[idx] != b { t.Error("mismatched between expected and actual unmarshalled bytes") } } - res, err := sut.Marshall() + res, err := sut.ToBytes() if err != nil { t.Errorf("unexpected error during marshalling") } @@ -60,7 +61,7 @@ func TestClipPortalVerts_Marshall(t *testing.T) { } } -func TestClipPortalVerts_Unmarshall(t *testing.T) { +func TestClipPortalVerts_FromBytes(t *testing.T) { sut := ClipPortalVerts{} data := []mgl32.Vec3{ {0, 1, 2}, @@ -71,11 +72,11 @@ func TestClipPortalVerts_Unmarshall(t *testing.T) { if err != nil { t.Error(err) } - err = sut.Unmarshall(buf.Bytes()) + err = sut.FromBytes(buf.Bytes()) if err != nil { t.Error(err) } - for idx, b := range sut.data { + for idx, b := range sut.Data { if data[idx] != b { t.Error("mismatched between expected and actual unmarshalled bytes") } diff --git a/lump/cubemap.go b/lump/cubemap.go new file mode 100644 index 0000000..30299b5 --- /dev/null +++ b/lump/cubemap.go @@ -0,0 +1,32 @@ +package lump + +import ( + primitives "github.com/galaco/bsp/lump/primitive/cubemap" +) + +// Cubemap is Lump 42: Cubemaps +type Cubemap struct { + Metadata + Data []primitives.CubemapSample `json:"data"` +} + +// FromBytes imports this lump from raw byte Data +func (lump *Cubemap) FromBytes(raw []byte) error { + data, err := unmarshallBasicLump[primitives.CubemapSample](raw) + if err != nil { + return err + } + + lump.Data = data + return nil +} + +// Contents returns internal format structure Data +func (lump *Cubemap) Contents() []primitives.CubemapSample { + return lump.Data +} + +// ToBytes converts this lump back to raw byte Data +func (lump *Cubemap) ToBytes() ([]byte, error) { + return marshallBasicLump(lump.Data) +} diff --git a/lump/dispinfo.go b/lump/dispinfo.go new file mode 100644 index 0000000..112c2a8 --- /dev/null +++ b/lump/dispinfo.go @@ -0,0 +1,32 @@ +package lump + +import ( + primitives "github.com/galaco/bsp/lump/primitive/dispinfo" +) + +// DispInfo is Lump 26: DispInfo +type DispInfo struct { + Metadata + Data []primitives.DispInfo `json:"data"` +} + +// FromBytes imports this lump from raw byte Data +func (lump *DispInfo) FromBytes(raw []byte) error { + data, err := unmarshallBasicLump[primitives.DispInfo](raw) + if err != nil { + return err + } + + lump.Data = data + return nil +} + +// Contents returns internal format structure Data +func (lump *DispInfo) Contents() []primitives.DispInfo { + return lump.Data +} + +// ToBytes converts this lump back to raw byte Data +func (lump *DispInfo) ToBytes() ([]byte, error) { + return marshallBasicLump(lump.Data) +} diff --git a/lump/displightmapsampleposition.go b/lump/displightmapsampleposition.go new file mode 100644 index 0000000..803aac9 --- /dev/null +++ b/lump/displightmapsampleposition.go @@ -0,0 +1,5 @@ +package lump + +// DispLightmapSamplePosition is Lump 34: DispLightmapSamplePosition +// NOTE: This does NOT have a mapped format yet, and is readable as []byte only +type DispLightmapSamplePosition = rawBytes diff --git a/lumps/displightmapsampleposition_test.go b/lump/displightmapsampleposition_test.go similarity index 73% rename from lumps/displightmapsampleposition_test.go rename to lump/displightmapsampleposition_test.go index 95173d0..f6f9d09 100644 --- a/lumps/displightmapsampleposition_test.go +++ b/lump/displightmapsampleposition_test.go @@ -1,14 +1,14 @@ -package lumps +package lump import "testing" func TestDispLightmapSamplePosition_GetData(t *testing.T) { sut := DispLightmapSamplePosition{} data := []byte{0, 1, 2, 5, 43, 156, 146, 3, 3, 6} - if err := sut.Unmarshall(data); err != nil { + if err := sut.FromBytes(data); err != nil { t.Error(err) } - for idx, b := range sut.GetData() { + for idx, b := range sut.Contents() { if data[idx] != b { t.Error("mismatched between expected and actual unmarshalled bytes") } @@ -18,15 +18,15 @@ func TestDispLightmapSamplePosition_GetData(t *testing.T) { func TestDispLightmapSamplePosition_Marshall(t *testing.T) { sut := DispLightmapSamplePosition{} data := []byte{0, 1, 2, 5, 43, 156, 146, 3, 3, 6} - if err := sut.Unmarshall(data); err != nil { + if err := sut.FromBytes(data); err != nil { t.Error(err) } - for idx, b := range sut.data { + for idx, b := range sut.Data { if data[idx] != b { t.Error("mismatched between expected and actual unmarshalled bytes") } } - res, err := sut.Marshall() + res, err := sut.ToBytes() if err != nil { t.Errorf("unexpected error during marshalling") } @@ -37,13 +37,13 @@ func TestDispLightmapSamplePosition_Marshall(t *testing.T) { } } -func TestDispLightmapSamplePosition_Unmarshall(t *testing.T) { +func TestDispLightmapSamplePosition_FromBytes(t *testing.T) { sut := DispLightmapSamplePosition{} data := []byte{0, 1, 2, 5, 43, 156, 146, 3, 3, 6} - if err := sut.Unmarshall(data); err != nil { + if err := sut.FromBytes(data); err != nil { t.Error(err) } - for idx, b := range sut.data { + for idx, b := range sut.Data { if data[idx] != b { t.Error("mismatched between expected and actual unmarshalled bytes") } diff --git a/lump/disptris.go b/lump/disptris.go new file mode 100644 index 0000000..6cc95dc --- /dev/null +++ b/lump/disptris.go @@ -0,0 +1,32 @@ +package lump + +import ( + primitives "github.com/galaco/bsp/lump/primitive/disptris" +) + +// DispTris is Lump 48: DispTris +type DispTris struct { + Metadata + Data []primitives.DispTri `json:"data"` +} + +// FromBytes imports this lump from raw byte Data +func (lump *DispTris) FromBytes(raw []byte) error { + data, err := unmarshallBasicLump[primitives.DispTri](raw) + if err != nil { + return err + } + + lump.Data = data + return nil +} + +// Contents returns internal format structure Data +func (lump *DispTris) Contents() []primitives.DispTri { + return lump.Data +} + +// ToBytes converts this lump back to raw byte Data +func (lump *DispTris) ToBytes() ([]byte, error) { + return marshallBasicLump(lump.Data) +} diff --git a/lumps/disptris_test.go b/lump/disptris_test.go similarity index 78% rename from lumps/disptris_test.go rename to lump/disptris_test.go index d4cf988..22b98b3 100644 --- a/lumps/disptris_test.go +++ b/lump/disptris_test.go @@ -1,4 +1,4 @@ -package lumps +package lump import ( "bytes" @@ -14,10 +14,10 @@ func TestDispTris_GetData(t *testing.T) { if err != nil { t.Error(err) } - if err := sut.Unmarshall(buf.Bytes()); err != nil { + if err := sut.FromBytes(buf.Bytes()); err != nil { t.Error(err) } - for idx, b := range sut.GetData() { + for idx, b := range sut.Contents() { if data[idx] != b.Tags { t.Error("mismatched between expected and actual unmarshalled bytes") } @@ -32,15 +32,15 @@ func TestDispTris_Marshall(t *testing.T) { if err != nil { t.Error(err) } - if err := sut.Unmarshall(buf.Bytes()); err != nil { + if err := sut.FromBytes(buf.Bytes()); err != nil { t.Error(err) } - for idx, b := range sut.data { + for idx, b := range sut.Data { if data[idx] != b.Tags { t.Error("mismatched between expected and actual unmarshalled bytes") } } - res, err := sut.Marshall() + res, err := sut.ToBytes() if err != nil { t.Errorf("unexpected error during marshalling") } @@ -51,7 +51,7 @@ func TestDispTris_Marshall(t *testing.T) { } } -func TestDispTris_Unmarshall(t *testing.T) { +func TestDispTris_FromBytes(t *testing.T) { sut := DispTris{} data := []uint16{0, 1, 2, 5, 43, 156, 146, 3, 3, 6} var buf bytes.Buffer @@ -59,10 +59,10 @@ func TestDispTris_Unmarshall(t *testing.T) { if err != nil { t.Error(err) } - if err := sut.Unmarshall(buf.Bytes()); err != nil { + if err := sut.FromBytes(buf.Bytes()); err != nil { t.Error(err) } - for idx, b := range sut.data { + for idx, b := range sut.Data { if data[idx] != b.Tags { t.Error("mismatched between expected and actual unmarshalled bytes") } diff --git a/lump/dispvert.go b/lump/dispvert.go new file mode 100644 index 0000000..9cd9d7e --- /dev/null +++ b/lump/dispvert.go @@ -0,0 +1,32 @@ +package lump + +import ( + primitives "github.com/galaco/bsp/lump/primitive/dispvert" +) + +// DispVert is Lump 33: DispVert +type DispVert struct { + Metadata + Data []primitives.DispVert `json:"data"` +} + +// FromBytes imports this lump from raw byte Data +func (lump *DispVert) FromBytes(raw []byte) error { + data, err := unmarshallBasicLump[primitives.DispVert](raw) + if err != nil { + return err + } + + lump.Data = data + return nil +} + +// Contents returns internal format structure Data +func (lump *DispVert) Contents() []primitives.DispVert { + return lump.Data +} + +// ToBytes converts this lump back to raw byte Data +func (lump *DispVert) ToBytes() ([]byte, error) { + return marshallBasicLump(lump.Data) +} diff --git a/lump/edge.go b/lump/edge.go new file mode 100644 index 0000000..d31e7fb --- /dev/null +++ b/lump/edge.go @@ -0,0 +1,32 @@ +package lump + +import ( + "bytes" + "encoding/binary" +) + +// Edge is Lump 12: Edge +type Edge struct { + Metadata + Data [][2]uint16 `json:"data"` // MAX_MAP_EDGES = 256000 +} + +// FromBytes imports this lump from raw byte Data +func (lump *Edge) FromBytes(raw []byte) error { + lump.Data = make([][2]uint16, len(raw)/4) + if err := binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.Data); err != nil { + return err + } + + return nil +} + +// Contents returns internal format structure Data +func (lump *Edge) Contents() [][2]uint16 { + return lump.Data +} + +// ToBytes converts this lump back to raw byte Data +func (lump *Edge) ToBytes() ([]byte, error) { + return marshallBasicLump(lump.Data) +} diff --git a/lumps/edge_test.go b/lump/edge_test.go similarity index 79% rename from lumps/edge_test.go rename to lump/edge_test.go index 99dd29d..cc1fe2d 100644 --- a/lumps/edge_test.go +++ b/lump/edge_test.go @@ -1,4 +1,4 @@ -package lumps +package lump import ( "bytes" @@ -14,10 +14,10 @@ func TestEdge_GetData(t *testing.T) { if err != nil { t.Error(err) } - if err := sut.Unmarshall(buf.Bytes()); err != nil { + if err := sut.FromBytes(buf.Bytes()); err != nil { t.Error(err) } - for idx, b := range sut.GetData() { + for idx, b := range sut.Contents() { if data[idx][0] != b[0] || data[idx][1] != b[1] { t.Error("mismatched between expected and actual unmarshalled bytes") } @@ -32,15 +32,15 @@ func TestEdge_Marshall(t *testing.T) { if err != nil { t.Error(err) } - if err := sut.Unmarshall(buf.Bytes()); err != nil { + if err := sut.FromBytes(buf.Bytes()); err != nil { t.Error(err) } - for idx, b := range sut.data { + for idx, b := range sut.Data { if data[idx] != b { t.Error("mismatched between expected and actual unmarshalled bytes") } } - res, err := sut.Marshall() + res, err := sut.ToBytes() if err != nil { t.Errorf("unexpected error during marshalling") } @@ -51,7 +51,7 @@ func TestEdge_Marshall(t *testing.T) { } } -func TestEdge_Unmarshall(t *testing.T) { +func TestEdge_FromBytes(t *testing.T) { sut := Edge{} data := [][2]uint16{{0, 1}, {2, 5}, {43, 156}, {146, 3}, {3, 6}} var buf bytes.Buffer @@ -59,10 +59,10 @@ func TestEdge_Unmarshall(t *testing.T) { if err != nil { t.Error(err) } - if err := sut.Unmarshall(buf.Bytes()); err != nil { + if err := sut.FromBytes(buf.Bytes()); err != nil { t.Error(err) } - for idx, b := range sut.data { + for idx, b := range sut.Data { if data[idx] != b { t.Error("mismatched between expected and actual unmarshalled bytes") } diff --git a/lump/entdata.go b/lump/entdata.go new file mode 100644 index 0000000..c38c50a --- /dev/null +++ b/lump/entdata.go @@ -0,0 +1,66 @@ +package lump + +import ( + "github.com/galaco/bsp/lump/primitive/entities" + "strings" +) + +// EntData is Lump 0: Entdata +type EntData struct { + Metadata + Data []entities.Entity `json:"data"` +} + +// FromBytes imports this lump from raw byte Data. +// @TODO this is a very naive implementation. +func (lump *EntData) FromBytes(raw []byte) error { + var ents []entities.Entity + + // split on quotes after trimming escape char. + components := strings.Split(string(raw), "\"") + + currentEnt := entities.Entity{} + // Start at 1 to skip the opening "{". + for i := 1; i < len(components); i += 4 { + + // format is "key" + " " + "value" + "\n" + currentEnt = append(currentEnt, entities.KeyValue{Key: components[i], Value: components[i+2]}) + + // New entity) + components[i+3] = strings.Trim(components[i+3], " \n\r\t") + if components[i+3] == "}\n{" || components[i+3] == "}\n\x00" { + ents = append(ents, currentEnt) + currentEnt = entities.Entity{} + continue + } + } + + lump.Data = ents + + return nil +} + +// Contents returns internal format structure Data +func (lump *EntData) Contents() []entities.Entity { + return lump.Data +} + +// ToBytes converts this lump back to raw byte Data. +// @TODO this is a very naive implementation. +func (lump *EntData) ToBytes() ([]byte, error) { + if len(lump.Data) == 0 { + return []byte{}, nil + } + + var raw string + for _, ent := range lump.Data { + raw += "{\n" + for _, v := range ent { + raw += "\"" + v.Key + "\" \"" + v.Value + "\"\n" + } + raw += "}\n" + } + raw += "\x00" + + return []byte(raw), nil +} diff --git a/lump/entdata_test.go b/lump/entdata_test.go new file mode 100644 index 0000000..f19af6f --- /dev/null +++ b/lump/entdata_test.go @@ -0,0 +1,140 @@ +package lump + +import ( + "bytes" + "github.com/galaco/bsp/lump/primitive/entities" + "reflect" + "testing" +) + +func TestEntData_Contents(t *testing.T) { + sut := EntData{ + Data: []entities.Entity{ + { + entities.KeyValue{Key: "classname", Value: "worldspawn"}, + entities.KeyValue{Key: "skyname", Value: "sky_dust"}, + }, + }, + } + + if !reflect.DeepEqual(sut.Contents(), sut.Data) { + t.Error("mismatched between expected and actual unmarshalled bytes") + } +} + +func TestEntData_FromBytes(t *testing.T) { + testCases := []struct { + name string + entdata []byte + expected []entities.Entity + }{ + { + name: "simple", + entdata: []byte(`{ +"classname" "worldspawn" +"skyname" "sky_dust" +} +` + "\x00"), + expected: []entities.Entity{ + { + entities.KeyValue{Key: "classname", Value: "worldspawn"}, + entities.KeyValue{Key: "skyname", Value: "sky_dust"}, + }, + }, + }, + { + name: "multiple entities", + entdata: []byte(`{ +"classname" "worldspawn" +"skyname" "sky_dust" +} +{ +"classname" "prop_dynamic" +"name" "bottle_01" +} +` + "\x00"), + expected: []entities.Entity{ + { + entities.KeyValue{Key: "classname", Value: "worldspawn"}, + entities.KeyValue{Key: "skyname", Value: "sky_dust"}, + }, + { + entities.KeyValue{Key: "classname", Value: "prop_dynamic"}, + entities.KeyValue{Key: "name", Value: "bottle_01"}, + }, + }, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + ent := EntData{} + err := ent.FromBytes(tc.entdata) + if err != nil { + t.Error(err) + } + if !reflect.DeepEqual(ent.Data, tc.expected) { + t.Error("mismatched between expected and actual unmarshalled bytes") + } + }) + } +} + +func TestEntData_ToBytes(t *testing.T) { + testCases := []struct { + name string + ents []entities.Entity + expected []byte + }{ + { + name: "simple", + ents: []entities.Entity{ + { + entities.KeyValue{Key: "classname", Value: "worldspawn"}, + entities.KeyValue{Key: "skyname", Value: "sky_dust"}, + }, + }, + expected: []byte(`{ +"classname" "worldspawn" +"skyname" "sky_dust" +} +` + "\x00"), + }, + { + name: "multiple entities", + ents: []entities.Entity{ + { + entities.KeyValue{Key: "classname", Value: "worldspawn"}, + entities.KeyValue{Key: "skyname", Value: "sky_dust"}, + }, + { + entities.KeyValue{Key: "classname", Value: "prop_dynamic"}, + entities.KeyValue{Key: "name", Value: "bottle_01"}, + }, + }, + expected: []byte(`{ +"classname" "worldspawn" +"skyname" "sky_dust" +} +{ +"classname" "prop_dynamic" +"name" "bottle_01" +} +` + "\x00"), + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + l := EntData{ + Data: tc.ents, + } + res, err := l.ToBytes() + if err != nil { + t.Error(err) + } + if !bytes.Equal(res, tc.expected) { + t.Errorf("expected %s, got %s", tc.expected, res) + } + }) + } +} diff --git a/lump/face.go b/lump/face.go new file mode 100644 index 0000000..479c04e --- /dev/null +++ b/lump/face.go @@ -0,0 +1,32 @@ +package lump + +import ( + primitives "github.com/galaco/bsp/lump/primitive/face" +) + +// Face is Lump 7: Face +type Face struct { + Metadata + Data []primitives.Face `json:"data"` +} + +// FromBytes imports this lump from raw byte Data +func (lump *Face) FromBytes(raw []byte) error { + data, err := unmarshallBasicLump[primitives.Face](raw) + if err != nil { + return err + } + + lump.Data = data + return nil +} + +// Contents returns internal format structure Data +func (lump *Face) Contents() []primitives.Face { + return lump.Data +} + +// ToBytes converts this lump back to raw byte Data +func (lump *Face) ToBytes() ([]byte, error) { + return marshallBasicLump(lump.Data) +} diff --git a/lump/facehdr.go b/lump/facehdr.go new file mode 100644 index 0000000..1b69873 --- /dev/null +++ b/lump/facehdr.go @@ -0,0 +1,32 @@ +package lump + +import ( + primitives "github.com/galaco/bsp/lump/primitive/face" +) + +// FaceHDR is Lump 58: FaceHDR +type FaceHDR struct { + Metadata + Data []primitives.Face `json:"data"` +} + +// FromBytes imports this lump from raw byte Data +func (lump *FaceHDR) FromBytes(raw []byte) error { + data, err := unmarshallBasicLump[primitives.Face](raw) + if err != nil { + return err + } + + lump.Data = data + return nil +} + +// Contents returns internal format structure Data +func (lump *FaceHDR) Contents() []primitives.Face { + return lump.Data +} + +// ToBytes converts this lump back to raw byte Data +func (lump *FaceHDR) ToBytes() ([]byte, error) { + return marshallBasicLump(lump.Data) +} diff --git a/lump/faceid.go b/lump/faceid.go new file mode 100644 index 0000000..1c2f67d --- /dev/null +++ b/lump/faceid.go @@ -0,0 +1,32 @@ +package lump + +import ( + primitives "github.com/galaco/bsp/lump/primitive/faceid" +) + +// FaceId is Lump 11: FaceIds +type FaceId struct { + Metadata + Data []primitives.FaceId `json:"data"` +} + +// FromBytes imports this lump from raw byte Data +func (lump *FaceId) FromBytes(raw []byte) error { + data, err := unmarshallBasicLump[primitives.FaceId](raw) + if err != nil { + return err + } + + lump.Data = data + return nil +} + +// Contents returns internal format structure Data +func (lump *FaceId) Contents() []primitives.FaceId { + return lump.Data +} + +// ToBytes converts this lump back to raw byte Data +func (lump *FaceId) ToBytes() ([]byte, error) { + return marshallBasicLump(lump.Data) +} diff --git a/lumps/faceid_test.go b/lump/faceid_test.go similarity index 78% rename from lumps/faceid_test.go rename to lump/faceid_test.go index 0665a33..942f27e 100644 --- a/lumps/faceid_test.go +++ b/lump/faceid_test.go @@ -1,4 +1,4 @@ -package lumps +package lump import ( "bytes" @@ -14,10 +14,10 @@ func TestFaceId_GetData(t *testing.T) { if err != nil { t.Error(err) } - if err := sut.Unmarshall(buf.Bytes()); err != nil { + if err := sut.FromBytes(buf.Bytes()); err != nil { t.Error(err) } - for idx, b := range sut.GetData() { + for idx, b := range sut.Contents() { if data[idx] != b.HammerFaceId { t.Error("mismatched between expected and actual unmarshalled bytes") } @@ -32,15 +32,15 @@ func TestFaceId_Marshall(t *testing.T) { if err != nil { t.Error(err) } - if err := sut.Unmarshall(buf.Bytes()); err != nil { + if err := sut.FromBytes(buf.Bytes()); err != nil { t.Error(err) } - for idx, b := range sut.data { + for idx, b := range sut.Data { if data[idx] != b.HammerFaceId { t.Error("mismatched between expected and actual unmarshalled bytes") } } - res, err := sut.Marshall() + res, err := sut.ToBytes() if err != nil { t.Errorf("unexpected error during marshalling") } @@ -51,7 +51,7 @@ func TestFaceId_Marshall(t *testing.T) { } } -func TestFaceId_Unmarshall(t *testing.T) { +func TestFaceId_FromBytes(t *testing.T) { sut := FaceId{} data := []uint16{0, 1, 2, 5, 43, 156, 146, 3, 3, 6} var buf bytes.Buffer @@ -59,10 +59,10 @@ func TestFaceId_Unmarshall(t *testing.T) { if err != nil { t.Error(err) } - if err := sut.Unmarshall(buf.Bytes()); err != nil { + if err := sut.FromBytes(buf.Bytes()); err != nil { t.Error(err) } - for idx, b := range sut.data { + for idx, b := range sut.Data { if data[idx] != b.HammerFaceId { t.Error("mismatched between expected and actual unmarshalled bytes") } diff --git a/lump/facemacrotextureinfo.go b/lump/facemacrotextureinfo.go new file mode 100644 index 0000000..02cd100 --- /dev/null +++ b/lump/facemacrotextureinfo.go @@ -0,0 +1,32 @@ +package lump + +import ( + primitives "github.com/galaco/bsp/lump/primitive/facemacrotextureinfo" +) + +// FaceMacroTextureInfo is Lump 47: FaceMacroTextureInfo +type FaceMacroTextureInfo struct { + Metadata + Data []primitives.FaceMacroTextureInfo `json:"data"` +} + +// FromBytes imports this lump from raw byte Data +func (lump *FaceMacroTextureInfo) FromBytes(raw []byte) error { + data, err := unmarshallBasicLump[primitives.FaceMacroTextureInfo](raw) + if err != nil { + return err + } + + lump.Data = data + return nil +} + +// Contents returns internal format structure Data +func (lump *FaceMacroTextureInfo) Contents() []primitives.FaceMacroTextureInfo { + return lump.Data +} + +// ToBytes converts this lump back to raw byte Data +func (lump *FaceMacroTextureInfo) ToBytes() ([]byte, error) { + return marshallBasicLump(lump.Data) +} diff --git a/lump/game.go b/lump/game.go new file mode 100644 index 0000000..2d5d50e --- /dev/null +++ b/lump/game.go @@ -0,0 +1,284 @@ +package lump + +import ( + "bytes" + "encoding/binary" + "fmt" + "strings" + "unsafe" + + primitive "github.com/galaco/bsp/lump/primitive/game" +) + +type GameGeneric interface { + SetAbsoluteFileOffset(fileOffset int) +} + +// Game is Lump 35. +// @TODO NOTE: This really needs per-game implementations to be completely useful, +// otherwise we only get staticprop Data from it +type Game struct { + Metadata + Header primitive.Header `json:"data"` + GameLumps []primitive.GenericGameLump `json:"gameLumps"` + + // absoluteFileOffset tracks the offset of the game lump into the whole BSP. + // Game lump has special rules where it contains offsets into the file, not offsets into the lump, + // so we need to know where the lump is in the file to actually use the offsets. + // This is required for read game lumps which are offset based. + absoluteFileOffset int +} + +// FromBytes imports this lump from raw byte Data +func (lump *Game) FromBytes(raw []byte) (err error) { + if len(raw) == 0 { + return nil + } + + // First reconstruct the header to be of the right size + lumpCount := binary.LittleEndian.Uint32(raw[:4]) + lump.Header.SetLumpCount(int32(lumpCount)) + + // Read header + lump.Header.GameLumps = make([]primitive.LumpDef, lumpCount) + if err := binary.Read( + bytes.NewBuffer(raw[4:4+(int(unsafe.Sizeof(primitive.LumpDef{}))*int(lumpCount))]), + binary.LittleEndian, + &lump.Header.GameLumps, + ); err != nil { + return err + } + + // Correct file offsets. + if lump.absoluteFileOffset == 0 { + return fmt.Errorf("lump offset not set. Cannot correct offsets") + } + + // Read gamelumps. + lump.GameLumps = make([]primitive.GenericGameLump, lumpCount) + for i, lumpHeader := range lump.Header.GameLumps { + offset := lump.absoluteToRelativeOffset(int(lumpHeader.FileOffset)) + lump.GameLumps[i].Data = raw[offset : offset+int(lumpHeader.FileLength)] + } + + return nil +} + +// Contents returns internal format structure Data +func (lump *Game) Contents() *Game { + return lump +} + +// ToBytes converts this lump back to raw byte Data +func (lump *Game) ToBytes() ([]byte, error) { + // Lazy way to calculate header size early. + lumpDataOffsetBaseOffset := lump.absoluteFileOffset + 4 + (int(unsafe.Sizeof(primitive.LumpDef{})) * int(lump.Header.LumpCount)) + + var bodyBuf bytes.Buffer + var old int + for i, l := range lump.GameLumps { + old = bodyBuf.Len() + lump.Header.GameLumps[i].FileOffset = int32(lumpDataOffsetBaseOffset + (bodyBuf.Len())) + if err := binary.Write(&bodyBuf, binary.LittleEndian, l.Data); err != nil { + return nil, err + } + lump.Header.GameLumps[i].FileLength = int32(bodyBuf.Len() - old) + //lump.Header.GameLumps[i].FileOffset = int32(lumpDataOffsetBaseOffset + (bodyBuf.Len() - old)) + } + + var buf bytes.Buffer + if err := binary.Write(&buf, binary.LittleEndian, lump.Header.LumpCount); err != nil { + return nil, err + } + for _, lumpHeader := range lump.Header.GameLumps { + if err := binary.Write(&buf, binary.LittleEndian, lumpHeader); err != nil { + return nil, err + } + } + buf.Write(bodyBuf.Bytes()) + + return buf.Bytes(), nil +} + +// SetAbsoluteFileOffset updates the lumps offsets to be relative to the lump, rather +// than the bsp start. +func (lump *Game) SetAbsoluteFileOffset(fileOffset int) { + lump.absoluteFileOffset = fileOffset +} + +// GetStaticPropLump returns the staticprop lump. +func (lump *Game) GetStaticPropLump() *primitive.StaticPropLump { + for i, gameLump := range lump.Header.GameLumps { + + if gameLump.Id == primitive.StaticPropLumpId { + sprpLump := lump.GameLumps[i] + + offset := 0 + + //dict + numDicts := int32(0) + err := binary.Read(bytes.NewBuffer(sprpLump.Data[offset:offset+4]), binary.LittleEndian, &numDicts) + if err != nil { + return nil + } + offset += 4 + dicts := primitive.StaticPropDictLump{ + DictEntries: numDicts, + } + dictNames := make([]string, numDicts) + for i := int32(0); i < numDicts; i++ { + t := make([]byte, 128) + err = binary.Read(bytes.NewBuffer(sprpLump.Data[offset:offset+128]), binary.LittleEndian, &t) + if err != nil { + return nil + } + dictNames[i] = strings.TrimRight(string(t), "\x00") + offset += 128 + } + dicts.Name = dictNames + + //leaf + numLeafs := int32(0) + err = binary.Read(bytes.NewBuffer(sprpLump.Data[offset:offset+4]), binary.LittleEndian, &numLeafs) + if err != nil { + return nil + } + offset += 4 + leaf := primitive.StaticPropLeafLump{ + LeafEntries: numLeafs, + } + leafs := make([]uint16, numLeafs) + err = binary.Read(bytes.NewBuffer(sprpLump.Data[offset:offset+int(2*numLeafs)]), binary.LittleEndian, &leafs) + if err != nil { + return nil + } + leaf.Leaf = leafs + offset += int(2 * numLeafs) + + //props + numProps := int32(0) + err = binary.Read(bytes.NewBuffer(sprpLump.Data[offset:offset+4]), binary.LittleEndian, &numProps) + if err != nil { + return nil + } + offset += 4 + props := make([]primitive.IStaticPropDataLump, numProps) + propLumpSize := 0 + switch gameLump.Version { + case 4: + propLumpSize = int(unsafe.Sizeof(primitive.StaticPropV4{})) * int(numProps) + vprops := make([]primitive.StaticPropV4, numProps) + err = binary.Read(bytes.NewBuffer(sprpLump.Data[offset:offset+propLumpSize]), binary.LittleEndian, &vprops) + if err != nil { + return nil + } + for idx := range vprops { + props[idx] = primitive.IStaticPropDataLump(&vprops[idx]) + } + case 5: + propLumpSize = int(unsafe.Sizeof(primitive.StaticPropV5{})) * int(numProps) + vprops := make([]primitive.StaticPropV5, numProps) + err = binary.Read(bytes.NewBuffer(sprpLump.Data[offset:offset+propLumpSize]), binary.LittleEndian, &vprops) + if err != nil { + return nil + } + for idx := range vprops { + props[idx] = primitive.IStaticPropDataLump(&vprops[idx]) + } + case 6: + propLumpSize = int(unsafe.Sizeof(primitive.StaticPropV6{})) * int(numProps) + vprops := make([]primitive.StaticPropV6, numProps) + err = binary.Read(bytes.NewBuffer(sprpLump.Data[offset:offset+propLumpSize]), binary.LittleEndian, &vprops) + if err != nil { + return nil + } + for idx := range vprops { + props[idx] = primitive.IStaticPropDataLump(&vprops[idx]) + } + case 7: + propLumpSize = int(unsafe.Sizeof(primitive.StaticPropV7{})) * int(numProps) + vprops := make([]primitive.StaticPropV7, numProps) + err = binary.Read(bytes.NewBuffer(sprpLump.Data[offset:offset+propLumpSize]), binary.LittleEndian, &vprops) + if err != nil { + return nil + } + for idx := range vprops { + props[idx] = primitive.IStaticPropDataLump(&vprops[idx]) + } + case 8: + propLumpSize = int(unsafe.Sizeof(primitive.StaticPropV8{})) * int(numProps) + vprops := make([]primitive.StaticPropV8, numProps) + err = binary.Read(bytes.NewBuffer(sprpLump.Data[offset:offset+propLumpSize]), binary.LittleEndian, &vprops) + if err != nil { + return nil + } + for idx := range vprops { + props[idx] = primitive.IStaticPropDataLump(&vprops[idx]) + } + case 9: + propLumpSize = int(unsafe.Sizeof(primitive.StaticPropV9{})) * int(numProps) + vprops := make([]primitive.StaticPropV9, numProps) + err = binary.Read(bytes.NewBuffer(sprpLump.Data[offset:offset+propLumpSize]), binary.LittleEndian, &vprops) + if err != nil { + return nil + } + for idx := range vprops { + props[idx] = primitive.IStaticPropDataLump(&vprops[idx]) + } + case 10: + // This switch is a major hackjob to avoid the need to know what game the bsp is for. + // Because Valve in all their wisdom have multiple DIFFERENT v10 formats (a true v10, + // and the MP2013 updated v6 which is REPORTED as v10 as well) we can attempt to infer + // which format it actually is. + switch { + case offset+(int(unsafe.Sizeof(primitive.StaticPropV10{}))*int(numProps)) <= len(sprpLump.Data): + // Real v10 format + propLumpSize = int(unsafe.Sizeof(primitive.StaticPropV10{})) * int(numProps) + vprops := make([]primitive.StaticPropV10, numProps) + err = binary.Read(bytes.NewBuffer(sprpLump.Data[offset:offset+propLumpSize]), binary.LittleEndian, &vprops) + if err != nil { + return nil + } + for idx := range vprops { + props[idx] = primitive.IStaticPropDataLump(&vprops[idx]) + } + case offset+(int(unsafe.Sizeof(primitive.StaticPropV10MP2013{}))*int(numProps)) <= len(sprpLump.Data): + // Fake v7* 2013MP format. + propLumpSize = int(unsafe.Sizeof(primitive.StaticPropV10MP2013{})) * int(numProps) + vprops := make([]primitive.StaticPropV10MP2013, numProps) + err = binary.Read(bytes.NewBuffer(sprpLump.Data[offset:offset+propLumpSize]), binary.LittleEndian, &vprops) + if err != nil { + return nil + } + for idx := range vprops { + props[idx] = primitive.IStaticPropDataLump(&vprops[idx]) + } + default: + panic("staticpropdata doesn't correspond to a known v10 format") + } + case 11: + vprops := make([]primitive.StaticPropV11, numProps) + err = binary.Read(bytes.NewBuffer(sprpLump.Data[offset:]), binary.LittleEndian, &vprops) + if err != nil { + return nil + } + for idx := range vprops { + props[idx] = primitive.IStaticPropDataLump(&vprops[idx]) + } + } + + return &primitive.StaticPropLump{ + DictLump: dicts, + LeafLump: leaf, + PropLumps: props, + } + } + } + + return nil +} + +// absoluteToRelativeOffset converts an absolute offset into the file into a relative offset into the lump. +func (lump *Game) absoluteToRelativeOffset(absoluteOffset int) int { + return absoluteOffset - lump.absoluteFileOffset +} diff --git a/lump/leaf.go b/lump/leaf.go new file mode 100644 index 0000000..079c242 --- /dev/null +++ b/lump/leaf.go @@ -0,0 +1,44 @@ +package lump + +import ( + "fmt" + primitives "github.com/galaco/bsp/lump/primitive/leaf" +) + +const ( + // MaxMapLeafs is the maximum number of leafs the engine can handle + MaxMapLeafs = 65536 +) + +const ( + // maxBspVersionOfV0Leaf is the last bsp revision to use the old leafv0 structure + maxBspVersionOfV0Leaf = 19 +) + +// Leaf is Lump 10: Leaf +type Leaf struct { + Metadata + Data []primitives.Leaf `json:"data"` +} + +// FromBytes imports this lump from raw byte Data +func (lump *Leaf) FromBytes(raw []byte) error { + data, err := unmarshallTaggedLump[primitives.Leaf](raw, fmt.Sprintf("v%d", lump.Version())) + if err != nil { + return err + } + + lump.Data = data + + return nil +} + +// Contents returns internal format structure Data +func (lump *Leaf) Contents() []primitives.Leaf { + return lump.Data +} + +// ToBytes converts this lump back to raw byte Data +func (lump *Leaf) ToBytes() ([]byte, error) { + return marshallTaggedLump[primitives.Leaf](lump.Data, fmt.Sprintf("v%d", lump.Version())) +} diff --git a/lumps/leaf_test.go b/lump/leaf_test.go similarity index 76% rename from lumps/leaf_test.go rename to lump/leaf_test.go index 66bb1ce..6af5993 100644 --- a/lumps/leaf_test.go +++ b/lump/leaf_test.go @@ -1,29 +1,28 @@ -package lumps +package lump import ( - primitives "github.com/galaco/bsp/primitives/leaf" "log" "testing" + + primitives "github.com/galaco/bsp/lump/primitive/leaf" ) -// Assert leaf data when read from bytes is valid -func TestLeafUnmarshall(t *testing.T) { +// Assert leaf Data when read from bytes is valid +func TestLeafFromBytes(t *testing.T) { lump := Leaf{} lump.SetVersion(20) - err := lump.Unmarshall(GetTestDataBytes()) - if err != nil { + if err := lump.FromBytes(GetTestDataBytes()); err != nil { t.Error(err) } expected := GetTestLeafData() - log.Println(lump) - actual := lump.GetData()[0] + actual := lump.Contents()[0] if actual != expected { log.Println("Expected: ") log.Println(expected) log.Println("Actual: ") log.Println(actual) - t.Errorf("Imported Leaf data mismatch.") + t.Errorf("Imported Leaf Data mismatch.") } } diff --git a/lump/leafambientindex.go b/lump/leafambientindex.go new file mode 100644 index 0000000..4a12c50 --- /dev/null +++ b/lump/leafambientindex.go @@ -0,0 +1,32 @@ +package lump + +import ( + primitives "github.com/galaco/bsp/lump/primitive/leafambientindex" +) + +// LeafAmbientIndex is Lump 52: Leaf Ambient Index +type LeafAmbientIndex struct { + Metadata + Data []primitives.LeafAmbientIndex `json:"data"` +} + +// FromBytes imports this lump from raw byte Data +func (lump *LeafAmbientIndex) FromBytes(raw []byte) error { + data, err := unmarshallBasicLump[primitives.LeafAmbientIndex](raw) + if err != nil { + return err + } + + lump.Data = data + return nil +} + +// Contents returns internal format structure Data +func (lump *LeafAmbientIndex) Contents() []primitives.LeafAmbientIndex { + return lump.Data +} + +// ToBytes converts this lump back to raw byte Data +func (lump *LeafAmbientIndex) ToBytes() ([]byte, error) { + return marshallBasicLump(lump.Data) +} diff --git a/lump/leafambientindexhdr.go b/lump/leafambientindexhdr.go new file mode 100644 index 0000000..bbd2bdd --- /dev/null +++ b/lump/leafambientindexhdr.go @@ -0,0 +1,32 @@ +package lump + +import ( + primitives "github.com/galaco/bsp/lump/primitive/leafambientindex" +) + +// LeafAmbientIndexHDR is Lump 51: Leaf Ambient Index HDR +type LeafAmbientIndexHDR struct { + Metadata + Data []primitives.LeafAmbientIndex `json:"data"` +} + +// FromBytes imports this lump from raw byte Data +func (lump *LeafAmbientIndexHDR) FromBytes(raw []byte) error { + data, err := unmarshallBasicLump[primitives.LeafAmbientIndex](raw) + if err != nil { + return err + } + + lump.Data = data + return nil +} + +// Contents returns internal format structure Data +func (lump *LeafAmbientIndexHDR) Contents() []primitives.LeafAmbientIndex { + return lump.Data +} + +// ToBytes converts this lump back to raw byte Data +func (lump *LeafAmbientIndexHDR) ToBytes() ([]byte, error) { + return marshallBasicLump(lump.Data) +} diff --git a/lump/leafambientlighting.go b/lump/leafambientlighting.go new file mode 100644 index 0000000..4b0baf2 --- /dev/null +++ b/lump/leafambientlighting.go @@ -0,0 +1,32 @@ +package lump + +import ( + primitives "github.com/galaco/bsp/lump/primitive/leafambientlighting" +) + +// LeafAmbientLighting is Lump 56: LeafAmbientLighting +type LeafAmbientLighting struct { + Metadata + Data []primitives.LeafAmbientLighting `json:"data"` +} + +// FromBytes imports this lump from raw byte Data +func (lump *LeafAmbientLighting) FromBytes(raw []byte) error { + data, err := unmarshallBasicLump[primitives.LeafAmbientLighting](raw) + if err != nil { + return err + } + + lump.Data = data + return nil +} + +// Contents returns internal format structure Data +func (lump *LeafAmbientLighting) Contents() []primitives.LeafAmbientLighting { + return lump.Data +} + +// ToBytes converts this lump back to raw byte Data +func (lump *LeafAmbientLighting) ToBytes() ([]byte, error) { + return marshallBasicLump(lump.Data) +} diff --git a/lump/leafambientlightinghdr.go b/lump/leafambientlightinghdr.go new file mode 100644 index 0000000..922cc7d --- /dev/null +++ b/lump/leafambientlightinghdr.go @@ -0,0 +1,32 @@ +package lump + +import ( + primitives "github.com/galaco/bsp/lump/primitive/leafambientlighting" +) + +// LeafAmbientLightingHDR is Lump 55: LeafAmbientLightingHDR +type LeafAmbientLightingHDR struct { + Metadata + Data []primitives.LeafAmbientLighting `json:"data"` +} + +// FromBytes imports this lump from raw byte Data +func (lump *LeafAmbientLightingHDR) FromBytes(raw []byte) error { + data, err := unmarshallBasicLump[primitives.LeafAmbientLighting](raw) + if err != nil { + return err + } + + lump.Data = data + return nil +} + +// Contents returns internal format structure Data +func (lump *LeafAmbientLightingHDR) Contents() []primitives.LeafAmbientLighting { + return lump.Data +} + +// ToBytes converts this lump back to raw byte Data +func (lump *LeafAmbientLightingHDR) ToBytes() ([]byte, error) { + return marshallBasicLump(lump.Data) +} diff --git a/lump/leafbrush.go b/lump/leafbrush.go new file mode 100644 index 0000000..8577f3b --- /dev/null +++ b/lump/leafbrush.go @@ -0,0 +1,28 @@ +package lump + +// LeafBrush is Lump 17: LeafBrush +type LeafBrush struct { + Metadata + Data []uint16 `json:"data"` // MAX_MAP_LEAFBRUSHES = 65536 +} + +// FromBytes imports this lump from raw byte Data +func (lump *LeafBrush) FromBytes(raw []byte) error { + data, err := unmarshallBasicLump[uint16](raw) + if err != nil { + return err + } + + lump.Data = data + return nil +} + +// Contents returns internal format structure Data +func (lump *LeafBrush) Contents() []uint16 { + return lump.Data +} + +// ToBytes converts this lump back to raw byte Data +func (lump *LeafBrush) ToBytes() ([]byte, error) { + return marshallBasicLump(lump.Data) +} diff --git a/lumps/leafbrush_test.go b/lump/leafbrush_test.go similarity index 78% rename from lumps/leafbrush_test.go rename to lump/leafbrush_test.go index 9cf8e06..39bfddf 100644 --- a/lumps/leafbrush_test.go +++ b/lump/leafbrush_test.go @@ -1,4 +1,4 @@ -package lumps +package lump import ( "bytes" @@ -14,10 +14,10 @@ func TestLeafBrush_GetData(t *testing.T) { if err != nil { t.Error(err) } - if err := sut.Unmarshall(buf.Bytes()); err != nil { + if err := sut.FromBytes(buf.Bytes()); err != nil { t.Error(err) } - for idx, b := range sut.GetData() { + for idx, b := range sut.Contents() { if data[idx] != b { t.Error("mismatched between expected and actual unmarshalled bytes") } @@ -32,15 +32,15 @@ func TestLeafBrush_Marshall(t *testing.T) { if err != nil { t.Error(err) } - if err := sut.Unmarshall(buf.Bytes()); err != nil { + if err := sut.FromBytes(buf.Bytes()); err != nil { t.Error(err) } - for idx, b := range sut.data { + for idx, b := range sut.Data { if data[idx] != b { t.Error("mismatched between expected and actual unmarshalled bytes") } } - res, err := sut.Marshall() + res, err := sut.ToBytes() if err != nil { t.Errorf("unexpected error during marshalling") } @@ -51,7 +51,7 @@ func TestLeafBrush_Marshall(t *testing.T) { } } -func TestLeafBrush_Unmarshall(t *testing.T) { +func TestLeafBrush_FromBytes(t *testing.T) { sut := LeafBrush{} data := []uint16{0, 1, 2, 5, 43, 156, 146, 3, 3, 6} var buf bytes.Buffer @@ -59,10 +59,10 @@ func TestLeafBrush_Unmarshall(t *testing.T) { if err != nil { t.Error(err) } - if err := sut.Unmarshall(buf.Bytes()); err != nil { + if err := sut.FromBytes(buf.Bytes()); err != nil { t.Error(err) } - for idx, b := range sut.data { + for idx, b := range sut.Data { if data[idx] != b { t.Error("mismatched between expected and actual unmarshalled bytes") } diff --git a/lump/leafface.go b/lump/leafface.go new file mode 100644 index 0000000..c797c25 --- /dev/null +++ b/lump/leafface.go @@ -0,0 +1,28 @@ +package lump + +// LeafFace is Lump 16: LeafFace +type LeafFace struct { + Metadata + Data []uint16 `json:"data"` // MAX_MAP_LEAFFACES = 65536 +} + +// FromBytes imports this lump from raw byte Data +func (lump *LeafFace) FromBytes(raw []byte) error { + data, err := unmarshallBasicLump[uint16](raw) + if err != nil { + return err + } + + lump.Data = data + return nil +} + +// Contents returns internal format structure Data +func (lump *LeafFace) Contents() []uint16 { + return lump.Data +} + +// ToBytes converts this lump back to raw byte Data +func (lump *LeafFace) ToBytes() ([]byte, error) { + return marshallBasicLump(lump.Data) +} diff --git a/lumps/leafface_test.go b/lump/leafface_test.go similarity index 78% rename from lumps/leafface_test.go rename to lump/leafface_test.go index 9d6655a..086d142 100644 --- a/lumps/leafface_test.go +++ b/lump/leafface_test.go @@ -1,4 +1,4 @@ -package lumps +package lump import ( "bytes" @@ -14,10 +14,10 @@ func TestLeafFace_GetData(t *testing.T) { if err != nil { t.Error(err) } - if err := sut.Unmarshall(buf.Bytes()); err != nil { + if err := sut.FromBytes(buf.Bytes()); err != nil { t.Error(err) } - for idx, b := range sut.GetData() { + for idx, b := range sut.Contents() { if data[idx] != b { t.Error("mismatched between expected and actual unmarshalled bytes") } @@ -32,15 +32,15 @@ func TestLeafFace_Marshall(t *testing.T) { if err != nil { t.Error(err) } - if err := sut.Unmarshall(buf.Bytes()); err != nil { + if err := sut.FromBytes(buf.Bytes()); err != nil { t.Error(err) } - for idx, b := range sut.data { + for idx, b := range sut.Data { if data[idx] != b { t.Error("mismatched between expected and actual unmarshalled bytes") } } - res, err := sut.Marshall() + res, err := sut.ToBytes() if err != nil { t.Errorf("unexpected error during marshalling") } @@ -51,7 +51,7 @@ func TestLeafFace_Marshall(t *testing.T) { } } -func TestLeafFace_Unmarshall(t *testing.T) { +func TestLeafFace_FromBytes(t *testing.T) { sut := LeafFace{} data := []uint16{0, 1, 2, 5, 43, 156, 146, 3, 3, 6} var buf bytes.Buffer @@ -59,10 +59,10 @@ func TestLeafFace_Unmarshall(t *testing.T) { if err != nil { t.Error(err) } - if err := sut.Unmarshall(buf.Bytes()); err != nil { + if err := sut.FromBytes(buf.Bytes()); err != nil { t.Error(err) } - for idx, b := range sut.data { + for idx, b := range sut.Data { if data[idx] != b { t.Error("mismatched between expected and actual unmarshalled bytes") } diff --git a/lump/leafmindisttowater.go b/lump/leafmindisttowater.go new file mode 100644 index 0000000..82bd75f --- /dev/null +++ b/lump/leafmindisttowater.go @@ -0,0 +1,28 @@ +package lump + +// LeafMinDistToWater is Lump 46: LeafMinDistToWater +type LeafMinDistToWater struct { + Metadata + Data []uint16 `json:"data"` +} + +// FromBytes imports this lump from raw byte Data +func (lump *LeafMinDistToWater) FromBytes(raw []byte) error { + data, err := unmarshallBasicLump[uint16](raw) + if err != nil { + return err + } + + lump.Data = data + return nil +} + +// Contents returns internal format structure Data +func (lump *LeafMinDistToWater) Contents() []uint16 { + return lump.Data +} + +// ToBytes converts this lump back to raw byte Data +func (lump *LeafMinDistToWater) ToBytes() ([]byte, error) { + return marshallBasicLump(lump.Data) +} diff --git a/lumps/leafmindisttowater_test.go b/lump/leafmindisttowater_test.go similarity index 78% rename from lumps/leafmindisttowater_test.go rename to lump/leafmindisttowater_test.go index 6d27e44..931d3e5 100644 --- a/lumps/leafmindisttowater_test.go +++ b/lump/leafmindisttowater_test.go @@ -1,4 +1,4 @@ -package lumps +package lump import ( "bytes" @@ -14,10 +14,10 @@ func TestLeafMinDistToWater_GetData(t *testing.T) { if err != nil { t.Error(err) } - if err := sut.Unmarshall(buf.Bytes()); err != nil { + if err := sut.FromBytes(buf.Bytes()); err != nil { t.Error(err) } - for idx, b := range sut.GetData() { + for idx, b := range sut.Contents() { if data[idx] != b { t.Error("mismatched between expected and actual unmarshalled bytes") } @@ -32,15 +32,15 @@ func TestLeafMinDistToWater_Marshall(t *testing.T) { if err != nil { t.Error(err) } - if err := sut.Unmarshall(buf.Bytes()); err != nil { + if err := sut.FromBytes(buf.Bytes()); err != nil { t.Error(err) } - for idx, b := range sut.data { + for idx, b := range sut.Data { if data[idx] != b { t.Error("mismatched between expected and actual unmarshalled bytes") } } - res, err := sut.Marshall() + res, err := sut.ToBytes() if err != nil { t.Errorf("unexpected error during marshalling") } @@ -51,7 +51,7 @@ func TestLeafMinDistToWater_Marshall(t *testing.T) { } } -func TestLeafMinDistToWater_Unmarshall(t *testing.T) { +func TestLeafMinDistToWater_FromBytes(t *testing.T) { sut := LeafMinDistToWater{} data := []uint16{0, 1, 2, 5, 43, 156, 146, 3, 3, 6} var buf bytes.Buffer @@ -59,10 +59,10 @@ func TestLeafMinDistToWater_Unmarshall(t *testing.T) { if err != nil { t.Error(err) } - if err := sut.Unmarshall(buf.Bytes()); err != nil { + if err := sut.FromBytes(buf.Bytes()); err != nil { t.Error(err) } - for idx, b := range sut.data { + for idx, b := range sut.Data { if data[idx] != b { t.Error("mismatched between expected and actual unmarshalled bytes") } diff --git a/lump/leafwaterdata.go b/lump/leafwaterdata.go new file mode 100644 index 0000000..a5d2d26 --- /dev/null +++ b/lump/leafwaterdata.go @@ -0,0 +1,32 @@ +package lump + +import ( + primitives "github.com/galaco/bsp/lump/primitive/leafwaterdata" +) + +// LeafWaterData is Lump 36: leafwaterdata +type LeafWaterData struct { + Metadata + Data []primitives.LeafWaterData `json:"data"` +} + +// FromBytes imports this lump from raw byte Data +func (lump *LeafWaterData) FromBytes(raw []byte) error { + data, err := unmarshallBasicLump[primitives.LeafWaterData](raw) + if err != nil { + return err + } + + lump.Data = data + return nil +} + +// Contents returns internal format structure Data +func (lump *LeafWaterData) Contents() []primitives.LeafWaterData { + return lump.Data +} + +// ToBytes converts this lump back to raw byte Data +func (lump *LeafWaterData) ToBytes() ([]byte, error) { + return marshallBasicLump(lump.Data) +} diff --git a/lump/lighting.go b/lump/lighting.go new file mode 100644 index 0000000..f41dd2b --- /dev/null +++ b/lump/lighting.go @@ -0,0 +1,32 @@ +package lump + +import ( + primitives "github.com/galaco/bsp/lump/primitive/common" +) + +// Lighting is Lump 8: Lighting +type Lighting struct { + Metadata + Data []primitives.ColorRGBExponent32 `json:"data"` +} + +// FromBytes imports this lump from raw byte Data +func (lump *Lighting) FromBytes(raw []byte) error { + data, err := unmarshallBasicLump[primitives.ColorRGBExponent32](raw) + if err != nil { + return err + } + + lump.Data = data + return nil +} + +// Contents returns internal format structure Data +func (lump *Lighting) Contents() []primitives.ColorRGBExponent32 { + return lump.Data +} + +// ToBytes converts this lump back to raw byte Data +func (lump *Lighting) ToBytes() ([]byte, error) { + return marshallBasicLump(lump.Data) +} diff --git a/lump/lump.go b/lump/lump.go new file mode 100644 index 0000000..7cfd06a --- /dev/null +++ b/lump/lump.go @@ -0,0 +1,143 @@ +package lump + +import ( + "bytes" + "encoding/binary" + "fmt" + "reflect" + "strings" + "unsafe" +) + +// Unimplemented is Lump n: Unimplemented lump type. +type Unimplemented = rawBytes + +// Metadata is a Helper info for a lump +type Metadata struct { + version int32 +} + +// Version Returns lump import version in bytes. +func (info *Metadata) Version() int32 { + return info.version +} + +// SetVersion sets bsp version of lump +func (info *Metadata) SetVersion(version int32) { + info.version = version +} + +// rawBytes is Lump n: rawBytes lump type +// the contents are just raw bytes, left up to the implementer to handle. +type rawBytes struct { + Metadata + Data []byte `json:"data"` +} + +// FromBytes imports this lump from raw byte Data +func (lump *rawBytes) FromBytes(raw []byte) error { + lump.Data = raw + + return nil +} + +// Contents returns internal format structure Data +func (lump *rawBytes) Contents() []byte { + return lump.Data +} + +// ToBytes converts this lump back to raw byte Data +func (lump *rawBytes) ToBytes() ([]byte, error) { + return lump.Data, nil +} + +// unmarshallBasicLump is a helper function for unmarshalling []byte to lumps that are just a single []T. +func unmarshallBasicLump[V any](raw []byte) ([]V, error) { + if len(raw) == 0 { + return nil, nil + } + + var sampleV V + v := make([]V, len(raw)/int(unsafe.Sizeof(sampleV))) + if err := binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &v); err != nil { + return nil, err + } + + return v, nil +} + +// marshallBasicLump is a helper function for marshalling lumps that are just a single []T to []byte. +func marshallBasicLump(data any) ([]byte, error) { + var buf bytes.Buffer + err := binary.Write(&buf, binary.LittleEndian, data) + return buf.Bytes(), err +} + +// unmarshallTaggedLump is a helper function for unmarshalling []byte to lumps that are just a single []T. +// It uses the "bsp" tag on struct fields to determine which fields to read. +// It is generally useful for lumps that have multiple versions and have different fields & lengths of T. +func unmarshallTaggedLump[T any](raw []byte, version string) ([]T, error) { + if len(raw) == 0 { + return nil, nil + } + + var sampleT T + + // Figure out the length of our struct for our version. + var binarylenT int + for _, field := range reflect.VisibleFields(reflect.TypeOf(sampleT)) { + // Fields that are in this version should contribute to the length. + if t := field.Tag.Get("bsp"); t == "" || t == version { + binarylenT += int(field.Type.Size()) + } + } + if len(raw)%binarylenT != 0 { + // length doesn't match exactly a multiple of our calculated struct size. + return nil, fmt.Errorf("lump length %d is not a multiple of %d", len(raw), binarylenT) + } + + // Calculate the padding size to properly read the struct. + padSize := int(unsafe.Sizeof(sampleT)) - binarylenT + + v := make([]T, len(raw)/binarylenT) + for i := range v { + b2 := append([]byte{}, raw[(binarylenT*i):(binarylenT*i)+binarylenT]...) + buf := bytes.NewBuffer(append(b2, []byte(strings.Repeat("\x00", padSize))...)) + if err := binary.Read(buf, binary.LittleEndian, &v[i]); err != nil { + return nil, err + } + } + + return v, nil +} + +// marshallTaggedLump is a helper function for marshalling lumps that are just a single []T to []byte. +// It uses the "bsp" tag on struct fields to determine which fields to write. +// It is generally useful for lumps that have multiple versions and have different fields & lengths of V. +func marshallTaggedLump[T any](data []T, version string) ([]byte, error) { + if len(data) == 0 { + return []byte{}, nil + } + + var buf bytes.Buffer + visibleFields := reflect.VisibleFields(reflect.TypeOf(data[0])) + for _, v := range data { + for _, field := range visibleFields { + rv := reflect.Indirect(reflect.ValueOf(&v)) + // Fields that are in this version should contribute to the length. + if t := field.Tag.Get("bsp"); t == "" || t == version { + switch rv.FieldByName(field.Name).Kind() { + //case reflect.Slice: + // if err := binary.Write(&buf, binary.LittleEndian, rv.FieldByName(field.Name).Elem()); err != nil { + // return nil, err + // } + default: + if err := binary.Write(&buf, binary.LittleEndian, rv.FieldByName(field.Name).Interface()); err != nil { + return nil, err + } + } + } + } + } + return buf.Bytes(), nil +} diff --git a/lumps/unimplemented_test.go b/lump/lump_test.go similarity index 58% rename from lumps/unimplemented_test.go rename to lump/lump_test.go index cf50f12..33afb7f 100644 --- a/lumps/unimplemented_test.go +++ b/lump/lump_test.go @@ -1,14 +1,16 @@ -package lumps +package lump -import "testing" +import ( + "testing" +) func TestUnimplemented_GetData(t *testing.T) { sut := Unimplemented{} data := []byte{0, 1, 2, 5, 43, 156, 146, 3, 3, 6} - if err := sut.Unmarshall(data); err != nil { + if err := sut.FromBytes(data); err != nil { t.Error(err) } - for idx, b := range sut.GetData() { + for idx, b := range sut.Contents() { if data[idx] != b { t.Error("mismatched between expected and actual unmarshalled bytes") } @@ -18,15 +20,15 @@ func TestUnimplemented_GetData(t *testing.T) { func TestUnimplemented_Marshall(t *testing.T) { sut := Unimplemented{} data := []byte{0, 1, 2, 5, 43, 156, 146, 3, 3, 6} - if err := sut.Unmarshall(data); err != nil { + if err := sut.FromBytes(data); err != nil { t.Error(err) } - for idx, b := range sut.data { + for idx, b := range sut.Data { if data[idx] != b { t.Error("mismatched between expected and actual unmarshalled bytes") } } - res, err := sut.Marshall() + res, err := sut.ToBytes() if err != nil { t.Errorf("unexpected error during marshalling") } @@ -37,15 +39,31 @@ func TestUnimplemented_Marshall(t *testing.T) { } } -func TestUnimplemented_Unmarshall(t *testing.T) { +func TestUnimplemented_FromBytes(t *testing.T) { sut := Unimplemented{} data := []byte{0, 1, 2, 5, 43, 156, 146, 3, 3, 6} - if err := sut.Unmarshall(data); err != nil { + if err := sut.FromBytes(data); err != nil { t.Error(err) } - for idx, b := range sut.data { + for idx, b := range sut.Data { if data[idx] != b { t.Error("mismatched between expected and actual unmarshalled bytes") } } } + +func TestMetadata_Version(t *testing.T) { + sut := Metadata{} + sut.SetVersion(11) + if sut.Version() != 11 { + t.Errorf("wrong version set") + } +} + +func TestMetadata_SetVersion(t *testing.T) { + sut := Metadata{} + sut.SetVersion(11) + if sut.version != 11 { + t.Errorf("wrong version set") + } +} diff --git a/lump/mapflags.go b/lump/mapflags.go new file mode 100644 index 0000000..63b953f --- /dev/null +++ b/lump/mapflags.go @@ -0,0 +1,38 @@ +package lump + +import ( + "bytes" + "encoding/binary" + + primitives "github.com/galaco/bsp/lump/primitive/mapflags" +) + +// MapFlags is Lump 59: MapFlags +type MapFlags struct { + Metadata + Data *primitives.MapFlags `json:"data"` +} + +// FromBytes imports this lump from raw byte Data +func (lump *MapFlags) FromBytes(raw []byte) error { + if len(raw) == 0 { + return nil + } + + lump.Data = &primitives.MapFlags{} + + return binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, lump.Data) +} + +// Contents returns internal format structure Data +func (lump *MapFlags) Contents() *primitives.MapFlags { + return lump.Data +} + +// ToBytes converts this lump back to raw byte Data +func (lump *MapFlags) ToBytes() ([]byte, error) { + if lump.Data == nil { + return nil, nil + } + return marshallBasicLump(lump.Data) +} diff --git a/lumps/mapflags_test.go b/lump/mapflags_test.go similarity index 72% rename from lumps/mapflags_test.go rename to lump/mapflags_test.go index c0eaeaf..9ad3afb 100644 --- a/lumps/mapflags_test.go +++ b/lump/mapflags_test.go @@ -1,10 +1,11 @@ -package lumps +package lump import ( "bytes" "encoding/binary" - "github.com/galaco/bsp/primitives/mapflags" "testing" + + "github.com/galaco/bsp/lump/primitive/mapflags" ) func TestMapFlags_GetData(t *testing.T) { @@ -18,10 +19,10 @@ func TestMapFlags_GetData(t *testing.T) { t.Error(err) } - if err := sut.Unmarshall(buf.Bytes()); err != nil { + if err := sut.FromBytes(buf.Bytes()); err != nil { t.Error(err) } - if sut.GetData().LevelFlags != data.LevelFlags { + if sut.Contents().LevelFlags != data.LevelFlags { t.Error("mismatched between expected and actual unmarshalled bytes") } } @@ -37,13 +38,13 @@ func TestMapFlags_Marshall(t *testing.T) { t.Error(err) } - if err := sut.Unmarshall(buf.Bytes()); err != nil { + if err := sut.FromBytes(buf.Bytes()); err != nil { t.Error(err) } - if sut.data.LevelFlags != data.LevelFlags { + if sut.Data.LevelFlags != data.LevelFlags { t.Error("mismatched between expected and actual unmarshalled bytes") } - res, err := sut.Marshall() + res, err := sut.ToBytes() if err != nil { t.Error(err) } @@ -55,7 +56,7 @@ func TestMapFlags_Marshall(t *testing.T) { } -func TestMapFlags_Unmarshall(t *testing.T) { +func TestMapFlags_FromBytes(t *testing.T) { sut := MapFlags{} data := mapflags.MapFlags{ LevelFlags: 523423, @@ -66,10 +67,10 @@ func TestMapFlags_Unmarshall(t *testing.T) { t.Error(err) } - if err := sut.Unmarshall(buf.Bytes()); err != nil { + if err := sut.FromBytes(buf.Bytes()); err != nil { t.Error(err) } - if sut.data.LevelFlags != data.LevelFlags { + if sut.Data.LevelFlags != data.LevelFlags { t.Error("mismatched between expected and actual unmarshalled bytes") } } diff --git a/lump/model.go b/lump/model.go new file mode 100644 index 0000000..363c5a7 --- /dev/null +++ b/lump/model.go @@ -0,0 +1,32 @@ +package lump + +import ( + primitives "github.com/galaco/bsp/lump/primitive/model" +) + +// Model is Lump 14: Model +type Model struct { + Metadata + Data []primitives.Model `json:"data"` +} + +// FromBytes imports this lump from raw byte Data +func (lump *Model) FromBytes(raw []byte) error { + data, err := unmarshallBasicLump[primitives.Model](raw) + if err != nil { + return err + } + + lump.Data = data + return nil +} + +// Contents returns internal format structure Data +func (lump *Model) Contents() []primitives.Model { + return lump.Data +} + +// ToBytes converts this lump back to raw byte Data +func (lump *Model) ToBytes() ([]byte, error) { + return marshallBasicLump(lump.Data) +} diff --git a/lump/node.go b/lump/node.go new file mode 100644 index 0000000..0f2c897 --- /dev/null +++ b/lump/node.go @@ -0,0 +1,32 @@ +package lump + +import ( + primitives "github.com/galaco/bsp/lump/primitive/node" +) + +// Node is Lump 5: Node +type Node struct { + Metadata + Data []primitives.Node `json:"data"` // MAP_MAX_NODES = 65536 +} + +// FromBytes imports this lump from raw byte Data +func (lump *Node) FromBytes(raw []byte) error { + data, err := unmarshallBasicLump[primitives.Node](raw) + if err != nil { + return err + } + + lump.Data = data + return nil +} + +// Contents returns internal format structure Data +func (lump *Node) Contents() []primitives.Node { + return lump.Data +} + +// ToBytes converts this lump back to raw byte Data +func (lump *Node) ToBytes() ([]byte, error) { + return marshallBasicLump(lump.Data) +} diff --git a/lump/occlusion.go b/lump/occlusion.go new file mode 100644 index 0000000..289c2a8 --- /dev/null +++ b/lump/occlusion.go @@ -0,0 +1,102 @@ +package lump + +import ( + "bytes" + "encoding/binary" + "unsafe" + + primitives "github.com/galaco/bsp/lump/primitive/occlusion" +) + +// Occlusion is Lump 9: Occlusion +type Occlusion struct { + Metadata + Count int32 `json:"count"` + Data []primitives.OcclusionData `json:"data"` // len(slice) = Count + PolyDataCount int32 `json:"polyDataCount"` + PolyData []primitives.OcclusionPolyData `json:"polyData"` //len(slice) = PolyDataCount + VertexIndexCount int32 `json:"VertexIndexCount"` + VertexIndices []int32 `json:"VertexIndices"` //len(slice) = VertexIndexCount +} + +// FromBytes imports this lump from raw byte Data +func (lump *Occlusion) FromBytes(raw []byte) error { + if len(raw) == 0 { + return nil + } + offset := 0 + // Data + if err := binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.Count); err != nil { + return err + } + offset += 4 + lump.Data = make([]primitives.OcclusionData, lump.Count) + if err := binary.Read(bytes.NewBuffer(raw[offset:]), binary.LittleEndian, &lump.Data); err != nil { + return err + } + offset += int(unsafe.Sizeof(primitives.OcclusionData{})) * int(lump.Count) + + // polydata + if err := binary.Read(bytes.NewBuffer(raw[offset:]), binary.LittleEndian, &lump.PolyDataCount); err != nil { + return err + } + offset += 4 + lump.PolyData = make([]primitives.OcclusionPolyData, lump.PolyDataCount) + if err := binary.Read(bytes.NewBuffer(raw[offset:]), binary.LittleEndian, &lump.PolyData); err != nil { + return err + } + offset += int(unsafe.Sizeof(primitives.OcclusionPolyData{})) * int(lump.PolyDataCount) + + // vertexdata + if err := binary.Read(bytes.NewBuffer(raw[offset:]), binary.LittleEndian, &lump.VertexIndexCount); err != nil { + return err + } + offset += 4 + lump.VertexIndices = make([]int32, lump.VertexIndexCount) + if err := binary.Read(bytes.NewBuffer(raw[offset:]), binary.LittleEndian, &lump.VertexIndices); err != nil { + return err + } + + return nil +} + +// Contents returns internal format structure Data +func (lump *Occlusion) Contents() *Occlusion { + return lump +} + +// ToBytes converts this lump back to raw byte Data +func (lump *Occlusion) ToBytes() ([]byte, error) { + var buf bytes.Buffer + + // write Data + if err := binary.Write(&buf, binary.LittleEndian, lump.Count); err != nil { + return nil, err + } + for _, data := range lump.Data { + if err := binary.Write(&buf, binary.LittleEndian, data); err != nil { + return nil, err + } + } + + // write polydata + if err := binary.Write(&buf, binary.LittleEndian, lump.PolyDataCount); err != nil { + return nil, err + } + for _, data := range lump.PolyData { + if err := binary.Write(&buf, binary.LittleEndian, data); err != nil { + return nil, err + } + } + + // write indices + if err := binary.Write(&buf, binary.LittleEndian, lump.VertexIndexCount); err != nil { + return nil, err + } + for _, data := range lump.VertexIndices { + if err := binary.Write(&buf, binary.LittleEndian, data); err != nil { + return nil, err + } + } + return buf.Bytes(), nil +} diff --git a/lump/overlay.go b/lump/overlay.go new file mode 100644 index 0000000..0579aa3 --- /dev/null +++ b/lump/overlay.go @@ -0,0 +1,33 @@ +package lump + +import ( + primitives "github.com/galaco/bsp/lump/primitive/overlay" +) + +// Overlay is Lump 45: Overlay +// Consists of an array of Overlay structs +type Overlay struct { + Metadata + Data []primitives.Overlay `json:"data"` +} + +// FromBytes imports this lump from raw byte Data +func (lump *Overlay) FromBytes(raw []byte) error { + data, err := unmarshallBasicLump[primitives.Overlay](raw) + if err != nil { + return err + } + + lump.Data = data + return nil +} + +// Contents returns internal format structure Data +func (lump *Overlay) Contents() []primitives.Overlay { + return lump.Data +} + +// ToBytes converts this lump back to raw byte Data +func (lump *Overlay) ToBytes() ([]byte, error) { + return marshallBasicLump(lump.Data) +} diff --git a/lump/overlayfade.go b/lump/overlayfade.go new file mode 100644 index 0000000..f7f031c --- /dev/null +++ b/lump/overlayfade.go @@ -0,0 +1,32 @@ +package lump + +import ( + primitives "github.com/galaco/bsp/lump/primitive/overlayfade" +) + +// OverlayFade is Lump 60: Overlayfades +type OverlayFade struct { + Metadata + Data []primitives.OverlayFade `json:"data"` +} + +// FromBytes imports this lump from raw byte Data +func (lump *OverlayFade) FromBytes(raw []byte) error { + data, err := unmarshallBasicLump[primitives.OverlayFade](raw) + if err != nil { + return err + } + + lump.Data = data + return nil +} + +// Contents returns internal format structure Data +func (lump *OverlayFade) Contents() []primitives.OverlayFade { + return lump.Data +} + +// ToBytes converts this lump back to raw byte Data +func (lump *OverlayFade) ToBytes() ([]byte, error) { + return marshallBasicLump(lump.Data) +} diff --git a/lumps/pakfile.go b/lump/pakfile.go similarity index 56% rename from lumps/pakfile.go rename to lump/pakfile.go index 84d43aa..3266b9e 100644 --- a/lumps/pakfile.go +++ b/lump/pakfile.go @@ -1,46 +1,34 @@ -package lumps +package lump import ( "archive/zip" "bytes" - "encoding/binary" - "io/ioutil" + "io" "strings" ) // Pakfile is Lump 40: Pakfile type Pakfile struct { - Generic + rawBytes zipReader *zip.Reader } -// Unmarshall Imports this lump from raw byte data -func (lump *Pakfile) Unmarshall(raw []byte) (err error) { - length := len(raw) - lump.data = raw - lump.Metadata.SetLength(length) - - b := bytes.NewReader(raw) - zipReader, err := zip.NewReader(b, int64(length)) - if err == nil { - lump.zipReader = zipReader +// FromBytes imports this lump from raw byte Data +func (lump *Pakfile) FromBytes(raw []byte) error { + lump.Data = raw + var err error + if lump.zipReader, err = zip.NewReader(bytes.NewReader(lump.Data), int64(len(raw))); err != nil { + return err } - return err + return nil } -// GetData GetData gets internal format structure data -func (lump *Pakfile) GetData() *zip.Reader { +// Contents gets internal format structure Data +func (lump *Pakfile) Contents() *zip.Reader { return lump.zipReader } -// Marshall Returns the contents of this lump as a []byte -func (lump *Pakfile) Marshall() ([]byte, error) { - var buf bytes.Buffer - err := binary.Write(&buf, binary.LittleEndian, lump.data) - return buf.Bytes(), err -} - // GetFile Get a specific file from the pak // This function does have the expectation that filenames are case-insensitive, // so in the (shouldn't happen) case of name collisions over case-insensitivity, @@ -53,7 +41,7 @@ func (lump *Pakfile) GetFile(filePath string) ([]byte, error) { if err != nil { return nil, err } - return ioutil.ReadAll(rc) + return io.ReadAll(rc) } } return []byte{}, nil diff --git a/lump/physcollide.go b/lump/physcollide.go new file mode 100644 index 0000000..c1ee502 --- /dev/null +++ b/lump/physcollide.go @@ -0,0 +1,52 @@ +package lump + +import ( + "bytes" + "encoding/binary" + + primitives "github.com/galaco/bsp/lump/primitive/physcollide" +) + +// PhysCollide is Lump 20: PhysCollide +type PhysCollide struct { + Metadata + Data []primitives.PhysCollideEntry `json:"data"` +} + +// FromBytes imports this lump from raw byte Data +func (lump *PhysCollide) FromBytes(raw []byte) error { + data, err := unmarshallBasicLump[primitives.PhysCollideEntry](raw) + if err != nil { + return err + } + + lump.Data = data + return nil +} + +// Contents returns internal format structure Data +func (lump *PhysCollide) Contents() []primitives.PhysCollideEntry { + return lump.Data +} + +// ToBytes converts this lump back to raw byte Data +func (lump *PhysCollide) ToBytes() ([]byte, error) { + var buf bytes.Buffer + for _, entry := range lump.Data { + if err := binary.Write(&buf, binary.LittleEndian, entry.ModelHeader); err != nil { + return nil, err + } + for _, solid := range entry.Solids { + if err := binary.Write(&buf, binary.LittleEndian, solid.Size); err != nil { + return nil, err + } + if err := binary.Write(&buf, binary.LittleEndian, solid.CollisionBinary); err != nil { + return nil, err + } + } + if err := binary.Write(&buf, binary.LittleEndian, []byte(entry.TextBuffer)); err != nil { + return nil, err + } + } + return buf.Bytes(), nil +} diff --git a/lump/physdisp.go b/lump/physdisp.go new file mode 100644 index 0000000..da824e7 --- /dev/null +++ b/lump/physdisp.go @@ -0,0 +1,4 @@ +package lump + +// PhysDisp is Lump 28: PhysDisp +type PhysDisp = rawBytes diff --git a/lumps/physdisp_test.go b/lump/physdisp_test.go similarity index 72% rename from lumps/physdisp_test.go rename to lump/physdisp_test.go index 637d7b1..cf4a7ff 100644 --- a/lumps/physdisp_test.go +++ b/lump/physdisp_test.go @@ -1,14 +1,14 @@ -package lumps +package lump import "testing" func TestPhysDisp_GetData(t *testing.T) { sut := PhysDisp{} data := []byte{0, 1, 2, 5, 43, 156, 146, 3, 3, 6} - if err := sut.Unmarshall(data); err != nil { + if err := sut.FromBytes(data); err != nil { t.Error(err) } - for idx, b := range sut.GetData() { + for idx, b := range sut.Contents() { if data[idx] != b { t.Error("mismatched between expected and actual unmarshalled bytes") } @@ -18,15 +18,15 @@ func TestPhysDisp_GetData(t *testing.T) { func TestPhysDisp_Marshall(t *testing.T) { sut := PhysDisp{} data := []byte{0, 1, 2, 5, 43, 156, 146, 3, 3, 6} - if err := sut.Unmarshall(data); err != nil { + if err := sut.FromBytes(data); err != nil { t.Error(err) } - for idx, b := range sut.data { + for idx, b := range sut.Data { if data[idx] != b { t.Error("mismatched between expected and actual unmarshalled bytes") } } - res, err := sut.Marshall() + res, err := sut.ToBytes() if err != nil { t.Errorf("unexpected error during marshalling") } @@ -37,13 +37,13 @@ func TestPhysDisp_Marshall(t *testing.T) { } } -func TestPhysDisp_Unmarshall(t *testing.T) { +func TestPhysDisp_FromBytes(t *testing.T) { sut := PhysDisp{} data := []byte{0, 1, 2, 5, 43, 156, 146, 3, 3, 6} - if err := sut.Unmarshall(data); err != nil { + if err := sut.FromBytes(data); err != nil { t.Error(err) } - for idx, b := range sut.data { + for idx, b := range sut.Data { if data[idx] != b { t.Error("mismatched between expected and actual unmarshalled bytes") } diff --git a/lump/planes.go b/lump/planes.go new file mode 100644 index 0000000..f4a5128 --- /dev/null +++ b/lump/planes.go @@ -0,0 +1,32 @@ +package lump + +import ( + primitives "github.com/galaco/bsp/lump/primitive/plane" +) + +// Planes is Lump 1: Planes +type Planes struct { + Metadata + Data []primitives.Plane `json:"data"` // MAP_MAX_PLANES = 65536 +} + +// FromBytes imports this lump from raw byte Data +func (lump *Planes) FromBytes(raw []byte) error { + data, err := unmarshallBasicLump[primitives.Plane](raw) + if err != nil { + return err + } + + lump.Data = data + return nil +} + +// Contents returns internal format structure Data +func (lump *Planes) Contents() []primitives.Plane { + return lump.Data +} + +// ToBytes converts this lump back to raw byte Data +func (lump *Planes) ToBytes() ([]byte, error) { + return marshallBasicLump(lump.Data) +} diff --git a/lump/primindice.go b/lump/primindice.go new file mode 100644 index 0000000..18a3196 --- /dev/null +++ b/lump/primindice.go @@ -0,0 +1,28 @@ +package lump + +// PrimIndice is Lump 39: PrimIndice +type PrimIndice struct { + Metadata + Data []uint16 `json:"data"` +} + +// FromBytes imports this lump from raw byte Data +func (lump *PrimIndice) FromBytes(raw []byte) error { + data, err := unmarshallBasicLump[uint16](raw) + if err != nil { + return err + } + + lump.Data = data + return nil +} + +// Contents returns internal format structure Data +func (lump *PrimIndice) Contents() []uint16 { + return lump.Data +} + +// ToBytes converts this lump back to raw byte Data +func (lump *PrimIndice) ToBytes() ([]byte, error) { + return marshallBasicLump(lump.Data) +} diff --git a/lumps/primindice_test.go b/lump/primindice_test.go similarity index 78% rename from lumps/primindice_test.go rename to lump/primindice_test.go index 6bd5ca7..4d36a05 100644 --- a/lumps/primindice_test.go +++ b/lump/primindice_test.go @@ -1,4 +1,4 @@ -package lumps +package lump import ( "bytes" @@ -14,10 +14,10 @@ func TestPrimIndice_GetData(t *testing.T) { if err != nil { t.Error(err) } - if err := sut.Unmarshall(buf.Bytes()); err != nil { + if err := sut.FromBytes(buf.Bytes()); err != nil { t.Error(err) } - for idx, b := range sut.GetData() { + for idx, b := range sut.Contents() { if data[idx] != b { t.Error("mismatched between expected and actual unmarshalled bytes") } @@ -32,15 +32,15 @@ func TestPrimIndice_Marshall(t *testing.T) { if err != nil { t.Error(err) } - if err := sut.Unmarshall(buf.Bytes()); err != nil { + if err := sut.FromBytes(buf.Bytes()); err != nil { t.Error(err) } - for idx, b := range sut.data { + for idx, b := range sut.Data { if data[idx] != b { t.Error("mismatched between expected and actual unmarshalled bytes") } } - res, err := sut.Marshall() + res, err := sut.ToBytes() if err != nil { t.Errorf("unexpected error during marshalling") } @@ -51,7 +51,7 @@ func TestPrimIndice_Marshall(t *testing.T) { } } -func TestPrimIndice_Unmarshall(t *testing.T) { +func TestPrimIndice_FromBytes(t *testing.T) { sut := PrimIndice{} data := []uint16{0, 1, 2, 5, 43, 156, 146, 3, 3, 6} var buf bytes.Buffer @@ -59,10 +59,10 @@ func TestPrimIndice_Unmarshall(t *testing.T) { if err != nil { t.Error(err) } - if err := sut.Unmarshall(buf.Bytes()); err != nil { + if err := sut.FromBytes(buf.Bytes()); err != nil { t.Error(err) } - for idx, b := range sut.data { + for idx, b := range sut.Data { if data[idx] != b { t.Error("mismatched between expected and actual unmarshalled bytes") } diff --git a/lump/primitive.go b/lump/primitive.go new file mode 100644 index 0000000..84f8acf --- /dev/null +++ b/lump/primitive.go @@ -0,0 +1,32 @@ +package lump + +import ( + primitives "github.com/galaco/bsp/lump/primitive/primitive" +) + +// Primitive is Lump 36: Primitive +type Primitive struct { + Metadata + Data []primitives.Primitive `json:"data"` +} + +// FromBytes imports this lump from raw byte Data +func (lump *Primitive) FromBytes(raw []byte) error { + data, err := unmarshallBasicLump[primitives.Primitive](raw) + if err != nil { + return err + } + + lump.Data = data + return nil +} + +// Contents returns internal format structure Data +func (lump *Primitive) Contents() []primitives.Primitive { + return lump.Data +} + +// ToBytes converts this lump back to raw byte Data +func (lump *Primitive) ToBytes() ([]byte, error) { + return marshallBasicLump(lump.Data) +} diff --git a/primitives/area/area.go b/lump/primitive/area/area.go similarity index 60% rename from primitives/area/area.go rename to lump/primitive/area/area.go index 45f03c3..3135eb0 100644 --- a/primitives/area/area.go +++ b/lump/primitive/area/area.go @@ -3,7 +3,7 @@ package area // Area type Area struct { // NumAreaPortals number of AreaPortals in this area - NumAreaPortals int32 + NumAreaPortals int32 `json:"numAreaPortals"` // FirstAreaPortal index of first AreaPortal - FirstAreaPortal int32 + FirstAreaPortal int32 `json:"firstAreaPortal"` } diff --git a/primitives/areaportal/areaportal.go b/lump/primitive/areaportal/areaportal.go similarity index 63% rename from primitives/areaportal/areaportal.go rename to lump/primitive/areaportal/areaportal.go index 838e9dd..bb43cb3 100644 --- a/primitives/areaportal/areaportal.go +++ b/lump/primitive/areaportal/areaportal.go @@ -4,13 +4,13 @@ package areaportal type AreaPortal struct { // PortalKey - entities have a key called portal number (and in vbsp a variable called areaportalnum) to bind // them to the area portals by comparing this value - PortalKey uint16 + PortalKey uint16 `json:"portalKey"` // OtherArea is the area this portal looks into - OtherArea uint16 + OtherArea uint16 `json:"otherArea"` // FirstClipPortalVert Portal geometry - FirstClipPortalVert uint16 + FirstClipPortalVert uint16 `json:"firstClipPortalVert"` // NumClipPortalVerts number of geometry verts - NumClipPortalVerts uint16 + NumClipPortalVerts uint16 `json:"numClipPortalVerts"` // PlaneNum - PlaneNum int32 + PlaneNum int32 `json:"planeNum"` } diff --git a/primitives/brush/brush.go b/lump/primitive/brush/brush.go similarity index 59% rename from primitives/brush/brush.go rename to lump/primitive/brush/brush.go index c1e9a09..f9e5288 100644 --- a/primitives/brush/brush.go +++ b/lump/primitive/brush/brush.go @@ -3,9 +3,9 @@ package brush // Brush type Brush struct { // FirstSide index of first side of a brush - FirstSide int32 + FirstSide int32 `json:"firstSide"` // NumSides is number of sides this brush has - NumSides int32 + NumSides int32 `json:"numSides"` // Contents - Contents int32 + Contents int32 `json:"contents"` } diff --git a/primitives/brushside/brushside.go b/lump/primitive/brushside/brushside.go similarity index 58% rename from primitives/brushside/brushside.go rename to lump/primitive/brushside/brushside.go index 3656591..2be5704 100644 --- a/primitives/brushside/brushside.go +++ b/lump/primitive/brushside/brushside.go @@ -3,11 +3,11 @@ package brushside // BrushSide type BrushSide struct { // PlaneNum is index into planes - PlaneNum uint16 + PlaneNum uint16 `json:"planeNum"` // TexInfo is index into TexInfo slice - TexInfo int16 + TexInfo int16 `json:"texInfo"` // DispInfo is index into DispInfo slice - DispInfo int16 + DispInfo int16 `json:"dispInfo"` // Bevel - Bevel int16 + Bevel int16 `json:"bevel"` } diff --git a/lump/primitive/common/common.go b/lump/primitive/common/common.go new file mode 100644 index 0000000..67c53ec --- /dev/null +++ b/lump/primitive/common/common.go @@ -0,0 +1,111 @@ +package common + +import ( + "github.com/galaco/bsp/lump/primitive/face" + "github.com/go-gl/mathgl/mgl32" +) + +// ColorRGBExponent32 is a 4 byte color representation (RGBExp) +type ColorRGBExponent32 struct { + // R red + R uint8 `json:"R"` + // G green + G uint8 `json:"G"` + // B blue + B uint8 `json:"B"` + // Exponent exponent + Exponent int8 `json:"Exponent"` +} + +// Winding +type Winding struct { + // Original + Original int32 `json:"original"` // qboolean = int32 + // NumPoints + NumPoints int32 `json:"numPoints"` + // Points + Points []mgl32.Vec3 `json:"points"` +} + +// Side +type Side struct { + // PlaneNum + PlaneNum int32 `json:"planeNum"` + // TexInfo + TexInfo int32 `json:"texInfo"` + // MapDisp + MapDisp *MapDispInfo `json:"mapDisp"` + // Winding + Winding *Winding `json:"winding"` + // Original + Original *Side `json:"original"` + // Contents + Contents int32 `json:"contents"` + // Surf + Surf int32 `json:"surf"` + // Visible + Visible int32 `json:"visible"` + // Tested + Tested int32 `json:"tested"` + // Bevel + Bevel int32 `json:"bevel"` + // Next + Next *Side `json:"next"` + // OrigIndex + OrigIndex int32 `json:"origIndex"` + // Id is side Id + Id int32 `json:"id"` + // SmoothingGroups + SmoothingGroups uint32 `json:"smoothingGroups"` + // AOverlayIds + AOverlayIds []int32 `json:"aOverlayIds"` + // AWaterOverlayIds + AWaterOverlayIds []int32 `json:"aWaterOverlayIds"` + // DynamicShadowsEnabled are dynamic shadows enabled for this side + DynamicShadowsEnabled bool `json:"dynamicShadowsEnabled"` +} + +// MapDispInfo +type MapDispInfo struct { + // Face + Face face.Face `json:"face"` + // EntityNum + EntityNum int32 `json:"entityNum"` + // Power is power of this displacement (normally 2-4) + Power int32 `json:"power"` + // MinTess + MinTess int32 `json:"minTess"` + // SmoothingAngle + SmoothingAngle float32 `json:"smoothingAngle"` + // UAxis + UAxis mgl32.Vec3 `json:"uAxis"` + // VAxis + VAxis mgl32.Vec3 `json:"vAxis"` + // StartPosition + StartPosition mgl32.Vec3 `json:"startPosition"` + // AlphaValues + AlphaValues []float32 `json:"alphaValues"` + // MaxDispDist + MaxDispDist float32 `json:"maxDispDist"` + // DispDists + DispDists []float32 `json:"dispDists"` + // VectorDisps + VectorDisps []mgl32.Vec3 `json:"vectorDisps"` + // VectorOffsets + VectorOffsets []mgl32.Vec3 `json:"vectorOffsets"` + // Contents + Contents int32 `json:"contents"` + // BrushSideID + BrushSideID int32 `json:"brushSideID"` + // Flags + Flags int32 `json:"flags"` + + // @TODO Figure out what this old C++ ifdef was for. + // #ifdef VSVMFIO + // Elevation float32 + // OffsetNormals []Vector + // #endif +} + +// CompressedLightCube +type CompressedLightCube = [6]ColorRGBExponent32 diff --git a/primitives/cubemap/cubemap.go b/lump/primitive/cubemap/cubemap.go similarity index 68% rename from primitives/cubemap/cubemap.go rename to lump/primitive/cubemap/cubemap.go index 1dd988c..b36b638 100644 --- a/primitives/cubemap/cubemap.go +++ b/lump/primitive/cubemap/cubemap.go @@ -7,9 +7,9 @@ import ( // CubemapSample type CubemapSample struct { // Origin is sample location - Origin mgl32.Vec3 + Origin mgl32.Vec3 `json:"origin"` // Size - Size byte + Size byte `json:"size"` // AlignmentPadding - probably unused, likely exists for ensure 4byte alignment during read/write. - AlignmentPadding [3]byte + AlignmentPadding [3]byte `json:"alignmentPadding"` } diff --git a/lump/primitive/dispinfo/dispinfo.go b/lump/primitive/dispinfo/dispinfo.go new file mode 100644 index 0000000..002a29e --- /dev/null +++ b/lump/primitive/dispinfo/dispinfo.go @@ -0,0 +1,69 @@ +package dispinfo + +import ( + "github.com/go-gl/mathgl/mgl32" +) + +// MaxDispCornerNeighbours maximum number of adjoining neigherbours per corner. +const MaxDispCornerNeighbours = 4 + +// DispInfo +type DispInfo struct { + // StartPosition + StartPosition mgl32.Vec3 `json:"startPosition"` + // DispVertStart + DispVertStart int32 `json:"dispVertStart"` + // DispTriStart + DispTriStart int32 `json:"dispTriStart"` + // Power + Power int32 `json:"power"` + // MinTess + MinTess int32 `json:"minTess"` + // SmoothingAngle + SmoothingAngle float32 `json:"smoothingAngle"` + // Contents + Contents int32 `json:"contents"` + // MapFace + MapFace uint16 `json:"mapFace"` + // Unknown1 are unknown bytes. + // @TODO figure these out. + Unknown1 [2]byte `json:"unknown1"` + // LightmapAlphaStart + LightmapAlphaStart int32 `json:"lightmapAlphaStart"` + // LightmapSampleStartPosition + LightmapSampleStartPosition int32 `json:"lightmapSampleStartPosition"` + // Unknown2 contains bytes with unknown purpose and representation. + // @TODO This should be updated as purpose id discovered + Unknown2 [32]uint32 `json:"unknown2"` + //EdgeNeighbors [4]DispNeighbor + //CornerNeighbors [4]DispCornerNeighbors + //AllowedVerts [8]uint32 + +} + +// DispNeighbor +type DispNeighbor struct { + // SubNeighbours + SubNeighbours [2]DispSubNeighbor `json:"subNeighbours"` +} + +// DispSubNeighbor +type DispSubNeighbor struct { + // Index + // 0xFFFF if no neighbor + Index uint16 `json:"index"` + // NeighborOrientation + NeighborOrientation uint8 `json:"neighborOrientation"` + // Span + Span uint8 `json:"span"` + // NeighborSpan + NeighborSpan uint8 `json:"neighborSpan"` +} + +// DispCornerNeighbors +type DispCornerNeighbors struct { + // Neighbors + Neighbors [MaxDispCornerNeighbours]uint16 `json:"neighbors"` + // NumNeighbors is number of neighbours + NumNeighbors uint8 `json:"numNeighbors"` +} diff --git a/primitives/disptris/disptris.go b/lump/primitive/disptris/disptris.go similarity index 62% rename from primitives/disptris/disptris.go rename to lump/primitive/disptris/disptris.go index fcc852e..7642514 100644 --- a/primitives/disptris/disptris.go +++ b/lump/primitive/disptris/disptris.go @@ -3,5 +3,5 @@ package disptris // DispTri type DispTri struct { // Tags - Tags uint16 + Tags uint16 `json:"tags,omitempty"` } diff --git a/primitives/dispvert/dispvert.go b/lump/primitive/dispvert/dispvert.go similarity index 84% rename from primitives/dispvert/dispvert.go rename to lump/primitive/dispvert/dispvert.go index ea6958a..67cee26 100644 --- a/primitives/dispvert/dispvert.go +++ b/lump/primitive/dispvert/dispvert.go @@ -8,9 +8,9 @@ import ( // required to compute the correct position of the displacement vertex after transformation. type DispVert struct { // Vec is direction of vertex from its default computed position - Vec mgl32.Vec3 + Vec mgl32.Vec3 `json:"vec"` // Dist is distance of vertex from its default computed position - Dist float32 + Dist float32 `json:"dist"` // Alpha is the alpha value of this vertex (normally used for blend materials) - Alpha float32 + Alpha float32 `json:"alpha"` } diff --git a/lump/primitive/entities/entities.go b/lump/primitive/entities/entities.go new file mode 100644 index 0000000..28932b7 --- /dev/null +++ b/lump/primitive/entities/entities.go @@ -0,0 +1,8 @@ +package entities + +type Entity []KeyValue + +type KeyValue struct { + Key string `json:"key"` + Value string `json:"value"` +} diff --git a/lump/primitive/face/face.go b/lump/primitive/face/face.go new file mode 100644 index 0000000..cc233c7 --- /dev/null +++ b/lump/primitive/face/face.go @@ -0,0 +1,39 @@ +package face + +// Face +type Face struct { + // Planenum + Planenum uint16 `json:"planenum"` + // Side + Side byte `json:"side"` + // OnNode + OnNode byte `json:"onNode"` + // FirstEdge is index into the edges lump data + FirstEdge int32 `json:"firstEdge"` + // NumEdges is the number of edges that make up this face + NumEdges int16 `json:"numEdges"` + // TexInfo is index into texinfos lump + TexInfo int16 `json:"texInfo"` + // DispInfo is index into dispinfos lump + DispInfo int16 `json:"dispInfo"` + // SurfaceFogVolumeID + SurfaceFogVolumeID int16 `json:"surfaceFogVolumeID"` + // Styles + Styles [4]byte `json:"styles"` + // Lightofs is offset into lighting lump (-1 if unlit) + Lightofs int32 `json:"lightofs"` + // Area is total size of this side (units^2) + Area float32 `json:"area"` + // LightmapTextureMinsInLuxels + LightmapTextureMinsInLuxels [2]int32 `json:"lightmapTextureMinsInLuxels"` + // LightmapTextureSizeInLuxels is XY size of lightmap data + LightmapTextureSizeInLuxels [2]int32 `json:"lightmapTextureSizeInLuxels"` + // OrigFace + OrigFace int32 `json:"origFace"` + // NumPrims + NumPrims uint16 `json:"numPrims"` + // FirstPrimID + FirstPrimID uint16 `json:"firstPrimID"` + // SmoothingGroups + SmoothingGroups uint32 `json:"smoothingGroups"` +} diff --git a/primitives/faceid/faceid.go b/lump/primitive/faceid/faceid.go similarity index 60% rename from primitives/faceid/faceid.go rename to lump/primitive/faceid/faceid.go index a100acd..23dda9d 100644 --- a/primitives/faceid/faceid.go +++ b/lump/primitive/faceid/faceid.go @@ -3,5 +3,5 @@ package faceid // FaceId type FaceId struct { // HammerFaceId - HammerFaceId uint16 + HammerFaceId uint16 `json:"hammerFaceId"` } diff --git a/primitives/facemacrotextureinfo/facemacrotextureinfo.go b/lump/primitive/facemacrotextureinfo/facemacrotextureinfo.go similarity index 67% rename from primitives/facemacrotextureinfo/facemacrotextureinfo.go rename to lump/primitive/facemacrotextureinfo/facemacrotextureinfo.go index 92f4935..f4076d8 100644 --- a/primitives/facemacrotextureinfo/facemacrotextureinfo.go +++ b/lump/primitive/facemacrotextureinfo/facemacrotextureinfo.go @@ -3,5 +3,5 @@ package facemacrotextureinfo // FaceMacroTextureInfo type FaceMacroTextureInfo struct { // MacroTextureNameId - MacroTextureNameId uint16 + MacroTextureNameId uint16 `json:"macroTextureNameId"` } diff --git a/primitives/game/game.go b/lump/primitive/game/game.go similarity index 74% rename from primitives/game/game.go rename to lump/primitive/game/game.go index abaedf1..1bae469 100644 --- a/primitives/game/game.go +++ b/lump/primitive/game/game.go @@ -6,10 +6,10 @@ const StaticPropLumpId = 1936749168 // Header type Header struct { // LumpCount is the number of data lumps contained in the game lump - LumpCount int32 + LumpCount int32 `json:"lumpCount"` // GameLumps contains location and metadata about contained lumps // Slice length must equal lumpCount. Validation to be added - GameLumps []LumpDef + GameLumps []LumpDef `json:"gameLumps"` } // SetLumpCount set number of lumps @@ -23,21 +23,19 @@ func (header *Header) SetLumpCount(num int32) *Header { // lump type LumpDef struct { // Id is lump id - Id int32 + Id int32 `json:"id"` // Flags is lump flags - Flags uint16 + Flags uint16 `json:"flags"` // Versionis lump version - Version uint16 + Version uint16 `json:"version"` // FileOffset is absolute offset into whole bsp - FileOffset int32 + FileOffset int32 `json:"fileOffset"` // FileLength is length of lump - FileLength int32 + FileLength int32 `json:"fileLength"` } // GenericGameLump represents a game lump with unknown/unmappable data type GenericGameLump struct { - // Length is length in bytes - Length int32 - // Datais byte representation of lump data - Data []byte + // Data is byte representation of lump data + Data []byte `json:"data"` } diff --git a/primitives/game/game_test.go b/lump/primitive/game/game_test.go similarity index 100% rename from primitives/game/game_test.go rename to lump/primitive/game/game_test.go diff --git a/primitives/game/staticprop.go b/lump/primitive/game/staticprop.go similarity index 100% rename from primitives/game/staticprop.go rename to lump/primitive/game/staticprop.go diff --git a/lump/primitive/game/staticpropsv10MP2013_test.go b/lump/primitive/game/staticpropsv10MP2013_test.go new file mode 100644 index 0000000..50b33b3 --- /dev/null +++ b/lump/primitive/game/staticpropsv10MP2013_test.go @@ -0,0 +1,202 @@ +package game + +import ( + "testing" + + "github.com/go-gl/mathgl/mgl32" +) + +func TestStaticPropV10MP2013_GetAngles(t *testing.T) { + sut := getStaticPropV10MP2013() + if sut.GetAngles().X() != 2 && + sut.GetAngles().Y() != 5 && + sut.GetAngles().Z() != 8 { + t.Error("unexpected value for angles property") + } +} + +func TestStaticPropV10MP2013_GetDiffuseModulation(t *testing.T) { + sut := getStaticPropV10MP2013() + if sut.GetDiffuseModulation() != 0 { + t.Error("unexpected value for diffuseModulation property") + } + +} + +func TestStaticPropV10MP2013_GetDisableXBox360(t *testing.T) { + sut := getStaticPropV10MP2013() + if sut.GetDisableXBox360() != false { + t.Error("unexpected value for unknown property") + } + +} + +func TestStaticPropV10MP2013_GetFadeMaxDist(t *testing.T) { + sut := getStaticPropV10MP2013() + if sut.GetFadeMaxDist() != 256 { + t.Error("unexpected value for fadeMaxDist property") + } + +} + +func TestStaticPropV10MP2013_GetFadeMinDist(t *testing.T) { + sut := getStaticPropV10MP2013() + if sut.GetFadeMinDist() != 84.5 { + t.Error("unexpected value for fadeMinDist property") + } + +} + +func TestStaticPropV10MP2013_GetFirstLeaf(t *testing.T) { + sut := getStaticPropV10MP2013() + if sut.GetFirstLeaf() != 21 { + t.Error("unexpected value for firstLeaf property") + } + +} + +func TestStaticPropV10MP2013_GetFlags(t *testing.T) { + sut := getStaticPropV10MP2013() + if sut.GetFlags() != 0 { + t.Error("unexpected value for flags property") + } + +} + +func TestStaticPropV10MP2013_GetForcedFadeScale(t *testing.T) { + sut := getStaticPropV10MP2013() + if sut.GetForcedFadeScale() != 65 { + t.Error("unexpected value for forcedFadeScale property") + } + +} + +func TestStaticPropV10MP2013_GetLeafCount(t *testing.T) { + sut := getStaticPropV10MP2013() + if sut.GetLeafCount() != 65 { + t.Error("unexpected value for leafCount property") + } + +} + +func TestStaticPropV10MP2013_GetLightingOrigin(t *testing.T) { + sut := getStaticPropV10MP2013() + if sut.GetLightingOrigin().X() != 32 && + sut.GetLightingOrigin().Y() != 64 && + sut.GetLightingOrigin().Z() != 128 { + t.Error("unexpected value for angles property") + } +} + +func TestStaticPropV10MP2013_GetMaxCPULevel(t *testing.T) { + sut := getStaticPropV10MP2013() + if sut.GetMaxCPULevel() != 0 { + t.Error("unexpected value for maxCPULevel property") + } + +} + +func TestStaticPropV10MP2013_GetMaxDXLevel(t *testing.T) { + sut := getStaticPropV10MP2013() + if sut.GetMaxDXLevel() != 0 { + t.Error("unexpected value for maxDXLevel property") + } + +} + +func TestStaticPropV10MP2013_GetMaxGPULevel(t *testing.T) { + sut := getStaticPropV10MP2013() + if sut.GetMaxGPULevel() != 0 { + t.Error("unexpected value for maxGPULevel property") + } + +} + +func TestStaticPropV10MP2013_GetMinCPULevel(t *testing.T) { + sut := getStaticPropV10MP2013() + if sut.GetMinCPULevel() != 0 { + t.Error("unexpected value for minCPULevel property") + } + +} + +func TestStaticPropV10MP2013_GetMinDXLevel(t *testing.T) { + sut := getStaticPropV10MP2013() + if sut.GetMinDXLevel() != 0 { + t.Error("unexpected value for minDXLevel property") + } + +} + +func TestStaticPropV10MP2013_GetMinGPULevel(t *testing.T) { + sut := getStaticPropV10MP2013() + if sut.GetMinGPULevel() != 0 { + t.Error("unexpected value for minGPULevel property") + } + +} + +func TestStaticPropV10MP2013_GetOrigin(t *testing.T) { + sut := getStaticPropV10MP2013() + + if sut.GetOrigin().X() != 1 && + sut.GetOrigin().Y() != 3 && + sut.GetOrigin().Z() != 6 { + t.Error("unexpected value for origin property") + } +} + +func TestStaticPropV10MP2013_GetPropType(t *testing.T) { + sut := getStaticPropV10MP2013() + if sut.GetPropType() != 11 { + t.Error("unexpected value for propType property") + } + +} + +func TestStaticPropV10MP2013_GetSkin(t *testing.T) { + sut := getStaticPropV10MP2013() + if sut.GetSkin() != 2 { + t.Error("unexpected value for skin property") + } + +} + +func TestStaticPropV10MP2013_GetSolid(t *testing.T) { + sut := getStaticPropV10MP2013() + if sut.GetSolid() != 1 { + t.Error("unexpected value for solid property") + } + +} + +func TestStaticPropV10MP2013_GetUniformScale(t *testing.T) { + sut := getStaticPropV10MP2013() + if sut.GetUniformScale() != 1 { + t.Error("unexpected value for uniformScale property") + } +} + +func TestStaticPropV10MP2013_GetUnknown(t *testing.T) { + sut := getStaticPropV10MP2013() + if sut.GetUnknown() != 0 { + t.Error("unexpected value for unknown property") + } +} + +func getStaticPropV10MP2013() *StaticPropV10MP2013 { + return &StaticPropV10MP2013{ + Origin: mgl32.Vec3{1, 3, 6}, + Angles: mgl32.Vec3{2, 5, 8}, + PropType: 11, + FirstLeaf: 21, + LeafCount: 65, + Solid: 1, + Flags: 85, + Skin: 2, + FadeMinDist: 84.5, + FadeMaxDist: 256, + LightingOrigin: mgl32.Vec3{32, 64, 128}, + ForcedFadeScale: 65, + } +} diff --git a/primitives/game/staticpropv10.go b/lump/primitive/game/staticpropv10.go similarity index 100% rename from primitives/game/staticpropv10.go rename to lump/primitive/game/staticpropv10.go diff --git a/primitives/game/staticpropv10MP2013.go b/lump/primitive/game/staticpropv10MP2013.go similarity index 100% rename from primitives/game/staticpropv10MP2013.go rename to lump/primitive/game/staticpropv10MP2013.go diff --git a/primitives/game/staticpropv10_test.go b/lump/primitive/game/staticpropv10_test.go similarity index 99% rename from primitives/game/staticpropv10_test.go rename to lump/primitive/game/staticpropv10_test.go index ab699a3..51f6d1a 100644 --- a/primitives/game/staticpropv10_test.go +++ b/lump/primitive/game/staticpropv10_test.go @@ -1,8 +1,9 @@ package game import ( - "github.com/go-gl/mathgl/mgl32" "testing" + + "github.com/go-gl/mathgl/mgl32" ) func TestStaticPropV10_GetAngles(t *testing.T) { diff --git a/primitives/game/staticpropv11.go b/lump/primitive/game/staticpropv11.go similarity index 100% rename from primitives/game/staticpropv11.go rename to lump/primitive/game/staticpropv11.go diff --git a/primitives/game/staticpropv11_test.go b/lump/primitive/game/staticpropv11_test.go similarity index 99% rename from primitives/game/staticpropv11_test.go rename to lump/primitive/game/staticpropv11_test.go index 8e93179..af521a9 100644 --- a/primitives/game/staticpropv11_test.go +++ b/lump/primitive/game/staticpropv11_test.go @@ -1,8 +1,9 @@ package game import ( - "github.com/go-gl/mathgl/mgl32" "testing" + + "github.com/go-gl/mathgl/mgl32" ) func TestStaticPropV11_GetAngles(t *testing.T) { diff --git a/primitives/game/staticpropv4.go b/lump/primitive/game/staticpropv4.go similarity index 100% rename from primitives/game/staticpropv4.go rename to lump/primitive/game/staticpropv4.go diff --git a/primitives/game/staticpropv4_test.go b/lump/primitive/game/staticpropv4_test.go similarity index 99% rename from primitives/game/staticpropv4_test.go rename to lump/primitive/game/staticpropv4_test.go index 4740961..ed45755 100644 --- a/primitives/game/staticpropv4_test.go +++ b/lump/primitive/game/staticpropv4_test.go @@ -1,8 +1,9 @@ package game import ( - "github.com/go-gl/mathgl/mgl32" "testing" + + "github.com/go-gl/mathgl/mgl32" ) func TestStaticPropV4_GetAngles(t *testing.T) { diff --git a/primitives/game/staticpropv5.go b/lump/primitive/game/staticpropv5.go similarity index 100% rename from primitives/game/staticpropv5.go rename to lump/primitive/game/staticpropv5.go diff --git a/primitives/game/staticpropv5_test.go b/lump/primitive/game/staticpropv5_test.go similarity index 99% rename from primitives/game/staticpropv5_test.go rename to lump/primitive/game/staticpropv5_test.go index b458405..c2377da 100644 --- a/primitives/game/staticpropv5_test.go +++ b/lump/primitive/game/staticpropv5_test.go @@ -1,8 +1,9 @@ package game import ( - "github.com/go-gl/mathgl/mgl32" "testing" + + "github.com/go-gl/mathgl/mgl32" ) func TestStaticPropV5_GetAngles(t *testing.T) { diff --git a/primitives/game/staticpropv6.go b/lump/primitive/game/staticpropv6.go similarity index 100% rename from primitives/game/staticpropv6.go rename to lump/primitive/game/staticpropv6.go diff --git a/primitives/game/staticpropv6_test.go b/lump/primitive/game/staticpropv6_test.go similarity index 99% rename from primitives/game/staticpropv6_test.go rename to lump/primitive/game/staticpropv6_test.go index bf5859a..88a14e5 100644 --- a/primitives/game/staticpropv6_test.go +++ b/lump/primitive/game/staticpropv6_test.go @@ -1,8 +1,9 @@ package game import ( - "github.com/go-gl/mathgl/mgl32" "testing" + + "github.com/go-gl/mathgl/mgl32" ) func TestStaticPropV6_GetAngles(t *testing.T) { diff --git a/primitives/game/staticpropv7.go b/lump/primitive/game/staticpropv7.go similarity index 100% rename from primitives/game/staticpropv7.go rename to lump/primitive/game/staticpropv7.go diff --git a/primitives/game/staticpropv7_test.go b/lump/primitive/game/staticpropv7_test.go similarity index 99% rename from primitives/game/staticpropv7_test.go rename to lump/primitive/game/staticpropv7_test.go index 28073a0..97700aa 100644 --- a/primitives/game/staticpropv7_test.go +++ b/lump/primitive/game/staticpropv7_test.go @@ -1,8 +1,9 @@ package game import ( - "github.com/go-gl/mathgl/mgl32" "testing" + + "github.com/go-gl/mathgl/mgl32" ) func TestStaticPropV7_GetAngles(t *testing.T) { diff --git a/primitives/game/staticpropv8.go b/lump/primitive/game/staticpropv8.go similarity index 100% rename from primitives/game/staticpropv8.go rename to lump/primitive/game/staticpropv8.go diff --git a/primitives/game/staticpropv8_test.go b/lump/primitive/game/staticpropv8_test.go similarity index 99% rename from primitives/game/staticpropv8_test.go rename to lump/primitive/game/staticpropv8_test.go index 904e836..b3387c3 100644 --- a/primitives/game/staticpropv8_test.go +++ b/lump/primitive/game/staticpropv8_test.go @@ -1,8 +1,9 @@ package game import ( - "github.com/go-gl/mathgl/mgl32" "testing" + + "github.com/go-gl/mathgl/mgl32" ) func TestStaticPropV8_GetAngles(t *testing.T) { diff --git a/primitives/game/staticpropv9.go b/lump/primitive/game/staticpropv9.go similarity index 100% rename from primitives/game/staticpropv9.go rename to lump/primitive/game/staticpropv9.go diff --git a/primitives/game/staticpropv9_test.go b/lump/primitive/game/staticpropv9_test.go similarity index 99% rename from primitives/game/staticpropv9_test.go rename to lump/primitive/game/staticpropv9_test.go index 5c13537..3d6e7d6 100644 --- a/primitives/game/staticpropv9_test.go +++ b/lump/primitive/game/staticpropv9_test.go @@ -1,8 +1,9 @@ package game import ( - "github.com/go-gl/mathgl/mgl32" "testing" + + "github.com/go-gl/mathgl/mgl32" ) func TestStaticPropV9_GetAngles(t *testing.T) { diff --git a/primitives/leaf/flags.go b/lump/primitive/leaf/flags.go similarity index 100% rename from primitives/leaf/flags.go rename to lump/primitive/leaf/flags.go diff --git a/primitives/leaf/leaf.go b/lump/primitive/leaf/leaf.go similarity index 58% rename from primitives/leaf/leaf.go rename to lump/primitive/leaf/leaf.go index acd5e6e..67a2f44 100644 --- a/primitives/leaf/leaf.go +++ b/lump/primitive/leaf/leaf.go @@ -1,7 +1,7 @@ package leaf import ( - "github.com/galaco/bsp/primitives/common" + "github.com/galaco/bsp/lump/primitive/common" ) const bitmaskLower9 = 0x1FF // 511 (2^9 - 1) @@ -11,50 +11,51 @@ const bitmaskLower7 = 0x7F // 127 (2^7 - 1) // faces (or entities) within its bounds type Leaf struct { // Contents - Contents int32 + Contents int32 `json:"contents"` // Cluster that this leaf is a part of - Cluster int16 + Cluster int16 `json:"cluster"` // BitField is a C Union of char Name || Area:9 && Flags:7 - BitField int16 + BitField int16 `json:"bitField"` // Mins is this leafs bounding volumes minimum - Mins [3]int16 + Mins [3]int16 `json:"mins"` // Maxs is this leafs bounding volumes maximum - Maxs [3]int16 + Maxs [3]int16 `json:"maxs"` // FirstLeafFace index into LeafFaces lump data - FirstLeafFace uint16 + FirstLeafFace uint16 `json:"firstLeafFace"` // NumLeafFaces is number of LeafFaces in this Leaf - NumLeafFaces uint16 + NumLeafFaces uint16 `json:"numLeafFaces"` // FirstLeafBrush is index into LeafBrushes lump data - FirstLeafBrush uint16 + FirstLeafBrush uint16 `json:"firstLeafBrush"` // NumLeafBrushes is number of LeafBrushes in this Leaf - NumLeafBrushes uint16 + NumLeafBrushes uint16 `json:"numLeafBrushes"` // LeafWaterDataID - LeafWaterDataID int16 + LeafWaterDataID int16 `json:"leafWaterDataID"` // LightSample - LightSample common.CompressedLightCube + LightSample common.CompressedLightCube `json:"lightSample" bsp:"v19"` - _ [2]byte + // @TODO: Unknown1 + Unknown1 [2]byte `json:"unknown1"` } // Area returns area (first 9 bits) func (b *Leaf) Area() int16 { - return int16((b.BitField) & bitmaskLower7) + return (b.BitField) & bitmaskLower7 } // SetArea sets area (first 9 bits) func (b *Leaf) SetArea(area int16) { v := b.BitField - b.BitField = int16((v & bitmaskLower9) | (area)) + b.BitField = (v & bitmaskLower9) | (area) } // Flags returns flags (second 7 bits) func (b *Leaf) Flags() int16 { - return int16((b.BitField >> 9) & bitmaskLower9) + return (b.BitField >> 9) & bitmaskLower9 } // SetFlags sets flags (second 7 bits) func (b *Leaf) SetFlags(flags int16) { v := b.BitField - b.BitField = int16((v & bitmaskLower7) | (int16(flags) << 9)) + b.BitField = (v & bitmaskLower7) | (flags << 9) } diff --git a/primitives/leaf/leaf_test.go b/lump/primitive/leaf/leaf_test.go similarity index 100% rename from primitives/leaf/leaf_test.go rename to lump/primitive/leaf/leaf_test.go diff --git a/primitives/leafambientindex/leafambientindex.go b/lump/primitive/leafambientindex/leafambientindex.go similarity index 53% rename from primitives/leafambientindex/leafambientindex.go rename to lump/primitive/leafambientindex/leafambientindex.go index a55c9a6..d64cc6c 100644 --- a/primitives/leafambientindex/leafambientindex.go +++ b/lump/primitive/leafambientindex/leafambientindex.go @@ -3,7 +3,7 @@ package leafambientindex // LeafAmbientIndex type LeafAmbientIndex struct { // AmbientSampleCount - AmbientSampleCount uint16 + AmbientSampleCount uint16 `json:"ambientSampleCount"` // FirstAmbientSample - FirstAmbientSample uint16 + FirstAmbientSample uint16 `json:"firstAmbientSample"` } diff --git a/lump/primitive/leafambientlighting/leafambientlighting.go b/lump/primitive/leafambientlighting/leafambientlighting.go new file mode 100644 index 0000000..ba710d9 --- /dev/null +++ b/lump/primitive/leafambientlighting/leafambientlighting.go @@ -0,0 +1,19 @@ +package leafambientlighting + +import ( + primitives "github.com/galaco/bsp/lump/primitive/common" +) + +// LeafAmbientLighting +type LeafAmbientLighting struct { + // Cube + Cube primitives.CompressedLightCube `json:"cube"` + // X x + X byte `json:"x"` + // Y y + Y byte `json:"y"` + // Z z + Z byte `json:"z"` + // Pad is padding to 4 bytes (any other purpose unknown). + Pad byte +} diff --git a/primitives/leafwaterdata/leafwaterdata.go b/lump/primitive/leafwaterdata/leafwaterdata.go similarity index 60% rename from primitives/leafwaterdata/leafwaterdata.go rename to lump/primitive/leafwaterdata/leafwaterdata.go index f3d2d84..141daec 100644 --- a/primitives/leafwaterdata/leafwaterdata.go +++ b/lump/primitive/leafwaterdata/leafwaterdata.go @@ -3,10 +3,10 @@ package leafwaterdata // LeafWaterData type LeafWaterData struct { // SurfaceZ - SurfaceZ float32 + SurfaceZ float32 `json:"surfaceZ"` // MinZ - MinZ float32 + MinZ float32 `json:"minZ"` // SurfaceTexInfoID - SurfaceTexInfoID int16 + SurfaceTexInfoID int16 `json:"surfaceTexInfoID"` _ int16 // Because struct is 4byte aligned } diff --git a/primitives/mapflags/mapflags.go b/lump/primitive/mapflags/mapflags.go similarity index 75% rename from primitives/mapflags/mapflags.go rename to lump/primitive/mapflags/mapflags.go index 55d6e66..fc9c390 100644 --- a/primitives/mapflags/mapflags.go +++ b/lump/primitive/mapflags/mapflags.go @@ -3,5 +3,5 @@ package mapflags // MapFlags represents flags set per map type MapFlags struct { // LevelFlags contains all set flags - LevelFlags uint32 + LevelFlags uint32 `json:"levelFlags"` } diff --git a/primitives/model/model.go b/lump/primitive/model/model.go similarity index 64% rename from primitives/model/model.go rename to lump/primitive/model/model.go index 45050ea..7056159 100644 --- a/primitives/model/model.go +++ b/lump/primitive/model/model.go @@ -7,15 +7,15 @@ import ( // Model is a BSP root node. type Model struct { // Mins is bounding volume smallest value corner - Mins mgl32.Vec3 + Mins mgl32.Vec3 `json:"mins"` // Maxs is bounding volume largest value corner - Maxs mgl32.Vec3 + Maxs mgl32.Vec3 `json:"maxs"` // Origin is the center/specified origin position - Origin mgl32.Vec3 + Origin mgl32.Vec3 `json:"origin"` // HeadNode - HeadNode int32 + HeadNode int32 `json:"headNode"` // FirstFace is index into the faces lump - FirstFace int32 + FirstFace int32 `json:"firstFace"` // NumFaces is number of faces in this model - NumFaces int32 + NumFaces int32 `json:"numFaces"` } diff --git a/primitives/node/node.go b/lump/primitive/node/node.go similarity index 68% rename from primitives/node/node.go rename to lump/primitive/node/node.go index a518d1c..f91b08c 100644 --- a/primitives/node/node.go +++ b/lump/primitive/node/node.go @@ -4,19 +4,19 @@ package node // It represents a non-convex volume with child volumes type Node struct { // PlaneNum is index of splitting place - PlaneNum int32 + PlaneNum int32 `json:"planeNum"` // Children are indexes of children that resulted from the split. It can reference either other Nodes or Leafs - Children [2]int32 + Children [2]int32 `json:"children"` // Mins is bounding volume min - Mins [3]int16 + Mins [3]int16 `json:"mins"` // Maxs is bounding volume maxs - Maxs [3]int16 + Maxs [3]int16 `json:"maxs"` // FirstFace is index into faces lump data - FirstFace uint16 + FirstFace uint16 `json:"firstFace"` // NumFaces is number of faces in this node - NumFaces uint16 + NumFaces uint16 `json:"numFaces"` // Area - Area int16 + Area int16 `json:"area"` // Padding pads this structure to 4byte alignment (no other documented purpose) - Padding int16 + Padding int16 `json:"padding"` } diff --git a/lump/primitive/occlusion/occlusion.go b/lump/primitive/occlusion/occlusion.go new file mode 100644 index 0000000..12469aa --- /dev/null +++ b/lump/primitive/occlusion/occlusion.go @@ -0,0 +1,29 @@ +package occlusion + +import ( + "github.com/go-gl/mathgl/mgl32" +) + +type OcclusionData struct { + Flags int32 `json:"flags"` + FirstPoly int32 `json:"firstPoly"` + PolyCount int32 `json:"polyCount"` + Mins mgl32.Vec3 `json:"mins"` + Maxs mgl32.Vec3 `json:"maxs"` + Area int32 `json:"area"` +} + +type OcclusionPolyData struct { + FirstVertexIndex int32 `json:"firstVertexIndex"` + VertexCount int32 `json:"vertexCount"` + PlaneNum int32 `json:"planeNum"` +} + +//Implementation info +/* +Lump size: +num occlusions * size() ++ num occlusionPolyDatas * size() ++ num OccluderVertexIndices * size() ++ 3* sizeof(Int32) +*/ diff --git a/primitives/overlay/overlay.go b/lump/primitive/overlay/overlay.go similarity index 84% rename from primitives/overlay/overlay.go rename to lump/primitive/overlay/overlay.go index d2434c4..d0fb8ab 100644 --- a/primitives/overlay/overlay.go +++ b/lump/primitive/overlay/overlay.go @@ -29,9 +29,9 @@ public: // Overlay type Overlay struct { // NId - NId int32 + NId int32 `json:"nId"` // NTexInfo - NTexInfo int16 + NTexInfo int16 `json:"nTexInfo"` // AlignmentPadding - AlignmentPadding int16 + AlignmentPadding int16 `json:"alignmentPadding"` } diff --git a/primitives/overlayfade/overlayfade.go b/lump/primitive/overlayfade/overlayfade.go similarity index 63% rename from primitives/overlayfade/overlayfade.go rename to lump/primitive/overlayfade/overlayfade.go index 844331a..94293ed 100644 --- a/primitives/overlayfade/overlayfade.go +++ b/lump/primitive/overlayfade/overlayfade.go @@ -3,7 +3,7 @@ package overlayfade // OverlayFade type OverlayFade struct { // FadeDistMinSq is distance to start fading in - FadeDistMinSq float32 + FadeDistMinSq float32 `json:"fadeDistMinSq"` // FadeDistMaxSq is distance to fully fade in - FadeDistMaxSq float32 + FadeDistMaxSq float32 `json:"fadeDistMaxSq"` } diff --git a/lump/primitive/physcollide/physcollide.go b/lump/primitive/physcollide/physcollide.go new file mode 100644 index 0000000..f9dbd0c --- /dev/null +++ b/lump/primitive/physcollide/physcollide.go @@ -0,0 +1,31 @@ +package physcollide + +// PhysCollideEntry +type PhysCollideEntry struct { + // ModelHeader + ModelHeader PhysModel `json:"modelHeader"` + // Solids + Solids []Solid `json:"solids"` + // TextBuffer + TextBuffer string `json:"textBuffer"` +} + +// PhysModel +type PhysModel struct { + // ModelIndex + ModelIndex int32 `json:"modelIndex"` + // DataSize + DataSize int32 `json:"dataSize"` + // KeydataSize + KeydataSize int32 `json:"keydataSize"` + // SolidCount + SolidCount int32 `json:"solidCount"` +} + +// Solid +type Solid struct { + // Size + Size int32 `json:"size"` + // CollisionBinary + CollisionBinary []int32 `json:"collisionBinary"` +} diff --git a/primitives/plane/plane.go b/lump/primitive/plane/plane.go similarity index 66% rename from primitives/plane/plane.go rename to lump/primitive/plane/plane.go index a6cc311..f5cfe53 100644 --- a/primitives/plane/plane.go +++ b/lump/primitive/plane/plane.go @@ -7,9 +7,9 @@ import ( // Plane represents an arbitrary plane of infinite length. type Plane struct { // Normal is normal vector - Normal mgl32.Vec3 // normal vector + Normal mgl32.Vec3 `json:"normal"` // normal vector // Distance is distance from origin - Distance float32 + Distance float32 `json:"distance"` // AxisType is plane axis identifier - AxisType int32 + AxisType int32 `json:"axisType"` } diff --git a/lump/primitive/portal/portal.go b/lump/primitive/portal/portal.go new file mode 100644 index 0000000..6f72765 --- /dev/null +++ b/lump/primitive/portal/portal.go @@ -0,0 +1,33 @@ +package portal + +import ( + "github.com/galaco/bsp/lump/primitive/common" + "github.com/galaco/bsp/lump/primitive/face" + "github.com/galaco/bsp/lump/primitive/node" + "github.com/galaco/bsp/lump/primitive/plane" +) + +// MaxPortals is the maximum number of portals that can be generated +const MaxPortals = 65536 + +// Portal +type Portal struct { + // Id portal id + Id int32 `json:"id"` + // Plane + Plane plane.Plane `json:"plane"` + // OnNode + OnNode *node.Node `json:"onNode"` + // Nodes + Nodes [2]*node.Node `json:"nodes"` + // Next + Next [2]*Portal `json:"next"` + // Winding + Winding *common.Winding `json:"winding"` + // SideFound + SideFound int32 `json:"sideFound"` //qboolean = int32 + // Side + Side *common.Side `json:"side"` + // Face + Face [2]*face.Face `json:"face"` +} diff --git a/lump/primitive/primitive/primitive.go b/lump/primitive/primitive/primitive.go new file mode 100644 index 0000000..12e0631 --- /dev/null +++ b/lump/primitive/primitive/primitive.go @@ -0,0 +1,15 @@ +package primitive + +// Primitive +type Primitive struct { + // Type + Type byte `json:"type"` + // FirstIndex + FirstIndex uint16 `json:"firstIndex"` + // IndexCount + IndexCount uint16 `json:"indexCount"` + // FirstVert + FirstVert uint16 `json:"firstVert"` + // VertCount + VertCount uint16 `json:"vertCount"` +} diff --git a/primitives/primvert/primvert.go b/lump/primitive/primvert/primvert.go similarity index 81% rename from primitives/primvert/primvert.go rename to lump/primitive/primvert/primvert.go index 9b2627c..7491428 100644 --- a/primitives/primvert/primvert.go +++ b/lump/primitive/primvert/primvert.go @@ -7,5 +7,5 @@ import ( // PrimVert type PrimVert struct { // Pos is vertex position - Pos mgl32.Vec3 + Pos mgl32.Vec3 `json:"pos"` } diff --git a/primitives/texdata/texdata.go b/lump/primitive/texdata/texdata.go similarity index 54% rename from primitives/texdata/texdata.go rename to lump/primitive/texdata/texdata.go index 16ec801..511503e 100644 --- a/primitives/texdata/texdata.go +++ b/lump/primitive/texdata/texdata.go @@ -7,15 +7,15 @@ import ( // TexData contains texture properties type TexData struct { // Reflectivity is reflectivity vector - Reflectivity mgl32.Vec3 + Reflectivity mgl32.Vec3 `json:"reflectivity"` // NameStringTableID - NameStringTableID int32 + NameStringTableID int32 `json:"nameStringTableId"` // Width is texture width - Width int32 + Width int32 `json:"width"` // Height is texture height - Height int32 + Height int32 `json:"height"` // ViewWidth - ViewWidth int32 + ViewWidth int32 `json:"viewWidth"` // ViewHeight - ViewHeight int32 + ViewHeight int32 `json:"viewHeight"` } diff --git a/lump/primitive/texinfo/texinfo.go b/lump/primitive/texinfo/texinfo.go new file mode 100644 index 0000000..fabdfac --- /dev/null +++ b/lump/primitive/texinfo/texinfo.go @@ -0,0 +1,14 @@ +package texinfo + +// TexInfo contains texture information for a face +// (tex, uv info, and lightmap info) +type TexInfo struct { + // TextureVecsTexelsPerWorldUnits is texture scale + TextureVecsTexelsPerWorldUnits [2][4]float32 `json:"textureVecsTexelsPerWorldUnits"` // [s/t][xyz offset] + // LightmapVecsLuxelsPerWorldUnits is lightmap scale + LightmapVecsLuxelsPerWorldUnits [2][4]float32 `json:"lightmapVecsLuxelsPerWorldUnits"` // [s/t][xyz offset] - length is in units of texels/area + // Flags + Flags int32 `json:"flags"` + // TexData is index into TexData lump data + TexData int32 `json:"texData"` +} diff --git a/primitives/vertnormal/vertnormal.go b/lump/primitive/vertnormal/vertnormal.go similarity index 85% rename from primitives/vertnormal/vertnormal.go rename to lump/primitive/vertnormal/vertnormal.go index ff1b3c0..527a3c2 100644 --- a/primitives/vertnormal/vertnormal.go +++ b/lump/primitive/vertnormal/vertnormal.go @@ -7,5 +7,5 @@ import ( // VertNormal is normal for a single vertex type VertNormal struct { // Pos is Normal for vertex - Pos mgl32.Vec3 + Pos mgl32.Vec3 `json:"pos"` } diff --git a/primitives/visibility/visibility.go b/lump/primitive/visibility/visibility.go similarity index 90% rename from primitives/visibility/visibility.go rename to lump/primitive/visibility/visibility.go index 1ade15f..768ec77 100644 --- a/primitives/visibility/visibility.go +++ b/lump/primitive/visibility/visibility.go @@ -13,12 +13,12 @@ const VisPAS = 1 // Includes both PVS (Potential Visible Set) and PAS (Potential Audible Set) type Vis struct { // NumClusters is number of computed cluster - NumClusters int32 + NumClusters int32 `json:"numClusters"` // ByteOffset contains offsets for cluster pvs and pas // Slice length = NumClusters [0]=offset to PVS bit array for cluster - ByteOffset [][2]int32 + ByteOffset [][2]int32 `json:"byteOffset"` // BitVectors are compressed bit vectors, contains run-length compression PVS data - BitVectors []byte + BitVectors []byte `json:"bitVectors"` } // GetVisibleClusters returns all visible clusters ids for a given cluster @@ -43,7 +43,7 @@ func (vis *Vis) GetPVSForCluster(clusterId int16) []bool { for currentClusterIdx := int32(0); currentClusterIdx < vis.NumClusters; v++ { if int(vis.BitVectors[v]) == 0 { v++ - currentClusterIdx += (int32(vis.BitVectors[v]) << 3) + currentClusterIdx += int32(vis.BitVectors[v]) << 3 continue } for i := uint8(0); i < 8 && currentClusterIdx+int32(i) < vis.NumClusters; i++ { diff --git a/primitives/visibility/visibility_test.go b/lump/primitive/visibility/visibility_test.go similarity index 100% rename from primitives/visibility/visibility_test.go rename to lump/primitive/visibility/visibility_test.go diff --git a/primitives/wateroverlay/wateroverlay.go b/lump/primitive/wateroverlay/wateroverlay.go similarity index 100% rename from primitives/wateroverlay/wateroverlay.go rename to lump/primitive/wateroverlay/wateroverlay.go diff --git a/lump/primitive/worldlight/worldlight.go b/lump/primitive/worldlight/worldlight.go new file mode 100644 index 0000000..a79b0fd --- /dev/null +++ b/lump/primitive/worldlight/worldlight.go @@ -0,0 +1,105 @@ +package worldlight + +import ( + "github.com/go-gl/mathgl/mgl32" +) + +// EmitType light emission mode. +type EmitType uint8 // assumed this is 1 byte.. + +const ( + // EmitSurface 90 degree spotlight + EmitSurface EmitType = 0 + // EmitPoint simple point light source + EmitPoint EmitType = 1 + // EmitSpotLight spotlight with penumbra + EmitSpotLight EmitType = 2 + // EmitSkyLight directional light with no falloff (surface must trace to SKY texture) + EmitSkyLight EmitType = 3 + // EmitQuakeLight linear falloff, non-lambertian + EmitQuakeLight EmitType = 4 + // EmitSkyAmbient spherical light source with no falloff (surface must trace to SKY texture) + EmitSkyAmbient EmitType = 5 +) + +// WorldLight is a single light in the world +// This data may also be stored in entdata +type WorldLight struct { + // Origin is position in the world + Origin mgl32.Vec3 `json:"origin"` + // Intensity + Intensity mgl32.Vec3 `json:"intensity"` + // Normal + Normal mgl32.Vec3 `json:"normal"` + // Cluster + Cluster int32 `json:"cluster"` + // Type + Type EmitType `json:"type"` + // Unknown1 + // @TODO Think for alignments sake with is uint8. May be 3 bytes padding... + Unknown1 [3]byte `json:"unknown1"` + // Style + Style int32 `json:"style"` + // StopDot + StopDot float32 `json:"stopDot"` + // StopDot2 + StopDot2 float32 `json:"stopDot2"` + // Exponent + Exponent float32 `json:"exponent"` + // Radius + Radius float32 `json:"radius"` + // ConstantAttenuation + ConstantAttenuation float32 `json:"constantAttenuation"` + // LinearAttenuation + LinearAttenuation float32 `json:"linearAttenuation"` + // QuadraticAttenuation + QuadraticAttenuation float32 `json:"quadraticAttenuation"` + // Flags + Flags int32 `json:"flags"` + // TexInfo + TexInfo int32 `json:"texInfo"` + // Owner + Owner int32 `json:"owner"` +} + +// WorldLightHDR is a single light in the world +// This data may also be stored in entdata +type WorldLightHDR struct { + // Origin is position in the world + Origin mgl32.Vec3 `json:"origin"` + // Intensity + Intensity mgl32.Vec3 `json:"intensity"` + // Normal + Normal mgl32.Vec3 `json:"normal"` + // Cluster + Cluster int32 `json:"cluster"` + // Type + Type EmitType `json:"type"` + // Unknown1 + // @TODO Think for alignments sake with is uint8. May be 3 bytes padding... + Unknown1 [3]byte `json:"unknown1"` + // Style + Style int32 `json:"style"` + // StopDot + StopDot float32 `json:"stopDot"` + // StopDot2 + StopDot2 float32 `json:"stopDot2"` + // Exponent + Exponent float32 `json:"exponent"` + // Radius + Radius float32 `json:"radius"` + // ConstantAttenuation + ConstantAttenuation float32 `json:"constantAttenuation"` + // LinearAttenuation + LinearAttenuation float32 `json:"linearAttenuation"` + // QuadraticAttenuation + QuadraticAttenuation float32 `json:"quadraticAttenuation"` + // Flags + Flags int32 `json:"flags"` + // TexInfo + TexInfo int32 `json:"texInfo"` + // Owner + Owner int32 `json:"owner"` + // @TODO We don't know what this is. + Unknown2 [12]byte `json:"unknown2" bsp:"v21"` +} diff --git a/lump/primvert.go b/lump/primvert.go new file mode 100644 index 0000000..fb05a74 --- /dev/null +++ b/lump/primvert.go @@ -0,0 +1,32 @@ +package lump + +import ( + primitives "github.com/galaco/bsp/lump/primitive/primvert" +) + +// PrimVert is Lump 37: PrimVert +type PrimVert struct { + Metadata + Data []primitives.PrimVert `json:"data"` +} + +// FromBytes imports this lump from raw byte Data +func (lump *PrimVert) FromBytes(raw []byte) error { + data, err := unmarshallBasicLump[primitives.PrimVert](raw) + if err != nil { + return err + } + + lump.Data = data + return nil +} + +// Contents returns internal format structure Data +func (lump *PrimVert) Contents() []primitives.PrimVert { + return lump.Data +} + +// ToBytes converts this lump back to raw byte Data +func (lump *PrimVert) ToBytes() ([]byte, error) { + return marshallBasicLump(lump.Data) +} diff --git a/lumps/primvert_test.go b/lump/primvert_test.go similarity index 78% rename from lumps/primvert_test.go rename to lump/primvert_test.go index 6a3742d..781f405 100644 --- a/lumps/primvert_test.go +++ b/lump/primvert_test.go @@ -1,10 +1,11 @@ -package lumps +package lump import ( "bytes" "encoding/binary" - "github.com/go-gl/mathgl/mgl32" "testing" + + "github.com/go-gl/mathgl/mgl32" ) func TestPrimVert_GetData(t *testing.T) { @@ -18,10 +19,10 @@ func TestPrimVert_GetData(t *testing.T) { if err != nil { t.Error(err) } - if err := sut.Unmarshall(buf.Bytes()); err != nil { + if err := sut.FromBytes(buf.Bytes()); err != nil { t.Error(err) } - for idx, b := range sut.GetData() { + for idx, b := range sut.Contents() { if data[idx] != b.Pos { t.Error("mismatched between expected and actual unmarshalled bytes") } @@ -39,15 +40,15 @@ func TestPrimVert_Marshall(t *testing.T) { if err != nil { t.Error(err) } - if err := sut.Unmarshall(buf.Bytes()); err != nil { + if err := sut.FromBytes(buf.Bytes()); err != nil { t.Error(err) } - for idx, b := range sut.data { + for idx, b := range sut.Data { if data[idx] != b.Pos { t.Error("mismatched between expected and actual unmarshalled bytes") } } - res, err := sut.Marshall() + res, err := sut.ToBytes() if err != nil { t.Errorf("unexpected error during marshalling") } @@ -58,7 +59,7 @@ func TestPrimVert_Marshall(t *testing.T) { } } -func TestPrimVert_Unmarshall(t *testing.T) { +func TestPrimVert_FromBytes(t *testing.T) { sut := PrimVert{} data := []mgl32.Vec3{ {0, 1, 2}, @@ -69,10 +70,10 @@ func TestPrimVert_Unmarshall(t *testing.T) { if err != nil { t.Error(err) } - if err := sut.Unmarshall(buf.Bytes()); err != nil { + if err := sut.FromBytes(buf.Bytes()); err != nil { t.Error(err) } - for idx, b := range sut.data { + for idx, b := range sut.Data { if data[idx] != b.Pos { t.Error("mismatched between expected and actual unmarshalled bytes") } diff --git a/lump/surfedge.go b/lump/surfedge.go new file mode 100644 index 0000000..e0f47fa --- /dev/null +++ b/lump/surfedge.go @@ -0,0 +1,28 @@ +package lump + +// Surfedge is Lump 13: Surfedge +type Surfedge struct { + Metadata + Data []int32 `json:"data"` // MAX_MAP_SURFEDGES = 512000 +} + +// FromBytes imports this lump from raw byte Data +func (lump *Surfedge) FromBytes(raw []byte) error { + data, err := unmarshallBasicLump[int32](raw) + if err != nil { + return err + } + + lump.Data = data + return nil +} + +// Contents returns internal format structure Data +func (lump *Surfedge) Contents() []int32 { + return lump.Data +} + +// ToBytes converts this lump back to raw byte Data +func (lump *Surfedge) ToBytes() ([]byte, error) { + return marshallBasicLump(lump.Data) +} diff --git a/lumps/surfedge_test.go b/lump/surfedge_test.go similarity index 78% rename from lumps/surfedge_test.go rename to lump/surfedge_test.go index bb953d1..03e2fc6 100644 --- a/lumps/surfedge_test.go +++ b/lump/surfedge_test.go @@ -1,4 +1,4 @@ -package lumps +package lump import ( "bytes" @@ -14,10 +14,10 @@ func TestSurfedge_GetData(t *testing.T) { if err != nil { t.Error(err) } - if err := sut.Unmarshall(buf.Bytes()); err != nil { + if err := sut.FromBytes(buf.Bytes()); err != nil { t.Error(err) } - for idx, b := range sut.GetData() { + for idx, b := range sut.Contents() { if data[idx] != b { t.Error("mismatched between expected and actual unmarshalled bytes") } @@ -32,15 +32,15 @@ func TestSurfedge_Marshall(t *testing.T) { if err != nil { t.Error(err) } - if err := sut.Unmarshall(buf.Bytes()); err != nil { + if err := sut.FromBytes(buf.Bytes()); err != nil { t.Error(err) } - for idx, b := range sut.data { + for idx, b := range sut.Data { if data[idx] != b { t.Error("mismatched between expected and actual unmarshalled bytes") } } - res, err := sut.Marshall() + res, err := sut.ToBytes() if err != nil { t.Errorf("unexpected error during marshalling") } @@ -51,7 +51,7 @@ func TestSurfedge_Marshall(t *testing.T) { } } -func TestSurfedge_Unmarshall(t *testing.T) { +func TestSurfedge_FromBytes(t *testing.T) { sut := Surfedge{} data := []int32{0, 1, 2, 5, 43, 156, 146, 3, 3, 6} var buf bytes.Buffer @@ -59,10 +59,10 @@ func TestSurfedge_Unmarshall(t *testing.T) { if err != nil { t.Error(err) } - if err := sut.Unmarshall(buf.Bytes()); err != nil { + if err := sut.FromBytes(buf.Bytes()); err != nil { t.Error(err) } - for idx, b := range sut.data { + for idx, b := range sut.Data { if data[idx] != b { t.Error("mismatched between expected and actual unmarshalled bytes") } diff --git a/lump/texdata.go b/lump/texdata.go new file mode 100644 index 0000000..5a206e9 --- /dev/null +++ b/lump/texdata.go @@ -0,0 +1,32 @@ +package lump + +import ( + primitives "github.com/galaco/bsp/lump/primitive/texdata" +) + +// TexData is Lump 2: TexData +type TexData struct { + Metadata + Data []primitives.TexData `json:"data"` +} + +// FromBytes imports this lump from raw byte Data +func (lump *TexData) FromBytes(raw []byte) error { + data, err := unmarshallBasicLump[primitives.TexData](raw) + if err != nil { + return err + } + + lump.Data = data + return nil +} + +// Contents returns internal format structure Data +func (lump *TexData) Contents() []primitives.TexData { + return lump.Data +} + +// ToBytes converts this lump back to raw byte Data +func (lump *TexData) ToBytes() ([]byte, error) { + return marshallBasicLump(lump.Data) +} diff --git a/lump/texdatastringdata.go b/lump/texdatastringdata.go new file mode 100644 index 0000000..e3df1b4 --- /dev/null +++ b/lump/texdatastringdata.go @@ -0,0 +1,24 @@ +package lump + +// TexDataStringData is Lump 43: TexDataStringData +type TexDataStringData struct { + Metadata + Data string `json:"data"` // MAX_MAP_TEXDATA_STRING_DATA = 256000, TEXTURE_NAME_LENGTH = 128 +} + +// FromBytes imports this lump from raw byte Data +func (lump *TexDataStringData) FromBytes(raw []byte) error { + lump.Data = string(raw) + + return nil +} + +// Contents returns internal format structure Data +func (lump *TexDataStringData) Contents() string { + return lump.Data +} + +// ToBytes converts this lump back to raw byte Data +func (lump *TexDataStringData) ToBytes() ([]byte, error) { + return []byte(lump.Data), nil +} diff --git a/lumps/texdatastringdata_test.go b/lump/texdatastringdata_test.go similarity index 69% rename from lumps/texdatastringdata_test.go rename to lump/texdatastringdata_test.go index 7830ed6..d12affc 100644 --- a/lumps/texdatastringdata_test.go +++ b/lump/texdatastringdata_test.go @@ -1,14 +1,14 @@ -package lumps +package lump import "testing" func TestTexDataStringData_GetData(t *testing.T) { sut := TexDataStringData{} data := []byte{0, 1, 2, 5, 43, 156, 146, 3, 3, 6} - if err := sut.Unmarshall(data); err != nil { + if err := sut.FromBytes(data); err != nil { t.Error(err) } - if string(sut.GetData()) != string(data) { + if sut.Contents() != string(data) { t.Error("mismatched between expected and actual unmarshalled bytes") } } @@ -16,13 +16,13 @@ func TestTexDataStringData_GetData(t *testing.T) { func TestTexDataStringData_Marshall(t *testing.T) { sut := TexDataStringData{} data := []byte{0, 1, 2, 5, 43, 156, 146, 3, 3, 6} - if err := sut.Unmarshall(data); err != nil { + if err := sut.FromBytes(data); err != nil { t.Error(err) } - if string(sut.GetData()) != string(data) { + if sut.Contents() != string(data) { t.Error("mismatched between expected and actual unmarshalled bytes") } - res, err := sut.Marshall() + res, err := sut.ToBytes() if err != nil { t.Errorf("unexpected error during marshalling") } @@ -31,13 +31,13 @@ func TestTexDataStringData_Marshall(t *testing.T) { } } -func TestTexDataStringData_Unmarshall(t *testing.T) { +func TestTexDataStringData_FromBytes(t *testing.T) { sut := TexDataStringData{} data := []byte{0, 1, 2, 5, 43, 156, 146, 3, 3, 6} - if err := sut.Unmarshall(data); err != nil { + if err := sut.FromBytes(data); err != nil { t.Error(err) } - if string(sut.data) != string(data) { + if sut.Data != string(data) { t.Error("mismatched between expected and actual unmarshalled bytes") } } diff --git a/lump/texdatastringtable.go b/lump/texdatastringtable.go new file mode 100644 index 0000000..c510e39 --- /dev/null +++ b/lump/texdatastringtable.go @@ -0,0 +1,28 @@ +package lump + +// TexDataStringTable is Lump 44: TexDataStringTable +type TexDataStringTable struct { + Metadata + Data []int32 `json:"data"` // MAX_MAP_TEXINFO = 2048 +} + +// FromBytes imports this lump from raw byte Data +func (lump *TexDataStringTable) FromBytes(raw []byte) error { + data, err := unmarshallBasicLump[int32](raw) + if err != nil { + return err + } + + lump.Data = data + return nil +} + +// Contents returns internal format structure Data +func (lump *TexDataStringTable) Contents() []int32 { + return lump.Data +} + +// ToBytes converts this lump back to raw byte Data +func (lump *TexDataStringTable) ToBytes() ([]byte, error) { + return marshallBasicLump(lump.Data) +} diff --git a/lumps/texdatastringtable_test.go b/lump/texdatastringtable_test.go similarity index 78% rename from lumps/texdatastringtable_test.go rename to lump/texdatastringtable_test.go index 65b8e54..2345e6d 100644 --- a/lumps/texdatastringtable_test.go +++ b/lump/texdatastringtable_test.go @@ -1,4 +1,4 @@ -package lumps +package lump import ( "bytes" @@ -14,10 +14,10 @@ func TestTexDataStringTable_GetData(t *testing.T) { if err != nil { t.Error(err) } - if err := sut.Unmarshall(buf.Bytes()); err != nil { + if err := sut.FromBytes(buf.Bytes()); err != nil { t.Error(err) } - for idx, b := range sut.GetData() { + for idx, b := range sut.Contents() { if data[idx] != b { t.Error("mismatched between expected and actual unmarshalled bytes") } @@ -32,15 +32,15 @@ func TestTexDataStringTable_Marshall(t *testing.T) { if err != nil { t.Error(err) } - if err := sut.Unmarshall(buf.Bytes()); err != nil { + if err := sut.FromBytes(buf.Bytes()); err != nil { t.Error(err) } - for idx, b := range sut.data { + for idx, b := range sut.Data { if data[idx] != b { t.Error("mismatched between expected and actual unmarshalled bytes") } } - res, err := sut.Marshall() + res, err := sut.ToBytes() if err != nil { t.Errorf("unexpected error during marshalling") } @@ -51,7 +51,7 @@ func TestTexDataStringTable_Marshall(t *testing.T) { } } -func TestTexDataStringTable_Unmarshall(t *testing.T) { +func TestTexDataStringTable_FromBytes(t *testing.T) { sut := TexDataStringTable{} data := []int32{0, 1, 2, 5, 43, 156, 146, 3, 3, 6} var buf bytes.Buffer @@ -59,10 +59,10 @@ func TestTexDataStringTable_Unmarshall(t *testing.T) { if err != nil { t.Error(err) } - if err := sut.Unmarshall(buf.Bytes()); err != nil { + if err := sut.FromBytes(buf.Bytes()); err != nil { t.Error(err) } - for idx, b := range sut.data { + for idx, b := range sut.Data { if data[idx] != b { t.Error("mismatched between expected and actual unmarshalled bytes") } diff --git a/lump/texinfo.go b/lump/texinfo.go new file mode 100644 index 0000000..4d48b65 --- /dev/null +++ b/lump/texinfo.go @@ -0,0 +1,32 @@ +package lump + +import ( + primitives "github.com/galaco/bsp/lump/primitive/texinfo" +) + +// TexInfo is Lump 6: TexInfo +type TexInfo struct { + Metadata + Data []primitives.TexInfo `json:"data"` +} + +// FromBytes imports this lump from raw byte Data +func (lump *TexInfo) FromBytes(raw []byte) error { + data, err := unmarshallBasicLump[primitives.TexInfo](raw) + if err != nil { + return err + } + + lump.Data = data + return nil +} + +// Contents returns internal format structure Data +func (lump *TexInfo) Contents() []primitives.TexInfo { + return lump.Data +} + +// ToBytes converts this lump back to raw byte Data +func (lump *TexInfo) ToBytes() ([]byte, error) { + return marshallBasicLump(lump.Data) +} diff --git a/lump/vertex.go b/lump/vertex.go new file mode 100644 index 0000000..d0b250e --- /dev/null +++ b/lump/vertex.go @@ -0,0 +1,32 @@ +package lump + +import ( + "github.com/go-gl/mathgl/mgl32" +) + +// Vertex is Lump 3: Vertex +type Vertex struct { + Metadata + Data []mgl32.Vec3 `json:"data"` +} + +// FromBytes imports this lump from raw byte Data +func (lump *Vertex) FromBytes(raw []byte) error { + data, err := unmarshallBasicLump[mgl32.Vec3](raw) + if err != nil { + return err + } + + lump.Data = data + return nil +} + +// Contents returns internal format structure Data +func (lump *Vertex) Contents() []mgl32.Vec3 { + return lump.Data +} + +// ToBytes converts this lump back to raw byte Data +func (lump *Vertex) ToBytes() ([]byte, error) { + return marshallBasicLump(lump.Data) +} diff --git a/lumps/vertex_test.go b/lump/vertex_test.go similarity index 78% rename from lumps/vertex_test.go rename to lump/vertex_test.go index 7d1db0c..fd2d70d 100644 --- a/lumps/vertex_test.go +++ b/lump/vertex_test.go @@ -1,10 +1,11 @@ -package lumps +package lump import ( "bytes" "encoding/binary" - "github.com/go-gl/mathgl/mgl32" "testing" + + "github.com/go-gl/mathgl/mgl32" ) func TestVertex_GetData(t *testing.T) { @@ -18,10 +19,10 @@ func TestVertex_GetData(t *testing.T) { if err != nil { t.Error(err) } - if err := sut.Unmarshall(buf.Bytes()); err != nil { + if err := sut.FromBytes(buf.Bytes()); err != nil { t.Error(err) } - for idx, b := range sut.GetData() { + for idx, b := range sut.Contents() { if data[idx] != b { t.Error("mismatched between expected and actual unmarshalled bytes") } @@ -39,15 +40,15 @@ func TestVertex_Marshall(t *testing.T) { if err != nil { t.Error(err) } - if err := sut.Unmarshall(buf.Bytes()); err != nil { + if err := sut.FromBytes(buf.Bytes()); err != nil { t.Error(err) } - for idx, b := range sut.data { + for idx, b := range sut.Data { if data[idx] != b { t.Error("mismatched between expected and actual unmarshalled bytes") } } - res, err := sut.Marshall() + res, err := sut.ToBytes() if err != nil { t.Errorf("unexpected error during marshalling") } @@ -58,7 +59,7 @@ func TestVertex_Marshall(t *testing.T) { } } -func TestVertex_Unmarshall(t *testing.T) { +func TestVertex_FromBytes(t *testing.T) { sut := Vertex{} data := []mgl32.Vec3{ {0, 1, 2}, @@ -69,10 +70,10 @@ func TestVertex_Unmarshall(t *testing.T) { if err != nil { t.Error(err) } - if err := sut.Unmarshall(buf.Bytes()); err != nil { + if err := sut.FromBytes(buf.Bytes()); err != nil { t.Error(err) } - for idx, b := range sut.data { + for idx, b := range sut.Data { if data[idx] != b { t.Error("mismatched between expected and actual unmarshalled bytes") } diff --git a/lump/vertnormal.go b/lump/vertnormal.go new file mode 100644 index 0000000..1c42b11 --- /dev/null +++ b/lump/vertnormal.go @@ -0,0 +1,32 @@ +package lump + +import ( + primitives "github.com/galaco/bsp/lump/primitive/vertnormal" +) + +// VertNormal is Lump 30: VertNormal +type VertNormal struct { + Metadata + Data []primitives.VertNormal `json:"data"` +} + +// FromBytes imports this lump from raw byte Data +func (lump *VertNormal) FromBytes(raw []byte) error { + data, err := unmarshallBasicLump[primitives.VertNormal](raw) + if err != nil { + return err + } + + lump.Data = data + return nil +} + +// Contents returns internal format structure Data +func (lump *VertNormal) Contents() []primitives.VertNormal { + return lump.Data +} + +// ToBytes converts this lump back to raw byte Data +func (lump *VertNormal) ToBytes() ([]byte, error) { + return marshallBasicLump(lump.Data) +} diff --git a/lumps/vertnormal_test.go b/lump/vertnormal_test.go similarity index 79% rename from lumps/vertnormal_test.go rename to lump/vertnormal_test.go index b145118..7e3addc 100644 --- a/lumps/vertnormal_test.go +++ b/lump/vertnormal_test.go @@ -1,10 +1,11 @@ -package lumps +package lump import ( "bytes" "encoding/binary" - "github.com/go-gl/mathgl/mgl32" "testing" + + "github.com/go-gl/mathgl/mgl32" ) func TestVertNormal_GetData(t *testing.T) { @@ -18,10 +19,10 @@ func TestVertNormal_GetData(t *testing.T) { if err != nil { t.Error(err) } - if err := sut.Unmarshall(buf.Bytes()); err != nil { + if err := sut.FromBytes(buf.Bytes()); err != nil { t.Error(err) } - for idx, b := range sut.GetData() { + for idx, b := range sut.Contents() { if data[idx] != b.Pos { t.Error("mismatched between expected and actual unmarshalled bytes") } @@ -39,15 +40,15 @@ func TestVertNormal_Marshall(t *testing.T) { if err != nil { t.Error(err) } - if err := sut.Unmarshall(buf.Bytes()); err != nil { + if err := sut.FromBytes(buf.Bytes()); err != nil { t.Error(err) } - for idx, b := range sut.data { + for idx, b := range sut.Data { if data[idx] != b.Pos { t.Error("mismatched between expected and actual unmarshalled bytes") } } - res, err := sut.Marshall() + res, err := sut.ToBytes() if err != nil { t.Errorf("unexpected error during marshalling") } @@ -58,7 +59,7 @@ func TestVertNormal_Marshall(t *testing.T) { } } -func TestVertNormal_Unmarshall(t *testing.T) { +func TestVertNormal_FromBytes(t *testing.T) { sut := VertNormal{} data := []mgl32.Vec3{ {0, 1, 2}, @@ -69,10 +70,10 @@ func TestVertNormal_Unmarshall(t *testing.T) { if err != nil { t.Error(err) } - if err := sut.Unmarshall(buf.Bytes()); err != nil { + if err := sut.FromBytes(buf.Bytes()); err != nil { t.Error(err) } - for idx, b := range sut.data { + for idx, b := range sut.Data { if data[idx] != b.Pos { t.Error("mismatched between expected and actual unmarshalled bytes") } diff --git a/lump/vertnormalindice.go b/lump/vertnormalindice.go new file mode 100644 index 0000000..66314e8 --- /dev/null +++ b/lump/vertnormalindice.go @@ -0,0 +1,28 @@ +package lump + +// VertNormalIndice is Lump 31: VertNormalIndice +type VertNormalIndice struct { + Metadata + Data []uint16 `json:"data"` +} + +// FromBytes imports this lump from raw byte Data +func (lump *VertNormalIndice) FromBytes(raw []byte) error { + data, err := unmarshallBasicLump[uint16](raw) + if err != nil { + return err + } + + lump.Data = data + return nil +} + +// Contents returns internal format structure Data +func (lump *VertNormalIndice) Contents() []uint16 { + return lump.Data +} + +// ToBytes converts this lump back to raw byte Data +func (lump *VertNormalIndice) ToBytes() ([]byte, error) { + return marshallBasicLump(lump.Data) +} diff --git a/lumps/vertnormalindice_test.go b/lump/vertnormalindice_test.go similarity index 78% rename from lumps/vertnormalindice_test.go rename to lump/vertnormalindice_test.go index 10e5ff5..943d2a7 100644 --- a/lumps/vertnormalindice_test.go +++ b/lump/vertnormalindice_test.go @@ -1,4 +1,4 @@ -package lumps +package lump import ( "bytes" @@ -14,10 +14,10 @@ func TestVertNormalIndice_GetData(t *testing.T) { if err != nil { t.Error(err) } - if err := sut.Unmarshall(buf.Bytes()); err != nil { + if err := sut.FromBytes(buf.Bytes()); err != nil { t.Error(err) } - for idx, b := range sut.GetData() { + for idx, b := range sut.Contents() { if data[idx] != b { t.Error("mismatched between expected and actual unmarshalled bytes") } @@ -32,15 +32,15 @@ func TestVertNormalIndice_Marshall(t *testing.T) { if err != nil { t.Error(err) } - if err := sut.Unmarshall(buf.Bytes()); err != nil { + if err := sut.FromBytes(buf.Bytes()); err != nil { t.Error(err) } - for idx, b := range sut.data { + for idx, b := range sut.Data { if data[idx] != b { t.Error("mismatched between expected and actual unmarshalled bytes") } } - res, err := sut.Marshall() + res, err := sut.ToBytes() if err != nil { t.Errorf("unexpected error during marshalling") } @@ -51,7 +51,7 @@ func TestVertNormalIndice_Marshall(t *testing.T) { } } -func TestVertNormalIndice_Unmarshall(t *testing.T) { +func TestVertNormalIndice_FromBytes(t *testing.T) { sut := VertNormalIndice{} data := []uint16{0, 1, 2, 5, 43, 156, 146, 3, 3, 6} var buf bytes.Buffer @@ -59,10 +59,10 @@ func TestVertNormalIndice_Unmarshall(t *testing.T) { if err != nil { t.Error(err) } - if err := sut.Unmarshall(buf.Bytes()); err != nil { + if err := sut.FromBytes(buf.Bytes()); err != nil { t.Error(err) } - for idx, b := range sut.data { + for idx, b := range sut.Data { if data[idx] != b { t.Error("mismatched between expected and actual unmarshalled bytes") } diff --git a/lump/visibility.go b/lump/visibility.go new file mode 100644 index 0000000..3bcc2ab --- /dev/null +++ b/lump/visibility.go @@ -0,0 +1,40 @@ +package lump + +import ( + "bytes" + "encoding/binary" + + primitives "github.com/galaco/bsp/lump/primitive/visibility" +) + +// Visibility is Lump 4: Visibility +type Visibility struct { + Metadata + Data primitives.Vis `json:"data"` +} + +// FromBytes imports this lump from raw byte Data +func (lump *Visibility) FromBytes(raw []byte) error { + if err := binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.Data.NumClusters); err != nil { + return err + } + lump.Data.ByteOffset = make([][2]int32, lump.Data.NumClusters) + if err := binary.Read(bytes.NewBuffer(raw[4:]), binary.LittleEndian, &lump.Data.ByteOffset); err != nil { + return err + } + lump.Data.BitVectors = make([]byte, len(raw)) + if err := binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.Data.BitVectors); err != nil { + return err + } + return nil +} + +// Contents returns internal format structure Data +func (lump *Visibility) Contents() *primitives.Vis { + return &lump.Data +} + +// ToBytes converts this lump back to raw byte Data +func (lump *Visibility) ToBytes() ([]byte, error) { + return marshallBasicLump(lump.Data.BitVectors) +} diff --git a/lump/worldlight.go b/lump/worldlight.go new file mode 100644 index 0000000..bf79552 --- /dev/null +++ b/lump/worldlight.go @@ -0,0 +1,32 @@ +package lump + +import ( + primitives "github.com/galaco/bsp/lump/primitive/worldlight" +) + +// WorldLight is Lump 15: Worldlight +type WorldLight struct { + Metadata + Data []primitives.WorldLight `json:"data"` +} + +// FromBytes imports this lump from raw byte Data +func (lump *WorldLight) FromBytes(raw []byte) error { + data, err := unmarshallBasicLump[primitives.WorldLight](raw) + if err != nil { + return err + } + + lump.Data = data + return nil +} + +// Contents returns internal format structure Data +func (lump *WorldLight) Contents() []primitives.WorldLight { + return lump.Data +} + +// ToBytes converts this lump back to raw byte Data +func (lump *WorldLight) ToBytes() ([]byte, error) { + return marshallBasicLump(lump.Data) +} diff --git a/lump/worldlighthdr.go b/lump/worldlighthdr.go new file mode 100644 index 0000000..7b7aa7e --- /dev/null +++ b/lump/worldlighthdr.go @@ -0,0 +1,35 @@ +package lump + +import ( + "fmt" + + primitives "github.com/galaco/bsp/lump/primitive/worldlight" +) + +// WorldLightHDR is Lump 15: Worldlight +type WorldLightHDR struct { + Metadata + Data []primitives.WorldLightHDR `json:"data"` +} + +// FromBytes imports this lump from raw byte Data +func (lump *WorldLightHDR) FromBytes(raw []byte) error { + data, err := unmarshallTaggedLump[primitives.WorldLightHDR](raw, fmt.Sprintf("v%d", lump.Version())) + if err != nil { + return err + } + + lump.Data = data + + return nil +} + +// Contents returns internal format structure Data +func (lump *WorldLightHDR) Contents() []primitives.WorldLightHDR { + return lump.Data +} + +// ToBytes converts this lump back to raw byte Data +func (lump *WorldLightHDR) ToBytes() ([]byte, error) { + return marshallTaggedLump[primitives.WorldLightHDR](lump.Data, fmt.Sprintf("v%d", lump.Version())) +} diff --git a/lumpFlags.go b/lumpFlags.go index 12fb11f..38ac3e1 100644 --- a/lumpFlags.go +++ b/lumpFlags.go @@ -141,7 +141,8 @@ const ( LumpLeafAmbientLightingHDR = LumpId(55) // LumpLeafAmbientLighting LumpLeafAmbientLighting = LumpId(56) - // LumpXZipPakfile is deprecated, and xbox specific + // LumpXZipPakfile is xbox specific. + // Deprecated: Valve says it's deprecated. LumpXZipPakfile = LumpId(57) // LumpFacesHDR LumpFacesHDR = LumpId(58) diff --git a/lump_test.go b/lump_test.go deleted file mode 100644 index 9511b8a..0000000 --- a/lump_test.go +++ /dev/null @@ -1,149 +0,0 @@ -package bsp - -import ( - "github.com/galaco/bsp/lumps" - "reflect" - "testing" -) - -func TestGetReferenceLumpByIndex(t *testing.T) { - for i := 0; i < 64; i++ { - lump, _ := getReferenceLumpByIndex(i, 20) - if reflect.TypeOf(lump) != reflect.TypeOf(getExpectedLump(i)) { - t.Errorf("Lump type does not match expected for identifer: %d\n", i) - } - } - - _, err := getReferenceLumpByIndex(65, 20) - if err == nil { - t.Error("invalid lump provided, but no error returned") - } -} - -func TestLump_Contents(t *testing.T) { - sut := new(Lump) - l := new(lumps.Generic) - l.SetLength(654) - sut.SetContents(l) - if sut.Contents() != l { - t.Error("unexpected lump data returned") - } -} - -func TestLump_Length(t *testing.T) { - sut := Lump{} - sut.length = 32 - if sut.Length() != 32 { - t.Error("incorrect length returned for lump") - } -} - -func TestLump_RawContents(t *testing.T) { - sut := new(Lump) - data := []byte{0,1,3,4,5,6} - sut.SetRawContents(data) - if len(sut.RawContents()) != len(data) { - t.Error("unexpected lump data returned") - } -} - -func TestLump_SetContents(t *testing.T) { - sut := new(Lump) - l := new(lumps.Generic) - l.SetLength(654) - sut.SetContents(l) - if sut.Contents() != l { - t.Error("unexpected lump data returned") - } -} - -func TestLump_SetId(t *testing.T) { - sut := Lump{} - sut.SetId(LumpPakfile) - - if sut.id != LumpPakfile { - t.Error("incorrect lump id") - } -} - -func TestLump_SetRawContents(t *testing.T) { - sut := Lump{} - data := []byte{0, 1, 4, 3, 2} - sut.SetRawContents(data) - for idx, b := range sut.RawContents() { - if data[idx] != b { - t.Error("raw lump data mismatch") - } - } -} - -func getExpectedLump(index int) lumps.ILump { - lMap := [64]lumps.ILump{ - &lumps.EntData{}, - &lumps.Planes{}, - &lumps.TexData{}, - &lumps.Vertex{}, - &lumps.Visibility{}, - &lumps.Node{}, - &lumps.TexInfo{}, - &lumps.Face{}, - &lumps.Lighting{}, - &lumps.Occlusion{}, - &lumps.Leaf{}, - &lumps.FaceId{}, - &lumps.Edge{}, - &lumps.Surfedge{}, - &lumps.Model{}, - &lumps.WorldLight{}, - &lumps.LeafFace{}, - &lumps.LeafBrush{}, - &lumps.Brush{}, - &lumps.BrushSide{}, - &lumps.Area{}, - &lumps.AreaPortal{}, - &lumps.Unimplemented{}, - &lumps.Unimplemented{}, - &lumps.Unimplemented{}, - &lumps.Unimplemented{}, - &lumps.DispInfo{}, - &lumps.Face{}, - &lumps.PhysDisp{}, - &lumps.Unimplemented{}, - &lumps.VertNormal{}, - &lumps.VertNormalIndice{}, - &lumps.Unimplemented{}, - &lumps.DispVert{}, - &lumps.DispLightmapSamplePosition{}, - &lumps.Game{}, - &lumps.LeafWaterData{}, - &lumps.Unimplemented{}, - &lumps.PrimVert{}, - &lumps.PrimIndice{}, - &lumps.Pakfile{}, - &lumps.ClipPortalVerts{}, - &lumps.Cubemap{}, - &lumps.TexDataStringData{}, - &lumps.TexDataStringTable{}, - &lumps.Overlay{}, - &lumps.LeafMinDistToWater{}, - &lumps.FaceMacroTextureInfo{}, - &lumps.DispTris{}, - &lumps.Unimplemented{}, - &lumps.Unimplemented{}, - &lumps.LeafAmbientIndexHDR{}, - &lumps.LeafAmbientIndex{}, - &lumps.Lighting{}, - &lumps.WorldLightHDR{}, - &lumps.LeafAmbientLightingHDR{}, - &lumps.LeafAmbientLighting{}, - &lumps.Unimplemented{}, - &lumps.FaceHDR{}, - &lumps.MapFlags{}, - &lumps.OverlayFade{}, - &lumps.Unimplemented{}, - &lumps.Unimplemented{}, - &lumps.Unimplemented{}, //disp multiblend - } - - return lMap[index] -} diff --git a/lumps/area.go b/lumps/area.go deleted file mode 100644 index 1a9a40b..0000000 --- a/lumps/area.go +++ /dev/null @@ -1,44 +0,0 @@ -package lumps - -import ( - "bytes" - "encoding/binary" - primitives "github.com/galaco/bsp/primitives/area" - "unsafe" -) - -// Area is Lump 20: Areas -type Area struct { - Generic - data []primitives.Area -} - -// Unmarshall Imports this lump from raw byte data -func (lump *Area) Unmarshall(raw []byte) (err error) { - length := len(raw) - lump.Metadata.SetLength(length) - if length == 0 { - return nil - } - - lump.data = make([]primitives.Area, length/int(unsafe.Sizeof(primitives.Area{}))) - err = binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.data) - if err != nil { - return err - } - lump.Metadata.SetLength(length) - - return nil -} - -// GetData gets internal format structure data -func (lump *Area) GetData() []primitives.Area { - return lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *Area) Marshall() ([]byte, error) { - var buf bytes.Buffer - err := binary.Write(&buf, binary.LittleEndian, lump.data) - return buf.Bytes(), err -} diff --git a/lumps/areaportal.go b/lumps/areaportal.go deleted file mode 100644 index dcf0533..0000000 --- a/lumps/areaportal.go +++ /dev/null @@ -1,42 +0,0 @@ -package lumps - -import ( - "bytes" - "encoding/binary" - primitives "github.com/galaco/bsp/primitives/areaportal" - "unsafe" -) - -// AreaPortal is Lump 21: Areaportals -type AreaPortal struct { - Generic - data []primitives.AreaPortal -} - -// Unmarshall Imports this lump from raw byte data -func (lump *AreaPortal) Unmarshall(raw []byte) (err error) { - length := len(raw) - lump.Metadata.SetLength(length) - if length == 0 { - return nil - } - lump.data = make([]primitives.AreaPortal, length/int(unsafe.Sizeof(primitives.AreaPortal{}))) - err = binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.data) - if err != nil { - return err - } - - return nil -} - -// GetData gets internal format structure data -func (lump *AreaPortal) GetData() []primitives.AreaPortal { - return lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *AreaPortal) Marshall() ([]byte, error) { - var buf bytes.Buffer - err := binary.Write(&buf, binary.LittleEndian, lump.data) - return buf.Bytes(), err -} diff --git a/lumps/brush.go b/lumps/brush.go deleted file mode 100644 index 11fc707..0000000 --- a/lumps/brush.go +++ /dev/null @@ -1,39 +0,0 @@ -package lumps - -import ( - "bytes" - "encoding/binary" - primitives "github.com/galaco/bsp/primitives/brush" - "unsafe" -) - -// Brush is Lump 18: Brush -type Brush struct { - Generic - data []primitives.Brush -} - -// Unmarshall Imports this lump from raw byte data -func (lump *Brush) Unmarshall(raw []byte) (err error) { - length := len(raw) - lump.data = make([]primitives.Brush, length/int(unsafe.Sizeof(primitives.Brush{}))) - err = binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.data) - if err != nil { - return err - } - lump.Metadata.SetLength(length) - - return nil -} - -// GetData gets internal format structure data -func (lump *Brush) GetData() []primitives.Brush { - return lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *Brush) Marshall() ([]byte, error) { - var buf bytes.Buffer - err := binary.Write(&buf, binary.LittleEndian, lump.data) - return buf.Bytes(), err -} diff --git a/lumps/brushside.go b/lumps/brushside.go deleted file mode 100644 index 4286381..0000000 --- a/lumps/brushside.go +++ /dev/null @@ -1,39 +0,0 @@ -package lumps - -import ( - "bytes" - "encoding/binary" - primitives "github.com/galaco/bsp/primitives/brushside" - "unsafe" -) - -// BrushSide is Lump 19: BrushSide -type BrushSide struct { - Generic - data []primitives.BrushSide // MAX_MAP_BRUSHSIDES = 65536 -} - -// Unmarshall Imports this lump from raw byte data -func (lump *BrushSide) Unmarshall(raw []byte) (err error) { - length := len(raw) - lump.data = make([]primitives.BrushSide, length/int(unsafe.Sizeof(primitives.BrushSide{}))) - err = binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.data) - if err != nil { - return err - } - lump.Metadata.SetLength(length) - - return nil -} - -// GetData gets internal format structure data -func (lump *BrushSide) GetData() []primitives.BrushSide { - return lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *BrushSide) Marshall() ([]byte, error) { - var buf bytes.Buffer - err := binary.Write(&buf, binary.LittleEndian, lump.data) - return buf.Bytes(), err -} diff --git a/lumps/clipportalverts.go b/lumps/clipportalverts.go deleted file mode 100644 index 13fd16f..0000000 --- a/lumps/clipportalverts.go +++ /dev/null @@ -1,38 +0,0 @@ -package lumps - -import ( - "bytes" - "encoding/binary" - "github.com/go-gl/mathgl/mgl32" - "unsafe" -) - -// ClipPortalVerts is Lump 41: ClipPortalVerts -type ClipPortalVerts struct { - Generic - data []mgl32.Vec3 -} - -// Unmarshall Imports this lump from raw byte data -func (lump *ClipPortalVerts) Unmarshall(raw []byte) (err error) { - length := len(raw) - lump.data = make([]mgl32.Vec3, length/int(unsafe.Sizeof(mgl32.Vec3{}))) - err = binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.data) - if err != nil { - return err - } - lump.Metadata.SetLength(length) - return nil -} - -// GetData gets internal format structure data -func (lump *ClipPortalVerts) GetData() []mgl32.Vec3 { - return lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *ClipPortalVerts) Marshall() ([]byte, error) { - var buf bytes.Buffer - err := binary.Write(&buf, binary.LittleEndian, lump.data) - return buf.Bytes(), err -} diff --git a/lumps/cubemap.go b/lumps/cubemap.go deleted file mode 100644 index 90519e7..0000000 --- a/lumps/cubemap.go +++ /dev/null @@ -1,42 +0,0 @@ -package lumps - -import ( - "bytes" - "encoding/binary" - primitives "github.com/galaco/bsp/primitives/cubemap" - "unsafe" -) - -// Cubemap is Lump 42: Cubemaps -type Cubemap struct { - Generic - data []primitives.CubemapSample -} - -// Unmarshall Imports this lump from raw byte data -func (lump *Cubemap) Unmarshall(raw []byte) (err error) { - length := len(raw) - lump.Metadata.SetLength(length) - if length == 0 { - return - } - lump.data = make([]primitives.CubemapSample, length/int(unsafe.Sizeof(primitives.CubemapSample{}))) - err = binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.data) - if err != nil { - return err - } - - return nil -} - -// GetData gets internal format structure data -func (lump *Cubemap) GetData() []primitives.CubemapSample { - return lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *Cubemap) Marshall() ([]byte, error) { - var buf bytes.Buffer - err := binary.Write(&buf, binary.LittleEndian, lump.data) - return buf.Bytes(), err -} diff --git a/lumps/dispinfo.go b/lumps/dispinfo.go deleted file mode 100644 index 30f88c2..0000000 --- a/lumps/dispinfo.go +++ /dev/null @@ -1,39 +0,0 @@ -package lumps - -import ( - "bytes" - "encoding/binary" - primitives "github.com/galaco/bsp/primitives/dispinfo" - "unsafe" -) - -// DispInfo is Lump 26: DispInfo -type DispInfo struct { - Generic - data []primitives.DispInfo -} - -// Unmarshall Imports this lump from raw byte data -func (lump *DispInfo) Unmarshall(raw []byte) (err error) { - length := len(raw) - lump.data = make([]primitives.DispInfo, length/int(unsafe.Sizeof(primitives.DispInfo{}))) - err = binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.data) - if err != nil { - return err - } - lump.Metadata.SetLength(length) - - return nil -} - -// GetData gets internal format structure data -func (lump *DispInfo) GetData() []primitives.DispInfo { - return lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *DispInfo) Marshall() ([]byte, error) { - var buf bytes.Buffer - err := binary.Write(&buf, binary.LittleEndian, lump.data) - return buf.Bytes(), err -} diff --git a/lumps/displightmapsampleposition.go b/lumps/displightmapsampleposition.go deleted file mode 100644 index f0d969e..0000000 --- a/lumps/displightmapsampleposition.go +++ /dev/null @@ -1,26 +0,0 @@ -package lumps - -// DispLightmapSamplePosition is Lump 34: DispLightmapSamplePosition -// NOTE: This does NOT have a mapped format yet, and is readable as []byte only -type DispLightmapSamplePosition struct { - Generic - data []byte -} - -// Unmarshall Imports this lump from raw byte data -func (lump *DispLightmapSamplePosition) Unmarshall(raw []byte) (err error) { - lump.data = raw - lump.Metadata.SetLength(len(raw)) - - return nil -} - -// GetData gets internal format structure data -func (lump *DispLightmapSamplePosition) GetData() []byte { - return lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *DispLightmapSamplePosition) Marshall() ([]byte, error) { - return lump.data, nil -} diff --git a/lumps/disptris.go b/lumps/disptris.go deleted file mode 100644 index 0b2542c..0000000 --- a/lumps/disptris.go +++ /dev/null @@ -1,40 +0,0 @@ -package lumps - -import ( - "bytes" - "encoding/binary" - primitives "github.com/galaco/bsp/primitives/disptris" - "unsafe" -) - -// DispTris is Lump 48: DispTris -type DispTris struct { - Generic - data []primitives.DispTri -} - -// Unmarshall Imports this lump from raw byte data -func (lump *DispTris) Unmarshall(raw []byte) (err error) { - length := len(raw) - lump.Metadata.SetLength(length) - if length == 0 { - return - } - - lump.data = make([]primitives.DispTri, length/int(unsafe.Sizeof(primitives.DispTri{}))) - err = binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.data) - - return err -} - -// GetData gets internal format structure data -func (lump *DispTris) GetData() []primitives.DispTri { - return lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *DispTris) Marshall() ([]byte, error) { - var buf bytes.Buffer - err := binary.Write(&buf, binary.LittleEndian, lump.data) - return buf.Bytes(), err -} diff --git a/lumps/dispvert.go b/lumps/dispvert.go deleted file mode 100644 index 201d0d5..0000000 --- a/lumps/dispvert.go +++ /dev/null @@ -1,40 +0,0 @@ -package lumps - -import ( - "bytes" - "encoding/binary" - primitives "github.com/galaco/bsp/primitives/dispvert" - "unsafe" -) - -// DispVert is Lump 33: DispVert -type DispVert struct { - Generic - data []primitives.DispVert -} - -// Unmarshall Imports this lump from raw byte data -func (lump *DispVert) Unmarshall(raw []byte) (err error) { - length := len(raw) - lump.Metadata.SetLength(length) - if length == 0 { - return - } - - lump.data = make([]primitives.DispVert, length/int(unsafe.Sizeof(primitives.DispVert{}))) - err = binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.data) - - return err -} - -// GetData gets internal format structure data -func (lump *DispVert) GetData() []primitives.DispVert { - return lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *DispVert) Marshall() ([]byte, error) { - var buf bytes.Buffer - err := binary.Write(&buf, binary.LittleEndian, lump.data) - return buf.Bytes(), err -} diff --git a/lumps/edge.go b/lumps/edge.go deleted file mode 100644 index d3743c8..0000000 --- a/lumps/edge.go +++ /dev/null @@ -1,37 +0,0 @@ -package lumps - -import ( - "bytes" - "encoding/binary" -) - -// Edge is Lump 12: Edge -type Edge struct { - Generic - data [][2]uint16 // MAX_MAP_EDGES = 256000 -} - -// Unmarshall Imports this lump from raw byte data -func (lump *Edge) Unmarshall(raw []byte) (err error) { - length := len(raw) - lump.data = make([][2]uint16, length/4) - err = binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.data) - if err != nil { - return err - } - lump.Metadata.SetLength(length) - - return err -} - -// GetData gets internal format structure data -func (lump *Edge) GetData() [][2]uint16 { - return lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *Edge) Marshall() ([]byte, error) { - var buf bytes.Buffer - err := binary.Write(&buf, binary.LittleEndian, lump.data) - return buf.Bytes(), err -} diff --git a/lumps/entdata.go b/lumps/entdata.go deleted file mode 100644 index a2a0b41..0000000 --- a/lumps/entdata.go +++ /dev/null @@ -1,26 +0,0 @@ -package lumps - -// EntData is Lump 0: Entdata -type EntData struct { - Generic - data string -} - -// Unmarshall Imports this lump from raw byte data -func (lump *EntData) Unmarshall(raw []byte) (err error) { - lump.data = string(raw) - length := len(raw) - lump.Metadata.SetLength(length) - - return err -} - -// GetData gets internal format structure data -func (lump *EntData) GetData() string { - return lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *EntData) Marshall() ([]byte, error) { - return []byte(lump.data), nil -} diff --git a/lumps/entdata_test.go b/lumps/entdata_test.go deleted file mode 100644 index e29e4dc..0000000 --- a/lumps/entdata_test.go +++ /dev/null @@ -1,43 +0,0 @@ -package lumps - -import "testing" - -func TestEntData_GetData(t *testing.T) { - sut := EntData{} - data := []byte{0, 1, 2, 5, 43, 156, 146, 3, 3, 6} - if err := sut.Unmarshall(data); err != nil { - t.Error(err) - } - if string(sut.GetData()) != string(data) { - t.Error("mismatched between expected and actual unmarshalled bytes") - } -} - -func TestEntData_Marshall(t *testing.T) { - sut := EntData{} - data := []byte{0, 1, 2, 5, 43, 156, 146, 3, 3, 6} - if err := sut.Unmarshall(data); err != nil { - t.Error(err) - } - if string(sut.GetData()) != string(data) { - t.Error("mismatched between expected and actual unmarshalled bytes") - } - res, err := sut.Marshall() - if err != nil { - t.Errorf("unexpected error during marshalling") - } - if string(res) != string(data) { - t.Error("mismatched between expected and actual marshalled bytes") - } -} - -func TestEntData_Unmarshall(t *testing.T) { - sut := EntData{} - data := []byte{0, 1, 2, 5, 43, 156, 146, 3, 3, 6} - if err := sut.Unmarshall(data); err != nil { - t.Error(err) - } - if string(sut.data) != string(data) { - t.Error("mismatched between expected and actual unmarshalled bytes") - } -} diff --git a/lumps/face.go b/lumps/face.go deleted file mode 100644 index 0aa068c..0000000 --- a/lumps/face.go +++ /dev/null @@ -1,39 +0,0 @@ -package lumps - -import ( - "bytes" - "encoding/binary" - primitives "github.com/galaco/bsp/primitives/face" - "unsafe" -) - -// Face is Lump 7: Face -type Face struct { - Generic - data []primitives.Face -} - -// Unmarshall Imports this lump from raw byte data -func (lump *Face) Unmarshall(raw []byte) (err error) { - length := len(raw) - lump.data = make([]primitives.Face, length/int(unsafe.Sizeof(primitives.Face{}))) - err = binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.data) - if err != nil { - return err - } - lump.Metadata.SetLength(length) - - return err -} - -// GetData gets internal format structure data -func (lump *Face) GetData() []primitives.Face { - return lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *Face) Marshall() ([]byte, error) { - var buf bytes.Buffer - err := binary.Write(&buf, binary.LittleEndian, lump.data) - return buf.Bytes(), err -} diff --git a/lumps/facehdr.go b/lumps/facehdr.go deleted file mode 100644 index a3e997d..0000000 --- a/lumps/facehdr.go +++ /dev/null @@ -1,39 +0,0 @@ -package lumps - -import ( - "bytes" - "encoding/binary" - primitives "github.com/galaco/bsp/primitives/face" - "unsafe" -) - -// FaceHDR is Lump 58: FaceHDR -type FaceHDR struct { - Generic - data []primitives.Face -} - -// Unmarshall Imports this lump from raw byte data -func (lump *FaceHDR) Unmarshall(raw []byte) (err error) { - length := len(raw) - lump.data = make([]primitives.Face, length/int(unsafe.Sizeof(primitives.Face{}))) - err = binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.data) - if err != nil { - return err - } - lump.Metadata.SetLength(length) - - return err -} - -// GetData gets internal format structure data -func (lump *FaceHDR) GetData() []primitives.Face { - return lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *FaceHDR) Marshall() ([]byte, error) { - var buf bytes.Buffer - err := binary.Write(&buf, binary.LittleEndian, lump.data) - return buf.Bytes(), err -} diff --git a/lumps/faceid.go b/lumps/faceid.go deleted file mode 100644 index 3a540f3..0000000 --- a/lumps/faceid.go +++ /dev/null @@ -1,39 +0,0 @@ -package lumps - -import ( - "bytes" - "encoding/binary" - primitives "github.com/galaco/bsp/primitives/faceid" - "unsafe" -) - -// FaceId is Lump 11: FaceIds -type FaceId struct { - Generic - data []primitives.FaceId -} - -// Unmarshall Imports this lump from raw byte data -func (lump *FaceId) Unmarshall(raw []byte) (err error) { - length := len(raw) - lump.Metadata.SetLength(length) - if length == 0 { - return - } - lump.data = make([]primitives.FaceId, length/int(unsafe.Sizeof(primitives.FaceId{}))) - err = binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.data) - - return err -} - -// GetData gets internal format structure data -func (lump *FaceId) GetData() []primitives.FaceId { - return lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *FaceId) Marshall() ([]byte, error) { - var buf bytes.Buffer - err := binary.Write(&buf, binary.LittleEndian, lump.data) - return buf.Bytes(), err -} diff --git a/lumps/facemacrotextureinfo.go b/lumps/facemacrotextureinfo.go deleted file mode 100644 index 4c27667..0000000 --- a/lumps/facemacrotextureinfo.go +++ /dev/null @@ -1,40 +0,0 @@ -package lumps - -import ( - "bytes" - "encoding/binary" - primitives "github.com/galaco/bsp/primitives/facemacrotextureinfo" - "unsafe" -) - -// FaceMacroTextureInfo is Lump 47: FaceMacroTextureInfo -type FaceMacroTextureInfo struct { - Generic - data []primitives.FaceMacroTextureInfo -} - -// Unmarshall Imports this lump from raw byte data -func (lump *FaceMacroTextureInfo) Unmarshall(raw []byte) (err error) { - length := len(raw) - lump.Metadata.SetLength(length) - if length == 0 { - return - } - - lump.data = make([]primitives.FaceMacroTextureInfo, length/int(unsafe.Sizeof(primitives.FaceMacroTextureInfo{}))) - err = binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.data) - - return err -} - -// GetData gets internal format structure data -func (lump *FaceMacroTextureInfo) GetData() []primitives.FaceMacroTextureInfo { - return lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *FaceMacroTextureInfo) Marshall() ([]byte, error) { - var buf bytes.Buffer - err := binary.Write(&buf, binary.LittleEndian, lump.data) - return buf.Bytes(), err -} diff --git a/lumps/game.go b/lumps/game.go deleted file mode 100644 index f28ba2a..0000000 --- a/lumps/game.go +++ /dev/null @@ -1,266 +0,0 @@ -package lumps - -import ( - "bytes" - "encoding/binary" - primitives "github.com/galaco/bsp/primitives/game" - "strings" - "unsafe" -) - -// Game is Lump 35. -// @TODO NOTE: This really needs per-game implementations to be completely useful, -// otherwise we only get staticprop data from it -type Game struct { - Generic - Header primitives.Header - GameLumps []primitives.GenericGameLump - LumpOffset int32 - areOffsetsCorrected bool -} - -// Unmarshall Imports this lump from raw byte data -func (lump *Game) Unmarshall(raw []byte) (err error) { - length := len(raw) - lump.Metadata.SetLength(length) - - if len(raw) == 0 { - return - } - - // First reconstruct the header to be of the right size - lumpCount := binary.LittleEndian.Uint32(raw[:4]) - lump.Header.SetLumpCount(int32(lumpCount)) - - // Read header - lump.Header.GameLumps = make([]primitives.LumpDef, lumpCount) - headerSize := 4 + (int32(unsafe.Sizeof(primitives.LumpDef{})) * int32(lumpCount)) - err = binary.Read(bytes.NewBuffer(raw[4:headerSize]), binary.LittleEndian, &lump.Header.GameLumps) - if err != nil { - return err - } - - // Correct file offsets - if !lump.areOffsetsCorrected { - for index := range lump.Header.GameLumps { - lump.Header.GameLumps[index].FileOffset -= lump.LumpOffset - } - lump.areOffsetsCorrected = true - } - - // Read gamelumps - lump.GameLumps = make([]primitives.GenericGameLump, lumpCount) - for i, lumpHeader := range lump.Header.GameLumps { - lump.GameLumps[i].Length = lumpHeader.FileLength - lump.GameLumps[i].Data = raw[lumpHeader.FileOffset : lumpHeader.FileOffset+lumpHeader.FileLength] - } - - return err -} - -// GetData gets internal format structure data -func (lump *Game) GetData() *Game { - return lump -} - -// Marshall dumps this lump back to raw byte data -func (lump *Game) Marshall() ([]byte, error) { - var buf bytes.Buffer - err := binary.Write(&buf, binary.LittleEndian, lump.Header.LumpCount) - if err != nil { - return nil, err - } - for _, lumpHeader := range lump.Header.GameLumps { - if err = binary.Write(&buf, binary.LittleEndian, lumpHeader); err != nil { - return nil, err - } - } - for _, l := range lump.GameLumps { - if err = binary.Write(&buf, binary.LittleEndian, l.Length); err != nil { - return nil, err - } - if err = binary.Write(&buf, binary.LittleEndian, l.Data); err != nil { - return nil, err - } - } - return buf.Bytes(), err -} - -// UpdateInternalOffsets updates the lumps offsets to be relative to the lump, rather -// than the bsp start -func (lump *Game) UpdateInternalOffsets(fileOffset int32) *Game { - lump.LumpOffset = fileOffset - - return lump -} - -// GetStaticPropLump returns the staticprop lump -func (lump *Game) GetStaticPropLump() *primitives.StaticPropLump { - for i, gameLump := range lump.Header.GameLumps { - if gameLump.Id == primitives.StaticPropLumpId { - sprpLump := lump.GameLumps[i] - - offset := 0 - - //dict - numDicts := int32(0) - err := binary.Read(bytes.NewBuffer(sprpLump.Data[offset:offset+4]), binary.LittleEndian, &numDicts) - if err != nil { - return nil - } - offset += 4 - dicts := primitives.StaticPropDictLump{ - DictEntries: numDicts, - } - dictNames := make([]string, numDicts) - for i := int32(0); i < numDicts; i++ { - t := make([]byte, 128) - err = binary.Read(bytes.NewBuffer(sprpLump.Data[offset:offset+128]), binary.LittleEndian, &t) - if err != nil { - return nil - } - dictNames[i] = strings.TrimRight(string(t), "\x00") - offset += 128 - } - dicts.Name = dictNames - - //leaf - numLeafs := int32(0) - err = binary.Read(bytes.NewBuffer(sprpLump.Data[offset:offset+4]), binary.LittleEndian, &numLeafs) - if err != nil { - return nil - } - offset += 4 - leaf := primitives.StaticPropLeafLump{ - LeafEntries: numLeafs, - } - leafs := make([]uint16, numLeafs) - err = binary.Read(bytes.NewBuffer(sprpLump.Data[offset:offset+int(2*numLeafs)]), binary.LittleEndian, &leafs) - if err != nil { - return nil - } - leaf.Leaf = leafs - offset += int(2 * numLeafs) - - //props - numProps := int32(0) - err = binary.Read(bytes.NewBuffer(sprpLump.Data[offset:offset+4]), binary.LittleEndian, &numProps) - if err != nil { - return nil - } - offset += 4 - props := make([]primitives.IStaticPropDataLump, numProps) - propLumpSize := 0 - switch gameLump.Version { - case 4: - propLumpSize = int(unsafe.Sizeof(primitives.StaticPropV4{})) * int(numProps) - vprops := make([]primitives.StaticPropV4, numProps) - err = binary.Read(bytes.NewBuffer(sprpLump.Data[offset:offset+propLumpSize]), binary.LittleEndian, &vprops) - if err != nil { - return nil - } - for idx := range vprops { - props[idx] = primitives.IStaticPropDataLump(&vprops[idx]) - } - case 5: - propLumpSize = int(unsafe.Sizeof(primitives.StaticPropV5{})) * int(numProps) - vprops := make([]primitives.StaticPropV5, numProps) - err = binary.Read(bytes.NewBuffer(sprpLump.Data[offset:offset+propLumpSize]), binary.LittleEndian, &vprops) - if err != nil { - return nil - } - for idx := range vprops { - props[idx] = primitives.IStaticPropDataLump(&vprops[idx]) - } - case 6: - propLumpSize = int(unsafe.Sizeof(primitives.StaticPropV6{})) * int(numProps) - vprops := make([]primitives.StaticPropV6, numProps) - err = binary.Read(bytes.NewBuffer(sprpLump.Data[offset:offset+propLumpSize]), binary.LittleEndian, &vprops) - if err != nil { - return nil - } - for idx := range vprops { - props[idx] = primitives.IStaticPropDataLump(&vprops[idx]) - } - case 7: - propLumpSize = int(unsafe.Sizeof(primitives.StaticPropV7{})) * int(numProps) - vprops := make([]primitives.StaticPropV7, numProps) - err = binary.Read(bytes.NewBuffer(sprpLump.Data[offset:offset+propLumpSize]), binary.LittleEndian, &vprops) - if err != nil { - return nil - } - for idx := range vprops { - props[idx] = primitives.IStaticPropDataLump(&vprops[idx]) - } - case 8: - propLumpSize = int(unsafe.Sizeof(primitives.StaticPropV8{})) * int(numProps) - vprops := make([]primitives.StaticPropV8, numProps) - err = binary.Read(bytes.NewBuffer(sprpLump.Data[offset:offset+propLumpSize]), binary.LittleEndian, &vprops) - if err != nil { - return nil - } - for idx := range vprops { - props[idx] = primitives.IStaticPropDataLump(&vprops[idx]) - } - case 9: - propLumpSize = int(unsafe.Sizeof(primitives.StaticPropV9{})) * int(numProps) - vprops := make([]primitives.StaticPropV9, numProps) - err = binary.Read(bytes.NewBuffer(sprpLump.Data[offset:offset+propLumpSize]), binary.LittleEndian, &vprops) - if err != nil { - return nil - } - for idx := range vprops { - props[idx] = primitives.IStaticPropDataLump(&vprops[idx]) - } - case 10: - // This switch is a major hackjob to avoid the need to know what game the bsp is for. - // Because Valve in all their wisdom have multiple DIFFERENT v10 formats (a true v10, - // and the MP2013 updated v6 which is REPORTED as v10 as well) we can attempt to infer - // which format it actually is. - switch { - case offset+(int(unsafe.Sizeof(primitives.StaticPropV10{}))*int(numProps)) <= len(sprpLump.Data): - // Real v10 format - propLumpSize = int(unsafe.Sizeof(primitives.StaticPropV10{})) * int(numProps) - vprops := make([]primitives.StaticPropV10, numProps) - err = binary.Read(bytes.NewBuffer(sprpLump.Data[offset:offset+propLumpSize]), binary.LittleEndian, &vprops) - if err != nil { - return nil - } - for idx := range vprops { - props[idx] = primitives.IStaticPropDataLump(&vprops[idx]) - } - case offset+(int(unsafe.Sizeof(primitives.StaticPropV10MP2013{}))*int(numProps)) <= len(sprpLump.Data): - // Fake v7* 2013MP format. - propLumpSize = int(unsafe.Sizeof(primitives.StaticPropV10MP2013{})) * int(numProps) - vprops := make([]primitives.StaticPropV10MP2013, numProps) - err = binary.Read(bytes.NewBuffer(sprpLump.Data[offset:offset+propLumpSize]), binary.LittleEndian, &vprops) - if err != nil { - return nil - } - for idx := range vprops { - props[idx] = primitives.IStaticPropDataLump(&vprops[idx]) - } - default: - panic("staticpropdata doesn't correspond to a known v10 format") - } - case 11: - vprops := make([]primitives.StaticPropV11, numProps) - err = binary.Read(bytes.NewBuffer(sprpLump.Data[offset:]), binary.LittleEndian, &vprops) - if err != nil { - return nil - } - for idx := range vprops { - props[idx] = primitives.IStaticPropDataLump(&vprops[idx]) - } - } - - return &primitives.StaticPropLump{ - DictLump: dicts, - LeafLump: leaf, - PropLumps: props, - } - } - } - - return nil -} diff --git a/lumps/interface.go b/lumps/interface.go deleted file mode 100644 index fcdca8d..0000000 --- a/lumps/interface.go +++ /dev/null @@ -1,13 +0,0 @@ -package lumps - -// ILump Lump interface. -// Organise Lump data in a cleaner and more accessible manner -type ILump interface { - // Unmarshall Imports a []byte to a defined lump structure(s). - Unmarshall([]byte) error - - // Marshall Exports lump structure back to []byte. - Marshall() ([]byte, error) - - SetVersion(version int32) -} diff --git a/lumps/leaf.go b/lumps/leaf.go deleted file mode 100644 index c6f2361..0000000 --- a/lumps/leaf.go +++ /dev/null @@ -1,91 +0,0 @@ -package lumps - -import ( - "bytes" - "encoding/binary" - "fmt" - "github.com/galaco/bsp/primitives/common" - primitives "github.com/galaco/bsp/primitives/leaf" - "unsafe" -) - -const ( - // MaxMapLeafs is the maximum number of leafs the engine can handle - MaxMapLeafs = 65536 -) - -const ( - // maxBspVersionOfV0Leaf is the last bsp revision to use the old leafv0 structure - maxBspVersionOfV0Leaf = 19 -) - -// Leaf is Lump 10: Leaf -type Leaf struct { - Generic - data []primitives.Leaf -} - -// Unmarshall Imports this lump from raw byte data -func (lump *Leaf) Unmarshall(raw []byte) (err error) { - // There are 2 version of leaf: - // v0 contains a light sample - // v1 removes the light sample, and is padded by 2 bytes - structSize := int(unsafe.Sizeof(primitives.Leaf{})) - if lump.Version() > maxBspVersionOfV0Leaf { - // Correct size of v1+ leafs - structSize -= int(unsafe.Sizeof(common.CompressedLightCube{})) - } - - length := len(raw) - lump.data = make([]primitives.Leaf, length/structSize) - numLeafs := len(lump.data) - i := 0 - - for i < numLeafs { - leafBuf := make([]byte, structSize) - copy(leafBuf, raw[(structSize * i) : (structSize*i)+structSize]) - // Pad the raw data to the correct size of a leaf - if lump.Version() > maxBspVersionOfV0Leaf { - leafBuf = append(leafBuf, make([]byte, int(unsafe.Sizeof(common.CompressedLightCube{})))...) - } - rawLeaf := bytes.NewBuffer(leafBuf) - err = binary.Read(rawLeaf, binary.LittleEndian, &lump.data[i]) - if err != nil { - return err - } - i++ - if i > MaxMapLeafs { - return fmt.Errorf("leaf count overflows maximum allowed size of %d", MaxMapLeafs) - } - } - lump.SetLength(length) - - return err -} - -// GetData gets internal format structure data -func (lump *Leaf) GetData() []primitives.Leaf { - return lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *Leaf) Marshall() ([]byte, error) { - var buf bytes.Buffer - var err error - - switch lump.Version() { - case 0: - err = binary.Write(&buf, binary.LittleEndian, lump.data) - default: - structSize := int(unsafe.Sizeof(primitives.Leaf{})) - int(unsafe.Sizeof(common.CompressedLightCube{})) - for _, l := range lump.data { - var leafBuf bytes.Buffer - if err = binary.Write(&leafBuf, binary.LittleEndian, l); err != nil { - return nil, err - } - err = binary.Write(&buf, binary.LittleEndian, leafBuf.Bytes()[0:structSize]) - } - } - - return buf.Bytes(), err -} diff --git a/lumps/leafambientindex.go b/lumps/leafambientindex.go deleted file mode 100644 index 1b96598..0000000 --- a/lumps/leafambientindex.go +++ /dev/null @@ -1,39 +0,0 @@ -package lumps - -import ( - "bytes" - "encoding/binary" - primitives "github.com/galaco/bsp/primitives/leafambientindex" - "unsafe" -) - -// LeafAmbientIndex is Lump 52: Leaf Ambient Index -type LeafAmbientIndex struct { - Generic - data []primitives.LeafAmbientIndex -} - -// Unmarshall Imports this lump from raw byte data -func (lump *LeafAmbientIndex) Unmarshall(raw []byte) (err error) { - length := len(raw) - lump.Metadata.SetLength(length) - if length == 0 { - return - } - lump.data = make([]primitives.LeafAmbientIndex, length/int(unsafe.Sizeof(primitives.LeafAmbientIndex{}))) - err = binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.data) - - return err -} - -// GetData gets internal format structure data -func (lump *LeafAmbientIndex) GetData() []primitives.LeafAmbientIndex { - return lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *LeafAmbientIndex) Marshall() ([]byte, error) { - var buf bytes.Buffer - err := binary.Write(&buf, binary.LittleEndian, lump.data) - return buf.Bytes(), err -} diff --git a/lumps/leafambientindexhdr.go b/lumps/leafambientindexhdr.go deleted file mode 100644 index 1a7cde1..0000000 --- a/lumps/leafambientindexhdr.go +++ /dev/null @@ -1,42 +0,0 @@ -package lumps - -import ( - "bytes" - "encoding/binary" - primitives "github.com/galaco/bsp/primitives/leafambientindex" - "unsafe" -) - -// LeafAmbientIndexHDR is Lump 51: Leaf Ambient Index HDR -type LeafAmbientIndexHDR struct { - Generic - data []primitives.LeafAmbientIndex -} - -// Unmarshall Imports this lump from raw byte data -func (lump *LeafAmbientIndexHDR) Unmarshall(raw []byte) (err error) { - length := len(raw) - if length == 0 { - return - } - lump.data = make([]primitives.LeafAmbientIndex, length/int(unsafe.Sizeof(primitives.LeafAmbientIndex{}))) - err = binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.data) - if err != nil { - return err - } - lump.Metadata.SetLength(length) - - return err -} - -// GetData gets internal format structure data -func (lump *LeafAmbientIndexHDR) GetData() []primitives.LeafAmbientIndex { - return lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *LeafAmbientIndexHDR) Marshall() ([]byte, error) { - var buf bytes.Buffer - err := binary.Write(&buf, binary.LittleEndian, lump.data) - return buf.Bytes(), err -} diff --git a/lumps/leafambientlighting.go b/lumps/leafambientlighting.go deleted file mode 100644 index 35d64f0..0000000 --- a/lumps/leafambientlighting.go +++ /dev/null @@ -1,43 +0,0 @@ -package lumps - -import ( - "bytes" - "encoding/binary" - primitives "github.com/galaco/bsp/primitives/leafambientlighting" - "unsafe" -) - -// LeafAmbientLighting is Lump 56: LeafAmbientLighting -type LeafAmbientLighting struct { - Generic - data []primitives.LeafAmbientLighting -} - -// Unmarshall Imports this lump from raw byte data -func (lump *LeafAmbientLighting) Unmarshall(raw []byte) (err error) { - length := len(raw) - lump.Metadata.SetLength(length) - if length == 0 { - return - } - lump.data = make([]primitives.LeafAmbientLighting, length/int(unsafe.Sizeof(primitives.LeafAmbientLighting{}))) - err = binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.data) - if err != nil { - return err - } - lump.Metadata.SetLength(length) - - return err -} - -// GetData gets internal format structure data -func (lump *LeafAmbientLighting) GetData() []primitives.LeafAmbientLighting { - return lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *LeafAmbientLighting) Marshall() ([]byte, error) { - var buf bytes.Buffer - err := binary.Write(&buf, binary.LittleEndian, lump.data) - return buf.Bytes(), err -} diff --git a/lumps/leafambientlightinghdr.go b/lumps/leafambientlightinghdr.go deleted file mode 100644 index 87c8261..0000000 --- a/lumps/leafambientlightinghdr.go +++ /dev/null @@ -1,39 +0,0 @@ -package lumps - -import ( - "bytes" - "encoding/binary" - primitives "github.com/galaco/bsp/primitives/leafambientlighting" - "unsafe" -) - -// LeafAmbientLightingHDR is Lump 55: LeafAmbientLightingHDR -type LeafAmbientLightingHDR struct { - Generic - data []primitives.LeafAmbientLighting -} - -// Unmarshall Imports this lump from raw byte data -func (lump *LeafAmbientLightingHDR) Unmarshall(raw []byte) (err error) { - length := len(raw) - lump.Metadata.SetLength(length) - if length == 0 { - return - } - lump.data = make([]primitives.LeafAmbientLighting, length/int(unsafe.Sizeof(primitives.LeafAmbientLighting{}))) - err = binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.data) - - return err -} - -// GetData gets internal format structure data -func (lump *LeafAmbientLightingHDR) GetData() []primitives.LeafAmbientLighting { - return lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *LeafAmbientLightingHDR) Marshall() ([]byte, error) { - var buf bytes.Buffer - err := binary.Write(&buf, binary.LittleEndian, lump.data) - return buf.Bytes(), err -} diff --git a/lumps/leafbrush.go b/lumps/leafbrush.go deleted file mode 100644 index 2aad87c..0000000 --- a/lumps/leafbrush.go +++ /dev/null @@ -1,37 +0,0 @@ -package lumps - -import ( - "bytes" - "encoding/binary" -) - -// LeafBrush is Lump 17: LeafBrush -type LeafBrush struct { - Generic - data []uint16 // MAX_MAP_LEAFBRUSHES = 65536 -} - -// Unmarshall Imports this lump from raw byte data -func (lump *LeafBrush) Unmarshall(raw []byte) (err error) { - length := len(raw) - lump.data = make([]uint16, length/2) - err = binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.data) - if err != nil { - return err - } - lump.Metadata.SetLength(length) - - return err -} - -// GetData gets internal format structure data -func (lump *LeafBrush) GetData() []uint16 { - return lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *LeafBrush) Marshall() ([]byte, error) { - var buf bytes.Buffer - err := binary.Write(&buf, binary.LittleEndian, lump.data) - return buf.Bytes(), err -} diff --git a/lumps/leafface.go b/lumps/leafface.go deleted file mode 100644 index c4ae8de..0000000 --- a/lumps/leafface.go +++ /dev/null @@ -1,37 +0,0 @@ -package lumps - -import ( - "bytes" - "encoding/binary" -) - -// LeafFace is Lump 16: LeafFace -type LeafFace struct { - Generic - data []uint16 // MAX_MAP_LEAFFACES = 65536 -} - -// Unmarshall Imports this lump from raw byte data -func (lump *LeafFace) Unmarshall(raw []byte) (err error) { - length := len(raw) - lump.data = make([]uint16, length/2) - err = binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.data) - if err != nil { - return err - } - lump.Metadata.SetLength(length) - - return err -} - -// GetData gets internal format structure data -func (lump *LeafFace) GetData() []uint16 { - return lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *LeafFace) Marshall() ([]byte, error) { - var buf bytes.Buffer - err := binary.Write(&buf, binary.LittleEndian, lump.data) - return buf.Bytes(), err -} diff --git a/lumps/leafmindisttowater.go b/lumps/leafmindisttowater.go deleted file mode 100644 index e2b0d22..0000000 --- a/lumps/leafmindisttowater.go +++ /dev/null @@ -1,37 +0,0 @@ -package lumps - -import ( - "bytes" - "encoding/binary" -) - -// LeafMinDistToWater is Lump 46: LeafMinDistToWater -type LeafMinDistToWater struct { - Generic - data []uint16 -} - -// Unmarshall Imports this lump from raw byte data -func (lump *LeafMinDistToWater) Unmarshall(raw []byte) (err error) { - length := len(raw) - lump.data = make([]uint16, length/2) - err = binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.data) - if err != nil { - return err - } - lump.Metadata.SetLength(length) - - return err -} - -// GetData gets internal format structure data -func (lump *LeafMinDistToWater) GetData() []uint16 { - return lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *LeafMinDistToWater) Marshall() ([]byte, error) { - var buf bytes.Buffer - err := binary.Write(&buf, binary.LittleEndian, lump.data) - return buf.Bytes(), err -} diff --git a/lumps/leafwaterdata.go b/lumps/leafwaterdata.go deleted file mode 100644 index d6ce4d5..0000000 --- a/lumps/leafwaterdata.go +++ /dev/null @@ -1,39 +0,0 @@ -package lumps - -import ( - "bytes" - "encoding/binary" - primitives "github.com/galaco/bsp/primitives/leafwaterdata" - "unsafe" -) - -// LeafWaterData is Lump 36: leafwaterdata -type LeafWaterData struct { - Generic - data []primitives.LeafWaterData -} - -// Unmarshall Imports this lump from raw byte data -func (lump *LeafWaterData) Unmarshall(raw []byte) (err error) { - length := len(raw) - lump.Metadata.SetLength(length) - if length == 0 { - return - } - lump.data = make([]primitives.LeafWaterData, length/int(unsafe.Sizeof(primitives.LeafWaterData{}))) - err = binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.data) - - return err -} - -// GetData gets internal format structure data -func (lump *LeafWaterData) GetData() []primitives.LeafWaterData { - return lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *LeafWaterData) Marshall() ([]byte, error) { - var buf bytes.Buffer - err := binary.Write(&buf, binary.LittleEndian, lump.data) - return buf.Bytes(), err -} diff --git a/lumps/lighting.go b/lumps/lighting.go deleted file mode 100644 index e4c18b0..0000000 --- a/lumps/lighting.go +++ /dev/null @@ -1,39 +0,0 @@ -package lumps - -import ( - "bytes" - "encoding/binary" - primitives "github.com/galaco/bsp/primitives/common" - "unsafe" -) - -// Lighting is Lump 8: Lighting -type Lighting struct { - Generic - data []primitives.ColorRGBExponent32 -} - -// Unmarshall Imports this lump from raw byte data -func (lump *Lighting) Unmarshall(raw []byte) (err error) { - length := len(raw) - lump.Metadata.SetLength(length) - if length == 0 { - return - } - lump.data = make([]primitives.ColorRGBExponent32, length/int(unsafe.Sizeof(primitives.ColorRGBExponent32{}))) - err = binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.data) - - return err -} - -// GetData gets internal format structure data -func (lump *Lighting) GetData() []primitives.ColorRGBExponent32 { - return lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *Lighting) Marshall() ([]byte, error) { - var buf bytes.Buffer - err := binary.Write(&buf, binary.LittleEndian, lump.data) - return buf.Bytes(), err -} diff --git a/lumps/lump.go b/lumps/lump.go deleted file mode 100644 index 67f58e0..0000000 --- a/lumps/lump.go +++ /dev/null @@ -1,48 +0,0 @@ -package lumps - -// Generic is a raw byte lump -// It should be used in cases where the data has no or unknown -// structures. -type Generic struct { - Metadata - data []byte -} - -// Unmarshall Imports this lump from raw byte data -func (lump *Generic) Unmarshall(data []byte) (err error) { - lump.length = len(data) - lump.data = data - - return err -} - -// Marshall Dumps this lump back to raw byte data -func (lump *Generic) Marshall() ([]byte, error) { - return lump.data, nil -} - -// Metadata is a Helper info for a lump -type Metadata struct { - length int - version int32 -} - -// Length Returns lump import length in bytes. -func (info *Metadata) Length() int { - return info.length -} - -// SetLength sets lump import length in bytes -func (info *Metadata) SetLength(length int) { - info.length = length -} - -// Version Returns lump import version in bytes. -func (info *Metadata) Version() int32 { - return info.version -} - -// SetVersion sets bsp version of lump -func (info *Metadata) SetVersion(version int32) { - info.version = version -} diff --git a/lumps/lump_test.go b/lumps/lump_test.go deleted file mode 100644 index 894c7c4..0000000 --- a/lumps/lump_test.go +++ /dev/null @@ -1,74 +0,0 @@ -package lumps - -import ( - "log" - "testing" -) - -func TestGeneric_Marshall(t *testing.T) { - sut := Generic{} - data := []byte{0, 1, 2, 5, 43, 156, 146, 3, 3, 6} - if err := sut.Unmarshall(data); err != nil { - t.Error(err) - } - for idx, b := range sut.data { - if data[idx] != b { - t.Error("mismatched between expected and actual unmarshalled bytes") - } - } - res, err := sut.Marshall() - if err != nil { - t.Errorf("unexpected error during marshalling") - } - for idx, b := range res { - if data[idx] != b { - t.Error("mismatched between expected and actual marshalled bytes") - } - } -} - -func TestGeneric_Unmarshall(t *testing.T) { - sut := Generic{} - data := []byte{0, 1, 2, 5, 43, 156, 146, 3, 3, 6} - if err := sut.Unmarshall(data); err != nil { - t.Error(err) - } - for idx, b := range sut.data { - if data[idx] != b { - t.Error("mismatched between expected and actual unmarshalled bytes") - } - } -} - -func TestMetadata_Length(t *testing.T) { - sut := Metadata{} - sut.SetLength(32) - if sut.Length() != 32 { - t.Errorf("wrong length returned fron Length") - } -} - -func TestMetadata_SetLength(t *testing.T) { - sut := Metadata{} - sut.SetLength(32) - if sut.Length() != 32 { - log.Println(sut.length) - t.Errorf("wrong length returned fron Length") - } -} - -func TestMetadata_Version(t *testing.T) { - sut := Metadata{} - sut.SetVersion(11) - if sut.Version() != 11 { - t.Errorf("wrong version set") - } -} - -func TestMetadata_SetVersion(t *testing.T) { - sut := Metadata{} - sut.SetVersion(11) - if sut.version != 11 { - t.Errorf("wrong version set") - } -} diff --git a/lumps/mapflags.go b/lumps/mapflags.go deleted file mode 100644 index 8c906dd..0000000 --- a/lumps/mapflags.go +++ /dev/null @@ -1,41 +0,0 @@ -package lumps - -import ( - "bytes" - "encoding/binary" - primitives "github.com/galaco/bsp/primitives/mapflags" -) - -// MapFlags is Lump 59: MapFlags -type MapFlags struct { - Generic - data primitives.MapFlags -} - -// Unmarshall Imports this lump from raw byte data -func (lump *MapFlags) Unmarshall(raw []byte) (err error) { - length := len(raw) - if length == 0 { - return - } - - err = binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.data) - if err != nil { - return err - } - lump.Metadata.SetLength(length) - - return err -} - -// GetData gets internal format structure data -func (lump *MapFlags) GetData() *primitives.MapFlags { - return &lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *MapFlags) Marshall() ([]byte, error) { - var buf bytes.Buffer - err := binary.Write(&buf, binary.LittleEndian, lump.data) - return buf.Bytes(), err -} diff --git a/lumps/model.go b/lumps/model.go deleted file mode 100644 index 99edabb..0000000 --- a/lumps/model.go +++ /dev/null @@ -1,39 +0,0 @@ -package lumps - -import ( - "bytes" - "encoding/binary" - primitives "github.com/galaco/bsp/primitives/model" - "unsafe" -) - -// Model is Lump 14: Model -type Model struct { - Generic - data []primitives.Model -} - -// Unmarshall Imports this lump from raw byte data -func (lump *Model) Unmarshall(raw []byte) (err error) { - length := len(raw) - lump.data = make([]primitives.Model, length/int(unsafe.Sizeof(primitives.Model{}))) - err = binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.data) - if err != nil { - return err - } - lump.Metadata.SetLength(length) - - return err -} - -// GetData gets internal format structure data -func (lump *Model) GetData() []primitives.Model { - return lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *Model) Marshall() ([]byte, error) { - var buf bytes.Buffer - err := binary.Write(&buf, binary.LittleEndian, lump.data) - return buf.Bytes(), err -} diff --git a/lumps/node.go b/lumps/node.go deleted file mode 100644 index 4c770ba..0000000 --- a/lumps/node.go +++ /dev/null @@ -1,39 +0,0 @@ -package lumps - -import ( - "bytes" - "encoding/binary" - primitives "github.com/galaco/bsp/primitives/node" - "unsafe" -) - -// Node is Lump 5: Node -type Node struct { - Generic - data []primitives.Node // MAP_MAX_NODES = 65536 -} - -// Unmarshall Imports this lump from raw byte data -func (lump *Node) Unmarshall(raw []byte) (err error) { - length := len(raw) - lump.data = make([]primitives.Node, length/int(unsafe.Sizeof(primitives.Node{}))) - err = binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.data) - if err != nil { - return err - } - lump.Metadata.SetLength(length) - - return err -} - -// GetData gets internal format structure data -func (lump *Node) GetData() []primitives.Node { - return lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *Node) Marshall() ([]byte, error) { - var buf bytes.Buffer - err := binary.Write(&buf, binary.LittleEndian, lump.data) - return buf.Bytes(), err -} diff --git a/lumps/occlusion.go b/lumps/occlusion.go deleted file mode 100644 index 0780d02..0000000 --- a/lumps/occlusion.go +++ /dev/null @@ -1,108 +0,0 @@ -package lumps - -import ( - "bytes" - "encoding/binary" - primitives "github.com/galaco/bsp/primitives/occlusion" - "unsafe" -) - -// Occlusion is Lump 9: Occlusion -type Occlusion struct { - Generic - Count int32 - Data []primitives.OcclusionData // len(slice) = Count - PolyDataCount int32 - PolyData []primitives.OcclusionPolyData //len(slice) = PolyDataCount - VertexIndexCount int32 - VertexIndices []int32 //len(slice) = VertexIndexCount -} - -// Unmarshall Imports this lump from raw byte data -func (lump *Occlusion) Unmarshall(raw []byte) (err error) { - length := len(raw) - if length == 0 { - return - } - offset := 0 - // data - err = binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.Count) - if err != nil { - return err - } - offset += 4 - lump.Data = make([]primitives.OcclusionData, lump.Count) - err = binary.Read(bytes.NewBuffer(raw[offset:]), binary.LittleEndian, &lump.Data) - if err != nil { - return err - } - offset += int(unsafe.Sizeof(primitives.OcclusionData{})) * int(lump.Count) - - // polydata - err = binary.Read(bytes.NewBuffer(raw[offset:]), binary.LittleEndian, &lump.PolyDataCount) - if err != nil { - return err - } - offset += 4 - lump.PolyData = make([]primitives.OcclusionPolyData, lump.PolyDataCount) - err = binary.Read(bytes.NewBuffer(raw[offset:]), binary.LittleEndian, &lump.PolyData) - if err != nil { - return err - } - offset += int(unsafe.Sizeof(primitives.OcclusionPolyData{})) * int(lump.PolyDataCount) - - // vertexdata - err = binary.Read(bytes.NewBuffer(raw[offset:]), binary.LittleEndian, &lump.VertexIndexCount) - if err != nil { - return err - } - offset += 4 - lump.VertexIndices = make([]int32, lump.VertexIndexCount) - err = binary.Read(bytes.NewBuffer(raw[offset:]), binary.LittleEndian, &lump.VertexIndices) - if err != nil { - return err - } - - lump.Metadata.SetLength(length) - - return err -} - -// GetData gets internal format structure data -func (lump *Occlusion) GetData() *Occlusion { - return lump -} - -// Marshall dumps this lump back to raw byte data -func (lump *Occlusion) Marshall() ([]byte, error) { - var buf bytes.Buffer - // write data - err := binary.Write(&buf, binary.LittleEndian, lump.Count) - if err != nil { - return nil, err - } - for _, data := range lump.Data { - if err = binary.Write(&buf, binary.LittleEndian, data); err != nil { - return nil, err - } - } - - // write polydata - if err = binary.Write(&buf, binary.LittleEndian, lump.PolyDataCount); err != nil { - return nil, err - } - for _, data := range lump.PolyData { - if err = binary.Write(&buf, binary.LittleEndian, data); err != nil { - return nil, err - } - } - - // write indices - err = binary.Write(&buf, binary.LittleEndian, lump.VertexIndexCount) - for _, data := range lump.VertexIndices { - if err = binary.Write(&buf, binary.LittleEndian, data); err != nil { - return nil, err - } - } - return buf.Bytes(), err -} diff --git a/lumps/overlay.go b/lumps/overlay.go deleted file mode 100644 index 0600286..0000000 --- a/lumps/overlay.go +++ /dev/null @@ -1,40 +0,0 @@ -package lumps - -import ( - "bytes" - "encoding/binary" - primitives "github.com/galaco/bsp/primitives/overlay" - "unsafe" -) - -// Overlay is Lump 45: Overlay -// Consists of an array of Overlay structs -type Overlay struct { - Generic - data []primitives.Overlay -} - -// Unmarshall Imports this lump from raw byte data -func (lump *Overlay) Unmarshall(raw []byte) (err error) { - length := len(raw) - lump.Metadata.SetLength(length) - if length == 0 { - return - } - lump.data = make([]primitives.Overlay, length/int(unsafe.Sizeof(primitives.Overlay{}))) - err = binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.data) - - return err -} - -// GetData gets internal format structure data -func (lump *Overlay) GetData() []primitives.Overlay { - return lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *Overlay) Marshall() ([]byte, error) { - var buf bytes.Buffer - err := binary.Write(&buf, binary.LittleEndian, lump.data) - return buf.Bytes(), err -} diff --git a/lumps/overlayfade.go b/lumps/overlayfade.go deleted file mode 100644 index 2d3fbdf..0000000 --- a/lumps/overlayfade.go +++ /dev/null @@ -1,39 +0,0 @@ -package lumps - -import ( - "bytes" - "encoding/binary" - primitives "github.com/galaco/bsp/primitives/overlayfade" - "unsafe" -) - -// OverlayFade is Lump 60: Overlayfades -type OverlayFade struct { - Generic - data []primitives.OverlayFade -} - -// Unmarshall Imports this lump from raw byte data -func (lump *OverlayFade) Unmarshall(raw []byte) (err error) { - length := len(raw) - lump.Metadata.SetLength(length) - if length == 0 { - return - } - lump.data = make([]primitives.OverlayFade, length/int(unsafe.Sizeof(primitives.OverlayFade{}))) - err = binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.data) - - return err -} - -// GetData gets internal format structure data -func (lump *OverlayFade) GetData() []primitives.OverlayFade { - return lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *OverlayFade) Marshall() ([]byte, error) { - var buf bytes.Buffer - err := binary.Write(&buf, binary.LittleEndian, lump.data) - return buf.Bytes(), err -} diff --git a/lumps/physcollide.go b/lumps/physcollide.go deleted file mode 100644 index 0962525..0000000 --- a/lumps/physcollide.go +++ /dev/null @@ -1,56 +0,0 @@ -package lumps - -import ( - "bytes" - "encoding/binary" - primitives "github.com/galaco/bsp/primitives/physcollide" - "unsafe" -) - -// PhysCollide is Lump 20: PhysCollide -type PhysCollide struct { - Generic - data []primitives.PhysCollideEntry -} - -// Unmarshall Imports this lump from raw byte data -func (lump *PhysCollide) Unmarshall(raw []byte) (err error) { - length := len(raw) - lump.Metadata.SetLength(length) - if length == 0 { - return - } - - lump.data = make([]primitives.PhysCollideEntry, length/int(unsafe.Sizeof(primitives.PhysCollideEntry{}))) - err = binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.data) - - return err -} - -// GetData gets internal format structure data -func (lump *PhysCollide) GetData() []primitives.PhysCollideEntry { - return lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *PhysCollide) Marshall() ([]byte, error) { - var buf bytes.Buffer - for _, entry := range lump.data { - err := binary.Write(&buf, binary.LittleEndian, entry.ModelHeader) - if err != nil { - return nil, err - } - for _, solid := range entry.Solids { - if err = binary.Write(&buf, binary.LittleEndian, solid.Size); err != nil { - return nil, err - } - if err = binary.Write(&buf, binary.LittleEndian, solid.CollisionBinary); err != nil { - return nil, err - } - } - if err = binary.Write(&buf, binary.LittleEndian, []byte(entry.TextBuffer)); err != nil { - return nil, err - } - } - return buf.Bytes(), nil -} diff --git a/lumps/physdisp.go b/lumps/physdisp.go deleted file mode 100644 index d45998e..0000000 --- a/lumps/physdisp.go +++ /dev/null @@ -1,26 +0,0 @@ -package lumps - -// PhysDisp is Lump 28: PhysDisp -type PhysDisp struct { - Generic - data []byte -} - -// Unmarshall Imports this lump from raw byte data -func (lump *PhysDisp) Unmarshall(raw []byte) (err error) { - length := len(raw) - lump.data = raw - lump.Metadata.SetLength(length) - - return err -} - -// GetData gets internal format structure data -func (lump *PhysDisp) GetData() []byte { - return lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *PhysDisp) Marshall() ([]byte, error) { - return lump.data, nil -} diff --git a/lumps/planes.go b/lumps/planes.go deleted file mode 100644 index 801b640..0000000 --- a/lumps/planes.go +++ /dev/null @@ -1,39 +0,0 @@ -package lumps - -import ( - "bytes" - "encoding/binary" - primitives "github.com/galaco/bsp/primitives/plane" - "unsafe" -) - -// Planes is Lump 1: Planes -type Planes struct { - Generic - data []primitives.Plane // MAP_MAX_PLANES = 65536 -} - -// Unmarshall Imports this lump from raw byte data -func (lump *Planes) Unmarshall(raw []byte) (err error) { - length := len(raw) - lump.data = make([]primitives.Plane, length/int(unsafe.Sizeof(primitives.Plane{}))) - err = binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.data) - if err != nil { - return err - } - lump.Metadata.SetLength(length) - - return err -} - -// GetData gets internal format structure data -func (lump *Planes) GetData() []primitives.Plane { - return lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *Planes) Marshall() ([]byte, error) { - var buf bytes.Buffer - err := binary.Write(&buf, binary.LittleEndian, lump.data) - return buf.Bytes(), err -} diff --git a/lumps/primindice.go b/lumps/primindice.go deleted file mode 100644 index 044ce48..0000000 --- a/lumps/primindice.go +++ /dev/null @@ -1,38 +0,0 @@ -package lumps - -import ( - "bytes" - "encoding/binary" -) - -// PrimIndice is Lump 39: PrimIndice -type PrimIndice struct { - Generic - data []uint16 -} - -// Unmarshall Imports this lump from raw byte data -func (lump *PrimIndice) Unmarshall(raw []byte) (err error) { - length := len(raw) - lump.Metadata.SetLength(length) - if length == 0 { - return - } - - lump.data = make([]uint16, length/2) - err = binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.data) - - return err -} - -// GetData gets internal format structure data -func (lump *PrimIndice) GetData() []uint16 { - return lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *PrimIndice) Marshall() ([]byte, error) { - var buf bytes.Buffer - err := binary.Write(&buf, binary.LittleEndian, lump.data) - return buf.Bytes(), err -} diff --git a/lumps/primitive.go b/lumps/primitive.go deleted file mode 100644 index 392e0d7..0000000 --- a/lumps/primitive.go +++ /dev/null @@ -1,40 +0,0 @@ -package lumps - -import ( - "bytes" - "encoding/binary" - primitives "github.com/galaco/bsp/primitives/primitive" - "unsafe" -) - -// Primitive is Lump 36: Primitive -type Primitive struct { - Generic - data []primitives.Primitive -} - -// Unmarshall Imports this lump from raw byte data -func (lump *Primitive) Unmarshall(raw []byte) (err error) { - length := len(raw) - lump.Metadata.SetLength(length) - if length == 0 { - return - } - - lump.data = make([]primitives.Primitive, length/int(unsafe.Sizeof(primitives.Primitive{}))) - err = binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.data) - - return err -} - -// GetData gets internal format structure data -func (lump *Primitive) GetData() []primitives.Primitive { - return lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *Primitive) Marshall() ([]byte, error) { - var buf bytes.Buffer - err := binary.Write(&buf, binary.LittleEndian, lump.data) - return buf.Bytes(), err -} diff --git a/lumps/primvert.go b/lumps/primvert.go deleted file mode 100644 index 4298500..0000000 --- a/lumps/primvert.go +++ /dev/null @@ -1,40 +0,0 @@ -package lumps - -import ( - "bytes" - "encoding/binary" - primitives "github.com/galaco/bsp/primitives/primvert" - "unsafe" -) - -// PrimVert is Lump 37: PrimVert -type PrimVert struct { - Generic - data []primitives.PrimVert -} - -// Unmarshall Imports this lump from raw byte data -func (lump *PrimVert) Unmarshall(raw []byte) (err error) { - length := len(raw) - lump.Metadata.SetLength(length) - if length == 0 { - return - } - - lump.data = make([]primitives.PrimVert, length/int(unsafe.Sizeof(primitives.PrimVert{}))) - err = binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.data) - - return err -} - -// GetData gets internal format structure data -func (lump *PrimVert) GetData() []primitives.PrimVert { - return lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *PrimVert) Marshall() ([]byte, error) { - var buf bytes.Buffer - err := binary.Write(&buf, binary.LittleEndian, lump.data) - return buf.Bytes(), err -} diff --git a/lumps/surfedge.go b/lumps/surfedge.go deleted file mode 100644 index e084f0d..0000000 --- a/lumps/surfedge.go +++ /dev/null @@ -1,37 +0,0 @@ -package lumps - -import ( - "bytes" - "encoding/binary" -) - -// Surfedge is Lump 13: Surfedge -type Surfedge struct { - Generic - data []int32 // MAX_MAP_SURFEDGES = 512000 -} - -// Unmarshall Imports this lump from raw byte data -func (lump *Surfedge) Unmarshall(raw []byte) (err error) { - length := len(raw) - lump.data = make([]int32, length/4) - err = binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.data) - if err != nil { - return err - } - lump.Metadata.SetLength(length) - - return err -} - -// GetData gets internal format structure data -func (lump *Surfedge) GetData() []int32 { - return lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *Surfedge) Marshall() ([]byte, error) { - var buf bytes.Buffer - err := binary.Write(&buf, binary.LittleEndian, lump.data) - return buf.Bytes(), err -} diff --git a/lumps/texdata.go b/lumps/texdata.go deleted file mode 100644 index a80ab33..0000000 --- a/lumps/texdata.go +++ /dev/null @@ -1,39 +0,0 @@ -package lumps - -import ( - "bytes" - "encoding/binary" - primitives "github.com/galaco/bsp/primitives/texdata" - "unsafe" -) - -// TexData is Lump 2: TexData -type TexData struct { - Generic - data []primitives.TexData -} - -// Unmarshall Imports this lump from raw byte data -func (lump *TexData) Unmarshall(raw []byte) (err error) { - length := len(raw) - lump.data = make([]primitives.TexData, length/int(unsafe.Sizeof(primitives.TexData{}))) - err = binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.data) - if err != nil { - return err - } - lump.Metadata.SetLength(length) - - return err -} - -// GetData gets internal format structure data -func (lump *TexData) GetData() []primitives.TexData { - return lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *TexData) Marshall() ([]byte, error) { - var buf bytes.Buffer - err := binary.Write(&buf, binary.LittleEndian, lump.data) - return buf.Bytes(), err -} diff --git a/lumps/texdatastringdata.go b/lumps/texdatastringdata.go deleted file mode 100644 index 00021bb..0000000 --- a/lumps/texdatastringdata.go +++ /dev/null @@ -1,26 +0,0 @@ -package lumps - -// TexDataStringData is Lump 43: TexDataStringData -type TexDataStringData struct { - Generic - data string // MAX_MAP_TEXDATA_STRING_DATA = 256000, TEXTURE_NAME_LENGTH = 128 -} - -// Unmarshall Imports this lump from raw byte data -func (lump *TexDataStringData) Unmarshall(raw []byte) (err error) { - length := len(raw) - lump.data = string(raw) - lump.Metadata.SetLength(length) - - return err -} - -// GetData gets internal format structure data -func (lump *TexDataStringData) GetData() string { - return lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *TexDataStringData) Marshall() ([]byte, error) { - return []byte(lump.data), nil -} diff --git a/lumps/texdatastringtable.go b/lumps/texdatastringtable.go deleted file mode 100644 index 687db6c..0000000 --- a/lumps/texdatastringtable.go +++ /dev/null @@ -1,37 +0,0 @@ -package lumps - -import ( - "bytes" - "encoding/binary" -) - -// TexDataStringTable is Lump 44: TexDataStringTable -type TexDataStringTable struct { - Generic - data []int32 // MAX_MAP_TEXINFO = 2048 -} - -// Unmarshall Imports this lump from raw byte data -func (lump *TexDataStringTable) Unmarshall(raw []byte) (err error) { - length := len(raw) - lump.data = make([]int32, length/4) - err = binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.data) - if err != nil { - return err - } - lump.Metadata.SetLength(length) - - return nil -} - -// GetData gets internal format structure data -func (lump *TexDataStringTable) GetData() []int32 { - return lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *TexDataStringTable) Marshall() ([]byte, error) { - var buf bytes.Buffer - err := binary.Write(&buf, binary.LittleEndian, lump.data) - return buf.Bytes(), err -} diff --git a/lumps/texinfo.go b/lumps/texinfo.go deleted file mode 100644 index a8a9283..0000000 --- a/lumps/texinfo.go +++ /dev/null @@ -1,39 +0,0 @@ -package lumps - -import ( - "bytes" - "encoding/binary" - primitives "github.com/galaco/bsp/primitives/texinfo" - "unsafe" -) - -// TexInfo is Lump 6: TexInfo -type TexInfo struct { - Generic - data []primitives.TexInfo -} - -// Unmarshall Imports this lump from raw byte data -func (lump *TexInfo) Unmarshall(raw []byte) (err error) { - length := len(raw) - lump.data = make([]primitives.TexInfo, length/int(unsafe.Sizeof(primitives.TexInfo{}))) - err = binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.data) - if err != nil { - return err - } - lump.Metadata.SetLength(length) - - return err -} - -// GetData gets internal format structure data -func (lump *TexInfo) GetData() []primitives.TexInfo { - return lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *TexInfo) Marshall() ([]byte, error) { - var buf bytes.Buffer - err := binary.Write(&buf, binary.LittleEndian, lump.data) - return buf.Bytes(), err -} diff --git a/lumps/unimplemented.go b/lumps/unimplemented.go deleted file mode 100644 index 4269a5a..0000000 --- a/lumps/unimplemented.go +++ /dev/null @@ -1,26 +0,0 @@ -package lumps - -// Unimplemented is Lump n: Unimplemented lump type -type Unimplemented struct { - Generic - data []byte -} - -// Unmarshall Imports this lump from raw byte data -func (lump *Unimplemented) Unmarshall(raw []byte) (err error) { - length := len(raw) - lump.data = raw - lump.Metadata.SetLength(length) - - return err -} - -// GetData gets internal format structure data -func (lump *Unimplemented) GetData() []byte { - return lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *Unimplemented) Marshall() ([]byte, error) { - return lump.data, nil -} diff --git a/lumps/vertex.go b/lumps/vertex.go deleted file mode 100644 index 220969a..0000000 --- a/lumps/vertex.go +++ /dev/null @@ -1,38 +0,0 @@ -package lumps - -import ( - "bytes" - "encoding/binary" - "github.com/go-gl/mathgl/mgl32" - "unsafe" -) - -// Vertex is Lump 3: Vertex -type Vertex struct { - Generic - data []mgl32.Vec3 -} - -// Unmarshall Imports this lump from raw byte data -func (lump *Vertex) Unmarshall(raw []byte) (err error) { - length := len(raw) - lump.Metadata.SetLength(length) - if length == 0 { - return - } - lump.data = make([]mgl32.Vec3, length/int(unsafe.Sizeof(mgl32.Vec3{}))) - err = binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.data) - return err -} - -// GetData gets internal format structure data -func (lump *Vertex) GetData() []mgl32.Vec3 { - return lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *Vertex) Marshall() ([]byte, error) { - var buf bytes.Buffer - err := binary.Write(&buf, binary.LittleEndian, lump.data) - return buf.Bytes(), err -} diff --git a/lumps/vertnormal.go b/lumps/vertnormal.go deleted file mode 100644 index 2977da7..0000000 --- a/lumps/vertnormal.go +++ /dev/null @@ -1,39 +0,0 @@ -package lumps - -import ( - "bytes" - "encoding/binary" - primitives "github.com/galaco/bsp/primitives/vertnormal" - "unsafe" -) - -// VertNormal is Lump 30: VertNormal -type VertNormal struct { - Generic - data []primitives.VertNormal -} - -// Unmarshall Imports this lump from raw byte data -func (lump *VertNormal) Unmarshall(raw []byte) (err error) { - length := len(raw) - lump.Metadata.SetLength(length) - if length == 0 { - return - } - - lump.data = make([]primitives.VertNormal, length/int(unsafe.Sizeof(primitives.VertNormal{}))) - err = binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.data) - return err -} - -// GetData gets internal format structure data -func (lump *VertNormal) GetData() []primitives.VertNormal { - return lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *VertNormal) Marshall() ([]byte, error) { - var buf bytes.Buffer - err := binary.Write(&buf, binary.LittleEndian, lump.data) - return buf.Bytes(), err -} diff --git a/lumps/vertnormalindice.go b/lumps/vertnormalindice.go deleted file mode 100644 index 97af173..0000000 --- a/lumps/vertnormalindice.go +++ /dev/null @@ -1,37 +0,0 @@ -package lumps - -import ( - "bytes" - "encoding/binary" -) - -// VertNormalIndice is Lump 31: VertNormalIndice -type VertNormalIndice struct { - Generic - data []uint16 -} - -// Unmarshall Imports this lump from raw byte data -func (lump *VertNormalIndice) Unmarshall(raw []byte) (err error) { - length := len(raw) - lump.Metadata.SetLength(length) - if length == 0 { - return - } - - lump.data = make([]uint16, length/2) - err = binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.data) - return err -} - -// GetData gets internal format structure data -func (lump *VertNormalIndice) GetData() []uint16 { - return lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *VertNormalIndice) Marshall() ([]byte, error) { - var buf bytes.Buffer - err := binary.Write(&buf, binary.LittleEndian, lump.data) - return buf.Bytes(), err -} diff --git a/lumps/visibility.go b/lumps/visibility.go deleted file mode 100644 index a4c9085..0000000 --- a/lumps/visibility.go +++ /dev/null @@ -1,48 +0,0 @@ -package lumps - -import ( - "bytes" - "encoding/binary" - primitives "github.com/galaco/bsp/primitives/visibility" -) - -// Visibility is Lump 4: Visibility -type Visibility struct { - Generic - data primitives.Vis -} - -// Unmarshall Imports this lump from raw byte data -func (lump *Visibility) Unmarshall(raw []byte) (err error) { - length := len(raw) - err = binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.data.NumClusters) - if err != nil { - return err - } - lump.data.ByteOffset = make([][2]int32, lump.data.NumClusters) - err = binary.Read(bytes.NewBuffer(raw[4:]), binary.LittleEndian, &lump.data.ByteOffset) - if err != nil { - return err - } - lump.data.BitVectors = make([]byte, length) - err = binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.data.BitVectors) - if err != nil { - return err - } - - lump.Metadata.SetLength(length) - - return nil -} - -// GetData gets internal format structure data -func (lump *Visibility) GetData() *primitives.Vis { - return &lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *Visibility) Marshall() ([]byte, error) { - var buf bytes.Buffer - err := binary.Write(&buf, binary.LittleEndian, lump.data.BitVectors) - return buf.Bytes(), err -} diff --git a/lumps/worldlight.go b/lumps/worldlight.go deleted file mode 100644 index 46cf9d7..0000000 --- a/lumps/worldlight.go +++ /dev/null @@ -1,38 +0,0 @@ -package lumps - -import ( - "bytes" - "encoding/binary" - primitives "github.com/galaco/bsp/primitives/worldlight" - "unsafe" -) - -// WorldLight is Lump 15: Worldlight -type WorldLight struct { - Generic - data []primitives.WorldLight -} - -// Unmarshall Imports this lump from raw byte data -func (lump *WorldLight) Unmarshall(raw []byte) (err error) { - length := len(raw) - lump.Metadata.SetLength(length) - if length == 0 { - return - } - lump.data = make([]primitives.WorldLight, length/int(unsafe.Sizeof(primitives.WorldLight{}))) - err = binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.data) - return err -} - -// GetData gets internal format structure data -func (lump *WorldLight) GetData() []primitives.WorldLight { - return lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *WorldLight) Marshall() ([]byte, error) { - var buf bytes.Buffer - err := binary.Write(&buf, binary.LittleEndian, lump.data) - return buf.Bytes(), err -} diff --git a/lumps/worldlighthdr.go b/lumps/worldlighthdr.go deleted file mode 100644 index 8fcc99c..0000000 --- a/lumps/worldlighthdr.go +++ /dev/null @@ -1,38 +0,0 @@ -package lumps - -import ( - "bytes" - "encoding/binary" - primitives "github.com/galaco/bsp/primitives/worldlight" - "unsafe" -) - -// WorldLightHDR is Lump 15: Worldlight -type WorldLightHDR struct { - Generic - data []primitives.WorldLight -} - -// Unmarshall Imports this lump from raw byte data -func (lump *WorldLightHDR) Unmarshall(raw []byte) (err error) { - length := len(raw) - lump.Metadata.SetLength(length) - if length == 0 { - return - } - lump.data = make([]primitives.WorldLight, length/int(unsafe.Sizeof(primitives.WorldLight{}))) - err = binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &lump.data) - return err -} - -// GetData gets internal format structure data -func (lump *WorldLightHDR) GetData() []primitives.WorldLight { - return lump.data -} - -// Marshall dumps this lump back to raw byte data -func (lump *WorldLightHDR) Marshall() ([]byte, error) { - var buf bytes.Buffer - err := binary.Write(&buf, binary.LittleEndian, lump.data) - return buf.Bytes(), err -} diff --git a/primitives/common/common.go b/primitives/common/common.go deleted file mode 100644 index fa87e0d..0000000 --- a/primitives/common/common.go +++ /dev/null @@ -1,112 +0,0 @@ -package common - -import ( - "github.com/galaco/bsp/primitives/face" - "github.com/go-gl/mathgl/mgl32" -) - -// ColorRGBExponent32 is a 4 byte color representation (RGBExp) -type ColorRGBExponent32 struct { - // R red - R uint8 - // G green - G uint8 - // B blue - B uint8 - // Exponent exponent - Exponent int8 -} - -// Winding -type Winding struct { - // Original - Original int32 // qboolean = int32 - // NumPoints - NumPoints int32 - // Points - Points []mgl32.Vec3 -} - -// Side -type Side struct { - // PlaneNum - PlaneNum int32 - // TexInfo - TexInfo int32 - // MapDisp - MapDisp *MapDispInfo - // Winding - Winding *Winding - // Original - Original *Side - // Contents - Contents int32 - // Surf - Surf int32 - // Visible - Visible int32 - // Tested - Tested int32 - // Bevel - Bevel int32 - // Next - Next *Side - // OrigIndex - OrigIndex int32 - // Id is side Id - Id int32 - // SmoothingGroups - SmoothingGroups uint32 - // AOverlayIds - AOverlayIds []int32 - // AWaterOverlayIds - AWaterOverlayIds []int32 - // DynamicShadowsEnabled are dynamic shadows enabled for this side - DynamicShadowsEnabled bool -} - -// MapDispInfo -type MapDispInfo struct { - // Face - Face face.Face - // EntityNum - EntityNum int32 - // Power is power of this displacement (normally 2-4) - Power int32 - // MinTess - MinTess int32 - // SmoothingAngle - SmoothingAngle float32 - // UAxis - UAxis mgl32.Vec3 - // VAxis - VAxis mgl32.Vec3 - // StartPosition - StartPosition mgl32.Vec3 - // AlphaValues - AlphaValues []float32 - // MaxDispDist - MaxDispDist float32 - // DispDists - DispDists []float32 - // VectorDisps - VectorDisps []mgl32.Vec3 - // VectorOffsets - VectorOffsets []mgl32.Vec3 - // Contents - Contents int32 - // BrushSideID - BrushSideID int32 - // Flags - Flags int32 - - // #ifdef VSVMFIO - // Elevation float32 - // OffsetNormals []Vector -} - -// CompressedLightCube -type CompressedLightCube struct { - // Color - Color [6]ColorRGBExponent32 -} diff --git a/primitives/dispinfo/dispinfo.go b/primitives/dispinfo/dispinfo.go deleted file mode 100644 index cdcfefc..0000000 --- a/primitives/dispinfo/dispinfo.go +++ /dev/null @@ -1,66 +0,0 @@ -package dispinfo - -import ( - "github.com/go-gl/mathgl/mgl32" -) - -// MaxDispCornerNeighbours maximum number of adjoining neigherbours per corner. -const MaxDispCornerNeighbours = 4 - -// DispInfo -type DispInfo struct { - // StartPosition - StartPosition mgl32.Vec3 - // DispVertStart - DispVertStart int32 - // DispTriStart - DispTriStart int32 - // Power - Power int32 - // MinTess - MinTess int32 - // SmoothingAngle - SmoothingAngle float32 - // Contents - Contents int32 - // MapFace - MapFace uint16 - _ [2]byte - // LightmapAlphaStart - LightmapAlphaStart int32 - // LightmapSampleStartPosition - LightmapSampleStartPosition int32 - // Ignore contains bytes with unknown purpose and representation. This should be updated as purpose id discovered - Ignore [32]uint32 - //EdgeNeighbors [4]DispNeighbor - //CornerNeighbors [4]DispCornerNeighbors - //AllowedVerts [8]uint32 - -} - -// DispNeighbor -type DispNeighbor struct { - // SubNeighbours - SubNeighbours [2]DispSubNeighbor -} - -// DispSubNeighbor -type DispSubNeighbor struct { - // Index - // 0xFFFF if no neighbor - Index uint16 - // NeighborOrientation - NeighborOrientation uint8 - // Span - Span uint8 - // NeighborSpan - NeighborSpan uint8 -} - -// DispCornerNeighbors -type DispCornerNeighbors struct { - // Neighbors - Neighbors [MaxDispCornerNeighbours]uint16 - // NumNeighbors is number of neighbours - NumNeighbors uint8 -} diff --git a/primitives/face/face.go b/primitives/face/face.go deleted file mode 100644 index fa24efa..0000000 --- a/primitives/face/face.go +++ /dev/null @@ -1,39 +0,0 @@ -package face - -// Face -type Face struct { - // Planenum - Planenum uint16 - // Side - Side byte - // OnNode - OnNode byte - // FirstEdge is index into the edges lump data - FirstEdge int32 - // NumEdges is the number of edges that make up this face - NumEdges int16 - // TexInfo is index into texinfos lump - TexInfo int16 - // DispInfo is index into dispinfos lump - DispInfo int16 - // SurfaceFogVolumeID - SurfaceFogVolumeID int16 - // Styles - Styles [4]byte - // Lightofs is offset into lighting lump (-1 if unlit) - Lightofs int32 - // Area is total size of this side (units^2) - Area float32 - // LightmapTextureMinsInLuxels - LightmapTextureMinsInLuxels [2]int32 - // LightmapTextureSizeInLuxels is XY size of lightmap data - LightmapTextureSizeInLuxels [2]int32 - // OrigFace - OrigFace int32 - // NumPrims - NumPrims uint16 - // FirstPrimID - FirstPrimID uint16 - // SmoothingGroups - SmoothingGroups uint32 -} diff --git a/primitives/leafambientlighting/leafambientlighting.go b/primitives/leafambientlighting/leafambientlighting.go deleted file mode 100644 index 6f207cf..0000000 --- a/primitives/leafambientlighting/leafambientlighting.go +++ /dev/null @@ -1,17 +0,0 @@ -package leafambientlighting - -import primitives "github.com/galaco/bsp/primitives/common" - -// LeafAmbientLighting -type LeafAmbientLighting struct { - // Cube - Cube primitives.CompressedLightCube - // X x - X byte - // Y y - Y byte - // Z z - Z byte - // Pad is padding to 4 bytes (any other purpose unknown) - Pad byte -} diff --git a/primitives/occlusion/occlusion.go b/primitives/occlusion/occlusion.go deleted file mode 100644 index d521705..0000000 --- a/primitives/occlusion/occlusion.go +++ /dev/null @@ -1,29 +0,0 @@ -package occlusion - -import ( - "github.com/go-gl/mathgl/mgl32" -) - -type OcclusionData struct { - Flags int32 - FirstPoly int32 - PolyCount int32 - Mins mgl32.Vec3 - Maxs mgl32.Vec3 - Area int32 -} - -type OcclusionPolyData struct { - FirstVertexIndex int32 - VertexCount int32 - PlaneNum int32 -} - -//Implementation info -/* -Lump size: -num occlusions * size() -+ num occlusionPolyDatas * size() -+ num OccluderVertexIndices * size() -+ 3* sizeof(Int32) -*/ diff --git a/primitives/physcollide/physcollide.go b/primitives/physcollide/physcollide.go deleted file mode 100644 index 17d96bf..0000000 --- a/primitives/physcollide/physcollide.go +++ /dev/null @@ -1,31 +0,0 @@ -package physcollide - -// PhysCollideEntry -type PhysCollideEntry struct { - // ModelHeader - ModelHeader PhysModel - // Solids - Solids []Solid - // TextBuffer - TextBuffer string -} - -// PhysModel -type PhysModel struct { - // ModelIndex - ModelIndex int32 - // DataSize - DataSize int32 - // KeydataSize - KeydataSize int32 - // SolidCount - SolidCount int32 -} - -// Solid -type Solid struct { - // Size - Size int32 - // CollisionBinary - CollisionBinary []int32 -} diff --git a/primitives/portal/portal.go b/primitives/portal/portal.go deleted file mode 100644 index 496b78c..0000000 --- a/primitives/portal/portal.go +++ /dev/null @@ -1,33 +0,0 @@ -package portal - -import ( - "github.com/galaco/bsp/primitives/common" - "github.com/galaco/bsp/primitives/face" - "github.com/galaco/bsp/primitives/node" - "github.com/galaco/bsp/primitives/plane" -) - -// MaxPortals is the maximum number of portals that can be generated -const MaxPortals = 65536 - -// Portal -type Portal struct { - // Id portal id - Id int32 - // Plane - Plane plane.Plane - // OnNode - OnNode *node.Node - // Nodes - Nodes [2]*node.Node - // Next - Next [2]*Portal - // Winding - Winding *common.Winding - // SideFound - SideFound int32 //qboolean = int32 - // Side - Side *common.Side - // Face - Face [2]*face.Face -} diff --git a/primitives/primitive/primitive.go b/primitives/primitive/primitive.go deleted file mode 100644 index 24a308a..0000000 --- a/primitives/primitive/primitive.go +++ /dev/null @@ -1,15 +0,0 @@ -package primitive - -// Primitive -type Primitive struct { - // Type - Type byte - // FirstIndex - FirstIndex uint16 - // IndexCount - IndexCount uint16 - // FirstVert - FirstVert uint16 - // VertCount - VertCount uint16 -} diff --git a/primitives/texinfo/texinfo.go b/primitives/texinfo/texinfo.go deleted file mode 100644 index 1e6e197..0000000 --- a/primitives/texinfo/texinfo.go +++ /dev/null @@ -1,14 +0,0 @@ -package texinfo - -// TexInfo contains texture information for a face -// (tex, uv info, and lightmap info) -type TexInfo struct { - // TextureVecsTexelsPerWorldUnits is texture scale - TextureVecsTexelsPerWorldUnits [2][4]float32 - // LightmapVecsLuxelsPerWorldUnits is lightmap scale - LightmapVecsLuxelsPerWorldUnits [2][4]float32 - // Flags - Flags int32 - // TexData is index into TexData lump data - TexData int32 -} diff --git a/primitives/worldlight/worldlight.go b/primitives/worldlight/worldlight.go deleted file mode 100644 index dc0aff2..0000000 --- a/primitives/worldlight/worldlight.go +++ /dev/null @@ -1,61 +0,0 @@ -package worldlight - -import ( - "github.com/go-gl/mathgl/mgl32" -) - -// EmitType light emission mode. -type EmitType uint8 // assumed this is 1 byte.. - -const ( - // EmitSurface 90 degree spotlight - EmitSurface EmitType = 0 - // EmitPoint simple point light source - EmitPoint EmitType = 1 - // EmitSpotLight spotlight with penumbra - EmitSpotLight EmitType = 2 - // EmitSkyLight directional light with no falloff (surface must trace to SKY texture) - EmitSkyLight EmitType = 3 - // EmitQuakeLight linear falloff, non-lambertian - EmitQuakeLight EmitType = 4 - // EmitSkyAmbient spherical light source with no falloff (surface must trace to SKY texture) - EmitSkyAmbient EmitType = 5 -) - -// WorldLight is a single light in the world -// This data may also be stored in entdata -type WorldLight struct { - // Origin is position in the world - Origin mgl32.Vec3 - // Intensity - Intensity mgl32.Vec3 - // Normal - Normal mgl32.Vec3 - // Cluster - Cluster int32 - // Type - Type EmitType //Think for alignments sake with is uint8. May be 3 bytes padding... - _ [3]byte - // Style - Style int32 - // Stopdot - Stopdot float32 - // Stopdot2 - Stopdot2 float32 - // Exponent - Exponent float32 - // Radius - Radius float32 - // ConstantAttenuation - ConstantAttenuation float32 - // LinearAttenuation - LinearAttenuation float32 - // QuadraticAttenuation - QuadraticAttenuation float32 - // Flags - Flags int32 - // TexInfo - TexInfo int32 - // Owner - Owner int32 -} diff --git a/reader.go b/reader.go index d075a18..3d0bfab 100644 --- a/reader.go +++ b/reader.go @@ -3,145 +3,128 @@ package bsp import ( "bytes" "encoding/binary" - "errors" - "github.com/galaco/bsp/lumps" + "fmt" "io" - "os" "unsafe" + + "github.com/galaco/bsp/lump" ) +// LumpResolver Return an instance of a Lump for a given offset. +type LumpResolver func(id LumpId, header Header) (Lump, error) + +// ReaderConfig offers configurable parameters for reading BSP. +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 +} + +// DefaultReaderConfig returns the default config for a reader. +func DefaultReaderConfig() ReaderConfig { + return ReaderConfig{ + LumpResolver: LumpResolverByBSPVersion, + } +} + // Reader is a Bsp File reader. type Reader struct { - stream io.Reader + config ReaderConfig } -// Read reads the BSP into internal byte structure -// Note that parsing is somewhat lazy. Proper data structures are only generated for -// lumps that are requested at a later time. This generated the header, then []byte -// data for each lump -func (r *Reader) Read() (bsp *Bsp, err error) { - defer func() { - if r := recover(); r != nil { - bsp = nil - switch x := r.(type) { - case string: - err = errors.New(x) - case error: - err = x - default: - err = errors.New("unknown panic") - } - } - }() +// NewReader creates a new Bsp reader with default config. +func NewReader() *Reader { + return &Reader{ + config: DefaultReaderConfig(), + } +} - bsp = &Bsp{} +// NewReaderWithConfig creates a new Bsp reader. +func NewReaderWithConfig(config ReaderConfig) *Reader { + return &Reader{ + config: config, + } +} - buf := bytes.Buffer{} - _, err = buf.ReadFrom(r.stream) - if err != nil { +// Read reads from an io.Reader into a structured Bsp. +func (r *Reader) Read(stream io.Reader) (*Bsp, error) { + buf := bytes.NewBuffer([]byte{}) + if _, err := io.Copy(buf, stream); err != nil { return nil, err } reader := bytes.NewReader(buf.Bytes()) - //Create Header - h, err := r.readHeader(reader, bsp.header) + bsp := &Bsp{} + + // Create Header. + header, err := r.readHeader(reader) if err != nil { return nil, err } - bsp.header = *h + bsp.Header = header - // Create lumps from header data - for index := range bsp.header.Lumps { - lp, err := r.readLump(reader, bsp.header, index) + // Create lumps from header data. + for index := range bsp.Header.Lumps { + lp, err := r.readLump(reader, &bsp.Header.Lumps[index]) + if err != nil { + return nil, err + } + refLump, err := r.config.LumpResolver(LumpId(index), bsp.Header) if err != nil { return nil, err } - bsp.lumps[index].SetId(LumpId(index)) - bsp.lumps[index].SetRawContents(lp) - refLump, err := getReferenceLumpByIndex(index, bsp.header.Version) - // There are specific rules for the game lump that requires some extra information - // Game lump lumps have offset data relative to file start, not lump start + // There are specific rules for the game lump that requires some extra information. + // Game lump lumps have offset data relative to file start, not lump start. // This will correct the offsets to the start of the lump. - // @NOTE: Portal2 console uses relative offsets. This game+platform are not supported currently + // @TODO: Portal2 console uses relative offsets. This game+platform are not supported currently. if index == int(LumpGame) { - refLump.(*lumps.Game).UpdateInternalOffsets(bsp.header.Lumps[index].Offset) + if _, ok := refLump.(lump.GameGeneric); !ok { + return nil, fmt.Errorf("game lump does not implement GameGeneric interface") + } + refLump.(lump.GameGeneric).SetAbsoluteFileOffset(int(header.Lumps[index].Offset)) } - if err != nil { + if err := refLump.FromBytes(lp); err != nil { return nil, err } - bsp.lumps[index].SetContents(refLump) + bsp.Lumps[index] = refLump } - return bsp, err + return bsp, nil } // readHeader Parses header from the bsp file. -func (r *Reader) readHeader(reader *bytes.Reader, header Header) (*Header, error) { - headerSize := unsafe.Sizeof(header) - headerBytes := make([]byte, headerSize) +func (r *Reader) readHeader(reader io.ReaderAt) (header Header, err error) { + headerBytes := make([]byte, unsafe.Sizeof(header)) sectionReader := io.NewSectionReader(reader, 0, int64(len(headerBytes))) - _, err := sectionReader.Read(headerBytes) - if err != nil { - return nil, err + if _, err := sectionReader.Read(headerBytes); err != nil { + return header, err } - err = binary.Read(bytes.NewBuffer(headerBytes), binary.LittleEndian, &header) - if err != nil { - return nil, err + if err := binary.Read(bytes.NewBuffer(headerBytes), binary.LittleEndian, &header); err != nil { + return header, err } - return &header, nil + return header, nil } -// readLump Reads a single lumps data -// Expect a byte reader containing the lump data, as well as the -// header and lump identifier (id) -func (r *Reader) readLump(reader *bytes.Reader, header Header, index int) ([]byte, error) { - //Limit lump data to declared size - lumpHeader := header.Lumps[index] - raw := make([]byte, lumpHeader.Length) - - // Skip reading for empty lump - if lumpHeader.Length > 0 { - sectionReader := io.NewSectionReader(reader, int64(lumpHeader.Offset), int64(lumpHeader.Length)) - _, err := sectionReader.Read(raw) - if err != nil { - return nil, err - } +// readLump reads a single lumps data, +// Expects a byte reader containing the lump data, as well as the +// header and lump identifier (id). +func (r *Reader) readLump(reader *bytes.Reader, headerLump *HeaderLump) ([]byte, error) { + // Skip reading for empty lump. + if headerLump.Length == 0 { + return nil, nil } - return raw, nil -} + // Limit lump data to declared size. + raw := make([]byte, headerLump.Length) -// ReadFromFile Wraps ReadFromStream to control the file access as well. -// Use ReadFromStream if you already have a file handle -func ReadFromFile(filepath string) (*Bsp, error) { - f, err := os.Open(filepath) - if err != nil { - return nil, err - } - b, err := ReadFromStream(f) - if err != nil { - err2 := f.Close() - if err2 != nil { - return nil, err2 - } + if _, err := io.NewSectionReader(reader, int64(headerLump.Offset), int64(headerLump.Length)).Read(raw); err != nil { return nil, err } - err = f.Close() - return b, err -} - -// ReadFromStream Reads from any struct that implements io.Reader -// handy for passing in a string/bytes/other stream -func ReadFromStream(reader io.Reader) (*Bsp, error) { - r := &Reader{ - reader, - } - - return r.Read() + return raw, nil } diff --git a/reader_test.go b/reader_test.go index 953beaf..9a450b3 100644 --- a/reader_test.go +++ b/reader_test.go @@ -1,30 +1,55 @@ package bsp import ( - "github.com/galaco/bsp/lumps" + "compress/gzip" "os" "testing" -) -func TestReadFromFile(t *testing.T) { - t.Skip() - _, err := ReadFromFile("maps/v20/de_dust2.bsp") - if err != nil { - t.Error(err) - } -} + "github.com/galaco/bsp/lump" +) func TestReadFromStream(t *testing.T) { - t.Skip() - f, err := os.Open("maps/v20/de_dust2.bsp") - if err != nil { - t.Error(err) + testCases := []struct { + name string + filePath string + expectedError error + }{ + { + name: "de_dust2", + filePath: "testdata/v20/de_dust2.bsp.gz", + expectedError: nil, + }, + { + name: "ar_baggage", + filePath: "testdata/v21/ar_baggage.bsp.gz", + expectedError: nil, + }, } - r, err := ReadFromStream(f) - if err != nil { - t.Error(err) - } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + f, err := os.Open(tc.filePath) + if err != nil { + t.Error(err) + } + defer func(f *os.File) { + if err := f.Close(); err != nil { + t.Error(err) + } + }(f) + binarygzr, err := gzip.NewReader(f) + if err != nil { + t.Error(err) + } + + r, err := NewReaderWithConfig(ReaderConfig{ + LumpResolver: LumpResolverByBSPVersion, + }).Read(binarygzr) + if err != nil { + t.Error(err) + } - r.Lump(LumpGame).(*lumps.Game).GetStaticPropLump() + r.Lumps[LumpGame].(*lump.Game).GetStaticPropLump() + }) + } } diff --git a/renovate.json b/renovate.json deleted file mode 100644 index f45d8f1..0000000 --- a/renovate.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": [ - "config:base" - ] -} diff --git a/testdata/v20/de_dust2.bsp.gz b/testdata/v20/de_dust2.bsp.gz new file mode 100644 index 0000000..0b0f260 --- /dev/null +++ b/testdata/v20/de_dust2.bsp.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e477b89b0a8792d5e9284926d1413a4eee08b792f8f330b201ad7dff4a770b0a +size 7232258 diff --git a/testdata/v21/ar_baggage.bsp.gz b/testdata/v21/ar_baggage.bsp.gz new file mode 100644 index 0000000..37bb2c0 --- /dev/null +++ b/testdata/v21/ar_baggage.bsp.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0211be1c518ccfec6e65043331b8541a01cc50d223f584b729ae4eb7dac677a0 +size 5862561 diff --git a/version.go b/version.go new file mode 100644 index 0000000..f955b89 --- /dev/null +++ b/version.go @@ -0,0 +1,171 @@ +package bsp + +import ( + "fmt" + + "github.com/galaco/bsp/lump" +) + +// 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. +func LumpResolverByBSPVersion(id LumpId, header Header) (l Lump, err error) { + if id < 0 || id > 63 { + return nil, fmt.Errorf("invalid lump id: %d provided", id) + } + + switch header.Version { + case 19: + // @TODO: Implement v21. + l, err = getV20Lump(id) + case 20: + l, err = getV20Lump(id) + case 21: + // @TODO: Implement v21. + l, err = getV20Lump(id) + default: + l, err = &lump.Unimplemented{}, nil + } + + l.SetVersion(header.Version) + + return l, nil +} + +// getV20Lump returns the corresponding v20 lump for provided index. +func getV20Lump(index LumpId) (Lump, error) { + switch index { + case 0: + return &lump.EntData{}, nil + case 1: + return &lump.Planes{}, nil + case 2: + return &lump.TexData{}, nil + case 3: + return &lump.Vertex{}, nil + case 4: + return &lump.Visibility{}, nil + case 5: + return &lump.Node{}, nil + case 6: + return &lump.TexInfo{}, nil + case 7: + return &lump.Face{}, nil + case 8: + return &lump.Lighting{}, nil + case 9: + return &lump.Occlusion{}, nil + case 10: + return &lump.Leaf{}, nil + case 11: + return &lump.FaceId{}, nil + case 12: + return &lump.Edge{}, nil + case 13: + return &lump.Surfedge{}, nil + case 14: + return &lump.Model{}, nil + case 15: + return &lump.WorldLight{}, nil + case 16: + return &lump.LeafFace{}, nil + case 17: + return &lump.LeafBrush{}, nil + case 18: + return &lump.Brush{}, nil + case 19: + return &lump.BrushSide{}, nil + case 20: + return &lump.Area{}, nil + case 21: + return &lump.AreaPortal{}, nil + case 22: + return &lump.Unimplemented{}, nil //portals | unused0 | propcollision + case 23: + return &lump.Unimplemented{}, nil //clusters | unused1 | prophulls + case 24: + return &lump.Unimplemented{}, nil //portalverts | unused2 | prophullverts + case 25: + return &lump.Unimplemented{}, nil //clusterportals | unused3 | proptris + case 26: + return &lump.DispInfo{}, nil + case 27: + return &lump.Face{}, nil + case 28: + return &lump.PhysDisp{}, nil + case 29: + return &lump.Unimplemented{}, nil //physcollide. + case 30: + return &lump.VertNormal{}, nil + case 31: + return &lump.VertNormalIndice{}, nil + case 32: + // @TODO: This appears to be stripped by VRAD. + // Find samples to test. + return &lump.Unimplemented{}, nil //disp lightmap alphas + case 33: + return &lump.DispVert{}, nil + case 34: + return &lump.DispLightmapSamplePosition{}, nil + case 35: + return &lump.Game{}, nil + case 36: + return &lump.LeafWaterData{}, nil + case 37: + return &lump.Unimplemented{}, nil //primitives @TODO - Appears to be 4bytes unaccounted for at end of lump? + case 38: + return &lump.PrimVert{}, nil + case 39: + return &lump.PrimIndice{}, nil + case 40: + return &lump.Pakfile{}, nil //pakfile + case 41: + return &lump.ClipPortalVerts{}, nil + case 42: + return &lump.Cubemap{}, nil + case 43: + return &lump.TexDataStringData{}, nil + case 44: + return &lump.TexDataStringTable{}, nil + case 45: + return &lump.Overlay{}, nil + case 46: + return &lump.LeafMinDistToWater{}, nil + case 47: + return &lump.FaceMacroTextureInfo{}, nil + case 48: + return &lump.DispTris{}, nil + case 49: + return &lump.Unimplemented{}, nil //physcollidesurface | prop blob + case 50: + return &lump.Unimplemented{}, nil //wateroverlays + case 51: + return &lump.LeafAmbientIndexHDR{}, nil + case 52: + return &lump.LeafAmbientIndex{}, nil + case 53: + return &lump.Lighting{}, nil //lighting hdr + case 54: + return &lump.WorldLightHDR{}, nil //worldlights hdr + case 55: + return &lump.LeafAmbientLightingHDR{}, nil + case 56: + return &lump.LeafAmbientLighting{}, nil //leaf ambient lighting + case 57: + return &lump.Unimplemented{}, nil //xzippakfile + case 58: + return &lump.FaceHDR{}, nil + case 59: + return &lump.MapFlags{}, nil + case 60: + return &lump.OverlayFade{}, nil + case 61: + return &lump.Unimplemented{}, nil //overlay system levels + case 62: + return &lump.Unimplemented{}, nil //physlevel + case 63: + return &lump.Unimplemented{}, nil //disp multiblend + default: + return nil, fmt.Errorf("invalid lump id") + } +} diff --git a/version_test.go b/version_test.go new file mode 100644 index 0000000..dbcd261 --- /dev/null +++ b/version_test.go @@ -0,0 +1,56 @@ +package bsp + +import ( + "reflect" + "testing" + + "github.com/galaco/bsp/lump" +) + +func TestGetLumpForVersion(t *testing.T) { + testCases := []struct { + name string + id int + version int + t reflect.Type + }{ + { + name: "unknown version", + id: 4, + version: 987, + t: reflect.TypeOf(&lump.Unimplemented{}), + }, + { + name: "v19", + id: 4, + version: 19, + t: reflect.TypeOf(&lump.Visibility{}), + }, + { + name: "v20", + id: 4, + version: 20, + t: reflect.TypeOf(&lump.Visibility{}), + }, + { + name: "v21", + id: 4, + version: 21, + t: reflect.TypeOf(&lump.Visibility{}), + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + l, err := LumpResolverByBSPVersion(LumpId(tc.id), Header{ + Version: int32(tc.version), + }) + if err != nil { + t.Error(err) + } + if reflect.TypeOf(l) != tc.t { + t.Error("Lump type mismatch.") + } + }) + } +} diff --git a/writer.go b/writer.go index c22c885..45fd9a0 100644 --- a/writer.go +++ b/writer.go @@ -3,151 +3,106 @@ package bsp import ( "bytes" "encoding/binary" - "github.com/galaco/bsp/lumps" + "fmt" + "github.com/galaco/bsp/lump" + "sort" + "unsafe" ) // Writer is a Bsp export writer. -type Writer struct { - data Bsp -} +type Writer struct{} -// GetBsp Gets bsp file to write. -func (w *Writer) GetBsp() Bsp { - return w.data +// NewWriter Returns a new bsp writer instance. +func NewWriter() *Writer { + return &Writer{} } -// SetBsp Sets bsp file to write. -func (w *Writer) SetBsp(file Bsp) { - w.data = file +func (w *Writer) Write(bsp *Bsp) ([]byte, error) { + return w.toBytes(bsp) } -// Write bsp to []byte. -func (w *Writer) Write() ([]byte, error) { - // First we need to update the header to reflect any lump changes - // At the same time we can dump our lumps as bytes to write later - lumpBytes := make([][]byte, 64) - currentOffset := 1036 // Header always 1036bytes +// toBytes bsp to []byte. +func (w *Writer) toBytes(data *Bsp) ([]byte, error) { + var err error + + bytesWritten := int(unsafe.Sizeof(Header{})) // Header always 1036bytes, so we start immediately afterward. - for _, index := range getDefaultLumpOrdering() { - // We have to handle lump 35 (GameData differently) - // Because valve mis-designed the file format and relatively positioned data contains absolute file offsets. + // Now we can export our lumps. + var lumpBuffer bytes.Buffer + + var temp []byte + for _, index := range resolveLumpExportOrder(&data.Header) { + // Update our game data offsets. if index == LumpGame { - gamelump := w.data.lumps[int(index)].Contents().(*lumps.Game) - w.data.lumps[int(index)].SetContents( - gamelump.UpdateInternalOffsets(int32(currentOffset) - w.data.header.Lumps[int(index)].Offset)) + if _, ok := data.Lumps[index].(lump.GameGeneric); !ok { + return nil, fmt.Errorf("game lump does not implement GameGeneric interface") + } + data.Lumps[index].(lump.GameGeneric).SetAbsoluteFileOffset(bytesWritten) } - exportBytes, err := w.WriteLump(index) + + if temp, err = data.Lumps[index].ToBytes(); err != nil { + return nil, err + } + lumpSize, err := lumpBuffer.Write(temp) if err != nil { return nil, err } - lumpBytes[int(index)] = exportBytes - - lumpSize := len(lumpBytes[int(index)]) - - w.data.header.Lumps[int(index)].Length = int32(lumpSize) - w.data.header.Lumps[int(index)].Offset = int32(currentOffset) - currentOffset += lumpSize - - // Finally 4byte align each lump. - lumpBytes[int(index)] = append(lumpBytes[int(index)], make([]byte, currentOffset%4)...) - currentOffset += currentOffset % 4 + // If the lump is empty, we can skip it. + if lumpSize == 0 { + // 0 bytes is a valid lump, but some fields are actually set to 0, other not. + // @TODO why are some offsets and versions non-0 when length is 0. (e.g. ar_baggage) + data.Header.Lumps[index].Length = 0 + data.Header.Lumps[index].ID = [4]byte{0, 0, 0, 0} + continue + } + // Update header with new lump offset and lumpSize. + data.Header.Lumps[index].Offset = int32(bytesWritten) + data.Header.Lumps[index].Length = int32(lumpSize) + + // Update current offset for next iteration. + bytesWritten += lumpSize + + // Finally 4byte align the data and current offset. + // Note that we don't adjust the lump lumpSize in the header to reflect this; + // it's for padding reasons. + if pad := bytesWritten % 4; pad != 0 { + lumpBuffer.Write(make([]byte, 4-pad)) + bytesWritten += 4 - pad + } } - // Now we can export our bsp - var buf bytes.Buffer - - //Write Header - err := binary.Write(&buf, binary.LittleEndian, w.data.header) - if err != nil { + var buffer bytes.Buffer + if err := binary.Write(&buffer, binary.LittleEndian, data.Header); err != nil { return nil, err } - //Write lumps - for _, lumpData := range lumpBytes { - if err = binary.Write(&buf, binary.LittleEndian, lumpData); err != nil { - return nil, err - } + if _, err := buffer.Write(lumpBuffer.Bytes()); err != nil { + return nil, err } - - return buf.Bytes(), nil + return buffer.Bytes(), nil } -// WriteLump Exports a single lump to []byte. -func (w *Writer) WriteLump(index LumpId) ([]byte, error) { - lump := w.data.Lump(index) - return lump.Marshall() -} +// resolveLumpExportOrder returns a lump export order that matches the order of the original file +// based on lump offsets. +func resolveLumpExportOrder(header *Header) [64]LumpId { + temp := make([]struct { + Offset int32 + Id int + }, 64) + for idx, l := range header.Lumps { + temp[idx].Offset = l.Offset + temp[idx].Id = idx + } -// NewWriter Returns a new bsp writer instance. -func NewWriter() Writer { - w := Writer{} - return w -} + sort.Slice(temp, func(i, j int) bool { + return temp[i].Offset < temp[j].Offset + }) + + res := [64]LumpId{} -// getDefaultLumpOrdering gets Source Engines default export order. -// Source compile tools write lumps out of order -// While the ordering doesn't actually matter, it may -// be useful/more performant to maintain the same order, particularly post-export -func getDefaultLumpOrdering() [64]LumpId { - return [64]LumpId{ - LumpPlanes, - LumpLeafs, - LumpLeafAmbientLighting, - LumpLeafAmbientIndex, - LumpLeafAmbientIndexHDR, - LumpLeafAmbientLightingHDR, - LumpVertexes, - LumpNodes, - LumpTexInfo, - LumpTexData, - LumpDispInfo, - LumpDispVerts, - LumpDispTris, - LumpDispLightmapSamplePositions, - LumpFaceMacroTextureInfo, - LumpPrimitives, - LumpPrimVerts, - LumpPrimIndices, - LumpFaces, - LumpFacesHDR, - LumpFaceIds, - LumpOriginalFaces, - LumpBrushes, - LumpBrushSides, - LumpLeafFaces, - LumpLeafBrushes, - LumpSurfEdges, - LumpEdges, - LumpModels, - LumpAreas, - LumpAreaPortals, - LumpLighting, - LumpLightingHDR, - LumpVisibility, - LumpEntities, - LumpWorldLights, - LumpWorldLightsHDR, - LumpLeafWaterData, - LumpOcclusion, - LumpMapFlags, - LumpPortals, - LumpClusters, - LumpPortalVerts, - LumpClusterPortals, - LumpClipPortalVerts, - LumpCubemaps, - LumpTexDataStringData, - LumpTexDataStringTable, - LumpOverlays, - LumpWaterOverlays, - LumpOverlayFades, - LumpPhysCollide, - LumpPhysDisp, - LumpVertNormals, - LumpVertNormalIndices, - LumpLeafMinDistToWater, - LumpGame, - LumpPakfile, + for idx := range temp { + res[idx] = LumpId(temp[idx].Id) } + return res } diff --git a/writer_test.go b/writer_test.go new file mode 100644 index 0000000..a8ba72f --- /dev/null +++ b/writer_test.go @@ -0,0 +1,135 @@ +package bsp + +import ( + "bytes" + "compress/gzip" + "encoding/binary" + "io" + "log" + "os" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestNewWriter(t *testing.T) { + w := NewWriter() + if w == nil { + t.Error("NewWriter returned nil") + } +} + +func TestWriter_Write(t *testing.T) { + testCases := []struct { + name string + filePath string + }{ + { + name: "cs_assault", + filePath: "testdata/v20/cs_assault.bsp.gz", + }, + { + name: "de_nuke", + filePath: "testdata/v20/de_nuke.bsp.gz", + }, + { + name: "de_tides", + filePath: "testdata/v20/de_tides.bsp.gz", + }, + { + name: "de_dust2", + filePath: "testdata/v20/de_dust2.bsp.gz", + }, + { + name: "ar_baggage", + filePath: "testdata/v21/ar_baggage.bsp.gz", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + f, err := os.Open(tc.filePath) + if err != nil { + t.Fatal(err) + } + defer func(f *os.File) { + if err := f.Close(); err != nil { + t.Error(err) + } + }(f) + binarygzr, err := gzip.NewReader(f) + if err != nil { + t.Error(err) + } + + var expected bytes.Buffer + bsp, err := NewReaderWithConfig(ReaderConfig{ + LumpResolver: LumpResolverByBSPVersion, + }).Read(io.TeeReader(binarygzr, &expected)) + if err != nil { + t.Fatal(err) + } + expectedBytes := expected.Bytes() + + actual, err := NewWriter().Write(bsp) + if err != nil { + t.Fatalf("toBytes(%s) returned error: %v", tc.filePath, err) + } + + if !bytes.Equal(expectedBytes, actual) { + t.Errorf("toBytes(%s) returned unexpected bytes", tc.filePath) + + // It's rather painful to easily debug this kind of output, + // below are some helpers for diffing header and lump data. + baseOffset := 8 + compSize := 16 + for _, i := range resolveLumpExportOrder(&bsp.Header) { + offset := baseOffset + (int(i) * compSize) + + expectedLumpOffset := int(binary.LittleEndian.Uint32(expectedBytes[offset : offset+4])) + expectedLumpLength := int(binary.LittleEndian.Uint32(expectedBytes[offset+4 : offset+8])) + actualLumpOffset := int(binary.LittleEndian.Uint32(actual[offset : offset+4])) + actualLumpLength := int(binary.LittleEndian.Uint32(actual[offset+4 : offset+8])) + + log.Printf("%d: offset: %d, %d (%d). length: %d, %d (%d). Version: %d, %d\n", i, + expectedLumpOffset, + actualLumpOffset, + actualLumpOffset-expectedLumpOffset, + expectedLumpLength, + actualLumpLength, + expectedLumpLength-actualLumpLength, + binary.LittleEndian.Uint32(expectedBytes[offset+8:offset+12]), + binary.LittleEndian.Uint32(actual[offset+8:offset+12]), + ) + + // Compare header + if !bytes.Equal(expectedBytes[offset:offset+compSize], actual[offset:offset+compSize]) { + t.Errorf("%d: header.toBytes(%s) returned unexpected bytes", i, tc.filePath) + + if diff := cmp.Diff(expectedBytes[offset:offset+compSize], actual[offset:offset+compSize]); diff != "" { + t.Errorf("%d: header.toBytes(%s) returned unexpected diff (-want +got):\n%s", i, tc.filePath, diff) + } + } + + // Compare lump the header references. + if !bytes.Equal(expectedBytes[expectedLumpOffset:expectedLumpOffset+expectedLumpLength], actual[actualLumpOffset:actualLumpOffset+actualLumpLength]) { + t.Errorf("%d: lump.toBytes(%s) returned unexpected bytes", i, tc.filePath) + + if diff := cmp.Diff(expectedBytes[expectedLumpOffset:expectedLumpOffset+expectedLumpLength], actual[actualLumpOffset:actualLumpOffset+actualLumpLength]); diff != "" { + t.Fatalf("%d: lump.toBytes(%s) returned unexpected diff (-want +got):\n%s", i, tc.filePath, diff) + } + } + } + + // now compare each body. + if len(expectedBytes) != len(actual) { + t.Errorf("toBytes(%s) returned unexpected bytes", tc.filePath) + } + + if diff := cmp.Diff(expectedBytes, actual); diff != "" { + t.Errorf("toBytes(%s) returned unexpected diff (-want +got):\n%s", tc.filePath, diff) + } + } + }) + } +}