From a9f280a2efdf5966b827dc2d79a2c2870dcddf39 Mon Sep 17 00:00:00 2001 From: wish Date: Sun, 1 Oct 2023 03:17:51 +1100 Subject: [PATCH] initial warehouse-v2 concept commit --- common/mhfitem/mhfitem.go | 133 ++++++++++++ .../mhfpacket/msg_mhf_enumerate_warehouse.go | 12 +- .../mhfpacket/msg_mhf_operate_warehouse.go | 14 +- network/mhfpacket/msg_mhf_update_warehouse.go | 46 +---- server/channelserver/handlers.go | 15 +- server/channelserver/handlers_house.go | 189 +++++++++--------- 6 files changed, 258 insertions(+), 151 deletions(-) create mode 100644 common/mhfitem/mhfitem.go diff --git a/common/mhfitem/mhfitem.go b/common/mhfitem/mhfitem.go new file mode 100644 index 000000000..f0e66ac69 --- /dev/null +++ b/common/mhfitem/mhfitem.go @@ -0,0 +1,133 @@ +package mhfitem + +import "erupe-ce/common/byteframe" + +type MHFItem struct { + ItemID uint16 +} + +type MHFSigilEffect struct { + ID uint16 + Level uint16 +} + +type MHFSigil struct { + Effects []MHFSigilEffect + Unk0 uint8 + Unk1 uint8 + Unk2 uint8 + Unk3 uint8 +} + +type MHFEquipment struct { + WarehouseID uint32 + ItemType uint8 + Unk0 uint8 + ItemID uint16 + Level uint16 + Decorations []MHFItem + Sigils []MHFSigil + Unk1 uint16 +} + +type MHFItemStack struct { + WarehouseID uint32 + Item MHFItem + Quantity uint16 + Unk0 uint32 +} + +func ReadWarehouseItem(bf *byteframe.ByteFrame) MHFItemStack { + var item MHFItemStack + item.WarehouseID = bf.ReadUint32() + item.Item.ItemID = bf.ReadUint16() + item.Quantity = bf.ReadUint16() + item.Unk0 = bf.ReadUint32() + return item +} + +func (is MHFItemStack) ToBytes() []byte { + bf := byteframe.NewByteFrame() + bf.WriteUint32(is.WarehouseID) + bf.WriteUint16(is.Item.ItemID) + bf.WriteUint16(is.Quantity) + bf.WriteUint32(is.Unk0) + return bf.Data() +} + +func SerializeWarehouseItems(i []MHFItemStack) []byte { + bf := byteframe.NewByteFrame() + bf.WriteUint16(uint16(len(i))) + bf.WriteUint16(0) // Unused + for _, j := range i { + bf.WriteBytes(j.ToBytes()) + } + return bf.Data() +} + +func ReadWarehouseEquipment(bf *byteframe.ByteFrame) MHFEquipment { + var equipment MHFEquipment + equipment.Decorations = make([]MHFItem, 3) + equipment.Sigils = make([]MHFSigil, 3) + for i := 0; i < 3; i++ { + equipment.Sigils[i].Effects = make([]MHFSigilEffect, 3) + } + equipment.WarehouseID = bf.ReadUint32() + equipment.ItemType = bf.ReadUint8() + equipment.Unk0 = bf.ReadUint8() + equipment.ItemID = bf.ReadUint16() + equipment.Level = bf.ReadUint16() + for i := 0; i < 3; i++ { + equipment.Decorations[i].ItemID = bf.ReadUint16() + } + for i := 0; i < 3; i++ { + for j := 0; j < 3; j++ { + equipment.Sigils[i].Effects[j].ID = bf.ReadUint16() + } + for j := 0; j < 3; j++ { + equipment.Sigils[i].Effects[j].Level = bf.ReadUint16() + } + equipment.Sigils[i].Unk0 = bf.ReadUint8() + equipment.Sigils[i].Unk1 = bf.ReadUint8() + equipment.Sigils[i].Unk2 = bf.ReadUint8() + equipment.Sigils[i].Unk3 = bf.ReadUint8() + } + equipment.Unk1 = bf.ReadUint16() + return equipment +} + +func (e MHFEquipment) ToBytes() []byte { + bf := byteframe.NewByteFrame() + bf.WriteUint32(e.WarehouseID) + bf.WriteUint8(e.ItemType) + bf.WriteUint8(e.Unk0) + bf.WriteUint16(e.ItemID) + bf.WriteUint16(e.Level) + for i := 0; i < 3; i++ { + bf.WriteUint16(e.Decorations[i].ItemID) + } + for i := 0; i < 3; i++ { + for j := 0; j < 3; j++ { + bf.WriteUint16(e.Sigils[i].Effects[j].ID) + } + for j := 0; j < 3; j++ { + bf.WriteUint16(e.Sigils[i].Effects[j].Level) + } + bf.WriteUint8(e.Sigils[i].Unk0) + bf.WriteUint8(e.Sigils[i].Unk1) + bf.WriteUint8(e.Sigils[i].Unk2) + bf.WriteUint8(e.Sigils[i].Unk3) + } + bf.WriteUint16(e.Unk1) + return bf.Data() +} + +func SerializeWarehouseEquipment(i []MHFEquipment) []byte { + bf := byteframe.NewByteFrame() + bf.WriteUint16(uint16(len(i))) + bf.WriteUint16(0) // Unused + for _, j := range i { + bf.WriteBytes(j.ToBytes()) + } + return bf.Data() +} diff --git a/network/mhfpacket/msg_mhf_enumerate_warehouse.go b/network/mhfpacket/msg_mhf_enumerate_warehouse.go index 3f1358045..30d30b605 100644 --- a/network/mhfpacket/msg_mhf_enumerate_warehouse.go +++ b/network/mhfpacket/msg_mhf_enumerate_warehouse.go @@ -11,7 +11,7 @@ import ( // MsgMhfEnumerateWarehouse represents the MSG_MHF_ENUMERATE_WAREHOUSE type MsgMhfEnumerateWarehouse struct { AckHandle uint32 - BoxType string + BoxType uint8 BoxIndex uint8 } @@ -23,15 +23,9 @@ func (m *MsgMhfEnumerateWarehouse) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfEnumerateWarehouse) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - boxType := bf.ReadUint8() - switch boxType { - case 0: - m.BoxType = "item" - case 1: - m.BoxType = "equip" - } + m.BoxType = bf.ReadUint8() m.BoxIndex = bf.ReadUint8() - _ = bf.ReadUint16() + bf.ReadBytes(2) // Zeroed return nil } diff --git a/network/mhfpacket/msg_mhf_operate_warehouse.go b/network/mhfpacket/msg_mhf_operate_warehouse.go index ba0ee7b78..aa5b70501 100644 --- a/network/mhfpacket/msg_mhf_operate_warehouse.go +++ b/network/mhfpacket/msg_mhf_operate_warehouse.go @@ -13,7 +13,7 @@ import ( type MsgMhfOperateWarehouse struct { AckHandle uint32 Operation uint8 - BoxType string + BoxType uint8 BoxIndex uint8 Name string } @@ -27,16 +27,10 @@ func (m *MsgMhfOperateWarehouse) Opcode() network.PacketID { func (m *MsgMhfOperateWarehouse) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() m.Operation = bf.ReadUint8() - boxType := bf.ReadUint8() - switch boxType { - case 0: - m.BoxType = "item" - case 1: - m.BoxType = "equip" - } + m.BoxType = bf.ReadUint8() m.BoxIndex = bf.ReadUint8() - _ = bf.ReadUint8() // lenName - _ = bf.ReadUint16() // Unk + _ = bf.ReadUint8() // lenName + bf.ReadUint16() // Zeroed m.Name = stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes()) return nil } diff --git a/network/mhfpacket/msg_mhf_update_warehouse.go b/network/mhfpacket/msg_mhf_update_warehouse.go index 962906988..5cac9425b 100644 --- a/network/mhfpacket/msg_mhf_update_warehouse.go +++ b/network/mhfpacket/msg_mhf_update_warehouse.go @@ -3,25 +3,18 @@ package mhfpacket import ( "errors" "erupe-ce/common/byteframe" + "erupe-ce/common/mhfitem" "erupe-ce/network" "erupe-ce/network/clientctx" ) -type WarehouseStack struct { - ID uint32 - Index uint16 - EquipType uint16 - ItemID uint16 - Quantity uint16 - Data []byte -} - // MsgMhfUpdateWarehouse represents the MSG_MHF_UPDATE_WAREHOUSE type MsgMhfUpdateWarehouse struct { - AckHandle uint32 - BoxType string - BoxIndex uint8 - Updates []WarehouseStack + AckHandle uint32 + BoxType uint8 + BoxIndex uint8 + UpdatedItems []mhfitem.MHFItemStack + UpdatedEquipment []mhfitem.MHFEquipment } // Opcode returns the ID associated with this packet type. @@ -32,35 +25,18 @@ func (m *MsgMhfUpdateWarehouse) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfUpdateWarehouse) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - boxType := bf.ReadUint8() - switch boxType { - case 0: - m.BoxType = "item" - case 1: - m.BoxType = "equip" - } + m.BoxType = bf.ReadUint8() m.BoxIndex = bf.ReadUint8() changes := int(bf.ReadUint16()) - var stackUpdate WarehouseStack + bf.ReadBytes(2) // Zeroed for i := 0; i < changes; i++ { - switch boxType { + switch m.BoxType { case 0: - stackUpdate.ID = bf.ReadUint32() - stackUpdate.Index = bf.ReadUint16() - stackUpdate.ItemID = bf.ReadUint16() - stackUpdate.Quantity = bf.ReadUint16() - _ = bf.ReadUint16() // Unk - m.Updates = append(m.Updates, stackUpdate) + m.UpdatedItems = append(m.UpdatedItems, mhfitem.ReadWarehouseItem(bf)) case 1: - stackUpdate.ID = bf.ReadUint32() - stackUpdate.Index = bf.ReadUint16() - stackUpdate.EquipType = bf.ReadUint16() - stackUpdate.ItemID = bf.ReadUint16() - stackUpdate.Data = bf.ReadBytes(56) - m.Updates = append(m.Updates, stackUpdate) + m.UpdatedEquipment = append(m.UpdatedEquipment, mhfitem.ReadWarehouseEquipment(bf)) } } - _ = bf.ReadUint16() return nil } diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 517da6e6d..e502bfa0f 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -4,6 +4,7 @@ import ( "encoding/binary" "encoding/hex" "erupe-ce/common/mhfcourse" + "erupe-ce/common/mhfitem" ps "erupe-ce/common/pascalstring" "erupe-ce/common/stringsupport" _config "erupe-ce/config" @@ -762,19 +763,19 @@ func handleMsgMhfCheckWeeklyStamp(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfExchangeWeeklyStamp(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfExchangeWeeklyStamp) var total, redeemed uint16 - var tktStack mhfpacket.WarehouseStack + var tktStack mhfitem.MHFItemStack if pkt.Unk1 == 0xA { // Yearly Sub Ex s.server.db.QueryRow("UPDATE stamps SET hl_total=hl_total-48, hl_redeemed=hl_redeemed-48 WHERE character_id=$1 RETURNING hl_total, hl_redeemed", s.charID).Scan(&total, &redeemed) - tktStack = mhfpacket.WarehouseStack{ItemID: 0x08A2, Quantity: 1} + tktStack = mhfitem.MHFItemStack{Item: mhfitem.MHFItem{ItemID: 2210}, Quantity: 1} } else { s.server.db.QueryRow(fmt.Sprintf("UPDATE stamps SET %s_redeemed=%s_redeemed+8 WHERE character_id=$1 RETURNING %s_total, %s_redeemed", pkt.StampType, pkt.StampType, pkt.StampType, pkt.StampType), s.charID).Scan(&total, &redeemed) if pkt.StampType == "hl" { - tktStack = mhfpacket.WarehouseStack{ItemID: 0x065E, Quantity: 5} + tktStack = mhfitem.MHFItemStack{Item: mhfitem.MHFItem{ItemID: 1630}, Quantity: 5} } else { - tktStack = mhfpacket.WarehouseStack{ItemID: 0x065F, Quantity: 5} + tktStack = mhfitem.MHFItemStack{Item: mhfitem.MHFItem{ItemID: 1631}, Quantity: 5} } } - addWarehouseGift(s, "item", tktStack) + addWarehouseItem(s, tktStack) bf := byteframe.NewByteFrame() bf.WriteUint16(total) bf.WriteUint16(redeemed) @@ -1626,13 +1627,13 @@ func handleMsgMhfStampcardStamp(s *Session, p mhfpacket.MHFPacket) { bf.WriteUint16(pkt.Reward2) bf.WriteUint16(pkt.Item2) bf.WriteUint16(pkt.Quantity2) - addWarehouseGift(s, "item", mhfpacket.WarehouseStack{ItemID: pkt.Item2, Quantity: pkt.Quantity2}) + addWarehouseItem(s, mhfitem.MHFItemStack{Item: mhfitem.MHFItem{ItemID: pkt.Item2}, Quantity: pkt.Quantity2}) } else if stamps%15 == 0 { bf.WriteUint16(1) bf.WriteUint16(pkt.Reward1) bf.WriteUint16(pkt.Item1) bf.WriteUint16(pkt.Quantity1) - addWarehouseGift(s, "item", mhfpacket.WarehouseStack{ItemID: pkt.Item1, Quantity: pkt.Quantity1}) + addWarehouseItem(s, mhfitem.MHFItemStack{Item: mhfitem.MHFItem{ItemID: pkt.Item1}, Quantity: pkt.Quantity1}) } else { bf.WriteBytes(make([]byte, 8)) } diff --git a/server/channelserver/handlers_house.go b/server/channelserver/handlers_house.go index 4cc53d303..231e6a72d 100644 --- a/server/channelserver/handlers_house.go +++ b/server/channelserver/handlers_house.go @@ -1,7 +1,9 @@ package channelserver import ( + "crypto/rand" "erupe-ce/common/byteframe" + "erupe-ce/common/mhfitem" ps "erupe-ce/common/pascalstring" "erupe-ce/common/stringsupport" _config "erupe-ce/config" @@ -406,7 +408,12 @@ func handleMsgMhfOperateWarehouse(s *Session, p mhfpacket.MHFPacket) { case 1: bf.WriteUint8(0) case 2: - s.server.db.Exec(fmt.Sprintf("UPDATE warehouse SET %s%dname=$1 WHERE character_id=$2", pkt.BoxType, pkt.BoxIndex), pkt.Name, s.charID) + switch pkt.BoxType { + case 0: + s.server.db.Exec(fmt.Sprintf("UPDATE warehouse SET item%dname=$1 WHERE character_id=$2", pkt.BoxIndex), pkt.Name, s.charID) + case 1: + s.server.db.Exec(fmt.Sprintf("UPDATE warehouse SET equip%dname=$1 WHERE character_id=$2", pkt.BoxIndex), pkt.Name, s.charID) + } case 3: bf.WriteUint32(0) // Usage renewal time, >1 = disabled bf.WriteUint16(10000) // Usages @@ -424,81 +431,74 @@ func handleMsgMhfOperateWarehouse(s *Session, p mhfpacket.MHFPacket) { doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } -func addWarehouseGift(s *Session, boxType string, giftStack mhfpacket.WarehouseStack) { - giftBox := getWarehouseBox(s, boxType, 10) - if boxType == "item" { - exists := false - for i, stack := range giftBox { - if stack.ItemID == giftStack.ItemID { - exists = true - giftBox[i].Quantity += giftStack.Quantity - break - } +func addWarehouseItem(s *Session, item mhfitem.MHFItemStack) { + giftBox := warehouseGetItems(s, 10) + exists := false + for i, stack := range giftBox { + if stack.Item.ItemID == item.Item.ItemID { + exists = true + giftBox[i].Quantity += item.Quantity + break } - if exists == false { - giftBox = append(giftBox, giftStack) - } - } else { - giftBox = append(giftBox, giftStack) } - s.server.db.Exec(fmt.Sprintf("UPDATE warehouse SET %s10=$1 WHERE character_id=$2", boxType), boxToBytes(giftBox, boxType), s.charID) + if !exists { + giftBox = append(giftBox, item) + } + s.server.db.Exec("UPDATE warehouse SET item10=$1 WHERE character_id=$2", mhfitem.SerializeWarehouseItems(giftBox), s.charID) } -func getWarehouseBox(s *Session, boxType string, boxIndex uint8) []mhfpacket.WarehouseStack { +func addWarehouseEquipment(s *Session, equipment mhfitem.MHFEquipment) { + giftBox := warehouseGetEquipment(s, 10) + giftBox = append(giftBox, equipment) + s.server.db.Exec("UPDATE warehouse SET equip10=$1 WHERE character_id=$2", mhfitem.SerializeWarehouseEquipment(giftBox), s.charID) +} + +func warehouseGetItems(s *Session, index uint8) []mhfitem.MHFItemStack { var data []byte - s.server.db.QueryRow(fmt.Sprintf("SELECT %s%d FROM warehouse WHERE character_id=$1", boxType, boxIndex), s.charID).Scan(&data) + var items []mhfitem.MHFItemStack + s.server.db.QueryRow(fmt.Sprintf(`SELECT item%d FROM warehouse WHERE character_id=$1`, index), s.charID).Scan(&data) if len(data) > 0 { box := byteframe.NewByteFrameFromBytes(data) numStacks := box.ReadUint16() - stacks := make([]mhfpacket.WarehouseStack, numStacks) + box.ReadUint16() // Unused for i := 0; i < int(numStacks); i++ { - if boxType == "item" { - stacks[i].ID = box.ReadUint32() - stacks[i].Index = box.ReadUint16() - stacks[i].ItemID = box.ReadUint16() - stacks[i].Quantity = box.ReadUint16() - box.ReadUint16() - } else { - stacks[i].ID = box.ReadUint32() - stacks[i].Index = box.ReadUint16() - stacks[i].EquipType = box.ReadUint16() - stacks[i].ItemID = box.ReadUint16() - stacks[i].Data = box.ReadBytes(56) - } + items = append(items, mhfitem.ReadWarehouseItem(box)) } - return stacks - } else { - return make([]mhfpacket.WarehouseStack, 0) } + return items } -func boxToBytes(stacks []mhfpacket.WarehouseStack, boxType string) []byte { - bf := byteframe.NewByteFrame() - bf.WriteUint16(uint16(len(stacks))) - for i, stack := range stacks { - if boxType == "item" { - bf.WriteUint32(stack.ID) - bf.WriteUint16(uint16(i + 1)) - bf.WriteUint16(stack.ItemID) - bf.WriteUint16(stack.Quantity) - bf.WriteUint16(0) - } else { - bf.WriteUint32(stack.ID) - bf.WriteUint16(uint16(i + 1)) - bf.WriteUint16(stack.EquipType) - bf.WriteUint16(stack.ItemID) - bf.WriteBytes(stack.Data) +func warehouseGetEquipment(s *Session, index uint8) []mhfitem.MHFEquipment { + var data []byte + var equipment []mhfitem.MHFEquipment + s.server.db.QueryRow(fmt.Sprintf(`SELECT equip%d FROM warehouse WHERE character_id=$1`, index), s.charID).Scan(&data) + if len(data) > 0 { + box := byteframe.NewByteFrameFromBytes(data) + numStacks := box.ReadUint16() + box.ReadUint16() // Unused + for i := 0; i < int(numStacks); i++ { + temp := mhfitem.ReadWarehouseEquipment(box) + if !temp.Deleted { + equipment = append(equipment, temp) + } } } - bf.WriteUint16(0) - return bf.Data() + return equipment } func handleMsgMhfEnumerateWarehouse(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEnumerateWarehouse) - box := getWarehouseBox(s, pkt.BoxType, pkt.BoxIndex) - if len(box) > 0 { - doAckBufSucceed(s, pkt.AckHandle, boxToBytes(box, pkt.BoxType)) + bf := byteframe.NewByteFrame() + switch pkt.BoxType { + case 0: + items := warehouseGetItems(s, pkt.BoxIndex) + bf.WriteBytes(mhfitem.SerializeWarehouseItems(items)) + case 1: + equipment := warehouseGetEquipment(s, pkt.BoxIndex) + bf.WriteBytes(mhfitem.SerializeWarehouseEquipment(equipment)) + } + if bf.Index() > 0 { + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } else { doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) } @@ -506,49 +506,58 @@ func handleMsgMhfEnumerateWarehouse(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfUpdateWarehouse(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfUpdateWarehouse) - box := getWarehouseBox(s, pkt.BoxType, pkt.BoxIndex) - // Update existing stacks - var newStacks []mhfpacket.WarehouseStack - for _, update := range pkt.Updates { - exists := false - if pkt.BoxType == "item" { - for i, stack := range box { - if stack.Index == update.Index { + // o = old, u = update, f = final + var fItems []mhfitem.MHFItemStack + var fEquip []mhfitem.MHFEquipment + switch pkt.BoxType { + case 0: + oItems := warehouseGetItems(s, pkt.BoxIndex) + for _, uItem := range pkt.UpdatedItems { + exists := false + for _, oItem := range oItems { + if uItem.Item.ItemID == oItem.Item.ItemID { + if uItem.Quantity > 0 { + fItems = append(fItems, uItem) + } exists = true - box[i].Quantity = update.Quantity break } } - } else { - for i, stack := range box { - if stack.Index == update.Index { + if !exists { + newID := make([]byte, 4) + _, _ = rand.Read(newID) + bf := byteframe.NewByteFrameFromBytes(newID) + uItem.WarehouseID = bf.ReadUint32() + fItems = append(fItems, uItem) + } + } + s.server.db.Exec(fmt.Sprintf(`UPDATE warehouse SET item%d=$1 WHERE character_id=$2`, pkt.BoxIndex), mhfitem.SerializeWarehouseItems(fItems), s.charID) + case 1: + oEquips := warehouseGetEquipment(s, pkt.BoxIndex) + for _, uEquip := range pkt.UpdatedEquipment { + exists := false + for _, oEquip := range oEquips { + if oEquip.WarehouseID == uEquip.WarehouseID { exists = true - box[i].ItemID = update.ItemID + // Will set removed items to 0 + oEquip.ItemID = uEquip.ItemID break } } - } - if exists == false { - newStacks = append(newStacks, update) - } - } - // Append new stacks - for _, stack := range newStacks { - box = append(box, stack) - } - // Slice empty stacks - var cleanedBox []mhfpacket.WarehouseStack - for _, stack := range box { - if pkt.BoxType == "item" { - if stack.Quantity > 0 { - cleanedBox = append(cleanedBox, stack) - } - } else { - if stack.ItemID != 0 { - cleanedBox = append(cleanedBox, stack) + if !exists { + newID := make([]byte, 4) + _, _ = rand.Read(newID) + bf := byteframe.NewByteFrameFromBytes(newID) + uEquip.WarehouseID = bf.ReadUint32() + fEquip = append(fEquip, uEquip) } } + for _, oEquip := range oEquips { + if oEquip.ItemID > 0 { + fEquip = append(fEquip, oEquip) + } + } + s.server.db.Exec(fmt.Sprintf(`UPDATE warehouse SET equip%d=$1 WHERE character_id=$2`, pkt.BoxIndex), mhfitem.SerializeWarehouseEquipment(fEquip), s.charID) } - s.server.db.Exec(fmt.Sprintf("UPDATE warehouse SET %s%d=$1 WHERE character_id=$2", pkt.BoxType, pkt.BoxIndex), boxToBytes(cleanedBox, pkt.BoxType), s.charID) doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) }