mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-22 23:54:33 +01:00
330 non-vendor files had minor formatting inconsistencies (comment alignment, whitespace). No logic changes.
107 lines
3.4 KiB
Go
107 lines
3.4 KiB
Go
package protocol
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"erupe-ce/common/byteframe"
|
|
"erupe-ce/common/stringsupport"
|
|
|
|
"erupe-ce/cmd/protbot/conn"
|
|
)
|
|
|
|
// SignResult holds the parsed response from a successful DSGN sign-in.
|
|
type SignResult struct {
|
|
TokenID uint32
|
|
TokenString string // 16 raw bytes as string
|
|
Timestamp uint32
|
|
EntranceAddr string
|
|
CharIDs []uint32
|
|
}
|
|
|
|
// DoSign connects to the sign server and performs a DSGN login.
|
|
// Reference: Erupe server/signserver/session.go (handleDSGN) and dsgn_resp.go (makeSignResponse).
|
|
func DoSign(addr, username, password string) (*SignResult, error) {
|
|
c, err := conn.DialWithInit(addr)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("sign connect: %w", err)
|
|
}
|
|
defer func() { _ = c.Close() }()
|
|
|
|
// Build DSGN request: "DSGN:041" + \x00 + SJIS(user) + \x00 + SJIS(pass) + \x00 + \x00
|
|
// The server reads: null-terminated request type, null-terminated user, null-terminated pass, null-terminated unk.
|
|
// The request type has a 3-char version suffix (e.g. "041" for ZZ client mode 41) that the server strips.
|
|
bf := byteframe.NewByteFrame()
|
|
bf.WriteNullTerminatedBytes([]byte("DSGN:041")) // reqType with version suffix (server strips last 3 chars to get "DSGN:")
|
|
bf.WriteNullTerminatedBytes(stringsupport.UTF8ToSJIS(username))
|
|
bf.WriteNullTerminatedBytes(stringsupport.UTF8ToSJIS(password))
|
|
bf.WriteUint8(0) // Unk null-terminated empty string
|
|
|
|
if err := c.SendPacket(bf.Data()); err != nil {
|
|
return nil, fmt.Errorf("sign send: %w", err)
|
|
}
|
|
|
|
resp, err := c.ReadPacket()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("sign recv: %w", err)
|
|
}
|
|
|
|
return parseSignResponse(resp)
|
|
}
|
|
|
|
// parseSignResponse parses the binary response from the sign server.
|
|
// Reference: Erupe server/signserver/dsgn_resp.go:makeSignResponse
|
|
func parseSignResponse(data []byte) (*SignResult, error) {
|
|
if len(data) < 1 {
|
|
return nil, fmt.Errorf("empty sign response")
|
|
}
|
|
|
|
rbf := byteframe.NewByteFrameFromBytes(data)
|
|
|
|
resultCode := rbf.ReadUint8()
|
|
if resultCode != 1 { // SIGN_SUCCESS = 1
|
|
return nil, fmt.Errorf("sign failed with code %d", resultCode)
|
|
}
|
|
|
|
patchCount := rbf.ReadUint8() // patch server count (usually 2)
|
|
_ = rbf.ReadUint8() // entrance server count (usually 1)
|
|
charCount := rbf.ReadUint8() // character count
|
|
|
|
result := &SignResult{}
|
|
result.TokenID = rbf.ReadUint32()
|
|
result.TokenString = string(rbf.ReadBytes(16)) // 16 raw bytes
|
|
result.Timestamp = rbf.ReadUint32()
|
|
|
|
// Skip patch server URLs (pascal strings with uint8 length prefix)
|
|
for i := uint8(0); i < patchCount; i++ {
|
|
strLen := rbf.ReadUint8()
|
|
_ = rbf.ReadBytes(uint(strLen))
|
|
}
|
|
|
|
// Read entrance server address (pascal string with uint8 length prefix)
|
|
entranceLen := rbf.ReadUint8()
|
|
result.EntranceAddr = string(rbf.ReadBytes(uint(entranceLen - 1)))
|
|
_ = rbf.ReadUint8() // null terminator
|
|
|
|
// Read character entries
|
|
for i := uint8(0); i < charCount; i++ {
|
|
charID := rbf.ReadUint32()
|
|
result.CharIDs = append(result.CharIDs, charID)
|
|
|
|
_ = rbf.ReadUint16() // HR
|
|
_ = rbf.ReadUint16() // WeaponType
|
|
_ = rbf.ReadUint32() // LastLogin
|
|
_ = rbf.ReadUint8() // IsFemale
|
|
_ = rbf.ReadUint8() // IsNewCharacter
|
|
_ = rbf.ReadUint8() // Old GR
|
|
_ = rbf.ReadUint8() // Use uint16 GR flag
|
|
_ = rbf.ReadBytes(16) // Character name (padded)
|
|
_ = rbf.ReadBytes(32) // Unk desc string (padded)
|
|
// ZZ mode: additional fields
|
|
_ = rbf.ReadUint16() // GR
|
|
_ = rbf.ReadUint8() // Unk
|
|
_ = rbf.ReadUint8() // Unk
|
|
}
|
|
|
|
return result, nil
|
|
}
|