1
0
Protocol/packets/apppacket.go

246 lines
5.9 KiB
Go

package packets
import (
"encoding/binary"
)
// DefaultOpcodeSize is the default opcode size for application packets
var DefaultOpcodeSize uint8 = 2
// EmuOpcode represents an emulator opcode
type EmuOpcode uint16
// Common emulator opcodes
const (
OpUnknown EmuOpcode = 0
// Add other opcodes as needed from op_codes.h
)
// AppPacket handles high-level game opcodes and application data
// This is the main packet type used by game logic
type AppPacket struct {
*Packet
emuOpcode EmuOpcode // Cached emulator opcode
opcodeSize uint8 // Size of opcode in bytes (1 or 2)
}
// NewAppPacket creates a new application packet
func NewAppPacket() *AppPacket {
return &AppPacket{
Packet: NewPacket(0, nil),
emuOpcode: OpUnknown,
opcodeSize: DefaultOpcodeSize,
}
}
// NewAppPacketWithOp creates a new application packet with opcode
func NewAppPacketWithOp(op EmuOpcode) *AppPacket {
app := &AppPacket{
Packet: NewPacket(0, nil),
opcodeSize: DefaultOpcodeSize,
}
app.SetOpcode(op)
return app
}
// NewAppPacketWithSize creates a new application packet with opcode and size
func NewAppPacketWithSize(op EmuOpcode, size uint32) *AppPacket {
app := &AppPacket{
Packet: NewPacket(0, make([]byte, size)),
opcodeSize: DefaultOpcodeSize,
}
app.SetOpcode(op)
return app
}
// NewAppPacketWithData creates a new application packet with opcode and data
func NewAppPacketWithData(op EmuOpcode, data []byte) *AppPacket {
app := &AppPacket{
Packet: NewPacket(0, data),
opcodeSize: DefaultOpcodeSize,
}
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) *AppPacket {
if opcodeSize == 0 {
opcodeSize = DefaultOpcodeSize
}
app := &AppPacket{
Packet: NewPacket(0, nil),
opcodeSize: opcodeSize,
}
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:])
}
}
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
}
// SetOpcode sets the emulator opcode
func (a *AppPacket) SetOpcode(op EmuOpcode) {
a.emuOpcode = op
// In full implementation, this would convert to protocol opcode
// using opcode manager. For now, direct assignment
a.Opcode = uint16(op)
}
// GetOpcode returns the emulator opcode (with caching)
func (a *AppPacket) GetOpcode() EmuOpcode {
if a.emuOpcode == OpUnknown {
// In full implementation, convert from protocol opcode
a.emuOpcode = EmuOpcode(a.Opcode)
}
return a.emuOpcode
}
// 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,
}
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
}