This commit is contained in:
2025-07-08 14:54:41 +07:00
commit 644a6f9803
86 changed files with 12422 additions and 0 deletions

35
pkg/constant/constant.go Normal file
View File

@@ -0,0 +1,35 @@
package constant
const ProxyGitUrl = "https://git.kain.io.vn/api/v1/repos/Firefly-Shelter/Firefly_Proxy/releases"
const ServerGitUrl = "https://git.kain.io.vn/api/v1/repos/Firefly-Shelter/FireflyGo_Local_Archive/releases"
const ServerStorageUrl = "./server"
const ProxyStorageUrl = "./proxy"
const ServerZipFile = "prebuild_win_x86.zip"
const ProxyZipFile = "64bit.zip"
const TempUrl = "./temp"
type ToolFile string
const (
Tool7zaExe ToolFile = "bin/7za.exe"
Tool7zaDLL ToolFile = "bin/7za.dll"
Tool7zxaDLL ToolFile = "bin/7zxa.dll"
ToolHPatchzExe ToolFile = "bin/hpatchz.exe"
ToolHDiffzExe ToolFile = "bin/hdiffz.exe"
)
var RequiredFiles = map[ToolFile]string{
Tool7zaExe: "assets/7zip/7za.exe",
Tool7zaDLL: "assets/7zip/7za.dll",
Tool7zxaDLL: "assets/7zip/7zxa.dll",
ToolHPatchzExe: "assets/HDiffPatch/hpatchz.exe",
ToolHDiffzExe: "assets/HDiffPatch/hdiffz.exe",
}
func (t ToolFile) GetEmbedPath() string {
return RequiredFiles[t]
}
func (t ToolFile) String() string {
return string(t)
}

View File

@@ -0,0 +1,73 @@
package models
import (
"errors"
"fmt"
"os"
"strconv"
"strings"
)
type BinaryVersion struct {
Major int
Minor int
Patch int
}
func ParseBinaryVersion(path string) (*BinaryVersion, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, err
}
content := string(data)
dashPos := strings.LastIndex(content, "-")
if dashPos == -1 {
return nil, errors.New("no dash found in version string")
}
start := dashPos - 6
if start < 0 {
start = 0
}
versionSlice := content[start:]
end := strings.Index(versionSlice, "-")
if end == -1 {
end = len(versionSlice)
}
versionStr := versionSlice[:end]
parts := strings.SplitN(versionStr, ".", 3)
if len(parts) != 3 {
return nil, errors.New("invalid version format")
}
major, err := strconv.Atoi(parts[0])
if err != nil {
return nil, err
}
minor, err := strconv.Atoi(parts[1])
if err != nil {
return nil, err
}
patch, err := strconv.Atoi(parts[2])
if err != nil {
return nil, err
}
return &BinaryVersion{major, minor, patch}, nil
}
func (v *BinaryVersion) String() string {
return fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch)
}
func (v BinaryVersion) ToInt() int {
return v.Major*100 + v.Minor*10 + v.Patch
}
func (v BinaryVersion) Subtract(other BinaryVersion) int {
return v.ToInt() - other.ToInt()
}

15
pkg/models/hdiffmap.go Normal file
View File

@@ -0,0 +1,15 @@
package models
type DiffMapType struct {
SourceFileName string `json:"source_file_name"`
SourceFileMD5 string `json:"source_file_md5"`
SourceFileSize int64 `json:"source_file_size"`
TargetFileName string `json:"target_file_name"`
TargetFileMD5 string `json:"target_file_md5"`
TargetFileSize int64 `json:"target_file_size"`
PatchFileName string `json:"patch_file_name"`
PatchFileMD5 string `json:"patch_file_md5"`
PatchFileSize int64 `json:"patch_file_size"`
}

56
pkg/models/release.go Normal file
View File

@@ -0,0 +1,56 @@
package models
type ReleaseType struct {
ID int `json:"id"`
TagName string `json:"tag_name"`
TargetCommitish string `json:"target_commitish"`
Name string `json:"name"`
Body string `json:"body"`
URL string `json:"url"`
HTMLURL string `json:"html_url"`
TarballURL string `json:"tarball_url"`
ZipballURL string `json:"zipball_url"`
UploadURL string `json:"upload_url"`
Draft bool `json:"draft"`
Prerelease bool `json:"prerelease"`
CreatedAt string `json:"created_at"`
PublishedAt string `json:"published_at"`
Author AuthorType `json:"author"`
Assets []AssetType `json:"assets"`
}
type AuthorType struct {
ID int `json:"id"`
Login string `json:"login"`
LoginName string `json:"login_name"`
SourceID int `json:"source_id"`
FullName string `json:"full_name"`
Email string `json:"email"`
AvatarURL string `json:"avatar_url"`
HTMLURL string `json:"html_url"`
Language string `json:"language"`
IsAdmin bool `json:"is_admin"`
LastLogin string `json:"last_login"`
Created string `json:"created"`
Restricted bool `json:"restricted"`
Active bool `json:"active"`
ProhibitLogin bool `json:"prohibit_login"`
Location string `json:"location"`
Website string `json:"website"`
Description string `json:"description"`
Visibility string `json:"visibility"`
FollowersCount int `json:"followers_count"`
FollowingCount int `json:"following_count"`
StarredReposCount int `json:"starred_repos_count"`
Username string `json:"username"`
}
type AssetType struct {
ID int `json:"id"`
Name string `json:"name"`
Size int `json:"size"`
DownloadCount int `json:"download_count"`
CreatedAt string `json:"created_at"`
UUID string `json:"uuid"`
BrowserDownloadURL string `json:"browser_download_url"`
}

