OpenDiablo2/d2common/d2util/stringutils.go

159 lines
3.2 KiB
Go

package d2util
import (
"bytes"
"fmt"
"strconv"
"strings"
"unicode"
"unicode/utf16"
"unicode/utf8"
)
// AsterToEmpty converts strings beginning with "*" to "", for use when handling columns where an asterix can be used to comment out entries
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
func EmptyToZero(text string) string {
if text == "" || text == " " {
return "0"
}
return text
}
// StringToInt converts a string to an integer
func StringToInt(text string) int {
result, err := strconv.Atoi(text)
if err != nil {
panic(err)
}
return result
}
// SafeStringToInt converts a string to an integer, or returns -1 on falure
// StringToUint converts a string to a uint32
func StringToUint(text string) uint {
result, err := strconv.ParseUint(text, 10, 32)
if err != nil {
panic(err)
}
return uint(result)
}
// StringToUint8 converts a string to an uint8
func StringToUint8(text string) uint8 {
result, err := strconv.Atoi(text)
if err != nil {
panic(err)
}
if result < 0 || result > 255 {
panic(fmt.Sprintf("value %d out of range of byte", result))
}
return uint8(result)
}
// StringToInt8 converts a string to an int8
func StringToInt8(text string) int8 {
result, err := strconv.Atoi(text)
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)
}
// 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")
}
u16s := make([]uint16, 1)
ret := &bytes.Buffer{}
b8buf := make([]byte, 4)
lb := len(b)
for i := 0; i < lb; i += 2 {
// nolint:gomnd // byte operation
u16s[0] = uint16(b[i]) + (uint16(b[i+1]) << 8)
r := utf16.Decode(u16s)
n := utf8.EncodeRune(b8buf, r[0])
ret.Write(b8buf[:n])
}
return ret.String(), nil
}
// 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++
} else {
// 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:])
}