diff --git a/network/mhfpacket/msg_mhf_add_achievement.go b/network/mhfpacket/msg_mhf_add_achievement.go
index 4d94e0ce2..49aa0ebd2 100644
--- a/network/mhfpacket/msg_mhf_add_achievement.go
+++ b/network/mhfpacket/msg_mhf_add_achievement.go
@@ -6,7 +6,9 @@ import (
)
// MsgMhfAddAchievement represents the MSG_MHF_ADD_ACHIEVEMENT
-type MsgMhfAddAchievement struct{}
+type MsgMhfAddAchievement struct{
+ Unk0 []byte
+}
// Opcode returns the ID associated with this packet type.
func (m *MsgMhfAddAchievement) Opcode() network.PacketID {
@@ -15,10 +17,12 @@ func (m *MsgMhfAddAchievement) Opcode() network.PacketID {
// Parse parses the packet from binary
func (m *MsgMhfAddAchievement) Parse(bf *byteframe.ByteFrame) error {
- panic("Not implemented")
+ m.Unk0 = bf.ReadBytes(5)
+ // doesn't expect a response
+ return nil
}
// Build builds a binary packet from the current data.
func (m *MsgMhfAddAchievement) Build(bf *byteframe.ByteFrame) error {
panic("Not implemented")
-}
\ No newline at end of file
+}
diff --git a/network/mhfpacket/msg_mhf_save_hunter_navi.go b/network/mhfpacket/msg_mhf_save_hunter_navi.go
index 36b1bdbc1..237321ecd 100644
--- a/network/mhfpacket/msg_mhf_save_hunter_navi.go
+++ b/network/mhfpacket/msg_mhf_save_hunter_navi.go
@@ -9,7 +9,7 @@ import (
type MsgMhfSaveHunterNavi struct {
AckHandle uint32
DataSize uint32
- Unk0 bool
+ IsDataDiff bool
RawDataPayload []byte
}
@@ -22,7 +22,7 @@ func (m *MsgMhfSaveHunterNavi) Opcode() network.PacketID {
func (m *MsgMhfSaveHunterNavi) Parse(bf *byteframe.ByteFrame) error {
m.AckHandle = bf.ReadUint32()
m.DataSize = bf.ReadUint32()
- m.Unk0 = bf.ReadBool()
+ m.IsDataDiff = bf.ReadBool()
m.RawDataPayload = bf.ReadBytes(uint(m.DataSize))
return nil
}
diff --git a/network/mhfpacket/msg_mhf_save_plate_box.go b/network/mhfpacket/msg_mhf_save_plate_box.go
index 85362400f..830892ba6 100644
--- a/network/mhfpacket/msg_mhf_save_plate_box.go
+++ b/network/mhfpacket/msg_mhf_save_plate_box.go
@@ -6,7 +6,12 @@ import (
)
// MsgMhfSavePlateBox represents the MSG_MHF_SAVE_PLATE_BOX
-type MsgMhfSavePlateBox struct{}
+type MsgMhfSavePlateBox struct{
+ AckHandle uint32
+ DataSize uint32
+ IsDataDiff bool
+ RawDataPayload []byte
+}
// Opcode returns the ID associated with this packet type.
func (m *MsgMhfSavePlateBox) Opcode() network.PacketID {
@@ -15,10 +20,14 @@ func (m *MsgMhfSavePlateBox) Opcode() network.PacketID {
// Parse parses the packet from binary
func (m *MsgMhfSavePlateBox) Parse(bf *byteframe.ByteFrame) error {
- panic("Not implemented")
+ m.AckHandle = bf.ReadUint32()
+ m.DataSize = bf.ReadUint32()
+ m.IsDataDiff = bf.ReadBool()
+ m.RawDataPayload = bf.ReadBytes(uint(m.DataSize))
+ return nil
}
// Build builds a binary packet from the current data.
func (m *MsgMhfSavePlateBox) Build(bf *byteframe.ByteFrame) error {
panic("Not implemented")
-}
\ No newline at end of file
+}
diff --git a/network/mhfpacket/msg_mhf_save_plate_data.go b/network/mhfpacket/msg_mhf_save_plate_data.go
index aa4b8ddbf..822653dde 100644
--- a/network/mhfpacket/msg_mhf_save_plate_data.go
+++ b/network/mhfpacket/msg_mhf_save_plate_data.go
@@ -6,7 +6,12 @@ import (
)
// MsgMhfSavePlateData represents the MSG_MHF_SAVE_PLATE_DATA
-type MsgMhfSavePlateData struct{}
+type MsgMhfSavePlateData struct{
+ AckHandle uint32
+ DataSize uint32
+ IsDataDiff bool
+ RawDataPayload []byte
+}
// Opcode returns the ID associated with this packet type.
func (m *MsgMhfSavePlateData) Opcode() network.PacketID {
@@ -15,10 +20,14 @@ func (m *MsgMhfSavePlateData) Opcode() network.PacketID {
// Parse parses the packet from binary
func (m *MsgMhfSavePlateData) Parse(bf *byteframe.ByteFrame) error {
- panic("Not implemented")
+ m.AckHandle = bf.ReadUint32()
+ m.DataSize = bf.ReadUint32()
+ m.IsDataDiff = bf.ReadBool()
+ m.RawDataPayload = bf.ReadBytes(uint(m.DataSize))
+ return nil
}
// Build builds a binary packet from the current data.
func (m *MsgMhfSavePlateData) Build(bf *byteframe.ByteFrame) error {
panic("Not implemented")
-}
\ No newline at end of file
+}
diff --git a/network/mhfpacket/msg_mhf_savedata.go b/network/mhfpacket/msg_mhf_savedata.go
index 556d4f3ab..a29e241af 100644
--- a/network/mhfpacket/msg_mhf_savedata.go
+++ b/network/mhfpacket/msg_mhf_savedata.go
@@ -9,7 +9,7 @@ import (
type MsgMhfSavedata struct {
AckHandle uint32
AllocMemSize uint32
- Unk0 uint8 // Either 1 or 2, representing a true or false value for some reason.
+ SaveType uint8 // Either 1 or 2, representing a true or false value for some reason.
Unk1 uint32
DataSize uint32
RawDataPayload []byte
@@ -24,10 +24,14 @@ func (m *MsgMhfSavedata) Opcode() network.PacketID {
func (m *MsgMhfSavedata) Parse(bf *byteframe.ByteFrame) error {
m.AckHandle = bf.ReadUint32()
m.AllocMemSize = bf.ReadUint32()
- m.Unk0 = bf.ReadUint8()
+ m.SaveType = bf.ReadUint8()
m.Unk1 = bf.ReadUint32()
m.DataSize = bf.ReadUint32()
- m.RawDataPayload = bf.ReadBytes(uint(m.DataSize))
+ if m.SaveType == 1 {
+ m.RawDataPayload = bf.ReadBytes(uint(m.AllocMemSize))
+ } else if m.SaveType == 2 {
+ m.RawDataPayload = bf.ReadBytes(uint(m.DataSize))
+ }
return nil
}
diff --git a/network/mhfpacket/msg_mhf_update_equip_skin_hist.go b/network/mhfpacket/msg_mhf_update_equip_skin_hist.go
index cea63b767..e40bd0c82 100644
--- a/network/mhfpacket/msg_mhf_update_equip_skin_hist.go
+++ b/network/mhfpacket/msg_mhf_update_equip_skin_hist.go
@@ -6,7 +6,9 @@ import (
)
// MsgMhfUpdateEquipSkinHist represents the MSG_MHF_UPDATE_EQUIP_SKIN_HIST
-type MsgMhfUpdateEquipSkinHist struct{}
+type MsgMhfUpdateEquipSkinHist struct{
+ AckHandle uint32
+}
// Opcode returns the ID associated with this packet type.
func (m *MsgMhfUpdateEquipSkinHist) Opcode() network.PacketID {
@@ -15,10 +17,11 @@ func (m *MsgMhfUpdateEquipSkinHist) Opcode() network.PacketID {
// Parse parses the packet from binary
func (m *MsgMhfUpdateEquipSkinHist) Parse(bf *byteframe.ByteFrame) error {
- panic("Not implemented")
+ m.AckHandle = bf.ReadUint32()
+ return nil
}
// Build builds a binary packet from the current data.
func (m *MsgMhfUpdateEquipSkinHist) Build(bf *byteframe.ByteFrame) error {
panic("Not implemented")
-}
\ No newline at end of file
+}
diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go
index 53b577aa3..fb5707e3f 100644
--- a/server/channelserver/handlers.go
+++ b/server/channelserver/handlers.go
@@ -8,6 +8,7 @@ import (
"fmt"
"io/ioutil"
"os"
+ "io"
"path/filepath"
"strings"
"time"
@@ -52,6 +53,128 @@ func doSizedAckResp(s *Session, ackHandle uint32, data []byte) {
s.QueueAck(ackHandle, bfw.Data())
}
+// process a datadiff save for platebox and platedata
+func saveDataDiff(b []byte, save []byte) []byte {
+ // there are a bunch of extra variations on this method in use which this does not handle yet
+ // specifically this is for diffs with seek amounts trailed by 02 followed by bytes to be written
+ var seekBytes []byte
+ seekOperation := 0
+ write := byte(0)
+ for(len(b) > 2){
+ if bytes.IndexRune(b, 2) != 0 {
+ seekBytes = b[:bytes.IndexRune(b, 2)+1]
+ } else {
+ seekBytes = b[:bytes.IndexRune(b[1:], 2)+2]
+ }
+ if len(seekBytes) == 1{
+ seekBytes = b[:bytes.IndexRune(b, 2)+2]
+ //fmt.Printf("Seek: %d SeekBytes: %X Write: %X\n", seekBytes[0], seekBytes, b[len(seekBytes)] )
+ seekOperation += int(seekBytes[0])
+ write = b[len(seekBytes)]
+ b = b[3:]
+ } else {
+ seek := int32(0)
+ for _, b := range seekBytes[:len(seekBytes)-1] {
+ seek = (seek << 8) | int32(b)
+ }
+ //fmt.Printf("Seek: %d SeekBytes: %X Write: %X\n", seek, seekBytes, b[len(seekBytes)] )
+ seekOperation += int(seek)
+ write = b[len(seekBytes)]
+ b = b[len(seekBytes)+1:]
+ }
+ save[seekOperation-1] = write
+ }
+
+ return save
+}
+
+// decompress save data
+func saveDecompress(compData []byte) ([]byte, error) {
+ r := bytes.NewReader(compData)
+
+ header := make([]byte, 16)
+ n, err := r.Read(header)
+ if err != nil {
+ return nil, err
+ } else if n != len(header) {
+ return nil, err
+ }
+
+ if !bytes.Equal(header, []byte("cmp\x2020110113\x20\x20\x20\x00")) {
+ return nil, err
+ }
+
+ var output []byte
+ for {
+ b, err := r.ReadByte()
+ if err == io.EOF {
+ break
+ } else if err != nil {
+ return nil, err
+ }
+
+ if b == 0 {
+ // If it's a null byte, then the next byte is how many nulls to add.
+ nullCount, err := r.ReadByte()
+ if err == io.EOF {
+ break
+ } else if err != nil {
+ return nil, err
+ }
+
+ output = append(output, make([]byte, int(nullCount))...)
+ } else {
+ output = append(output, b)
+ }
+ }
+
+ return output, nil
+}
+
+// Null compresses a save
+func saveCompress(rawData []byte) ([]byte, error) {
+ r := bytes.NewReader(rawData)
+ var output []byte
+ output = append(output, []byte("cmp\x2020110113\x20\x20\x20\x00")...)
+ for {
+ b, err := r.ReadByte()
+ if err == io.EOF {
+ break
+ } else if err != nil {
+ return nil, err
+ }
+ if b == 0 {
+ output = append(output, []byte{0x00}...)
+ // read to get null count
+ nullCount := 1
+ for {
+ i, err := r.ReadByte()
+ if err == io.EOF {
+ output = append(output, []byte{byte(nullCount)}...)
+ break
+ } else if i != 0 {
+ r.UnreadByte()
+ output = append(output, []byte{byte(nullCount)}...)
+ break
+ } else if err != nil {
+ return nil, err
+ }
+ nullCount++
+
+ if(nullCount == 255){
+ output = append(output, []byte{0xFF, 0x00}...)
+ nullCount = 0
+ }
+ }
+ //output = append(output, []byte{byte(nullCount)}...)
+ } else {
+ output = append(output, b)
+ }
+ }
+ return output, nil
+}
+
+
func updateRights(s *Session) {
update := &mhfpacket.MsgSysUpdateRight{
Unk0: 0,
@@ -899,9 +1022,13 @@ func handleMsgMhfSavedata(s *Session, p mhfpacket.MHFPacket) {
s.logger.Fatal("Error dumping savedata", zap.Error(err))
}
- _, err = s.server.db.Exec("UPDATE characters SET is_new_character=false, savedata=$1 WHERE id=$2", pkt.RawDataPayload, s.charID)
- if err != nil {
- s.logger.Fatal("Failed to update savedata in db", zap.Error(err))
+ if pkt.SaveType == 2{
+ _, err = s.server.db.Exec("UPDATE characters SET is_new_character=false, savedata=$1 WHERE id=$2", pkt.RawDataPayload, s.charID)
+ if err != nil {
+ s.logger.Fatal("Failed to update savedata in db", zap.Error(err))
+ }
+ } else {
+ fmt.Printf("Got savedata packet of type 1, not saving.")
}
s.QueueAck(pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
@@ -1446,7 +1573,45 @@ func handleMsgMhfLoadPlateData(s *Session, p mhfpacket.MHFPacket) {
}
}
-func handleMsgMhfSavePlateData(s *Session, p mhfpacket.MHFPacket) {}
+func handleMsgMhfSavePlateData(s *Session, p mhfpacket.MHFPacket) {
+ pkt := p.(*mhfpacket.MsgMhfSavePlateData)
+ err := ioutil.WriteFile(fmt.Sprintf("savedata\\%d_platedata.bin", time.Now().Unix()), pkt.RawDataPayload, 0644)
+ if err != nil {
+ s.logger.Fatal("Error dumping platedata", zap.Error(err))
+ }
+ if pkt.IsDataDiff {
+ // https://gist.github.com/Andoryuuta/9c524da7285e4b5ca7e52e0fc1ca1daf
+ var data []byte
+ //load existing save
+ err := s.server.db.QueryRow("SELECT platedata FROM characters WHERE id = $1", s.charID).Scan(&data)
+ if err != nil {
+ s.logger.Fatal("Failed to get platedata savedata from db", zap.Error(err))
+ }
+ //decompress
+ fmt.Println("Decompressing...")
+ data, err = saveDecompress(data)
+ if err != nil {
+ s.logger.Fatal("Failed to decompress platedata from db", zap.Error(err))
+ }
+ // perform diff and compress it to write back to db
+ fmt.Println("Diffing...")
+ saveOutput, err := saveCompress(saveDataDiff(pkt.RawDataPayload, data))
+ if err != nil {
+ s.logger.Fatal("Failed to diff and compress platedata savedata", zap.Error(err))
+ }
+ _, err = s.server.db.Exec("UPDATE characters SET platedata=$1 WHERE id=$2", saveOutput, s.charID)
+ if err != nil {
+ s.logger.Fatal("Failed to update platedata savedata in db", zap.Error(err))
+ }
+ } else {
+ // simply update database, no extra processing
+ _, err := s.server.db.Exec("UPDATE characters SET platedata=$1 WHERE id=$2", pkt.RawDataPayload, s.charID)
+ if err != nil {
+ s.logger.Fatal("Failed to update platedata savedata in db", zap.Error(err))
+ }
+ }
+ s.QueueAck(pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
+}
func handleMsgMhfLoadPlateBox(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfLoadPlateBox)
@@ -1463,7 +1628,47 @@ func handleMsgMhfLoadPlateBox(s *Session, p mhfpacket.MHFPacket) {
}
}
-func handleMsgMhfSavePlateBox(s *Session, p mhfpacket.MHFPacket) {}
+func handleMsgMhfSavePlateBox(s *Session, p mhfpacket.MHFPacket) {
+ pkt := p.(*mhfpacket.MsgMhfSavePlateBox)
+ err := ioutil.WriteFile(fmt.Sprintf("savedata\\%d_platebox.bin", time.Now().Unix()), pkt.RawDataPayload, 0644)
+ if err != nil {
+ s.logger.Fatal("Error dumping hunter platebox savedata", zap.Error(err))
+ }
+ if pkt.IsDataDiff {
+ var data []byte
+ //load existing save
+ err := s.server.db.QueryRow("SELECT platebox FROM characters WHERE id = $1", s.charID).Scan(&data)
+ if err != nil {
+ s.logger.Fatal("Failed to get sigil box savedata from db", zap.Error(err))
+ }
+ //decompress
+ fmt.Println("Decompressing...")
+ data, err = saveDecompress(data)
+ if err != nil {
+ s.logger.Fatal("Failed to decompress savedata from db", zap.Error(err))
+ }
+ // perform diff and compress it to write back to db
+ fmt.Println("Diffing...")
+ saveOutput, err := saveCompress(saveDataDiff(pkt.RawDataPayload, data))
+ if err != nil {
+ s.logger.Fatal("Failed to diff and compress savedata", zap.Error(err))
+ }
+ _, err = s.server.db.Exec("UPDATE characters SET platebox=$1 WHERE id=$2", saveOutput, s.charID)
+ if err != nil {
+ s.logger.Fatal("Failed to update platebox savedata in db", zap.Error(err))
+ } else {
+ fmt.Println("Wrote recompressed save back to DB.")
+ }
+
+ } else {
+ // simply update database, no extra processing
+ _, err := s.server.db.Exec("UPDATE characters SET platebox=$1 WHERE id=$2", pkt.RawDataPayload, s.charID)
+ if err != nil {
+ s.logger.Fatal("Failed to update platedata savedata in db", zap.Error(err))
+ }
+ }
+ s.QueueAck(pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
+}
func handleMsgMhfReadGuildcard(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfReadGuildcard)
@@ -1678,7 +1883,26 @@ func handleMsgMhfLoadHunterNavi(s *Session, p mhfpacket.MHFPacket) {
}
}
-func handleMsgMhfSaveHunterNavi(s *Session, p mhfpacket.MHFPacket) {}
+func handleMsgMhfSaveHunterNavi(s *Session, p mhfpacket.MHFPacket) {
+ pkt := p.(*mhfpacket.MsgMhfSaveHunterNavi)
+ err := ioutil.WriteFile(fmt.Sprintf("savedata\\%d_hunternavi.bin", time.Now().Unix()), pkt.RawDataPayload, 0644)
+ if err != nil {
+ s.logger.Fatal("Error dumping hunter navigation savedata", zap.Error(err))
+ }
+
+ if pkt.IsDataDiff {
+ // https://gist.github.com/Andoryuuta/9c524da7285e4b5ca7e52e0fc1ca1daf
+ // doesn't seem fully consistent with platedata?
+ //
+ } else {
+ // simply update database, no extra processing
+ _, err := s.server.db.Exec("UPDATE characters SET hunternavi=$1 WHERE id=$2", pkt.RawDataPayload, s.charID)
+ if err != nil {
+ s.logger.Fatal("Failed to update hunternavi savedata in db", zap.Error(err))
+ }
+ }
+ s.QueueAck(pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
+}
func handleMsgMhfRegistSpabiTime(s *Session, p mhfpacket.MHFPacket) {}
@@ -2109,122 +2333,122 @@ func handleMsgMhfGetUdMonsterPoint(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetUdMonsterPoint)
monsterPoints := []struct {
- MID uint8 // Monster ID ?
+ MID uint8
Points uint16
}{
- {MID: 0x01, Points: 0x3C},
- {MID: 0x02, Points: 0x5A},
- {MID: 0x06, Points: 0x14},
- {MID: 0x07, Points: 0x50},
- {MID: 0x08, Points: 0x28},
- {MID: 0x0B, Points: 0x3C},
- {MID: 0x0E, Points: 0x3C},
- {MID: 0x0F, Points: 0x46},
- {MID: 0x11, Points: 0x46},
- {MID: 0x14, Points: 0x28},
- {MID: 0x15, Points: 0x3C},
- {MID: 0x16, Points: 0x32},
- {MID: 0x1A, Points: 0x32},
- {MID: 0x1B, Points: 0x0A},
- {MID: 0x1C, Points: 0x0A},
- {MID: 0x1F, Points: 0x0A},
- {MID: 0x21, Points: 0x50},
- {MID: 0x24, Points: 0x64},
- {MID: 0x25, Points: 0x3C},
- {MID: 0x26, Points: 0x1E},
- {MID: 0x27, Points: 0x28},
- {MID: 0x28, Points: 0x50},
- {MID: 0x29, Points: 0x5A},
- {MID: 0x2A, Points: 0x50},
- {MID: 0x2B, Points: 0x3C},
- {MID: 0x2C, Points: 0x3C},
- {MID: 0x2D, Points: 0x46},
- {MID: 0x2E, Points: 0x3C},
- {MID: 0x2F, Points: 0x50},
- {MID: 0x30, Points: 0x1E},
- {MID: 0x31, Points: 0x3C},
- {MID: 0x32, Points: 0x50},
- {MID: 0x33, Points: 0x3C},
- {MID: 0x34, Points: 0x28},
- {MID: 0x35, Points: 0x50},
- {MID: 0x36, Points: 0x6E},
- {MID: 0x37, Points: 0x50},
- {MID: 0x3A, Points: 0x50},
- {MID: 0x3B, Points: 0x6E},
- {MID: 0x40, Points: 0x64},
- {MID: 0x41, Points: 0x6E},
- {MID: 0x43, Points: 0x28},
- {MID: 0x44, Points: 0x0A},
- {MID: 0x47, Points: 0x6E},
- {MID: 0x4A, Points: 0xFA},
- {MID: 0x4B, Points: 0xFA},
- {MID: 0x4C, Points: 0x46},
- {MID: 0x4D, Points: 0x64},
- {MID: 0x4E, Points: 0xFA},
- {MID: 0x4F, Points: 0xFA},
- {MID: 0x50, Points: 0xFA},
- {MID: 0x51, Points: 0xFA},
- {MID: 0x52, Points: 0xFA},
- {MID: 0x53, Points: 0xFA},
- {MID: 0x54, Points: 0xFA},
- {MID: 0x55, Points: 0xFA},
- {MID: 0x59, Points: 0xFA},
- {MID: 0x5A, Points: 0xFA},
- {MID: 0x5B, Points: 0xFA},
- {MID: 0x5C, Points: 0xFA},
- {MID: 0x5E, Points: 0xFA},
- {MID: 0x5F, Points: 0xFA},
- {MID: 0x60, Points: 0xFA},
- {MID: 0x63, Points: 0xFA},
- {MID: 0x65, Points: 0xFA},
- {MID: 0x67, Points: 0xFA},
- {MID: 0x68, Points: 0xFA},
- {MID: 0x69, Points: 0xFA},
- {MID: 0x6A, Points: 0xFA},
- {MID: 0x6B, Points: 0xFA},
- {MID: 0x6C, Points: 0xFA},
- {MID: 0x6D, Points: 0xFA},
- {MID: 0x6E, Points: 0xFA},
- {MID: 0x6F, Points: 0xFA},
- {MID: 0x70, Points: 0xFA},
- {MID: 0x72, Points: 0xFA},
- {MID: 0x73, Points: 0xFA},
- {MID: 0x74, Points: 0xFA},
- {MID: 0x77, Points: 0xFA},
- {MID: 0x78, Points: 0xFA},
- {MID: 0x79, Points: 0xFA},
- {MID: 0x7A, Points: 0xFA},
- {MID: 0x7B, Points: 0xFA},
- {MID: 0x7D, Points: 0xFA},
- {MID: 0x7E, Points: 0xFA},
- {MID: 0x7F, Points: 0xFA},
- {MID: 0x80, Points: 0xFA},
- {MID: 0x81, Points: 0xFA},
- {MID: 0x82, Points: 0xFA},
- {MID: 0x83, Points: 0xFA},
- {MID: 0x8B, Points: 0xFA},
- {MID: 0x8C, Points: 0xFA},
- {MID: 0x8D, Points: 0xFA},
- {MID: 0x8E, Points: 0xFA},
- {MID: 0x90, Points: 0xFA},
- {MID: 0x92, Points: 0x78},
- {MID: 0x93, Points: 0x78},
- {MID: 0x94, Points: 0x78},
- {MID: 0x96, Points: 0xFA},
- {MID: 0x97, Points: 0x78},
- {MID: 0x98, Points: 0x78},
- {MID: 0x99, Points: 0x78},
- {MID: 0x9A, Points: 0xFA},
- {MID: 0x9E, Points: 0xFA},
- {MID: 0x9F, Points: 0x78},
- {MID: 0xA0, Points: 0xFA},
- {MID: 0xA1, Points: 0xFA},
- {MID: 0xA2, Points: 0x78},
- {MID: 0xA4, Points: 0x78},
- {MID: 0xA5, Points: 0x78},
- {MID: 0xA6, Points: 0xFA},
- {MID: 0xA9, Points: 0x78},
- {MID: 0xAA, Points: 0xFA},
+ {MID: 0x01, Points: 0x3C}, // em1 Rathian
+ {MID: 0x02, Points: 0x5A}, // em2 Fatalis
+ {MID: 0x06, Points: 0x14}, // em6 Yian Kut-Ku
+ {MID: 0x07, Points: 0x50}, // em7 Lao-Shan Lung
+ {MID: 0x08, Points: 0x28}, // em8 Cephadrome
+ {MID: 0x0B, Points: 0x3C}, // em11 Rathalos
+ {MID: 0x0E, Points: 0x3C}, // em14 Diablos
+ {MID: 0x0F, Points: 0x46}, // em15 Khezu
+ {MID: 0x11, Points: 0x46}, // em17 Gravios
+ {MID: 0x14, Points: 0x28}, // em20 Gypceros
+ {MID: 0x15, Points: 0x3C}, // em21 Plesioth
+ {MID: 0x16, Points: 0x32}, // em22 Basarios
+ {MID: 0x1A, Points: 0x32}, // em26 Monoblos
+ {MID: 0x1B, Points: 0x0A}, // em27 Velocidrome
+ {MID: 0x1C, Points: 0x0A}, // em28 Gendrome
+ {MID: 0x1F, Points: 0x0A}, // em31 Iodrome
+ {MID: 0x21, Points: 0x50}, // em33 Kirin
+ {MID: 0x24, Points: 0x64}, // em36 Crimson Fatalis
+ {MID: 0x25, Points: 0x3C}, // em37 Pink Rathian
+ {MID: 0x26, Points: 0x1E}, // em38 Blue Yian Kut-Ku
+ {MID: 0x27, Points: 0x28}, // em39 Purple Gypceros
+ {MID: 0x28, Points: 0x50}, // em40 Yian Garuga
+ {MID: 0x29, Points: 0x5A}, // em41 Silver Rathalos
+ {MID: 0x2A, Points: 0x50}, // em42 Gold Rathian
+ {MID: 0x2B, Points: 0x3C}, // em43 Black Diablos
+ {MID: 0x2C, Points: 0x3C}, // em44 White Monoblos
+ {MID: 0x2D, Points: 0x46}, // em45 Red Khezu
+ {MID: 0x2E, Points: 0x3C}, // em46 Green Plesioth
+ {MID: 0x2F, Points: 0x50}, // em47 Black Gravios
+ {MID: 0x30, Points: 0x1E}, // em48 Daimyo Hermitaur
+ {MID: 0x31, Points: 0x3C}, // em49 Azure Rathalos
+ {MID: 0x32, Points: 0x50}, // em50 Ashen Lao-Shan Lung
+ {MID: 0x33, Points: 0x3C}, // em51 Blangonga
+ {MID: 0x34, Points: 0x28}, // em52 Congalala
+ {MID: 0x35, Points: 0x50}, // em53 Rajang
+ {MID: 0x36, Points: 0x6E}, // em54 Kushala Daora
+ {MID: 0x37, Points: 0x50}, // em55 Shen Gaoren
+ {MID: 0x3A, Points: 0x50}, // em58 Yama Tsukami
+ {MID: 0x3B, Points: 0x6E}, // em59 Chameleos
+ {MID: 0x40, Points: 0x64}, // em64 Lunastra
+ {MID: 0x41, Points: 0x6E}, // em65 Teostra
+ {MID: 0x43, Points: 0x28}, // em67 Shogun Ceanataur
+ {MID: 0x44, Points: 0x0A}, // em68 Bulldrome
+ {MID: 0x47, Points: 0x6E}, // em71 White Fatalis
+ {MID: 0x4A, Points: 0xFA}, // em74 Hypnocatrice
+ {MID: 0x4B, Points: 0xFA}, // em75 Lavasioth
+ {MID: 0x4C, Points: 0x46}, // em76 Tigrex
+ {MID: 0x4D, Points: 0x64}, // em77 Akantor
+ {MID: 0x4E, Points: 0xFA}, // em78 Bright Hypnoc
+ {MID: 0x4F, Points: 0xFA}, // em79 Lavasioth Subspecies
+ {MID: 0x50, Points: 0xFA}, // em80 Espinas
+ {MID: 0x51, Points: 0xFA}, // em81 Orange Espinas
+ {MID: 0x52, Points: 0xFA}, // em82 White Hypnoc
+ {MID: 0x53, Points: 0xFA}, // em83 Akura Vashimu
+ {MID: 0x54, Points: 0xFA}, // em84 Akura Jebia
+ {MID: 0x55, Points: 0xFA}, // em85 Berukyurosu
+ {MID: 0x59, Points: 0xFA}, // em89 Pariapuria
+ {MID: 0x5A, Points: 0xFA}, // em90 White Espinas
+ {MID: 0x5B, Points: 0xFA}, // em91 Kamu Orugaron
+ {MID: 0x5C, Points: 0xFA}, // em92 Nono Orugaron
+ {MID: 0x5E, Points: 0xFA}, // em94 Dyuragaua
+ {MID: 0x5F, Points: 0xFA}, // em95 Doragyurosu
+ {MID: 0x60, Points: 0xFA}, // em96 Gurenzeburu
+ {MID: 0x63, Points: 0xFA}, // em99 Rukodiora
+ {MID: 0x65, Points: 0xFA}, // em101 Gogomoa
+ {MID: 0x67, Points: 0xFA}, // em103 Taikun Zamuza
+ {MID: 0x68, Points: 0xFA}, // em104 Abiorugu
+ {MID: 0x69, Points: 0xFA}, // em105 Kuarusepusu
+ {MID: 0x6A, Points: 0xFA}, // em106 Odibatorasu
+ {MID: 0x6B, Points: 0xFA}, // em107 Disufiroa
+ {MID: 0x6C, Points: 0xFA}, // em108 Rebidiora
+ {MID: 0x6D, Points: 0xFA}, // em109 Anorupatisu
+ {MID: 0x6E, Points: 0xFA}, // em110 Hyujikiki
+ {MID: 0x6F, Points: 0xFA}, // em111 Midogaron
+ {MID: 0x70, Points: 0xFA}, // em112 Giaorugu
+ {MID: 0x72, Points: 0xFA}, // em114 Farunokku
+ {MID: 0x73, Points: 0xFA}, // em115 Pokaradon
+ {MID: 0x74, Points: 0xFA}, // em116 Shantien
+ {MID: 0x77, Points: 0xFA}, // em119 Goruganosu
+ {MID: 0x78, Points: 0xFA}, // em120 Aruganosu
+ {MID: 0x79, Points: 0xFA}, // em121 Baruragaru
+ {MID: 0x7A, Points: 0xFA}, // em122 Zerureusu
+ {MID: 0x7B, Points: 0xFA}, // em123 Gougarf
+ {MID: 0x7D, Points: 0xFA}, // em125 Forokururu
+ {MID: 0x7E, Points: 0xFA}, // em126 Meraginasu
+ {MID: 0x7F, Points: 0xFA}, // em127 Diorekkusu
+ {MID: 0x80, Points: 0xFA}, // em128 Garuba Daora
+ {MID: 0x81, Points: 0xFA}, // em129 Inagami
+ {MID: 0x82, Points: 0xFA}, // em130 Varusaburosu
+ {MID: 0x83, Points: 0xFA}, // em131 Poborubarumu
+ {MID: 0x8B, Points: 0xFA}, // em139 Gureadomosu
+ {MID: 0x8C, Points: 0xFA}, // em140 Harudomerugu
+ {MID: 0x8D, Points: 0xFA}, // em141 Toridcless
+ {MID: 0x8E, Points: 0xFA}, // em142 Gasurabazura
+ {MID: 0x90, Points: 0xFA}, // em144 Yama Kurai
+ {MID: 0x92, Points: 0x78}, // em146 Zinogre
+ {MID: 0x93, Points: 0x78}, // em147 Deviljho
+ {MID: 0x94, Points: 0x78}, // em148 Brachydios
+ {MID: 0x96, Points: 0xFA}, // em150 Toa Tesukatora
+ {MID: 0x97, Points: 0x78}, // em151 Barioth
+ {MID: 0x98, Points: 0x78}, // em152 Uragaan
+ {MID: 0x99, Points: 0x78}, // em153 Stygian Zinogre
+ {MID: 0x9A, Points: 0xFA}, // em154 Guanzorumu
+ {MID: 0x9E, Points: 0xFA}, // em158 Voljang
+ {MID: 0x9F, Points: 0x78}, // em159 Nargacuga
+ {MID: 0xA0, Points: 0xFA}, // em160 Keoaruboru
+ {MID: 0xA1, Points: 0xFA}, // em161 Zenaserisu
+ {MID: 0xA2, Points: 0x78}, // em162 Gore Magala
+ {MID: 0xA4, Points: 0x78}, // em164 Shagaru Magala
+ {MID: 0xA5, Points: 0x78}, // em165 Amatsu
+ {MID: 0xA6, Points: 0xFA}, // em166 Elzelion
+ {MID: 0xA9, Points: 0x78}, // em169 Seregios
+ {MID: 0xAA, Points: 0xFA}, // em170 Bogabadorumu
}
resp := byteframe.NewByteFrame()
@@ -2278,9 +2502,20 @@ func handleMsgMhfGetUdTacticsRewardList(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgMhfGetUdTacticsLog(s *Session, p mhfpacket.MHFPacket) {}
-func handleMsgMhfGetEquipSkinHist(s *Session, p mhfpacket.MHFPacket) {}
+func handleMsgMhfGetEquipSkinHist(s *Session, p mhfpacket.MHFPacket) {
+ pkt := p.(*mhfpacket.MsgMhfGetEquipSkinHist)
+ // Transmog / reskin system, bitmask of 3200 bytes length
+ // presumably divided by 5 sections for 5120 armour IDs covered
+ // +10,000 for actual ID to be unlocked by each bit
+ // Returning 3200 bytes of FF just unlocks everything for now
+ doSizedAckResp(s, pkt.AckHandle, bytes.Repeat([]byte{0xFF}, 0xC80))
+}
-func handleMsgMhfUpdateEquipSkinHist(s *Session, p mhfpacket.MHFPacket) {}
+func handleMsgMhfUpdateEquipSkinHist(s *Session, p mhfpacket.MHFPacket) {
+ pkt := p.(*mhfpacket.MsgMhfUpdateEquipSkinHist)
+ // sends a raw armour ID back that needs to be mapped into the persistent bitmask above (-10,000)
+ s.QueueAck(pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
+}
func handleMsgMhfGetUdTacticsFollower(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetUdTacticsFollower)
diff --git a/www/erupe/charsel.html b/www/erupe/charsel.html
index 6bed3e069..c70888949 100644
--- a/www/erupe/charsel.html
+++ b/www/erupe/charsel.html
@@ -66,7 +66,9 @@
-->
+
+
diff --git a/www/erupe/js/charsel.js b/www/erupe/js/charsel.js
index b993ff099..ea7a4b875 100644
--- a/www/erupe/js/charsel.js
+++ b/www/erupe/js/charsel.js
@@ -94,5 +94,13 @@ $(function() {
window.external.exitLauncher();
}, 500);
});
+
+ $("#btn_config").click(function() {
+ try{
+ window.external.openMhlConfig();
+ } catch(e){
+ createErrorAlert("Error on openMhlConfig: " + e);
+ }
+ })
});
diff --git a/www/erupe/js/login.js b/www/erupe/js/login.js
index 51774349c..ac58e74e5 100644
--- a/www/erupe/js/login.js
+++ b/www/erupe/js/login.js
@@ -36,13 +36,10 @@ $(function() {
});
$("#btn_config").click(function() {
- DoOpenMhlConfig()
+ try{
+ window.external.openMhlConfig();
+ } catch(e){
+ createErrorAlert("Error on openMhlConfig: " + e);
+ }
})
- function DoOpenMhlConfig() {
- "use strict";
- try {
- window.external.openMhlConfig()
- } catch (e) {}
- }
-
});