1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2024-06-20 06:05:23 +00:00

lint fixes for the d2common package (#558)

This commit is contained in:
Gürkan Kaymak 2020-07-08 16:16:56 +03:00 committed by GitHub
parent db5e844aac
commit d1f499fb79
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 177 additions and 89 deletions

View File

@ -1,10 +1,12 @@
package d2common
// Point represents a point
type Point struct {
X int
Y int
}
// Pointf represents a point with float coordinates
type Pointf struct {
X float64
Y float64

View File

@ -18,6 +18,7 @@ func CreateBitStream(newData []byte) *BitStream {
current: 0,
bitCount: 0,
}
return result
}
@ -26,11 +27,14 @@ func (v *BitStream) ReadBits(bitCount int) int {
if bitCount > 16 {
log.Panic("Maximum BitCount is 16")
}
if !v.EnsureBits(bitCount) {
return -1
}
result := v.current & (0xffff >> uint(16-bitCount))
v.WasteBits(bitCount)
return result
}
@ -39,6 +43,7 @@ func (v *BitStream) PeekByte() int {
if !v.EnsureBits(8) {
return -1
}
return v.current & 0xff
}
@ -47,19 +52,22 @@ func (v *BitStream) EnsureBits(bitCount int) bool {
if bitCount <= v.bitCount {
return true
}
if v.dataPosition >= len(v.data) {
return false
}
nextValue := v.data[v.dataPosition]
v.dataPosition++
v.current |= int(nextValue) << uint(v.bitCount)
v.bitCount += 8
return true
}
// WasteBits dry-reads the specified number of bits
func (v *BitStream) WasteBits(bitCount int) {
//noinspection GoRedundantConversion
// noinspection GoRedundantConversion
v.current >>= uint(bitCount)
v.bitCount -= bitCount
}

View File

@ -8,11 +8,13 @@ func TestBitStreamBits(t *testing.T) {
data := []byte{0xAA}
bitStream := CreateBitStream(data)
shouldBeOne := 0
for i := 0; i < 8; i++ {
bit := bitStream.ReadBits(1)
if bit != shouldBeOne {
t.Fatalf("Expected %d but got %d on iteration %d", shouldBeOne, bit, i)
}
if shouldBeOne == 1 {
shouldBeOne = 0
} else {
@ -24,6 +26,7 @@ func TestBitStreamBits(t *testing.T) {
func TestBitStreamBytes(t *testing.T) {
data := []byte{0xAA, 0xBB, 0xCC, 0xDD, 0x12, 0x34, 0x56, 0x78}
bitStream := CreateBitStream(data)
for i := 0; i < 8; i++ {
b := byte(bitStream.ReadBits(8))
if b != data[i] {

View File

@ -2,9 +2,10 @@ package d2common
import (
"errors"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
"log"
"sync"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
)
type cacheNode struct {
@ -109,6 +110,7 @@ func (c *Cache) Retrieve(key string) (interface{}, bool) {
if node.next != nil {
node.next.prev = node.prev
}
if node.prev != nil {
node.prev.next = node.next
}

View File

@ -1,9 +1,8 @@
package d2common
// a calcstring is a type of string often used in datafiles to specify
// CalcString is a type of string often used in datafiles to specify
// a value that is calculated dynamically based on the stats of the relevant
// source, for instance a missile might have a movement speed of lvl*2
type CalcString string
// todo: the logic for parsing these should exist here

View File

@ -27,7 +27,7 @@ func LoadAnimationData(rawData []byte) {
AnimationData = make(map[string][]*AnimationDataRecord)
streamReader := d2common.CreateStreamReader(rawData)
for !streamReader.Eof() {
for !streamReader.EOF() {
dataCount := int(streamReader.GetInt32())
for i := 0; i < dataCount; i++ {
cofNameBytes := streamReader.ReadBytes(8)

View File

@ -41,10 +41,11 @@ func LoadDataDictionary(buf []byte) *DataDictionary {
}
// Next reads the next row, skips Expansion lines or
// returns false when the end of a file is reached or an error occured
// returns false when the end of a file is reached or an error occurred
func (d *DataDictionary) Next() bool {
var err error
d.record, err = d.r.Read()
if err == io.EOF {
return false
} else if err != nil {
@ -55,6 +56,7 @@ func (d *DataDictionary) Next() bool {
if d.record[0] == "Expansion" {
return d.Next()
}
return true
}
@ -69,6 +71,7 @@ func (d *DataDictionary) Number(field string) int {
if err != nil {
return 0
}
return n
}
@ -84,5 +87,6 @@ func (d *DataDictionary) Bool(field string) bool {
if n > 1 {
log.Panic("Bool on non-bool field")
}
return n == 1
}

View File

@ -4,17 +4,21 @@ import (
"math"
)
// MinInt returns the minimum of the given values
func MinInt(a, b int) int {
if a < b {
return a
}
return b
}
// MaxInt returns the maximum of the given values
func MaxInt(a, b int) int {
if a > b {
return a
}
return b
}
@ -23,6 +27,7 @@ func Min(a, b uint32) uint32 {
if a < b {
return a
}
return b
}
@ -31,6 +36,7 @@ func Max(a, b uint32) uint32 {
if a > b {
return a
}
return b
}
@ -39,13 +45,16 @@ func MaxInt32(a, b int32) int32 {
if a > b {
return a
}
return b
}
// AbsInt32 returns the absolute of the given int32
func AbsInt32(a int32) int32 {
if a < 0 {
return -a
}
return a
}
@ -54,6 +63,7 @@ func MinInt32(a, b int32) int32 {
if a < b {
return a
}
return b
}
@ -70,12 +80,15 @@ func GetAngleBetween(p1X, p1Y, p2X, p2Y float64) int {
result := math.Atan2(deltaY, deltaX) * (180 / math.Pi)
iResult := int(result)
for iResult < 0 {
iResult += 360
}
for iResult >= 360 {
iResult -= 360
}
return iResult
}
@ -92,17 +105,18 @@ func AlmostEqual(a, b, threshold float64) bool {
return math.Abs(a-b) <= threshold
}
// Return the new adjusted value, as well as any remaining amount after the max
// AdjustWithRemainder returns the new adjusted value, as well as any remaining amount after the max
func AdjustWithRemainder(sourceValue, adjustment, targetvalue float64) (newValue, remainder float64) {
if adjustment == 0 || math.Abs(adjustment) < 0.000001 {
return sourceValue, 0
}
adjustNegative := adjustment < 0.0
maxNegative := targetvalue-sourceValue < 0.0
if adjustNegative != maxNegative {
// FIXME: This shouldn't happen but it happens all the time..
return sourceValue, 0
//panic("Cannot move towards the opposite direction...")
}
finalValue := sourceValue + adjustment
@ -111,11 +125,13 @@ func AdjustWithRemainder(sourceValue, adjustment, targetvalue float64) (newValue
diff := finalValue - targetvalue
return targetvalue, diff
}
return finalValue, 0
}
if finalValue < targetvalue {
return targetvalue, finalValue - targetvalue
}
return finalValue, 0
}

View File

@ -2,51 +2,56 @@ package d2common
import "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
// MusicDef stores the music definitions of a region
type MusicDef struct {
Region d2enum.RegionIdType
InTown bool
MusicFile string
}
var musicDefs = [...]MusicDef{
{d2enum.RegionAct1Town, false, "/data/global/music/Act1/town1.wav"},
{d2enum.RegionAct1Wilderness, false, "/data/global/music/Act1/wild.wav"},
{d2enum.RegionAct1Cave, false, "/data/global/music/Act1/caves.wav"},
{d2enum.RegionAct1Crypt, false, "/data/global/music/Act1/crypt.wav"},
{d2enum.RegionAct1Monestary, false, "/data/global/music/Act1/monastery.wav"},
{d2enum.RegionAct1Courtyard, false, "/data/global/music/Act1/wild.wav"}, // ?
{d2enum.RegionAct1Barracks, false, "/data/global/music/Act1/wild.wav"}, // ?
{d2enum.RegionAct1Jail, false, "/data/global/music/Act1/wild.wav"}, // ?
{d2enum.RegionAct1Cathedral, false, "/data/global/music/Act1/monastery.wav"}, // ?
{d2enum.RegionAct1Catacombs, false, "/data/global/music/Act1/crypt.wav"}, // ?
{d2enum.RegionAct1Tristram, false, "/data/global/music/Act1/tristram.wav"}, // ?
{d2enum.RegionAct2Town, false, "/data/global/music/Act2/town2.wav"},
{d2enum.RegionAct2Sewer, false, "/data/global/music/Act2/sewer.wav"},
{d2enum.RegionAct2Harem, false, "/data/global/music/Act2/harem.wav"},
{d2enum.RegionAct2Basement, false, "/data/global/music/Act2/lair.wav"}, // ?
{d2enum.RegionAct2Desert, false, "/data/global/music/Act2/desrt.wav"},
{d2enum.RegionAct2Tomb, false, "/data/global/music/Act2/tombs.wav"},
{d2enum.RegionAct2Lair, false, "/data/global/music/Act2/lair.wav"},
{d2enum.RegionAct2Arcane, false, "/data/global/music/Act2/sanctuary.wav"}, // ?
{d2enum.RegionAct3Town, false, "/data/global/music/Act3/town3.wav"},
{d2enum.RegionAct3Jungle, false, "/data/global/music/Act3/jungle.wav"},
{d2enum.RegionAct3Kurast, false, "/data/global/music/Act3/kurast.wav"},
{d2enum.RegionAct3Spider, false, "/data/global/music/Act3/spider.wav"},
{d2enum.RegionAct3Dungeon, false, "/data/global/music/Act3/kurastsewer.wav"}, // ?
{d2enum.RegionAct3Sewer, false, "/data/global/music/Act3/kurastsewer.wav"},
{d2enum.RegionAct4Town, false, "/data/global/music/Act4/town4.wav"},
{d2enum.RegionAct4Mesa, false, "/data/global/music/Act4/mesa.wav"},
{d2enum.RegionAct4Lava, false, "/data/global/music/Act4/diablo.wav"}, // ?
{d2enum.RegonAct5Town, false, "/data/global/music/Act5/xtown.wav"},
{d2enum.RegionAct5Siege, false, "/data/global/music/Act5/siege.wav"},
{d2enum.RegionAct5Barricade, false, "/data/global/music/Act5/shenkmusic.wav"}, // ?
{d2enum.RegionAct5Temple, false, "/data/global/music/Act5/xtemple.wav"},
{d2enum.RegionAct5IceCaves, false, "/data/global/music/Act5/icecaves.wav"},
{d2enum.RegionAct5Baal, false, "/data/global/music/Act5/baal.wav"},
{d2enum.RegionAct5Lava, false, "/data/global/music/Act5/nihlathakmusic.wav"}, // ?
func getMusicDefs() []MusicDef {
return []MusicDef{
{d2enum.RegionAct1Town, false, "/data/global/music/Act1/town1.wav"},
{d2enum.RegionAct1Wilderness, false, "/data/global/music/Act1/wild.wav"},
{d2enum.RegionAct1Cave, false, "/data/global/music/Act1/caves.wav"},
{d2enum.RegionAct1Crypt, false, "/data/global/music/Act1/crypt.wav"},
{d2enum.RegionAct1Monestary, false, "/data/global/music/Act1/monastery.wav"},
{d2enum.RegionAct1Courtyard, false, "/data/global/music/Act1/wild.wav"}, // ?
{d2enum.RegionAct1Barracks, false, "/data/global/music/Act1/wild.wav"}, // ?
{d2enum.RegionAct1Jail, false, "/data/global/music/Act1/wild.wav"}, // ?
{d2enum.RegionAct1Cathedral, false, "/data/global/music/Act1/monastery.wav"}, // ?
{d2enum.RegionAct1Catacombs, false, "/data/global/music/Act1/crypt.wav"}, // ?
{d2enum.RegionAct1Tristram, false, "/data/global/music/Act1/tristram.wav"}, // ?
{d2enum.RegionAct2Town, false, "/data/global/music/Act2/town2.wav"},
{d2enum.RegionAct2Sewer, false, "/data/global/music/Act2/sewer.wav"},
{d2enum.RegionAct2Harem, false, "/data/global/music/Act2/harem.wav"},
{d2enum.RegionAct2Basement, false, "/data/global/music/Act2/lair.wav"}, // ?
{d2enum.RegionAct2Desert, false, "/data/global/music/Act2/desrt.wav"},
{d2enum.RegionAct2Tomb, false, "/data/global/music/Act2/tombs.wav"},
{d2enum.RegionAct2Lair, false, "/data/global/music/Act2/lair.wav"},
{d2enum.RegionAct2Arcane, false, "/data/global/music/Act2/sanctuary.wav"}, // ?
{d2enum.RegionAct3Town, false, "/data/global/music/Act3/town3.wav"},
{d2enum.RegionAct3Jungle, false, "/data/global/music/Act3/jungle.wav"},
{d2enum.RegionAct3Kurast, false, "/data/global/music/Act3/kurast.wav"},
{d2enum.RegionAct3Spider, false, "/data/global/music/Act3/spider.wav"},
{d2enum.RegionAct3Dungeon, false, "/data/global/music/Act3/kurastsewer.wav"}, // ?
{d2enum.RegionAct3Sewer, false, "/data/global/music/Act3/kurastsewer.wav"},
{d2enum.RegionAct4Town, false, "/data/global/music/Act4/town4.wav"},
{d2enum.RegionAct4Mesa, false, "/data/global/music/Act4/mesa.wav"},
{d2enum.RegionAct4Lava, false, "/data/global/music/Act4/diablo.wav"}, // ?
{d2enum.RegonAct5Town, false, "/data/global/music/Act5/xtown.wav"},
{d2enum.RegionAct5Siege, false, "/data/global/music/Act5/siege.wav"},
{d2enum.RegionAct5Barricade, false, "/data/global/music/Act5/shenkmusic.wav"}, // ?
{d2enum.RegionAct5Temple, false, "/data/global/music/Act5/xtemple.wav"},
{d2enum.RegionAct5IceCaves, false, "/data/global/music/Act5/icecaves.wav"},
{d2enum.RegionAct5Baal, false, "/data/global/music/Act5/baal.wav"},
{d2enum.RegionAct5Lava, false, "/data/global/music/Act5/nihlathakmusic.wav"}, // ?
}
}
// GetMusicDef returns the MusicDef of the given region
func GetMusicDef(regionType d2enum.RegionIdType) *MusicDef {
musicDefs := getMusicDefs()
for idx := range musicDefs {
if musicDefs[idx].Region != regionType {
continue

View File

@ -1,5 +1,6 @@
package d2common
// Path represents a path
type Path struct {
X int
Y int

View File

@ -2,35 +2,44 @@ package d2common
import "github.com/OpenDiablo2/OpenDiablo2/d2common/d2astar"
// PathTile represents a node in path finding
type PathTile struct {
Walkable bool
Up, Down, Left, Right, UpLeft, UpRight, DownLeft, DownRight *PathTile
X, Y float64
}
// PathNeighbors returns the direct neighboring nodes of this node which can be pathed to
func (t *PathTile) PathNeighbors() []d2astar.Pather {
result := make([]d2astar.Pather, 0, 8)
if t.Up != nil {
result = append(result, t.Up)
}
if t.Right != nil {
result = append(result, t.Right)
}
if t.Down != nil {
result = append(result, t.Down)
}
if t.Left != nil {
result = append(result, t.Left)
}
if t.UpLeft != nil {
result = append(result, t.UpLeft)
}
if t.UpRight != nil {
result = append(result, t.UpRight)
}
if t.DownLeft != nil {
result = append(result, t.DownLeft)
}
if t.DownRight != nil {
result = append(result, t.DownRight)
}
@ -38,20 +47,26 @@ func (t *PathTile) PathNeighbors() []d2astar.Pather {
return result
}
// PathNeighborCost calculates the exact movement cost to neighbor nodes
func (t *PathTile) PathNeighborCost(to d2astar.Pather) float64 {
return 1 // No cost specifics currently...
}
// PathEstimatedCost is a heuristic method for estimating movement costs between non-adjacent nodes
func (t *PathTile) PathEstimatedCost(to d2astar.Pather) float64 {
toT := to.(*PathTile)
absX := toT.X - t.X
if absX < 0 {
absX = -absX
}
absY := toT.Y - t.Y
if absY < 0 {
absY = -absY
}
r := absX + absY
return r

View File

@ -1,5 +1,6 @@
package d2common
// Rectangle represents a rectangle
type Rectangle struct {
Left int
Top int
@ -7,14 +8,17 @@ type Rectangle struct {
Height int
}
// Bottom returns y of the bottom point of the rectangle
func (v *Rectangle) Bottom() int {
return v.Top + v.Height
}
// Right returns x of the right point of the rectangle
func (v *Rectangle) Right() int {
return v.Left + v.Width
}
// IsInRect returns if the given position is in the rectangle or not
func (v *Rectangle) IsInRect(x, y int) bool {
return x >= v.Left && x < v.Left+v.Width && y >= v.Top && y < v.Top+v.Height
}

View File

@ -1,5 +1,6 @@
package d2common
// Size represents a size
type Size struct {
Width, Height int
}

View File

@ -16,6 +16,7 @@ func CreateStreamReader(source []byte) *StreamReader {
data: source,
position: 0,
}
return result
}
@ -33,6 +34,7 @@ func (v *StreamReader) GetSize() uint64 {
func (v *StreamReader) GetByte() byte {
result := v.data[v.position]
v.position++
return result
}
@ -41,6 +43,7 @@ func (v *StreamReader) GetUInt16() uint16 {
result := uint16(v.data[v.position])
result += uint16(v.data[v.position+1]) << 8
v.position += 2
return result
}
@ -48,24 +51,34 @@ func (v *StreamReader) GetUInt16() uint16 {
func (v *StreamReader) GetInt16() int16 {
result := (int16(v.data[v.position+1]) << uint(8)) + int16(v.data[v.position])
v.position += 2
return result
}
// SetPosition sets the stream position with the given position
func (v *StreamReader) SetPosition(newPosition uint64) {
v.position = newPosition
}
// GetUInt32 returns a uint32 dword from the stream
func (v *StreamReader) GetUInt32() uint32 {
result := (uint32(v.data[v.position+3]) << uint(24)) + (uint32(v.data[v.position+2]) << uint(16)) + (uint32(v.data[v.position+1]) << uint(8)) + uint32(v.data[v.position])
result := (uint32(v.data[v.position+3]) << uint(24)) +
(uint32(v.data[v.position+2]) << uint(16)) +
(uint32(v.data[v.position+1]) << uint(8)) +
uint32(v.data[v.position])
v.position += 4
return result
}
// GetInt32 returns an int32 dword from the stream
func (v *StreamReader) GetInt32() int32 {
result := (int32(v.data[v.position+3]) << uint(24)) + (int32(v.data[v.position+2]) << uint(16)) + (int32(v.data[v.position+1]) << uint(8)) + int32(v.data[v.position])
result := (int32(v.data[v.position+3]) << uint(24)) +
(int32(v.data[v.position+2]) << uint(16)) +
(int32(v.data[v.position+1]) << uint(8)) +
int32(v.data[v.position])
v.position += 4
return result
}
@ -80,21 +93,13 @@ func (v *StreamReader) GetUint64() uint64 {
(uint64(v.data[v.position+1]) << uint(8)) +
uint64(v.data[v.position])
v.position += 8
return result
}
// GetInt64 returns a uint64 qword from the stream
func (v *StreamReader) GetInt64() int64 {
result := (uint64(v.data[v.position+7]) << uint(56)) +
(uint64(v.data[v.position+6]) << uint(48)) +
(uint64(v.data[v.position+5]) << uint(40)) +
(uint64(v.data[v.position+4]) << uint(32)) +
(uint64(v.data[v.position+3]) << uint(24)) +
(uint64(v.data[v.position+2]) << uint(16)) +
(uint64(v.data[v.position+1]) << uint(8)) +
uint64(v.data[v.position])
v.position += 8
return int64(result)
return int64(v.GetUint64())
}
// ReadByte implements io.ByteReader
@ -106,9 +111,11 @@ func (v *StreamReader) ReadByte() (byte, error) {
func (v *StreamReader) ReadBytes(count int) []byte {
result := v.data[v.position : v.position+uint64(count)]
v.position += uint64(count)
return result
}
// SkipBytes moves the stream position forward by the given amount
func (v *StreamReader) SkipBytes(count int) {
v.position += uint64(count)
}
@ -116,17 +123,21 @@ func (v *StreamReader) SkipBytes(count int) {
// Read implements io.Reader
func (v *StreamReader) Read(p []byte) (n int, err error) {
streamLength := v.GetSize()
for i := 0; ; i++ {
if v.GetPosition() >= streamLength {
return i, io.EOF
}
if i >= len(p) {
return i, nil
}
p[i] = v.GetByte()
}
}
func (v *StreamReader) Eof() bool {
// EOF returns if the stream position is reached to the end of the data, or not
func (v *StreamReader) EOF() bool {
return v.position >= uint64(len(v.data))
}

View File

@ -7,17 +7,21 @@ import (
func TestStreamReaderByte(t *testing.T) {
data := []byte{0x78, 0x56, 0x34, 0x12}
sr := CreateStreamReader(data)
if sr.GetPosition() != 0 {
t.Fatal("StreamReader.GetPosition() did not start at 0")
}
if ss := sr.GetSize(); ss != 4 {
t.Fatalf("StreamREader.GetSize() was expected to return %d, but returned %d instead", 4, ss)
}
for i := 0; i < len(data); i++ {
ret := sr.GetByte()
if ret != data[i] {
t.Fatalf("StreamReader.GetDword() was expected to return %X, but returned %X instead", data[i], ret)
}
if pos := sr.GetPosition(); pos != uint64(i+1) {
t.Fatalf("StreamReader.GetPosition() should be at %d, but was at %d instead", i, pos)
}
@ -28,16 +32,20 @@ func TestStreamReaderWord(t *testing.T) {
data := []byte{0x78, 0x56, 0x34, 0x12}
sr := CreateStreamReader(data)
ret := sr.GetUInt16()
if ret != 0x5678 {
t.Fatalf("StreamReader.GetDword() was expected to return %X, but returned %X instead", 0x5678, ret)
}
if pos := sr.GetPosition(); pos != 2 {
t.Fatalf("StreamReader.GetPosition() should be at %d, but was at %d instead", 2, pos)
}
ret = sr.GetUInt16()
if ret != 0x1234 {
t.Fatalf("StreamReader.GetDword() was expected to return %X, but returned %X instead", 0x1234, ret)
}
if pos := sr.GetPosition(); pos != 4 {
t.Fatalf("StreamReader.GetPosition() should be at %d, but was at %d instead", 4, pos)
}
@ -47,9 +55,11 @@ func TestStreamReaderDword(t *testing.T) {
data := []byte{0x78, 0x56, 0x34, 0x12}
sr := CreateStreamReader(data)
ret := sr.GetUInt32()
if ret != 0x12345678 {
t.Fatalf("StreamReader.GetDword() was expected to return %X, but returned %X instead", 0x12345678, ret)
}
if pos := sr.GetPosition(); pos != 4 {
t.Fatalf("StreamReader.GetPosition() should be at %d, but was at %d instead", 4, pos)
}

View File

@ -12,6 +12,7 @@ func CreateStreamWriter() *StreamWriter {
result := &StreamWriter{
data: new(bytes.Buffer),
}
return result
}

View File

@ -7,9 +7,11 @@ import (
func TestStreamWriterByte(t *testing.T) {
sr := CreateStreamWriter()
data := []byte{0x12, 0x34, 0x56, 0x78}
for _, d := range data {
sr.PushByte(d)
}
output := sr.GetBytes()
for i, d := range data {
if output[i] != d {
@ -21,8 +23,10 @@ func TestStreamWriterByte(t *testing.T) {
func TestStreamWriterWord(t *testing.T) {
sr := CreateStreamWriter()
data := []byte{0x12, 0x34, 0x56, 0x78}
sr.PushUint16(0x3412)
sr.PushUint16(0x7856)
output := sr.GetBytes()
for i, d := range data {
if output[i] != d {
@ -34,7 +38,9 @@ func TestStreamWriterWord(t *testing.T) {
func TestStreamWriterDword(t *testing.T) {
sr := CreateStreamWriter()
data := []byte{0x12, 0x34, 0x56, 0x78}
sr.PushUint32(0x78563412)
output := sr.GetBytes()
for i, d := range data {
if output[i] != d {

View File

@ -15,14 +15,17 @@ func AsterToEmpty(text string) string {
if strings.HasPrefix(text, "*") {
return ""
}
return text
}
// EmptyToZero converts empty strings to "0" and leaves non-empty strings as is, for use before converting numerical data which equates empty to zero
// EmptyToZero converts empty strings to "0" and leaves non-empty strings as is,
// for use before converting numerical data which equates empty to zero
func EmptyToZero(text string) string {
if text == "" || text == " " {
return "0"
}
return text
}
@ -32,6 +35,7 @@ func StringToInt(text string) int {
if err != nil {
panic(err)
}
return result
}
@ -43,6 +47,7 @@ func StringToUint(text string) uint {
if err != nil {
panic(err)
}
return uint(result)
}
@ -52,9 +57,11 @@ func StringToUint8(text string) uint8 {
if err != nil {
panic(err)
}
if result < 0 || result > 255 {
panic(fmt.Sprintf("value %d out of range of byte", result))
}
return uint8(result)
}
@ -64,16 +71,16 @@ func StringToInt8(text string) int8 {
if err != nil {
panic(err)
}
if result < -128 || result > 122 {
panic(fmt.Sprintf("value %d out of range of a signed byte", result))
}
return int8(result)
}
// StringToFloat64 converts a string to a float64
// Utf16BytesToString converts a utf16 byte array to string
func Utf16BytesToString(b []byte) (string, error) {
if len(b)%2 != 0 {
return "", fmt.Errorf("must have even length byte slice")
}
@ -95,40 +102,43 @@ func Utf16BytesToString(b []byte) (string, error) {
return ret.String(), nil
}
func CombineStrings(input []string) string {
return strings.Join(input, "\n")
}
// SplitIntoLinesWithMaxWidth splits the given string into lines considering the given maxChars
func SplitIntoLinesWithMaxWidth(fullSentence string, maxChars int) []string {
lines := make([]string, 0)
line := ""
totalLength := 0
words := strings.Split(fullSentence, " ")
if len(words[0]) > maxChars {
// mostly happened within CJK characters (no whitespace)
return splitCjkIntoChunks(fullSentence, maxChars)
}
for _, word := range words {
totalLength += 1 + len(word)
if totalLength > maxChars {
totalLength = len(word)
lines = append(lines, line)
line = ""
} else {
line += " "
}
line += word
}
if len(line) > 0 {
lines = append(lines, line)
}
return lines
}
func splitCjkIntoChunks(str string, chars int) []string {
chunks := make([]string, chars/len(str))
i, count := 0, 0
for j, ch := range str {
if ch < unicode.MaxLatin1 {
count++
@ -136,10 +146,12 @@ func splitCjkIntoChunks(str string, chars int) []string {
// assume we're truncating CJK characters
count += 2
}
if count >= chars {
chunks = append(chunks, str[i:j])
i, count = j, 0
}
}
return append(chunks, str[i:])
}

View File

@ -16,6 +16,7 @@ type textDictionaryHashEntry struct {
var lookupTable map[string]string
// TranslateString returns the translation of the given string
func TranslateString(key string) string {
result, ok := lookupTable[key]
if !ok {
@ -23,24 +24,16 @@ func TranslateString(key string) string {
// log.Panicf("Could not find a string for the key '%s'", key)
return key
}
return result
}
func GetDictionaryEntryCount() int {
if lookupTable == nil {
return 0
}
return len(lookupTable)
}
func GetTranslationMap() map[string]string {
return lookupTable
}
// LoadTextDictionary loads the text dictionary from the given data
func LoadTextDictionary(dictionaryData []byte) {
if lookupTable == nil {
lookupTable = make(map[string]string)
}
br := CreateStreamReader(dictionaryData)
// CRC
br.ReadBytes(2)
@ -50,6 +43,7 @@ func LoadTextDictionary(dictionaryData []byte) {
if _, err := br.ReadByte(); err != nil {
log.Fatal("Error reading Version record")
}
br.GetUInt32() // StringOffset
br.GetUInt32() // 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.GetUInt32() // FileSize
@ -75,37 +69,31 @@ func LoadTextDictionary(dictionaryData []byte) {
if !hashEntry.IsActive {
continue
}
br.SetPosition(uint64(hashEntry.NameString))
nameVal := br.ReadBytes(int(hashEntry.NameLength - 1))
value := string(nameVal)
br.SetPosition(uint64(hashEntry.IndexString))
key := ""
for {
b := br.GetByte()
if b == 0 {
break
}
key += string(b)
}
if key == "x" || key == "X" {
key = "#" + strconv.Itoa(idx)
}
_, exists := lookupTable[key]
if !exists {
lookupTable[key] = value
}
// Use the following code to write out the values
/*=
f, err := os.OpenFile(`C:\Users\lunat\Desktop\D2\langdict.txt`,
os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Println(err)
}
defer f.Close()
if _, err := f.WriteString("\n[" + key + "] " + value); err != nil {
log.Println(err)
}
*/
}
}