如何计算 WoW 零售插件的 CurseForge API 指纹?

问题描述 投票:0回答:1

没有这方面的文档。

我了解到Murmur2用于散列东西(也没有在任何地方提到..)

但我不完全知道如何对插件进行哈希处理。 我是否需要对文件名、内容、什么内容、所有内容、按什么顺序进行哈希处理?

有人可以详细说明一个简单的示例插件吗

_retail_/Interface/Addons/AutoRepair/

.rw-rw-r-- 2.3k marco  9 Mar 20:22 AutoRepair.lua
.rw-rw-r--  249 marco  9 Mar 20:22 AutoRepair.toc
.rw-rw-r--  371 marco  9 Mar 20:22 AutoRepair.xml

需要做什么才能获取指纹哈希并将其发送到 CF API 以获得与 mod 的精确匹配?

我当前的方法是对文件的内容进行哈希处理,将这些指纹按升序排序,按该顺序将它们附加到字符串中,然后对该字符串进行哈希处理以获得最终的指纹。

但是,当使用该指纹查询 API 时,这并没有取得任何成功。

当前的方法

package main

import (
    "bufio"
    "fmt"
    "io/ioutil"
    "log"
    "os"
    "sort"
    "strconv"
    "strings"

    "github.com/joho/godotenv"
    mm "github.com/mistweaverco/go-murmur/murmur2"
)

func ReadFileAsBytes(path string) []byte {
    b, err := os.ReadFile(path)
    if err != nil {
        panic(err)
    }
    return b
}

func GetFileListRecursive(dir string) []string {
    files := []string{}
    fileInfos, err := ioutil.ReadDir(dir)
    if err != nil {
        log.Fatal(err)
    }
    for _, fileInfo := range fileInfos {
        if fileInfo.IsDir() {
            files = append(files, GetFileListRecursive(dir+fileInfo.Name()+"/")...)
        } else {
            files = append(files, dir+fileInfo.Name())
        }
    }
    return files
}

type Fingerprint struct {
    Title       string
    Hash        string
    Modified    int64
    AddonDir    string
    Fingerprint uint32
}

func GetFingerprint(addonDir string) Fingerprint {
    fingerprint := Fingerprint{}
    fingerprint.AddonDir = addonDir
    fullAddonDir := wowDirPrefix + "/Interface/Addons/" + addonDir + "/"
    toFingerprint := []string{}
    fingerprints := []uint32{}
    files := GetFileListRecursive(fullAddonDir)
    for _, file := range files {
        if strings.HasSuffix(file, ".lua") || strings.HasSuffix(file, ".toc") || strings.HasSuffix(file, ".xml") {
            toFingerprint = append(toFingerprint, file)
        }
    }
    sort.Strings(toFingerprint)
    fmt.Println(toFingerprint)
    for _, s := range toFingerprint {
        fc := ReadFileAsBytes(s)
        fingerprints = append(fingerprints, mm.MurmurHash2(fc, 1))
    }
    compositeFingerprint := ""
    sort.Slice(fingerprints, func(i, j int) bool { return fingerprints[i] < fingerprints[j] })
    fmt.Println(fingerprints)
    for _, f := range fingerprints {
        compositeFingerprint += strconv.Itoa(int(f))
    }

    fmt.Println(compositeFingerprint)
    fingerprint.Fingerprint = mm.MurmurHash2([]byte(compositeFingerprint), 1)
    return fingerprint
}

func env(k string, failOnMissing bool) string {
    value := os.Getenv(k)
    if value != "" {
        return value
    }
    if failOnMissing {
        log.Fatalf("%v environment variable is not set.", k)
    }
    return ""
}

func main() {
    godotenv.Load()

    wowDirPrefix = env("DEBUG_WOW_RETAIL_DIR", false)

    fp := GetFingerprint("AutoRepair")
    fmt.Println(fp)
}

程序的输出看起来像这样:

[Interface/Addons/AutoRepair/AutoRepair.lua Interface/Addons/AutoRepair/AutoRepair.toc Interface/Addons/AutoRepair/AutoRepair.xml]
[1276060341 2556314513 2805448843]
127606034125563145132805448843
{  0 AutoRepair 1798771541}

使用该输出,最终计算出的指纹是

1798771541
,但是使用该指纹查询 API 不会产生任何结果,而它应该产生
AutoRepair
插件。

api rest go world-of-warcraft murmurhash
1个回答
0
投票

有一个算法的 C++ 实现似乎可以在这里工作:https://github.com/meza/curseforge-fingerprint/blob/b15012c026c56ca89fad90f8cf9a8e140616e2c0/src/addon/fingerprint.cpp#L36-L90.

我在一些文件(不是《魔兽世界》,而是《我的世界》)上进行了测试,当提供给 HTTP API 时,得到了所需的结果。

Go 的 1:1 翻译:

func isWhiteSpace(b byte) bool {
    return b == '\t' || b == '\n' || b == '\r' || b == ' '
}

func computeCurseForgeFingerprintNormalizedLength(buf *bytes.Buffer) int {
    var len_no_whitespace int = 0
    bytes := buf.Bytes()

    for i := 0; i < buf.Len(); i++ {
        char := bytes[i]
        if !isWhiteSpace(char) {
            len_no_whitespace++
        }
    }

    return len_no_whitespace
}

// https://github.com/meza/curseforge-fingerprint/blob/b15012c026c56ca89fad90f8cf9a8e140616e2c0/src/addon/fingerprint.cpp#L36-L90
func CalculateCurseForgeFingerprint(buf *bytes.Buffer) uint32 {
    const multiplex = 1540483477
    len := buf.Len()
    bytes := buf.Bytes()

    var num1 uint32 = uint32(computeCurseForgeFingerprintNormalizedLength(buf))
    var num2 uint32 = 1 ^ num1

    var num3 uint32 = 0
    var num4 uint32 = 0

    for i := 0; i < len; i++ {
        b := bytes[i]

        if !isWhiteSpace(b) {
            num3 |= uint32(b) << num4
            num4 += 8

            if num4 == 32 {
                var num6 uint32 = num3 * multiplex
                var num7 uint32 = (num6 ^ num6>>24) * multiplex

                num2 = num2*multiplex ^ num7
                num3 = 0
                num4 = 0
            }
        }
    }

    if num4 > 0 {
        num2 = (num2 ^ num3) * multiplex
    }

    num6 := (num2 ^ num2>>13) * multiplex

    return num6 ^ num6>>15
}
© www.soinside.com 2019 - 2024. All rights reserved.