package signserver import ( "erupe-ce/common/byteframe" ps "erupe-ce/common/pascalstring" "erupe-ce/common/stringsupport" _config "erupe-ce/config" "erupe-ce/server/channelserver" "fmt" "strings" "time" "go.uber.org/zap" ) func (s *Session) makeSignResponse(uid uint32) []byte { // Get the characters from the DB. chars, err := s.server.getCharactersForUser(uid) if len(chars) == 0 && uid != 0 { err = s.server.newUserChara(uid) if err == nil { chars, err = s.server.getCharactersForUser(uid) } } if err != nil { s.logger.Warn("Error getting characters from DB", zap.Error(err)) } bf := byteframe.NewByteFrame() var tokenID uint32 var sessToken string if uid == 0 && s.psn != "" { tokenID, sessToken, err = s.server.registerPsnToken(s.psn) } else { tokenID, sessToken, err = s.server.registerUidToken(uid) } if err != nil { bf.WriteUint8(uint8(SIGN_EABORT)) return bf.Data() } if s.client == PS3 && (s.server.erupeConfig.PatchServerFile == "" || s.server.erupeConfig.PatchServerManifest == "") { bf.WriteUint8(uint8(SIGN_EABORT)) return bf.Data() } bf.WriteUint8(uint8(SIGN_SUCCESS)) bf.WriteUint8(2) // patch server count bf.WriteUint8(1) // entrance server count bf.WriteUint8(uint8(len(chars))) bf.WriteUint32(tokenID) bf.WriteBytes([]byte(sessToken)) bf.WriteUint32(uint32(channelserver.TimeAdjusted().Unix())) if s.client == PS3 { ps.Uint8(bf, fmt.Sprintf("%s/ps3", s.server.erupeConfig.PatchServerManifest), false) ps.Uint8(bf, fmt.Sprintf("%s/ps3", s.server.erupeConfig.PatchServerFile), false) } else { ps.Uint8(bf, s.server.erupeConfig.PatchServerManifest, false) ps.Uint8(bf, s.server.erupeConfig.PatchServerFile, false) } if strings.Split(s.rawConn.RemoteAddr().String(), ":")[0] == "127.0.0.1" { ps.Uint8(bf, fmt.Sprintf("127.0.0.1:%d", s.server.erupeConfig.Entrance.Port), false) } else { ps.Uint8(bf, fmt.Sprintf("%s:%d", s.server.erupeConfig.Host, s.server.erupeConfig.Entrance.Port), false) } lastPlayed := uint32(0) for _, char := range chars { if lastPlayed == 0 { lastPlayed = char.ID } bf.WriteUint32(char.ID) if s.server.erupeConfig.DebugOptions.MaxLauncherHR { bf.WriteUint16(999) } else { bf.WriteUint16(char.HR) } bf.WriteUint16(char.WeaponType) // Weapon, 0-13. bf.WriteUint32(char.LastLogin) // Last login date, unix timestamp in seconds. bf.WriteBool(char.IsFemale) // Sex, 0=male, 1=female. bf.WriteBool(char.IsNewCharacter) // Is new character, 1 replaces character name with ?????. bf.WriteUint8(0) // Old GR bf.WriteBool(true) // Use uint16 GR, no reason not to bf.WriteBytes(stringsupport.PaddedString(char.Name, 16, true)) // Character name bf.WriteBytes(stringsupport.PaddedString(char.UnkDescString, 32, false)) // unk str if s.server.erupeConfig.RealClientMode >= _config.G7 { bf.WriteUint16(char.GR) bf.WriteUint8(0) // Unk bf.WriteUint8(0) // Unk } } friends := s.server.getFriendsForCharacters(chars) if len(friends) == 0 { bf.WriteUint8(0) } else { if len(friends) > 255 { bf.WriteUint8(255) bf.WriteUint16(uint16(len(friends))) } else { bf.WriteUint8(uint8(len(friends))) } for _, friend := range friends { bf.WriteUint32(friend.CID) bf.WriteUint32(friend.ID) ps.Uint8(bf, friend.Name, true) } } guildmates := s.server.getGuildmatesForCharacters(chars) if len(guildmates) == 0 { bf.WriteUint8(0) } else { if len(guildmates) > 255 { bf.WriteUint8(255) bf.WriteUint16(uint16(len(guildmates))) } else { bf.WriteUint8(uint8(len(guildmates))) } for _, guildmate := range guildmates { bf.WriteUint32(guildmate.CID) bf.WriteUint32(guildmate.ID) ps.Uint8(bf, guildmate.Name, true) } } if s.server.erupeConfig.HideLoginNotice { bf.WriteBool(false) } else { bf.WriteBool(true) bf.WriteUint8(0) bf.WriteUint8(0) ps.Uint16(bf, strings.Join(s.server.erupeConfig.LoginNotices[:], ""), true) } bf.WriteUint32(s.server.getLastCID(uid)) bf.WriteUint32(s.server.getUserRights(uid)) namNGWords := []string{"test", "痴女", "てすと"} msgNGWords := []string{"test", "痴女", "てすと"} filters := byteframe.NewByteFrame() filters.SetLE() filters.WriteNullTerminatedBytes([]byte("smc")) smc := byteframe.NewByteFrame() smc.SetLE() smcData := []struct { charGroup [][]rune }{ {[][]rune{{'='}, {'='}}}, {[][]rune{{')'}, {')'}}}, {[][]rune{{'('}, {'('}}}, {[][]rune{{'!'}, {'!'}}}, {[][]rune{{'/'}, {'/'}}}, {[][]rune{{'+'}, {'+'}}}, {[][]rune{{'&'}, {'&'}}}, {[][]rune{{'ぼ'}, {'ボ'}, {'ホ', '゙'}, {'ほ', '゙'}, {'ホ', '゙'}, {'ほ', '゛'}, {'ホ', '゛'}, {'ホ', '゛'}}}, {[][]rune{{'べ'}, {'ベ'}, {'ヘ', '゙'}, {'へ', '゙'}, {'ヘ', '゙'}, {'へ', '゛'}, {'ヘ', '゛'}, {'ヘ', '゛'}}}, {[][]rune{{'で'}, {'デ'}, {'テ', '゙'}, {'て', '゙'}, {'テ', '゙'}, {'て', '゛'}, {'テ', '゛'}, {'テ', '゛'}, {'〒', '゛'}, {'〒', '゙'}, {'乙', '゙'}, {'乙', '゛'}}}, {[][]rune{{'び'}, {'ビ'}, {'ヒ', '゙'}, {'ひ', '゙'}, {'ヒ', '゙'}, {'ひ', '゛'}, {'ヒ', '゛'}, {'ヒ', '゛'}}}, {[][]rune{{'ど'}, {'ド'}, {'ト', '゙'}, {'と', '゙'}, {'ト', '゙'}, {'と', '゛'}, {'ト', '゛'}, {'ト', '゛'}, {'┣', '゙'}, {'┣', '゛'}, {'├', '゙'}, {'├', '゛'}}}, {[][]rune{{'ば'}, {'バ'}, {'ハ', '゙'}, {'は', '゙'}, {'ハ', '゙'}, {'八', '゙'}, {'は', '゛'}, {'ハ', '゛'}, {'ハ', '゛'}, {'八', '゛'}}}, {[][]rune{{'つ', '゙'}, {'ヅ'}, {'ツ', '゙'}, {'つ', '゛'}, {'ツ', '゛'}, {'ツ', '゙'}, {'ツ', '゛'}, {'づ'}, {'っ', '゙'}, {'ッ', '゙'}, {'ッ', '゙'}, {'っ', '゛'}, {'ッ', '゛'}, {'ッ', '゛'}}}, {[][]rune{{'ぶ'}, {'ブ'}, {'フ', '゙'}, {'ヴ'}, {'ウ', '゙'}, {'う', '゛'}, {'う', '゙'}, {'ウ', '゙'}, {'ゥ', '゙'}, {'ぅ', '゙'}, {'ふ', '゙'}, {'フ', '゙'}, {'フ', '゛'}}}, {[][]rune{{'ぢ'}, {'ヂ'}, {'チ', '゙'}, {'ち', '゙'}, {'チ', '゙'}, {'ち', '゛'}, {'チ', '゛'}, {'チ', '゛'}, {'千', '゛'}, {'千', '゙'}}}, {[][]rune{{'だ'}, {'ダ'}, {'タ', '゙'}, {'た', '゙'}, {'タ', '゙'}, {'夕', '゙'}, {'た', '゛'}, {'タ', '゛'}, {'タ', '゛'}, {'夕', '゛'}}}, {[][]rune{{'ぞ'}, {'ゾ'}, {'ソ', '゙'}, {'そ', '゙'}, {'ソ', '゙'}, {'そ', '゛'}, {'ソ', '゛'}, {'ソ', '゛'}, {'ン', '゙'}, {'ン', '゛'}, {'ン', '゛'}, {'ン', '゙'}, {'リ', '゙'}, {'リ', '゙'}, {'リ', '゛'}, {'リ', '゛'}}}, {[][]rune{{'ぜ'}, {'セ', '゙'}, {'せ', '゙'}, {'セ', '゙'}, {'せ', '゛'}, {'セ', '゛'}, {'セ', '゛'}, {'ゼ'}}}, {[][]rune{{'ず'}, {'ズ'}, {'ス', '゙'}, {'す', '゙'}, {'ス', '゙'}, {'す', '゛'}, {'ス', '゛'}, {'ス', '゛'}}}, {[][]rune{{'じ'}, {'ジ'}, {'シ', '゙'}, {'し', '゙'}, {'シ', '゙'}, {'し', '゛'}, {'シ', '゛'}, {'シ', '゛'}}}, {[][]rune{{'ざ'}, {'ザ'}, {'サ', '゙'}, {'さ', '゙'}, {'サ', '゙'}, {'さ', '゛'}, {'サ', '゛'}, {'サ', '゛'}}}, {[][]rune{{'ご'}, {'ゴ'}, {'コ', '゙'}, {'こ', '゙'}, {'コ', '゙'}, {'こ', '゛'}, {'コ', '゛'}, {'コ', '゛'}}}, {[][]rune{{'げ'}, {'ゲ'}, {'ケ', '゙'}, {'け', '゙'}, {'ケ', '゙'}, {'け', '゛'}, {'ケ', '゛'}, {'ケ', '゛'}, {'ヶ', '゙'}, {'ヶ', '゛'}}}, {[][]rune{{'ぐ'}, {'グ'}, {'ク', '゙'}, {'く', '゙'}, {'ク', '゙'}, {'く', '゛'}, {'ク', '゛'}, {'ク', '゛'}}}, {[][]rune{{'ぎ'}, {'ギ'}, {'キ', '゙'}, {'き', '゙'}, {'キ', '゙'}, {'き', '゛'}, {'キ', '゛'}, {'キ', '゛'}}}, {[][]rune{{'が'}, {'ガ'}, {'カ', '゙'}, {'ヵ', '゙'}, {'カ', '゙'}, {'か', '゙'}, {'力', '゙'}, {'ヵ', '゛'}, {'カ', '゛'}, {'か', '゛'}, {'力', '゛'}, {'カ', '゛'}}}, {[][]rune{{'を'}, {'ヲ'}, {'ヲ'}}}, {[][]rune{{'わ'}, {'ワ'}, {'ワ'}, {'ヮ'}}}, {[][]rune{{'ろ'}, {'ロ'}, {'ロ'}, {'□'}, {'口'}}}, {[][]rune{{'れ'}, {'レ'}, {'レ'}}}, {[][]rune{{'る'}, {'ル'}, {'ル'}}}, {[][]rune{{'り'}, {'リ'}, {'リ'}}}, {[][]rune{{'ら'}, {'ラ'}, {'ラ'}}}, {[][]rune{{'よ'}, {'ヨ'}, {'ヨ'}, {'ョ'}, {'ょ'}, {'ョ'}}}, {[][]rune{{'ゆ'}, {'ユ'}, {'ユ'}, {'ュ'}, {'ゅ'}, {'ュ'}}}, {[][]rune{{'や'}, {'ヤ'}, {'ヤ'}, {'ャ'}, {'ゃ'}, {'ャ'}}}, {[][]rune{{'も'}, {'モ'}, {'モ'}}}, {[][]rune{{'め'}, {'メ'}, {'メ'}, {'M', 'E'}}}, {[][]rune{{'む'}, {'ム'}, {'ム'}}}, {[][]rune{{'み'}, {'ミ'}, {'ミ'}}}, {[][]rune{{'ま'}, {'マ'}, {'マ'}}}, {[][]rune{{'ほ'}, {'ホ'}, {'ホ'}}}, {[][]rune{{'へ'}, {'ヘ'}, {'ヘ'}}}, {[][]rune{{'ふ'}, {'フ'}, {'フ'}}}, {[][]rune{{'ひ'}, {'ヒ'}, {'ヒ'}}}, {[][]rune{{'は'}, {'ハ'}, {'ハ'}, {'八'}}}, {[][]rune{{'の'}, {'ノ'}, {'ノ'}}}, {[][]rune{{'ね'}, {'ネ'}, {'ネ'}}}, {[][]rune{{'ぬ'}, {'ヌ'}, {'ヌ'}}}, {[][]rune{{'に'}, {'ニ'}, {'ニ'}, {'二'}}}, {[][]rune{{'な'}, {'ナ'}, {'ナ'}}}, {[][]rune{{'と'}, {'ト'}, {'ト'}, {'┣'}, {'├'}}}, {[][]rune{{'て'}, {'テ'}, {'テ'}, {'〒'}, {'乙'}}}, {[][]rune{{'つ'}, {'ツ'}, {'ツ'}, {'っ'}, {'ッ'}, {'ッ'}}}, {[][]rune{{'ち'}, {'チ'}, {'チ'}, {'千'}}}, {[][]rune{{'た'}, {'タ'}, {'タ'}, {'夕'}}}, {[][]rune{{'そ'}, {'ソ'}, {'ソ'}}}, {[][]rune{{'せ'}, {'セ'}, {'セ'}}}, {[][]rune{{'す'}, {'ス'}, {'ス'}}}, {[][]rune{{'し'}, {'シ'}, {'シ'}}}, {[][]rune{{'さ'}, {'サ'}, {'サ'}}}, {[][]rune{{'こ'}, {'コ'}, {'コ'}}}, {[][]rune{{'け'}, {'ケ'}, {'ケ'}, {'ヶ'}}}, {[][]rune{{'く'}, {'ク'}, {'ク'}}}, {[][]rune{{'き'}, {'キ'}, {'キ'}}}, {[][]rune{{'か'}, {'カ'}, {'カ'}, {'ヵ'}, {'力'}}}, {[][]rune{{'お'}, {'オ'}, {'オ'}, {'ォ'}, {'ぉ'}, {'ォ'}}}, {[][]rune{{'え'}, {'エ'}, {'エ'}, {'ェ'}, {'ぇ'}, {'ェ'}, {'工'}}}, {[][]rune{{'う'}, {'ウ'}, {'ウ'}, {'ゥ'}, {'ぅ'}, {'ゥ'}}}, {[][]rune{{'い'}, {'イ'}, {'イ'}, {'ィ'}, {'ぃ'}, {'ィ'}}}, {[][]rune{{'あ'}, {'ア'}, {'ァ'}, {'ア'}, {'ぁ'}, {'ァ'}}}, {[][]rune{{'ー'}, {'―'}, {'‐'}, {'-'}, {'-'}, {'ー'}, {'一'}}}, {[][]rune{{'9'}, {'9'}}}, {[][]rune{{'8'}, {'8'}}}, {[][]rune{{'7'}, {'7'}}}, {[][]rune{{'6'}, {'6'}}}, {[][]rune{{'5'}, {'5'}}}, {[][]rune{{'4'}, {'4'}}}, {[][]rune{{'3'}, {'3'}}}, {[][]rune{{'2'}, {'2'}}}, {[][]rune{{'1'}, {'1'}}}, {[][]rune{{'ぽ'}, {'ポ'}, {'ホ', '゚'}, {'ほ', '゚'}, {'ホ', '゚'}, {'ホ', '°'}, {'ほ', '°'}, {'ホ', '°'}}}, {[][]rune{{'ぺ'}, {'ペ'}, {'ヘ', '゚'}, {'へ', '゚'}, {'ヘ', '゚'}, {'ヘ', '°'}, {'へ', '°'}, {'ヘ', '°'}}}, {[][]rune{{'ぷ'}, {'プ'}, {'フ', '゚'}, {'ふ', '゚'}, {'フ', '゚'}, {'フ', '°'}, {'ふ', '°'}, {'フ', '°'}}}, {[][]rune{{'ぴ'}, {'ピ'}, {'ヒ', '゚'}, {'ひ', '゚'}, {'ヒ', '゚'}, {'ヒ', '°'}, {'ひ', '°'}, {'ヒ', '°'}}}, {[][]rune{{'ぱ'}, {'パ'}, {'ハ', '゚'}, {'は', '゚'}, {'ハ', '゚'}, {'ハ', '°'}, {'は', '°'}, {'ハ', '°'}, {'八', '゚'}, {'八', '゜'}}}, {[][]rune{{'z'}, {'z'}, {'Z'}, {'Z'}, {'Ζ'}}}, {[][]rune{{'y'}, {'y'}, {'Y'}, {'Y'}, {'Υ'}, {'У'}, {'у'}}}, {[][]rune{{'x'}, {'x'}, {'X'}, {'X'}, {'Χ'}, {'χ'}, {'Х'}, {'×'}, {'х'}}}, {[][]rune{{'w'}, {'w'}, {'W'}, {'W'}, {'ω'}, {'Ш'}, {'ш'}, {'щ'}}}, {[][]rune{{'v'}, {'v'}, {'V'}, {'V'}, {'ν'}, {'υ'}}}, {[][]rune{{'u'}, {'u'}, {'U'}, {'U'}, {'μ'}, {'∪'}}}, {[][]rune{{'t'}, {'t'}, {'T'}, {'T'}, {'Τ'}, {'τ'}, {'Т'}, {'т'}}}, {[][]rune{{'s'}, {'s'}, {'S'}, {'S'}, {'∫'}, {'$'}, {'$'}}}, {[][]rune{{'r'}, {'r'}, {'R'}, {'R'}, {'Я'}, {'я'}}}, {[][]rune{{'q'}, {'q'}, {'Q'}, {'Q'}}}, {[][]rune{{'p'}, {'p'}, {'P'}, {'P'}, {'Ρ'}, {'ρ'}, {'Р'}, {'р'}}}, {[][]rune{{'o'}, {'o'}, {'O'}, {'O'}, {'○'}, {'Ο'}, {'ο'}, {'О'}, {'о'}, {'◯'}, {'〇'}, {'0'}, {'0'}}}, {[][]rune{{'n'}, {'n'}, {'N'}, {'N'}, {'Ν'}, {'η'}, {'ン'}, {'ん'}, {'ン'}}}, {[][]rune{{'m'}, {'m'}, {'M'}, {'M'}, {'Μ'}, {'М'}, {'м'}}}, {[][]rune{{'l'}, {'l'}, {'L'}, {'L'}, {'|'}}}, {[][]rune{{'k'}, {'k'}, {'K'}, {'K'}, {'Κ'}, {'κ'}, {'К'}, {'к'}}}, {[][]rune{{'j'}, {'j'}, {'J'}, {'J'}}}, {[][]rune{{'i'}, {'i'}, {'I'}, {'I'}, {'Ι'}}}, {[][]rune{{'h'}, {'h'}, {'H'}, {'H'}, {'Η'}, {'Н'}, {'н'}}}, {[][]rune{{'f'}, {'f'}, {'F'}, {'F'}}}, {[][]rune{{'g'}, {'g'}, {'G'}, {'G'}}}, {[][]rune{{'e'}, {'e'}, {'E'}, {'E'}, {'Ε'}, {'ε'}, {'Е'}, {'Ё'}, {'е'}, {'ё'}, {'∈'}}}, {[][]rune{{'d'}, {'d'}, {'D'}, {'D'}}}, {[][]rune{{'c'}, {'c'}, {'C'}, {'С'}, {'с'}, {'C'}, {'℃'}}}, {[][]rune{{'b'}, {'B'}, {'b'}, {'B'}, {'β'}, {'Β'}, {'В'}, {'в'}, {'ъ'}, {'ь'}, {'♭'}}}, {[][]rune{{'\''}, {'’'}}}, {[][]rune{{'a'}, {'A'}, {'a'}, {'A'}, {'α'}, {'@'}, {'@'}, {'а'}, {'Å'}, {'А'}, {'Α'}}}, {[][]rune{{'"'}, {'”'}}}, {[][]rune{{'%'}, {'%'}}}, } for _, smcGroup := range smcData { for _, smcPair := range smcGroup.charGroup { smc.WriteUint16(stringsupport.ToNGWord(string(smcPair[0]))[0]) if len(smcPair) > 1 { smc.WriteUint16(stringsupport.ToNGWord(string(smcPair[1]))[0]) } else { smc.WriteUint16(0) } } smc.WriteUint32(0) } filters.WriteUint32(uint32(len(smc.Data()))) filters.WriteBytes(smc.Data()) filters.WriteNullTerminatedBytes([]byte("nam")) nam := byteframe.NewByteFrame() nam.SetLE() for _, word := range namNGWords { parts := stringsupport.ToNGWord(word) nam.WriteUint32(uint32(len(parts))) for _, part := range parts { nam.WriteUint16(part) nam.WriteInt16(-1) // TODO: figure out how this value relates to corresponding SMC part } nam.WriteUint16(0) nam.WriteInt16(-1) } filters.WriteUint32(uint32(len(nam.Data()))) filters.WriteBytes(nam.Data()) filters.WriteNullTerminatedBytes([]byte("msg")) msg := byteframe.NewByteFrame() msg.SetLE() for _, word := range msgNGWords { parts := stringsupport.ToNGWord(word) msg.WriteUint32(uint32(len(parts))) for _, part := range parts { msg.WriteUint16(part) msg.WriteInt16(-1) } msg.WriteUint16(0) msg.WriteInt16(-1) } filters.WriteUint32(uint32(len(msg.Data()))) filters.WriteBytes(msg.Data()) bf.WriteUint16(uint16(len(filters.Data()))) bf.WriteBytes(filters.Data()) if s.client == VITA || s.client == PS3 || s.client == PS4 { var psnUser string s.server.db.QueryRow("SELECT psn_id FROM users WHERE id = $1", uid).Scan(&psnUser) bf.WriteBytes(stringsupport.PaddedString(psnUser, 20, true)) } bf.WriteUint16(s.server.erupeConfig.DebugOptions.CapLink.Values[0]) if s.server.erupeConfig.DebugOptions.CapLink.Values[0] == 51728 { bf.WriteUint16(s.server.erupeConfig.DebugOptions.CapLink.Values[1]) if s.server.erupeConfig.DebugOptions.CapLink.Values[1] == 20000 || s.server.erupeConfig.DebugOptions.CapLink.Values[1] == 20002 { ps.Uint16(bf, s.server.erupeConfig.DebugOptions.CapLink.Key, false) } } caStruct := []struct { Unk0 uint8 Unk1 uint32 Unk2 string }{} bf.WriteUint8(uint8(len(caStruct))) for i := range caStruct { bf.WriteUint8(caStruct[i].Unk0) bf.WriteUint32(caStruct[i].Unk1) ps.Uint8(bf, caStruct[i].Unk2, false) } bf.WriteUint16(s.server.erupeConfig.DebugOptions.CapLink.Values[2]) bf.WriteUint16(s.server.erupeConfig.DebugOptions.CapLink.Values[3]) bf.WriteUint16(s.server.erupeConfig.DebugOptions.CapLink.Values[4]) if s.server.erupeConfig.DebugOptions.CapLink.Values[2] == 51729 && s.server.erupeConfig.DebugOptions.CapLink.Values[3] == 1 && s.server.erupeConfig.DebugOptions.CapLink.Values[4] == 20000 { ps.Uint16(bf, fmt.Sprintf(`%s:%d`, s.server.erupeConfig.DebugOptions.CapLink.Host, s.server.erupeConfig.DebugOptions.CapLink.Port), false) } bf.WriteUint32(uint32(s.server.getReturnExpiry(uid).Unix())) bf.WriteUint32(0) tickets := []uint32{ s.server.erupeConfig.GameplayOptions.MezFesSoloTickets, s.server.erupeConfig.GameplayOptions.MezFesGroupTickets, } stalls := []uint8{ 10, 3, 6, 9, 4, 8, 5, 7, } if s.server.erupeConfig.GameplayOptions.MezFesSwitchMinigame { stalls[4] = 2 } // We can just use the start timestamp as the event ID bf.WriteUint32(uint32(channelserver.TimeWeekStart().Unix())) // Start time bf.WriteUint32(uint32(channelserver.TimeWeekNext().Add(-time.Duration(s.server.erupeConfig.GameplayOptions.MezFesDuration) * time.Second).Unix())) // End time bf.WriteUint32(uint32(channelserver.TimeWeekNext().Unix())) bf.WriteUint8(uint8(len(tickets))) for i := range tickets { bf.WriteUint32(tickets[i]) } bf.WriteUint8(uint8(len(stalls))) for i := range stalls { bf.WriteUint8(stalls[i]) } return bf.Data() }