wfc-server/common/friend_code.go

109 lines
1.9 KiB
Go

package common
import (
"crypto/md5"
"encoding/binary"
"fmt"
"strings"
)
const (
fcCRC8 = iota
fcMD5 = iota
)
var crc8Table = func() [256]byte {
var table [256]byte
poly := byte(0x07)
for i := 0; i < 256; i++ {
crc := byte(i)
for j := 0; j < 8; j++ {
if crc&0x80 != 0 {
crc = (crc << 1) ^ poly
} else {
crc <<= 1
}
}
table[i] = crc
}
return table
}()
func crc8(data []byte) byte {
crc := byte(0)
for _, b := range data {
crc = crc8Table[crc^b]
}
return crc
}
func getCRCType(gameId string) (crcType byte, reverse bool) {
reverse = false
if strings.HasPrefix(gameId, "RSB") || strings.HasPrefix(gameId, "RPB") {
crcType = fcCRC8
return
}
if strings.HasPrefix(gameId, "HDM") || strings.HasPrefix(gameId, "WDM") {
crcType = fcCRC8
reverse = true
return
}
switch gameId[0] {
case 'R', 'S', 'H', 'W', 'X', 'Y':
crcType = fcMD5
default:
crcType = fcCRC8
}
return
}
func CalcFriendCode(pid uint32, gameId string) uint64 {
if pid == 0 {
return 0
}
buffer := make([]byte, 8)
binary.LittleEndian.PutUint32(buffer, pid)
buffer[4] = gameId[3]
buffer[5] = gameId[2]
buffer[6] = gameId[1]
buffer[7] = gameId[0]
crc, _ := getCRCType(gameId)
if crc == fcCRC8 {
return uint64(pid) | (uint64(crc8(buffer)&0x7f) << 32)
}
digest := md5.Sum(buffer)
return uint64(pid) | (uint64(digest[0]&0xfe) << 31)
}
func CalcFriendCodeString(pid uint32, gameId string) string {
_, reverse := getCRCType(gameId)
return GetRawFriendCodeString(CalcFriendCode(pid, gameId), reverse)
}
func GetRawFriendCodeString(fc uint64, reverse bool) string {
s := fmt.Sprintf("%012d", max(min(fc, 999999999999), 0))
if reverse {
// Reverse the digit order
s = s[11:12] + s[10:11] + s[9:10] + s[8:9] + s[7:8] + s[6:7] + s[5:6] + s[4:5] + s[3:4] + s[2:3] + s[1:2] + s[0:1]
}
return s[len(s)-12:len(s)-8] + "-" + s[len(s)-8:len(s)-4] + "-" + s[len(s)-4:]
}