1
0
Protocol/packets/apppacket.go
2025-09-03 13:50:03 -05:00

209 lines
5.2 KiB
Go

package packets
import (
"encoding/binary"
"git.sharkk.net/EQ2/Protocol/opcodes"
)
// DefaultOpcodeSize is the default opcode size for application packets
var DefaultOpcodeSize uint8 = 2
// AppPacket handles high-level game opcodes (matches EQApplicationPacket)
type AppPacket struct {
*Packet
emuOpcode opcodes.EmuOpcode // Cached emulator opcode
opcodeSize uint8 // Size of opcode in bytes (1 or 2)
manager opcodes.Manager // Opcode manager for translation
}
// NewAppPacket creates a new application packet
func NewAppPacket(manager opcodes.Manager) *AppPacket {
return &AppPacket{
Packet: NewPacket(0, nil),
emuOpcode: opcodes.OP_Unknown,
opcodeSize: DefaultOpcodeSize,
manager: manager,
}
}
// NewAppPacketWithOp creates with opcode
func NewAppPacketWithOp(op opcodes.EmuOpcode, manager opcodes.Manager) *AppPacket {
app := &AppPacket{
Packet: NewPacket(0, nil),
opcodeSize: DefaultOpcodeSize,
manager: manager,
}
app.SetOpcode(op)
return app
}
// NewAppPacketWithSize creates with opcode and size
func NewAppPacketWithSize(op opcodes.EmuOpcode, size uint32, manager opcodes.Manager) *AppPacket {
app := &AppPacket{
Packet: NewPacket(0, make([]byte, size)),
opcodeSize: DefaultOpcodeSize,
manager: manager,
}
app.SetOpcode(op)
return app
}
// NewAppPacketWithData creates with opcode and data
func NewAppPacketWithData(op opcodes.EmuOpcode, data []byte, manager opcodes.Manager) *AppPacket {
app := &AppPacket{
Packet: NewPacket(0, data),
opcodeSize: DefaultOpcodeSize,
manager: manager,
}
app.SetOpcode(op)
return app
}
// NewAppPacketFromRaw creates from raw buffer (matches EQApplicationPacket constructor)
func NewAppPacketFromRaw(buf []byte, opcodeSize uint8, manager opcodes.Manager) *AppPacket {
if opcodeSize == 0 {
opcodeSize = DefaultOpcodeSize
}
app := &AppPacket{
Packet: NewPacket(0, nil),
opcodeSize: opcodeSize,
manager: manager,
}
offset := 0
// Extract opcode based on size
if opcodeSize == 1 && len(buf) >= 1 {
app.Opcode = uint16(buf[0])
offset = 1
} else if len(buf) >= 2 {
app.Opcode = binary.BigEndian.Uint16(buf[:2])
offset = 2
}
// Copy remaining data as payload
if len(buf) > offset {
app.Buffer = make([]byte, len(buf)-offset)
copy(app.Buffer, buf[offset:])
}
// Convert EQ opcode to emulator opcode
if app.manager != nil {
app.emuOpcode = app.manager.EQToEmu(app.Opcode)
} else {
app.emuOpcode = opcodes.EmuOpcode(app.Opcode)
}
return app
}
// Size returns total packet size including opcode
func (a *AppPacket) Size() uint32 {
// Handle special encoding where low byte = 0x00 needs extra byte
extraBytes := uint32(0)
if a.opcodeSize == 2 && (a.Opcode&0x00ff) == 0 {
extraBytes = 1
}
return uint32(len(a.Buffer)) + uint32(a.opcodeSize) + extraBytes
}
// SetOpcode sets the emulator opcode and translates to EQ opcode
func (a *AppPacket) SetOpcode(op opcodes.EmuOpcode) {
a.emuOpcode = op
if a.manager != nil {
a.Opcode = a.manager.EmuToEQ(op)
} else {
a.Opcode = uint16(op)
}
}
// GetOpcode returns the emulator opcode
func (a *AppPacket) GetOpcode() opcodes.EmuOpcode {
if a.emuOpcode == opcodes.OP_Unknown && a.manager != nil {
a.emuOpcode = a.manager.EQToEmu(a.Opcode)
}
return a.emuOpcode
}
// GetOpcodeName returns the name of the current opcode
func (a *AppPacket) GetOpcodeName() string {
if a.manager != nil {
return a.manager.EmuToName(a.GetOpcode())
}
return "OP_Unknown"
}
// SetManager sets the opcode manager
func (a *AppPacket) SetManager(manager opcodes.Manager) {
a.manager = manager
// Re-translate if we have an opcode
if a.emuOpcode != opcodes.OP_Unknown && a.manager != nil {
a.Opcode = a.manager.EmuToEQ(a.emuOpcode)
}
}
// Serialize writes to destination (matches EQApplicationPacket::serialize)
func (a *AppPacket) Serialize(dest []byte) uint32 {
opcodeBytes := a.opcodeSize
pos := 0
if a.opcodeSize == 1 {
// Single-byte opcode
dest[pos] = byte(a.Opcode)
pos++
} else {
// Two-byte opcode with special encoding rules
// Application opcodes with low byte = 0x00 need extra 0x00 prefix
if (a.Opcode & 0x00ff) == 0 {
dest[pos] = 0
pos++
binary.BigEndian.PutUint16(dest[pos:], a.Opcode)
pos += 2
opcodeBytes++
} else {
binary.BigEndian.PutUint16(dest[pos:], a.Opcode)
pos += 2
}
}
// Copy packet data after opcode
copy(dest[pos:], a.Buffer)
return uint32(len(a.Buffer)) + uint32(opcodeBytes)
}
// Combine combines with another app packet (matches EQApplicationPacket::combine)
func (a *AppPacket) Combine(rhs *AppPacket) bool {
// Application packet combining is not implemented in original
// Use protocol-level combining instead
return false
}
// Copy creates a deep copy
func (a *AppPacket) Copy() *AppPacket {
newApp := &AppPacket{
Packet: NewPacket(a.Opcode, a.Buffer),
emuOpcode: a.emuOpcode,
opcodeSize: a.opcodeSize,
manager: a.manager,
}
newApp.Packet.CopyInfo(a.Packet)
return newApp
}
// ToProto converts to protocol packet
func (a *AppPacket) ToProto() *ProtoPacket {
// Serialize the application packet
tmpBuf := make([]byte, a.Size())
a.Serialize(tmpBuf)
proto := NewProtoPacket(opcodes.OP_Packet, tmpBuf, a.manager)
proto.LoginOp = a.emuOpcode
proto.CopyInfo(a.Packet)
return proto
}