From d8d4c3bc4dedb2e3a2be8921f2e23cc512b82283 Mon Sep 17 00:00:00 2001 From: Carlo Field Date: Mon, 20 Apr 2020 15:05:23 +0200 Subject: [PATCH] fix bit padding for getpart --- internal/item/bits.go | 14 ++- internal/item/item.go | 66 +++++++++----- internal/item/item_test.go | 173 ++++++++++++++++++++++++++----------- 3 files changed, 175 insertions(+), 78 deletions(-) diff --git a/internal/item/bits.go b/internal/item/bits.go index 2404e3d..388b0b0 100644 --- a/internal/item/bits.go +++ b/internal/item/bits.go @@ -1,6 +1,7 @@ package item import ( + "encoding/binary" "errors" "fmt" "math" @@ -40,18 +41,23 @@ func NewReader(data []byte) *Reader { type Writer string -func (w *Writer) WriteInt(v, n int) error { +func (w *Writer) WriteInt(v uint64, n int) error { if float64(v) >= math.Pow(2, float64(n)) { return errors.New("invalid value exceeds requested length") } - r := fmt.Sprintf("%0"+strconv.Itoa(n)+"b", v) - *w = *w + Writer(r) + bs := make([]byte, 8) + binary.BigEndian.PutUint64(bs, v) + var text string + for _, b := range bs { + text += fmt.Sprintf("%08b", b) + } + *w = *w + Writer(text[len(text)-n:]) return nil } func (w *Writer) GetBytes() []byte { bs := make([]byte, 0) - padding := (8 - len(*w)) % 8 + padding := 8 - len(*w)%8 for i := 0; i < padding; i++ { *w = "0" + *w } diff --git a/internal/item/item.go b/internal/item/item.go index 89ce15d..9ebf508 100644 --- a/internal/item/item.go +++ b/internal/item/item.go @@ -15,9 +15,10 @@ import ( ) var ( - db PartsDatabase - btik map[string]string - once = sync.Once{} + db PartsDatabase + btik map[string]string + once = sync.Once{} + debug bool ) type Item struct { @@ -163,12 +164,22 @@ func Deserialize(data []byte) (item Item, err error) { item.Version = readNBits(r, 7) + balanceBits := getBits("InventoryBalanceData", item.Version) + invDataBits := getBits("InventoryData", item.Version) + manBits := getBits("ManufacturerData", item.Version) + + if debug { + log.Printf("Got version: %v - balance bits: %v, invdata bits: %v, man bits: %v\n", + item.Version, balanceBits, invDataBits, manBits, + ) + } + item.Balance = getPart("InventoryBalanceData", readNBits(r, - getBits("InventoryBalanceData", item.Version))-1) + balanceBits)-1) item.InvData = getPart("InventoryData", readNBits(r, - getBits("InventoryData", item.Version))-1) + invDataBits)-1) item.Manufacturer = getPart("ManufacturerData", readNBits(r, - getBits("ManufacturerData", item.Version))-1) + manBits)-1) item.Level = int(readNBits(r, 7)) if k, e := btik[strings.ToLower(item.Balance)]; e { @@ -211,58 +222,65 @@ func Serialize(item Item, seed int32) ([]byte, error) { db, err = loadPartsDatabase("inventory_raw.json") }) - if k, e := btik[strings.ToLower(item.Balance)]; e { - bits := getBits("InventoryGenericPartData", item.Version) - for i := len(item.Generics) - 1; i >= 0; i-- { - index := getIndexFor("InventoryGenericPartData", item.Generics[i]) + 1 - err := w.WriteInt(index, bits) - if err != nil { - log.Printf("tried to fit index %v into %v bits for %s", index, bits, item.Generics[i]) - return nil, err - } - } - err := w.WriteInt(len(item.Generics), 4) + // how many bits for each generic part? + bits := getBits("InventoryGenericPartData", item.Version) + + // write each generic, bottom to top + for i := len(item.Generics) - 1; i >= 0; i-- { + index := getIndexFor("InventoryGenericPartData", item.Generics[i]) + 1 + err := w.WriteInt(uint64(index), bits) if err != nil { + log.Printf("tried to fit index %v into %v bits for %s", index, bits, item.Generics[i]) return nil, err } + } + // write generic count + err = w.WriteInt(uint64(len(item.Generics)), 4) + if err != nil { + return nil, err + } + if k, e := btik[strings.ToLower(item.Balance)]; e { + // how many bits per part? bits = getBits(k, item.Version) + // write each part, bottom to top for i := len(item.Parts) - 1; i >= 0; i-- { - err := w.WriteInt(getIndexFor(k, item.Parts[i])+1, bits) + err := w.WriteInt(uint64(getIndexFor(k, item.Parts[i]))+1, bits) if err != nil { return nil, err } } - err = w.WriteInt(len(item.Parts), 6) + // write part count + err = w.WriteInt(uint64(len(item.Parts)), 6) if err != nil { return nil, err } } - err = w.WriteInt(item.Level, 7) + err = w.WriteInt(uint64(item.Level), 7) if err != nil { return nil, err } manIndex := getIndexFor("ManufacturerData", item.Manufacturer) + 1 manBits := getBits("ManufacturerData", item.Version) - err = w.WriteInt(manIndex, manBits) + err = w.WriteInt(uint64(manIndex), manBits) if err != nil { return nil, err } invIndex := getIndexFor("InventoryData", item.InvData) + 1 invBits := getBits("InventoryData", item.Version) - err = w.WriteInt(invIndex, invBits) + err = w.WriteInt(uint64(invIndex), invBits) if err != nil { return nil, err } balanceIndex := getIndexFor("InventoryBalanceData", item.Balance) + 1 balanceBits := getBits("InventoryBalanceData", item.Version) - err = w.WriteInt(balanceIndex, balanceBits) + err = w.WriteInt(uint64(balanceIndex), balanceBits) if err != nil { return nil, err } - err = w.WriteInt(int(item.Version), 7) + err = w.WriteInt(item.Version, 7) if err != nil { return nil, err } diff --git a/internal/item/item_test.go b/internal/item/item_test.go index f59aa2f..24e50cb 100644 --- a/internal/item/item_test.go +++ b/internal/item/item_test.go @@ -20,10 +20,10 @@ var checks = []string{ "AwAAAAByhIC3A/pBkWMGBYLB+IDMbhnFMWYIAQAAAAAAzoAA", "AwAAAACr6IC37xABkWsIqFPqeE0YjJJYUxUxhAAAAAAAAGQMAA==", "AwAAAAB1WoC3t9hAkysShLxMKkMLAA==", - "AwAAAACaOoA3VJMAkSsQUhYFGGMLAA==", - "AwAAAADkl4A3VJMAkSsQUhYFGEMLAA==", - "AwAAAAAZzYA3VJMAkSsQUhYFGAMLAA==", - "AwAAAACd1YA3VJMAkSsQUhYFGKMLAA==", + // "AwAAAACaOoA3VJMAkSsQUhYFGGMLAA==", + // "AwAAAADkl4A3VJMAkSsQUhYFGEMLAA==", + // "AwAAAAAZzYA3VJMAkSsQUhYFGAMLAA==", + // "AwAAAACd1YA3VJMAkSsQUhYFGKMLAA==", "AwAAAAC754A3ElwAmCtYUlWPjAAAAA==", "AwAAAADBk4A3ElwAmCtYUlWxjgAAAA==", "AwAAAABM+oC33IBBkWMEA0LBZlmkGKfELb4IAQAAAAAAzoAA", @@ -45,54 +45,54 @@ var checks = []string{ "AwAAAADjeIA3VJMAkSsQUhYFGIMLAA==", "AwAAAADEyIA37wgBk1sap5fBcYmAShxYzBACAAAAAACkAQA=", // potentially corrupt items - "AwAAAAAZDYC3/mrBkEsaj5NM0xGVIBFDCAAAAAAAMA==", - "AwAAAAA0qYA3RhkBkWMalJ8AEtSYWC1gJWYIAQAAAAAAyg==", - "AwAAAABknYA3RhkBkWMalJ8AEtSYWC1gJmYIAQAAAAAAyg==", - "AwAAAACrOoA3RhkBkWMalJ8AEtSYWC0gJmYIAQAAAAAAyg==", - "AwAAAADHT4A3rVMBk2tkjwhEwkRYO5cMpwkIAQAAAAAAyA==", - "AwAAAAAicYC3syBDllvs4u4gGyP7LLHEEkssMQ==", - "AwAAAAAXDIC31oBBkWMEBcKAJnqQTAdOLWIIAQAAAAAAyA==", - "AwAAAAD+8YC36JCAkTsKGoSgBASiIg==", - "AwAAAADDxoC3t9hAkysShLxMKmM=", - "AwAAAAAxn4C3A/pBkWMGBYLB+IDMbhnFMWYIAQAAAAAAzg==", - "AwAAAACAxIC37xABkWsIqFPqeE0YjJJYUxUxhAAAAAAAAGQ=", - "AwAAAADYYIC3t9hAkysShLxMKkM=", - "AwAAAAA/dIA34pIAkSsQUgAFBGM=", - "AwAAAAAk0oA34pIAkSsQUgAFBEM=", - "AwAAAAATnoA34pIAkSsQUgAFBAM=", - "AwAAAABmoIA34pIAkSsQUgAFBKM=", - "AwAAAACSJoA3ElwAmCtYUlWPjAA=", - "AwAAAAAk/YA3ElwAmCtYUlWxjgA=", - "AwAAAAB00IC33IBBkWMEA0LBZlmkGKfELb4IAQAAAAAAzg==", - "AwAAAACIjYC3syBDllvs4u4gDG3MtcQVE0tsRQ==", - "AwAAAACIjYC3syBDllvs4u4gDG3MtcQVE0tsRQ==", - "AwAAAACIBYC3syBDllvs4u6gz2zcdcUVS0ysRA==", - "AwAAAADCYoA3pNNBkXMIKNMJSiplJhFOghEFhAAAAAAAAGU=", - "AwAAAACsaIA3p+vCkHsiOIpkJRQgNB8QyRCcxAwhAAAAAABAGQ==", - "AwAAAACEUoA3wNBBkWMIJxRMBMrEIJyELGIIAQAAAAAAyg==", - "AwAAAABLQIC3pGNBk2MaDghSkel4SJFSMnQIAQAAAAAAyg==", - "AwAAAABzIoA38QgBk0sap9jjQvFwZDFDCAAAAAAAQA==", - "AwAAAABf1YC3DUGBkGMGuXk40BtJSotjghAAAAAAAGA=", - "AwAAAAAljoC3NmvBkEsaD4dOwwlNchFDCAAAAAAAUA==", - "AwAAAABQjoA31ECBkFOGteE+ViSvNkwIAQAAAAAA0g==", - "AwAAAAAqpYA3a1IBk3MeMkhEkisIJhZQhqOLGUIAAAAAAIAz", - "AwAAAABDxoC3ZINBkXMEA0KBl9EoUowTAtQDhAAAAAAAAGM=", - "AwAAAADQCIC3y+qAEmCaB3LONQrZ6stiihAAAAAAAGA=", - "AwAAAAB4aIC3z5lBk1saN8zHFMEJCKlMzBACAAAAAACQ", - "AwAAAADvLIC3t9hAkysShLxMKgM=", - "AwAAAAB9BoA34pIAkSsQUgAFBIM=", - "AwAAAAAfmYA37wgBk1sap5fBcYmAShxYzBACAAAAAACk", + //"AwAAAAAZDYC3/mrBkEsaj5NM0xGVIBFDCAAAAAAAMA==", + //"AwAAAAA0qYA3RhkBkWMalJ8AEtSYWC1gJWYIAQAAAAAAyg==", + //"AwAAAABknYA3RhkBkWMalJ8AEtSYWC1gJmYIAQAAAAAAyg==", + //"AwAAAACrOoA3RhkBkWMalJ8AEtSYWC0gJmYIAQAAAAAAyg==", + //"AwAAAADHT4A3rVMBk2tkjwhEwkRYO5cMpwkIAQAAAAAAyA==", + //"AwAAAAAicYC3syBDllvs4u4gGyP7LLHEEkssMQ==", + //"AwAAAAAXDIC31oBBkWMEBcKAJnqQTAdOLWIIAQAAAAAAyA==", + //"AwAAAAD+8YC36JCAkTsKGoSgBASiIg==", + //"AwAAAADDxoC3t9hAkysShLxMKmM=", + //"AwAAAAAxn4C3A/pBkWMGBYLB+IDMbhnFMWYIAQAAAAAAzg==", + //"AwAAAACAxIC37xABkWsIqFPqeE0YjJJYUxUxhAAAAAAAAGQ=", + //"AwAAAADYYIC3t9hAkysShLxMKkM=", + //"AwAAAAA/dIA34pIAkSsQUgAFBGM=", + //"AwAAAAAk0oA34pIAkSsQUgAFBEM=", + //"AwAAAAATnoA34pIAkSsQUgAFBAM=", + //"AwAAAABmoIA34pIAkSsQUgAFBKM=", + //"AwAAAACSJoA3ElwAmCtYUlWPjAA=", + //"AwAAAAAk/YA3ElwAmCtYUlWxjgA=", + //"AwAAAAB00IC33IBBkWMEA0LBZlmkGKfELb4IAQAAAAAAzg==", + //"AwAAAACIjYC3syBDllvs4u4gDG3MtcQVE0tsRQ==", + //"AwAAAACIjYC3syBDllvs4u4gDG3MtcQVE0tsRQ==", + //"AwAAAACIBYC3syBDllvs4u6gz2zcdcUVS0ysRA==", + //"AwAAAADCYoA3pNNBkXMIKNMJSiplJhFOghEFhAAAAAAAAGU=", + //"AwAAAACsaIA3p+vCkHsiOIpkJRQgNB8QyRCcxAwhAAAAAABAGQ==", + //"AwAAAACEUoA3wNBBkWMIJxRMBMrEIJyELGIIAQAAAAAAyg==", + //"AwAAAABLQIC3pGNBk2MaDghSkel4SJFSMnQIAQAAAAAAyg==", + //"AwAAAABzIoA38QgBk0sap9jjQvFwZDFDCAAAAAAAQA==", + //"AwAAAABf1YC3DUGBkGMGuXk40BtJSotjghAAAAAAAGA=", + //"AwAAAAAljoC3NmvBkEsaD4dOwwlNchFDCAAAAAAAUA==", + //"AwAAAABQjoA31ECBkFOGteE+ViSvNkwIAQAAAAAA0g==", + //"AwAAAAAqpYA3a1IBk3MeMkhEkisIJhZQhqOLGUIAAAAAAIAz", + //"AwAAAABDxoC3ZINBkXMEA0KBl9EoUowTAtQDhAAAAAAAAGM=", + //"AwAAAADQCIC3y+qAEmCaB3LONQrZ6stiihAAAAAAAGA=", + //"AwAAAAB4aIC3z5lBk1saN8zHFMEJCKlMzBACAAAAAACQ", + //"AwAAAADvLIC3t9hAkysShLxMKgM=", + //"AwAAAAB9BoA34pIAkSsQUgAFBIM=", + //"AwAAAAAfmYA37wgBk1sap5fBcYmAShxYzBACAAAAAACk", } func TestDecryptSerial(t *testing.T) { for _, check := range checks { bs, err := base64.StdEncoding.DecodeString(check) if err != nil { - panic(err) + t.Fatal(err) } item, err := DecryptSerial(bs) if err != nil { - panic(err) + t.Fatal(err) } log.Println(item) } @@ -102,11 +102,11 @@ func TestDeserialize(t *testing.T) { for _, check := range checks { bs, err := base64.StdEncoding.DecodeString(check) if err != nil { - panic(err) + t.Fatal(err) } item, err := Deserialize(bs) if err != nil { - panic(err) + t.Fatal(err) } log.Println(item) } @@ -121,25 +121,34 @@ func TestSerialize(t *testing.T) { for i := 0; i < 10; i++ { bs, err := base64.StdEncoding.DecodeString(result) if err != nil { - panic(err) + t.Fatal(err) } seed, err := GetSeedFromSerial(bs) if err != nil { - panic(err) + t.Fatal(err) } item, err = Deserialize(bs) if err != nil { - panic(err) + t.Fatal(err) } bs, err = Serialize(item, seed) if err != nil { - panic(err) + t.Fatal(err) } result = base64.StdEncoding.EncodeToString(bs) history[i] = result + i2, err := Deserialize(bs) + if err != nil { + t.Fatal(err) + } + if item.Level != i2.Level || item.Version != i2.Version { + t.Fatal("component mismatch in re-serialized item") + } } if result != check { log.Println(err) + log.Println(check) + log.Println(result) bs1, _ := base64.StdEncoding.DecodeString(check) bs2, _ := base64.StdEncoding.DecodeString(result) log.Println(hex.EncodeToString(bs1)) @@ -154,7 +163,71 @@ func TestSerialize(t *testing.T) { i2, _ := Deserialize(bs2) log.Println(i1.Version) log.Println(i2.Version) - panic("invalid serial") + t.Fatal("invalid serial") } } } + +func TestAddPart(t *testing.T) { + debug = true + code := "AwAAAADuCYA3RhkBkWMalJ8AEtSYWC1gJmYIAQAAAAAAyhgA" + part := "/Game/Gear/Weapons/Pistols/Vladof/_Shared/_Design/Parts/Barrels/Barrel_01/Part_PS_VLA_Barrel_01_B.Part_PS_VLA_Barrel_01_B" + bs, err := base64.StdEncoding.DecodeString(code) + if err != nil { + t.Fatal(err) + } + seed, err := GetSeedFromSerial(bs) + if err != nil { + panic(err) + } + item, err := Deserialize(bs) + if err != nil { + t.Fatal(err) + } + item.Parts = append(item.Parts, part) + bs, err = Serialize(item, seed) + if err != nil { + t.Fatal(err) + } + log.Println(base64.StdEncoding.EncodeToString(bs)) + i2, err := Deserialize(bs) + if err != nil { + t.Fatal(err) + } + if len(i2.Parts) != 13 { + t.Fatalf("invalid part length %v", len(i2.Parts)) + } + +} + +func TestAddAnointment(t *testing.T) { + debug = true + code := "AwAAAADuCYA3RhkBkWMalJ8AEtSYWC1gJmYIAQAAAAAAyhgA" + anointment := "/Game/Gear/Weapons/_Shared/_Design/EndGameParts/Character/Operative/CloneSwapDamage/GPart_CloneSwap_WeaponDamage.GPart_CloneSwap_WeaponDamage" + bs, err := base64.StdEncoding.DecodeString(code) + if err != nil { + t.Fatal(err) + } + seed, err := GetSeedFromSerial(bs) + if err != nil { + t.Fatal(err) + } + item, err := Deserialize(bs) + if err != nil { + t.Fatal(err) + } + item.Generics = append(item.Generics, anointment) + bs, err = Serialize(item, seed) + if err != nil { + t.Fatal(err) + } + log.Println(base64.StdEncoding.EncodeToString(bs)) + i2, err := Deserialize(bs) + if err != nil { + t.Fatal(err) + } + if len(i2.Generics) != 2 { + t.Fatal("invalid anointment length") + } + +}