From b4cd34e351176bdcec7d5cab0e5966ba4bcd5fa3 Mon Sep 17 00:00:00 2001 From: "M. Sz" Date: Thu, 25 Feb 2021 09:28:11 +0100 Subject: [PATCH 01/14] cof: splited long Marshal method to avoid nolint:funlen --- d2common/d2fileformats/d2cof/cof.go | 70 ++++++++++++++++++----------- 1 file changed, 44 insertions(+), 26 deletions(-) diff --git a/d2common/d2fileformats/d2cof/cof.go b/d2common/d2fileformats/d2cof/cof.go index ceb9ab11..f80809ae 100644 --- a/d2common/d2fileformats/d2cof/cof.go +++ b/d2common/d2fileformats/d2cof/cof.go @@ -80,24 +80,17 @@ type COF struct { } // Unmarshal a byte slice to this COF -// nolint:funlen // no need to change func (c *COF) Unmarshal(fileData []byte) error { - streamReader := d2datautils.CreateStreamReader(fileData) - - var b []byte - var err error - b, err = streamReader.ReadBytes(numHeaderBytes) + streamReader := d2datautils.CreateStreamReader(fileData) + + headerBytes, err := streamReader.ReadBytes(numHeaderBytes) if err != nil { return err } - c.NumberOfLayers = int(b[headerNumLayers]) - c.FramesPerDirection = int(b[headerFramesPerDir]) - c.NumberOfDirections = int(b[headerNumDirs]) - c.unknownHeaderBytes = b[headerNumDirs+1 : headerSpeed] - c.Speed = int(b[headerSpeed]) + c.loadHeader(headerBytes) c.unknownBodyBytes, err = streamReader.ReadBytes(numUnknownBodyBytes) if err != nil { @@ -107,10 +100,44 @@ func (c *COF) Unmarshal(fileData []byte) error { c.CofLayers = make([]CofLayer, c.NumberOfLayers) c.CompositeLayers = make(map[d2enum.CompositeType]int) + err = c.loadCOFLayers(streamReader) + if err != nil { + return err + } + + animationFramesData, err := streamReader.ReadBytes(c.FramesPerDirection) + if err != nil { + return err + } + + c.loadAnimationFrames(animationFramesData) + + priorityLen := c.FramesPerDirection * c.NumberOfDirections * c.NumberOfLayers + c.Priority = make([][][]d2enum.CompositeType, c.NumberOfDirections) + + priorityBytes, err := streamReader.ReadBytes(priorityLen) + if err != nil { + return err + } + + c.loadPriority(priorityBytes) + + return nil +} + +func (c *COF) loadHeader(b []byte) { + c.NumberOfLayers = int(b[headerNumLayers]) + c.FramesPerDirection = int(b[headerFramesPerDir]) + c.NumberOfDirections = int(b[headerNumDirs]) + c.unknownHeaderBytes = b[headerNumDirs+1 : headerSpeed] + c.Speed = int(b[headerSpeed]) +} + +func (c *COF) loadCOFLayers(streamReader *d2datautils.StreamReader) error { for i := 0; i < c.NumberOfLayers; i++ { layer := CofLayer{} - b, err = streamReader.ReadBytes(numLayerBytes) + b, err := streamReader.ReadBytes(numLayerBytes) if err != nil { return err } @@ -128,25 +155,18 @@ func (c *COF) Unmarshal(fileData []byte) error { c.CompositeLayers[layer.Type] = i } - b, err = streamReader.ReadBytes(c.FramesPerDirection) - if err != nil { - return err - } + return nil +} +func (c *COF) loadAnimationFrames(b []byte) { c.AnimationFrames = make([]d2enum.AnimationFrame, c.FramesPerDirection) for i := range b { c.AnimationFrames[i] = d2enum.AnimationFrame(b[i]) } +} - priorityLen := c.FramesPerDirection * c.NumberOfDirections * c.NumberOfLayers - c.Priority = make([][][]d2enum.CompositeType, c.NumberOfDirections) - - priorityBytes, err := streamReader.ReadBytes(priorityLen) - if err != nil { - return err - } - +func (c *COF) loadPriority(priorityBytes []byte) { priorityIndex := 0 for direction := 0; direction < c.NumberOfDirections; direction++ { @@ -159,8 +179,6 @@ func (c *COF) Unmarshal(fileData []byte) error { } } } - - return nil } // Marshal this COF to a byte slice From e7c5efe8e4216dc00068c54658aadbba5b1e4556 Mon Sep 17 00:00:00 2001 From: "M. Sz" Date: Thu, 25 Feb 2021 09:30:59 +0100 Subject: [PATCH 02/14] cof: added SpeedToFPS method --- d2common/d2fileformats/d2cof/helpers.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 d2common/d2fileformats/d2cof/helpers.go diff --git a/d2common/d2fileformats/d2cof/helpers.go b/d2common/d2fileformats/d2cof/helpers.go new file mode 100644 index 00000000..0cc7f73b --- /dev/null +++ b/d2common/d2fileformats/d2cof/helpers.go @@ -0,0 +1,16 @@ +package d2cof + +// SpeedToFPS returns FPS value basing on cof's speed +func (c *COF) SpeedToFPS() float64 { + const ( + baseFPS = 25 + speedDivisor = 256 + ) + + fps := baseFPS * (float64(c.Speed) / speedDivisor) + if fps == 0 { + fps = baseFPS + } + + return fps +} From 78404ed56c12a0c57af9c40ef2150023991a146b Mon Sep 17 00:00:00 2001 From: "M. Sz" Date: Thu, 25 Feb 2021 09:33:47 +0100 Subject: [PATCH 03/14] cof: added Duration method --- d2common/d2fileformats/d2cof/helpers.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/d2common/d2fileformats/d2cof/helpers.go b/d2common/d2fileformats/d2cof/helpers.go index 0cc7f73b..0b3b7fb2 100644 --- a/d2common/d2fileformats/d2cof/helpers.go +++ b/d2common/d2fileformats/d2cof/helpers.go @@ -14,3 +14,14 @@ func (c *COF) SpeedToFPS() float64 { return fps } + +// Duration returns animation's duration +func (c *COF) Duration() float64 { + const ( + milliseconds = 1000 + ) + + frameDelay := milliseconds / c.SpeedToFPS() + + return float64(c.FramesPerDirection) * frameDelay +} From 2859eae91c1dd4e5897fee5e6453c2786a30b83b Mon Sep 17 00:00:00 2001 From: "M. Sz" Date: Thu, 25 Feb 2021 10:02:10 +0100 Subject: [PATCH 04/14] Revert "data encoding: tbl" This reverts commit 5a0571763ed5e5f76550bfb5e0d4be5b810f3f0e. --- .../d2fileformats/d2tbl/text_dictionary.go | 161 ++++-------------- .../d2tbl/text_dictionary_test.go | 11 ++ d2core/d2asset/asset_manager.go | 27 ++- d2core/d2asset/d2asset.go | 2 +- 4 files changed, 54 insertions(+), 147 deletions(-) create mode 100644 d2common/d2fileformats/d2tbl/text_dictionary_test.go diff --git a/d2common/d2fileformats/d2tbl/text_dictionary.go b/d2common/d2fileformats/d2tbl/text_dictionary.go index 8aea6139..b744bfc9 100644 --- a/d2common/d2fileformats/d2tbl/text_dictionary.go +++ b/d2common/d2fileformats/d2tbl/text_dictionary.go @@ -2,36 +2,25 @@ package d2tbl import ( "errors" - "sort" "strconv" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2datautils" ) // TextDictionary is a string map -type TextDictionary struct { - crcBytes []byte - elementIndex []uint16 - hashTableSize uint32 - version byte - stringOffset uint32 - unknown1 uint32 - fileSize uint32 - hashEntries []*textDictionaryHashEntry - Entries map[string]string -} +type TextDictionary map[string]string -func (td *TextDictionary) loadHashEntries(br *d2datautils.StreamReader) error { - var err error - - for i := 0; i < len(td.hashEntries); i++ { +func (td TextDictionary) loadHashEntries(hashEntries []*textDictionaryHashEntry, br *d2datautils.StreamReader) error { + for i := 0; i < len(hashEntries); i++ { entry := textDictionaryHashEntry{} - entry.active, err = br.ReadByte() + active, err := br.ReadByte() if err != nil { return err } + entry.IsActive = active > 0 + entry.Index, err = br.ReadUInt16() if err != nil { return err @@ -57,15 +46,15 @@ func (td *TextDictionary) loadHashEntries(br *d2datautils.StreamReader) error { return err } - td.hashEntries[i] = &entry + hashEntries[i] = &entry } - for idx := range td.hashEntries { - if !td.hashEntries[idx].IsActive() { + for idx := range hashEntries { + if !hashEntries[idx].IsActive { continue } - if err := td.loadHashEntry(idx, td.hashEntries[idx], br); err != nil { + if err := td.loadHashEntry(idx, hashEntries[idx], br); err != nil { return err } } @@ -73,9 +62,7 @@ func (td *TextDictionary) loadHashEntries(br *d2datautils.StreamReader) error { return nil } -func (td *TextDictionary) loadHashEntry(idx int, hashEntry *textDictionaryHashEntry, br *d2datautils.StreamReader) error { - var err error - +func (td TextDictionary) loadHashEntry(idx int, hashEntry *textDictionaryHashEntry, br *d2datautils.StreamReader) error { br.SetPosition(uint64(hashEntry.NameString)) nameVal, err := br.ReadBytes(int(hashEntry.NameLength - 1)) @@ -83,7 +70,7 @@ func (td *TextDictionary) loadHashEntry(idx int, hashEntry *textDictionaryHashEn return err } - hashEntry.name = string(nameVal) + value := string(nameVal) br.SetPosition(uint64(hashEntry.IndexString)) @@ -102,33 +89,25 @@ func (td *TextDictionary) loadHashEntry(idx int, hashEntry *textDictionaryHashEn key += string(b) } - hashEntry.key = key - - if hashEntry.key == "x" || hashEntry.key == "X" { + if key == "x" || key == "X" { key = "#" + strconv.Itoa(idx) } - _, exists := td.Entries[key] + _, exists := td[key] if !exists { - td.Entries[key] = hashEntry.name + td[key] = value } return nil } type textDictionaryHashEntry struct { - name string - key string + IsActive bool + Index uint16 HashValue uint32 IndexString uint32 NameString uint32 - Index uint16 NameLength uint16 - active byte -} - -func (t *textDictionaryHashEntry) IsActive() bool { - return t.active > 0 } const ( @@ -136,54 +115,38 @@ const ( ) // LoadTextDictionary loads the text dictionary from the given data -func LoadTextDictionary(dictionaryData []byte) (*TextDictionary, error) { - var err error - - lookupTable := &TextDictionary{ - Entries: make(map[string]string), - } +func LoadTextDictionary(dictionaryData []byte) (TextDictionary, error) { + lookupTable := make(TextDictionary) br := d2datautils.CreateStreamReader(dictionaryData) // skip past the CRC - lookupTable.crcBytes, err = br.ReadBytes(crcByteCount) - if err != nil { - return nil, err - } + _, _ = br.ReadBytes(crcByteCount) + + var err error numberOfElements, err := br.ReadUInt16() if err != nil { return nil, err } - lookupTable.hashTableSize, err = br.ReadUInt32() + hashTableSize, err := br.ReadUInt32() if err != nil { return nil, err } // Version (always 0) - if lookupTable.version, err = br.ReadByte(); err != nil { + if _, err = br.ReadByte(); err != nil { return nil, errors.New("error reading Version record") } - // StringOffset - lookupTable.stringOffset, err = br.ReadUInt32() - if err != nil { - return nil, errors.New("error reading string offset") - } + _, _ = br.ReadUInt32() // StringOffset // When the number of times you have missed a match with a // hash key equals this value, you give up because it is not there. - lookupTable.unknown1, err = br.ReadUInt32() - if err != nil { - return nil, err - } + _, _ = br.ReadUInt32() - // FileSize - lookupTable.fileSize, err = br.ReadUInt32() - if err != nil { - return nil, err - } + _, _ = br.ReadUInt32() // FileSize elementIndex := make([]uint16, numberOfElements) for i := 0; i < int(numberOfElements); i++ { @@ -193,78 +156,12 @@ func LoadTextDictionary(dictionaryData []byte) (*TextDictionary, error) { } } - lookupTable.elementIndex = elementIndex + hashEntries := make([]*textDictionaryHashEntry, hashTableSize) - lookupTable.hashEntries = make([]*textDictionaryHashEntry, lookupTable.hashTableSize) - - err = lookupTable.loadHashEntries(br) + err = lookupTable.loadHashEntries(hashEntries, br) if err != nil { return nil, err } return lookupTable, nil } - -// Marshal encodes text dictionary back to byte slice -func (td *TextDictionary) Marshal() []byte { - // create stream writter - sw := d2datautils.CreateStreamWriter() - - sw.PushBytes(td.crcBytes...) - sw.PushUint16(uint16(len(td.elementIndex))) - sw.PushUint32(td.hashTableSize) - sw.PushBytes(td.version) - sw.PushUint32(td.stringOffset) - sw.PushUint32(td.unknown1) - - sw.PushUint32(td.fileSize) - - for _, i := range td.elementIndex { - sw.PushUint16(i) - } - - for i := 0; i < len(td.hashEntries); i++ { - sw.PushBytes(td.hashEntries[i].active) - sw.PushUint16(td.hashEntries[i].Index) - sw.PushUint32(td.hashEntries[i].HashValue) - sw.PushUint32(td.hashEntries[i].IndexString) - sw.PushUint32(td.hashEntries[i].NameString) - sw.PushUint16(td.hashEntries[i].NameLength) - } - - // values are table entries data (key & values) - var values map[int]string = make(map[int]string) - // valuesSorted are sorted values - var valuesSorted map[int]string = make(map[int]string) - - // add values key / names to map - for _, i := range td.hashEntries { - values[int(i.IndexString)] = i.key - values[int(i.NameString)] = i.name - } - - // added map keys - keys := make([]int, 0, len(values)) - for k := range values { - keys = append(keys, k) - } - - // sort keys - sort.Ints(keys) - - // create sorted values map - for _, k := range keys { - valuesSorted[k] = values[k] - } - - // add first value (without 0-byte separator) - sw.PushBytes([]byte(valuesSorted[keys[0]])...) - - // adds values to result - for i := 1; i < len(valuesSorted); i++ { - sw.PushBytes([]byte(valuesSorted[keys[i]])...) - sw.PushBytes(0) - } - - return sw.GetBytes() -} diff --git a/d2common/d2fileformats/d2tbl/text_dictionary_test.go b/d2common/d2fileformats/d2tbl/text_dictionary_test.go new file mode 100644 index 00000000..7a450953 --- /dev/null +++ b/d2common/d2fileformats/d2tbl/text_dictionary_test.go @@ -0,0 +1,11 @@ +package d2tbl + +import ( + "testing" +) + +func exampleData() *TextDictionary { + result := &TextDictionary{ + crcBytes: make([]byte, crcByteCount), + } +} diff --git a/d2core/d2asset/asset_manager.go b/d2core/d2asset/asset_manager.go index d3c8cb6f..cba1be27 100644 --- a/d2core/d2asset/asset_manager.go +++ b/d2core/d2asset/asset_manager.go @@ -65,18 +65,17 @@ type AssetManager struct { *d2util.Logger *d2loader.Loader - tables []*d2tbl.TextDictionary - dt1s d2interface.Cache - ds1s d2interface.Cache - cofs d2interface.Cache - dccs d2interface.Cache - animations d2interface.Cache - fonts d2interface.Cache - palettes d2interface.Cache - transforms d2interface.Cache - Records *d2records.RecordManager - language string - languageModifier int + tables []d2tbl.TextDictionary + dt1s d2interface.Cache + ds1s d2interface.Cache + cofs d2interface.Cache + dccs d2interface.Cache + animations d2interface.Cache + fonts d2interface.Cache + palettes d2interface.Cache + transforms d2interface.Cache + Records *d2records.RecordManager + language string } // SetLogLevel sets the log level for the asset manager, record manager, and file loader @@ -268,7 +267,7 @@ func (am *AssetManager) LoadPalette(palettePath string) (d2interface.Palette, er } // LoadStringTable loads a string table from the given path -func (am *AssetManager) LoadStringTable(tablePath string) (*d2tbl.TextDictionary, error) { +func (am *AssetManager) LoadStringTable(tablePath string) (d2tbl.TextDictionary, error) { data, err := am.LoadFile(tablePath) if err != nil { return nil, err @@ -302,7 +301,7 @@ func (am *AssetManager) TranslateString(input interface{}) string { } for idx := range am.tables { - if value, found := am.tables[idx].Entries[key]; found { + if value, found := am.tables[idx][key]; found { return value } } diff --git a/d2core/d2asset/d2asset.go b/d2core/d2asset/d2asset.go index 768a2ac0..ed5260c4 100644 --- a/d2core/d2asset/d2asset.go +++ b/d2core/d2asset/d2asset.go @@ -27,7 +27,7 @@ func NewAssetManager(logLevel d2util.LogLevel) (*AssetManager, error) { manager := &AssetManager{ Logger: logger, Loader: loader, - tables: make([]*d2tbl.TextDictionary, 0), + tables: make([]d2tbl.TextDictionary, 0), animations: d2cache.CreateCache(animationBudget), fonts: d2cache.CreateCache(fontBudget), palettes: d2cache.CreateCache(paletteBudget), From 522f749cfcfeab56a7031f780da362e91c3819f6 Mon Sep 17 00:00:00 2001 From: "M. Sz" Date: Thu, 25 Feb 2021 11:58:32 +0100 Subject: [PATCH 05/14] tbl: encoder --- .../d2fileformats/d2tbl/text_dictionary.go | 106 ++++++++++++++++-- .../d2tbl/text_dictionary_test.go | 22 +++- 2 files changed, 116 insertions(+), 12 deletions(-) diff --git a/d2common/d2fileformats/d2tbl/text_dictionary.go b/d2common/d2fileformats/d2tbl/text_dictionary.go index b744bfc9..3d53b478 100644 --- a/d2common/d2fileformats/d2tbl/text_dictionary.go +++ b/d2common/d2fileformats/d2tbl/text_dictionary.go @@ -1,7 +1,8 @@ package d2tbl import ( - "errors" + "fmt" + "strconv" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2datautils" @@ -16,14 +17,14 @@ func (td TextDictionary) loadHashEntries(hashEntries []*textDictionaryHashEntry, active, err := br.ReadByte() if err != nil { - return err + return fmt.Errorf("reading active: %v", err) } entry.IsActive = active > 0 entry.Index, err = br.ReadUInt16() if err != nil { - return err + return fmt.Errorf("reading Index: %v", err) } entry.HashValue, err = br.ReadUInt32() @@ -55,7 +56,7 @@ func (td TextDictionary) loadHashEntries(hashEntries []*textDictionaryHashEntry, } if err := td.loadHashEntry(idx, hashEntries[idx], br); err != nil { - return err + return fmt.Errorf("loading entry %d: %v", idx, err) } } @@ -67,7 +68,7 @@ func (td TextDictionary) loadHashEntry(idx int, hashEntry *textDictionaryHashEnt nameVal, err := br.ReadBytes(int(hashEntry.NameLength - 1)) if err != nil { - return err + return fmt.Errorf("reading name value: %v", err) } value := string(nameVal) @@ -125,19 +126,32 @@ func LoadTextDictionary(dictionaryData []byte) (TextDictionary, error) { var err error + /* + number of indicates + (https://d2mods.info/forum/viewtopic.php?p=202077#p202077) + Indices ... + An array of WORD. Each entry is an index into the hash table. + The actual string key index in the .bin file is an index into this table. + So to get a string from a key index ... + */ numberOfElements, err := br.ReadUInt16() if err != nil { - return nil, err + return nil, fmt.Errorf("reading number of elements: %v", err) } hashTableSize, err := br.ReadUInt32() if err != nil { - return nil, err + return nil, fmt.Errorf("reading hash table size: %v", err) } // Version (always 0) - if _, err = br.ReadByte(); err != nil { - return nil, errors.New("error reading Version record") + version, err := br.ReadByte() + if err != nil { + return nil, fmt.Errorf("reading version: %v", err) + } + + if version != 0 { + return nil, fmt.Errorf("version isn't equal to 0") } _, _ = br.ReadUInt32() // StringOffset @@ -152,7 +166,7 @@ func LoadTextDictionary(dictionaryData []byte) (TextDictionary, error) { for i := 0; i < int(numberOfElements); i++ { elementIndex[i], err = br.ReadUInt16() if err != nil { - return nil, err + return nil, fmt.Errorf("reading element index %d: %v", i, err) } } @@ -160,8 +174,78 @@ func LoadTextDictionary(dictionaryData []byte) (TextDictionary, error) { err = lookupTable.loadHashEntries(hashEntries, br) if err != nil { - return nil, err + return nil, fmt.Errorf("loading has entries: %v", err) } return lookupTable, nil } + +// Marshal encodes text dictionary back into byte slice +func (td *TextDictionary) Marshal() []byte { + sw := d2datautils.CreateStreamWriter() + + // https://github.com/OpenDiablo2/OpenDiablo2/issues/1043 + sw.PushBytes(0, 0) + + sw.PushUint16(0) + + sw.PushInt32(int32(len(*td))) + + // version (always 0) + sw.PushBytes(0) + + // offset of start of data (unnecessary for our decoder) + sw.PushUint32(0) + + // Max retry count for a hash hit. + sw.PushUint32(0) + + // offset to end of data (noop) + sw.PushUint32(0) + + // indicates (len = 0, so nothing here) + + // nolint:gomnd // 17 comes from the size of one "data-header index" + // dataPos is a position, when we're placing data stream + dataPos := len(sw.GetBytes()) + 17*len(*td) + + for key, value := range *td { + // non-zero if record is used (for us, every record is used ;-) + sw.PushBytes(1) + + // generally unused; + // string key index (used in .bin) + sw.PushUint16(0) + + // also unused in our decoder + // calculated hash of the string. + sw.PushUint32(0) + + sw.PushUint32(uint32(dataPos)) + dataPos += len(key) + 1 + + sw.PushUint32(uint32(dataPos)) + dataPos += len(value) + 1 + + sw.PushUint16(uint16(len(value) + 0)) + } + + // data stream: put all data in appropiate order + for key, value := range *td { + for _, i := range key { + sw.PushBytes(byte(i)) + } + + // 0 as separator + sw.PushBytes(0) + + for _, i := range value { + sw.PushBytes(byte(i)) + } + + // 0 as separator + sw.PushBytes(0) + } + + return sw.GetBytes() +} diff --git a/d2common/d2fileformats/d2tbl/text_dictionary_test.go b/d2common/d2fileformats/d2tbl/text_dictionary_test.go index 7a450953..5f04bcf4 100644 --- a/d2common/d2fileformats/d2tbl/text_dictionary_test.go +++ b/d2common/d2fileformats/d2tbl/text_dictionary_test.go @@ -1,11 +1,31 @@ package d2tbl import ( + "fmt" + "testing" ) func exampleData() *TextDictionary { result := &TextDictionary{ - crcBytes: make([]byte, crcByteCount), + "abc": "def", + "someStr": "Some long string", + "lolstring": "lol", + } + + return result +} + +func TestTBL_Marshal(t *testing.T) { + tbl := exampleData() + data := tbl.Marshal() + newTbl, err := LoadTextDictionary(data) + if err != nil { + t.Error(err) + } + + _, ok := newTbl["lolstring"] + if !ok { + t.Fatal("no string found") } } From 91b32903221f72c892d93aa029a8b2a804aeb168 Mon Sep 17 00:00:00 2001 From: "M. Sz" Date: Thu, 25 Feb 2021 12:03:00 +0100 Subject: [PATCH 06/14] tbl: completed test --- d2common/d2fileformats/d2tbl/text_dictionary.go | 2 +- .../d2fileformats/d2tbl/text_dictionary_test.go | 13 ++++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/d2common/d2fileformats/d2tbl/text_dictionary.go b/d2common/d2fileformats/d2tbl/text_dictionary.go index 3d53b478..257be33e 100644 --- a/d2common/d2fileformats/d2tbl/text_dictionary.go +++ b/d2common/d2fileformats/d2tbl/text_dictionary.go @@ -227,7 +227,7 @@ func (td *TextDictionary) Marshal() []byte { sw.PushUint32(uint32(dataPos)) dataPos += len(value) + 1 - sw.PushUint16(uint16(len(value) + 0)) + sw.PushUint16(uint16(len(value) + 1)) } // data stream: put all data in appropiate order diff --git a/d2common/d2fileformats/d2tbl/text_dictionary_test.go b/d2common/d2fileformats/d2tbl/text_dictionary_test.go index 5f04bcf4..df5012cf 100644 --- a/d2common/d2fileformats/d2tbl/text_dictionary_test.go +++ b/d2common/d2fileformats/d2tbl/text_dictionary_test.go @@ -19,13 +19,20 @@ func exampleData() *TextDictionary { func TestTBL_Marshal(t *testing.T) { tbl := exampleData() data := tbl.Marshal() + newTbl, err := LoadTextDictionary(data) if err != nil { t.Error(err) } - _, ok := newTbl["lolstring"] - if !ok { - t.Fatal("no string found") + for key, value := range *tbl { + newValue, ok := newTbl[key] + fmt.Println(newValue, value) + if !ok { + t.Fatal("string wasn't encoded to table") + } + if newValue != value { + t.Fatal("unexpected value set") + } } } From e163c40107ca316d0fa7178e84190f527e21a90f Mon Sep 17 00:00:00 2001 From: "M. Sz" Date: Thu, 25 Feb 2021 12:06:37 +0100 Subject: [PATCH 07/14] tbl: completed error mesages --- d2common/d2fileformats/d2tbl/text_dictionary.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/d2common/d2fileformats/d2tbl/text_dictionary.go b/d2common/d2fileformats/d2tbl/text_dictionary.go index 257be33e..cb108dd8 100644 --- a/d2common/d2fileformats/d2tbl/text_dictionary.go +++ b/d2common/d2fileformats/d2tbl/text_dictionary.go @@ -29,22 +29,22 @@ func (td TextDictionary) loadHashEntries(hashEntries []*textDictionaryHashEntry, entry.HashValue, err = br.ReadUInt32() if err != nil { - return err + return fmt.Errorf("reading hash value: %v", err) } entry.IndexString, err = br.ReadUInt32() if err != nil { - return err + return fmt.Errorf("reading index string pos: %v", err) } entry.NameString, err = br.ReadUInt32() if err != nil { - return err + return fmt.Errorf("reading name string pos: %v", err) } entry.NameLength, err = br.ReadUInt16() if err != nil { - return err + return fmt.Errorf("reading name length: %v", err) } hashEntries[i] = &entry @@ -84,7 +84,7 @@ func (td TextDictionary) loadHashEntry(idx int, hashEntry *textDictionaryHashEnt } if err != nil { - return err + return fmt.Errorf("reading kay char: %v", err) } key += string(b) From 3b8cdffe151c9b5965fd0141f7a88906665055ba Mon Sep 17 00:00:00 2001 From: "M. Sz" Date: Thu, 25 Feb 2021 12:08:02 +0100 Subject: [PATCH 08/14] tbl: lintfix --- d2common/d2fileformats/d2tbl/text_dictionary.go | 2 +- d2common/d2fileformats/d2tbl/text_dictionary_test.go | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/d2common/d2fileformats/d2tbl/text_dictionary.go b/d2common/d2fileformats/d2tbl/text_dictionary.go index cb108dd8..4413d5c2 100644 --- a/d2common/d2fileformats/d2tbl/text_dictionary.go +++ b/d2common/d2fileformats/d2tbl/text_dictionary.go @@ -230,7 +230,7 @@ func (td *TextDictionary) Marshal() []byte { sw.PushUint16(uint16(len(value) + 1)) } - // data stream: put all data in appropiate order + // data stream: put all data in appropriate order for key, value := range *td { for _, i := range key { sw.PushBytes(byte(i)) diff --git a/d2common/d2fileformats/d2tbl/text_dictionary_test.go b/d2common/d2fileformats/d2tbl/text_dictionary_test.go index df5012cf..bfb0794a 100644 --- a/d2common/d2fileformats/d2tbl/text_dictionary_test.go +++ b/d2common/d2fileformats/d2tbl/text_dictionary_test.go @@ -1,8 +1,6 @@ package d2tbl import ( - "fmt" - "testing" ) @@ -27,10 +25,11 @@ func TestTBL_Marshal(t *testing.T) { for key, value := range *tbl { newValue, ok := newTbl[key] - fmt.Println(newValue, value) + if !ok { t.Fatal("string wasn't encoded to table") } + if newValue != value { t.Fatal("unexpected value set") } From 8d5cf7a26b47e4c28a8fd3adc2ab81f34afd91fd Mon Sep 17 00:00:00 2001 From: "M. Sz" Date: Thu, 25 Feb 2021 13:09:38 +0100 Subject: [PATCH 09/14] tbl: replaced lolstring with teststring in tests --- d2common/d2fileformats/d2tbl/text_dictionary_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/d2common/d2fileformats/d2tbl/text_dictionary_test.go b/d2common/d2fileformats/d2tbl/text_dictionary_test.go index bfb0794a..26188cd5 100644 --- a/d2common/d2fileformats/d2tbl/text_dictionary_test.go +++ b/d2common/d2fileformats/d2tbl/text_dictionary_test.go @@ -6,9 +6,9 @@ import ( func exampleData() *TextDictionary { result := &TextDictionary{ - "abc": "def", - "someStr": "Some long string", - "lolstring": "lol", + "abc": "def", + "someStr": "Some long string", + "teststring": "TeSt", } return result From c933e4b89188b81f666add706fd4c9fd2a8a717c Mon Sep 17 00:00:00 2001 From: "M. Sz" Date: Thu, 25 Feb 2021 14:11:13 +0100 Subject: [PATCH 10/14] fixed build error & tbl: removed version check (because of error screen; we don't need to check version) --- .../d2fileformats/d2tbl/text_dictionary.go | 8 ++----- d2core/d2asset/asset_manager.go | 23 ++++++++++--------- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/d2common/d2fileformats/d2tbl/text_dictionary.go b/d2common/d2fileformats/d2tbl/text_dictionary.go index 4413d5c2..b0b8e51b 100644 --- a/d2common/d2fileformats/d2tbl/text_dictionary.go +++ b/d2common/d2fileformats/d2tbl/text_dictionary.go @@ -144,16 +144,12 @@ func LoadTextDictionary(dictionaryData []byte) (TextDictionary, error) { return nil, fmt.Errorf("reading hash table size: %v", err) } - // Version (always 0) - version, err := br.ReadByte() + // Version + _, err = br.ReadByte() if err != nil { return nil, fmt.Errorf("reading version: %v", err) } - if version != 0 { - return nil, fmt.Errorf("version isn't equal to 0") - } - _, _ = br.ReadUInt32() // StringOffset // When the number of times you have missed a match with a diff --git a/d2core/d2asset/asset_manager.go b/d2core/d2asset/asset_manager.go index cba1be27..a5ac1467 100644 --- a/d2core/d2asset/asset_manager.go +++ b/d2core/d2asset/asset_manager.go @@ -65,17 +65,18 @@ type AssetManager struct { *d2util.Logger *d2loader.Loader - tables []d2tbl.TextDictionary - dt1s d2interface.Cache - ds1s d2interface.Cache - cofs d2interface.Cache - dccs d2interface.Cache - animations d2interface.Cache - fonts d2interface.Cache - palettes d2interface.Cache - transforms d2interface.Cache - Records *d2records.RecordManager - language string + tables []d2tbl.TextDictionary + dt1s d2interface.Cache + ds1s d2interface.Cache + cofs d2interface.Cache + dccs d2interface.Cache + animations d2interface.Cache + fonts d2interface.Cache + palettes d2interface.Cache + transforms d2interface.Cache + Records *d2records.RecordManager + language string + languageModifier int } // SetLogLevel sets the log level for the asset manager, record manager, and file loader From d61d829b98286d01b846400ade4e124cb26a3513 Mon Sep 17 00:00:00 2001 From: "M. Sz" Date: Thu, 25 Feb 2021 19:19:56 +0100 Subject: [PATCH 11/14] pl2 encoder + test --- d2common/d2fileformats/d2pl2/pl2.go | 10 ++++++ d2common/d2fileformats/d2pl2/pl2_test.go | 39 ++++++++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 d2common/d2fileformats/d2pl2/pl2_test.go diff --git a/d2common/d2fileformats/d2pl2/pl2.go b/d2common/d2fileformats/d2pl2/pl2.go index 2023cff0..1dd93832 100644 --- a/d2common/d2fileformats/d2pl2/pl2.go +++ b/d2common/d2fileformats/d2pl2/pl2.go @@ -41,3 +41,13 @@ func Load(data []byte) (*PL2, error) { return result, nil } + +func (p *PL2) Marshal() []byte { + restruct.EnableExprBeta() + data, err := restruct.Pack(binary.LittleEndian, p) + if err != nil { + panic(err) + } + + return data +} diff --git a/d2common/d2fileformats/d2pl2/pl2_test.go b/d2common/d2fileformats/d2pl2/pl2_test.go new file mode 100644 index 00000000..f1e5a2f5 --- /dev/null +++ b/d2common/d2fileformats/d2pl2/pl2_test.go @@ -0,0 +1,39 @@ +package d2pl2 + +import ( + "testing" +) + +func exampleData() *PL2 { + result := &PL2{ + BasePalette: PL2Palette{}, + SelectedUintShift: PL2PaletteTransform{}, + RedTones: PL2PaletteTransform{}, + GreenTones: PL2PaletteTransform{}, + BlueTones: PL2PaletteTransform{}, + DarkendColorShift: PL2PaletteTransform{}, + } + + result.BasePalette.Colors[0].R = 8 + result.DarkendColorShift.Indices[0] = 123 + + return result +} + +func TestPL2_MarshalUnmarshal(t *testing.T) { + pl2 := exampleData() + + data := pl2.Marshal() + + newPL2, err := Load(data) + if err != nil { + t.Error(err) + } + + if newPL2.BasePalette.Colors[0] != pl2.BasePalette.Colors[0] { + t.Fatal("unexpected length") + } + + if pl2.DarkendColorShift.Indices[0] != newPL2.DarkendColorShift.Indices[0] { + } +} From 976d78e5953de88d94094c91cc0c2ae95baf8069 Mon Sep 17 00:00:00 2001 From: "M. Sz" Date: Thu, 25 Feb 2021 19:26:01 +0100 Subject: [PATCH 12/14] pl2: lintfix --- d2common/d2fileformats/d2pl2/pl2.go | 2 ++ d2common/d2fileformats/d2pl2/pl2_test.go | 1 + 2 files changed, 3 insertions(+) diff --git a/d2common/d2fileformats/d2pl2/pl2.go b/d2common/d2fileformats/d2pl2/pl2.go index 1dd93832..79db5c21 100644 --- a/d2common/d2fileformats/d2pl2/pl2.go +++ b/d2common/d2fileformats/d2pl2/pl2.go @@ -42,8 +42,10 @@ func Load(data []byte) (*PL2, error) { return result, nil } +// Marshal encodes PL2 back into byte slice func (p *PL2) Marshal() []byte { restruct.EnableExprBeta() + data, err := restruct.Pack(binary.LittleEndian, p) if err != nil { panic(err) diff --git a/d2common/d2fileformats/d2pl2/pl2_test.go b/d2common/d2fileformats/d2pl2/pl2_test.go index f1e5a2f5..87be05ac 100644 --- a/d2common/d2fileformats/d2pl2/pl2_test.go +++ b/d2common/d2fileformats/d2pl2/pl2_test.go @@ -35,5 +35,6 @@ func TestPL2_MarshalUnmarshal(t *testing.T) { } if pl2.DarkendColorShift.Indices[0] != newPL2.DarkendColorShift.Indices[0] { + t.Fatal("unexpected index set") } } From 15d30ffcce6d8d1e8362e9793bfde27de1c912a1 Mon Sep 17 00:00:00 2001 From: "M. Sz" Date: Thu, 25 Feb 2021 20:21:44 +0100 Subject: [PATCH 13/14] cof: rename SpeedToFPS -> FPS --- d2common/d2fileformats/d2cof/helpers.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/d2common/d2fileformats/d2cof/helpers.go b/d2common/d2fileformats/d2cof/helpers.go index 0b3b7fb2..293719cb 100644 --- a/d2common/d2fileformats/d2cof/helpers.go +++ b/d2common/d2fileformats/d2cof/helpers.go @@ -1,7 +1,7 @@ package d2cof -// SpeedToFPS returns FPS value basing on cof's speed -func (c *COF) SpeedToFPS() float64 { +// FPS returns FPS value basing on cof's speed +func (c *COF) FPS() float64 { const ( baseFPS = 25 speedDivisor = 256 From b5fa6e77eb67917c1ceb97d9a34185b622151038 Mon Sep 17 00:00:00 2001 From: "M. Sz" Date: Thu, 25 Feb 2021 20:25:39 +0100 Subject: [PATCH 14/14] cof: buildfix --- d2common/d2fileformats/d2cof/helpers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2common/d2fileformats/d2cof/helpers.go b/d2common/d2fileformats/d2cof/helpers.go index 293719cb..9e1b9c5d 100644 --- a/d2common/d2fileformats/d2cof/helpers.go +++ b/d2common/d2fileformats/d2cof/helpers.go @@ -21,7 +21,7 @@ func (c *COF) Duration() float64 { milliseconds = 1000 ) - frameDelay := milliseconds / c.SpeedToFPS() + frameDelay := milliseconds / c.FPS() return float64(c.FramesPerDirection) * frameDelay }