1 Commits
1.4.1 ... 1.5.0

Author SHA1 Message Date
6b222bfa70 UPDATE: Add ldiff 2025-08-25 18:12:13 +07:00
45 changed files with 1700 additions and 535 deletions

1
.gitignore vendored
View File

@@ -3,6 +3,7 @@ bin
tests/ tests/
server/ server/
proxy/ proxy/
proto/
temp/ temp/
frontend/dist frontend/dist
frontend/node_modules frontend/node_modules

7
Makefile Normal file
View File

@@ -0,0 +1,7 @@
PROTO_SRC=./proto
GEN_DEST=./pkg
build_proto:
@echo Compiling Protobuf files...
protoc --go_out=${GEN_DEST} --go-grpc_out=${GEN_DEST} ${PROTO_SRC}/*.proto
@echo Done!

View File

@@ -11,7 +11,7 @@ import {Call as $Call, Create as $Create} from "@wailsio/runtime";
* @returns {Promise<[boolean, string]> & { cancel(): void }} * @returns {Promise<[boolean, string]> & { cancel(): void }}
*/ */
export function CloseAppAfterTimeout(timeout) { export function CloseAppAfterTimeout(timeout) {
let $resultPromise = /** @type {any} */($Call.ByID(982751559, timeout)); let $resultPromise = /** @type {any} */($Call.ByID(1705931481, timeout));
return $resultPromise; return $resultPromise;
} }
@@ -19,6 +19,6 @@ export function CloseAppAfterTimeout(timeout) {
* @returns {Promise<[boolean, string]> & { cancel(): void }} * @returns {Promise<[boolean, string]> & { cancel(): void }}
*/ */
export function GetCurrentLauncherVersion() { export function GetCurrentLauncherVersion() {
let $resultPromise = /** @type {any} */($Call.ByID(2542090372)); let $resultPromise = /** @type {any} */($Call.ByID(3575133982));
return $resultPromise; return $resultPromise;
} }

View File

@@ -0,0 +1,8 @@
// @ts-check
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
import * as AppService from "./appservice.js";
export {
AppService
};

View File

@@ -11,7 +11,7 @@ import {Call as $Call, Create as $Create} from "@wailsio/runtime";
* @returns {Promise<[boolean, string, string]> & { cancel(): void }} * @returns {Promise<[boolean, string, string]> & { cancel(): void }}
*/ */
export function CheckTypeHDiff(patchPath) { export function CheckTypeHDiff(patchPath) {
let $resultPromise = /** @type {any} */($Call.ByID(1068035136, patchPath)); let $resultPromise = /** @type {any} */($Call.ByID(3717449114, patchPath));
return $resultPromise; return $resultPromise;
} }
@@ -20,7 +20,7 @@ export function CheckTypeHDiff(patchPath) {
* @returns {Promise<[boolean, string]> & { cancel(): void }} * @returns {Promise<[boolean, string]> & { cancel(): void }}
*/ */
export function CutData(gamePath) { export function CutData(gamePath) {
let $resultPromise = /** @type {any} */($Call.ByID(3671642725, gamePath)); let $resultPromise = /** @type {any} */($Call.ByID(2019290107, gamePath));
return $resultPromise; return $resultPromise;
} }
@@ -31,7 +31,7 @@ export function CutData(gamePath) {
* @returns {Promise<[boolean, string]> & { cancel(): void }} * @returns {Promise<[boolean, string]> & { cancel(): void }}
*/ */
export function DataExtract(gamePath, patchPath, isSkipVerify) { export function DataExtract(gamePath, patchPath, isSkipVerify) {
let $resultPromise = /** @type {any} */($Call.ByID(1843136452, gamePath, patchPath, isSkipVerify)); let $resultPromise = /** @type {any} */($Call.ByID(2161622254, gamePath, patchPath, isSkipVerify));
return $resultPromise; return $resultPromise;
} }
@@ -40,7 +40,7 @@ export function DataExtract(gamePath, patchPath, isSkipVerify) {
* @returns {Promise<[boolean, string]> & { cancel(): void }} * @returns {Promise<[boolean, string]> & { cancel(): void }}
*/ */
export function DeleteFiles(gamePath) { export function DeleteFiles(gamePath) {
let $resultPromise = /** @type {any} */($Call.ByID(989019003, gamePath)); let $resultPromise = /** @type {any} */($Call.ByID(1103091613, gamePath));
return $resultPromise; return $resultPromise;
} }
@@ -48,8 +48,17 @@ export function DeleteFiles(gamePath) {
* @param {string} gamePath * @param {string} gamePath
* @returns {Promise<[boolean, string]> & { cancel(): void }} * @returns {Promise<[boolean, string]> & { cancel(): void }}
*/ */
export function PatchData(gamePath) { export function HDiffPatchData(gamePath) {
let $resultPromise = /** @type {any} */($Call.ByID(3608591627, gamePath)); let $resultPromise = /** @type {any} */($Call.ByID(3944051994, gamePath));
return $resultPromise;
}
/**
* @param {string} gamePath
* @returns {Promise<[boolean, string]> & { cancel(): void }}
*/
export function LDiffPatchData(gamePath) {
let $resultPromise = /** @type {any} */($Call.ByID(360123238, gamePath));
return $resultPromise; return $resultPromise;
} }
@@ -59,6 +68,6 @@ export function PatchData(gamePath) {
* @returns {Promise<[boolean, string]> & { cancel(): void }} * @returns {Promise<[boolean, string]> & { cancel(): void }}
*/ */
export function VersionValidate(gamePath, patchPath) { export function VersionValidate(gamePath, patchPath) {
let $resultPromise = /** @type {any} */($Call.ByID(3916254383, gamePath, patchPath)); let $resultPromise = /** @type {any} */($Call.ByID(2105077257, gamePath, patchPath));
return $resultPromise; return $resultPromise;
} }

View File

@@ -0,0 +1,8 @@
// @ts-check
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
import * as DiffService from "./diffservice.js";
export {
DiffService
};

View File

@@ -11,7 +11,7 @@ import {Call as $Call, Create as $Create} from "@wailsio/runtime";
* @returns {Promise<boolean> & { cancel(): void }} * @returns {Promise<boolean> & { cancel(): void }}
*/ */
export function DirExists(path) { export function DirExists(path) {
let $resultPromise = /** @type {any} */($Call.ByID(1291974398, path)); let $resultPromise = /** @type {any} */($Call.ByID(1772289644, path));
return $resultPromise; return $resultPromise;
} }
@@ -20,7 +20,7 @@ export function DirExists(path) {
* @returns {Promise<boolean> & { cancel(): void }} * @returns {Promise<boolean> & { cancel(): void }}
*/ */
export function FileExists(path) { export function FileExists(path) {
let $resultPromise = /** @type {any} */($Call.ByID(3373618865, path)); let $resultPromise = /** @type {any} */($Call.ByID(1782610747, path));
return $resultPromise; return $resultPromise;
} }
@@ -30,7 +30,7 @@ export function FileExists(path) {
* @returns {Promise<[boolean, string]> & { cancel(): void }} * @returns {Promise<[boolean, string]> & { cancel(): void }}
*/ */
export function FileExistsInZip(archivePath, fileInside) { export function FileExistsInZip(archivePath, fileInside) {
let $resultPromise = /** @type {any} */($Call.ByID(1737012553, archivePath, fileInside)); let $resultPromise = /** @type {any} */($Call.ByID(2509699047, archivePath, fileInside));
return $resultPromise; return $resultPromise;
} }
@@ -39,15 +39,16 @@ export function FileExistsInZip(archivePath, fileInside) {
* @returns {Promise<[boolean, string]> & { cancel(): void }} * @returns {Promise<[boolean, string]> & { cancel(): void }}
*/ */
export function OpenFolder(path) { export function OpenFolder(path) {
let $resultPromise = /** @type {any} */($Call.ByID(2733319263, path)); let $resultPromise = /** @type {any} */($Call.ByID(1635714001, path));
return $resultPromise; return $resultPromise;
} }
/** /**
* @param {string} filter
* @returns {Promise<string> & { cancel(): void }} * @returns {Promise<string> & { cancel(): void }}
*/ */
export function PickFile() { export function PickFile(filter) {
let $resultPromise = /** @type {any} */($Call.ByID(2181012076)); let $resultPromise = /** @type {any} */($Call.ByID(3756474934, filter));
return $resultPromise; return $resultPromise;
} }
@@ -55,7 +56,7 @@ export function PickFile() {
* @returns {Promise<string> & { cancel(): void }} * @returns {Promise<string> & { cancel(): void }}
*/ */
export function PickFolder() { export function PickFolder() {
let $resultPromise = /** @type {any} */($Call.ByID(3906046322)); let $resultPromise = /** @type {any} */($Call.ByID(3654471460));
return $resultPromise; return $resultPromise;
} }
@@ -64,7 +65,7 @@ export function PickFolder() {
* @returns {Promise<boolean> & { cancel(): void }} * @returns {Promise<boolean> & { cancel(): void }}
*/ */
export function StartApp(path) { export function StartApp(path) {
let $resultPromise = /** @type {any} */($Call.ByID(3825990132, path)); let $resultPromise = /** @type {any} */($Call.ByID(1267568402, path));
return $resultPromise; return $resultPromise;
} }
@@ -73,6 +74,6 @@ export function StartApp(path) {
* @returns {Promise<boolean> & { cancel(): void }} * @returns {Promise<boolean> & { cancel(): void }}
*/ */
export function StartWithConsole(path) { export function StartWithConsole(path) {
let $resultPromise = /** @type {any} */($Call.ByID(2364569062, path)); let $resultPromise = /** @type {any} */($Call.ByID(3249271428, path));
return $resultPromise; return $resultPromise;
} }

View File

@@ -0,0 +1,8 @@
// @ts-check
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
import * as FSService from "./fsservice.js";
export {
FSService
};

View File

@@ -11,7 +11,7 @@ import {Call as $Call, Create as $Create} from "@wailsio/runtime";
* @returns {Promise<[boolean, string]> & { cancel(): void }} * @returns {Promise<[boolean, string]> & { cancel(): void }}
*/ */
export function DownloadProxyProgress(version) { export function DownloadProxyProgress(version) {
let $resultPromise = /** @type {any} */($Call.ByID(1951249093, version)); let $resultPromise = /** @type {any} */($Call.ByID(3559275334, version));
return $resultPromise; return $resultPromise;
} }
@@ -20,7 +20,7 @@ export function DownloadProxyProgress(version) {
* @returns {Promise<[boolean, string]> & { cancel(): void }} * @returns {Promise<[boolean, string]> & { cancel(): void }}
*/ */
export function DownloadServerProgress(version) { export function DownloadServerProgress(version) {
let $resultPromise = /** @type {any} */($Call.ByID(314135954, version)); let $resultPromise = /** @type {any} */($Call.ByID(1954767259, version));
return $resultPromise; return $resultPromise;
} }
@@ -28,7 +28,7 @@ export function DownloadServerProgress(version) {
* @returns {Promise<[boolean, string, string]> & { cancel(): void }} * @returns {Promise<[boolean, string, string]> & { cancel(): void }}
*/ */
export function GetLatestLauncherVersion() { export function GetLatestLauncherVersion() {
let $resultPromise = /** @type {any} */($Call.ByID(3191972855)); let $resultPromise = /** @type {any} */($Call.ByID(262637602));
return $resultPromise; return $resultPromise;
} }
@@ -36,7 +36,7 @@ export function GetLatestLauncherVersion() {
* @returns {Promise<[boolean, string, string]> & { cancel(): void }} * @returns {Promise<[boolean, string, string]> & { cancel(): void }}
*/ */
export function GetLatestProxyVersion() { export function GetLatestProxyVersion() {
let $resultPromise = /** @type {any} */($Call.ByID(1462362449)); let $resultPromise = /** @type {any} */($Call.ByID(289488362));
return $resultPromise; return $resultPromise;
} }
@@ -44,7 +44,7 @@ export function GetLatestProxyVersion() {
* @returns {Promise<[boolean, string, string]> & { cancel(): void }} * @returns {Promise<[boolean, string, string]> & { cancel(): void }}
*/ */
export function GetLatestServerVersion() { export function GetLatestServerVersion() {
let $resultPromise = /** @type {any} */($Call.ByID(1447982978)); let $resultPromise = /** @type {any} */($Call.ByID(2918980975));
return $resultPromise; return $resultPromise;
} }
@@ -52,7 +52,7 @@ export function GetLatestServerVersion() {
* @returns {Promise<void> & { cancel(): void }} * @returns {Promise<void> & { cancel(): void }}
*/ */
export function UnzipProxy() { export function UnzipProxy() {
let $resultPromise = /** @type {any} */($Call.ByID(4071181044)); let $resultPromise = /** @type {any} */($Call.ByID(2563246729));
return $resultPromise; return $resultPromise;
} }
@@ -60,7 +60,7 @@ export function UnzipProxy() {
* @returns {Promise<void> & { cancel(): void }} * @returns {Promise<void> & { cancel(): void }}
*/ */
export function UnzipServer() { export function UnzipServer() {
let $resultPromise = /** @type {any} */($Call.ByID(4110296071)); let $resultPromise = /** @type {any} */($Call.ByID(1126363284));
return $resultPromise; return $resultPromise;
} }
@@ -69,6 +69,6 @@ export function UnzipServer() {
* @returns {Promise<[boolean, string]> & { cancel(): void }} * @returns {Promise<[boolean, string]> & { cancel(): void }}
*/ */
export function UpdateLauncherProgress(version) { export function UpdateLauncherProgress(version) {
let $resultPromise = /** @type {any} */($Call.ByID(2648938152, version)); let $resultPromise = /** @type {any} */($Call.ByID(97272861, version));
return $resultPromise; return $resultPromise;
} }

View File

@@ -0,0 +1,8 @@
// @ts-check
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
import * as GitService from "./gitservice.js";
export {
GitService
};

View File

@@ -1,16 +0,0 @@
// @ts-check
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
import * as AppService from "./appservice.js";
import * as FSService from "./fsservice.js";
import * as GitService from "./gitservice.js";
import * as HdiffzService from "./hdiffzservice.js";
import * as LanguageService from "./languageservice.js";
export {
AppService,
FSService,
GitService,
HdiffzService,
LanguageService
};

View File

@@ -0,0 +1,8 @@
// @ts-check
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
import * as LanguageService from "./languageservice.js";
export {
LanguageService
};

View File

@@ -11,7 +11,7 @@ import {Call as $Call, Create as $Create} from "@wailsio/runtime";
* @returns {Promise<[boolean, string, string, string]> & { cancel(): void }} * @returns {Promise<[boolean, string, string, string]> & { cancel(): void }}
*/ */
export function GetLanguage(path) { export function GetLanguage(path) {
let $resultPromise = /** @type {any} */($Call.ByID(3450750492, path)); let $resultPromise = /** @type {any} */($Call.ByID(3574191687, path));
return $resultPromise; return $resultPromise;
} }
@@ -22,6 +22,6 @@ export function GetLanguage(path) {
* @returns {Promise<[boolean, string]> & { cancel(): void }} * @returns {Promise<[boolean, string]> & { cancel(): void }}
*/ */
export function SetLanguage(path, text, voice) { export function SetLanguage(path, text, voice) {
let $resultPromise = /** @type {any} */($Call.ByID(2793672496, path, text, voice)); let $resultPromise = /** @type {any} */($Call.ByID(2768939795, path, text, voice));
return $resultPromise; return $resultPromise;
} }

View File

@@ -1,7 +1,8 @@
import useLauncherStore from "@/stores/launcherStore"; import useLauncherStore from "@/stores/launcherStore";
import { AppService, GitService } from "@bindings/firefly-launcher/internal"; import { AppService, } from "@bindings/firefly-launcher/internal/app-service";
import { toast } from "react-toastify"; import { toast } from "react-toastify";
import { sleep } from "./sleep"; import { sleep } from "./sleep";
import { GitService } from "@bindings/firefly-launcher/internal/git-service";
export async function CheckUpdateLauncher(): Promise<{ isUpdate: boolean; isExists: boolean; version: string }> { export async function CheckUpdateLauncher(): Promise<{ isUpdate: boolean; isExists: boolean; version: string }> {
const [currentOk, currentVersion] = await AppService.GetCurrentLauncherVersion() const [currentOk, currentVersion] = await AppService.GetCurrentLauncherVersion()

View File

@@ -1,6 +1,7 @@
import useLauncherStore from "@/stores/launcherStore"; import useLauncherStore from "@/stores/launcherStore";
import useSettingStore from "@/stores/settingStore"; import useSettingStore from "@/stores/settingStore";
import { FSService, GitService } from "@bindings/firefly-launcher/internal"; import { FSService } from "@bindings/firefly-launcher/internal/fs-service";
import { GitService } from "@bindings/firefly-launcher/internal/git-service";
import { toast } from "react-toastify"; import { toast } from "react-toastify";
export async function CheckUpdateProxy(proxyPath: string, proxyVersion: string) : Promise<{isUpdate: boolean, isExists: boolean, version: string}> { export async function CheckUpdateProxy(proxyPath: string, proxyVersion: string) : Promise<{isUpdate: boolean, isExists: boolean, version: string}> {

View File

@@ -1,6 +1,7 @@
import useLauncherStore from '@/stores/launcherStore'; import useLauncherStore from '@/stores/launcherStore';
import useSettingStore from '@/stores/settingStore'; import useSettingStore from '@/stores/settingStore';
import { FSService, GitService } from '@bindings/firefly-launcher/internal'; import { FSService } from '@bindings/firefly-launcher/internal/fs-service';
import { GitService } from '@bindings/firefly-launcher/internal/git-service';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
export async function CheckUpdateServer( export async function CheckUpdateServer(

View File

@@ -12,6 +12,7 @@ export function useGlobalEvents({
setProgressDownload, setProgressDownload,
setDownloadSpeed, setDownloadSpeed,
setMessageUpdate, setMessageUpdate,
setStageType,
}: { }: {
setGameRunning: (v: boolean) => void; setGameRunning: (v: boolean) => void;
@@ -22,6 +23,7 @@ export function useGlobalEvents({
setProgressDownload: (v: number) => void; setProgressDownload: (v: number) => void;
setDownloadSpeed: (v: number) => void; setDownloadSpeed: (v: number) => void;
setMessageUpdate: (v: string) => void; setMessageUpdate: (v: string) => void;
setStageType: (v: string) => void,
}) { }) {
useEffect(() => { useEffect(() => {
const onGameExit = () => setGameRunning(false); const onGameExit = () => setGameRunning(false);
@@ -45,14 +47,20 @@ export function useGlobalEvents({
setMessageUpdate(message); setMessageUpdate(message);
}; };
const onStageUpdate = (event: any) => {
const { stage } = event.data[0];
setStageType(stage);
};
Events.On("download:server", onDownload); Events.On("download:server", onDownload);
Events.On("download:proxy", onDownload); Events.On("download:proxy", onDownload);
Events.On("game:exit", onGameExit); Events.On("game:exit", onGameExit);
Events.On("server:exit", onServerExit); Events.On("server:exit", onServerExit);
Events.On("proxy:exit", onProxyExit); Events.On("proxy:exit", onProxyExit);
Events.On("hdiffz:progress", onUpdateProgress); Events.On("diff:progress", onUpdateProgress);
Events.On("hdiffz:message", onMessageUpdate); Events.On("diff:message", onMessageUpdate);
Events.On("hdiffz:error", (event: any) => { Events.On("diff:stage", onStageUpdate);
Events.On("diff:error", (event: any) => {
const { message } = event.data[0]; const { message } = event.data[0];
toast.error(message); toast.error(message);
}); });
@@ -63,8 +71,9 @@ export function useGlobalEvents({
Events.Off("game:exit"); Events.Off("game:exit");
Events.Off("server:exit"); Events.Off("server:exit");
Events.Off("proxy:exit"); Events.Off("proxy:exit");
Events.Off("hdiffz:progress"); Events.Off("diff:progress")
Events.Off("hdiffz:message"); Events.Off("diff:message");
Events.Off("diff:stage");
Events.Off("version:check"); Events.Off("version:check");
}; };
}, []); }, []);

View File

@@ -2,11 +2,12 @@ import useSettingStore from "@/stores/settingStore"
import { Check, Folder, File, X, Settings } from "lucide-react" import { Check, Folder, File, X, Settings } from "lucide-react"
import { useEffect } from "react" import { useEffect } from "react"
import { toast } from "react-toastify" import { toast } from "react-toastify"
import { FSService, HdiffzService} from "@bindings/firefly-launcher/internal" import { DiffService} from "@bindings/firefly-launcher/internal/diff-service"
import { FSService } from "@bindings/firefly-launcher/internal/fs-service"
import { motion } from "motion/react" import { motion } from "motion/react"
import useHdiffzStore from "@/stores/hdiffzStore" import useDiffStore from "@/stores/diffStore"
export default function HdiffzPage() { export default function DiffPage() {
const { gameDir, setGameDir } = useSettingStore() const { gameDir, setGameDir } = useSettingStore()
const { const {
isLoading, isLoading,
@@ -27,7 +28,7 @@ export default function HdiffzPage() {
setStageType, setStageType,
messageUpdate, messageUpdate,
setMessageUpdate setMessageUpdate
} = useHdiffzStore() } = useDiffStore()
useEffect(() => { useEffect(() => {
const getLanguage = async () => { const getLanguage = async () => {
@@ -78,7 +79,7 @@ export default function HdiffzPage() {
const handlePickDiffFile = async () => { const handlePickDiffFile = async () => {
try { try {
setIsLoading({game: false, diff: true}) setIsLoading({game: false, diff: true})
const basePath = await FSService.PickFile() const basePath = await FSService.PickFile("")
if (basePath) { if (basePath) {
if (!basePath.endsWith(".7z") && !basePath.endsWith(".zip") && !basePath.endsWith(".rar")) { if (!basePath.endsWith(".7z") && !basePath.endsWith(".zip") && !basePath.endsWith(".rar")) {
toast.error('Not valid file type') toast.error('Not valid file type')
@@ -86,9 +87,15 @@ export default function HdiffzPage() {
setDiffDir('') setDiffDir('')
return return
} }
const [exists, error] = await FSService.FileExistsInZip(basePath, "StarRail_Data\\StreamingAssets\\BinaryVersion.bytes") const [isOk, validType, errorType] = await DiffService.CheckTypeHDiff(basePath)
if (!exists) { if (!isOk) {
toast.error(error) toast.error(errorType)
setDiffCheckResult('error')
setDiffDir('')
return
}
if (validType == "") {
toast.error('Not valid file type')
setDiffCheckResult('error') setDiffCheckResult('error')
setDiffDir('') setDiffDir('')
return return
@@ -119,7 +126,7 @@ export default function HdiffzPage() {
setStageType('Check Type HDiff') setStageType('Check Type HDiff')
setProgressUpdate(0) setProgressUpdate(0)
setMaxProgressUpdate(1) setMaxProgressUpdate(1)
const [isOk, validType, errorType] = await HdiffzService.CheckTypeHDiff(diffDir) const [isOk, validType, errorType] = await DiffService.CheckTypeHDiff(diffDir)
if (!isOk) { if (!isOk) {
toast.error(errorType) toast.error(errorType)
setIsDiffLoading(false) setIsDiffLoading(false)
@@ -127,11 +134,13 @@ export default function HdiffzPage() {
} }
setProgressUpdate(1) setProgressUpdate(1)
if (validType === 'hdiffmap.json') { if (validType === 'hdiffmap.json') {
setStageType('Version Validate') setStageType('Version Validate')
setProgressUpdate(0) setProgressUpdate(0)
setMaxProgressUpdate(1) setMaxProgressUpdate(1)
const [validVersion, errorVersion] = await HdiffzService.VersionValidate(gameDir, diffDir) const [validVersion, errorVersion] = await DiffService.VersionValidate(gameDir, diffDir)
if (!validVersion) { if (!validVersion) {
toast.error(errorVersion) toast.error(errorVersion)
setIsDiffLoading(false) setIsDiffLoading(false)
@@ -140,8 +149,9 @@ export default function HdiffzPage() {
setProgressUpdate(1) setProgressUpdate(1)
} }
const isSkipVerify = validType === 'manifest' || validType === 'hdifffiles.txt'
setStageType('Data Extract') setStageType('Data Extract')
const [validData, errorData] = await HdiffzService.DataExtract(gameDir, diffDir, validType === 'hdifffiles.txt') const [validData, errorData] = await DiffService.DataExtract(gameDir, diffDir, isSkipVerify)
if (!validData) { if (!validData) {
toast.error(errorData) toast.error(errorData)
setIsDiffLoading(false) setIsDiffLoading(false)
@@ -150,28 +160,38 @@ export default function HdiffzPage() {
setStageType('Cut Data') setStageType('Cut Data')
setMessageUpdate('') setMessageUpdate('')
const [validCut, errorCut] = await HdiffzService.CutData(gameDir) const [validCut, errorCut] = await DiffService.CutData(gameDir)
if (!validCut) { if (!validCut) {
toast.error(errorCut) toast.error(errorCut)
setIsDiffLoading(false) setIsDiffLoading(false)
return return
} }
setStageType('Patch Data') if ( validType === 'hdifffiles.txt' || validType === 'hdiffmap.json') {
const [validPatch, errorPatch] = await HdiffzService.PatchData(gameDir) setStageType('Patch Data')
if (!validPatch) { const [validPatch, errorPatch] = await DiffService.HDiffPatchData(gameDir)
toast.error(errorPatch) if (!validPatch) {
setIsDiffLoading(false) toast.error(errorPatch)
return setIsDiffLoading(false)
return
}
setStageType('Delete old files')
const [validDelete, errorDelete] = await DiffService.DeleteFiles(gameDir)
if (!validDelete) {
toast.error(errorDelete)
setIsDiffLoading(false)
return
}
} else if (validType === 'manifest') {
setStageType('Patch Data')
const [validPatch, errorPatch] = await DiffService.LDiffPatchData(gameDir)
if (!validPatch) {
toast.error(errorPatch)
setIsDiffLoading(false)
return
}
} }
setStageType('Delete old files')
const [validDelete, errorDelete] = await HdiffzService.DeleteFiles(gameDir)
if (!validDelete) {
toast.error(errorDelete)
setIsDiffLoading(false)
return
}
toast.success('Update game completed') toast.success('Update game completed')
} catch (err: any) { } catch (err: any) {
console.error(err) console.error(err)

View File

@@ -1,7 +1,7 @@
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { Folder, Settings, Check, X, Globe, Mic } from 'lucide-react' import { Folder, Settings, Check, X, Globe, Mic } from 'lucide-react'
import { FSService } from '@bindings/firefly-launcher/internal' import { FSService } from '@bindings/firefly-launcher/internal/fs-service'
import { LanguageService } from '@bindings/firefly-launcher/internal' import { LanguageService } from '@bindings/firefly-launcher/internal/language-service'
import { toast } from 'react-toastify' import { toast } from 'react-toastify'
import useSettingStore from '@/stores/settingStore' import useSettingStore from '@/stores/settingStore'

View File

@@ -1,6 +1,7 @@
import { useEffect } from 'react'; import { useEffect } from 'react';
import { Play, Menu, FolderOpen, MessageCircleQuestionMark } from 'lucide-react'; import { Play, Menu, FolderOpen, MessageCircleQuestionMark } from 'lucide-react';
import { FSService, AppService } from '@bindings/firefly-launcher/internal'; import { AppService } from '@bindings/firefly-launcher/internal/app-service';
import { FSService } from '@bindings/firefly-launcher/internal/fs-service';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import path from 'path-browserify' import path from 'path-browserify'
import useSettingStore from '@/stores/settingStore'; import useSettingStore from '@/stores/settingStore';
@@ -120,7 +121,7 @@ export default function LauncherPage() {
const handlePickFile = async () => { const handlePickFile = async () => {
try { try {
setIsLoading(true) setIsLoading(true)
const basePath = await FSService.PickFile() const basePath = await FSService.PickFile("exe")
if (basePath.endsWith("StarRail.exe") || basePath.endsWith("launcher.exe")) { if (basePath.endsWith("StarRail.exe") || basePath.endsWith("launcher.exe")) {
const normalized = basePath.replace(/\\/g, '/') const normalized = basePath.replace(/\\/g, '/')
const folderPath = path.dirname(normalized) const folderPath = path.dirname(normalized)

View File

@@ -12,7 +12,7 @@ import { Route as rootRouteImport } from './routes/__root'
import { Route as SrtoolsRouteImport } from './routes/srtools' import { Route as SrtoolsRouteImport } from './routes/srtools'
import { Route as LanguageRouteImport } from './routes/language' import { Route as LanguageRouteImport } from './routes/language'
import { Route as HowtoRouteImport } from './routes/howto' import { Route as HowtoRouteImport } from './routes/howto'
import { Route as HdiffzRouteImport } from './routes/hdiffz' import { Route as DiffRouteImport } from './routes/diff'
import { Route as AnalysisRouteImport } from './routes/analysis' import { Route as AnalysisRouteImport } from './routes/analysis'
import { Route as AboutRouteImport } from './routes/about' import { Route as AboutRouteImport } from './routes/about'
import { Route as IndexRouteImport } from './routes/index' import { Route as IndexRouteImport } from './routes/index'
@@ -32,9 +32,9 @@ const HowtoRoute = HowtoRouteImport.update({
path: '/howto', path: '/howto',
getParentRoute: () => rootRouteImport, getParentRoute: () => rootRouteImport,
} as any) } as any)
const HdiffzRoute = HdiffzRouteImport.update({ const DiffRoute = DiffRouteImport.update({
id: '/hdiffz', id: '/diff',
path: '/hdiffz', path: '/diff',
getParentRoute: () => rootRouteImport, getParentRoute: () => rootRouteImport,
} as any) } as any)
const AnalysisRoute = AnalysisRouteImport.update({ const AnalysisRoute = AnalysisRouteImport.update({
@@ -57,7 +57,7 @@ export interface FileRoutesByFullPath {
'/': typeof IndexRoute '/': typeof IndexRoute
'/about': typeof AboutRoute '/about': typeof AboutRoute
'/analysis': typeof AnalysisRoute '/analysis': typeof AnalysisRoute
'/hdiffz': typeof HdiffzRoute '/diff': typeof DiffRoute
'/howto': typeof HowtoRoute '/howto': typeof HowtoRoute
'/language': typeof LanguageRoute '/language': typeof LanguageRoute
'/srtools': typeof SrtoolsRoute '/srtools': typeof SrtoolsRoute
@@ -66,7 +66,7 @@ export interface FileRoutesByTo {
'/': typeof IndexRoute '/': typeof IndexRoute
'/about': typeof AboutRoute '/about': typeof AboutRoute
'/analysis': typeof AnalysisRoute '/analysis': typeof AnalysisRoute
'/hdiffz': typeof HdiffzRoute '/diff': typeof DiffRoute
'/howto': typeof HowtoRoute '/howto': typeof HowtoRoute
'/language': typeof LanguageRoute '/language': typeof LanguageRoute
'/srtools': typeof SrtoolsRoute '/srtools': typeof SrtoolsRoute
@@ -76,7 +76,7 @@ export interface FileRoutesById {
'/': typeof IndexRoute '/': typeof IndexRoute
'/about': typeof AboutRoute '/about': typeof AboutRoute
'/analysis': typeof AnalysisRoute '/analysis': typeof AnalysisRoute
'/hdiffz': typeof HdiffzRoute '/diff': typeof DiffRoute
'/howto': typeof HowtoRoute '/howto': typeof HowtoRoute
'/language': typeof LanguageRoute '/language': typeof LanguageRoute
'/srtools': typeof SrtoolsRoute '/srtools': typeof SrtoolsRoute
@@ -87,7 +87,7 @@ export interface FileRouteTypes {
| '/' | '/'
| '/about' | '/about'
| '/analysis' | '/analysis'
| '/hdiffz' | '/diff'
| '/howto' | '/howto'
| '/language' | '/language'
| '/srtools' | '/srtools'
@@ -96,7 +96,7 @@ export interface FileRouteTypes {
| '/' | '/'
| '/about' | '/about'
| '/analysis' | '/analysis'
| '/hdiffz' | '/diff'
| '/howto' | '/howto'
| '/language' | '/language'
| '/srtools' | '/srtools'
@@ -105,7 +105,7 @@ export interface FileRouteTypes {
| '/' | '/'
| '/about' | '/about'
| '/analysis' | '/analysis'
| '/hdiffz' | '/diff'
| '/howto' | '/howto'
| '/language' | '/language'
| '/srtools' | '/srtools'
@@ -115,7 +115,7 @@ export interface RootRouteChildren {
IndexRoute: typeof IndexRoute IndexRoute: typeof IndexRoute
AboutRoute: typeof AboutRoute AboutRoute: typeof AboutRoute
AnalysisRoute: typeof AnalysisRoute AnalysisRoute: typeof AnalysisRoute
HdiffzRoute: typeof HdiffzRoute DiffRoute: typeof DiffRoute
HowtoRoute: typeof HowtoRoute HowtoRoute: typeof HowtoRoute
LanguageRoute: typeof LanguageRoute LanguageRoute: typeof LanguageRoute
SrtoolsRoute: typeof SrtoolsRoute SrtoolsRoute: typeof SrtoolsRoute
@@ -144,11 +144,11 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof HowtoRouteImport preLoaderRoute: typeof HowtoRouteImport
parentRoute: typeof rootRouteImport parentRoute: typeof rootRouteImport
} }
'/hdiffz': { '/diff': {
id: '/hdiffz' id: '/diff'
path: '/hdiffz' path: '/diff'
fullPath: '/hdiffz' fullPath: '/diff'
preLoaderRoute: typeof HdiffzRouteImport preLoaderRoute: typeof DiffRouteImport
parentRoute: typeof rootRouteImport parentRoute: typeof rootRouteImport
} }
'/analysis': { '/analysis': {
@@ -179,7 +179,7 @@ const rootRouteChildren: RootRouteChildren = {
IndexRoute: IndexRoute, IndexRoute: IndexRoute,
AboutRoute: AboutRoute, AboutRoute: AboutRoute,
AnalysisRoute: AnalysisRoute, AnalysisRoute: AnalysisRoute,
HdiffzRoute: HdiffzRoute, DiffRoute: DiffRoute,
HowtoRoute: HowtoRoute, HowtoRoute: HowtoRoute,
LanguageRoute: LanguageRoute, LanguageRoute: LanguageRoute,
SrtoolsRoute: SrtoolsRoute, SrtoolsRoute: SrtoolsRoute,

View File

@@ -3,7 +3,7 @@ import ThemeController from '../components/themeController'
import { ToastContainer } from 'react-toastify' import { ToastContainer } from 'react-toastify'
import { useGlobalEvents } from '@/hooks'; import { useGlobalEvents } from '@/hooks';
import useLauncherStore from '@/stores/launcherStore'; import useLauncherStore from '@/stores/launcherStore';
import useHdiffzStore from '@/stores/hdiffzStore'; import useDiffStore from '@/stores/diffStore';
export const Route = createRootRoute({ export const Route = createRootRoute({
component: RootLayout component: RootLayout
@@ -11,7 +11,7 @@ export const Route = createRootRoute({
function RootLayout() { function RootLayout() {
const { setGameRunning, setServerRunning, setProxyRunning, setProgressDownload, setDownloadSpeed } = useLauncherStore() const { setGameRunning, setServerRunning, setProxyRunning, setProgressDownload, setDownloadSpeed } = useLauncherStore()
const { setProgressUpdate, setMaxProgressUpdate, setMessageUpdate } = useHdiffzStore() const { setProgressUpdate, setMaxProgressUpdate, setMessageUpdate, setStageType } = useDiffStore()
useGlobalEvents({ useGlobalEvents({
setGameRunning, setGameRunning,
setServerRunning, setServerRunning,
@@ -21,6 +21,7 @@ function RootLayout() {
setProgressDownload, setProgressDownload,
setDownloadSpeed, setDownloadSpeed,
setMessageUpdate, setMessageUpdate,
setStageType
}); });
return ( return (
@@ -40,7 +41,7 @@ function RootLayout() {
<a>Tools</a> <a>Tools</a>
<ul className="p-2"> <ul className="p-2">
<li><Link to="/language">Language</Link></li> <li><Link to="/language">Language</Link></li>
<li><Link to="/hdiffz">Hdiffz</Link></li> <li><Link to="/diff">Diff</Link></li>
</ul> </ul>
</li> </li>
<li> <li>
@@ -77,7 +78,7 @@ function RootLayout() {
<summary>Tools</summary> <summary>Tools</summary>
<ul className="p-2"> <ul className="p-2">
<li><Link to="/language">Language</Link></li> <li><Link to="/language">Language</Link></li>
<li><Link to="/hdiffz">Hdiffz</Link></li> <li><Link to="/diff">Diff</Link></li>
</ul> </ul>
</details> </details>
</li> </li>

View File

@@ -0,0 +1,7 @@
import DiffPage from '@/pages/diff'
import { createFileRoute } from '@tanstack/react-router'
export const Route = createFileRoute('/diff')({
component: DiffPage,
})

View File

@@ -1,7 +0,0 @@
import HdiffzPage from '@/pages/hdiffz'
import { createFileRoute } from '@tanstack/react-router'
export const Route = createFileRoute('/hdiffz')({
component: HdiffzPage,
})

View File

@@ -1,7 +1,7 @@
import { create } from 'zustand' import { create } from 'zustand'
interface LauncherState { interface DiffState {
folderCheckResult: 'success' | 'error' | null, folderCheckResult: 'success' | 'error' | null,
isLoading: {game: boolean, diff: boolean}, isLoading: {game: boolean, diff: boolean},
diffDir: string, diffDir: string,
@@ -22,7 +22,7 @@ interface LauncherState {
setStageType: (value: string) => void, setStageType: (value: string) => void,
} }
const useLauncherStore = create<LauncherState>((set, get) => ({ const useDiffStore = create<DiffState>((set, get) => ({
isLoading: {game: false, diff: false}, isLoading: {game: false, diff: false},
folderCheckResult: null, folderCheckResult: null,
diffDir: "", diffDir: "",
@@ -43,4 +43,4 @@ const useLauncherStore = create<LauncherState>((set, get) => ({
setStageType: (value: string) => set({ stageType: value }), setStageType: (value: string) => set({ stageType: value }),
})); }));
export default useLauncherStore; export default useDiffStore;

4
go.mod
View File

@@ -33,6 +33,7 @@ require (
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/jchv/go-winloader v0.0.0-20250406163304-c1995be93bd1 // indirect github.com/jchv/go-winloader v0.0.0-20250406163304-c1995be93bd1 // indirect
github.com/kevinburke/ssh_config v1.4.0 // indirect github.com/kevinburke/ssh_config v1.4.0 // indirect
github.com/klauspost/compress v1.18.0
github.com/leaanthony/go-ansi-parser v1.6.1 // indirect github.com/leaanthony/go-ansi-parser v1.6.1 // indirect
github.com/leaanthony/u v1.1.1 // indirect github.com/leaanthony/u v1.1.1 // indirect
github.com/lmittmann/tint v1.1.2 // indirect github.com/lmittmann/tint v1.1.2 // indirect
@@ -49,8 +50,9 @@ require (
github.com/wailsapp/mimetype v1.4.1 // indirect github.com/wailsapp/mimetype v1.4.1 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect
golang.org/x/crypto v0.41.0 // indirect golang.org/x/crypto v0.41.0 // indirect
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b // indirect golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b
golang.org/x/net v0.43.0 // indirect golang.org/x/net v0.43.0 // indirect
google.golang.org/protobuf v1.36.8
gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect
) )

4
go.sum
View File

@@ -55,6 +55,8 @@ github.com/jchv/go-winloader v0.0.0-20250406163304-c1995be93bd1 h1:njuLRcjAuMKr7
github.com/jchv/go-winloader v0.0.0-20250406163304-c1995be93bd1/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs= github.com/jchv/go-winloader v0.0.0-20250406163304-c1995be93bd1/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs=
github.com/kevinburke/ssh_config v1.4.0 h1:6xxtP5bZ2E4NF5tuQulISpTO2z8XbtH8cg1PWkxoFkQ= github.com/kevinburke/ssh_config v1.4.0 h1:6xxtP5bZ2E4NF5tuQulISpTO2z8XbtH8cg1PWkxoFkQ=
github.com/kevinburke/ssh_config v1.4.0/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M= github.com/kevinburke/ssh_config v1.4.0/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
@@ -147,6 +149,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc=
google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=

View File

@@ -1,4 +1,4 @@
package internal package appService
import ( import (
"firefly-launcher/pkg/constant" "firefly-launcher/pkg/constant"

View File

@@ -1,15 +1,13 @@
package internal package diffService
import ( import (
"bufio" "bufio"
"encoding/json" "encoding/json"
"firefly-launcher/pkg/constant" "firefly-launcher/pkg/constant"
"firefly-launcher/pkg/hpatchz"
"firefly-launcher/pkg/models" "firefly-launcher/pkg/models"
"firefly-launcher/pkg/sevenzip" "firefly-launcher/pkg/sevenzip"
"firefly-launcher/pkg/verifier"
"firefly-launcher/pkg/hpatchz"
"fmt" "fmt"
"io"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
@@ -17,21 +15,23 @@ import (
"github.com/wailsapp/wails/v3/pkg/application" "github.com/wailsapp/wails/v3/pkg/application"
) )
type HdiffzService struct{} type DiffService struct{}
func (h *HdiffzService) CheckTypeHDiff(patchPath string) (bool, string, string) { func (h *DiffService) CheckTypeHDiff(patchPath string) (bool, string, string) {
isFileInTxt, _ := sevenzip.IsFileIn7z(patchPath, "hdifffiles.txt") if ok, err := sevenzip.IsFileIn7z(patchPath, "manifest"); err == nil && ok {
if isFileInTxt { return true, "manifest", ""
}
if ok, err := sevenzip.IsFileIn7z(patchPath, "hdifffiles.txt"); err == nil && ok {
return true, "hdifffiles.txt", "" return true, "hdifffiles.txt", ""
} }
isFileInJson, _ := sevenzip.IsFileIn7z(patchPath, "hdiffmap.json") if ok, err := sevenzip.IsFileIn7z(patchPath, "hdiffmap.json"); err == nil && ok {
if isFileInJson {
return true, "hdiffmap.json", "" return true, "hdiffmap.json", ""
} }
return false, "", "not found hdifffiles.txt or hdiffmap.json" return false, "", "not found hdifffiles.txt or hdiffmap.json"
} }
func (h *HdiffzService) VersionValidate(gamePath, patchPath string) (bool, string) { func (h *DiffService) VersionValidate(gamePath, patchPath string) (bool, string) {
oldVersionData, err := models.ParseBinaryVersion(filepath.Join(gamePath, "StarRail_Data\\StreamingAssets\\BinaryVersion.bytes")) oldVersionData, err := models.ParseBinaryVersion(filepath.Join(gamePath, "StarRail_Data\\StreamingAssets\\BinaryVersion.bytes"))
if err != nil { if err != nil {
return false, err.Error() return false, err.Error()
@@ -65,95 +65,7 @@ func (h *HdiffzService) VersionValidate(gamePath, patchPath string) (bool, strin
return true, "validated" return true, "validated"
} }
func (h *HdiffzService) DataExtract(gamePath, patchPath string, isSkipVerify bool) (bool, string) { func (h *DiffService) HDiffPatchData(gamePath string) (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().Event.Emit("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") hdiffMapPath := filepath.Join(gamePath, "hdiffmap.json")
hdiffFilesPath := filepath.Join(gamePath, "hdifffiles.txt") hdiffFilesPath := filepath.Join(gamePath, "hdifffiles.txt")
@@ -187,10 +99,10 @@ func (h *HdiffzService) PatchData(gamePath string) (bool, string) {
} else { } else {
return false, "no hdiff entries map exist" return false, "no hdiff entries map exist"
} }
application.Get().Event.Emit("diff:stage", map[string]string{"stage": "Patching HDiff"})
for i, entry := range jsonData.DiffMap { for i, entry := range jsonData.DiffMap {
application.Get().Event.Emit( application.Get().Event.Emit(
"hdiffz:progress", map[string]int{ "diff:progress", map[string]int{
"progress": i, "progress": i,
"maxProgress": len(jsonData.DiffMap), "maxProgress": len(jsonData.DiffMap),
}) })
@@ -199,20 +111,13 @@ func (h *HdiffzService) PatchData(gamePath string) (bool, string) {
patchFile := filepath.Join(gamePath, entry.PatchFileName) patchFile := filepath.Join(gamePath, entry.PatchFileName)
targetFile := filepath.Join(gamePath, entry.TargetFileName) targetFile := filepath.Join(gamePath, entry.TargetFileName)
// Check patch file tồn tại chưa
if _, err := os.Stat(patchFile); os.IsNotExist(err) { if _, err := os.Stat(patchFile); os.IsNotExist(err) {
continue continue
} }
// Nếu không có source file hoặc SourceFileName rỗng → apply_patch_empty
if entry.SourceFileName == "" { if entry.SourceFileName == "" {
err := hpatchz.ApplyPatchEmpty(patchFile, targetFile) hpatchz.ApplyPatchEmpty(patchFile, targetFile)
if err != nil { os.Remove(patchFile)
fmt.Printf("%s failed to patch! %v\n", entry.TargetFileName, err)
_ = os.Remove(patchFile)
return false, err.Error()
}
_ = os.Remove(patchFile)
continue continue
} }
@@ -220,18 +125,11 @@ func (h *HdiffzService) PatchData(gamePath string) (bool, string) {
continue continue
} }
// Có source file → apply_patch hpatchz.ApplyPatch(sourceFile, patchFile, targetFile)
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 { if entry.SourceFileName != entry.TargetFileName {
_ = os.Remove(sourceFile) os.Remove(sourceFile)
} }
_ = os.Remove(patchFile) os.Remove(patchFile)
} }
os.Remove(filepath.Join(gamePath, "hdiffmap.json")) os.Remove(filepath.Join(gamePath, "hdiffmap.json"))
@@ -239,7 +137,7 @@ func (h *HdiffzService) PatchData(gamePath string) (bool, string) {
return true, "patching completed" return true, "patching completed"
} }
func (h *HdiffzService) DeleteFiles(gamePath string) (bool, string) { func (h *DiffService) DeleteFiles(gamePath string) (bool, string) {
var deleteFiles []string var deleteFiles []string
file, err := os.Open(filepath.Join(gamePath, "deletefiles.txt")) file, err := os.Open(filepath.Join(gamePath, "deletefiles.txt"))
@@ -262,9 +160,8 @@ func (h *HdiffzService) DeleteFiles(gamePath string) (bool, string) {
for i, file := range deleteFiles { for i, file := range deleteFiles {
os.Remove(filepath.Join(gamePath, file)) os.Remove(filepath.Join(gamePath, file))
application.Get().Event.Emit("hdiffz:progress", map[string]int{"progress": i, "maxProgress": len(deleteFiles)}) application.Get().Event.Emit("diff:progress", map[string]int{"progress": i, "maxProgress": len(deleteFiles)})
} }
_ = os.Remove(filepath.Join(gamePath, "deletefiles.txt")) os.Remove(filepath.Join(gamePath, "deletefiles.txt"))
return true, "" return true, ""
} }

View File

@@ -0,0 +1,172 @@
package diffService
import (
"firefly-launcher/pkg/firefly"
"firefly-launcher/pkg/firefly/pb"
"firefly-launcher/pkg/hpatchz"
"firefly-launcher/pkg/models"
"fmt"
"os"
"path/filepath"
"strings"
"sync"
"github.com/wailsapp/wails/v3/pkg/application"
)
func (h *DiffService) LDiffPatchData(gamePath string) (bool, string) {
entries, err := os.ReadDir(gamePath)
if err != nil {
return false, err.Error()
}
ldiffPath := filepath.Join(gamePath, "ldiff")
for _, entry := range entries {
if entry.IsDir() {
continue
}
if !entry.Type().IsRegular() {
continue
}
name := entry.Name()
if strings.HasPrefix(name, "manifest") {
manifestName := entry.Name()
manifestPath := filepath.Join(gamePath, manifestName)
manifest, err := firefly.LoadManifestProto(manifestPath)
if err != nil {
continue
}
ldiffEntries, err := os.ReadDir(ldiffPath)
if err != nil {
return false, err.Error()
}
application.Get().Event.Emit("diff:stage", map[string]string{"stage": "Processing LDiff"})
for i, ldiffEntry := range ldiffEntries {
assetName := ldiffEntry.Name()
var matchingAssets []struct {
AssetName string
AssetSize int64
Asset *pb.AssetManifest
}
application.Get().Event.Emit(
"diff:progress", map[string]int{
"progress": i,
"maxProgress": len(ldiffEntries),
})
var wg sync.WaitGroup
var mu sync.Mutex
for _, assetGroup := range manifest.Assets {
assetGroup := assetGroup
wg.Go(func() {
if data := assetGroup.AssetData; data != nil {
for _, asset := range data.Assets {
if asset.ChunkFileName == assetName {
mu.Lock()
matchingAssets = append(matchingAssets, struct {
AssetName string
AssetSize int64
Asset *pb.AssetManifest
}{assetGroup.AssetName, assetGroup.AssetSize, asset})
mu.Unlock()
}
}
}
})
}
wg.Wait()
for _, ma := range matchingAssets {
err := firefly.LDiffFile(ma.Asset, ma.AssetName, ma.AssetSize, ldiffPath, gamePath)
if err != nil {
continue
}
}
}
diffMapNames := make([]string, len(ldiffEntries))
for i, e := range ldiffEntries {
diffMapNames[i] = e.Name()
}
diffMapList, err := MakeDiffMap(manifest, diffMapNames)
if err != nil {
return false, err.Error()
}
application.Get().Event.Emit("diff:stage", map[string]string{"stage": "Patching HDiff"})
for i, entry := range diffMapList {
application.Get().Event.Emit(
"diff:progress", map[string]int{
"progress": i,
"maxProgress": len(diffMapList),
})
sourceFile := filepath.Join(gamePath, entry.SourceFileName)
patchFile := filepath.Join(gamePath, entry.PatchFileName)
targetFile := filepath.Join(gamePath, entry.TargetFileName)
if _, err := os.Stat(patchFile); os.IsNotExist(err) {
continue
}
if entry.SourceFileName == "" {
hpatchz.ApplyPatchEmpty(patchFile, targetFile)
os.Remove(patchFile)
continue
}
if _, err := os.Stat(sourceFile); os.IsNotExist(err) {
continue
}
hpatchz.ApplyPatch(sourceFile, patchFile, targetFile)
if entry.SourceFileName != entry.TargetFileName {
os.Remove(sourceFile)
}
os.Remove(patchFile)
}
}
}
os.RemoveAll(ldiffPath)
return true, "patching completed"
}
func MakeDiffMap(manifest *pb.ManifestProto, chunkNames []string) ([]*models.HDiffData, error) {
var hdiffFiles []*models.HDiffData
for _, asset := range manifest.Assets {
assetName := asset.AssetName
assetSize := asset.AssetSize
if asset.AssetData != nil {
for _, chunk := range asset.AssetData.Assets {
matched := false
for _, name := range chunkNames {
if name == chunk.ChunkFileName {
matched = true
break
}
}
if !matched {
continue
}
if chunk.OriginalFileSize != 0 || chunk.HdiffFileSize != assetSize {
hdiffFiles = append(hdiffFiles, &models.HDiffData{
SourceFileName: chunk.OriginalFilePath,
TargetFileName: assetName,
PatchFileName: fmt.Sprintf("%s.hdiff", assetName),
})
}
}
}
}
return hdiffFiles, nil
}

View File

@@ -0,0 +1,100 @@
package diffService
import (
"firefly-launcher/pkg/constant"
"firefly-launcher/pkg/verifier"
"firefly-launcher/pkg/sevenzip"
"github.com/wailsapp/wails/v3/pkg/application"
"os"
"path/filepath"
"io"
)
func (h *DiffService) DataExtract(gamePath, patchPath string, isSkipVerify bool) (bool, string) {
os.RemoveAll(constant.TempUrl)
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 *DiffService) 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().Event.Emit("diff: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 {
os.RemoveAll(constant.TempUrl)
return false, err.Error()
}
os.RemoveAll(constant.TempUrl)
return true, "cut completed"
}

View File

@@ -1,4 +1,4 @@
package internal package fsService
import ( import (
"firefly-launcher/pkg/sevenzip" "firefly-launcher/pkg/sevenzip"
@@ -30,7 +30,7 @@ func (f *FSService) PickFolder() (string, error) {
return "", nil return "", nil
} }
func (f *FSService) PickFile() (string, error) { func (f *FSService) PickFile(filter string) (string, error) {
dialog := application.OpenFileDialog(). dialog := application.OpenFileDialog().
CanChooseFiles(true). CanChooseFiles(true).
ResolvesAliases(true) ResolvesAliases(true)
@@ -39,7 +39,9 @@ func (f *FSService) PickFile() (string, error) {
} else { } else {
dialog.SetTitle("Select a file/directory") dialog.SetTitle("Select a file/directory")
} }
dialog.AddFilter("Executable Files (*.exe)", "*.exe") if filter == "exe" {
dialog.AddFilter("Executable Files (*.exe)", "*.exe")
}
if path, err := dialog.PromptForSingleSelection(); err == nil { if path, err := dialog.PromptForSingleSelection(); err == nil {
return path, nil return path, nil
} }

View File

@@ -1,282 +0,0 @@
package internal
import (
"encoding/json"
"firefly-launcher/pkg/constant"
"firefly-launcher/pkg/models"
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"github.com/minio/selfupdate"
"github.com/wailsapp/wails/v3/pkg/application"
)
type GitService struct{}
func (g *GitService) GetLatestServerVersion() (bool, string, string) {
resp, err := http.Get(constant.ServerGitUrl)
if err != nil {
return false, "", err.Error()
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var releases []models.ReleaseType
err = json.Unmarshal(body, &releases)
if err != nil {
return false, "", err.Error()
}
if len(releases) == 0 {
return false, "", "no releases found"
}
return true, releases[0].TagName, ""
}
func (g *GitService) DownloadServerProgress(version string) (bool, string) {
resp, err := http.Get(constant.ServerGitUrl)
if err != nil {
panic(err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var releases []*models.ReleaseType
err = json.Unmarshal(body, &releases)
if err != nil {
return false, err.Error()
}
if len(releases) == 0 {
return false, "no releases found"
}
var releaseData *models.ReleaseType
for _, release := range releases {
if release.TagName == version {
releaseData = release
break
}
}
if releaseData == nil || releaseData.TagName == "" {
return false, "no release found"
}
var assetWin models.AssetType
for _, asset := range releaseData.Assets {
if asset.Name == constant.ServerZipFile {
assetWin = asset
break
}
}
if assetWin.Name == "" {
return false, "no assets found"
}
if err := os.Mkdir(constant.ServerStorageUrl, 0755); err != nil {
if !os.IsExist(err) {
return false, err.Error()
}
}
saveFile := filepath.Join(constant.ServerStorageUrl, assetWin.Name)
resp, err = http.Get(assetWin.BrowserDownloadURL)
if err != nil {
return false, err.Error()
}
defer resp.Body.Close()
DownloadFile(saveFile, assetWin.BrowserDownloadURL, func(percent float64, speed float64) {
application.Get().Event.Emit("download:server", map[string]interface{}{
"percent": fmt.Sprintf("%.2f", percent),
"speed": fmt.Sprintf("%.2f", speed),
})
})
return true, ""
}
func (g *GitService) UnzipServer() {
unzipParallel(filepath.Join(constant.ServerStorageUrl, constant.ServerZipFile), constant.ServerStorageUrl)
os.Remove(filepath.Join(constant.ServerStorageUrl, constant.ServerZipFile))
}
func (g *GitService) GetLatestProxyVersion() (bool, string, string) {
resp, err := http.Get(constant.ProxyGitUrl)
if err != nil {
return false, "", err.Error()
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var releases []models.ReleaseType
err = json.Unmarshal(body, &releases)
if err != nil {
return false, "", err.Error()
}
if len(releases) == 0 {
return false, "", "no releases found"
}
return true, releases[0].TagName, ""
}
func (g *GitService) DownloadProxyProgress(version string) (bool, string) {
resp, err := http.Get(constant.ProxyGitUrl)
if err != nil {
panic(err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var releases []*models.ReleaseType
err = json.Unmarshal(body, &releases)
if err != nil {
return false, err.Error()
}
if len(releases) == 0 {
return false, "no releases found"
}
var releaseData *models.ReleaseType
for _, release := range releases {
if release.TagName == version {
releaseData = release
break
}
}
if releaseData == nil || releaseData.TagName == "" {
return false, "no release found"
}
var assetWin models.AssetType
for _, asset := range releaseData.Assets {
if asset.Name == constant.ProxyZipFile {
assetWin = asset
break
}
}
if assetWin.Name == "" {
return false, "no assets found"
}
if err := os.Mkdir(constant.ProxyStorageUrl, 0755); err != nil {
if !os.IsExist(err) {
return false, err.Error()
}
}
saveFile := filepath.Join(constant.ProxyStorageUrl, assetWin.Name)
resp, err = http.Get(assetWin.BrowserDownloadURL)
if err != nil {
return false, err.Error()
}
defer resp.Body.Close()
DownloadFile(saveFile, assetWin.BrowserDownloadURL, func(percent float64, speed float64) {
application.Get().Event.Emit("download:proxy", map[string]interface{}{
"percent": fmt.Sprintf("%.2f", percent),
"speed": fmt.Sprintf("%.2f", speed),
})
})
return true, ""
}
func (g *GitService) UnzipProxy() {
unzipParallel(filepath.Join(constant.ProxyStorageUrl, constant.ProxyZipFile), constant.ProxyStorageUrl)
os.Remove(filepath.Join(constant.ProxyStorageUrl, constant.ProxyZipFile))
}
func (g *GitService) GetLatestLauncherVersion() (bool, string, string) {
resp, err := http.Get(constant.LauncherGitUrl)
if err != nil {
return false, "", err.Error()
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var releases []models.ReleaseType
err = json.Unmarshal(body, &releases)
if err != nil {
return false, "", err.Error()
}
if len(releases) == 0 {
return false, "", "no releases found"
}
return true, releases[0].TagName, ""
}
func (g *GitService) UpdateLauncherProgress(version string) (bool, string) {
resp, err := http.Get(constant.LauncherGitUrl)
if err != nil {
panic(err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var releases []*models.ReleaseType
err = json.Unmarshal(body, &releases)
if err != nil {
return false, err.Error()
}
if len(releases) == 0 {
return false, "no releases found"
}
var releaseData *models.ReleaseType
for _, release := range releases {
if release.TagName == version {
releaseData = release
break
}
}
if releaseData == nil || releaseData.TagName == "" {
return false, "no release found"
}
var assetWin models.AssetType
for _, asset := range releaseData.Assets {
if asset.Name == constant.LauncherFile {
assetWin = asset
break
}
}
if assetWin.Name == "" {
return false, "no assets found"
}
resp, err = http.Get(assetWin.BrowserDownloadURL)
if err != nil {
return false, err.Error()
}
defer resp.Body.Close()
err = selfupdate.Apply(resp.Body, selfupdate.Options{})
if err != nil {
return false, err.Error()
}
return true, ""
}

View File

@@ -0,0 +1,93 @@
package gitService
import (
"encoding/json"
"firefly-launcher/pkg/constant"
"firefly-launcher/pkg/models"
"io"
"net/http"
"github.com/minio/selfupdate"
)
type GitService struct{}
func (g *GitService) GetLatestLauncherVersion() (bool, string, string) {
resp, err := http.Get(constant.LauncherGitUrl)
if err != nil {
return false, "", err.Error()
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var releases []models.ReleaseType
err = json.Unmarshal(body, &releases)
if err != nil {
return false, "", err.Error()
}
if len(releases) == 0 {
return false, "", "no releases found"
}
return true, releases[0].TagName, ""
}
func (g *GitService) UpdateLauncherProgress(version string) (bool, string) {
resp, err := http.Get(constant.LauncherGitUrl)
if err != nil {
panic(err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var releases []*models.ReleaseType
err = json.Unmarshal(body, &releases)
if err != nil {
return false, err.Error()
}
if len(releases) == 0 {
return false, "no releases found"
}
var releaseData *models.ReleaseType
for _, release := range releases {
if release.TagName == version {
releaseData = release
break
}
}
if releaseData == nil || releaseData.TagName == "" {
return false, "no release found"
}
var assetWin models.AssetType
for _, asset := range releaseData.Assets {
if asset.Name == constant.LauncherFile {
assetWin = asset
break
}
}
if assetWin.Name == "" {
return false, "no assets found"
}
resp, err = http.Get(assetWin.BrowserDownloadURL)
if err != nil {
return false, err.Error()
}
defer resp.Body.Close()
err = selfupdate.Apply(resp.Body, selfupdate.Options{})
if err != nil {
return false, err.Error()
}
return true, ""
}

View File

@@ -0,0 +1,106 @@
package gitService
import (
"firefly-launcher/pkg/constant"
"firefly-launcher/pkg/models"
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"encoding/json"
"github.com/wailsapp/wails/v3/pkg/application"
)
func (g *GitService) GetLatestProxyVersion() (bool, string, string) {
resp, err := http.Get(constant.ProxyGitUrl)
if err != nil {
return false, "", err.Error()
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var releases []models.ReleaseType
err = json.Unmarshal(body, &releases)
if err != nil {
return false, "", err.Error()
}
if len(releases) == 0 {
return false, "", "no releases found"
}
return true, releases[0].TagName, ""
}
func (g *GitService) DownloadProxyProgress(version string) (bool, string) {
resp, err := http.Get(constant.ProxyGitUrl)
if err != nil {
panic(err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var releases []*models.ReleaseType
err = json.Unmarshal(body, &releases)
if err != nil {
return false, err.Error()
}
if len(releases) == 0 {
return false, "no releases found"
}
var releaseData *models.ReleaseType
for _, release := range releases {
if release.TagName == version {
releaseData = release
break
}
}
if releaseData == nil || releaseData.TagName == "" {
return false, "no release found"
}
var assetWin models.AssetType
for _, asset := range releaseData.Assets {
if asset.Name == constant.ProxyZipFile {
assetWin = asset
break
}
}
if assetWin.Name == "" {
return false, "no assets found"
}
if err := os.Mkdir(constant.ProxyStorageUrl, 0755); err != nil {
if !os.IsExist(err) {
return false, err.Error()
}
}
saveFile := filepath.Join(constant.ProxyStorageUrl, assetWin.Name)
resp, err = http.Get(assetWin.BrowserDownloadURL)
if err != nil {
return false, err.Error()
}
defer resp.Body.Close()
DownloadFile(saveFile, assetWin.BrowserDownloadURL, func(percent float64, speed float64) {
application.Get().Event.Emit("download:proxy", map[string]interface{}{
"percent": fmt.Sprintf("%.2f", percent),
"speed": fmt.Sprintf("%.2f", speed),
})
})
return true, ""
}
func (g *GitService) UnzipProxy() {
unzipParallel(filepath.Join(constant.ProxyStorageUrl, constant.ProxyZipFile), constant.ProxyStorageUrl)
os.Remove(filepath.Join(constant.ProxyStorageUrl, constant.ProxyZipFile))
}

View File

@@ -0,0 +1,107 @@
package gitService
import (
"encoding/json"
"firefly-launcher/pkg/constant"
"firefly-launcher/pkg/models"
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"github.com/wailsapp/wails/v3/pkg/application"
)
func (g *GitService) GetLatestServerVersion() (bool, string, string) {
resp, err := http.Get(constant.ServerGitUrl)
if err != nil {
return false, "", err.Error()
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var releases []models.ReleaseType
err = json.Unmarshal(body, &releases)
if err != nil {
return false, "", err.Error()
}
if len(releases) == 0 {
return false, "", "no releases found"
}
return true, releases[0].TagName, ""
}
func (g *GitService) DownloadServerProgress(version string) (bool, string) {
resp, err := http.Get(constant.ServerGitUrl)
if err != nil {
panic(err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var releases []*models.ReleaseType
err = json.Unmarshal(body, &releases)
if err != nil {
return false, err.Error()
}
if len(releases) == 0 {
return false, "no releases found"
}
var releaseData *models.ReleaseType
for _, release := range releases {
if release.TagName == version {
releaseData = release
break
}
}
if releaseData == nil || releaseData.TagName == "" {
return false, "no release found"
}
var assetWin models.AssetType
for _, asset := range releaseData.Assets {
if asset.Name == constant.ServerZipFile {
assetWin = asset
break
}
}
if assetWin.Name == "" {
return false, "no assets found"
}
if err := os.Mkdir(constant.ServerStorageUrl, 0755); err != nil {
if !os.IsExist(err) {
return false, err.Error()
}
}
saveFile := filepath.Join(constant.ServerStorageUrl, assetWin.Name)
resp, err = http.Get(assetWin.BrowserDownloadURL)
if err != nil {
return false, err.Error()
}
defer resp.Body.Close()
DownloadFile(saveFile, assetWin.BrowserDownloadURL, func(percent float64, speed float64) {
application.Get().Event.Emit("download:server", map[string]interface{}{
"percent": fmt.Sprintf("%.2f", percent),
"speed": fmt.Sprintf("%.2f", speed),
})
})
return true, ""
}
func (g *GitService) UnzipServer() {
unzipParallel(filepath.Join(constant.ServerStorageUrl, constant.ServerZipFile), constant.ServerStorageUrl)
os.Remove(filepath.Join(constant.ServerStorageUrl, constant.ServerZipFile))
}

View File

@@ -1,4 +1,4 @@
package internal package gitService
import ( import (
"archive/zip" "archive/zip"

View File

@@ -1,4 +1,4 @@
package internal package languageService
import ( import (
"bytes" "bytes"

View File

@@ -1 +0,0 @@
package internal

18
main.go
View File

@@ -3,7 +3,13 @@ package main
import ( import (
"embed" "embed"
_ "embed" _ "embed"
"firefly-launcher/internal"
appService "firefly-launcher/internal/app-service"
diffService "firefly-launcher/internal/diff-service"
fsService "firefly-launcher/internal/fs-service"
gitService "firefly-launcher/internal/git-service"
languageService "firefly-launcher/internal/language-service"
"firefly-launcher/pkg/constant" "firefly-launcher/pkg/constant"
"fmt" "fmt"
"log" "log"
@@ -63,11 +69,11 @@ func main() {
Name: "firefly-launcher", Name: "firefly-launcher",
Description: "Firefly Launcher - Kain", Description: "Firefly Launcher - Kain",
Services: []application.Service{ Services: []application.Service{
application.NewService(&internal.FSService{}), application.NewService(&fsService.FSService{}),
application.NewService(&internal.LanguageService{}), application.NewService(&languageService.LanguageService{}),
application.NewService(&internal.GitService{}), application.NewService(&gitService.GitService{}),
application.NewService(&internal.HdiffzService{}), application.NewService(&diffService.DiffService{}),
application.NewService(&internal.AppService{}), application.NewService(&appService.AppService{}),
}, },
Assets: application.AssetOptions{ Assets: application.AssetOptions{
Handler: application.AssetFileServerFS(assets), Handler: application.AssetFileServerFS(assets),

View File

@@ -10,8 +10,7 @@ const ProxyZipFile = "64bit.zip"
const LauncherFile = "firefly-launcher.exe" const LauncherFile = "firefly-launcher.exe"
const TempUrl = "./temp" const TempUrl = "./temp"
const CurrentLauncherVersion = "1.5.0"
const CurrentLauncherVersion = "1.4.1"
type ToolFile string type ToolFile string

73
pkg/firefly/chunk.go Normal file
View File

@@ -0,0 +1,73 @@
package firefly
import (
"fmt"
"io"
"os"
"path/filepath"
"golang.org/x/exp/mmap"
)
type ChunkInfo struct {
Name string
Offset int64
Size int64
}
func ProcessWithBufReader(path string, chunks []ChunkInfo) {
file, err := os.Open(path)
if err != nil {
fmt.Printf("Error opening file %s: %v\n", path, err)
return
}
defer file.Close()
for _, chunk := range chunks {
buffer, err := ReadChunkData(file, chunk.Offset, chunk.Size)
if err != nil {
fmt.Printf("Error reading chunk %s: %v\n", chunk.Name, err)
continue
}
assetPath := filepath.Join("chunk_tmp", chunk.Name)
parentDir := filepath.Dir(assetPath)
if _, err := os.Stat(parentDir); os.IsNotExist(err) {
if err := os.MkdirAll(parentDir, 0o755); err != nil {
fmt.Printf("Error creating directory %s: %v\n", parentDir, err)
continue
}
}
if err := os.WriteFile(assetPath, buffer, 0o644); err != nil {
fmt.Printf("Error writing chunk file %s: %v\n", assetPath, err)
}
}
}
func ReadChunkData(file *os.File, offset, size int64) ([]byte, error) {
info, err := file.Stat()
if err != nil {
return nil, fmt.Errorf("error getting file info: %w", err)
}
if info.Size() > 1024*1024 && size > 1024*1024 {
mmapReader, err := mmap.Open(file.Name())
if err == nil {
defer mmapReader.Close()
buffer := make([]byte, size)
_, err := mmapReader.ReadAt(buffer, offset)
if err != nil && err != io.EOF {
return nil, fmt.Errorf("error reading mmap: %w", err)
}
return buffer, nil
}
}
buffer := make([]byte, size)
_, err = file.ReadAt(buffer, offset)
if err != nil && err != io.EOF {
return nil, fmt.Errorf("error reading buffered: %w", err)
}
return buffer, nil
}

94
pkg/firefly/ldiff.go Normal file
View File

@@ -0,0 +1,94 @@
package firefly
import (
"firefly-launcher/pkg/firefly/pb"
"fmt"
"os"
"path/filepath"
"golang.org/x/exp/mmap"
)
func LDiffFile(data *pb.AssetManifest, assetName string, assetSize int64, ldiffsDir, outputDir string) error {
path := filepath.Join(ldiffsDir, data.ChunkFileName)
info, err := os.Stat(path)
if err != nil {
return fmt.Errorf("%s does not exist: %w", path, err)
}
fileSize := info.Size()
var buffer []byte
if fileSize > 10*1024*1024 && data.HdiffFileSize > 1*1024*1024 {
// mmap for large files using x/exp/mmap
reader, err := mmap.Open(path)
if err != nil {
// fallback to buffered read
file, err := os.Open(path)
if err != nil {
return fmt.Errorf("error opening file %s: %w", path, err)
}
defer file.Close()
buffer, err = ReadBuffer(file, data.HdiffFileInChunkOffset, data.HdiffFileSize)
if err != nil {
return err
}
} else {
defer reader.Close()
buffer = make([]byte, data.HdiffFileSize)
_, err := reader.ReadAt(buffer, data.HdiffFileInChunkOffset)
if err != nil {
return fmt.Errorf("error reading mmap data: %w", err)
}
}
} else {
// small files, buffered read
file, err := os.Open(path)
if err != nil {
return fmt.Errorf("error opening file %s: %w", path, err)
}
defer file.Close()
buffer, err = ReadBuffer(file, data.HdiffFileInChunkOffset, data.HdiffFileSize)
if err != nil {
return err
}
}
extension := ""
if data.OriginalFileSize != 0 || assetSize != data.HdiffFileSize {
extension = ".hdiff"
}
assetPath := filepath.Join(outputDir, assetName+extension)
parentDir := filepath.Dir(assetPath)
if _, err := os.Stat(parentDir); os.IsNotExist(err) {
if err := os.MkdirAll(parentDir, 0o755); err != nil {
return fmt.Errorf("error creating directory %s: %w", parentDir, err)
}
}
if err := os.WriteFile(assetPath, buffer, 0o644); err != nil {
return fmt.Errorf("error writing file %s: %w", assetPath, err)
}
return nil
}
func ReadBuffer(file *os.File, offset int64, size int64) ([]byte, error) {
buffer := make([]byte, size)
n, err := file.ReadAt(buffer, offset)
if err != nil {
return nil, fmt.Errorf("error reading data: %w", err)
}
if int64(n) != size {
return nil, fmt.Errorf("expected %d bytes, but read %d bytes", size, n)
}
return buffer, nil
}

66
pkg/firefly/loader.go Normal file
View File

@@ -0,0 +1,66 @@
package firefly
import (
"bufio"
"firefly-launcher/pkg/firefly/pb"
"io"
"os"
"github.com/klauspost/compress/zstd"
"google.golang.org/protobuf/proto"
)
func LoadChunkProto(path string) (*pb.ChunkProto, error) {
file, err := os.Open(path)
if err != nil {
return nil, err
}
defer file.Close()
reader := bufio.NewReader(file)
decoder, err := zstd.NewReader(reader)
if err != nil {
return nil, err
}
defer decoder.Close()
data, err := io.ReadAll(decoder)
if err != nil {
return nil, err
}
var chunk pb.ChunkProto
if err := proto.Unmarshal(data, &chunk); err != nil {
return nil, err
}
return &chunk, nil
}
// Load ManifestProto từ file Zstd + Protobuf
func LoadManifestProto(path string) (*pb.ManifestProto, error) {
file, err := os.Open(path)
if err != nil {
return nil, err
}
defer file.Close()
reader := bufio.NewReader(file)
decoder, err := zstd.NewReader(reader)
if err != nil {
return nil, err
}
defer decoder.Close()
data, err := io.ReadAll(decoder)
if err != nil {
return nil, err
}
var manifest pb.ManifestProto
if err := proto.Unmarshal(data, &manifest); err != nil {
return nil, err
}
return &manifest, nil
}

View File

@@ -0,0 +1,651 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.5
// protoc v4.25.6
// source: proto/firefly.proto
package pb
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type ChunkProto struct {
state protoimpl.MessageState `protogen:"open.v1"`
Assets []*AssetChunkProperty `protobuf:"bytes,1,rep,name=assets,proto3" json:"assets,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ChunkProto) Reset() {
*x = ChunkProto{}
mi := &file_proto_firefly_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ChunkProto) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ChunkProto) ProtoMessage() {}
func (x *ChunkProto) ProtoReflect() protoreflect.Message {
mi := &file_proto_firefly_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ChunkProto.ProtoReflect.Descriptor instead.
func (*ChunkProto) Descriptor() ([]byte, []int) {
return file_proto_firefly_proto_rawDescGZIP(), []int{0}
}
func (x *ChunkProto) GetAssets() []*AssetChunkProperty {
if x != nil {
return x.Assets
}
return nil
}
type AssetChunkProperty struct {
state protoimpl.MessageState `protogen:"open.v1"`
AssetName string `protobuf:"bytes,1,opt,name=asset_name,json=assetName,proto3" json:"asset_name,omitempty"`
AssetChunks []*AssetChunk `protobuf:"bytes,2,rep,name=asset_chunks,json=assetChunks,proto3" json:"asset_chunks,omitempty"`
AssetType int32 `protobuf:"varint,3,opt,name=asset_type,json=assetType,proto3" json:"asset_type,omitempty"`
AssetSize int64 `protobuf:"varint,4,opt,name=asset_size,json=assetSize,proto3" json:"asset_size,omitempty"`
AssetHashMd5 string `protobuf:"bytes,5,opt,name=asset_hash_md5,json=assetHashMd5,proto3" json:"asset_hash_md5,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *AssetChunkProperty) Reset() {
*x = AssetChunkProperty{}
mi := &file_proto_firefly_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *AssetChunkProperty) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*AssetChunkProperty) ProtoMessage() {}
func (x *AssetChunkProperty) ProtoReflect() protoreflect.Message {
mi := &file_proto_firefly_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use AssetChunkProperty.ProtoReflect.Descriptor instead.
func (*AssetChunkProperty) Descriptor() ([]byte, []int) {
return file_proto_firefly_proto_rawDescGZIP(), []int{1}
}
func (x *AssetChunkProperty) GetAssetName() string {
if x != nil {
return x.AssetName
}
return ""
}
func (x *AssetChunkProperty) GetAssetChunks() []*AssetChunk {
if x != nil {
return x.AssetChunks
}
return nil
}
func (x *AssetChunkProperty) GetAssetType() int32 {
if x != nil {
return x.AssetType
}
return 0
}
func (x *AssetChunkProperty) GetAssetSize() int64 {
if x != nil {
return x.AssetSize
}
return 0
}
func (x *AssetChunkProperty) GetAssetHashMd5() string {
if x != nil {
return x.AssetHashMd5
}
return ""
}
type AssetChunk struct {
state protoimpl.MessageState `protogen:"open.v1"`
ChunkName string `protobuf:"bytes,1,opt,name=chunk_name,json=chunkName,proto3" json:"chunk_name,omitempty"`
ChunkDecompressedHashMd5 string `protobuf:"bytes,2,opt,name=chunk_decompressed_hash_md5,json=chunkDecompressedHashMd5,proto3" json:"chunk_decompressed_hash_md5,omitempty"`
ChunkOnFileOffset int64 `protobuf:"varint,3,opt,name=chunk_on_file_offset,json=chunkOnFileOffset,proto3" json:"chunk_on_file_offset,omitempty"`
ChunkSize int64 `protobuf:"varint,4,opt,name=chunk_size,json=chunkSize,proto3" json:"chunk_size,omitempty"`
ChunkSizeDecompressed int64 `protobuf:"varint,5,opt,name=chunk_size_decompressed,json=chunkSizeDecompressed,proto3" json:"chunk_size_decompressed,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *AssetChunk) Reset() {
*x = AssetChunk{}
mi := &file_proto_firefly_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *AssetChunk) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*AssetChunk) ProtoMessage() {}
func (x *AssetChunk) ProtoReflect() protoreflect.Message {
mi := &file_proto_firefly_proto_msgTypes[2]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use AssetChunk.ProtoReflect.Descriptor instead.
func (*AssetChunk) Descriptor() ([]byte, []int) {
return file_proto_firefly_proto_rawDescGZIP(), []int{2}
}
func (x *AssetChunk) GetChunkName() string {
if x != nil {
return x.ChunkName
}
return ""
}
func (x *AssetChunk) GetChunkDecompressedHashMd5() string {
if x != nil {
return x.ChunkDecompressedHashMd5
}
return ""
}
func (x *AssetChunk) GetChunkOnFileOffset() int64 {
if x != nil {
return x.ChunkOnFileOffset
}
return 0
}
func (x *AssetChunk) GetChunkSize() int64 {
if x != nil {
return x.ChunkSize
}
return 0
}
func (x *AssetChunk) GetChunkSizeDecompressed() int64 {
if x != nil {
return x.ChunkSizeDecompressed
}
return 0
}
type ManifestProto struct {
state protoimpl.MessageState `protogen:"open.v1"`
Assets []*AssetManifestProperty `protobuf:"bytes,1,rep,name=assets,proto3" json:"assets,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ManifestProto) Reset() {
*x = ManifestProto{}
mi := &file_proto_firefly_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ManifestProto) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ManifestProto) ProtoMessage() {}
func (x *ManifestProto) ProtoReflect() protoreflect.Message {
mi := &file_proto_firefly_proto_msgTypes[3]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ManifestProto.ProtoReflect.Descriptor instead.
func (*ManifestProto) Descriptor() ([]byte, []int) {
return file_proto_firefly_proto_rawDescGZIP(), []int{3}
}
func (x *ManifestProto) GetAssets() []*AssetManifestProperty {
if x != nil {
return x.Assets
}
return nil
}
type AssetManifestProperty struct {
state protoimpl.MessageState `protogen:"open.v1"`
AssetName string `protobuf:"bytes,1,opt,name=asset_name,json=assetName,proto3" json:"asset_name,omitempty"`
AssetSize int64 `protobuf:"varint,2,opt,name=asset_size,json=assetSize,proto3" json:"asset_size,omitempty"`
AssetHashMd5 string `protobuf:"bytes,3,opt,name=asset_hash_md5,json=assetHashMd5,proto3" json:"asset_hash_md5,omitempty"`
AssetData *AssetManifestChunk `protobuf:"bytes,4,opt,name=asset_data,json=assetData,proto3" json:"asset_data,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *AssetManifestProperty) Reset() {
*x = AssetManifestProperty{}
mi := &file_proto_firefly_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *AssetManifestProperty) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*AssetManifestProperty) ProtoMessage() {}
func (x *AssetManifestProperty) ProtoReflect() protoreflect.Message {
mi := &file_proto_firefly_proto_msgTypes[4]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use AssetManifestProperty.ProtoReflect.Descriptor instead.
func (*AssetManifestProperty) Descriptor() ([]byte, []int) {
return file_proto_firefly_proto_rawDescGZIP(), []int{4}
}
func (x *AssetManifestProperty) GetAssetName() string {
if x != nil {
return x.AssetName
}
return ""
}
func (x *AssetManifestProperty) GetAssetSize() int64 {
if x != nil {
return x.AssetSize
}
return 0
}
func (x *AssetManifestProperty) GetAssetHashMd5() string {
if x != nil {
return x.AssetHashMd5
}
return ""
}
func (x *AssetManifestProperty) GetAssetData() *AssetManifestChunk {
if x != nil {
return x.AssetData
}
return nil
}
type AssetManifestChunk struct {
state protoimpl.MessageState `protogen:"open.v1"`
LatestAssetVersion string `protobuf:"bytes,1,opt,name=latest_asset_version,json=latestAssetVersion,proto3" json:"latest_asset_version,omitempty"`
Assets []*AssetManifest `protobuf:"bytes,2,rep,name=assets,proto3" json:"assets,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *AssetManifestChunk) Reset() {
*x = AssetManifestChunk{}
mi := &file_proto_firefly_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *AssetManifestChunk) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*AssetManifestChunk) ProtoMessage() {}
func (x *AssetManifestChunk) ProtoReflect() protoreflect.Message {
mi := &file_proto_firefly_proto_msgTypes[5]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use AssetManifestChunk.ProtoReflect.Descriptor instead.
func (*AssetManifestChunk) Descriptor() ([]byte, []int) {
return file_proto_firefly_proto_rawDescGZIP(), []int{5}
}
func (x *AssetManifestChunk) GetLatestAssetVersion() string {
if x != nil {
return x.LatestAssetVersion
}
return ""
}
func (x *AssetManifestChunk) GetAssets() []*AssetManifest {
if x != nil {
return x.Assets
}
return nil
}
type AssetManifest struct {
state protoimpl.MessageState `protogen:"open.v1"`
ChunkFileName string `protobuf:"bytes,1,opt,name=chunk_file_name,json=chunkFileName,proto3" json:"chunk_file_name,omitempty"`
ChunkFileVersion string `protobuf:"bytes,2,opt,name=chunk_file_version,json=chunkFileVersion,proto3" json:"chunk_file_version,omitempty"`
ChunkFileNode string `protobuf:"bytes,3,opt,name=chunk_file_node,json=chunkFileNode,proto3" json:"chunk_file_node,omitempty"`
ChunkFileSize int64 `protobuf:"varint,4,opt,name=chunk_file_size,json=chunkFileSize,proto3" json:"chunk_file_size,omitempty"`
ChunkFileMd5 string `protobuf:"bytes,5,opt,name=chunk_file_md5,json=chunkFileMd5,proto3" json:"chunk_file_md5,omitempty"`
HdiffFileInChunkOffset int64 `protobuf:"varint,6,opt,name=hdiff_file_in_chunk_offset,json=hdiffFileInChunkOffset,proto3" json:"hdiff_file_in_chunk_offset,omitempty"`
HdiffFileSize int64 `protobuf:"varint,7,opt,name=hdiff_file_size,json=hdiffFileSize,proto3" json:"hdiff_file_size,omitempty"`
OriginalFilePath string `protobuf:"bytes,8,opt,name=original_file_path,json=originalFilePath,proto3" json:"original_file_path,omitempty"`
OriginalFileSize int64 `protobuf:"varint,9,opt,name=original_file_size,json=originalFileSize,proto3" json:"original_file_size,omitempty"`
OriginalFileMd5 string `protobuf:"bytes,10,opt,name=original_file_md5,json=originalFileMd5,proto3" json:"original_file_md5,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *AssetManifest) Reset() {
*x = AssetManifest{}
mi := &file_proto_firefly_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *AssetManifest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*AssetManifest) ProtoMessage() {}
func (x *AssetManifest) ProtoReflect() protoreflect.Message {
mi := &file_proto_firefly_proto_msgTypes[6]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use AssetManifest.ProtoReflect.Descriptor instead.
func (*AssetManifest) Descriptor() ([]byte, []int) {
return file_proto_firefly_proto_rawDescGZIP(), []int{6}
}
func (x *AssetManifest) GetChunkFileName() string {
if x != nil {
return x.ChunkFileName
}
return ""
}
func (x *AssetManifest) GetChunkFileVersion() string {
if x != nil {
return x.ChunkFileVersion
}
return ""
}
func (x *AssetManifest) GetChunkFileNode() string {
if x != nil {
return x.ChunkFileNode
}
return ""
}
func (x *AssetManifest) GetChunkFileSize() int64 {
if x != nil {
return x.ChunkFileSize
}
return 0
}
func (x *AssetManifest) GetChunkFileMd5() string {
if x != nil {
return x.ChunkFileMd5
}
return ""
}
func (x *AssetManifest) GetHdiffFileInChunkOffset() int64 {
if x != nil {
return x.HdiffFileInChunkOffset
}
return 0
}
func (x *AssetManifest) GetHdiffFileSize() int64 {
if x != nil {
return x.HdiffFileSize
}
return 0
}
func (x *AssetManifest) GetOriginalFilePath() string {
if x != nil {
return x.OriginalFilePath
}
return ""
}
func (x *AssetManifest) GetOriginalFileSize() int64 {
if x != nil {
return x.OriginalFileSize
}
return 0
}
func (x *AssetManifest) GetOriginalFileMd5() string {
if x != nil {
return x.OriginalFileMd5
}
return ""
}
var File_proto_firefly_proto protoreflect.FileDescriptor
var file_proto_firefly_proto_rawDesc = string([]byte{
0x0a, 0x13, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x66, 0x69, 0x72, 0x65, 0x66, 0x6c, 0x79, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x66, 0x69, 0x72, 0x65, 0x66, 0x6c, 0x79, 0x22, 0x41,
0x0a, 0x0a, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x33, 0x0a, 0x06,
0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x66,
0x69, 0x72, 0x65, 0x66, 0x6c, 0x79, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x43, 0x68, 0x75, 0x6e,
0x6b, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x52, 0x06, 0x61, 0x73, 0x73, 0x65, 0x74,
0x73, 0x22, 0xcf, 0x01, 0x0a, 0x12, 0x41, 0x73, 0x73, 0x65, 0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b,
0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65,
0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x73,
0x73, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x36, 0x0a, 0x0c, 0x61, 0x73, 0x73, 0x65, 0x74,
0x5f, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e,
0x66, 0x69, 0x72, 0x65, 0x66, 0x6c, 0x79, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x43, 0x68, 0x75,
0x6e, 0x6b, 0x52, 0x0b, 0x61, 0x73, 0x73, 0x65, 0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x12,
0x1d, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20,
0x01, 0x28, 0x05, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1d,
0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x04, 0x20, 0x01,
0x28, 0x03, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x24, 0x0a,
0x0e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x5f, 0x6d, 0x64, 0x35, 0x18,
0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x61, 0x73, 0x73, 0x65, 0x74, 0x48, 0x61, 0x73, 0x68,
0x4d, 0x64, 0x35, 0x22, 0xf2, 0x01, 0x0a, 0x0a, 0x41, 0x73, 0x73, 0x65, 0x74, 0x43, 0x68, 0x75,
0x6e, 0x6b, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x6e, 0x61, 0x6d, 0x65,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x4e, 0x61, 0x6d,
0x65, 0x12, 0x3d, 0x0a, 0x1b, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x64, 0x65, 0x63, 0x6f, 0x6d,
0x70, 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x5f, 0x6d, 0x64, 0x35,
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x18, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x44, 0x65, 0x63,
0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x48, 0x61, 0x73, 0x68, 0x4d, 0x64, 0x35,
0x12, 0x2f, 0x0a, 0x14, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x6f, 0x6e, 0x5f, 0x66, 0x69, 0x6c,
0x65, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11,
0x63, 0x68, 0x75, 0x6e, 0x6b, 0x4f, 0x6e, 0x46, 0x69, 0x6c, 0x65, 0x4f, 0x66, 0x66, 0x73, 0x65,
0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18,
0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x53, 0x69, 0x7a, 0x65,
0x12, 0x36, 0x0a, 0x17, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x64,
0x65, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28,
0x03, 0x52, 0x15, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x53, 0x69, 0x7a, 0x65, 0x44, 0x65, 0x63, 0x6f,
0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x22, 0x47, 0x0a, 0x0d, 0x4d, 0x61, 0x6e, 0x69,
0x66, 0x65, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x36, 0x0a, 0x06, 0x61, 0x73, 0x73,
0x65, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x66, 0x69, 0x72, 0x65,
0x66, 0x6c, 0x79, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73,
0x74, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x52, 0x06, 0x61, 0x73, 0x73, 0x65, 0x74,
0x73, 0x22, 0xb7, 0x01, 0x0a, 0x15, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x69, 0x66,
0x65, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x61,
0x73, 0x73, 0x65, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x73,
0x73, 0x65, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09,
0x61, 0x73, 0x73, 0x65, 0x74, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x61, 0x73, 0x73,
0x65, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x5f, 0x6d, 0x64, 0x35, 0x18, 0x03, 0x20, 0x01, 0x28,
0x09, 0x52, 0x0c, 0x61, 0x73, 0x73, 0x65, 0x74, 0x48, 0x61, 0x73, 0x68, 0x4d, 0x64, 0x35, 0x12,
0x3a, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x66, 0x69, 0x72, 0x65, 0x66, 0x6c, 0x79, 0x2e, 0x41, 0x73,
0x73, 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b,
0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x22, 0x76, 0x0a, 0x12, 0x41,
0x73, 0x73, 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x43, 0x68, 0x75, 0x6e,
0x6b, 0x12, 0x30, 0x0a, 0x14, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x61, 0x73, 0x73, 0x65,
0x74, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x12, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x41, 0x73, 0x73, 0x65, 0x74, 0x56, 0x65, 0x72, 0x73,
0x69, 0x6f, 0x6e, 0x12, 0x2e, 0x0a, 0x06, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x18, 0x02, 0x20,
0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x66, 0x69, 0x72, 0x65, 0x66, 0x6c, 0x79, 0x2e, 0x41, 0x73,
0x73, 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x52, 0x06, 0x61, 0x73, 0x73,
0x65, 0x74, 0x73, 0x22, 0xc7, 0x03, 0x0a, 0x0d, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4d, 0x61, 0x6e,
0x69, 0x66, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x66,
0x69, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d,
0x63, 0x68, 0x75, 0x6e, 0x6b, 0x46, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2c, 0x0a,
0x12, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73,
0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x63, 0x68, 0x75, 0x6e, 0x6b,
0x46, 0x69, 0x6c, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x26, 0x0a, 0x0f, 0x63,
0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x03,
0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x46, 0x69, 0x6c, 0x65, 0x4e,
0x6f, 0x64, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x66, 0x69, 0x6c,
0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x63, 0x68,
0x75, 0x6e, 0x6b, 0x46, 0x69, 0x6c, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x63,
0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x6d, 0x64, 0x35, 0x18, 0x05, 0x20,
0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x46, 0x69, 0x6c, 0x65, 0x4d, 0x64,
0x35, 0x12, 0x3a, 0x0a, 0x1a, 0x68, 0x64, 0x69, 0x66, 0x66, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f,
0x69, 0x6e, 0x5f, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18,
0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x16, 0x68, 0x64, 0x69, 0x66, 0x66, 0x46, 0x69, 0x6c, 0x65,
0x49, 0x6e, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x26, 0x0a,
0x0f, 0x68, 0x64, 0x69, 0x66, 0x66, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65,
0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x68, 0x64, 0x69, 0x66, 0x66, 0x46, 0x69, 0x6c,
0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61,
0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x08, 0x20, 0x01, 0x28,
0x09, 0x52, 0x10, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x46, 0x69, 0x6c, 0x65, 0x50,
0x61, 0x74, 0x68, 0x12, 0x2c, 0x0a, 0x12, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x5f,
0x66, 0x69, 0x6c, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52,
0x10, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x46, 0x69, 0x6c, 0x65, 0x53, 0x69, 0x7a,
0x65, 0x12, 0x2a, 0x0a, 0x11, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x66, 0x69,
0x6c, 0x65, 0x5f, 0x6d, 0x64, 0x35, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6f, 0x72,
0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x46, 0x69, 0x6c, 0x65, 0x4d, 0x64, 0x35, 0x42, 0x0e, 0x5a,
0x0c, 0x2e, 0x2f, 0x66, 0x69, 0x72, 0x65, 0x66, 0x6c, 0x79, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x33,
})
var (
file_proto_firefly_proto_rawDescOnce sync.Once
file_proto_firefly_proto_rawDescData []byte
)
func file_proto_firefly_proto_rawDescGZIP() []byte {
file_proto_firefly_proto_rawDescOnce.Do(func() {
file_proto_firefly_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_proto_firefly_proto_rawDesc), len(file_proto_firefly_proto_rawDesc)))
})
return file_proto_firefly_proto_rawDescData
}
var file_proto_firefly_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
var file_proto_firefly_proto_goTypes = []any{
(*ChunkProto)(nil), // 0: firefly.ChunkProto
(*AssetChunkProperty)(nil), // 1: firefly.AssetChunkProperty
(*AssetChunk)(nil), // 2: firefly.AssetChunk
(*ManifestProto)(nil), // 3: firefly.ManifestProto
(*AssetManifestProperty)(nil), // 4: firefly.AssetManifestProperty
(*AssetManifestChunk)(nil), // 5: firefly.AssetManifestChunk
(*AssetManifest)(nil), // 6: firefly.AssetManifest
}
var file_proto_firefly_proto_depIdxs = []int32{
1, // 0: firefly.ChunkProto.assets:type_name -> firefly.AssetChunkProperty
2, // 1: firefly.AssetChunkProperty.asset_chunks:type_name -> firefly.AssetChunk
4, // 2: firefly.ManifestProto.assets:type_name -> firefly.AssetManifestProperty
5, // 3: firefly.AssetManifestProperty.asset_data:type_name -> firefly.AssetManifestChunk
6, // 4: firefly.AssetManifestChunk.assets:type_name -> firefly.AssetManifest
5, // [5:5] is the sub-list for method output_type
5, // [5:5] is the sub-list for method input_type
5, // [5:5] is the sub-list for extension type_name
5, // [5:5] is the sub-list for extension extendee
0, // [0:5] is the sub-list for field type_name
}
func init() { file_proto_firefly_proto_init() }
func file_proto_firefly_proto_init() {
if File_proto_firefly_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_proto_firefly_proto_rawDesc), len(file_proto_firefly_proto_rawDesc)),
NumEnums: 0,
NumMessages: 7,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_proto_firefly_proto_goTypes,
DependencyIndexes: file_proto_firefly_proto_depIdxs,
MessageInfos: file_proto_firefly_proto_msgTypes,
}.Build()
File_proto_firefly_proto = out.File
file_proto_firefly_proto_goTypes = nil
file_proto_firefly_proto_depIdxs = nil
}