diff --git a/d2common/d2fileformats/d2animdata/animdata.go b/d2common/d2fileformats/d2animdata/animdata.go index 2f74e5de..902508bb 100644 --- a/d2common/d2fileformats/d2animdata/animdata.go +++ b/d2common/d2fileformats/d2animdata/animdata.go @@ -147,8 +147,85 @@ func Load(data []byte) (*AnimationData, error) { } if reader.Position() != uint64(len(data)) { - return nil, errors.New("unable to parse animation data") + return nil, fmt.Errorf("unable to parse animation data: %d != %d", reader.Position(), len(data)) } return animdata, nil } + +// Marshal encodes animation data back into byte slice +// basing on AnimationData.records +func (ad *AnimationData) Marshal() []byte { + sw := d2datautils.CreateStreamWriter() + + // keys - all entries in animationData + keys := make([]string, len(ad.entries)) + // we must manually add index + idx := 0 + for i := range ad.entries { + keys[idx] = i + idx++ + } + + // name terminates current name + name := 0 + // recordIdx determinates current record index + recordIdx := 0 + + // numberOfEntries is a number of entries in all map indexes + var numberOfEntries int = 0 + for i := 0; i < len(keys); i++ { + numberOfEntries += len(ad.entries[keys[i]]) + } + + for idx := 0; idx < numBlocks; idx++ { + // number of records (max is maxRecordsPerObject) + l := 0 + // first condition: end up with all this and push 0 to dhe end + if numberOfEntries == 0 { + sw.PushUint32(0) + continue + // second condition - if number of entries left is smaller than + // maxRecordsPerBlock, push... + } else if numberOfEntries < maxRecordsPerBlock { + l = int(numberOfEntries) + sw.PushUint32(uint32(l)) + } else { + // else use maxRecordsPerBlock + l = maxRecordsPerBlock + sw.PushUint32(maxRecordsPerBlock) + } + + for currentRecordIdx := 0; currentRecordIdx < l; currentRecordIdx++ { + numberOfEntries-- + if recordIdx == len(ad.entries[keys[name]]) { + recordIdx = 0 + name++ + } + + animationRecord := ad.entries[keys[name]][recordIdx] + recordIdx++ + + name := animationRecord.name + missingZeroBytes := byteCountName - len(name) + fmt.Println(name) + sw.PushBytes([]byte(name)...) + for i := 0; i < missingZeroBytes; i++ { + sw.PushBytes(0) + } + + sw.PushUint32(animationRecord.framesPerDirection) + sw.PushUint16(animationRecord.speed) + + for i := 0; i < byteCountSpeedPadding; i++ { + sw.PushBytes(0) + } + + for event := 0; event < numEvents; event++ { + sw.PushBytes(byte(animationRecord.events[event])) + } + } + } + + return sw.GetBytes() +} diff --git a/d2common/d2fileformats/d2animdata/animdata_test.go b/d2common/d2fileformats/d2animdata/animdata_test.go index 4d3c0209..38d3068a 100644 --- a/d2common/d2fileformats/d2animdata/animdata_test.go +++ b/d2common/d2fileformats/d2animdata/animdata_test.go @@ -1,11 +1,57 @@ package d2animdata import ( + "fmt" "log" "os" "testing" ) +func exampleData() *AnimationData { + testEntries := []*AnimationDataRecord{ + &AnimationDataRecord{ + "TST", + 5, 8, + map[int]AnimationEvent{ + 1: AnimationEventNone, + }, + }, + &AnimationDataRecord{ + "TST", + 8, 3, + map[int]AnimationEvent{ + 2: AnimationEventNone, + }, + }, + } + + testEntries2 := []*AnimationDataRecord{ + &AnimationDataRecord{ + "TTT", + 7, 8, + map[int]AnimationEvent{ + 1: AnimationEventNone, + }, + }, + &AnimationDataRecord{ + "TTT", + 8, 9, + map[int]AnimationEvent{ + 8: AnimationEventNone, + }, + }, + } + + result := &AnimationData{ + entries: map[string][]*AnimationDataRecord{ + "TST": testEntries, + "TTT": testEntries2, + }, + } + + return result +} + func TestLoad(t *testing.T) { testFile, fileErr := os.Open("testdata/AnimData.d2") if fileErr != nil { @@ -154,3 +200,57 @@ func TestAnimationDataRecord_FPS(t *testing.T) { t.Error("incorrect fps") } } + +func TestAnimationDataRecord_Marshal(t *testing.T) { + file, fileErr := os.Open("testdata/AnimData.d2") + if fileErr != nil { + t.Error("cannot open test data file") + return + } + + data := make([]byte, 0) + buf := make([]byte, 16) + + for { + numRead, err := file.Read(buf) + + data = append(data, buf[:numRead]...) + + if err != nil { + break + } + } + + ad, err := Load(data) + if err != nil { + t.Error(err) + } + newData := ad.Marshal() + newAd, err := Load(newData) + if err != nil { + t.Error(err) + } + + keys1 := make([]string, 0) + for i := range ad.entries { + keys1 = append(keys1, i) + } + keys2 := make([]string, 0) + for i := range newAd.entries { + keys2 = append(keys2, i) + } + + if len(keys1) != len(keys2) { + t.Fatalf("unexpected length of keys in first and second dict: %d, %d", len(keys1), len(keys2)) + } + + fmt.Println(len(ad.entries["TST"])) + for key := range newAd.entries { + for n, i := range newAd.entries[key] { + fmt.Println(i.speed, ad.entries[key][n].speed) + if i.speed != ad.entries[key][n].speed { + t.Fatal("unexpected record set") + } + } + } +}