//go:build windows package cafegame import ( "encoding/binary" "encoding/hex" "encoding/json" "errors" "fmt" "os" "path/filepath" "strconv" "strings" ) // IsGameDirByYGX 云更新 id从40000开始 func IsGameDirByYGX(dir string) (bool, AppData, error) { info := AppData{} gamePatch := filepath.Join(dir, "gamepatch") ok, err := pathExists(gamePatch) if err != nil { return false, info, err } if !ok { return false, info, nil } gconfig := filepath.Join(gamePatch, "gconfig.ini") ok, err = pathExists(gconfig) if err != nil { return false, info, err } if !ok { return false, info, nil } id, _ := getIdByNumberIco(dir) if id < 40000 { return false, info, nil } info.AppId = id info.AppName = filepath.Base(dir) info.AppDir = dir info.AppSource = SourceYGX return true, info, nil } // IsGameDirByStory 蘑菇 id从8000开始 func IsGameDirByStory(dir string) (bool, AppData, error) { info := AppData{} gamePatch := filepath.Join(dir, "gamepatch") ok, err := pathExists(gamePatch) if err != nil { return false, info, err } if !ok { return false, info, nil } gconfig := filepath.Join(gamePatch, "gconfig.ini") ok, err = pathExists(gconfig) if err != nil { return false, info, err } if !ok { return false, info, nil } id, _ := getIdByNumberIco(dir) if id < 10000 { return false, info, nil } info.AppId = id info.AppName = filepath.Base(dir) info.AppDir = dir info.AppSource = SourceStory return true, info, nil } // IsGameDirByWWDS 网维大师 ID 从 10001 开始 func IsGameDirByWWDS(dir string) (bool, AppData, error) { info := AppData{} SunwardPkg := filepath.Join(dir, "SunwardPkg.idx") ok, err := pathExists(SunwardPkg) if err != nil { return false, info, err } if !ok { return false, info, nil } SunwardPkgCks := filepath.Join(dir, "SunwardPkg.cks") ok, err = pathExists(SunwardPkgCks) if err != nil { return false, info, err } if !ok { fmt.Printf("没有cks文件 dir:%s\n", dir) return false, info, nil } // 不能有这文件 这是盖伦文件 GameIndex := filepath.Join(dir, "GameIndex.txt") ok, err = pathExists(GameIndex) if err != nil { return false, info, err } if ok { return false, info, nil } // 不能有这文件,这是易乐游文件 GameInfodb := filepath.Join(dir, "GameInfodb.gz") ok, err = pathExists(GameInfodb) if err != nil { return false, info, err } if ok { return false, info, nil } // 不能有这文件 这是好司机文件 hsjGame := filepath.Join(dir, "hsjGame.exe") ok, err = pathExists(hsjGame) if err != nil { return false, info, err } if ok { return false, info, nil } id, _ := getIdBySunwardPkg(dir) if id < 10000 { return false, info, nil } info.AppId = id info.AppName = filepath.Base(dir) info.AppDir = dir info.AppSource = SourceWWDS return true, info, nil } // IsGameDirByGoodGame 盖伦 id应该是从 1001开始 steam是第一个游戏 1011 func IsGameDirByGoodGame(dir string) (bool, AppData, error) { info := AppData{} GameIndex := filepath.Join(dir, "GameIndex.txt") ok, err := pathExists(GameIndex) if err != nil { return false, info, err } if !ok { return false, info, nil } GameCksum := filepath.Join(dir, "GameCksum.txt") ok, err = pathExists(GameCksum) if err != nil { return false, info, err } if !ok { return false, info, nil } id, _ := getIdBySunwardPkg(dir) if id < 1000 { return false, info, nil } info.AppId = id info.AppName = filepath.Base(dir) info.AppDir = dir info.AppSource = SourceGoodGame return true, info, nil } // IsGameDirByYLY 易乐游 func IsGameDirByYLY(dir string) (bool, AppData, error) { info := AppData{} GameInfodb := filepath.Join(dir, "GameInfodb.gz") ok, err := pathExists(GameInfodb) if err != nil { return false, info, err } if !ok { return false, info, nil } Gamever := filepath.Join(dir, "Gamever.ver") ok, err = pathExists(Gamever) if err != nil { return false, info, err } if !ok { return false, info, nil } // TODO: 没有游戏ID,只有GUID guid, _ := GetGuidByYLY(dir) if guid == "{00000000-0000-0000-0000-000000000000}" { return false, info, nil } info.AppId = 0 info.AppName = filepath.Base(dir) info.AppDir = dir info.AppSource = SourceYLY info.AppGuid = guid return true, info, nil } // IsGameDirByHaoSiJi 好司机 func IsGameDirByHaoSiJi(dir string) (bool, AppData, error) { info := AppData{} hsjGame := filepath.Join(dir, "hsjGame.exe") ok, err := pathExists(hsjGame) if err != nil { return false, info, err } if !ok { return false, info, nil } hsjLoader := filepath.Join(dir, "hsjLoader.exe") ok, err = pathExists(hsjLoader) if !ok { return false, info, nil } if err != nil { return false, info, err } SunwardPkg := filepath.Join(dir, "SunwardPkg.idx") ok, err = pathExists(SunwardPkg) if !ok { return false, info, nil } if err != nil { return false, info, err } id, _ := getIdBySunwardPkg(dir) if id == 0 { return false, info, nil } guid, _ := GetGuidByYLY(dir) if guid == "{00000000-0000-0000-0000-000000000000}" { return false, info, nil } info.AppId = id info.AppName = filepath.Base(dir) info.AppDir = dir info.AppSource = SourceHaoSiJi info.AppGuid = guid return true, info, nil } // IsGameDirByMTGX 马蹄更新 id从10000开始 func IsGameDirByMTGX(dir string) (bool, AppData, error) { info := AppData{} patchdata := filepath.Join(dir, "patchdata") ok, err := pathExists(patchdata) if err != nil { return false, info, err } if !ok { return false, info, nil } config := filepath.Join(patchdata, "config.ini") ok, err = pathExists(config) if err != nil { return false, info, err } if !ok { return false, info, nil } ndstorrent := filepath.Join(dir, "ndstorrent.idx") ok, err = pathExists(ndstorrent) if err != nil { return false, info, err } if !ok { return false, info, nil } id, _ := getIdByNumberIco(dir) if id < 10000 { return false, info, nil } info.AppId = id info.AppName = filepath.Base(dir) info.AppDir = dir info.AppSource = SourceMTGX return true, info, nil } // IsGameDirByFast 火箭 id从8000开始 func IsGameDirByFast(dir string) (bool, AppData, error) { info := AppData{} patchdata := filepath.Join(dir, "patchdata") ok, err := pathExists(patchdata) if err != nil { return false, info, err } if !ok { return false, info, nil } rkttorrent := filepath.Join(dir, "rkttorrent.idx") ok, err = pathExists(rkttorrent) if err != nil { return false, info, err } if !ok { return false, info, nil } gconfig := filepath.Join(patchdata, "config.ini") ok, err = pathExists(gconfig) if err != nil { return false, info, err } if !ok { return false, info, nil } id, _ := getIdByNumberIco(dir) if id < 8000 { return false, info, nil } info.AppId = id info.AppName = filepath.Base(dir) info.AppDir = dir info.AppSource = SourceFast return true, info, nil } // IsGameDirByWfGame WF游戏 func IsGameDirByWfGame(dir string) (bool, AppData, error) { info := AppData{} ndsgameres := filepath.Join(dir, "ndsgameres.index") ok, err := pathExists(ndsgameres) if err != nil { return false, info, err } if !ok { return false, info, nil } dd_launcher := filepath.Join(dir, "dd_launcher.json") ok, err = pathExists(dd_launcher) if err != nil { return false, info, err } if !ok { return false, info, nil } id, _ := getIdByDdLauncher(dir) if id == 0 { return false, info, nil } info.AppId = id info.AppName = filepath.Base(dir) info.AppDir = dir info.AppSource = SourceWfGame return true, info, nil } // 网维大师 盖伦 好司机 func getIdBySunwardPkg(dir string) (int, error) { path := filepath.Join(dir, "SunwardPkg.idx") file, err := os.Open(path) if err != nil { fmt.Println("Error opening file:", err) return 0, err } defer file.Close() // Read a 4-byte integer var num int32 err = binary.Read(file, binary.BigEndian, &num) if err != nil { fmt.Println("Error reading integer:", err) return 0, err } // fmt.Println("Read integer:", num) return int(num), nil } // 云更新 蘑菇 火箭使用 func getIdByNumberIco(dir string) (int, error) { // 获该目录下所有文件 fileList, _ := getFileList(dir) for _, s := range fileList { ext := filepath.Ext(s) // 判断是否ico图标 if strings.ToLower(ext) == ".ico" { // fmt.Printf("ico: %s\n", s) // 通过ico图标的数字确定游戏ID fileName := strings.TrimSuffix(filepath.Base(s), filepath.Ext(s)) if IsAllNumberHandle(fileName) { gameId, _ := strconv.Atoi(fileName) if gameId >= 8000 { return gameId, nil } } } else { continue } } return 0, nil } // DdLauncherJson DDL Launcher 平台游戏信息(JSON 配置) type DdLauncherJson struct { Gameid int `json:"gameid"` } // getIdByDdLauncher 从 DDL Launcher 配置文件中读取游戏 ID func getIdByDdLauncher(dir string) (int, error) { path := filepath.Join(dir, "dd_launcher.json") ok, err := pathExists(path) if err != nil { return 0, err } if !ok { return 0, errors.New("文件不存在") } buf, err := os.ReadFile(path) if err != nil { fmt.Println(err) return 0, err } data := DdLauncherJson{} err = json.Unmarshal(buf, &data) if err != nil { fmt.Println(err) return 0, err } if data.Gameid == 0 { return 0, errors.New("游戏ID为空") } return data.Gameid, nil } // GetGuidByYLY 从易乐游目录配置中读取游戏 GUID func GetGuidByYLY(dir string) (string, error) { path := filepath.Join(dir, "$ResMetadata$", "ResAppData.dat") ok, err := pathExists(path) if err != nil { return "", err } if !ok { return "", errors.New("文件不存在") } file, err := os.Open(path) if err != nil { fmt.Println(err) return "", err } defer file.Close() // 设置读取起始位置为第25个字节 _, err = file.Seek(24, 0) if err != nil { fmt.Println(err) return "", err } // 创建一个字节切片来存储GUID buf := make([]byte, 16) // 读取GUID到字节切片中 _, err = file.Read(buf) if err != nil { fmt.Println(err) return "", err } guidInt32 := binary.LittleEndian.Uint32(buf[:4]) guidInt16_1 := binary.LittleEndian.Uint16(buf[4:6]) guidInt16_2 := binary.LittleEndian.Uint16(buf[6:8]) guidBytes_1 := buf[8:10] guidBytes_2 := buf[10:] guid := fmt.Sprintf("{%08x-%04x-%04x-%s-%s}", guidInt32, guidInt16_1, guidInt16_2, hex.EncodeToString(guidBytes_1), hex.EncodeToString(guidBytes_2)) guid = strings.ToTitle(guid) return guid, err } // GetGameInfoByDir 获取游戏信息 从目录获取 func GetGameInfoByDir(root string) (AppData, error) { // fmt.Println(root) data := AppData{} // 云更新 ok, data, err := IsGameDirByYGX(root) if ok { return data, err } // 网维大师 ok, data, err = IsGameDirByWWDS(root) if ok { return data, err } // 易乐游 ok, data, err = IsGameDirByYLY(root) if ok { return data, err } // 马蹄更新 ok, data, err = IsGameDirByMTGX(root) if ok { return data, err } // 蘑菇 ok, data, err = IsGameDirByStory(root) if ok { return data, err } // 盖伦 ok, data, err = IsGameDirByGoodGame(root) if ok { return data, err } // 好司机 ok, data, err = IsGameDirByHaoSiJi(root) if ok { return data, err } // 火箭 ok, data, err = IsGameDirByFast(root) if ok { return data, err } // WF游戏 ok, data, err = IsGameDirByWfGame(root) if ok { return data, err } return data, nil } // GetGameInfoByPath 获取游戏信息 从路径获取 func GetGameInfoByPath(exePath string) (AppData, error) { path := filepath.ToSlash(exePath) pathArr := strings.Split(path, "/") // 3个/ 比如 D:/网络游戏/英雄联盟/xxx.exe // 长度3 是 D:/英雄联盟/xxx.exe if len(pathArr) < 3 { return AppData{}, errors.New("不支持1级目录") } dir := "" // 连接二级目录 for i, s := range pathArr { // 最后是文件名,不检测 if i >= len(pathArr)-1 { break } // 最多支持3级目录 如 D:/网络游戏/腾讯游戏/英雄联盟/xxx.exe if i > 4 { break } if i == 0 { dir = s } else { dir = filepath.Join(dir, string(filepath.Separator), s) } // 获取游戏信息 info, err := GetGameInfoByDir(dir) if err != nil { return info, err } if info.AppId > 0 || info.AppGuid != "" { return info, nil } } return AppData{}, nil } // 获取文件列表 func getFileList(dir string) ([]string, error) { list := make([]string, 0) //fmt.Println(dir) f, err := os.Open(dir) if err != nil { fmt.Println(err) } files, err := f.Readdir(-1) f.Close() if err != nil { fmt.Println(err) return nil, err } for _, file := range files { if !file.IsDir() && file.Name() != "$RECYCLE.BIN" && file.Name() != "System Volume Information" { list = append(list, filepath.Join(dir, file.Name())) } } return list, nil } // IsAllNumberHandle /** 是否全部是数字 */ func IsAllNumberHandle(str string) bool { // 空串不认为是数字 if str == "" { return false } anyReplaceList := []int32{'.', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0'} for strIndex, strInt := range str { if strIndex == 0 { if strInt == '-' { continue } } if IsHaveReplaceList(strInt, anyReplaceList) { continue } return false } return true } // IsHaveReplaceList 是否在过滤列表中 func IsHaveReplaceList(strInt int32, replaceList []int32) bool { for _, chStrInt := range replaceList { if chStrInt == strInt { return true } } return false } // pathExists 判断一个文件或文件夹是否存在 // 输入文件路径,根据返回的bool值来判断文件或文件夹是否存在 func pathExists(path string) (bool, error) { _, err := os.Stat(path) if err == nil { return true, nil } if os.IsNotExist(err) { return false, nil } return false, err }