diff --git a/server/channelserver/compression/deltacomp/deltacomp.go b/server/channelserver/compression/deltacomp/deltacomp.go new file mode 100644 index 000000000..9c21b138c --- /dev/null +++ b/server/channelserver/compression/deltacomp/deltacomp.go @@ -0,0 +1,91 @@ +package deltacomp + +import ( + "bytes" + "io" +) + +func checkReadUint8(r *bytes.Reader) (uint8, error) { + b, err := r.ReadByte() + if err != nil { + return 0, err + } + return b, nil +} + +func checkReadUint16(r *bytes.Reader) (uint16, error) { + data := make([]byte, 2) + n, err := r.Read(data) + if err != nil { + return 0, err + } else if n != len(data) { + return 0, io.EOF + } + + return uint16(data[0])<<8 | uint16(data[1]), nil +} + +func readCount(r *bytes.Reader) (int, error) { + var count int + + count8, err := checkReadUint8(r) + if err != nil { + return 0, err + } + count = int(count8) + + if count == 0 { + count16, err := checkReadUint16(r) + if err != nil { + return 0, err + } + count = int(count16) + } + + return int(count), nil +} + +// ApplyDataDiff applies a delta data diff patch onto given base data. +func ApplyDataDiff(diff []byte, baseData []byte) []byte { + // Make a copy of the base data to return, + // (probably just make this modify the given slice in the future). + baseCopy := make([]byte, len(baseData)) + copy(baseCopy, baseData) + + patch := bytes.NewReader(diff) + + // The very first matchCount is +1 more than it should be, so we start at -1. + dataOffset := -1 + for { + // Read the amount of matching bytes. + matchCount, err := readCount(patch) + if err != nil { + // No more data + break + } + + dataOffset += matchCount + + // Read the amount of differing bytes. + differentCount, err := readCount(patch) + if err != nil { + // No more data + break + } + differentCount-- + + // Apply the patch bytes. + for i := 0; i < differentCount; i++ { + b, err := checkReadUint8(patch) + if err != nil { + panic("Invalid or misunderstood patch format!") + } + baseCopy[dataOffset+i] = b + } + + dataOffset += differentCount - 1 + + } + + return baseCopy +} diff --git a/server/channelserver/compression/deltacomp/deltacomp_test.go b/server/channelserver/compression/deltacomp/deltacomp_test.go new file mode 100644 index 000000000..246622f3a --- /dev/null +++ b/server/channelserver/compression/deltacomp/deltacomp_test.go @@ -0,0 +1,113 @@ +package deltacomp + +import ( + "bytes" + "encoding/hex" + "fmt" + "io/ioutil" + "testing" + + "github.com/Andoryuuta/Erupe/server/channelserver/compression/nullcomp" +) + +var tests = []struct { + before string + patches []string + after string +}{ + { + "hunternavi_0_before.bin", + []string{ + "hunternavi_0_patch_0.bin", + "hunternavi_0_patch_1.bin", + }, + "hunternavi_0_after.bin", + }, + { + // From "Character Progression 1 Creation-NPCs-Tours" + "hunternavi_1_before.bin", + []string{ + "hunternavi_1_patch_0.bin", + "hunternavi_1_patch_1.bin", + "hunternavi_1_patch_2.bin", + "hunternavi_1_patch_3.bin", + "hunternavi_1_patch_4.bin", + "hunternavi_1_patch_5.bin", + "hunternavi_1_patch_6.bin", + "hunternavi_1_patch_7.bin", + "hunternavi_1_patch_8.bin", + "hunternavi_1_patch_9.bin", + "hunternavi_1_patch_10.bin", + "hunternavi_1_patch_11.bin", + "hunternavi_1_patch_12.bin", + "hunternavi_1_patch_13.bin", + "hunternavi_1_patch_14.bin", + "hunternavi_1_patch_15.bin", + "hunternavi_1_patch_16.bin", + "hunternavi_1_patch_17.bin", + "hunternavi_1_patch_18.bin", + "hunternavi_1_patch_19.bin", + "hunternavi_1_patch_20.bin", + "hunternavi_1_patch_21.bin", + "hunternavi_1_patch_22.bin", + "hunternavi_1_patch_23.bin", + "hunternavi_1_patch_24.bin", + }, + "hunternavi_1_after.bin", + }, + { + // From "Progress Gogo GRP Grind 9 and Armor Upgrades and Partner Equip and Lost Cat and Manager talk and Pugi Order" + // Not really sure this one counts as a valid test as the input and output are exactly the same. The patches cancel each other out. + "platedata_0_before.bin", + []string{ + "platedata_0_patch_0.bin", + "platedata_0_patch_1.bin", + }, + "platedata_0_after.bin", + }, +} + +func readTestDataFile(filename string) []byte { + data, err := ioutil.ReadFile(fmt.Sprintf("./test_data/%s", filename)) + if err != nil { + panic(err) + } + return data +} + +func TestDeltaPatch(t *testing.T) { + for k, tt := range tests { + testname := fmt.Sprintf("delta_patch_test_%d", k) + t.Run(testname, func(t *testing.T) { + // Load the test binary data. + beforeData, err := nullcomp.Decompress(readTestDataFile(tt.before)) + if err != nil { + t.Error(err) + } + + var patches [][]byte + for _, patchName := range tt.patches { + patchData := readTestDataFile(patchName) + patches = append(patches, patchData) + } + + afterData, err := nullcomp.Decompress(readTestDataFile(tt.after)) + if err != nil { + t.Error(err) + } + + // Now actually test calling ApplyDataDiff. + data := beforeData + + // Apply the patches in order. + for i, patch := range patches { + fmt.Println("patch index: ", i) + data = ApplyDataDiff(patch, data) + } + + if !bytes.Equal(data, afterData) { + t.Errorf("got out\n\t%s\nwant\n\t%s", hex.Dump(data), hex.Dump(afterData)) + } + }) + } +} diff --git a/server/channelserver/compression/deltacomp/test_data/hunternavi_0_after.bin b/server/channelserver/compression/deltacomp/test_data/hunternavi_0_after.bin new file mode 100644 index 000000000..d4de4341b Binary files /dev/null and b/server/channelserver/compression/deltacomp/test_data/hunternavi_0_after.bin differ diff --git a/server/channelserver/compression/deltacomp/test_data/hunternavi_0_before.bin b/server/channelserver/compression/deltacomp/test_data/hunternavi_0_before.bin new file mode 100644 index 000000000..3268f9834 Binary files /dev/null and b/server/channelserver/compression/deltacomp/test_data/hunternavi_0_before.bin differ diff --git a/server/channelserver/compression/deltacomp/test_data/hunternavi_0_patch_0.bin b/server/channelserver/compression/deltacomp/test_data/hunternavi_0_patch_0.bin new file mode 100644 index 000000000..0d460ec60 Binary files /dev/null and b/server/channelserver/compression/deltacomp/test_data/hunternavi_0_patch_0.bin differ diff --git a/server/channelserver/compression/deltacomp/test_data/hunternavi_0_patch_1.bin b/server/channelserver/compression/deltacomp/test_data/hunternavi_0_patch_1.bin new file mode 100644 index 000000000..86b68677a Binary files /dev/null and b/server/channelserver/compression/deltacomp/test_data/hunternavi_0_patch_1.bin differ diff --git a/server/channelserver/compression/deltacomp/test_data/hunternavi_1_after.bin b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_after.bin new file mode 100644 index 000000000..a16e230f0 Binary files /dev/null and b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_after.bin differ diff --git a/server/channelserver/compression/deltacomp/test_data/hunternavi_1_before.bin b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_before.bin new file mode 100644 index 000000000..a3a35949d Binary files /dev/null and b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_before.bin differ diff --git a/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_0.bin b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_0.bin new file mode 100644 index 000000000..4d4087792 Binary files /dev/null and b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_0.bin differ diff --git a/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_1.bin b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_1.bin new file mode 100644 index 000000000..ebe6eadcb Binary files /dev/null and b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_1.bin differ diff --git a/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_10.bin b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_10.bin new file mode 100644 index 000000000..2ede80181 Binary files /dev/null and b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_10.bin differ diff --git a/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_11.bin b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_11.bin new file mode 100644 index 000000000..291775a3b Binary files /dev/null and b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_11.bin differ diff --git a/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_12.bin b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_12.bin new file mode 100644 index 000000000..28b1ab96c Binary files /dev/null and b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_12.bin differ diff --git a/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_13.bin b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_13.bin new file mode 100644 index 000000000..f524a87c7 Binary files /dev/null and b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_13.bin differ diff --git a/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_14.bin b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_14.bin new file mode 100644 index 000000000..d5a804b15 Binary files /dev/null and b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_14.bin differ diff --git a/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_15.bin b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_15.bin new file mode 100644 index 000000000..1f5144f5c Binary files /dev/null and b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_15.bin differ diff --git a/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_16.bin b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_16.bin new file mode 100644 index 000000000..29bbc5268 Binary files /dev/null and b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_16.bin differ diff --git a/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_17.bin b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_17.bin new file mode 100644 index 000000000..e524310b4 Binary files /dev/null and b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_17.bin differ diff --git a/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_18.bin b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_18.bin new file mode 100644 index 000000000..8559f21c8 Binary files /dev/null and b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_18.bin differ diff --git a/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_19.bin b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_19.bin new file mode 100644 index 000000000..81745fbf7 Binary files /dev/null and b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_19.bin differ diff --git a/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_2.bin b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_2.bin new file mode 100644 index 000000000..ab4f9a69f Binary files /dev/null and b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_2.bin differ diff --git a/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_20.bin b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_20.bin new file mode 100644 index 000000000..9c0d8cace Binary files /dev/null and b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_20.bin differ diff --git a/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_21.bin b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_21.bin new file mode 100644 index 000000000..4f0d5a3b0 Binary files /dev/null and b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_21.bin differ diff --git a/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_22.bin b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_22.bin new file mode 100644 index 000000000..274523f37 Binary files /dev/null and b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_22.bin differ diff --git a/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_23.bin b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_23.bin new file mode 100644 index 000000000..7e1cf8667 Binary files /dev/null and b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_23.bin differ diff --git a/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_24.bin b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_24.bin new file mode 100644 index 000000000..f6796439a Binary files /dev/null and b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_24.bin differ diff --git a/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_3.bin b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_3.bin new file mode 100644 index 000000000..830eae49f Binary files /dev/null and b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_3.bin differ diff --git a/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_4.bin b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_4.bin new file mode 100644 index 000000000..a034700bc Binary files /dev/null and b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_4.bin differ diff --git a/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_5.bin b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_5.bin new file mode 100644 index 000000000..342f05115 Binary files /dev/null and b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_5.bin differ diff --git a/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_6.bin b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_6.bin new file mode 100644 index 000000000..982996700 Binary files /dev/null and b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_6.bin differ diff --git a/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_7.bin b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_7.bin new file mode 100644 index 000000000..05ea1cc75 Binary files /dev/null and b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_7.bin differ diff --git a/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_8.bin b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_8.bin new file mode 100644 index 000000000..a96085c82 Binary files /dev/null and b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_8.bin differ diff --git a/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_9.bin b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_9.bin new file mode 100644 index 000000000..3844e747c Binary files /dev/null and b/server/channelserver/compression/deltacomp/test_data/hunternavi_1_patch_9.bin differ diff --git a/server/channelserver/compression/deltacomp/test_data/platedata_0_after.bin b/server/channelserver/compression/deltacomp/test_data/platedata_0_after.bin new file mode 100644 index 000000000..008e412a8 Binary files /dev/null and b/server/channelserver/compression/deltacomp/test_data/platedata_0_after.bin differ diff --git a/server/channelserver/compression/deltacomp/test_data/platedata_0_before.bin b/server/channelserver/compression/deltacomp/test_data/platedata_0_before.bin new file mode 100644 index 000000000..008e412a8 Binary files /dev/null and b/server/channelserver/compression/deltacomp/test_data/platedata_0_before.bin differ diff --git a/server/channelserver/compression/deltacomp/test_data/platedata_0_patch_0.bin b/server/channelserver/compression/deltacomp/test_data/platedata_0_patch_0.bin new file mode 100644 index 000000000..444b62e6b Binary files /dev/null and b/server/channelserver/compression/deltacomp/test_data/platedata_0_patch_0.bin differ diff --git a/server/channelserver/compression/deltacomp/test_data/platedata_0_patch_1.bin b/server/channelserver/compression/deltacomp/test_data/platedata_0_patch_1.bin new file mode 100644 index 000000000..d113e5e47 Binary files /dev/null and b/server/channelserver/compression/deltacomp/test_data/platedata_0_patch_1.bin differ diff --git a/server/channelserver/compression/nullcomp/nullcomp.go b/server/channelserver/compression/nullcomp/nullcomp.go new file mode 100644 index 000000000..ed62e99ca --- /dev/null +++ b/server/channelserver/compression/nullcomp/nullcomp.go @@ -0,0 +1,93 @@ +package nullcomp + +import ( + "bytes" + "io" +) + +// Decompress decompresses null-compressesed data. +func Decompress(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 + } + + // Just return the data if it doesn't contain the cmp header. + if !bytes.Equal(header, []byte("cmp\x2020110113\x20\x20\x20\x00")) { + return compData, nil + } + + 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 +} + +// Compress null compresses give given data. +func Compress(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++ + + // Flush the null-count if it gets to 255, start on the next null count. + if nullCount == 255 { + output = append(output, []byte{0xFF, 0x00}...) + nullCount = 0 + } + } + } else { + output = append(output, b) + } + } + return output, nil +} diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 38665bc2e..60d7457fd 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -6,7 +6,6 @@ import ( "encoding/base64" "encoding/hex" "fmt" - "io" "io/ioutil" "os" "path/filepath" @@ -14,6 +13,8 @@ import ( "time" "github.com/Andoryuuta/Erupe/network/mhfpacket" + "github.com/Andoryuuta/Erupe/server/channelserver/compression/deltacomp" + "github.com/Andoryuuta/Erupe/server/channelserver/compression/nullcomp" "github.com/Andoryuuta/byteframe" "go.uber.org/zap" "golang.org/x/text/encoding/japanese" @@ -53,127 +54,6 @@ 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, @@ -1023,28 +903,88 @@ func handleMsgSysReserve5F(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfSavedata(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfSavedata) + err := ioutil.WriteFile(fmt.Sprintf("savedata\\%d.bin", time.Now().Unix()), pkt.RawDataPayload, 0644) if err != nil { s.logger.Fatal("Error dumping savedata", 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) - // Temporary server launcher response stuff - // 0x1F715 Weapon Class - // 0x1FDF6 HR (small_gr_level) - // 0x88 Character Name - saveFile, _ := saveDecompress(pkt.RawDataPayload) - _, err = s.server.db.Exec("UPDATE characters SET weapon=$1 WHERE id=$2", uint16(saveFile[128789]), s.charID) - x := uint16(saveFile[130550])<<8 | uint16(saveFile[130551]) - _, err = s.server.db.Exec("UPDATE characters SET small_gr_level=$1 WHERE id=$2", uint16(x), s.charID) - _, err = s.server.db.Exec("UPDATE characters SET name=$1 WHERE id=$2", strings.SplitN(string(saveFile[88:100]), "\x00", 2)[0], s.charID) + // Var to hold the decompressed savedata for updating the launcher response fields. + var decompressedData []byte + + if pkt.SaveType == 1 { + // Diff-based update. + + // Load existing save + var data []byte + err := s.server.db.QueryRow("SELECT savedata FROM characters WHERE id = $1", s.charID).Scan(&data) + if err != nil { + s.logger.Fatal("Failed to get savedata from db", zap.Error(err)) + } + + // Decompress + s.logger.Info("Decompressing...") + data, err = nullcomp.Decompress(data) + if err != nil { + s.logger.Fatal("Failed to decompress savedata from db", zap.Error(err)) + } + + // Perform diff. + data = deltacomp.ApplyDataDiff(pkt.RawDataPayload, data) + + // Make a copy for updating the launcher fields. + decompressedData = make([]byte, len(data)) + copy(decompressedData, data) + + // Compress it to write back to db + s.logger.Info("Diffing...") + saveOutput, err := nullcomp.Compress(data) + if err != nil { + s.logger.Fatal("Failed to diff and compress savedata", zap.Error(err)) + } + + _, err = s.server.db.Exec("UPDATE characters SET savedata=$1 WHERE id=$2", saveOutput, s.charID) + if err != nil { + s.logger.Fatal("Failed to update savedata in db", zap.Error(err)) + } + + s.logger.Info("Wrote recompressed savedata back to DB.") + } else { + // Regular blob update. + + _, 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, no handling implemented. Not saving.") + + decompressedData, err = nullcomp.Decompress(pkt.RawDataPayload) // For updating launcher fields. + if err != nil { + s.logger.Fatal("Failed to decompress savedata from packet", zap.Error(err)) + } + } + + // Temporary server launcher response stuff + // 0x1F715 Weapon Class + // 0x1FDF6 HR (small_gr_level) + // 0x88 Character Name + _, err = s.server.db.Exec("UPDATE characters SET weapon=$1 WHERE id=$2", uint16(decompressedData[128789]), s.charID) + if err != nil { + s.logger.Fatal("Failed to character weapon in db", zap.Error(err)) + } + + gr := uint16(decompressedData[130550])<<8 | uint16(decompressedData[130551]) + s.logger.Info("Setting db field", zap.Uint16("gr_override_level", gr)) + + // We have to use `gr_override_level` (uint16), not `small_gr_level` (uint8) to store this. + _, err = s.server.db.Exec("UPDATE characters SET gr_override_mode=true, gr_override_level=$1 WHERE id=$2", gr, s.charID) + if err != nil { + s.logger.Fatal("Failed to update character gr_override_level in db", zap.Error(err)) + } + + _, err = s.server.db.Exec("UPDATE characters SET name=$1 WHERE id=$2", strings.SplitN(string(decompressedData[88:100]), "\x00", 2)[0], s.charID) + if err != nil { + s.logger.Fatal("Failed to update character name in db", zap.Error(err)) } s.QueueAck(pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) @@ -1658,34 +1598,41 @@ func handleMsgMhfLoadPlateData(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 + + // 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) + + // Decompress + s.logger.Info("Decompressing...") + data, err = nullcomp.Decompress(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)) + + // Perform diff and compress it to write back to db + s.logger.Info("Diffing...") + saveOutput, err := nullcomp.Compress(deltacomp.ApplyDataDiff(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)) } + + s.logger.Info("Wrote recompressed platedata back to DB.") } else { // simply update database, no extra processing _, err := s.server.db.Exec("UPDATE characters SET platedata=$1 WHERE id=$2", pkt.RawDataPayload, s.charID) @@ -1693,6 +1640,7 @@ func handleMsgMhfSavePlateData(s *Session, p mhfpacket.MHFPacket) { 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}) } @@ -1713,36 +1661,41 @@ func handleMsgMhfLoadPlateBox(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 + + // 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) + + // Decompress + s.logger.Info("Decompressing...") + data, err = nullcomp.Decompress(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)) + + // Perform diff and compress it to write back to db + s.logger.Info("Diffing...") + saveOutput, err := nullcomp.Compress(deltacomp.ApplyDataDiff(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.") } + s.logger.Info("Wrote recompressed platebox 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) @@ -2020,7 +1973,6 @@ func handleMsgMhfLoadHunterNavi(s *Session, p mhfpacket.MHFPacket) { if len(data) > 0 { doSizedAckResp(s, pkt.AckHandle, data) - //doSizedAckResp(s, pkt.AckHandle, data) } else { // set first byte to 1 to avoid pop up every time without save body := make([]byte, 0x226) @@ -2037,8 +1989,31 @@ func handleMsgMhfSaveHunterNavi(s *Session, p mhfpacket.MHFPacket) { } if pkt.IsDataDiff { - // https://gist.github.com/Andoryuuta/9c524da7285e4b5ca7e52e0fc1ca1daf - // doesn't seem fully consistent with platedata? + var data []byte + + // Load existing save + err := s.server.db.QueryRow("SELECT hunternavi FROM characters WHERE id = $1", s.charID).Scan(&data) + if err != nil { + s.logger.Fatal("Failed to get hunternavi savedata from db", zap.Error(err)) + } + + // Check if we actually had any hunternavi data, using a blank buffer if not. + // This is requried as the client will try to send a diff after character creation without a prior MsgMhfSaveHunterNavi packet. + if len(data) == 0 { + data = make([]byte, 0x226) + data[0] = 1 // set first byte to 1 to avoid pop up every time without save + } + + // Perform diff and compress it to write back to db + s.logger.Info("Diffing...") + saveOutput := deltacomp.ApplyDataDiff(pkt.RawDataPayload, data) + + _, err = s.server.db.Exec("UPDATE characters SET hunternavi=$1 WHERE id=$2", saveOutput, s.charID) + if err != nil { + s.logger.Fatal("Failed to update hunternavi savedata in db", zap.Error(err)) + } + + s.logger.Info("Wrote recompressed hunternavi back to DB.") } else { // simply update database, no extra processing _, err := s.server.db.Exec("UPDATE characters SET hunternavi=$1 WHERE id=$2", pkt.RawDataPayload, s.charID)