mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2024-11-18 02:16:23 -05:00
Merge branch 'master' into date-encoder-font
This commit is contained in:
commit
298fc786b8
@ -1,10 +1,15 @@
|
|||||||
package d2datautils
|
package d2datautils
|
||||||
|
|
||||||
import "bytes"
|
import (
|
||||||
|
"bytes"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
// StreamWriter allows you to create a byte array by streaming in writes of various sizes
|
// StreamWriter allows you to create a byte array by streaming in writes of various sizes
|
||||||
type StreamWriter struct {
|
type StreamWriter struct {
|
||||||
data *bytes.Buffer
|
data *bytes.Buffer
|
||||||
|
bitOffset int
|
||||||
|
bitCache byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateStreamWriter creates a new StreamWriter instance
|
// CreateStreamWriter creates a new StreamWriter instance
|
||||||
@ -28,6 +33,66 @@ func (v *StreamWriter) PushBytes(b ...byte) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PushBit pushes single bit into stream
|
||||||
|
// WARNING: if you'll use PushBit, offset'll be less than 8, and if you'll
|
||||||
|
// use another Push... method, bits'll not be pushed
|
||||||
|
func (v *StreamWriter) PushBit(b bool) {
|
||||||
|
if b {
|
||||||
|
v.bitCache |= (1 << v.bitOffset)
|
||||||
|
}
|
||||||
|
v.bitOffset++
|
||||||
|
|
||||||
|
if v.bitOffset != bitsPerByte {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
v.PushBytes(v.bitCache)
|
||||||
|
v.bitCache = 0
|
||||||
|
v.bitOffset = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// PushBits pushes bits (with max range 8)
|
||||||
|
func (v *StreamWriter) PushBits(b byte, bits int) {
|
||||||
|
if bits > bitsPerByte {
|
||||||
|
log.Print("input bits number must be less (or equal) than 8")
|
||||||
|
}
|
||||||
|
|
||||||
|
val := b
|
||||||
|
|
||||||
|
for i := 0; i < bits; i++ {
|
||||||
|
v.PushBit(val&1 == 1)
|
||||||
|
val >>= 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PushBits16 pushes bits (with max range 16)
|
||||||
|
func (v *StreamWriter) PushBits16(b uint16, bits int) {
|
||||||
|
if bits > bitsPerByte*bytesPerint16 {
|
||||||
|
log.Print("input bits number must be less (or equal) than 16")
|
||||||
|
}
|
||||||
|
|
||||||
|
val := b
|
||||||
|
|
||||||
|
for i := 0; i < bits; i++ {
|
||||||
|
v.PushBit(val&1 == 1)
|
||||||
|
val >>= 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PushBits32 pushes bits (with max range 32)
|
||||||
|
func (v *StreamWriter) PushBits32(b uint32, bits int) {
|
||||||
|
if bits > bitsPerByte*bytesPerint32 {
|
||||||
|
log.Print("input bits number must be less (or equal) than 32")
|
||||||
|
}
|
||||||
|
|
||||||
|
val := b
|
||||||
|
|
||||||
|
for i := 0; i < bits; i++ {
|
||||||
|
v.PushBit(val&1 == 1)
|
||||||
|
val >>= 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// PushInt16 writes a int16 word to the stream
|
// PushInt16 writes a int16 word to the stream
|
||||||
func (v *StreamWriter) PushInt16(val int16) {
|
func (v *StreamWriter) PushInt16(val int16) {
|
||||||
v.PushUint16(uint16(val))
|
v.PushUint16(uint16(val))
|
||||||
|
@ -4,6 +4,65 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestStreamWriterBits(t *testing.T) {
|
||||||
|
sr := CreateStreamWriter()
|
||||||
|
data := []byte{221, 19}
|
||||||
|
|
||||||
|
for _, i := range data {
|
||||||
|
sr.PushBits(i, bitsPerByte)
|
||||||
|
}
|
||||||
|
|
||||||
|
output := sr.GetBytes()
|
||||||
|
for i, d := range data {
|
||||||
|
if output[i] != d {
|
||||||
|
t.Fatalf("sr.PushBits() pushed %X, but wrote %X instead", d, output[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStreamWriterBits16(t *testing.T) {
|
||||||
|
sr := CreateStreamWriter()
|
||||||
|
data := []uint16{1024, 19}
|
||||||
|
|
||||||
|
for _, i := range data {
|
||||||
|
sr.PushBits16(i, bitsPerByte*bytesPerint16)
|
||||||
|
}
|
||||||
|
|
||||||
|
output := sr.GetBytes()
|
||||||
|
|
||||||
|
for i, d := range data {
|
||||||
|
// nolint:gomnd // offset in byte slice; bit shifts for uint16
|
||||||
|
outputInt := uint16(output[bytesPerint16*i]) |
|
||||||
|
uint16(output[bytesPerint16*i+1])<<8
|
||||||
|
if outputInt != d {
|
||||||
|
t.Fatalf("sr.PushBits16() pushed %X, but wrote %X instead", d, output[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStreamWriterBits32(t *testing.T) {
|
||||||
|
sr := CreateStreamWriter()
|
||||||
|
data := []uint32{19324, 87}
|
||||||
|
|
||||||
|
for _, i := range data {
|
||||||
|
sr.PushBits32(i, bitsPerByte*bytesPerint32)
|
||||||
|
}
|
||||||
|
|
||||||
|
output := sr.GetBytes()
|
||||||
|
|
||||||
|
for i, d := range data {
|
||||||
|
// nolint:gomnd // offset in byte slice; bit shifts for uint32
|
||||||
|
outputInt := uint32(output[bytesPerint32*i]) |
|
||||||
|
uint32(output[bytesPerint32*i+1])<<8 |
|
||||||
|
uint32(output[bytesPerint32*i+2])<<16 |
|
||||||
|
uint32(output[bytesPerint32*i+3])<<24
|
||||||
|
|
||||||
|
if outputInt != d {
|
||||||
|
t.Fatalf("sr.PushBits32() pushed %X, but wrote %X instead", d, output[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestStreamWriterByte(t *testing.T) {
|
func TestStreamWriterByte(t *testing.T) {
|
||||||
sr := CreateStreamWriter()
|
sr := CreateStreamWriter()
|
||||||
data := []byte{0x12, 0x34, 0x56, 0x78}
|
data := []byte{0x12, 0x34, 0x56, 0x78}
|
||||||
|
@ -121,7 +121,6 @@ func (c *COF) Unmarshal(fileData []byte) error {
|
|||||||
layer.Transparent = b[layerTransparent] > 0
|
layer.Transparent = b[layerTransparent] > 0
|
||||||
layer.DrawEffect = d2enum.DrawEffect(b[layerDrawEffect])
|
layer.DrawEffect = d2enum.DrawEffect(b[layerDrawEffect])
|
||||||
|
|
||||||
layer.weaponClassByte = b[layerWeaponClass:]
|
|
||||||
layer.WeaponClass = d2enum.WeaponClassFromString(strings.TrimSpace(strings.ReplaceAll(
|
layer.WeaponClass = d2enum.WeaponClassFromString(strings.TrimSpace(strings.ReplaceAll(
|
||||||
string(b[layerWeaponClass:]), badCharacter, "")))
|
string(b[layerWeaponClass:]), badCharacter, "")))
|
||||||
|
|
||||||
@ -193,7 +192,22 @@ func (c *COF) Marshal() []byte {
|
|||||||
|
|
||||||
sw.PushBytes(byte(c.CofLayers[i].DrawEffect))
|
sw.PushBytes(byte(c.CofLayers[i].DrawEffect))
|
||||||
|
|
||||||
sw.PushBytes(c.CofLayers[i].weaponClassByte...)
|
const (
|
||||||
|
maxCodeLength = 3 // we assume item codes to look like 'hax' or 'kit'
|
||||||
|
terminator = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
weaponCode := c.CofLayers[i].WeaponClass.String()
|
||||||
|
|
||||||
|
for idx, letter := range weaponCode {
|
||||||
|
if idx > maxCodeLength {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
sw.PushBytes(byte(letter))
|
||||||
|
}
|
||||||
|
|
||||||
|
sw.PushBytes(terminator)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, i := range c.AnimationFrames {
|
for _, i := range c.AnimationFrames {
|
||||||
|
@ -4,11 +4,10 @@ import "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
|||||||
|
|
||||||
// CofLayer is a structure that represents a single layer in a COF file.
|
// CofLayer is a structure that represents a single layer in a COF file.
|
||||||
type CofLayer struct {
|
type CofLayer struct {
|
||||||
Type d2enum.CompositeType
|
Type d2enum.CompositeType
|
||||||
Shadow byte
|
Shadow byte
|
||||||
Selectable bool
|
Selectable bool
|
||||||
Transparent bool
|
Transparent bool
|
||||||
DrawEffect d2enum.DrawEffect
|
DrawEffect d2enum.DrawEffect
|
||||||
WeaponClass d2enum.WeaponClass
|
WeaponClass d2enum.WeaponClass
|
||||||
weaponClassByte []byte
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package d2dat
|
package d2dat
|
||||||
|
|
||||||
import "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
import (
|
||||||
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// index offset helpers
|
// index offset helpers
|
||||||
@ -21,3 +23,14 @@ func Load(data []byte) (d2interface.Palette, error) {
|
|||||||
|
|
||||||
return palette, nil
|
return palette, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Marshal encodes data palette back into byte slice
|
||||||
|
func (p *DATPalette) Marshal() []byte {
|
||||||
|
result := make([]byte, len(p.colors))
|
||||||
|
|
||||||
|
for _, i := range &p.colors {
|
||||||
|
result = append(result, i.B(), i.G(), i.R())
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
@ -32,36 +32,62 @@ type DC6 struct {
|
|||||||
Frames []*DC6Frame // size is Directions*FramesPerDirection
|
Frames []*DC6Frame // size is Directions*FramesPerDirection
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load uses restruct to read the binary dc6 data into structs then parses image data from the frame data.
|
// New creates a new, empty DC6
|
||||||
|
func New() *DC6 {
|
||||||
|
result := &DC6{
|
||||||
|
Version: 0,
|
||||||
|
Flags: 0,
|
||||||
|
Encoding: 0,
|
||||||
|
Termination: make([]byte, 4),
|
||||||
|
Directions: 0,
|
||||||
|
FramesPerDirection: 0,
|
||||||
|
FramePointers: make([]uint32, 0),
|
||||||
|
Frames: make([]*DC6Frame, 0),
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load loads a dc6 animation
|
||||||
func Load(data []byte) (*DC6, error) {
|
func Load(data []byte) (*DC6, error) {
|
||||||
r := d2datautils.CreateStreamReader(data)
|
d := New()
|
||||||
|
|
||||||
var dc DC6
|
err := d.Unmarshal(data)
|
||||||
|
|
||||||
var err error
|
|
||||||
|
|
||||||
err = dc.loadHeader(r)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
frameCount := int(dc.Directions * dc.FramesPerDirection)
|
return d, nil
|
||||||
|
}
|
||||||
|
|
||||||
dc.FramePointers = make([]uint32, frameCount)
|
// Unmarshal converts bite slice into DC6 structure
|
||||||
|
func (d *DC6) Unmarshal(data []byte) error {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
r := d2datautils.CreateStreamReader(data)
|
||||||
|
|
||||||
|
err = d.loadHeader(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
frameCount := int(d.Directions * d.FramesPerDirection)
|
||||||
|
|
||||||
|
d.FramePointers = make([]uint32, frameCount)
|
||||||
for i := 0; i < frameCount; i++ {
|
for i := 0; i < frameCount; i++ {
|
||||||
dc.FramePointers[i], err = r.ReadUInt32()
|
d.FramePointers[i], err = r.ReadUInt32()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dc.Frames = make([]*DC6Frame, frameCount)
|
d.Frames = make([]*DC6Frame, frameCount)
|
||||||
|
|
||||||
if err := dc.loadFrames(r); err != nil {
|
if err := d.loadFrames(r); err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &dc, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DC6) loadHeader(r *d2datautils.StreamReader) error {
|
func (d *DC6) loadHeader(r *d2datautils.StreamReader) error {
|
||||||
@ -241,7 +267,7 @@ func (d *DC6) Clone() *DC6 {
|
|||||||
|
|
||||||
for i := range d.Frames {
|
for i := range d.Frames {
|
||||||
cloneFrame := *d.Frames[i]
|
cloneFrame := *d.Frames[i]
|
||||||
clone.Frames = append(clone.Frames, &cloneFrame)
|
clone.Frames[i] = &cloneFrame
|
||||||
}
|
}
|
||||||
|
|
||||||
return &clone
|
return &clone
|
||||||
|
69
d2common/d2fileformats/d2dc6/dc6_test.go
Normal file
69
d2common/d2fileformats/d2dc6/dc6_test.go
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
package d2dc6
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDC6New(t *testing.T) {
|
||||||
|
dc6 := New()
|
||||||
|
|
||||||
|
if dc6 == nil {
|
||||||
|
t.Error("d2dc6.New() method returned nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getExampleDC6() *DC6 {
|
||||||
|
exampleDC6 := &DC6{
|
||||||
|
Version: 6,
|
||||||
|
Flags: 1,
|
||||||
|
Encoding: 0,
|
||||||
|
Termination: []byte{238, 238, 238, 238},
|
||||||
|
Directions: 1,
|
||||||
|
FramesPerDirection: 1,
|
||||||
|
FramePointers: []uint32{56},
|
||||||
|
Frames: []*DC6Frame{
|
||||||
|
{
|
||||||
|
Flipped: 0,
|
||||||
|
Width: 32,
|
||||||
|
Height: 26,
|
||||||
|
OffsetX: 45,
|
||||||
|
OffsetY: 24,
|
||||||
|
Unknown: 0,
|
||||||
|
NextBlock: 50,
|
||||||
|
Length: 10,
|
||||||
|
FrameData: []byte{2, 23, 34, 128, 53, 64, 39, 43, 123, 12},
|
||||||
|
Terminator: []byte{2, 8, 5},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return exampleDC6
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDC6Unmarshal(t *testing.T) {
|
||||||
|
exampleDC6 := getExampleDC6()
|
||||||
|
|
||||||
|
data := exampleDC6.Marshal()
|
||||||
|
|
||||||
|
extractedDC6, err := Load(data)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if exampleDC6.Version != extractedDC6.Version ||
|
||||||
|
len(exampleDC6.Frames) != len(extractedDC6.Frames) ||
|
||||||
|
exampleDC6.Frames[0].NextBlock != extractedDC6.Frames[0].NextBlock {
|
||||||
|
t.Fatal("encoded and decoded DC6 isn't the same")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDC6Clone(t *testing.T) {
|
||||||
|
exampleDC6 := getExampleDC6()
|
||||||
|
clonedDC6 := exampleDC6.Clone()
|
||||||
|
|
||||||
|
if exampleDC6.Termination[0] != clonedDC6.Termination[0] ||
|
||||||
|
len(exampleDC6.Frames) != len(clonedDC6.Frames) ||
|
||||||
|
exampleDC6.Frames[0].NextBlock != clonedDC6.Frames[0].NextBlock {
|
||||||
|
t.Fatal("cloned dc6 isn't equal to original")
|
||||||
|
}
|
||||||
|
}
|
@ -9,22 +9,32 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
maxActNumber = 5
|
subType1 = 1
|
||||||
subType1 = 1
|
subType2 = 2
|
||||||
subType2 = 2
|
v2 = 2
|
||||||
v2 = 2
|
v3 = 3
|
||||||
v3 = 3
|
v4 = 4
|
||||||
v4 = 4
|
v7 = 7
|
||||||
v7 = 7
|
v8 = 8
|
||||||
v8 = 8
|
v9 = 9
|
||||||
v9 = 9
|
v10 = 10
|
||||||
v10 = 10
|
v12 = 12
|
||||||
v12 = 12
|
v13 = 13
|
||||||
v13 = 13
|
v14 = 14
|
||||||
v14 = 14
|
v15 = 15
|
||||||
v15 = 15
|
v16 = 16
|
||||||
v16 = 16
|
v18 = 18
|
||||||
v18 = 18
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
wallZeroBitmask = 0xFFFFFF00
|
||||||
|
wallZeroOffset = 8
|
||||||
|
|
||||||
|
wallTypeBitmask = 0x000000FF
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
unknown1BytesCount = 8
|
||||||
)
|
)
|
||||||
|
|
||||||
// DS1 represents the "stamp" data that is used to build up maps.
|
// DS1 represents the "stamp" data that is used to build up maps.
|
||||||
@ -43,10 +53,13 @@ type DS1 struct {
|
|||||||
NumberOfShadowLayers int32 // ShadowNum number of shadow layer used
|
NumberOfShadowLayers int32 // ShadowNum number of shadow layer used
|
||||||
NumberOfSubstitutionLayers int32 // SubstitutionNum number of substitution layer used
|
NumberOfSubstitutionLayers int32 // SubstitutionNum number of substitution layer used
|
||||||
SubstitutionGroupsNum int32 // SubstitutionGroupsNum number of substitution groups, datas between objects & NPC paths
|
SubstitutionGroupsNum int32 // SubstitutionGroupsNum number of substitution groups, datas between objects & NPC paths
|
||||||
|
unknown1 []byte
|
||||||
|
layerStreamTypes []d2enum.LayerStreamType
|
||||||
|
unknown2 uint32
|
||||||
|
npcIndexes []int
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadDS1 loads the specified DS1 file
|
// LoadDS1 loads the specified DS1 file
|
||||||
//nolint:funlen,gocognit,gocyclo // will refactor later
|
|
||||||
func LoadDS1(fileData []byte) (*DS1, error) {
|
func LoadDS1(fileData []byte) (*DS1, error) {
|
||||||
ds1 := &DS1{
|
ds1 := &DS1{
|
||||||
Act: 1,
|
Act: 1,
|
||||||
@ -60,74 +73,17 @@ func LoadDS1(fileData []byte) (*DS1, error) {
|
|||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
ds1.Version, err = br.ReadInt32()
|
err = ds1.loadHeader(br)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ds1.Width, err = br.ReadInt32()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ds1.Height, err = br.ReadInt32()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ds1.Width++
|
|
||||||
ds1.Height++
|
|
||||||
|
|
||||||
if ds1.Version >= v8 {
|
|
||||||
ds1.Act, err = br.ReadInt32()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ds1.Act = d2math.MinInt32(maxActNumber, ds1.Act+1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if ds1.Version >= v10 {
|
|
||||||
ds1.SubstitutionType, err = br.ReadInt32()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if ds1.SubstitutionType == 1 || ds1.SubstitutionType == 2 {
|
|
||||||
ds1.NumberOfSubstitutionLayers = 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ds1.Version >= v3 {
|
|
||||||
// These files reference things that don't exist anymore :-?
|
|
||||||
numberOfFiles, err := br.ReadInt32() //nolint:govet // i want to re-use the err variable...
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ds1.Files = make([]string, numberOfFiles)
|
|
||||||
|
|
||||||
for i := 0; i < int(numberOfFiles); i++ {
|
|
||||||
ds1.Files[i] = ""
|
|
||||||
|
|
||||||
for {
|
|
||||||
ch, err := br.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if ch == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
ds1.Files[i] += string(ch)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ds1.Version >= v9 && ds1.Version <= v13 {
|
if ds1.Version >= v9 && ds1.Version <= v13 {
|
||||||
// Skipping two dwords because they are "meaningless"?
|
// Skipping two dwords because they are "meaningless"?
|
||||||
br.SkipBytes(8) //nolint:gomnd // We don't know what's here
|
ds1.unknown1, err = br.ReadBytes(unknown1BytesCount)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ds1.Version >= v4 {
|
if ds1.Version >= v4 {
|
||||||
@ -146,7 +102,7 @@ func LoadDS1(fileData []byte) (*DS1, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
layerStream := ds1.setupStreamLayerTypes()
|
ds1.layerStreamTypes = ds1.setupStreamLayerTypes()
|
||||||
|
|
||||||
ds1.Tiles = make([][]TileRecord, ds1.Height)
|
ds1.Tiles = make([][]TileRecord, ds1.Height)
|
||||||
|
|
||||||
@ -160,7 +116,7 @@ func LoadDS1(fileData []byte) (*DS1, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ds1.loadLayerStreams(br, layerStream)
|
err = ds1.loadLayerStreams(br)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -183,6 +139,86 @@ func LoadDS1(fileData []byte) (*DS1, error) {
|
|||||||
return ds1, nil
|
return ds1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ds1 *DS1) loadHeader(br *d2datautils.StreamReader) error {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
ds1.Version, err = br.ReadInt32()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ds1.Width, err = br.ReadInt32()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ds1.Height, err = br.ReadInt32()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ds1.Width++
|
||||||
|
ds1.Height++
|
||||||
|
|
||||||
|
if ds1.Version >= v8 {
|
||||||
|
ds1.Act, err = br.ReadInt32()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ds1.Act = d2math.MinInt32(d2enum.ActsNumber, ds1.Act+1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ds1.Version >= v10 {
|
||||||
|
ds1.SubstitutionType, err = br.ReadInt32()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ds1.SubstitutionType == 1 || ds1.SubstitutionType == 2 {
|
||||||
|
ds1.NumberOfSubstitutionLayers = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ds1.loadFileList(br)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ds1 *DS1) loadFileList(br *d2datautils.StreamReader) error {
|
||||||
|
if ds1.Version >= v3 {
|
||||||
|
// These files reference things that don't exist anymore :-?
|
||||||
|
numberOfFiles, err := br.ReadInt32()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ds1.Files = make([]string, numberOfFiles)
|
||||||
|
|
||||||
|
for i := 0; i < int(numberOfFiles); i++ {
|
||||||
|
ds1.Files[i] = ""
|
||||||
|
|
||||||
|
for {
|
||||||
|
ch, err := br.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ch == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
ds1.Files[i] += string(ch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (ds1 *DS1) loadObjects(br *d2datautils.StreamReader) error {
|
func (ds1 *DS1) loadObjects(br *d2datautils.StreamReader) error {
|
||||||
if ds1.Version < v2 {
|
if ds1.Version < v2 {
|
||||||
ds1.Objects = make([]Object, 0)
|
ds1.Objects = make([]Object, 0)
|
||||||
@ -245,7 +281,10 @@ func (ds1 *DS1) loadSubstitutions(br *d2datautils.StreamReader) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ds1.Version >= v18 {
|
if ds1.Version >= v18 {
|
||||||
_, _ = br.ReadUInt32()
|
ds1.unknown2, err = br.ReadUInt32()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
numberOfSubGroups, err := br.ReadInt32()
|
numberOfSubGroups, err := br.ReadInt32()
|
||||||
@ -340,17 +379,17 @@ func (ds1 *DS1) loadNPCs(br *d2datautils.StreamReader) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for npcIdx := 0; npcIdx < int(numberOfNpcs); npcIdx++ {
|
for npcIdx := 0; npcIdx < int(numberOfNpcs); npcIdx++ {
|
||||||
numPaths, err := br.ReadInt32() //nolint:govet // i want to re-use the err variable...
|
numPaths, err := br.ReadInt32() // nolint:govet // I want to re-use this error variable
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
npcX, err := br.ReadInt32() //nolint:govet // i want to re-use the err variable...
|
npcX, err := br.ReadInt32()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
npcY, err := br.ReadInt32() //nolint:govet // i want to re-use the err variable...
|
npcY, err := br.ReadInt32()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -360,6 +399,8 @@ func (ds1 *DS1) loadNPCs(br *d2datautils.StreamReader) error {
|
|||||||
for idx, ds1Obj := range ds1.Objects {
|
for idx, ds1Obj := range ds1.Objects {
|
||||||
if ds1Obj.X == int(npcX) && ds1Obj.Y == int(npcY) {
|
if ds1Obj.X == int(npcX) && ds1Obj.Y == int(npcY) {
|
||||||
objIdx = idx
|
objIdx = idx
|
||||||
|
ds1.npcIndexes = append(ds1.npcIndexes, idx)
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -418,7 +459,7 @@ func (ds1 *DS1) loadNpcPaths(br *d2datautils.StreamReader, objIdx, numPaths int)
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ds1 *DS1) loadLayerStreams(br *d2datautils.StreamReader, layerStream []d2enum.LayerStreamType) error {
|
func (ds1 *DS1) loadLayerStreams(br *d2datautils.StreamReader) error {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
var dirLookup = []int32{
|
var dirLookup = []int32{
|
||||||
@ -427,8 +468,8 @@ func (ds1 *DS1) loadLayerStreams(br *d2datautils.StreamReader, layerStream []d2e
|
|||||||
0x0F, 0x10, 0x11, 0x12, 0x14,
|
0x0F, 0x10, 0x11, 0x12, 0x14,
|
||||||
}
|
}
|
||||||
|
|
||||||
for lIdx := range layerStream {
|
for lIdx := range ds1.layerStreamTypes {
|
||||||
layerStreamType := layerStream[lIdx]
|
layerStreamType := ds1.layerStreamTypes[lIdx]
|
||||||
|
|
||||||
for y := 0; y < int(ds1.Height); y++ {
|
for y := 0; y < int(ds1.Height); y++ {
|
||||||
for x := 0; x < int(ds1.Width); x++ {
|
for x := 0; x < int(ds1.Width); x++ {
|
||||||
@ -440,16 +481,11 @@ func (ds1 *DS1) loadLayerStreams(br *d2datautils.StreamReader, layerStream []d2e
|
|||||||
switch layerStreamType {
|
switch layerStreamType {
|
||||||
case d2enum.LayerStreamWall1, d2enum.LayerStreamWall2, d2enum.LayerStreamWall3, d2enum.LayerStreamWall4:
|
case d2enum.LayerStreamWall1, d2enum.LayerStreamWall2, d2enum.LayerStreamWall3, d2enum.LayerStreamWall4:
|
||||||
wallIndex := int(layerStreamType) - int(d2enum.LayerStreamWall1)
|
wallIndex := int(layerStreamType) - int(d2enum.LayerStreamWall1)
|
||||||
ds1.Tiles[y][x].Walls[wallIndex].Prop1 = byte(dw & 0x000000FF) //nolint:gomnd // Bitmask
|
ds1.Tiles[y][x].Walls[wallIndex].Decode(dw)
|
||||||
ds1.Tiles[y][x].Walls[wallIndex].Sequence = byte((dw & 0x00003F00) >> 8) //nolint:gomnd // Bitmask
|
|
||||||
ds1.Tiles[y][x].Walls[wallIndex].Unknown1 = byte((dw & 0x000FC000) >> 14) //nolint:gomnd // Bitmask
|
|
||||||
ds1.Tiles[y][x].Walls[wallIndex].Style = byte((dw & 0x03F00000) >> 20) //nolint:gomnd // Bitmask
|
|
||||||
ds1.Tiles[y][x].Walls[wallIndex].Unknown2 = byte((dw & 0x7C000000) >> 26) //nolint:gomnd // Bitmask
|
|
||||||
ds1.Tiles[y][x].Walls[wallIndex].Hidden = byte((dw&0x80000000)>>31) > 0 //nolint:gomnd // Bitmask
|
|
||||||
case d2enum.LayerStreamOrientation1, d2enum.LayerStreamOrientation2,
|
case d2enum.LayerStreamOrientation1, d2enum.LayerStreamOrientation2,
|
||||||
d2enum.LayerStreamOrientation3, d2enum.LayerStreamOrientation4:
|
d2enum.LayerStreamOrientation3, d2enum.LayerStreamOrientation4:
|
||||||
wallIndex := int(layerStreamType) - int(d2enum.LayerStreamOrientation1)
|
wallIndex := int(layerStreamType) - int(d2enum.LayerStreamOrientation1)
|
||||||
c := int32(dw & 0x000000FF) //nolint:gomnd // Bitmask
|
c := int32(dw & wallTypeBitmask)
|
||||||
|
|
||||||
if ds1.Version < v7 {
|
if ds1.Version < v7 {
|
||||||
if c < int32(len(dirLookup)) {
|
if c < int32(len(dirLookup)) {
|
||||||
@ -458,22 +494,12 @@ func (ds1 *DS1) loadLayerStreams(br *d2datautils.StreamReader, layerStream []d2e
|
|||||||
}
|
}
|
||||||
|
|
||||||
ds1.Tiles[y][x].Walls[wallIndex].Type = d2enum.TileType(c)
|
ds1.Tiles[y][x].Walls[wallIndex].Type = d2enum.TileType(c)
|
||||||
ds1.Tiles[y][x].Walls[wallIndex].Zero = byte((dw & 0xFFFFFF00) >> 8) //nolint:gomnd // Bitmask
|
ds1.Tiles[y][x].Walls[wallIndex].Zero = byte((dw & wallZeroBitmask) >> wallZeroOffset)
|
||||||
case d2enum.LayerStreamFloor1, d2enum.LayerStreamFloor2:
|
case d2enum.LayerStreamFloor1, d2enum.LayerStreamFloor2:
|
||||||
floorIndex := int(layerStreamType) - int(d2enum.LayerStreamFloor1)
|
floorIndex := int(layerStreamType) - int(d2enum.LayerStreamFloor1)
|
||||||
ds1.Tiles[y][x].Floors[floorIndex].Prop1 = byte(dw & 0x000000FF) //nolint:gomnd // Bitmask
|
ds1.Tiles[y][x].Floors[floorIndex].Decode(dw)
|
||||||
ds1.Tiles[y][x].Floors[floorIndex].Sequence = byte((dw & 0x00003F00) >> 8) //nolint:gomnd // Bitmask
|
|
||||||
ds1.Tiles[y][x].Floors[floorIndex].Unknown1 = byte((dw & 0x000FC000) >> 14) //nolint:gomnd // Bitmask
|
|
||||||
ds1.Tiles[y][x].Floors[floorIndex].Style = byte((dw & 0x03F00000) >> 20) //nolint:gomnd // Bitmask
|
|
||||||
ds1.Tiles[y][x].Floors[floorIndex].Unknown2 = byte((dw & 0x7C000000) >> 26) //nolint:gomnd // Bitmask
|
|
||||||
ds1.Tiles[y][x].Floors[floorIndex].Hidden = byte((dw&0x80000000)>>31) > 0 //nolint:gomnd // Bitmask
|
|
||||||
case d2enum.LayerStreamShadow:
|
case d2enum.LayerStreamShadow:
|
||||||
ds1.Tiles[y][x].Shadows[0].Prop1 = byte(dw & 0x000000FF) //nolint:gomnd // Bitmask
|
ds1.Tiles[y][x].Shadows[0].Decode(dw)
|
||||||
ds1.Tiles[y][x].Shadows[0].Sequence = byte((dw & 0x00003F00) >> 8) //nolint:gomnd // Bitmask
|
|
||||||
ds1.Tiles[y][x].Shadows[0].Unknown1 = byte((dw & 0x000FC000) >> 14) //nolint:gomnd // Bitmask
|
|
||||||
ds1.Tiles[y][x].Shadows[0].Style = byte((dw & 0x03F00000) >> 20) //nolint:gomnd // Bitmask
|
|
||||||
ds1.Tiles[y][x].Shadows[0].Unknown2 = byte((dw & 0x7C000000) >> 26) //nolint:gomnd // Bitmask
|
|
||||||
ds1.Tiles[y][x].Shadows[0].Hidden = byte((dw&0x80000000)>>31) > 0 //nolint:gomnd // Bitmask
|
|
||||||
case d2enum.LayerStreamSubstitute:
|
case d2enum.LayerStreamSubstitute:
|
||||||
ds1.Tiles[y][x].Substitutions[0].Unknown = dw
|
ds1.Tiles[y][x].Substitutions[0].Unknown = dw
|
||||||
}
|
}
|
||||||
@ -483,3 +509,134 @@ func (ds1 *DS1) loadLayerStreams(br *d2datautils.StreamReader, layerStream []d2e
|
|||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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.NumberOfWalls)
|
||||||
|
|
||||||
|
if ds1.Version >= v16 {
|
||||||
|
sw.PushInt32(ds1.NumberOfFloors)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2 - encode layers
|
||||||
|
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) {
|
||||||
|
for lIdx := range ds1.layerStreamTypes {
|
||||||
|
layerStreamType := ds1.layerStreamTypes[lIdx]
|
||||||
|
|
||||||
|
for y := 0; y < int(ds1.Height); y++ {
|
||||||
|
for x := 0; x < int(ds1.Width); x++ {
|
||||||
|
dw := uint32(0)
|
||||||
|
|
||||||
|
switch layerStreamType {
|
||||||
|
case d2enum.LayerStreamWall1, d2enum.LayerStreamWall2, d2enum.LayerStreamWall3, d2enum.LayerStreamWall4:
|
||||||
|
wallIndex := int(layerStreamType) - int(d2enum.LayerStreamWall1)
|
||||||
|
ds1.Tiles[y][x].Walls[wallIndex].Encode(sw)
|
||||||
|
case d2enum.LayerStreamOrientation1, d2enum.LayerStreamOrientation2,
|
||||||
|
d2enum.LayerStreamOrientation3, d2enum.LayerStreamOrientation4:
|
||||||
|
wallIndex := int(layerStreamType) - int(d2enum.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 d2enum.LayerStreamFloor1, d2enum.LayerStreamFloor2:
|
||||||
|
floorIndex := int(layerStreamType) - int(d2enum.LayerStreamFloor1)
|
||||||
|
ds1.Tiles[y][x].Floors[floorIndex].Encode(sw)
|
||||||
|
case d2enum.LayerStreamShadow:
|
||||||
|
ds1.Tiles[y][x].Shadows[0].Encode(sw)
|
||||||
|
case d2enum.LayerStreamSubstitute:
|
||||||
|
sw.PushUint32(ds1.Tiles[y][x].Substitutions[0].Unknown)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ds1 *DS1) encodeNPCs(sw *d2datautils.StreamWriter) {
|
||||||
|
// Step 5.1 - encode npc's
|
||||||
|
sw.PushUint32(uint32(len(ds1.npcIndexes)))
|
||||||
|
|
||||||
|
// Step 5.2 - enoce npcs' paths
|
||||||
|
for _, i := range ds1.npcIndexes {
|
||||||
|
sw.PushUint32(uint32(len(ds1.Objects[i].Paths)))
|
||||||
|
sw.PushUint32(uint32(ds1.Objects[i].X))
|
||||||
|
sw.PushUint32(uint32(ds1.Objects[i].Y))
|
||||||
|
|
||||||
|
for _, j := range ds1.Objects[i].Paths {
|
||||||
|
sw.PushUint32(uint32(j.Position.X()))
|
||||||
|
sw.PushUint32(uint32(j.Position.Y()))
|
||||||
|
|
||||||
|
if ds1.Version >= v15 {
|
||||||
|
sw.PushUint32(uint32(j.Action))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,5 +1,35 @@
|
|||||||
package d2ds1
|
package d2ds1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2datautils"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
prop1Bitmask = 0x000000FF
|
||||||
|
prop1Offset = 0
|
||||||
|
prop1Length = 8
|
||||||
|
|
||||||
|
sequenceBitmask = 0x00003F00
|
||||||
|
sequenceOffset = 8
|
||||||
|
sequenceLength = 6
|
||||||
|
|
||||||
|
unknown1Bitmask = 0x000FC000
|
||||||
|
unknown1Offset = 14
|
||||||
|
unknown1Length = 6
|
||||||
|
|
||||||
|
styleBitmask = 0x03F00000
|
||||||
|
styleOffset = 20
|
||||||
|
styleLength = 6
|
||||||
|
|
||||||
|
unknown2Bitmask = 0x7C000000
|
||||||
|
unknown2Offset = 26
|
||||||
|
unknown2Length = 5
|
||||||
|
|
||||||
|
hiddenBitmask = 0x80000000
|
||||||
|
hiddenOffset = 31
|
||||||
|
hiddenLength = 1
|
||||||
|
)
|
||||||
|
|
||||||
// FloorShadowRecord represents a floor or shadow record in a DS1 file.
|
// FloorShadowRecord represents a floor or shadow record in a DS1 file.
|
||||||
type FloorShadowRecord struct {
|
type FloorShadowRecord struct {
|
||||||
Prop1 byte
|
Prop1 byte
|
||||||
@ -7,8 +37,33 @@ type FloorShadowRecord struct {
|
|||||||
Unknown1 byte
|
Unknown1 byte
|
||||||
Style byte
|
Style byte
|
||||||
Unknown2 byte
|
Unknown2 byte
|
||||||
Hidden bool
|
hidden byte
|
||||||
RandomIndex byte
|
RandomIndex byte
|
||||||
Animated bool
|
Animated bool
|
||||||
YAdjust int
|
YAdjust int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hidden returns if floor/shadow is hidden
|
||||||
|
func (f *FloorShadowRecord) Hidden() bool {
|
||||||
|
return f.hidden > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode decodes floor-shadow record
|
||||||
|
func (f *FloorShadowRecord) Decode(dw uint32) {
|
||||||
|
f.Prop1 = byte((dw & prop1Bitmask) >> prop1Offset)
|
||||||
|
f.Sequence = byte((dw & sequenceBitmask) >> sequenceOffset)
|
||||||
|
f.Unknown1 = byte((dw & unknown1Bitmask) >> unknown1Offset)
|
||||||
|
f.Style = byte((dw & styleBitmask) >> styleOffset)
|
||||||
|
f.Unknown2 = byte((dw & unknown2Bitmask) >> unknown2Offset)
|
||||||
|
f.hidden = byte((dw & hiddenBitmask) >> hiddenOffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode adds Floor's bits to stream writter given
|
||||||
|
func (f *FloorShadowRecord) Encode(sw *d2datautils.StreamWriter) {
|
||||||
|
sw.PushBits32(uint32(f.Prop1), prop1Length)
|
||||||
|
sw.PushBits32(uint32(f.Sequence), sequenceLength)
|
||||||
|
sw.PushBits32(uint32(f.Unknown1), unknown1Length)
|
||||||
|
sw.PushBits32(uint32(f.Style), styleLength)
|
||||||
|
sw.PushBits32(uint32(f.Unknown2), unknown2Length)
|
||||||
|
sw.PushBits32(uint32(f.hidden), hiddenLength)
|
||||||
|
}
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
package d2ds1
|
package d2ds1
|
||||||
|
|
||||||
import "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
import (
|
||||||
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2datautils"
|
||||||
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||||
|
)
|
||||||
|
|
||||||
// WallRecord represents a wall record.
|
// WallRecord represents a wall record.
|
||||||
type WallRecord struct {
|
type WallRecord struct {
|
||||||
@ -11,7 +14,32 @@ type WallRecord struct {
|
|||||||
Unknown1 byte
|
Unknown1 byte
|
||||||
Style byte
|
Style byte
|
||||||
Unknown2 byte
|
Unknown2 byte
|
||||||
Hidden bool
|
hidden byte
|
||||||
RandomIndex byte
|
RandomIndex byte
|
||||||
YAdjust int
|
YAdjust int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hidden returns if wall is hidden
|
||||||
|
func (w *WallRecord) Hidden() bool {
|
||||||
|
return w.hidden > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode decodes wall record
|
||||||
|
func (w *WallRecord) Decode(dw uint32) {
|
||||||
|
w.Prop1 = byte((dw & prop1Bitmask) >> prop1Offset)
|
||||||
|
w.Sequence = byte((dw & sequenceBitmask) >> sequenceOffset)
|
||||||
|
w.Unknown1 = byte((dw & unknown1Bitmask) >> unknown1Offset)
|
||||||
|
w.Style = byte((dw & styleBitmask) >> styleOffset)
|
||||||
|
w.Unknown2 = byte((dw & unknown2Bitmask) >> unknown2Offset)
|
||||||
|
w.hidden = byte((dw & hiddenBitmask) >> hiddenOffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode adds wall's record's bytes into stream writer given
|
||||||
|
func (w *WallRecord) Encode(sw *d2datautils.StreamWriter) {
|
||||||
|
sw.PushBits32(uint32(w.Prop1), prop1Length)
|
||||||
|
sw.PushBits32(uint32(w.Sequence), sequenceLength)
|
||||||
|
sw.PushBits32(uint32(w.Unknown1), unknown1Length)
|
||||||
|
sw.PushBits32(uint32(w.Style), styleLength)
|
||||||
|
sw.PushBits32(uint32(w.Unknown2), unknown2Length)
|
||||||
|
sw.PushBits32(uint32(w.hidden), hiddenLength)
|
||||||
|
}
|
||||||
|
@ -359,19 +359,19 @@ func (mr *MapRenderer) renderPass4(target d2interface.Surface, startX, startY, e
|
|||||||
|
|
||||||
func (mr *MapRenderer) renderTilePass1(tile *d2mapengine.MapTile, target d2interface.Surface) {
|
func (mr *MapRenderer) renderTilePass1(tile *d2mapengine.MapTile, target d2interface.Surface) {
|
||||||
for _, wall := range tile.Components.Walls {
|
for _, wall := range tile.Components.Walls {
|
||||||
if !wall.Hidden && wall.Prop1 != 0 && wall.Type.LowerWall() {
|
if !wall.Hidden() && wall.Prop1 != 0 && wall.Type.LowerWall() {
|
||||||
mr.renderWall(wall, mr.viewport, target)
|
mr.renderWall(wall, mr.viewport, target)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, floor := range tile.Components.Floors {
|
for _, floor := range tile.Components.Floors {
|
||||||
if !floor.Hidden && floor.Prop1 != 0 {
|
if !floor.Hidden() && floor.Prop1 != 0 {
|
||||||
mr.renderFloor(floor, target)
|
mr.renderFloor(floor, target)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, shadow := range tile.Components.Shadows {
|
for _, shadow := range tile.Components.Shadows {
|
||||||
if !shadow.Hidden && shadow.Prop1 != 0 {
|
if !shadow.Hidden() && shadow.Prop1 != 0 {
|
||||||
mr.renderShadow(shadow, target)
|
mr.renderShadow(shadow, target)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -379,7 +379,7 @@ func (mr *MapRenderer) renderTilePass1(tile *d2mapengine.MapTile, target d2inter
|
|||||||
|
|
||||||
func (mr *MapRenderer) renderTilePass2(tile *d2mapengine.MapTile, target d2interface.Surface) {
|
func (mr *MapRenderer) renderTilePass2(tile *d2mapengine.MapTile, target d2interface.Surface) {
|
||||||
for _, wall := range tile.Components.Walls {
|
for _, wall := range tile.Components.Walls {
|
||||||
if !wall.Hidden && wall.Type.UpperWall() {
|
if !wall.Hidden() && wall.Type.UpperWall() {
|
||||||
mr.renderWall(wall, mr.viewport, target)
|
mr.renderWall(wall, mr.viewport, target)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,19 +34,19 @@ func (mr *MapRenderer) generateTileCache() {
|
|||||||
tile := &tiles[idx]
|
tile := &tiles[idx]
|
||||||
|
|
||||||
for i := range tile.Components.Floors {
|
for i := range tile.Components.Floors {
|
||||||
if !tile.Components.Floors[i].Hidden && tile.Components.Floors[i].Prop1 != 0 {
|
if !tile.Components.Floors[i].Hidden() && tile.Components.Floors[i].Prop1 != 0 {
|
||||||
mr.generateFloorCache(&tile.Components.Floors[i])
|
mr.generateFloorCache(&tile.Components.Floors[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range tile.Components.Shadows {
|
for i := range tile.Components.Shadows {
|
||||||
if !tile.Components.Shadows[i].Hidden && tile.Components.Shadows[i].Prop1 != 0 {
|
if !tile.Components.Shadows[i].Hidden() && tile.Components.Shadows[i].Prop1 != 0 {
|
||||||
mr.generateShadowCache(&tile.Components.Shadows[i])
|
mr.generateShadowCache(&tile.Components.Shadows[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range tile.Components.Walls {
|
for i := range tile.Components.Walls {
|
||||||
if !tile.Components.Walls[i].Hidden && tile.Components.Walls[i].Prop1 != 0 {
|
if !tile.Components.Walls[i].Hidden() && tile.Components.Walls[i].Prop1 != 0 {
|
||||||
mr.generateWallCache(&tile.Components.Walls[i])
|
mr.generateWallCache(&tile.Components.Walls[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user