271 lines
6.7 KiB
Go
271 lines
6.7 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 and application data
|
|
// This is the main packet type used by game logic
|
|
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 a new application packet 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 a new application packet 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 a new application packet 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 app packet from raw buffer (used by ProtoPacket)
|
|
// Assumes first bytes are opcode based on opcodeSize
|
|
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,
|
|
}
|
|
|
|
if opcodeSize == 1 && len(buf) >= 1 {
|
|
app.Opcode = uint16(buf[0])
|
|
if len(buf) > 1 {
|
|
app.Buffer = make([]byte, len(buf)-1)
|
|
copy(app.Buffer, buf[1:])
|
|
}
|
|
} else if len(buf) >= 2 {
|
|
app.Opcode = binary.BigEndian.Uint16(buf[:2])
|
|
if len(buf) > 2 {
|
|
app.Buffer = make([]byte, len(buf)-2)
|
|
copy(app.Buffer, buf[2:])
|
|
}
|
|
}
|
|
|
|
// Convert EQ opcode to emulator opcode
|
|
if app.manager != nil {
|
|
app.emuOpcode = app.manager.EQToEmu(app.Opcode)
|
|
}
|
|
|
|
return app
|
|
}
|
|
|
|
// Size returns total packet size including opcode
|
|
func (a *AppPacket) Size() uint32 {
|
|
return uint32(len(a.Buffer)) + uint32(a.opcodeSize)
|
|
}
|
|
|
|
// SetOpcodeSize sets the opcode size
|
|
func (a *AppPacket) SetOpcodeSize(size uint8) {
|
|
a.opcodeSize = size
|
|
}
|
|
|
|
// SetManager sets the opcode manager for translation
|
|
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)
|
|
}
|
|
}
|
|
|
|
// 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 {
|
|
// Fallback to direct assignment if no manager
|
|
a.Opcode = uint16(op)
|
|
}
|
|
}
|
|
|
|
// GetOpcode returns the emulator opcode (with caching)
|
|
func (a *AppPacket) GetOpcode() opcodes.EmuOpcode {
|
|
if a.emuOpcode == opcodes.OP_Unknown && a.manager != nil {
|
|
// Convert from protocol opcode
|
|
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"
|
|
}
|
|
|
|
// Copy creates a deep copy of this application packet
|
|
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
|
|
}
|
|
|
|
// Serialize writes the application packet to a destination buffer
|
|
// Handles special opcode encoding rules for application-level packets
|
|
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 this packet with another application packet
|
|
func (a *AppPacket) Combine(rhs *AppPacket) bool {
|
|
const opAppCombined = 0x19 // OP_AppCombined
|
|
|
|
// Check if we can combine these packets
|
|
totalSize := a.Size() + rhs.Size()
|
|
if totalSize > 512 { // Reasonable max combined packet size
|
|
return false
|
|
}
|
|
|
|
// If this isn't already a combined packet, convert it
|
|
if a.Opcode != opAppCombined {
|
|
// Create combined packet structure
|
|
oldSize := a.Size()
|
|
newSize := oldSize + rhs.Size() + 2 // +2 for size headers
|
|
|
|
newBuffer := make([]byte, newSize)
|
|
pos := 0
|
|
|
|
// Write first packet size and data
|
|
newBuffer[pos] = byte(oldSize)
|
|
pos++
|
|
|
|
// Serialize first packet
|
|
tmpBuf := make([]byte, oldSize)
|
|
a.Serialize(tmpBuf)
|
|
copy(newBuffer[pos:], tmpBuf)
|
|
pos += int(oldSize)
|
|
|
|
// Write second packet size and data
|
|
rhsSize := rhs.Size()
|
|
newBuffer[pos] = byte(rhsSize)
|
|
pos++
|
|
|
|
// Serialize second packet
|
|
tmpBuf = make([]byte, rhsSize)
|
|
rhs.Serialize(tmpBuf)
|
|
copy(newBuffer[pos:], tmpBuf)
|
|
|
|
// Update this packet to be a combined packet
|
|
a.Opcode = opAppCombined
|
|
a.Buffer = newBuffer
|
|
return true
|
|
}
|
|
|
|
// This is already a combined packet, add to it
|
|
rhsSize := rhs.Size()
|
|
if rhsSize >= 255 {
|
|
// Oversized packet handling
|
|
newBuffer := make([]byte, len(a.Buffer)+int(rhsSize)+3)
|
|
copy(newBuffer, a.Buffer)
|
|
pos := len(a.Buffer)
|
|
|
|
newBuffer[pos] = 255 // Oversized marker
|
|
pos++
|
|
binary.BigEndian.PutUint16(newBuffer[pos:], uint16(rhsSize))
|
|
pos += 2
|
|
|
|
tmpBuf := make([]byte, rhsSize)
|
|
rhs.Serialize(tmpBuf)
|
|
copy(newBuffer[pos:], tmpBuf)
|
|
|
|
a.Buffer = newBuffer
|
|
} else {
|
|
// Normal sized addition
|
|
newBuffer := make([]byte, len(a.Buffer)+int(rhsSize)+1)
|
|
copy(newBuffer, a.Buffer)
|
|
pos := len(a.Buffer)
|
|
|
|
newBuffer[pos] = byte(rhsSize)
|
|
pos++
|
|
|
|
tmpBuf := make([]byte, rhsSize)
|
|
rhs.Serialize(tmpBuf)
|
|
copy(newBuffer[pos:], tmpBuf)
|
|
|
|
a.Buffer = newBuffer
|
|
}
|
|
|
|
return true
|
|
}
|