44
pkg/sevenzip/sevenzip.go Normal file
View File

@@ -0,0 +1,44 @@
package sevenzip
import (
"bytes"
"firefly-launcher/pkg/constant"
"fmt"
"os"
"os/exec"
"strings"
)
func IsFileIn7z(archivePath, fileInside string) (bool, error) {
cmd := exec.Command(constant.Tool7zaExe.String(), "l", archivePath)
var out bytes.Buffer
cmd.Stdout = &out
cmd.Stderr = &out
if err := cmd.Run(); err != nil {
return false, fmt.Errorf("7za list failed: %v\nOutput: %s", err, out.String())
}
lines := strings.Split(out.String(), "\n")
for _, line := range lines {
if strings.Contains(line, fileInside) {
return true, nil
}
}
return false, fmt.Errorf("%s not found in %s", fileInside, archivePath)
}
func ExtractAFileFromZip(archivePath, fileInside, outDir string) error {
cmd := exec.Command(constant.Tool7zaExe.String(), "e", archivePath, fileInside, "-o"+outDir, "-y")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}
func ExtractAllFilesFromZip(archivePath, outDir string) error {
cmd := exec.Command(constant.Tool7zaExe.String(), "x", archivePath, "-o"+outDir, "-y")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}

98
pkg/verifier/verifier.go Normal file
View File

@@ -0,0 +1,98 @@
package verifier
import (
"crypto/md5"
"encoding/hex"
"encoding/json"
"firefly-launcher/pkg/models"
"fmt"
"io"
"os"
"path/filepath"
"github.com/wailsapp/wails/v3/pkg/application"
)
type Verifier struct {
GamePath string
HdiffPath string
DiffMapEntries []*models.DiffMapType
}
func NewVerifier(gamePath, hdiffPath string) (*Verifier, error) {
data, err := os.ReadFile(hdiffPath + "/hdiffmap.json")
if err != nil {
return nil, err
}
var jsonData struct {
DiffMap []*models.DiffMapType `json:"diff_map"`
}
if err := json.Unmarshal(data, &jsonData); err != nil {
return nil, err
}
return &Verifier{
GamePath: gamePath,
HdiffPath: hdiffPath,
DiffMapEntries: jsonData.DiffMap,
}, nil
}
func (v *Verifier) VerifyAll() error {
for i, entry := range v.DiffMapEntries {
application.Get().EmitEvent(
"hdiffz:progress", map[string]int{
"progress": i,
"maxProgress": len(v.DiffMapEntries),
})
if err := check(entry.SourceFileName, entry.SourceFileSize, entry.SourceFileMD5, v.GamePath); err != nil {
return fmt.Errorf("source_file failed: %w", err)
}
// if err := check(entry.PatchFileName, entry.PatchFileSize, entry.PatchFileMD5, v.HdiffPath); err != nil {
// return fmt.Errorf("patch_file failed: %w", err)
// }
}
return nil
}
func check(relPath string, expectedSize int64, expectedMD5, base string) error {
fullPath := filepath.Join(base, relPath)
info, err := os.Stat(fullPath)
if err != nil {
return fmt.Errorf("file not found: %s", fullPath)
}
if info.Size() != expectedSize {
return fmt.Errorf("file size mismatch for %s: expected %d, got %d",
fullPath, expectedSize, info.Size())
}
md5Hash, err := fileMD5(fullPath)
if err != nil {
return fmt.Errorf("error reading %s: %w", fullPath, err)
}
if md5Hash != expectedMD5 {
return fmt.Errorf("md5 mismatch for %s: expected %s, got %s",
fullPath, expectedMD5, md5Hash)
}
return nil
}
func fileMD5(path string) (string, error) {
f, err := os.Open(path)
if err != nil {
return "", err
}
defer f.Close()
h := md5.New()
if _, err := io.Copy(h, f); err != nil {
return "", err
}
return hex.EncodeToString(h.Sum(nil)), nil
}