package internal import ( "bufio" "encoding/json" "firefly-launcher/pkg/constant" "firefly-launcher/pkg/models" "firefly-launcher/pkg/sevenzip" "firefly-launcher/pkg/verifier" "firefly-launcher/pkg/hpatchz" "fmt" "io" "os" "path/filepath" "strings" "github.com/wailsapp/wails/v3/pkg/application" ) type HdiffzService struct{} func (h *HdiffzService) CheckTypeHDiff(patchPath string) (bool, string, string) { isFileInTxt, _ := sevenzip.IsFileIn7z(patchPath, "hdifffiles.txt") if isFileInTxt { return true, "hdifffiles.txt", "" } isFileInJson, _ := sevenzip.IsFileIn7z(patchPath, "hdiffmap.json") if isFileInJson { return true, "hdiffmap.json", "" } return false, "", "not found hdifffiles.txt or hdiffmap.json" } func (h *HdiffzService) VersionValidate(gamePath, patchPath string) (bool, string) { oldVersionData, err := models.ParseBinaryVersion(filepath.Join(gamePath, "StarRail_Data\\StreamingAssets\\BinaryVersion.bytes")) if err != nil { return false, err.Error() } if _, err := os.Stat(patchPath); err != nil { return false, err.Error() } if _, err := os.Stat(constant.TempUrl); os.IsNotExist(err) { if err := os.MkdirAll(constant.TempUrl, os.ModePerm); err != nil { return false, err.Error() } } if err := sevenzip.ExtractAFileFromZip(patchPath, "StarRail_Data\\StreamingAssets\\BinaryVersion.bytes", constant.TempUrl); err != nil { return false, err.Error() } binPath := filepath.Join(constant.TempUrl, "BinaryVersion.bytes") newVersionData, err := models.ParseBinaryVersion(binPath) if err != nil { return false, err.Error() } defer os.Remove(binPath) v := newVersionData.Subtract(*oldVersionData) if v != 0 && v != 1 { return false, fmt.Sprintf("the diff version %s not valid with game version %s", newVersionData, oldVersionData) } return true, "validated" } func (h *HdiffzService) DataExtract(gamePath, patchPath string, isSkipVerify bool) (bool, string) { if _, err := os.Stat(gamePath); err != nil { return false, err.Error() } if _, err := os.Stat(patchPath); err != nil { return false, err.Error() } if _, err := os.Stat(constant.TempUrl); os.IsNotExist(err) { if err := os.MkdirAll(constant.TempUrl, os.ModePerm); err != nil { return false, err.Error() } } if err := sevenzip.ExtractAllFilesFromZip(patchPath, constant.TempUrl); err != nil { os.RemoveAll(constant.TempUrl) return false, err.Error() } if !isSkipVerify { validator, err := verifier.NewVerifier(gamePath, constant.TempUrl) if err != nil { os.RemoveAll(constant.TempUrl) return false, err.Error() } if err := validator.VerifyAll(); err != nil { os.RemoveAll(constant.TempUrl) return false, err.Error() } } return true, "validated" } func (h *HdiffzService) CutData(gamePath string) (bool, string) { if _, err := os.Stat(constant.TempUrl); os.IsNotExist(err) { return false, err.Error() } err := filepath.Walk(constant.TempUrl, func(path string, info os.FileInfo, err error) error { if err != nil { return err } relPath, err := filepath.Rel(constant.TempUrl, path) if err != nil { return err } destPath := filepath.Join(gamePath, relPath) application.Get().EmitEvent("hdiffz:message", map[string]string{"message": destPath}) if info.IsDir() { return os.MkdirAll(destPath, os.ModePerm) } if err := os.MkdirAll(filepath.Dir(destPath), os.ModePerm); err != nil { return err } if err := os.Rename(path, destPath); err != nil { srcFile, err := os.Open(path) if err != nil { return err } defer srcFile.Close() dstFile, err := os.Create(destPath) if err != nil { return err } defer dstFile.Close() if _, err := io.Copy(dstFile, srcFile); err != nil { return err } _ = os.Remove(path) } return nil }) if err != nil { return false, err.Error() } return true, "cut completed" } func (h *HdiffzService) PatchData(gamePath string) (bool, string) { hdiffMapPath := filepath.Join(gamePath, "hdiffmap.json") hdiffFilesPath := filepath.Join(gamePath, "hdifffiles.txt") var jsonData struct { DiffMap []*models.HDiffData `json:"diff_map"` } if _, err := os.Stat(hdiffMapPath); err == nil { data, err := os.ReadFile(hdiffMapPath) if err != nil { return false, err.Error() } var jsonDataDiffMap struct { DiffMap []*models.DiffMapType `json:"diff_map"` } if err := json.Unmarshal(data, &jsonDataDiffMap); err != nil { return false, err.Error() } for _, entry := range jsonDataDiffMap.DiffMap { jsonData.DiffMap = append(jsonData.DiffMap, entry.ToHDiffData()) } } else if _, err := os.Stat(hdiffFilesPath); err == nil { files, err := models.LoadHDiffFiles(hdiffFilesPath) if err != nil { return false, err.Error() } for _, entry := range files { jsonData.DiffMap = append(jsonData.DiffMap, entry.ToHDiffData()) } } else { return false, "no hdiff entries map exist" } for i, entry := range jsonData.DiffMap { application.Get().EmitEvent( "hdiffz:progress", map[string]int{ "progress": i, "maxProgress": len(jsonData.DiffMap), }) sourceFile := filepath.Join(gamePath, entry.SourceFileName) patchFile := filepath.Join(gamePath, entry.PatchFileName) targetFile := filepath.Join(gamePath, entry.TargetFileName) // Check patch file tồn tại chưa if _, err := os.Stat(patchFile); os.IsNotExist(err) { continue } // Nếu không có source file hoặc SourceFileName rỗng → apply_patch_empty if entry.SourceFileName == "" { err := hpatchz.ApplyPatchEmpty(patchFile, targetFile) if err != nil { fmt.Printf("%s failed to patch! %v\n", entry.TargetFileName, err) _ = os.Remove(patchFile) return false, err.Error() } _ = os.Remove(patchFile) continue } if _, err := os.Stat(sourceFile); os.IsNotExist(err) { continue } // Có source file → apply_patch err := hpatchz.ApplyPatch(sourceFile, patchFile, targetFile) if err != nil { fmt.Printf("%s failed to patch! %v\n", entry.TargetFileName, err) _ = os.Remove(patchFile) return false, err.Error() } if entry.SourceFileName != entry.TargetFileName { _ = os.Remove(sourceFile) } _ = os.Remove(patchFile) } os.Remove(filepath.Join(gamePath, "hdiffmap.json")) os.Remove(filepath.Join(gamePath, "hdifffiles.txt")) return true, "patching completed" } func (h *HdiffzService) DeleteFiles(gamePath string) (bool, string) { var deleteFiles []string file, err := os.Open(filepath.Join(gamePath, "deletefiles.txt")) if err != nil { return false, "" } defer file.Close() scanner := bufio.NewScanner(file) for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) if line != "" { deleteFiles = append(deleteFiles, line) } } if err := scanner.Err(); err != nil { return false, "no delete files exist" } for i, file := range deleteFiles { os.Remove(filepath.Join(gamePath, file)) application.Get().EmitEvent("hdiffz:progress", map[string]int{"progress": i, "maxProgress": len(deleteFiles)}) } _ = os.Remove(filepath.Join(gamePath, "deletefiles.txt")) return true, "" }