Ds1 refactor (#11)

* Refactoring d2ds1

* Adding setters/getters so that state management can be maintained
internally when the ds1 struct is altered
* Adding unit tests for DS1

* unit tests for ds1 (#4)

* ds1 refactor: added test fore some methods; put tests in right order

* ds1 refactor: unit tests for all methods

* ds1 refactor: fixed build errors

* ds1 refactor: lintfix

* ds1 refactor: fixed bug with SetWidth, SetHeight methods

* ds1 refactor: rename tile_record.go -> tile.go

* ds1 refactor: unit test for SetTiles

Co-authored-by: M. Sz <mszeptuch@protonmail.com>

* renamed some files in d2ds1

* d2ds1.FloorShadow is now private

* renamed another file

* DS1.Tile() now calls update if dirty

* Ds1 refactor: some test improvement (#5)

* ds1 refactor: floor_shadow.go: methods Encode, Decode an Hidden are methods of floorShadow

* ds1 refactor: test checks, if our methods sets all fields correctly

* ds1 refactor: minor bugfixes

* i don't remember what's this, but i commit it ;-)

* ds1 refactor: reverted some pushed by mistake things

Co-authored-by: M. Sz <mszeptuch@protonmail.com>

* Ds1 refactor: test bugs + descriptive errors + SetNumberOfWall/FloorLayers (#6)

* ds1 refactor:
- removed DS1.layerStreamTypes field
- written unit test for setupStreamLayerTypes method
- added more descriptive error messages for LoadDS1 (and submethods)

* ds1 refactor: added some missing error messages

* ds1 refactor: fixed test bugs

* ds1 refactor: removed unnecessary c1. and c2. comments in ds1_test errors

* ds1 refactor: removed fmt from ds1_test

* ds1 refactor: fixed bug with SetTiles test + lintfix

* ds1 refactor: SetNumberOfWalls

* ds1 refactor: SetTile(s) now changes walls/floors length if neccesary

* ds1 refactor: removed unnecessary debugging fmt

* ds1 refactor: added substitution layer and object with paths to example data

Co-authored-by: M. Sz <mszeptuch@protonmail.com>

* Ds1 refactor: removed npcIndexes field+fixed SetNumberOfWalls bug (#7)

* ds1 refactor: removed npcIndexes field
it was unnecessary, because described a number of objects with paths to use in encoder, but we can calculate manually

* ds1 refactor: fixed set number of (layers) bug

* ds1 refactor: SetNumberOf...Layers now returns error if incorrect number given

* ds1 refactor: lintfix

* ds1 refactor: rename: setupStreamLayerTypes -> GetStreamLayerTypes

Co-authored-by: M. Sz <mszeptuch@protonmail.com>

* WIP

* Ds1 refactor - tests (#10)

* ds1 refactor test: example data

* added loader check

* ds1 refactor: fixed bug, with loading substitutions; added descriptive error message in engine.go:118 and changed Logger.Error with Logger.Fatal

* ds1 refactor: fixed loading bug

* ds1 refactor: fixed bug when walls wasn't rendered (now we can see only walls :-)

Co-authored-by: M. Sz <mszeptuch@protonmail.com>

* ds1: floor rendering bugfix

* ds1: implemented encode layers method

* ds1: implemented encoder

* ds1: update of ds1_test

Co-authored-by: gravestench <dknuth0101@gmail.com>
Co-authored-by: M. Sz <mszeptuch@protonmail.com>
This commit is contained in:
gucio321 2021-03-24 18:28:02 +01:00 committed by GitHub
parent 6f41387e30
commit 6e7e7b9d3f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 149 additions and 175 deletions

View File

@ -475,7 +475,7 @@ func (ds1 *DS1) loadNpcPaths(br *d2datautils.StreamReader, objIdx, numPaths int)
}
func (ds1 *DS1) loadLayerStreams(br *d2datautils.StreamReader) error {
var dirLookup = []int32{
dirLookup := []int32{
0x00, 0x01, 0x02, 0x01, 0x02, 0x03, 0x03, 0x05, 0x05, 0x06,
0x06, 0x07, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
0x0F, 0x10, 0x11, 0x12, 0x14,
@ -513,9 +513,9 @@ func (ds1 *DS1) loadLayerStreams(br *d2datautils.StreamReader) error {
floorIndex := int(layerStreamType) - int(layerStreamFloor1)
ds1.Floors[floorIndex].Tile(x, y).DecodeFloor(dw)
case layerStreamShadow1:
ds1.Floors[0].Tile(x, y).DecodeShadow(dw)
ds1.Shadows[0].Tile(x, y).DecodeShadow(dw)
case layerStreamSubstitute1:
ds1.Floors[0].Tile(x, y).Substitution = dw
ds1.Substitutions[0].Tile(x, y).Substitution = dw
}
}
}
@ -529,145 +529,144 @@ func (ds1 *DS1) SetSize(w, h int) {
ds1.ds1Layers.SetSize(w, h)
}
//
// // Marshal encodes ds1 back to byte slice
// func (ds1 *DS1) Marshal() []byte {
// // create stream writer
// sw := d2datautils.CreateStreamWriter()
//
// // Step 1 - encode header
// sw.PushInt32(ds1.version)
// sw.PushInt32(ds1.width - 1)
// sw.PushInt32(ds1.height - 1)
//
// if ds1.version >= v8 {
// sw.PushInt32(ds1.Act - 1)
// }
//
// if ds1.version >= v10 {
// sw.PushInt32(ds1.substitutionType)
// }
//
// if ds1.version >= v3 {
// sw.PushInt32(int32(len(ds1.Files)))
//
// for _, i := range ds1.Files {
// sw.PushBytes([]byte(i)...)
//
// // separator
// sw.PushBytes(0)
// }
// }
//
// if ds1.version >= v9 && ds1.version <= v13 {
// sw.PushBytes(ds1.unknown1...)
// }
//
// if ds1.version >= v4 {
// sw.PushInt32(ds1.numberOfWallLayers)
//
// if ds1.version >= v16 {
// sw.PushInt32(ds1.numberOfFloorLayers)
// }
// }
//
// // Step 2 - encode grid
// ds1.encodeLayers(sw)
//
// // Step 3 - encode Objects
// if !(ds1.version < v2) {
// sw.PushInt32(int32(len(ds1.Objects)))
//
// for _, i := range ds1.Objects {
// sw.PushUint32(uint32(i.Type))
// sw.PushUint32(uint32(i.ID))
// sw.PushUint32(uint32(i.X))
// sw.PushUint32(uint32(i.Y))
// sw.PushUint32(uint32(i.Flags))
// }
// }
//
// // Step 4 - encode Substitutions
// if ds1.version >= v12 && (ds1.substitutionType == subType1 || ds1.substitutionType == subType2) {
// sw.PushUint32(ds1.unknown2)
//
// sw.PushUint32(uint32(len(ds1.substitutionGroups)))
//
// for _, i := range ds1.substitutionGroups {
// sw.PushInt32(i.TileX)
// sw.PushInt32(i.TileY)
// sw.PushInt32(i.WidthInTiles)
// sw.PushInt32(i.HeightInTiles)
// sw.PushInt32(i.Unknown)
// }
// }
//
// // Step 5 - encode NPC's and its paths
// ds1.encodeNPCs(sw)
//
// return sw.GetBytes()
// }
//
// func (ds1 *DS1) encodeLayers(sw *d2datautils.StreamWriter) {
// /*
// layerStreamTypes := ds1.getLayerSchema()
//
// for _, layerStreamType := range layerStreamTypes {
// for y := 0; y < int(ds1.height); y++ {
// for x := 0; x < int(ds1.width); x++ {
// dw := uint32(0)
//
// switch layerStreamType {
// case layerStreamWall1, layerStreamWall2, layerStreamWall3, layerStreamWall4:
// wallIndex := int(layerStreamType) - int(layerStreamWall1)
// ds1.tiles[y][x].Walls[wallIndex].Encode(sw)
// case layerStreamOrientation1, layerStreamOrientation2,
// layerStreamOrientation3, layerStreamOrientation4:
// wallIndex := int(layerStreamType) - int(layerStreamOrientation1)
// dw |= uint32(ds1.tiles[y][x].Walls[wallIndex].Type)
// dw |= (uint32(ds1.tiles[y][x].Walls[wallIndex].Zero) & wallZeroBitmask) << wallZeroOffset
//
// sw.PushUint32(dw)
// case layerStreamFloor1, layerStreamFloor2:
// floorIndex := int(layerStreamType) - int(layerStreamFloor1)
// ds1.tiles[y][x].Floors[floorIndex].Encode(sw)
// case d2enum.layerStreamShadow1:
// ds1.tiles[y][x].Shadows[0].Encode(sw)
// case d2enum.layerStreamSubstitute1:
// sw.PushUint32(ds1.tiles[y][x].Substitutions[0].Unknown)
// }
// }
// }
// }
// */
// }
//
// func (ds1 *DS1) encodeNPCs(sw *d2datautils.StreamWriter) {
// objectsWithPaths := make([]int, 0)
//
// for n, obj := range ds1.Objects {
// if len(obj.Paths) != 0 {
// objectsWithPaths = append(objectsWithPaths, n)
// }
// }
//
// // Step 5.1 - encode npc's
// sw.PushUint32(uint32(len(objectsWithPaths)))
//
// // Step 5.2 - enoce npcs' paths
// for objectIdx := range objectsWithPaths {
// sw.PushUint32(uint32(len(ds1.Objects[objectIdx].Paths)))
// sw.PushUint32(uint32(ds1.Objects[objectIdx].X))
// sw.PushUint32(uint32(ds1.Objects[objectIdx].Y))
//
// for _, path := range ds1.Objects[objectIdx].Paths {
// sw.PushUint32(uint32(path.Position.X()))
// sw.PushUint32(uint32(path.Position.Y()))
//
// if ds1.version >= v15 {
// sw.PushUint32(uint32(path.Action))
// }
// }
// }
// }
//
// Marshal encodes ds1 back to byte slice
func (ds1 *DS1) Marshal() []byte {
// create stream writer
sw := d2datautils.CreateStreamWriter()
// Step 1 - encode header
sw.PushInt32(int32(ds1.version))
sw.PushInt32(int32(ds1.width - 1))
sw.PushInt32(int32(ds1.height - 1))
if ds1.version.specifiesAct() {
sw.PushInt32(ds1.Act - 1)
}
if ds1.version.specifiesSubstitutionType() {
sw.PushInt32(ds1.substitutionType)
}
if ds1.version.hasFileList() {
sw.PushInt32(int32(len(ds1.Files)))
for _, i := range ds1.Files {
sw.PushBytes([]byte(i)...)
// separator
sw.PushBytes(0)
}
}
if ds1.version.hasUnknown1Bytes() {
sw.PushBytes(ds1.unknown1...)
}
if ds1.version.specifiesWalls() {
sw.PushInt32(int32(len(ds1.Walls)))
if ds1.version.specifiesFloors() {
sw.PushInt32(int32(len(ds1.Walls)))
}
}
// Step 2 - encode grid
ds1.encodeLayers(sw)
// Step 3 - encode Objects
if ds1.version.hasObjects() {
sw.PushInt32(int32(len(ds1.Objects)))
for _, i := range ds1.Objects {
sw.PushUint32(uint32(i.Type))
sw.PushUint32(uint32(i.ID))
sw.PushUint32(uint32(i.X))
sw.PushUint32(uint32(i.Y))
sw.PushUint32(uint32(i.Flags))
}
}
// Step 4 - encode Substitutions
hasSubstitutions := ds1.version.hasSubstitutions() &&
(ds1.substitutionType == subType1 || ds1.substitutionType == subType2)
if hasSubstitutions {
sw.PushUint32(ds1.unknown2)
sw.PushUint32(uint32(len(ds1.substitutionGroups)))
for _, i := range ds1.substitutionGroups {
sw.PushInt32(i.TileX)
sw.PushInt32(i.TileY)
sw.PushInt32(i.WidthInTiles)
sw.PushInt32(i.HeightInTiles)
sw.PushInt32(i.Unknown)
}
}
// Step 5 - encode NPC's and its paths
ds1.encodeNPCs(sw)
return sw.GetBytes()
}
func (ds1 *DS1) encodeLayers(sw *d2datautils.StreamWriter) {
layerStreamTypes := ds1.getLayerSchema()
for _, layerStreamType := range layerStreamTypes {
for y := 0; y < int(ds1.height); y++ {
for x := 0; x < int(ds1.width); x++ {
dw := uint32(0)
switch layerStreamType {
case layerStreamWall1, layerStreamWall2, layerStreamWall3, layerStreamWall4:
wallIndex := int(layerStreamType) - int(layerStreamWall1)
ds1.Walls[wallIndex].Tile(x, y).EncodeWall(sw)
case layerStreamOrientation1, layerStreamOrientation2,
layerStreamOrientation3, layerStreamOrientation4:
wallIndex := int(layerStreamType) - int(layerStreamOrientation1)
dw |= uint32(ds1.Walls[wallIndex].Tile(x, y).Type)
dw |= (uint32(ds1.Walls[wallIndex].Tile(x, y).Zero) & wallZeroBitmask) << wallZeroOffset
sw.PushUint32(dw)
case layerStreamFloor1, layerStreamFloor2:
floorIndex := int(layerStreamType) - int(layerStreamFloor1)
ds1.Floors[floorIndex].Tile(x, y).EncodeFloor(sw)
case layerStreamShadow1:
ds1.Shadows[0].Tile(x, y).EncodeShadow(sw)
case layerStreamSubstitute1:
sw.PushUint32(ds1.Substitutions[0].Tile(x, y).Substitution)
}
}
}
}
}
func (ds1 *DS1) encodeNPCs(sw *d2datautils.StreamWriter) {
objectsWithPaths := make([]int, 0)
for n, obj := range ds1.Objects {
if len(obj.Paths) != 0 {
objectsWithPaths = append(objectsWithPaths, n)
}
}
// Step 5.1 - encode npc's
sw.PushUint32(uint32(len(objectsWithPaths)))
// Step 5.2 - enoce npcs' paths
for objectIdx := range objectsWithPaths {
sw.PushUint32(uint32(len(ds1.Objects[objectIdx].Paths)))
sw.PushUint32(uint32(ds1.Objects[objectIdx].X))
sw.PushUint32(uint32(ds1.Objects[objectIdx].Y))
for _, path := range ds1.Objects[objectIdx].Paths {
sw.PushUint32(uint32(path.Position.X()))
sw.PushUint32(uint32(path.Position.Y()))
if ds1.version >= v15 {
sw.PushUint32(uint32(path.Action))
}
}
}
}

View File

@ -3,9 +3,6 @@ package d2ds1
import (
"testing"
"log"
"os"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2path"
)
@ -98,8 +95,8 @@ func exampleData() *DS1 {
result := &DS1{
ds1Layers: &ds1Layers{
width: 20,
height: 80,
width: 2,
height: 2,
Floors: layerGroup{
// number of floors (one floor)
{
@ -188,35 +185,13 @@ func exampleData() *DS1 {
return result
}
func TestDS1_Load(t *testing.T) {
testFile, fileErr := os.Open("testdata/testdata.ds1")
if fileErr != nil {
t.Error("cannot open test data file")
return
}
func TestDS1_MarshalUnmarshal(t *testing.T) {
ds1 := exampleData()
data := make([]byte, 0)
buf := make([]byte, 16)
for {
numRead, err := testFile.Read(buf)
data = append(data, buf[:numRead]...)
if err != nil {
break
}
}
data := ds1.Marshal()
_, loadErr := Unmarshal(data)
if loadErr != nil {
t.Error(loadErr)
}
err := testFile.Close()
if err != nil {
t.Fail()
log.Print(err)
}
}