From 25bb69b6df7815b075a9e1f62d980bd4e4a2c039 Mon Sep 17 00:00:00 2001 From: wish Date: Mon, 11 Mar 2024 23:31:39 +1100 Subject: [PATCH 01/23] test weekly stamp fix --- schemas/patch-schema/fix-weekly-stamps.sql | 6 +++++ server/channelserver/handlers.go | 28 ++++++++++++---------- 2 files changed, 22 insertions(+), 12 deletions(-) create mode 100644 schemas/patch-schema/fix-weekly-stamps.sql diff --git a/schemas/patch-schema/fix-weekly-stamps.sql b/schemas/patch-schema/fix-weekly-stamps.sql new file mode 100644 index 000000000..30f551e5c --- /dev/null +++ b/schemas/patch-schema/fix-weekly-stamps.sql @@ -0,0 +1,6 @@ +BEGIN; + +ALTER TABLE IF EXISTS public.stamps RENAME hl_next TO hl_checked; +ALTER TABLE IF EXISTS public.stamps RENAME ex_next TO ex_checked; + +END; \ No newline at end of file diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index fb91f096f..565a577cc 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -852,26 +852,29 @@ func handleMsgMhfGetCogInfo(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfCheckWeeklyStamp(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfCheckWeeklyStamp) - weekCurrentStart := TimeWeekStart() - weekNextStart := TimeWeekNext() var total, redeemed, updated uint16 - var nextClaim time.Time - err := s.server.db.QueryRow(fmt.Sprintf("SELECT %s_next FROM stamps WHERE character_id=$1", pkt.StampType), s.charID).Scan(&nextClaim) + var lastCheck time.Time + err := s.server.db.QueryRow(fmt.Sprintf("SELECT %s_checked FROM stamps WHERE character_id=$1", pkt.StampType), s.charID).Scan(&lastCheck) if err != nil { - s.server.db.Exec("INSERT INTO stamps (character_id, hl_next, ex_next) VALUES ($1, $2, $2)", s.charID, weekNextStart) - nextClaim = weekNextStart + lastCheck = TimeAdjusted() + s.server.db.Exec("INSERT INTO stamps (character_id, hl_checked, ex_checked) VALUES ($1, $2, $2)", s.charID, TimeAdjusted()) + } else { + s.server.db.Exec(fmt.Sprintf(`UPDATE stamps SET %s_checked=$1 WHERE character_id=$2`, pkt.StampType), TimeAdjusted(), s.charID) } - if nextClaim.Before(weekCurrentStart) { - s.server.db.Exec(fmt.Sprintf("UPDATE stamps SET %s_total=%s_total+1, %s_next=$1 WHERE character_id=$2", pkt.StampType, pkt.StampType, pkt.StampType), weekNextStart, s.charID) + + if lastCheck.Before(TimeWeekStart()) { + s.server.db.Exec(fmt.Sprintf("UPDATE stamps SET %s_total=%s_total+1 WHERE character_id=$1", pkt.StampType, pkt.StampType), s.charID) updated = 1 } + s.server.db.QueryRow(fmt.Sprintf("SELECT %s_total, %s_redeemed FROM stamps WHERE character_id=$1", pkt.StampType, pkt.StampType), s.charID).Scan(&total, &redeemed) bf := byteframe.NewByteFrame() bf.WriteUint16(total) bf.WriteUint16(redeemed) bf.WriteUint16(updated) - bf.WriteUint32(0) // Unk - bf.WriteUint32(uint32(weekCurrentStart.Unix())) + bf.WriteUint16(0) + bf.WriteUint16(0) + bf.WriteUint32(uint32(TimeWeekStart().Unix())) doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } @@ -879,7 +882,7 @@ func handleMsgMhfExchangeWeeklyStamp(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfExchangeWeeklyStamp) var total, redeemed uint16 var tktStack mhfitem.MHFItemStack - if pkt.Unk1 == 0xA { // Yearly Sub Ex + if pkt.Unk1 == 10 { // 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 = mhfitem.MHFItemStack{Item: mhfitem.MHFItem{ItemID: 2210}, Quantity: 1} } else { @@ -895,7 +898,8 @@ func handleMsgMhfExchangeWeeklyStamp(s *Session, p mhfpacket.MHFPacket) { bf.WriteUint16(total) bf.WriteUint16(redeemed) bf.WriteUint16(0) - bf.WriteUint32(0) // Unk, but has possible values + bf.WriteUint16(tktStack.Item.ItemID) + bf.WriteUint16(tktStack.Quantity) bf.WriteUint32(uint32(TimeWeekStart().Unix())) doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } From def2bc3d2c616d48f160323c9eb50d429cca7e74 Mon Sep 17 00:00:00 2001 From: stratic-dev Date: Tue, 12 Mar 2024 23:00:01 +0000 Subject: [PATCH 02/23] initial commit --- .gitignore | 3 +- config.json | 7 ++- config/config.go | 30 ++++++---- schemas/patch-schema/screenshots.sql | 13 +++++ server/channelserver/handlers_bbs.go | 33 ++++++++++- server/channelserver/sys_channel_server.go | 9 +++ server/discordbot/discord_bot.go | 14 +++-- server/signv2server/endpoints.go | 68 ++++++++++++++++++++++ server/signv2server/signv2_server.go | 3 +- 9 files changed, 157 insertions(+), 23 deletions(-) create mode 100644 schemas/patch-schema/screenshots.sql diff --git a/.gitignore b/.gitignore index 4101960d2..493cbb0f6 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,5 @@ savedata/*/ *.exe *.lnk *.bat -/docker/db-data \ No newline at end of file +/docker/db-data +sreenshots/* \ No newline at end of file diff --git a/config.json b/config.json index 0e081c4e5..2943915ba 100644 --- a/config.json +++ b/config.json @@ -9,7 +9,12 @@ ], "PatchServerManifest": "", "PatchServerFile": "", - "ScreenshotAPIURL": "", + "Screenshots":{ + "Enabled":true, + "Host":"127.0.0.1", + "Port":8080, + "OutputDir":"screenshots" + }, "DeleteOnSaveCorruption": false, "ClientMode": "ZZ", "QuestCacheExpiry": 300, diff --git a/config/config.go b/config/config.go index 6f6d4d2af..c88bd3912 100644 --- a/config/config.go +++ b/config/config.go @@ -75,7 +75,6 @@ type Config struct { LoginNotices []string // MHFML string of the login notices displayed PatchServerManifest string // Manifest patch server override PatchServerFile string // File patch server override - ScreenshotAPIURL string // Destination for screenshots uploaded to BBS DeleteOnSaveCorruption bool // Attempts to save corrupted data will flag the save for deletion ClientMode string RealClientMode Mode @@ -87,16 +86,18 @@ type Config struct { EarthID int32 EarthMonsters []int32 SaveDumps SaveDumpOptions - DebugOptions DebugOptions - GameplayOptions GameplayOptions - Discord Discord - Commands []Command - Courses []Course - Database Database - Sign Sign - SignV2 SignV2 - Channel Channel - Entrance Entrance + Screenshots ScreenshotsOptions + + DebugOptions DebugOptions + GameplayOptions GameplayOptions + Discord Discord + Commands []Command + Courses []Course + Database Database + Sign Sign + SignV2 SignV2 + Channel Channel + Entrance Entrance } type SaveDumpOptions struct { @@ -105,6 +106,13 @@ type SaveDumpOptions struct { OutputDir string } +type ScreenshotsOptions struct { + Enabled bool + Host string // Destination for screenshots uploaded to BBS + Port uint32 // Port for screenshots API + OutputDir string +} + // DebugOptions holds various debug/temporary options for use while developing Erupe. type DebugOptions struct { CleanDB bool // Automatically wipes the DB on server reset. diff --git a/schemas/patch-schema/screenshots.sql b/schemas/patch-schema/screenshots.sql new file mode 100644 index 000000000..345fcda13 --- /dev/null +++ b/schemas/patch-schema/screenshots.sql @@ -0,0 +1,13 @@ +BEGIN; + +CREATE TABLE public.screenshots +( + id serial PRIMARY KEY, + article_id TEXT NOT NULL, + discord_message_id TEXT, + char_id integer NOT NULL, + title TEXT NOT NULL, + description TEXT NOT NULL, + discord_img_url TEXT, + ); +END; \ No newline at end of file diff --git a/server/channelserver/handlers_bbs.go b/server/channelserver/handlers_bbs.go index 222a8eadc..69abc542d 100644 --- a/server/channelserver/handlers_bbs.go +++ b/server/channelserver/handlers_bbs.go @@ -8,6 +8,7 @@ import ( ) func handleMsgMhfGetBbsUserStatus(s *Session, p mhfpacket.MHFPacket) { + //Post Screenshot pauses till this succeedes pkt := p.(*mhfpacket.MsgMhfGetBbsUserStatus) bf := byteframe.NewByteFrame() bf.WriteUint32(200) @@ -32,10 +33,36 @@ func handleMsgMhfApplyBbsArticle(s *Session, p mhfpacket.MHFPacket) { bf := byteframe.NewByteFrame() articleToken := token.Generate(40) bf.WriteUint32(200) - bf.WriteUint32(80) + bf.WriteUint32(s.server.erupeConfig.Screenshots.Port) bf.WriteUint32(0) bf.WriteUint32(0) bf.WriteBytes(stringsupport.PaddedString(articleToken, 64, false)) - bf.WriteBytes(stringsupport.PaddedString(s.server.erupeConfig.ScreenshotAPIURL, 64, false)) - doAckBufSucceed(s, pkt.AckHandle, bf.Data()) + bf.WriteBytes(stringsupport.PaddedString(s.server.erupeConfig.Screenshots.Host, 64, false)) + + if s.server.erupeConfig.SaveDumps.Enabled && s.server.erupeConfig.Discord.Enabled { + messageId := s.server.DiscordScreenShotSend(pkt.Name, pkt.Title, pkt.Description) // TODO: send and get back message id store in db + + _, err := s.server.db.Exec("INSERT INTO public.screenshots (article_id,discord_message_id,char_id,title,description) VALUES ($1,$2,$3,$4,$5)", articleToken, messageId, s.charID, pkt.Title, pkt.Description) + if err != nil { + doAckBufFail(s, pkt.AckHandle, make([]byte, 4)) + } else { + + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) + s.server.BroadcastChatMessage("Screenshot has been sent to discord") + + } + + } else if s.server.erupeConfig.SaveDumps.Enabled { + _, err := s.server.db.Exec("INSERT INTO public.screenshots (article_id,char_id,title,description) VALUES ($1,$2,$3,$4)", articleToken, s.charID, pkt.Title, pkt.Description) + if err != nil { + doAckBufFail(s, pkt.AckHandle, make([]byte, 4)) + } else { + s.server.BroadcastChatMessage("Screenshot has been sent to server") + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) + + } + } else { + doAckBufFail(s, pkt.AckHandle, make([]byte, 4)) + s.server.BroadcastChatMessage("No destination for screenshots have been configured by the host") + } } diff --git a/server/channelserver/sys_channel_server.go b/server/channelserver/sys_channel_server.go index 19bd04123..6d2c7c39d 100644 --- a/server/channelserver/sys_channel_server.go +++ b/server/channelserver/sys_channel_server.go @@ -367,6 +367,15 @@ func (s *Server) DiscordChannelSend(charName string, content string) { } } +func (s *Server) DiscordScreenShotSend(charName string, title string, description string) string { + if s.erupeConfig.Discord.Enabled && s.discordBot != nil { + message := fmt.Sprintf("**%s**: %s - %s", charName, title, description) + mesageId, _ := s.discordBot.RealtimeChannelSend(message) + return mesageId + } + return "" +} + func (s *Server) FindSessionByCharID(charID uint32) *Session { for _, c := range s.Channels { for _, session := range c.sessions { diff --git a/server/discordbot/discord_bot.go b/server/discordbot/discord_bot.go index a9b327cc3..9c824684f 100644 --- a/server/discordbot/discord_bot.go +++ b/server/discordbot/discord_bot.go @@ -1,10 +1,12 @@ package discordbot import ( - "erupe-ce/config" + "errors" + _config "erupe-ce/config" + "regexp" + "github.com/bwmarrin/discordgo" "go.uber.org/zap" - "regexp" ) var Commands = []*discordgo.ApplicationCommand{ @@ -104,14 +106,14 @@ func (bot *DiscordBot) NormalizeDiscordMessage(message string) string { return result } -func (bot *DiscordBot) RealtimeChannelSend(message string) (err error) { +func (bot *DiscordBot) RealtimeChannelSend(message string) (messageId string, err error) { if bot.RelayChannel == nil { - return + return "", errors.New("RelayChannel is nil") } - _, err = bot.Session.ChannelMessageSend(bot.RelayChannel.ID, message) + msg, err := bot.Session.ChannelMessageSend(bot.RelayChannel.ID, message) - return + return msg.ID, err } func ReplaceTextAll(text string, regex *regexp.Regexp, handler func(input string) string) string { diff --git a/server/signv2server/endpoints.go b/server/signv2server/endpoints.go index b3ac00254..7647ce743 100644 --- a/server/signv2server/endpoints.go +++ b/server/signv2server/endpoints.go @@ -6,7 +6,12 @@ import ( "errors" _config "erupe-ce/config" "erupe-ce/server/channelserver" + "fmt" + "image" + "image/jpeg" "net/http" + "os" + "path/filepath" "strings" "time" @@ -286,3 +291,66 @@ func (s *Server) ExportSave(w http.ResponseWriter, r *http.Request) { w.Header().Add("Content-Type", "application/json") json.NewEncoder(w).Encode(save) } + +func (s *Server) ScreenShot(w http.ResponseWriter, r *http.Request) { + if !s.erupeConfig.SaveDumps.Enabled { + http.Error(w, "Screenshots not enabled in Config", http.StatusBadRequest) + + return + } else { + + if r.Method != http.MethodPost { + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + return + } + // Get File from Request + file, _, err := r.FormFile("img") + if err != nil { + http.Error(w, "No valid file uploaded", http.StatusBadRequest) + return + } + token := r.FormValue("token") + if token == "" { + http.Error(w, "Token not specified cannot continue", http.StatusBadRequest) + return + } + + // Validate file + img, _, err := image.Decode(file) + if err != nil { + http.Error(w, "Invalid image file", http.StatusBadRequest) + return + } + + dir := filepath.Join(s.erupeConfig.Screenshots.OutputDir) + path := filepath.Join(s.erupeConfig.Screenshots.OutputDir, fmt.Sprintf("%s.jpg", token)) + _, err = os.Stat(dir) + if err != nil { + if os.IsNotExist(err) { + err = os.MkdirAll(dir, os.ModePerm) + if err != nil { + s.logger.Error("Error writing screenshot, could not create folder") + return + } + } else { + s.logger.Error("Error writing screenshot") + return + } + } + // Create or open the output file + outputFile, err := os.Create(path) + if err != nil { + panic(err) + } + defer outputFile.Close() + + // Encode the image and write it to the file + err = jpeg.Encode(outputFile, img, &jpeg.Options{}) + if err != nil { + panic(err) + } + if err != nil { + s.logger.Error("Error writing screenshot, could not write file", zap.Error(err)) + } + } +} diff --git a/server/signv2server/signv2_server.go b/server/signv2server/signv2_server.go index fedbabba2..3b278a3a7 100644 --- a/server/signv2server/signv2_server.go +++ b/server/signv2server/signv2_server.go @@ -2,7 +2,7 @@ package signv2server import ( "context" - "erupe-ce/config" + _config "erupe-ce/config" "fmt" "net/http" "os" @@ -52,6 +52,7 @@ func (s *Server) Start() error { r.HandleFunc("/character/create", s.CreateCharacter) r.HandleFunc("/character/delete", s.DeleteCharacter) r.HandleFunc("/character/export", s.ExportSave) + r.HandleFunc("/api/ss/bbs/upload.php", s.ScreenShot) handler := handlers.CORS(handlers.AllowedHeaders([]string{"Content-Type"}))(r) s.httpServer.Handler = handlers.LoggingHandler(os.Stdout, handler) s.httpServer.Addr = fmt.Sprintf(":%d", s.erupeConfig.SignV2.Port) From 3797438ca2c13d3ebf5889cbbf815d645f0d06e3 Mon Sep 17 00:00:00 2001 From: stratic-dev Date: Fri, 15 Mar 2024 00:54:18 +0000 Subject: [PATCH 03/23] No database --- .gitignore | 2 +- config.json | 3 +- config/config.go | 9 +-- schemas/patch-schema/screenshots.sql | 13 ---- server/channelserver/handlers_bbs.go | 47 +++++-------- server/channelserver/sys_channel_server.go | 9 ++- server/discordbot/discord_bot.go | 10 ++- server/signv2server/endpoints.go | 81 ++++++++++++++++------ server/signv2server/signv2_server.go | 1 + 9 files changed, 94 insertions(+), 81 deletions(-) delete mode 100644 schemas/patch-schema/screenshots.sql diff --git a/.gitignore b/.gitignore index 493cbb0f6..5b569b1c2 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,4 @@ savedata/*/ *.lnk *.bat /docker/db-data -sreenshots/* \ No newline at end of file +screenshots/* \ No newline at end of file diff --git a/config.json b/config.json index 2943915ba..a4ef568cf 100644 --- a/config.json +++ b/config.json @@ -13,7 +13,8 @@ "Enabled":true, "Host":"127.0.0.1", "Port":8080, - "OutputDir":"screenshots" + "OutputDir":"screenshots", + "UploadQuality":100 }, "DeleteOnSaveCorruption": false, "ClientMode": "ZZ", diff --git a/config/config.go b/config/config.go index c88bd3912..6e86c60ed 100644 --- a/config/config.go +++ b/config/config.go @@ -107,10 +107,11 @@ type SaveDumpOptions struct { } type ScreenshotsOptions struct { - Enabled bool - Host string // Destination for screenshots uploaded to BBS - Port uint32 // Port for screenshots API - OutputDir string + Enabled bool + Host string // Destination for screenshots uploaded to BBS + Port uint32 // Port for screenshots API + OutputDir string + UploadQuality int //Determines the upload quality to the server } // DebugOptions holds various debug/temporary options for use while developing Erupe. diff --git a/schemas/patch-schema/screenshots.sql b/schemas/patch-schema/screenshots.sql deleted file mode 100644 index 345fcda13..000000000 --- a/schemas/patch-schema/screenshots.sql +++ /dev/null @@ -1,13 +0,0 @@ -BEGIN; - -CREATE TABLE public.screenshots -( - id serial PRIMARY KEY, - article_id TEXT NOT NULL, - discord_message_id TEXT, - char_id integer NOT NULL, - title TEXT NOT NULL, - description TEXT NOT NULL, - discord_img_url TEXT, - ); -END; \ No newline at end of file diff --git a/server/channelserver/handlers_bbs.go b/server/channelserver/handlers_bbs.go index 69abc542d..d991ee67a 100644 --- a/server/channelserver/handlers_bbs.go +++ b/server/channelserver/handlers_bbs.go @@ -7,62 +7,47 @@ import ( "erupe-ce/network/mhfpacket" ) +// Handler BBS handles all the interactions with the for the screenshot sending to bulitin board functionality. For it to work it requires the API to be hosted somehwere. This implementation supports discord. + +// Checks the status of the user to see if they can use Bulitin Board yet func handleMsgMhfGetBbsUserStatus(s *Session, p mhfpacket.MHFPacket) { //Post Screenshot pauses till this succeedes pkt := p.(*mhfpacket.MsgMhfGetBbsUserStatus) bf := byteframe.NewByteFrame() - bf.WriteUint32(200) + bf.WriteUint32(200) //HTTP Status Codes //200 Success //404 You wont be able to post for a certain amount of time after creating your character //401/500 A error occured server side bf.WriteUint32(0) bf.WriteUint32(0) bf.WriteUint32(0) doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } +// Checks the status of Bultin Board Server to see if authenticated func handleMsgMhfGetBbsSnsStatus(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfGetBbsSnsStatus) bf := byteframe.NewByteFrame() - bf.WriteUint32(200) - bf.WriteUint32(401) - bf.WriteUint32(401) + bf.WriteUint32(200) //200 Success //4XX Authentication has expired Please re-authenticate //5XX + bf.WriteUint32(401) //unk http status? + bf.WriteUint32(401) //unk http status? bf.WriteUint32(0) doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } +// Tells the game client what host port and gives the bultin board article a token func handleMsgMhfApplyBbsArticle(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfApplyBbsArticle) bf := byteframe.NewByteFrame() articleToken := token.Generate(40) - bf.WriteUint32(200) + + bf.WriteUint32(200) //http status //200 success //4XX An error occured server side bf.WriteUint32(s.server.erupeConfig.Screenshots.Port) bf.WriteUint32(0) bf.WriteUint32(0) bf.WriteBytes(stringsupport.PaddedString(articleToken, 64, false)) bf.WriteBytes(stringsupport.PaddedString(s.server.erupeConfig.Screenshots.Host, 64, false)) - - if s.server.erupeConfig.SaveDumps.Enabled && s.server.erupeConfig.Discord.Enabled { - messageId := s.server.DiscordScreenShotSend(pkt.Name, pkt.Title, pkt.Description) // TODO: send and get back message id store in db - - _, err := s.server.db.Exec("INSERT INTO public.screenshots (article_id,discord_message_id,char_id,title,description) VALUES ($1,$2,$3,$4,$5)", articleToken, messageId, s.charID, pkt.Title, pkt.Description) - if err != nil { - doAckBufFail(s, pkt.AckHandle, make([]byte, 4)) - } else { - - doAckBufSucceed(s, pkt.AckHandle, bf.Data()) - s.server.BroadcastChatMessage("Screenshot has been sent to discord") - - } - - } else if s.server.erupeConfig.SaveDumps.Enabled { - _, err := s.server.db.Exec("INSERT INTO public.screenshots (article_id,char_id,title,description) VALUES ($1,$2,$3,$4)", articleToken, s.charID, pkt.Title, pkt.Description) - if err != nil { - doAckBufFail(s, pkt.AckHandle, make([]byte, 4)) - } else { - s.server.BroadcastChatMessage("Screenshot has been sent to server") - doAckBufSucceed(s, pkt.AckHandle, bf.Data()) - - } - } else { - doAckBufFail(s, pkt.AckHandle, make([]byte, 4)) - s.server.BroadcastChatMessage("No destination for screenshots have been configured by the host") + //pkt.unk1[3] == Changes sometimes? + if s.server.erupeConfig.Screenshots.Enabled && s.server.erupeConfig.Discord.Enabled { + s.server.DiscordScreenShotSend(pkt.Name, pkt.Title, pkt.Description, articleToken) } + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) + } diff --git a/server/channelserver/sys_channel_server.go b/server/channelserver/sys_channel_server.go index 6d2c7c39d..a0d1fe1b7 100644 --- a/server/channelserver/sys_channel_server.go +++ b/server/channelserver/sys_channel_server.go @@ -367,13 +367,12 @@ func (s *Server) DiscordChannelSend(charName string, content string) { } } -func (s *Server) DiscordScreenShotSend(charName string, title string, description string) string { +func (s *Server) DiscordScreenShotSend(charName string, title string, description string, articleToken string) { if s.erupeConfig.Discord.Enabled && s.discordBot != nil { - message := fmt.Sprintf("**%s**: %s - %s", charName, title, description) - mesageId, _ := s.discordBot.RealtimeChannelSend(message) - return mesageId + imageUrl := fmt.Sprintf("%s:%d/api/ss/bbs/%s", s.erupeConfig.Screenshots.Host, s.erupeConfig.Screenshots.Port, articleToken) + message := fmt.Sprintf("**%s**: %s - %s %s", charName, title, description, imageUrl) + s.discordBot.RealtimeChannelSend(message) } - return "" } func (s *Server) FindSessionByCharID(charID uint32) *Session { diff --git a/server/discordbot/discord_bot.go b/server/discordbot/discord_bot.go index 9c824684f..303cbc630 100644 --- a/server/discordbot/discord_bot.go +++ b/server/discordbot/discord_bot.go @@ -1,7 +1,6 @@ package discordbot import ( - "errors" _config "erupe-ce/config" "regexp" @@ -106,16 +105,15 @@ func (bot *DiscordBot) NormalizeDiscordMessage(message string) string { return result } -func (bot *DiscordBot) RealtimeChannelSend(message string) (messageId string, err error) { +func (bot *DiscordBot) RealtimeChannelSend(message string) (err error) { if bot.RelayChannel == nil { - return "", errors.New("RelayChannel is nil") + return } - msg, err := bot.Session.ChannelMessageSend(bot.RelayChannel.ID, message) + _, err = bot.Session.ChannelMessageSend(bot.RelayChannel.ID, message) - return msg.ID, err + return } - func ReplaceTextAll(text string, regex *regexp.Regexp, handler func(input string) string) string { result := regex.ReplaceAllFunc([]byte(text), func(s []byte) []byte { input := regex.ReplaceAllString(string(s), `$1`) diff --git a/server/signv2server/endpoints.go b/server/signv2server/endpoints.go index 7647ce743..8200cd824 100644 --- a/server/signv2server/endpoints.go +++ b/server/signv2server/endpoints.go @@ -3,18 +3,21 @@ package signv2server import ( "database/sql" "encoding/json" + "encoding/xml" "errors" _config "erupe-ce/config" "erupe-ce/server/channelserver" "fmt" "image" "image/jpeg" + "io" "net/http" "os" "path/filepath" "strings" "time" + "github.com/gorilla/mux" "github.com/lib/pq" "go.uber.org/zap" "golang.org/x/crypto/bcrypt" @@ -291,35 +294,63 @@ func (s *Server) ExportSave(w http.ResponseWriter, r *http.Request) { w.Header().Add("Content-Type", "application/json") json.NewEncoder(w).Encode(save) } - -func (s *Server) ScreenShot(w http.ResponseWriter, r *http.Request) { - if !s.erupeConfig.SaveDumps.Enabled { - http.Error(w, "Screenshots not enabled in Config", http.StatusBadRequest) - +func (s *Server) ScreenShotGet(w http.ResponseWriter, r *http.Request) { + // Get the 'id' parameter from the URL + vars := mux.Vars(r) + id := vars["id"] + // Open the image file + path := filepath.Join(s.erupeConfig.Screenshots.OutputDir, fmt.Sprintf("%s.jpg", id)) + file, err := os.Open(path) + if err != nil { + http.Error(w, "Image not found", http.StatusNotFound) return + } + defer file.Close() + // Set content type header to image/jpeg + w.Header().Set("Content-Type", "image/jpeg") + // Copy the image content to the response writer + if _, err := io.Copy(w, file); err != nil { + http.Error(w, "Unable to send image", http.StatusInternalServerError) + return + } +} +func (s *Server) ScreenShot(w http.ResponseWriter, r *http.Request) { + + // Create a struct representing the XML result + type Result struct { + XMLName xml.Name `xml:"result"` + Code string `xml:"code"` + } + // Set the Content-Type header to specify that the response is in XML format + w.Header().Set("Content-Type", "text/xml") + result := Result{Code: "200"} + + if !s.erupeConfig.Screenshots.Enabled { + result = Result{Code: "400"} + } else { if r.Method != http.MethodPost { - http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) - return + result = Result{Code: "405"} + } // Get File from Request file, _, err := r.FormFile("img") if err != nil { - http.Error(w, "No valid file uploaded", http.StatusBadRequest) - return + result = Result{Code: "400"} + } token := r.FormValue("token") if token == "" { - http.Error(w, "Token not specified cannot continue", http.StatusBadRequest) - return + result = Result{Code: "400"} + } // Validate file img, _, err := image.Decode(file) if err != nil { - http.Error(w, "Invalid image file", http.StatusBadRequest) - return + result = Result{Code: "400"} + } dir := filepath.Join(s.erupeConfig.Screenshots.OutputDir) @@ -330,27 +361,37 @@ func (s *Server) ScreenShot(w http.ResponseWriter, r *http.Request) { err = os.MkdirAll(dir, os.ModePerm) if err != nil { s.logger.Error("Error writing screenshot, could not create folder") - return + result = Result{Code: "500"} } } else { s.logger.Error("Error writing screenshot") - return + result = Result{Code: "500"} } } // Create or open the output file outputFile, err := os.Create(path) if err != nil { - panic(err) + result = Result{Code: "500"} } defer outputFile.Close() // Encode the image and write it to the file - err = jpeg.Encode(outputFile, img, &jpeg.Options{}) - if err != nil { - panic(err) - } + err = jpeg.Encode(outputFile, img, &jpeg.Options{Quality: s.erupeConfig.Screenshots.UploadQuality}) if err != nil { s.logger.Error("Error writing screenshot, could not write file", zap.Error(err)) + result = Result{Code: "500"} + } + } + // Marshal the struct into XML + xmlData, err := xml.Marshal(result) + if err != nil { + http.Error(w, "Unable to marshal XML", http.StatusInternalServerError) + return + } + + // Write the XML response with a 200 status code + w.WriteHeader(http.StatusOK) + w.Write(xmlData) } diff --git a/server/signv2server/signv2_server.go b/server/signv2server/signv2_server.go index 3b278a3a7..32c852e30 100644 --- a/server/signv2server/signv2_server.go +++ b/server/signv2server/signv2_server.go @@ -53,6 +53,7 @@ func (s *Server) Start() error { r.HandleFunc("/character/delete", s.DeleteCharacter) r.HandleFunc("/character/export", s.ExportSave) r.HandleFunc("/api/ss/bbs/upload.php", s.ScreenShot) + r.HandleFunc("/api/ss/bbs/{id}", s.ScreenShotGet) handler := handlers.CORS(handlers.AllowedHeaders([]string{"Content-Type"}))(r) s.httpServer.Handler = handlers.LoggingHandler(os.Stdout, handler) s.httpServer.Addr = fmt.Sprintf(":%d", s.erupeConfig.SignV2.Port) From 12b3dd1be32d0cac003bc73017d88b566fe77a08 Mon Sep 17 00:00:00 2001 From: stratic-dev Date: Fri, 15 Mar 2024 18:33:23 +0000 Subject: [PATCH 04/23] Add regex --- server/signv2server/endpoints.go | 10 ---------- server/signv2server/signv2_server.go | 2 +- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/server/signv2server/endpoints.go b/server/signv2server/endpoints.go index 8200cd824..3b9fd606f 100644 --- a/server/signv2server/endpoints.go +++ b/server/signv2server/endpoints.go @@ -315,7 +315,6 @@ func (s *Server) ScreenShotGet(w http.ResponseWriter, r *http.Request) { } } func (s *Server) ScreenShot(w http.ResponseWriter, r *http.Request) { - // Create a struct representing the XML result type Result struct { XMLName xml.Name `xml:"result"` @@ -324,33 +323,27 @@ func (s *Server) ScreenShot(w http.ResponseWriter, r *http.Request) { // Set the Content-Type header to specify that the response is in XML format w.Header().Set("Content-Type", "text/xml") result := Result{Code: "200"} - if !s.erupeConfig.Screenshots.Enabled { result = Result{Code: "400"} - } else { if r.Method != http.MethodPost { result = Result{Code: "405"} - } // Get File from Request file, _, err := r.FormFile("img") if err != nil { result = Result{Code: "400"} - } token := r.FormValue("token") if token == "" { result = Result{Code: "400"} - } // Validate file img, _, err := image.Decode(file) if err != nil { result = Result{Code: "400"} - } dir := filepath.Join(s.erupeConfig.Screenshots.OutputDir) @@ -380,9 +373,7 @@ func (s *Server) ScreenShot(w http.ResponseWriter, r *http.Request) { if err != nil { s.logger.Error("Error writing screenshot, could not write file", zap.Error(err)) result = Result{Code: "500"} - } - } // Marshal the struct into XML xmlData, err := xml.Marshal(result) @@ -390,7 +381,6 @@ func (s *Server) ScreenShot(w http.ResponseWriter, r *http.Request) { http.Error(w, "Unable to marshal XML", http.StatusInternalServerError) return } - // Write the XML response with a 200 status code w.WriteHeader(http.StatusOK) w.Write(xmlData) diff --git a/server/signv2server/signv2_server.go b/server/signv2server/signv2_server.go index 32c852e30..74ce2978c 100644 --- a/server/signv2server/signv2_server.go +++ b/server/signv2server/signv2_server.go @@ -53,7 +53,7 @@ func (s *Server) Start() error { r.HandleFunc("/character/delete", s.DeleteCharacter) r.HandleFunc("/character/export", s.ExportSave) r.HandleFunc("/api/ss/bbs/upload.php", s.ScreenShot) - r.HandleFunc("/api/ss/bbs/{id}", s.ScreenShotGet) + r.HandleFunc("/api/ss/bbs/{id:[A-Za-z0-9]+}", s.ScreenShotGet) handler := handlers.CORS(handlers.AllowedHeaders([]string{"Content-Type"}))(r) s.httpServer.Handler = handlers.LoggingHandler(os.Stdout, handler) s.httpServer.Addr = fmt.Sprintf(":%d", s.erupeConfig.SignV2.Port) From 62a2fe9f7300f6985a8547b5019d82e6499cb95a Mon Sep 17 00:00:00 2001 From: stratic-dev Date: Fri, 15 Mar 2024 18:43:33 +0000 Subject: [PATCH 05/23] Added more regex --- server/signv2server/endpoints.go | 17 +++++++++++++---- server/signv2server/signv2_server.go | 2 +- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/server/signv2server/endpoints.go b/server/signv2server/endpoints.go index 3b9fd606f..dd3864d2a 100644 --- a/server/signv2server/endpoints.go +++ b/server/signv2server/endpoints.go @@ -14,6 +14,7 @@ import ( "net/http" "os" "path/filepath" + "regexp" "strings" "time" @@ -297,9 +298,15 @@ func (s *Server) ExportSave(w http.ResponseWriter, r *http.Request) { func (s *Server) ScreenShotGet(w http.ResponseWriter, r *http.Request) { // Get the 'id' parameter from the URL vars := mux.Vars(r) - id := vars["id"] + token := vars["id"] + var tokenPattern = regexp.MustCompile(`[A-Za-z0-9]+`) + + if !tokenPattern.MatchString(token) || token == "" { + http.Error(w, "Not Valid Token", http.StatusBadRequest) + + } // Open the image file - path := filepath.Join(s.erupeConfig.Screenshots.OutputDir, fmt.Sprintf("%s.jpg", id)) + path := filepath.Join(s.erupeConfig.Screenshots.OutputDir, fmt.Sprintf("%s.jpg", token)) file, err := os.Open(path) if err != nil { http.Error(w, "Image not found", http.StatusNotFound) @@ -335,9 +342,11 @@ func (s *Server) ScreenShot(w http.ResponseWriter, r *http.Request) { if err != nil { result = Result{Code: "400"} } + var tokenPattern = regexp.MustCompile(`[A-Za-z0-9]+`) token := r.FormValue("token") - if token == "" { - result = Result{Code: "400"} + if !tokenPattern.MatchString(token) || token == "" { + result = Result{Code: "401"} + } // Validate file diff --git a/server/signv2server/signv2_server.go b/server/signv2server/signv2_server.go index 74ce2978c..32c852e30 100644 --- a/server/signv2server/signv2_server.go +++ b/server/signv2server/signv2_server.go @@ -53,7 +53,7 @@ func (s *Server) Start() error { r.HandleFunc("/character/delete", s.DeleteCharacter) r.HandleFunc("/character/export", s.ExportSave) r.HandleFunc("/api/ss/bbs/upload.php", s.ScreenShot) - r.HandleFunc("/api/ss/bbs/{id:[A-Za-z0-9]+}", s.ScreenShotGet) + r.HandleFunc("/api/ss/bbs/{id}", s.ScreenShotGet) handler := handlers.CORS(handlers.AllowedHeaders([]string{"Content-Type"}))(r) s.httpServer.Handler = handlers.LoggingHandler(os.Stdout, handler) s.httpServer.Addr = fmt.Sprintf(":%d", s.erupeConfig.SignV2.Port) From d123182a2fb56a7630f718d7176dc8ebf39700f3 Mon Sep 17 00:00:00 2001 From: stratic-dev Date: Fri, 15 Mar 2024 19:37:55 +0000 Subject: [PATCH 06/23] Renamed signv2 to api and enabled it by default --- config.json | 4 +-- config/config.go | 18 +++++----- main.go | 22 ++++++------ .../signv2_server.go => api/api_server.go} | 18 +++++----- server/{signv2server => api}/dbutils.go | 18 +++++----- server/{signv2server => api}/endpoints.go | 34 +++++++++---------- 6 files changed, 57 insertions(+), 57 deletions(-) rename server/{signv2server/signv2_server.go => api/api_server.go} (86%) rename server/{signv2server => api}/dbutils.go (81%) rename server/{signv2server => api}/endpoints.go (90%) diff --git a/config.json b/config.json index a4ef568cf..2b836ab03 100644 --- a/config.json +++ b/config.json @@ -195,8 +195,8 @@ "Enabled": true, "Port": 53312 }, - "SignV2": { - "Enabled": false, + "API": { + "Enabled": true, "Port": 8080, "PatchServer": "", "Banners": [], diff --git a/config/config.go b/config/config.go index 6e86c60ed..52642956b 100644 --- a/config/config.go +++ b/config/config.go @@ -95,7 +95,7 @@ type Config struct { Courses []Course Database Database Sign Sign - SignV2 SignV2 + API API Channel Channel Entrance Entrance } @@ -237,29 +237,29 @@ type Sign struct { Port int } -// SignV2 holds the new sign server config -type SignV2 struct { +// API holds server config +type API struct { Enabled bool Port int PatchServer string - Banners []SignV2Banner - Messages []SignV2Message - Links []SignV2Link + Banners []APISignBanner + Messages []APISignMessage + Links []APISignLink } -type SignV2Banner struct { +type APISignBanner struct { Src string `json:"src"` // Displayed image URL Link string `json:"link"` // Link accessed on click } -type SignV2Message struct { +type APISignMessage struct { Message string `json:"message"` // Displayed message Date int64 `json:"date"` // Displayed date Kind int `json:"kind"` // 0 for 'Default', 1 for 'New' Link string `json:"link"` // Link accessed on click } -type SignV2Link struct { +type APISignLink struct { Name string `json:"name"` // Displayed name Icon string `json:"icon"` // Displayed icon. It will be cast as a monochrome color as long as it is transparent. Link string `json:"link"` // Link accessed on click diff --git a/main.go b/main.go index a7d368930..2c776a78c 100644 --- a/main.go +++ b/main.go @@ -10,11 +10,11 @@ import ( "syscall" "time" + "erupe-ce/server/api" "erupe-ce/server/channelserver" "erupe-ce/server/discordbot" "erupe-ce/server/entranceserver" "erupe-ce/server/signserver" - "erupe-ce/server/signv2server" "github.com/jmoiron/sqlx" _ "github.com/lib/pq" @@ -181,21 +181,21 @@ func main() { } // New Sign server - var newSignServer *signv2server.Server - if config.SignV2.Enabled { - newSignServer = signv2server.NewServer( - &signv2server.Config{ + var ApiServer *api.APIServer + if config.API.Enabled { + ApiServer = api.NewAPIServer( + &api.Config{ Logger: logger.Named("sign"), ErupeConfig: _config.ErupeConfig, DB: db, }) - err = newSignServer.Start() + err = ApiServer.Start() if err != nil { - preventClose(fmt.Sprintf("SignV2: Failed to start, %s", err.Error())) + preventClose(fmt.Sprintf("API: Failed to start, %s", err.Error())) } - logger.Info("SignV2: Started successfully") + logger.Info("API: Started successfully") } else { - logger.Info("SignV2: Disabled") + logger.Info("API: Disabled") } var channels []*channelserver.Server @@ -273,8 +273,8 @@ func main() { signServer.Shutdown() } - if config.SignV2.Enabled { - newSignServer.Shutdown() + if config.API.Enabled { + ApiServer.Shutdown() } if config.Entrance.Enabled { diff --git a/server/signv2server/signv2_server.go b/server/api/api_server.go similarity index 86% rename from server/signv2server/signv2_server.go rename to server/api/api_server.go index 32c852e30..3774f3fb8 100644 --- a/server/signv2server/signv2_server.go +++ b/server/api/api_server.go @@ -1,4 +1,4 @@ -package signv2server +package api import ( "context" @@ -21,8 +21,8 @@ type Config struct { ErupeConfig *_config.Config } -// Server is the MHF custom launcher sign server. -type Server struct { +// APIServer is Erupes Standard API interface +type APIServer struct { sync.Mutex logger *zap.Logger erupeConfig *_config.Config @@ -31,9 +31,9 @@ type Server struct { isShuttingDown bool } -// NewServer creates a new Server type. -func NewServer(config *Config) *Server { - s := &Server{ +// NewAPIServer creates a new Server type. +func NewAPIServer(config *Config) *APIServer { + s := &APIServer{ logger: config.Logger, erupeConfig: config.ErupeConfig, db: config.DB, @@ -43,7 +43,7 @@ func NewServer(config *Config) *Server { } // Start starts the server in a new goroutine. -func (s *Server) Start() error { +func (s *APIServer) Start() error { // Set up the routes responsible for serving the launcher HTML, serverlist, unique name check, and JP auth. r := mux.NewRouter() r.HandleFunc("/launcher", s.Launcher) @@ -56,7 +56,7 @@ func (s *Server) Start() error { r.HandleFunc("/api/ss/bbs/{id}", s.ScreenShotGet) handler := handlers.CORS(handlers.AllowedHeaders([]string{"Content-Type"}))(r) s.httpServer.Handler = handlers.LoggingHandler(os.Stdout, handler) - s.httpServer.Addr = fmt.Sprintf(":%d", s.erupeConfig.SignV2.Port) + s.httpServer.Addr = fmt.Sprintf(":%d", s.erupeConfig.API.Port) serveError := make(chan error, 1) go func() { @@ -76,7 +76,7 @@ func (s *Server) Start() error { } // Shutdown exits the server gracefully. -func (s *Server) Shutdown() { +func (s *APIServer) Shutdown() { s.logger.Debug("Shutting down") s.Lock() diff --git a/server/signv2server/dbutils.go b/server/api/dbutils.go similarity index 81% rename from server/signv2server/dbutils.go rename to server/api/dbutils.go index b2d5872bb..fba1bab5c 100644 --- a/server/signv2server/dbutils.go +++ b/server/api/dbutils.go @@ -1,4 +1,4 @@ -package signv2server +package api import ( "context" @@ -10,7 +10,7 @@ import ( "golang.org/x/crypto/bcrypt" ) -func (s *Server) createNewUser(ctx context.Context, username string, password string) (uint32, uint32, error) { +func (s *APIServer) createNewUser(ctx context.Context, username string, password string) (uint32, uint32, error) { // Create salted hash of user password passwordHash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) if err != nil { @@ -32,7 +32,7 @@ func (s *Server) createNewUser(ctx context.Context, username string, password st return id, rights, err } -func (s *Server) createLoginToken(ctx context.Context, uid uint32) (uint32, string, error) { +func (s *APIServer) createLoginToken(ctx context.Context, uid uint32) (uint32, string, error) { loginToken := token.Generate(16) var tid uint32 err := s.db.QueryRowContext(ctx, "INSERT INTO sign_sessions (user_id, token) VALUES ($1, $2) RETURNING id", uid, loginToken).Scan(&tid) @@ -42,7 +42,7 @@ func (s *Server) createLoginToken(ctx context.Context, uid uint32) (uint32, stri return tid, loginToken, nil } -func (s *Server) userIDFromToken(ctx context.Context, token string) (uint32, error) { +func (s *APIServer) userIDFromToken(ctx context.Context, token string) (uint32, error) { var userID uint32 err := s.db.QueryRowContext(ctx, "SELECT user_id FROM sign_sessions WHERE token = $1", token).Scan(&userID) if err == sql.ErrNoRows { @@ -53,7 +53,7 @@ func (s *Server) userIDFromToken(ctx context.Context, token string) (uint32, err return userID, nil } -func (s *Server) createCharacter(ctx context.Context, userID uint32) (Character, error) { +func (s *APIServer) createCharacter(ctx context.Context, userID uint32) (Character, error) { var character Character err := s.db.GetContext(ctx, &character, "SELECT id, name, is_female, weapon_type, hr, gr, last_login FROM characters WHERE is_new_character = true AND user_id = $1 LIMIT 1", @@ -78,7 +78,7 @@ func (s *Server) createCharacter(ctx context.Context, userID uint32) (Character, return character, err } -func (s *Server) deleteCharacter(ctx context.Context, userID uint32, charID uint32) error { +func (s *APIServer) deleteCharacter(ctx context.Context, userID uint32, charID uint32) error { var isNew bool err := s.db.QueryRow("SELECT is_new_character FROM characters WHERE id = $1", charID).Scan(&isNew) if err != nil { @@ -92,7 +92,7 @@ func (s *Server) deleteCharacter(ctx context.Context, userID uint32, charID uint return err } -func (s *Server) getCharactersForUser(ctx context.Context, uid uint32) ([]Character, error) { +func (s *APIServer) getCharactersForUser(ctx context.Context, uid uint32) ([]Character, error) { var characters []Character err := s.db.SelectContext( ctx, &characters, ` @@ -107,7 +107,7 @@ func (s *Server) getCharactersForUser(ctx context.Context, uid uint32) ([]Charac return characters, nil } -func (s *Server) getReturnExpiry(uid uint32) time.Time { +func (s *APIServer) getReturnExpiry(uid uint32) time.Time { var returnExpiry, lastLogin time.Time s.db.Get(&lastLogin, "SELECT COALESCE(last_login, now()) FROM users WHERE id=$1", uid) if time.Now().Add((time.Hour * 24) * -90).After(lastLogin) { @@ -124,7 +124,7 @@ func (s *Server) getReturnExpiry(uid uint32) time.Time { return returnExpiry } -func (s *Server) exportSave(ctx context.Context, uid uint32, cid uint32) (map[string]interface{}, error) { +func (s *APIServer) exportSave(ctx context.Context, uid uint32, cid uint32) (map[string]interface{}, error) { row := s.db.QueryRowxContext(ctx, "SELECT * FROM characters WHERE id=$1 AND user_id=$2", cid, uid) result := make(map[string]interface{}) err := row.MapScan(result) diff --git a/server/signv2server/endpoints.go b/server/api/endpoints.go similarity index 90% rename from server/signv2server/endpoints.go rename to server/api/endpoints.go index dd3864d2a..ef82cecdb 100644 --- a/server/signv2server/endpoints.go +++ b/server/api/endpoints.go @@ -1,4 +1,4 @@ -package signv2server +package api import ( "database/sql" @@ -30,9 +30,9 @@ const ( ) type LauncherResponse struct { - Banners []_config.SignV2Banner `json:"banners"` - Messages []_config.SignV2Message `json:"messages"` - Links []_config.SignV2Link `json:"links"` + Banners []_config.APISignBanner `json:"banners"` + Messages []_config.APISignMessage `json:"messages"` + Links []_config.APISignLink `json:"links"` } type User struct { @@ -75,7 +75,7 @@ type ExportData struct { Character map[string]interface{} `json:"character"` } -func (s *Server) newAuthData(userID uint32, userRights uint32, userTokenID uint32, userToken string, characters []Character) AuthData { +func (s *APIServer) newAuthData(userID uint32, userRights uint32, userTokenID uint32, userToken string, characters []Character) AuthData { resp := AuthData{ CurrentTS: uint32(channelserver.TimeAdjusted().Unix()), ExpiryTS: uint32(s.getReturnExpiry(userID).Unix()), @@ -86,7 +86,7 @@ func (s *Server) newAuthData(userID uint32, userRights uint32, userTokenID uint3 Token: userToken, }, Characters: characters, - PatchServer: s.erupeConfig.SignV2.PatchServer, + PatchServer: s.erupeConfig.API.PatchServer, Notices: []string{}, } if s.erupeConfig.DebugOptions.MaxLauncherHR { @@ -112,16 +112,16 @@ func (s *Server) newAuthData(userID uint32, userRights uint32, userTokenID uint3 return resp } -func (s *Server) Launcher(w http.ResponseWriter, r *http.Request) { +func (s *APIServer) Launcher(w http.ResponseWriter, r *http.Request) { var respData LauncherResponse - respData.Banners = s.erupeConfig.SignV2.Banners - respData.Messages = s.erupeConfig.SignV2.Messages - respData.Links = s.erupeConfig.SignV2.Links + respData.Banners = s.erupeConfig.API.Banners + respData.Messages = s.erupeConfig.API.Messages + respData.Links = s.erupeConfig.API.Links w.Header().Add("Content-Type", "application/json") json.NewEncoder(w).Encode(respData) } -func (s *Server) Login(w http.ResponseWriter, r *http.Request) { +func (s *APIServer) Login(w http.ResponseWriter, r *http.Request) { ctx := r.Context() var reqData struct { Username string `json:"username"` @@ -173,7 +173,7 @@ func (s *Server) Login(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(respData) } -func (s *Server) Register(w http.ResponseWriter, r *http.Request) { +func (s *APIServer) Register(w http.ResponseWriter, r *http.Request) { ctx := r.Context() var reqData struct { Username string `json:"username"` @@ -213,7 +213,7 @@ func (s *Server) Register(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(respData) } -func (s *Server) CreateCharacter(w http.ResponseWriter, r *http.Request) { +func (s *APIServer) CreateCharacter(w http.ResponseWriter, r *http.Request) { ctx := r.Context() var reqData struct { Token string `json:"token"` @@ -242,7 +242,7 @@ func (s *Server) CreateCharacter(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(character) } -func (s *Server) DeleteCharacter(w http.ResponseWriter, r *http.Request) { +func (s *APIServer) DeleteCharacter(w http.ResponseWriter, r *http.Request) { ctx := r.Context() var reqData struct { Token string `json:"token"` @@ -267,7 +267,7 @@ func (s *Server) DeleteCharacter(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(struct{}{}) } -func (s *Server) ExportSave(w http.ResponseWriter, r *http.Request) { +func (s *APIServer) ExportSave(w http.ResponseWriter, r *http.Request) { ctx := r.Context() var reqData struct { Token string `json:"token"` @@ -295,7 +295,7 @@ func (s *Server) ExportSave(w http.ResponseWriter, r *http.Request) { w.Header().Add("Content-Type", "application/json") json.NewEncoder(w).Encode(save) } -func (s *Server) ScreenShotGet(w http.ResponseWriter, r *http.Request) { +func (s *APIServer) ScreenShotGet(w http.ResponseWriter, r *http.Request) { // Get the 'id' parameter from the URL vars := mux.Vars(r) token := vars["id"] @@ -321,7 +321,7 @@ func (s *Server) ScreenShotGet(w http.ResponseWriter, r *http.Request) { return } } -func (s *Server) ScreenShot(w http.ResponseWriter, r *http.Request) { +func (s *APIServer) ScreenShot(w http.ResponseWriter, r *http.Request) { // Create a struct representing the XML result type Result struct { XMLName xml.Name `xml:"result"` From 295ff6537bbf94cd5f9207d4d249c5ce1768131e Mon Sep 17 00:00:00 2001 From: stratic-dev Date: Fri, 15 Mar 2024 20:00:39 +0000 Subject: [PATCH 07/23] Added utils to verify paths --- server/api/endpoints.go | 86 ++++++++++++++++++++++++----------------- server/api/utils.go | 37 ++++++++++++++++++ 2 files changed, 88 insertions(+), 35 deletions(-) create mode 100644 server/api/utils.go diff --git a/server/api/endpoints.go b/server/api/endpoints.go index ef82cecdb..4eaac119e 100644 --- a/server/api/endpoints.go +++ b/server/api/endpoints.go @@ -297,8 +297,7 @@ func (s *APIServer) ExportSave(w http.ResponseWriter, r *http.Request) { } func (s *APIServer) ScreenShotGet(w http.ResponseWriter, r *http.Request) { // Get the 'id' parameter from the URL - vars := mux.Vars(r) - token := vars["id"] + token := mux.Vars(r)["id"] var tokenPattern = regexp.MustCompile(`[A-Za-z0-9]+`) if !tokenPattern.MatchString(token) || token == "" { @@ -306,19 +305,28 @@ func (s *APIServer) ScreenShotGet(w http.ResponseWriter, r *http.Request) { } // Open the image file - path := filepath.Join(s.erupeConfig.Screenshots.OutputDir, fmt.Sprintf("%s.jpg", token)) - file, err := os.Open(path) + safePath := s.erupeConfig.Screenshots.OutputDir + path := filepath.Join(safePath, fmt.Sprintf("%s.jpg", token)) + result, err := verifyPath(path, safePath) + if err != nil { - http.Error(w, "Image not found", http.StatusNotFound) - return - } - defer file.Close() - // Set content type header to image/jpeg - w.Header().Set("Content-Type", "image/jpeg") - // Copy the image content to the response writer - if _, err := io.Copy(w, file); err != nil { - http.Error(w, "Unable to send image", http.StatusInternalServerError) - return + fmt.Println("Error " + err.Error()) + } else { + fmt.Println("Canonical: " + result) + + file, err := os.Open(result) + if err != nil { + http.Error(w, "Image not found", http.StatusNotFound) + return + } + defer file.Close() + // Set content type header to image/jpeg + w.Header().Set("Content-Type", "image/jpeg") + // Copy the image content to the response writer + if _, err := io.Copy(w, file); err != nil { + http.Error(w, "Unable to send image", http.StatusInternalServerError) + return + } } } func (s *APIServer) ScreenShot(w http.ResponseWriter, r *http.Request) { @@ -355,33 +363,41 @@ func (s *APIServer) ScreenShot(w http.ResponseWriter, r *http.Request) { result = Result{Code: "400"} } - dir := filepath.Join(s.erupeConfig.Screenshots.OutputDir) - path := filepath.Join(s.erupeConfig.Screenshots.OutputDir, fmt.Sprintf("%s.jpg", token)) - _, err = os.Stat(dir) + safePath := s.erupeConfig.Screenshots.OutputDir + + path := filepath.Join(safePath, fmt.Sprintf("%s.jpg", token)) + verified, err := verifyPath(path, safePath) + if err != nil { - if os.IsNotExist(err) { - err = os.MkdirAll(dir, os.ModePerm) - if err != nil { - s.logger.Error("Error writing screenshot, could not create folder") + result = Result{Code: "500"} + } else { + + _, err = os.Stat(safePath) + if err != nil { + if os.IsNotExist(err) { + err = os.MkdirAll(safePath, os.ModePerm) + if err != nil { + s.logger.Error("Error writing screenshot, could not create folder") + result = Result{Code: "500"} + } + } else { + s.logger.Error("Error writing screenshot") result = Result{Code: "500"} } - } else { - s.logger.Error("Error writing screenshot") + } + // Create or open the output file + outputFile, err := os.Create(verified) + if err != nil { result = Result{Code: "500"} } - } - // Create or open the output file - outputFile, err := os.Create(path) - if err != nil { - result = Result{Code: "500"} - } - defer outputFile.Close() + defer outputFile.Close() - // Encode the image and write it to the file - err = jpeg.Encode(outputFile, img, &jpeg.Options{Quality: s.erupeConfig.Screenshots.UploadQuality}) - if err != nil { - s.logger.Error("Error writing screenshot, could not write file", zap.Error(err)) - result = Result{Code: "500"} + // Encode the image and write it to the file + err = jpeg.Encode(outputFile, img, &jpeg.Options{Quality: s.erupeConfig.Screenshots.UploadQuality}) + if err != nil { + s.logger.Error("Error writing screenshot, could not write file", zap.Error(err)) + result = Result{Code: "500"} + } } } // Marshal the struct into XML diff --git a/server/api/utils.go b/server/api/utils.go new file mode 100644 index 000000000..1a7a18d26 --- /dev/null +++ b/server/api/utils.go @@ -0,0 +1,37 @@ +package api + +import ( + "errors" + "fmt" + "path/filepath" +) + +func inTrustedRoot(path string, trustedRoot string) error { + for path != "/" { + path = filepath.Dir(path) + if path == trustedRoot { + return nil + } + } + return errors.New("path is outside of trusted root") +} + +func verifyPath(path string, trustedRoot string) (string, error) { + + c := filepath.Clean(path) + fmt.Println("Cleaned path: " + c) + + r, err := filepath.EvalSymlinks(c) + if err != nil { + fmt.Println("Error " + err.Error()) + return c, errors.New("Unsafe or invalid path specified") + } + + err = inTrustedRoot(r, trustedRoot) + if err != nil { + fmt.Println("Error " + err.Error()) + return r, errors.New("Unsafe or invalid path specified") + } else { + return r, nil + } +} From 7d7fd50ba8c4ad2295e401f162924f785c56236d Mon Sep 17 00:00:00 2001 From: stratic-dev Date: Wed, 20 Mar 2024 19:12:57 +0000 Subject: [PATCH 08/23] init ps4 support --- server/signserver/dsgn_resp.go | 5 +++-- server/signserver/session.go | 21 +++++++++++++++------ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/server/signserver/dsgn_resp.go b/server/signserver/dsgn_resp.go index 452b02475..250bdae46 100644 --- a/server/signserver/dsgn_resp.go +++ b/server/signserver/dsgn_resp.go @@ -7,9 +7,10 @@ import ( _config "erupe-ce/config" "erupe-ce/server/channelserver" "fmt" - "go.uber.org/zap" "strings" "time" + + "go.uber.org/zap" ) func (s *Session) makeSignResponse(uid uint32) []byte { @@ -135,7 +136,7 @@ func (s *Session) makeSignResponse(uid uint32) []byte { bf.WriteUint32(s.server.getLastCID(uid)) bf.WriteUint32(s.server.getUserRights(uid)) ps.Uint16(bf, "", false) // filters - if s.client == VITA || s.client == PS3 { + 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)) diff --git a/server/signserver/session.go b/server/signserver/session.go index e4cbd5537..feabae04f 100644 --- a/server/signserver/session.go +++ b/server/signserver/session.go @@ -11,6 +11,7 @@ import ( "erupe-ce/common/byteframe" "erupe-ce/network" + "go.uber.org/zap" ) @@ -20,6 +21,7 @@ const ( PC100 client = iota VITA PS3 + PS4 WIIU ) @@ -56,6 +58,9 @@ func (s *Session) handlePacket(pkt []byte) error { switch reqType[:len(reqType)-3] { case "DLTSKEYSIGN:", "DSGN:", "SIGN:": s.handleDSGN(bf) + case "PS4SGN:": + s.client = PS4 + s.handlePSSGN(bf) case "PS3SGN:": s.client = PS3 s.handlePSSGN(bf) @@ -127,13 +132,17 @@ func (s *Session) handleWIIUSGN(bf *byteframe.ByteFrame) { func (s *Session) handlePSSGN(bf *byteframe.ByteFrame) { // Prevent reading malformed request - if len(bf.DataFromCurrent()) < 128 { - s.sendCode(SIGN_EABORT) - return + if s.client != PS4 { + dataLength := len(bf.DataFromCurrent()) //PS4 is 24 + if dataLength < 128 { + s.sendCode(SIGN_EABORT) + return + } + + _ = bf.ReadNullTerminatedBytes() // VITA = 0000000256, PS3 = 0000000255 + _ = bf.ReadBytes(2) // VITA = 1, PS3 = ! + _ = bf.ReadBytes(82) } - _ = bf.ReadNullTerminatedBytes() // VITA = 0000000256, PS3 = 0000000255 - _ = bf.ReadBytes(2) // VITA = 1, PS3 = ! - _ = bf.ReadBytes(82) s.psn = string(bf.ReadNullTerminatedBytes()) var uid uint32 err := s.server.db.QueryRow(`SELECT id FROM users WHERE psn_id = $1`, s.psn).Scan(&uid) From 4d134d06246b6fde723de82b5ce082391b687e63 Mon Sep 17 00:00:00 2001 From: stratic-dev Date: Wed, 20 Mar 2024 19:44:54 +0000 Subject: [PATCH 09/23] Remove reformatting --- server/signserver/session.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/server/signserver/session.go b/server/signserver/session.go index feabae04f..c314a44a0 100644 --- a/server/signserver/session.go +++ b/server/signserver/session.go @@ -133,8 +133,7 @@ func (s *Session) handleWIIUSGN(bf *byteframe.ByteFrame) { func (s *Session) handlePSSGN(bf *byteframe.ByteFrame) { // Prevent reading malformed request if s.client != PS4 { - dataLength := len(bf.DataFromCurrent()) //PS4 is 24 - if dataLength < 128 { + if len(bf.DataFromCurrent()) < 128 { s.sendCode(SIGN_EABORT) return } From a10ecf2a11fa991f717bb7105f6977cd55edeb90 Mon Sep 17 00:00:00 2001 From: nageld Date: Sat, 23 Mar 2024 14:17:14 -0400 Subject: [PATCH 10/23] update init path --- docker/init/setup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/init/setup.sh b/docker/init/setup.sh index b84f83b4d..46e16274a 100644 --- a/docker/init/setup.sh +++ b/docker/init/setup.sh @@ -1,7 +1,7 @@ #!/bin/bash set -e echo "INIT!" -pg_restore --username="$POSTGRES_USER" --dbname="$POSTGRES_DB" --verbose /schemas/initialisation-schema/9.1-init.sql +pg_restore --username="$POSTGRES_USER" --dbname="$POSTGRES_DB" --verbose /schemas/init.sql From 5342dc4df12ade3bb77afe51b3673f183fc96866 Mon Sep 17 00:00:00 2001 From: nageld Date: Sat, 23 Mar 2024 14:19:23 -0400 Subject: [PATCH 11/23] reference the bin and config in the root directory so they don't need to be duplicated in docker folder --- docker/docker-compose.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index a610836ee..c961a3ce4 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -42,8 +42,8 @@ services: build: context: ../ volumes: - - ./config.json:/app/erupe/config.json - - ./bin:/app/erupe/bin + - ../config.json:/app/erupe/config.json + - ../bin:/app/erupe/bin - ./savedata:/app/erupe/savedata ports: # (Make sure these match config.json) From 100ec30fba5dc70ae0a651663a3bd014198a1cd0 Mon Sep 17 00:00:00 2001 From: wish Date: Sun, 16 Jun 2024 17:46:12 +1000 Subject: [PATCH 12/23] fix GetCafeDuration --- server/channelserver/handlers_cafe.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/server/channelserver/handlers_cafe.go b/server/channelserver/handlers_cafe.go index b87bac5ff..2211c5c64 100644 --- a/server/channelserver/handlers_cafe.go +++ b/server/channelserver/handlers_cafe.go @@ -4,6 +4,7 @@ import ( "erupe-ce/common/byteframe" "erupe-ce/common/mhfcourse" ps "erupe-ce/common/pascalstring" + _config "erupe-ce/config" "erupe-ce/network/mhfpacket" "fmt" "go.uber.org/zap" @@ -92,10 +93,11 @@ func handleMsgMhfGetCafeDuration(s *Session, p mhfpacket.MHFPacket) { if mhfcourse.CourseExists(30, s.courses) { cafeTime = uint32(TimeAdjusted().Unix()) - uint32(s.sessionStart) + cafeTime } - bf.WriteUint32(cafeTime) // Total cafe time - bf.WriteUint16(0) - ps.Uint16(bf, fmt.Sprintf(s.server.i18n.cafe.reset, int(cafeReset.Month()), cafeReset.Day()), true) - + bf.WriteUint32(cafeTime) + if _config.ErupeConfig.RealClientMode >= _config.G10 { // TODO: Confirm G10, not in G9.1 + bf.WriteUint16(0) + ps.Uint16(bf, fmt.Sprintf(s.server.i18n.cafe.reset, int(cafeReset.Month()), cafeReset.Day()), true) + } doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } From e12f444b8da121e2aa05e8f28d6347a60ff95ebd Mon Sep 17 00:00:00 2001 From: wish Date: Sun, 16 Jun 2024 18:16:12 +1000 Subject: [PATCH 13/23] fix GetCafeDuration --- server/channelserver/handlers_cafe.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/channelserver/handlers_cafe.go b/server/channelserver/handlers_cafe.go index 2211c5c64..4449c3316 100644 --- a/server/channelserver/handlers_cafe.go +++ b/server/channelserver/handlers_cafe.go @@ -94,7 +94,7 @@ func handleMsgMhfGetCafeDuration(s *Session, p mhfpacket.MHFPacket) { cafeTime = uint32(TimeAdjusted().Unix()) - uint32(s.sessionStart) + cafeTime } bf.WriteUint32(cafeTime) - if _config.ErupeConfig.RealClientMode >= _config.G10 { // TODO: Confirm G10, not in G9.1 + if _config.ErupeConfig.RealClientMode >= _config.Z2 { // TODO: Confirm Z2, not in Z1 bf.WriteUint16(0) ps.Uint16(bf, fmt.Sprintf(s.server.i18n.cafe.reset, int(cafeReset.Month()), cafeReset.Day()), true) } From 12c7774cc125d0f87a0cf1e05c6b7473b228cf12 Mon Sep 17 00:00:00 2001 From: wish Date: Tue, 25 Jun 2024 20:35:56 +1000 Subject: [PATCH 14/23] fix GetCafeDuration --- server/channelserver/handlers_cafe.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/channelserver/handlers_cafe.go b/server/channelserver/handlers_cafe.go index 4449c3316..406732371 100644 --- a/server/channelserver/handlers_cafe.go +++ b/server/channelserver/handlers_cafe.go @@ -94,7 +94,7 @@ func handleMsgMhfGetCafeDuration(s *Session, p mhfpacket.MHFPacket) { cafeTime = uint32(TimeAdjusted().Unix()) - uint32(s.sessionStart) + cafeTime } bf.WriteUint32(cafeTime) - if _config.ErupeConfig.RealClientMode >= _config.Z2 { // TODO: Confirm Z2, not in Z1 + if _config.ErupeConfig.RealClientMode >= _config.ZZ { bf.WriteUint16(0) ps.Uint16(bf, fmt.Sprintf(s.server.i18n.cafe.reset, int(cafeReset.Month()), cafeReset.Day()), true) } From dd13713bc15fff2a14ec53e7b36eeb85fd97921e Mon Sep 17 00:00:00 2001 From: wish Date: Tue, 25 Jun 2024 22:50:58 +1000 Subject: [PATCH 15/23] fix parsing SysTerminalLog --- network/mhfpacket/msg_sys_terminal_log.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/mhfpacket/msg_sys_terminal_log.go b/network/mhfpacket/msg_sys_terminal_log.go index 5033346b4..bad160a73 100644 --- a/network/mhfpacket/msg_sys_terminal_log.go +++ b/network/mhfpacket/msg_sys_terminal_log.go @@ -40,8 +40,8 @@ func (m *MsgSysTerminalLog) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Client entryCount := int(bf.ReadUint16()) bf.ReadUint16() // Zeroed - var e TerminalLogEntry for i := 0; i < entryCount; i++ { + var e TerminalLogEntry e.Index = bf.ReadUint32() e.Type1 = bf.ReadUint8() e.Type2 = bf.ReadUint8() From 0caaeac3af872a4c1e77e13f0d84b56516a1c9c8 Mon Sep 17 00:00:00 2001 From: wish Date: Mon, 15 Jul 2024 01:07:50 +1000 Subject: [PATCH 16/23] initial ngword commit --- common/stringsupport/string_convert.go | 13 +++++++ server/signserver/dsgn_resp.go | 49 +++++++++++++++++++++++++- 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/common/stringsupport/string_convert.go b/common/stringsupport/string_convert.go index de4d04364..3fb4bb097 100644 --- a/common/stringsupport/string_convert.go +++ b/common/stringsupport/string_convert.go @@ -29,6 +29,19 @@ func SJISToUTF8(b []byte) string { return string(result) } +func ToNGWord(x string) []uint16 { + var w []uint16 + for _, r := range []rune(x) { + if r > 0xFF { + t := UTF8ToSJIS(string(r)) + w = append(w, uint16(t[1])<<8|uint16(t[0])) + } else { + w = append(w, uint16(r)) + } + } + return w +} + func PaddedString(x string, size uint, t bool) []byte { if t { e := japanese.ShiftJIS.NewEncoder() diff --git a/server/signserver/dsgn_resp.go b/server/signserver/dsgn_resp.go index 250bdae46..3ae65a512 100644 --- a/server/signserver/dsgn_resp.go +++ b/server/signserver/dsgn_resp.go @@ -135,7 +135,54 @@ func (s *Session) makeSignResponse(uid uint32) []byte { bf.WriteUint32(s.server.getLastCID(uid)) bf.WriteUint32(s.server.getUserRights(uid)) - ps.Uint16(bf, "", false) // filters + + namNGWords := []string{"test", "痴女", "てすと"} + msgNGWords := []string{"test", "痴女", "てすと"} + + filters := byteframe.NewByteFrame() + filters.WriteNullTerminatedBytes([]byte("smc")) + smc := byteframe.NewByteFrame() + //smcBytes, _ := hex.DecodeString("3D000000818100000000000029000000816A0000000000002800000081690000000000002100000081490000000000002F000000815E0000000000002B000000817B00000000000026000000819500000000000082500000310000000000000082DA0000837B0000CE00DE0082D9DE00837ADE0082D9814A837A814ACE00814A0000000082D7000083780000CD00DE0082D6DE008377DE0082D6814ACD00814A8377814A0000000082C5000083660000C300DE0082C4DE008365DE0082C4814A8365814AC300814A81A7814A81A7DE0089B3DE0089B3814A0000000082D1000083720000CB00DE0082D0DE008371DE0082D0814A8371814ACB00814A0000000082C7000083680000C400DE0082C6DE008367DE0082C6814A8367814AC400814A84B0DE0084B0814A84A5DE0084A5814A0000000082CE0000836F0000CA00DE0082CDDE00836EDE0094AADE0082CD814A836E814ACA00814A94AA814A0000000082C2DE00836400008363DE0082C2814A8363814AC200DE00C200814A82C3000082C1DE008362DE00AF00DE0082C1814A8362814AAF00814A0000000082D4000083750000CC00DE0083940000B300DE0082A4814A82A4DE008345DE00A900DE0082A3DE0082D3DE008374DE00CC00814A0000000082C0000083610000C100DE0082BFDE008360DE0082BF814A8360814AC100814A90E7814A90E7DE000000000082BE0000835F0000C000DE0082BDDE00835EDE00975BDE0082BD814A835E814AC000814A975B814A0000000082BC0000835D0000BF00DE0082BBDE00835CDE0082BB814A835C814ABF00814A8393DE008393814ADD00814ADD00DE00838ADE00D800DE00D800814A838A814A0000000082BA0000BE00DE0082B9DE00835ADE0082B9814A835A814ABE00814A835B00000000000082B8000083590000BD00DE0082B7DE008358DE0082B7814A8358814ABD00814A0000000082B6000083570000BC00DE0082B5DE008356DE0082B5814A8356814ABC00814A0000000082B4000083550000BB00DE0082B3DE008354DE0082B3814A8354814ABB00814A0000000082B2000083530000BA00DE0082B1DE008352DE0082B1814A8352814ABA00814A0000000082B0000083510000B900DE0082AFDE008350DE0082AF814A8350814AB900814A8396DE008396814A0000000082AE0000834F0000B800DE0082ADDE00834EDE0082AD814A834E814AB800814A0000000082AC0000834D0000B700DE0082ABDE00834CDE0082AB814A834C814AB700814A0000000082AA0000834B0000B600DE008395DE00834ADE0082A9DE0097CDDE008395814A834A814A82A9814A97CD814AB600814A0000000082F0000083920000A60000000000000082ED0000838F0000DC000000838E00000000000082EB0000838D0000DB00000081A000008CFB00000000000082EA0000838C0000DA0000000000000082E90000838B0000D90000000000000082E80000838A0000D80000000000000082E7000083890000D70000000000000082E6000083880000D6000000AE00000082E50000838700000000000082E4000083860000D5000000AD00000082E30000838500000000000082E2000083840000D4000000AC00000082E10000838300000000000082E0000083820000D30000000000000082DF000083810000D20000004D0045000000000082DE000083800000D10000000000000082DD0000837E0000D00000000000000082DC0000837D0000CF0000000000000082D90000837A0000CE0000000000000082D6000083770000CD0000000000000082D3000083740000CC0000000000000082D0000083710000CB0000000000000082CD0000836E0000CA00000094AA00000000000082CC0000836D0000C90000000000000082CB0000836C0000C80000000000000082CA0000836B0000C70000000000000082C90000836A0000C600000093F100000000000082C8000083690000C50000000000000082C6000083670000C400000084B0000084A500000000000082C4000083650000C300000081A7000089B300000000000082C2000083630000C200000082C1000083620000AF0000000000000082BF000083600000C100000090E700000000000082BD0000835E0000C0000000975B00000000000082BB0000835C0000BF0000000000000082B90000835A0000BE0000000000000082B7000083580000BD0000000000000082B5000083560000BC0000000000000082B3000083540000BB0000000000000082B1000083520000BA0000000000000082AF000083500000B9000000839600000000000082AD0000834E0000B80000000000000082AB0000834C0000B70000000000000082A90000834A0000B60000008395000097CD00000000000082A8000083490000B5000000AB00000082A70000834800000000000082A6000083470000B4000000AA00000082A50000834600008D4800000000000082A4000083450000B3000000A900000082A30000834400000000000082A2000083430000B2000000A800000082A100008342000000000000A7000000B1000000829F000082A00000834000008341000000000000815B0000815C0000815D0000816000002D000000817C0000B000000088EA00000000000039000000825800000000000038000000825700000000000037000000825600000000000036000000825500000000000035000000825400000000000034000000825300000000000033000000825200000000000032000000825100000000000082DB0000837C0000CE00DF0082D9DF00837ADF00837A818B82D9818BCE00818B0000000082D8000083790000CD00DF0082D6DF008377DF008377818B82D6818BCD00818B0000000082D5000083760000CC00DF0082D3DF008374DF008374818B82D3818BCC00818B0000000082D2000083730000CB00DF0082D0DF008371DF008371818B82D0818BCB00818B0000000082CF000083700000CA00DF0082CDDF00836EDF00836E818B82CD818BCA00818B94AADF0094AA814B0000000082790000829A00005A0000007A00000083A40000000000008278000082990000590000007900000083B200008454000084850000000000008277000082980000580000007800000083B4000083D4000084560000817E000084870000875D0000000000008276000082970000570000007700000083D6000084590000848A0000848B0000000000008275000082960000560000007600000083CB000083D2000087580000000000008274000082950000550000007500000083CA000081BE0000000000008273000082940000540000007400000083B1000083D100008453000084840000000000002400000053000000730000008190000081E70000827200008293000000000000827100008292000052000000720000008460000084910000000000008270000082910000510000007100000000000000826F000082900000500000007000000083AF000083CF0000845100008482000000000000826E0000828F00004F0000006F000000819B000083AD000083CD0000844F00008480000081FC0000815A000030000000824F000000000000826D0000828E00004E0000006E00000083AB000083C50000DD00000082F100008393000000000000826C0000828D00004D0000006D00000083AA0000844D0000847D000000000000826B0000828C00004C0000006C000000879800007C00000000000000826A0000828B00004B0000006B00000083A8000083C80000844B0000847B00000000000082690000828A00004A0000006A000000000000008268000082890000490000006900000083A7000087540000000000008267000082880000480000006800000083A50000844E0000847E000000000000826500008286000046000000660000000000000082660000828700004700000067000000000000008264000082850000450000006500000083A3000083C300008445000084460000847500008476000081B80000000000008263000082840000440000006400000000000000430000008283000063000000845200008483000082620000818E0000000000004200000082610000828200006200000083C0000083A000008442000084720000848C0000848E000081F30000000000002700000081660000000000004100000082600000610000008281000083BF000040000000819700008470000081F0000084400000839F00000000000022000000816800000000000025000000819300000000000000000000") + //smc.WriteBytes(smcBytes) + filters.SetLE() + 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) From 632aa081b96261883e84fe0454737b0587974df4 Mon Sep 17 00:00:00 2001 From: wish Date: Tue, 16 Jul 2024 00:57:59 +1000 Subject: [PATCH 17/23] decode SMC table --- common/stringsupport/string_convert.go | 6 +- server/signserver/dsgn_resp.go | 137 ++++++++++++++++++++++++- 2 files changed, 139 insertions(+), 4 deletions(-) diff --git a/common/stringsupport/string_convert.go b/common/stringsupport/string_convert.go index 3fb4bb097..96c14c9ba 100644 --- a/common/stringsupport/string_convert.go +++ b/common/stringsupport/string_convert.go @@ -34,7 +34,11 @@ func ToNGWord(x string) []uint16 { for _, r := range []rune(x) { if r > 0xFF { t := UTF8ToSJIS(string(r)) - w = append(w, uint16(t[1])<<8|uint16(t[0])) + if len(t) > 1 { + w = append(w, uint16(t[1])<<8|uint16(t[0])) + } else { + w = append(w, uint16(t[0])) + } } else { w = append(w, uint16(r)) } diff --git a/server/signserver/dsgn_resp.go b/server/signserver/dsgn_resp.go index 3ae65a512..3066dc3d6 100644 --- a/server/signserver/dsgn_resp.go +++ b/server/signserver/dsgn_resp.go @@ -140,11 +140,142 @@ func (s *Session) makeSignResponse(uid uint32) []byte { msgNGWords := []string{"test", "痴女", "てすと"} filters := byteframe.NewByteFrame() + filters.SetLE() filters.WriteNullTerminatedBytes([]byte("smc")) smc := byteframe.NewByteFrame() - //smcBytes, _ := hex.DecodeString("3D000000818100000000000029000000816A0000000000002800000081690000000000002100000081490000000000002F000000815E0000000000002B000000817B00000000000026000000819500000000000082500000310000000000000082DA0000837B0000CE00DE0082D9DE00837ADE0082D9814A837A814ACE00814A0000000082D7000083780000CD00DE0082D6DE008377DE0082D6814ACD00814A8377814A0000000082C5000083660000C300DE0082C4DE008365DE0082C4814A8365814AC300814A81A7814A81A7DE0089B3DE0089B3814A0000000082D1000083720000CB00DE0082D0DE008371DE0082D0814A8371814ACB00814A0000000082C7000083680000C400DE0082C6DE008367DE0082C6814A8367814AC400814A84B0DE0084B0814A84A5DE0084A5814A0000000082CE0000836F0000CA00DE0082CDDE00836EDE0094AADE0082CD814A836E814ACA00814A94AA814A0000000082C2DE00836400008363DE0082C2814A8363814AC200DE00C200814A82C3000082C1DE008362DE00AF00DE0082C1814A8362814AAF00814A0000000082D4000083750000CC00DE0083940000B300DE0082A4814A82A4DE008345DE00A900DE0082A3DE0082D3DE008374DE00CC00814A0000000082C0000083610000C100DE0082BFDE008360DE0082BF814A8360814AC100814A90E7814A90E7DE000000000082BE0000835F0000C000DE0082BDDE00835EDE00975BDE0082BD814A835E814AC000814A975B814A0000000082BC0000835D0000BF00DE0082BBDE00835CDE0082BB814A835C814ABF00814A8393DE008393814ADD00814ADD00DE00838ADE00D800DE00D800814A838A814A0000000082BA0000BE00DE0082B9DE00835ADE0082B9814A835A814ABE00814A835B00000000000082B8000083590000BD00DE0082B7DE008358DE0082B7814A8358814ABD00814A0000000082B6000083570000BC00DE0082B5DE008356DE0082B5814A8356814ABC00814A0000000082B4000083550000BB00DE0082B3DE008354DE0082B3814A8354814ABB00814A0000000082B2000083530000BA00DE0082B1DE008352DE0082B1814A8352814ABA00814A0000000082B0000083510000B900DE0082AFDE008350DE0082AF814A8350814AB900814A8396DE008396814A0000000082AE0000834F0000B800DE0082ADDE00834EDE0082AD814A834E814AB800814A0000000082AC0000834D0000B700DE0082ABDE00834CDE0082AB814A834C814AB700814A0000000082AA0000834B0000B600DE008395DE00834ADE0082A9DE0097CDDE008395814A834A814A82A9814A97CD814AB600814A0000000082F0000083920000A60000000000000082ED0000838F0000DC000000838E00000000000082EB0000838D0000DB00000081A000008CFB00000000000082EA0000838C0000DA0000000000000082E90000838B0000D90000000000000082E80000838A0000D80000000000000082E7000083890000D70000000000000082E6000083880000D6000000AE00000082E50000838700000000000082E4000083860000D5000000AD00000082E30000838500000000000082E2000083840000D4000000AC00000082E10000838300000000000082E0000083820000D30000000000000082DF000083810000D20000004D0045000000000082DE000083800000D10000000000000082DD0000837E0000D00000000000000082DC0000837D0000CF0000000000000082D90000837A0000CE0000000000000082D6000083770000CD0000000000000082D3000083740000CC0000000000000082D0000083710000CB0000000000000082CD0000836E0000CA00000094AA00000000000082CC0000836D0000C90000000000000082CB0000836C0000C80000000000000082CA0000836B0000C70000000000000082C90000836A0000C600000093F100000000000082C8000083690000C50000000000000082C6000083670000C400000084B0000084A500000000000082C4000083650000C300000081A7000089B300000000000082C2000083630000C200000082C1000083620000AF0000000000000082BF000083600000C100000090E700000000000082BD0000835E0000C0000000975B00000000000082BB0000835C0000BF0000000000000082B90000835A0000BE0000000000000082B7000083580000BD0000000000000082B5000083560000BC0000000000000082B3000083540000BB0000000000000082B1000083520000BA0000000000000082AF000083500000B9000000839600000000000082AD0000834E0000B80000000000000082AB0000834C0000B70000000000000082A90000834A0000B60000008395000097CD00000000000082A8000083490000B5000000AB00000082A70000834800000000000082A6000083470000B4000000AA00000082A50000834600008D4800000000000082A4000083450000B3000000A900000082A30000834400000000000082A2000083430000B2000000A800000082A100008342000000000000A7000000B1000000829F000082A00000834000008341000000000000815B0000815C0000815D0000816000002D000000817C0000B000000088EA00000000000039000000825800000000000038000000825700000000000037000000825600000000000036000000825500000000000035000000825400000000000034000000825300000000000033000000825200000000000032000000825100000000000082DB0000837C0000CE00DF0082D9DF00837ADF00837A818B82D9818BCE00818B0000000082D8000083790000CD00DF0082D6DF008377DF008377818B82D6818BCD00818B0000000082D5000083760000CC00DF0082D3DF008374DF008374818B82D3818BCC00818B0000000082D2000083730000CB00DF0082D0DF008371DF008371818B82D0818BCB00818B0000000082CF000083700000CA00DF0082CDDF00836EDF00836E818B82CD818BCA00818B94AADF0094AA814B0000000082790000829A00005A0000007A00000083A40000000000008278000082990000590000007900000083B200008454000084850000000000008277000082980000580000007800000083B4000083D4000084560000817E000084870000875D0000000000008276000082970000570000007700000083D6000084590000848A0000848B0000000000008275000082960000560000007600000083CB000083D2000087580000000000008274000082950000550000007500000083CA000081BE0000000000008273000082940000540000007400000083B1000083D100008453000084840000000000002400000053000000730000008190000081E70000827200008293000000000000827100008292000052000000720000008460000084910000000000008270000082910000510000007100000000000000826F000082900000500000007000000083AF000083CF0000845100008482000000000000826E0000828F00004F0000006F000000819B000083AD000083CD0000844F00008480000081FC0000815A000030000000824F000000000000826D0000828E00004E0000006E00000083AB000083C50000DD00000082F100008393000000000000826C0000828D00004D0000006D00000083AA0000844D0000847D000000000000826B0000828C00004C0000006C000000879800007C00000000000000826A0000828B00004B0000006B00000083A8000083C80000844B0000847B00000000000082690000828A00004A0000006A000000000000008268000082890000490000006900000083A7000087540000000000008267000082880000480000006800000083A50000844E0000847E000000000000826500008286000046000000660000000000000082660000828700004700000067000000000000008264000082850000450000006500000083A3000083C300008445000084460000847500008476000081B80000000000008263000082840000440000006400000000000000430000008283000063000000845200008483000082620000818E0000000000004200000082610000828200006200000083C0000083A000008442000084720000848C0000848E000081F30000000000002700000081660000000000004100000082600000610000008281000083BF000040000000819700008470000081F0000084400000839F00000000000022000000816800000000000025000000819300000000000000000000") - //smc.WriteBytes(smcBytes) - filters.SetLE() + 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()) From ca38f5671dbc4943d8449ccdca4af18ef9338be3 Mon Sep 17 00:00:00 2001 From: wish Date: Tue, 16 Jul 2024 01:12:02 +1000 Subject: [PATCH 18/23] ascii working, sjis not working --- server/signserver/dsgn_resp.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/server/signserver/dsgn_resp.go b/server/signserver/dsgn_resp.go index 3066dc3d6..06e58994a 100644 --- a/server/signserver/dsgn_resp.go +++ b/server/signserver/dsgn_resp.go @@ -287,7 +287,16 @@ func (s *Session) makeSignResponse(uid uint32) []byte { 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 + var i int16 + j := int16(-1) + for _, smcGroup := range smcData { + if rune(part) == smcGroup.charGroup[0][0] { + j = i + break + } + i += int16(len(smcGroup.charGroup) + 1) + } + nam.WriteInt16(j) } nam.WriteUint16(0) nam.WriteInt16(-1) From 5de6570510ca3810a9787c7ab89f7b81ea5acbf0 Mon Sep 17 00:00:00 2001 From: wish Date: Tue, 16 Jul 2024 01:13:17 +1000 Subject: [PATCH 19/23] ascii working, sjis not working --- server/signserver/dsgn_resp.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/server/signserver/dsgn_resp.go b/server/signserver/dsgn_resp.go index 06e58994a..84bb0fa56 100644 --- a/server/signserver/dsgn_resp.go +++ b/server/signserver/dsgn_resp.go @@ -312,7 +312,16 @@ func (s *Session) makeSignResponse(uid uint32) []byte { msg.WriteUint32(uint32(len(parts))) for _, part := range parts { msg.WriteUint16(part) - msg.WriteInt16(-1) + var i int16 + j := int16(-1) + for _, smcGroup := range smcData { + if rune(part) == smcGroup.charGroup[0][0] { + j = i + break + } + i += int16(len(smcGroup.charGroup) + 1) + } + msg.WriteInt16(j) } msg.WriteUint16(0) msg.WriteInt16(-1) From aa5d95e7c55c84661605f0a3e6f4bcb4c4f545f7 Mon Sep 17 00:00:00 2001 From: wish Date: Mon, 22 Jul 2024 23:44:53 +1000 Subject: [PATCH 20/23] fix sjis ngwords --- server/signserver/dsgn_resp.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/server/signserver/dsgn_resp.go b/server/signserver/dsgn_resp.go index 84bb0fa56..3d102d52c 100644 --- a/server/signserver/dsgn_resp.go +++ b/server/signserver/dsgn_resp.go @@ -136,8 +136,8 @@ func (s *Session) makeSignResponse(uid uint32) []byte { bf.WriteUint32(s.server.getLastCID(uid)) bf.WriteUint32(s.server.getUserRights(uid)) - namNGWords := []string{"test", "痴女", "てすと"} - msgNGWords := []string{"test", "痴女", "てすと"} + namNGWords := []string{} + msgNGWords := []string{} filters := byteframe.NewByteFrame() filters.SetLE() @@ -290,7 +290,7 @@ func (s *Session) makeSignResponse(uid uint32) []byte { var i int16 j := int16(-1) for _, smcGroup := range smcData { - if rune(part) == smcGroup.charGroup[0][0] { + if rune(part) == rune(stringsupport.ToNGWord(string(smcGroup.charGroup[0][0]))[0]) { j = i break } @@ -315,7 +315,7 @@ func (s *Session) makeSignResponse(uid uint32) []byte { var i int16 j := int16(-1) for _, smcGroup := range smcData { - if rune(part) == smcGroup.charGroup[0][0] { + if rune(part) == rune(stringsupport.ToNGWord(string(smcGroup.charGroup[0][0]))[0]) { j = i break } From b755de269e388bbda9b0b10f0c505b02c6d0d657 Mon Sep 17 00:00:00 2001 From: wish Date: Tue, 23 Jul 2024 00:11:54 +1000 Subject: [PATCH 21/23] add retro stamp rewards --- server/channelserver/handlers.go | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index d301ad6c9..ed9308904 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -1051,6 +1051,32 @@ func handleMsgMhfUpdateEtcPoint(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfStampcardStamp(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfStampcardStamp) + + rewards := []struct { + HR uint16 + Item1 uint16 + Quantity1 uint16 + Item2 uint16 + Quantity2 uint16 + }{ + {0, 6164, 1, 6164, 2}, + {50, 6164, 2, 6164, 3}, + {100, 6164, 3, 5392, 1}, + {300, 5392, 1, 5392, 3}, + {999, 5392, 1, 5392, 4}, + } + if _config.ErupeConfig.RealClientMode <= _config.Z1 { + for _, reward := range rewards { + if pkt.HR >= reward.HR { + pkt.Item1 = reward.Item1 + pkt.Quantity1 = reward.Quantity1 + pkt.Item2 = reward.Item2 + pkt.Quantity2 = reward.Quantity2 + break + } + } + } + bf := byteframe.NewByteFrame() bf.WriteUint16(pkt.HR) var stamps uint16 From d29b7d00fc8c90ca9cd62bfad2ba8e9634bb837e Mon Sep 17 00:00:00 2001 From: wish Date: Tue, 23 Jul 2024 21:26:43 +1000 Subject: [PATCH 22/23] fix retro stamp rewards --- server/channelserver/handlers.go | 1 - 1 file changed, 1 deletion(-) diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index ed9308904..814518576 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -1072,7 +1072,6 @@ func handleMsgMhfStampcardStamp(s *Session, p mhfpacket.MHFPacket) { pkt.Quantity1 = reward.Quantity1 pkt.Item2 = reward.Item2 pkt.Quantity2 = reward.Quantity2 - break } } } From 717d7853422ae331a7dc94ba4f16e35c29b4e614 Mon Sep 17 00:00:00 2001 From: wish Date: Tue, 23 Jul 2024 21:56:01 +1000 Subject: [PATCH 23/23] fix possible warehouse error --- server/channelserver/handlers_house.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/server/channelserver/handlers_house.go b/server/channelserver/handlers_house.go index 5e47d0436..c91660b54 100644 --- a/server/channelserver/handlers_house.go +++ b/server/channelserver/handlers_house.go @@ -367,13 +367,17 @@ func handleMsgMhfAcquireTitle(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfResetTitle(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfOperateWarehouse(s *Session, p mhfpacket.MHFPacket) { - pkt := p.(*mhfpacket.MsgMhfOperateWarehouse) +func initializeWarehouse(s *Session) { var t int err := s.server.db.QueryRow("SELECT character_id FROM warehouse WHERE character_id=$1", s.charID).Scan(&t) if err != nil { s.server.db.Exec("INSERT INTO warehouse (character_id) VALUES ($1)", s.charID) } +} + +func handleMsgMhfOperateWarehouse(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfOperateWarehouse) + initializeWarehouse(s) bf := byteframe.NewByteFrame() bf.WriteUint8(pkt.Operation) switch pkt.Operation { @@ -446,6 +450,7 @@ func addWarehouseEquipment(s *Session, equipment mhfitem.MHFEquipment) { } func warehouseGetItems(s *Session, index uint8) []mhfitem.MHFItemStack { + initializeWarehouse(s) var data []byte var items []mhfitem.MHFItemStack s.server.db.QueryRow(fmt.Sprintf(`SELECT item%d FROM warehouse WHERE character_id=$1`, index), s.charID).Scan(&data)