1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2025-02-20 23:47:16 -05:00

tbl: encoder

This commit is contained in:
M. Sz 2021-02-25 11:58:32 +01:00
parent 2859eae91c
commit 522f749cfc
2 changed files with 116 additions and 12 deletions

View File

@ -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()
}

View File

@ -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")
}
}