2020-09-08 15:58:35 -04:00
|
|
|
package d2tbl
|
2020-01-26 00:39:13 -05:00
|
|
|
|
|
|
|
import (
|
2021-01-11 04:12:46 -05:00
|
|
|
"errors"
|
2020-01-26 00:39:13 -05:00
|
|
|
"strconv"
|
2020-09-11 19:24:33 -04:00
|
|
|
|
2020-09-08 15:58:35 -04:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2datautils"
|
2020-01-26 00:39:13 -05:00
|
|
|
)
|
|
|
|
|
2020-09-14 14:47:11 -04:00
|
|
|
// TextDictionary is a string map
|
|
|
|
type TextDictionary map[string]string
|
|
|
|
|
2021-01-11 20:23:43 -05:00
|
|
|
func (td TextDictionary) loadHashEntries(hashEntries []*textDictionaryHashEntry, br *d2datautils.StreamReader) error {
|
|
|
|
for i := 0; i < len(hashEntries); i++ {
|
|
|
|
entry := textDictionaryHashEntry{}
|
|
|
|
|
|
|
|
active, err := br.ReadByte()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
entry.IsActive = active > 0
|
|
|
|
|
|
|
|
entry.Index, err = br.ReadUInt16()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
entry.HashValue, err = br.ReadUInt32()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
entry.IndexString, err = br.ReadUInt32()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
entry.NameString, err = br.ReadUInt32()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
entry.NameLength, err = br.ReadUInt16()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
hashEntries[i] = &entry
|
|
|
|
}
|
|
|
|
|
|
|
|
for idx := range hashEntries {
|
|
|
|
if !hashEntries[idx].IsActive {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := td.loadHashEntry(idx, hashEntries[idx], br); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
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))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
value := string(nameVal)
|
|
|
|
|
|
|
|
br.SetPosition(uint64(hashEntry.IndexString))
|
|
|
|
|
|
|
|
key := ""
|
|
|
|
|
|
|
|
for {
|
|
|
|
b, err := br.ReadByte()
|
|
|
|
if b == 0 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
key += string(b)
|
|
|
|
}
|
|
|
|
|
|
|
|
if key == "x" || key == "X" {
|
|
|
|
key = "#" + strconv.Itoa(idx)
|
|
|
|
}
|
|
|
|
|
|
|
|
_, exists := td[key]
|
|
|
|
if !exists {
|
|
|
|
td[key] = value
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-01-26 00:39:13 -05:00
|
|
|
type textDictionaryHashEntry struct {
|
|
|
|
IsActive bool
|
|
|
|
Index uint16
|
|
|
|
HashValue uint32
|
|
|
|
IndexString uint32
|
|
|
|
NameString uint32
|
|
|
|
NameLength uint16
|
|
|
|
}
|
|
|
|
|
2020-07-18 18:07:38 -04:00
|
|
|
const (
|
|
|
|
crcByteCount = 2
|
|
|
|
)
|
|
|
|
|
2020-07-08 09:16:56 -04:00
|
|
|
// LoadTextDictionary loads the text dictionary from the given data
|
2021-01-11 04:12:46 -05:00
|
|
|
func LoadTextDictionary(dictionaryData []byte) (TextDictionary, error) {
|
2020-11-03 14:10:11 -05:00
|
|
|
lookupTable := make(TextDictionary)
|
2020-07-08 09:16:56 -04:00
|
|
|
|
2020-09-08 15:58:35 -04:00
|
|
|
br := d2datautils.CreateStreamReader(dictionaryData)
|
2020-07-18 18:07:38 -04:00
|
|
|
|
|
|
|
// skip past the CRC
|
2021-01-11 20:23:43 -05:00
|
|
|
_, _ = br.ReadBytes(crcByteCount)
|
2020-07-18 18:07:38 -04:00
|
|
|
|
2021-01-11 20:23:43 -05:00
|
|
|
var err error
|
2020-07-18 18:07:38 -04:00
|
|
|
|
2021-01-11 20:23:43 -05:00
|
|
|
numberOfElements, err := br.ReadUInt16()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2020-01-26 00:39:13 -05:00
|
|
|
}
|
2020-07-08 09:16:56 -04:00
|
|
|
|
2021-01-11 20:23:43 -05:00
|
|
|
hashTableSize, err := br.ReadUInt32()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2020-01-26 00:39:13 -05:00
|
|
|
}
|
|
|
|
|
2021-01-11 20:23:43 -05:00
|
|
|
// Version (always 0)
|
|
|
|
if _, err = br.ReadByte(); err != nil {
|
|
|
|
return nil, errors.New("error reading Version record")
|
2020-01-26 00:39:13 -05:00
|
|
|
}
|
|
|
|
|
2021-01-11 20:23:43 -05:00
|
|
|
_, _ = br.ReadUInt32() // StringOffset
|
2020-07-08 09:16:56 -04:00
|
|
|
|
2021-01-11 20:23:43 -05:00
|
|
|
// 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.
|
|
|
|
_, _ = br.ReadUInt32()
|
2020-07-08 09:16:56 -04:00
|
|
|
|
2021-01-11 20:23:43 -05:00
|
|
|
_, _ = br.ReadUInt32() // FileSize
|
2020-07-08 09:16:56 -04:00
|
|
|
|
2021-01-11 20:23:43 -05:00
|
|
|
elementIndex := make([]uint16, numberOfElements)
|
|
|
|
for i := 0; i < int(numberOfElements); i++ {
|
|
|
|
elementIndex[i], err = br.ReadUInt16()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2020-01-26 00:39:13 -05:00
|
|
|
}
|
2021-01-11 20:23:43 -05:00
|
|
|
}
|
2020-07-08 09:16:56 -04:00
|
|
|
|
2021-01-11 20:23:43 -05:00
|
|
|
hashEntries := make([]*textDictionaryHashEntry, hashTableSize)
|
2020-07-08 09:16:56 -04:00
|
|
|
|
2021-01-11 20:23:43 -05:00
|
|
|
err = lookupTable.loadHashEntries(hashEntries, br)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2020-01-26 00:39:13 -05:00
|
|
|
}
|
2020-09-11 19:24:33 -04:00
|
|
|
|
2021-01-11 04:12:46 -05:00
|
|
|
return lookupTable, nil
|
2020-01-26 00:39:13 -05:00
|
|
|
}
|