352 lines
16 KiB
TypeScript
352 lines
16 KiB
TypeScript
import useSettingStore from "@/stores/settingStore"
|
|
import { Check, Folder, File, X, Settings } from "lucide-react"
|
|
import { useEffect } from "react"
|
|
import { toast } from "react-toastify"
|
|
import { DiffService} from "@bindings/firefly-launcher/internal/diff-service"
|
|
import { FSService } from "@bindings/firefly-launcher/internal/fs-service"
|
|
import { motion } from "motion/react"
|
|
import useDiffStore from "@/stores/diffStore"
|
|
|
|
export default function DiffPage() {
|
|
const { gameDir, setGameDir } = useSettingStore()
|
|
const {
|
|
isLoading,
|
|
setIsLoading,
|
|
folderCheckResult,
|
|
setFolderCheckResult,
|
|
diffDir,
|
|
setDiffDir,
|
|
diffCheckResult,
|
|
setDiffCheckResult,
|
|
isDiffLoading,
|
|
setIsDiffLoading,
|
|
progressUpdate,
|
|
setProgressUpdate,
|
|
maxProgressUpdate,
|
|
setMaxProgressUpdate,
|
|
stageType,
|
|
setStageType,
|
|
messageUpdate,
|
|
setMessageUpdate
|
|
} = useDiffStore()
|
|
|
|
useEffect(() => {
|
|
const getLanguage = async () => {
|
|
if (gameDir) {
|
|
const subPath = 'StarRail_Data/StreamingAssets/DesignData/Windows'
|
|
const fullPath = `${gameDir}/${subPath}`
|
|
|
|
const exists = await FSService.DirExists(fullPath)
|
|
if (exists) {
|
|
setFolderCheckResult('success')
|
|
} else {
|
|
setFolderCheckResult('error')
|
|
setGameDir('')
|
|
}
|
|
}
|
|
}
|
|
getLanguage()
|
|
}, [gameDir])
|
|
|
|
const handlePickGameFolder = async () => {
|
|
try {
|
|
setIsLoading({game: true, diff: false})
|
|
const basePath = await FSService.PickFolder()
|
|
if (basePath) {
|
|
setGameDir(basePath)
|
|
const subPath = 'StarRail_Data/StreamingAssets/DesignData/Windows'
|
|
const fullPath = `${basePath}/${subPath}`
|
|
|
|
const exists = await FSService.DirExists(fullPath)
|
|
setFolderCheckResult(exists ? 'success' : 'error')
|
|
setGameDir(exists ? basePath : '')
|
|
if (!exists) {
|
|
toast.error('Game directory not found. Please select the correct folder.')
|
|
}
|
|
} else {
|
|
toast.error('No folder path selected')
|
|
setFolderCheckResult('error')
|
|
setGameDir('')
|
|
}
|
|
} catch (err: any) {
|
|
toast.error('PickFolder error:', err)
|
|
setFolderCheckResult('error')
|
|
} finally {
|
|
setIsLoading({game: false, diff: false})
|
|
}
|
|
}
|
|
|
|
const handlePickDiffFile = async () => {
|
|
try {
|
|
setIsLoading({game: false, diff: true})
|
|
const basePath = await FSService.PickFile("")
|
|
if (basePath) {
|
|
if (!basePath.endsWith(".7z") && !basePath.endsWith(".zip") && !basePath.endsWith(".rar")) {
|
|
toast.error('Not valid file type')
|
|
setDiffCheckResult('error')
|
|
setDiffDir('')
|
|
return
|
|
}
|
|
setDiffDir(basePath)
|
|
setDiffCheckResult('success')
|
|
} else {
|
|
toast.error('No file path selected')
|
|
setDiffCheckResult('error')
|
|
setDiffDir('')
|
|
}
|
|
} catch (err: any) {
|
|
toast.error('PickFile error:', err)
|
|
setDiffCheckResult('error')
|
|
} finally {
|
|
setIsLoading({game: false, diff: false})
|
|
}
|
|
}
|
|
|
|
const handleUpdateGame = async () => {
|
|
const handleResult = (ok: boolean, error: string) => {
|
|
if (!ok) {
|
|
toast.error(error)
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
try {
|
|
setIsDiffLoading(true)
|
|
|
|
if (!gameDir || !diffDir) {
|
|
toast.error('Please select game directory and diff file')
|
|
return
|
|
}
|
|
|
|
setStageType('Check Type HDiff')
|
|
setProgressUpdate(0)
|
|
setMaxProgressUpdate(1)
|
|
|
|
const [isOk, validType, errorType] = await DiffService.CheckTypeHDiff(diffDir)
|
|
if (!handleResult(isOk, errorType)) return
|
|
setProgressUpdate(1)
|
|
|
|
if (['hdiffmap.json', 'hdifffiles.txt', 'hdifffiles.json'].includes(validType)) {
|
|
setStageType('Version Validate')
|
|
setProgressUpdate(0)
|
|
setMaxProgressUpdate(1)
|
|
const [validVersion, errorVersion] = await DiffService.VersionValidate(gameDir, diffDir)
|
|
if (!handleResult(validVersion, errorVersion)) return
|
|
setProgressUpdate(1)
|
|
}
|
|
|
|
setStageType('Data Extract')
|
|
const [validData, errorData] = await DiffService.DataExtract(gameDir, diffDir)
|
|
if (!handleResult(validData, errorData)) return
|
|
|
|
setStageType('Cut Data')
|
|
setMessageUpdate('')
|
|
const [validCut, errorCut] = await DiffService.CutData(gameDir)
|
|
if (!handleResult(validCut, errorCut)) return
|
|
|
|
switch (validType) {
|
|
case 'hdifffiles.txt':
|
|
case 'hdiffmap.json':
|
|
case 'hdifffiles.json': {
|
|
setStageType('Patch Data')
|
|
const [validPatch, errorPatch] = await DiffService.HDiffPatchData(gameDir)
|
|
if (!handleResult(validPatch, errorPatch)) return
|
|
|
|
setStageType('Delete old files')
|
|
const [validDelete, errorDelete] = await DiffService.DeleteFiles(gameDir)
|
|
if (!handleResult(validDelete, errorDelete)) return
|
|
break
|
|
}
|
|
case 'manifest': {
|
|
setStageType('Patch Data')
|
|
const [validPatch, errorPatch] = await DiffService.LDiffPatchData(gameDir)
|
|
if (!handleResult(validPatch, errorPatch)) return
|
|
break
|
|
}
|
|
}
|
|
|
|
toast.success('Update game completed')
|
|
} catch (err: any) {
|
|
console.error(err)
|
|
toast.error(`PickFile error: ${err}`)
|
|
} finally {
|
|
setIsDiffLoading(false)
|
|
}
|
|
}
|
|
|
|
|
|
return (
|
|
<div className="p-2 mx-4">
|
|
<div className="max-w-4xl mx-auto">
|
|
{/* Header */}
|
|
<div className="text-center mb-2">
|
|
<h1 className="text-4xl font-bold mb-2">
|
|
🎮 Game Update by Hdiffz
|
|
</h1>
|
|
<p className="">Help you update game with hdiffz</p>
|
|
</div>
|
|
|
|
{/* Main Content */}
|
|
<div className="rounded-2xl p-2 space-y-4">
|
|
|
|
{/* Folder Selection Section */}
|
|
<div className="pb-2">
|
|
<h2 className="text-2xl font-semibold mb-4 flex items-center gap-2">
|
|
<Folder className="text-primary" size={24} />
|
|
Game Directory
|
|
</h2>
|
|
|
|
<div className="space-y-1">
|
|
<div className='grid grid-cols-1 md:grid-cols-2 gap-2 items-center'>
|
|
<button
|
|
onClick={handlePickGameFolder}
|
|
disabled={isLoading.game}
|
|
className="btn btn-primary"
|
|
>
|
|
<Folder size={20} />
|
|
{isLoading.game ? 'Selecting...' : 'Select Game Folder'}
|
|
</button>
|
|
|
|
{gameDir && (
|
|
<div className="rounded-lg p-2">
|
|
<p className="font-mono text-sm px-3 py-2 rounded border truncate max-w-full overflow-hidden whitespace-nowrap">
|
|
{gameDir}
|
|
</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
{folderCheckResult && (
|
|
<div className={`flex items-center gap-2 p-3 rounded-lg ${folderCheckResult === 'success'
|
|
? 'bg-success/5 text-success border border-success'
|
|
: 'bg-error/5 text-error border border-error'
|
|
}`}>
|
|
{folderCheckResult === 'success' ? (
|
|
<>
|
|
<Check size={20} />
|
|
<span>Valid game directory found!</span>
|
|
</>
|
|
) : (
|
|
<>
|
|
<X size={20} />
|
|
<span>Game directory not found. Please select the correct folder.</span>
|
|
</>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Folder Selection Section */}
|
|
<div className="pb-2">
|
|
<h2 className="text-2xl font-semibold mb-4 flex items-center gap-2">
|
|
<File className="text-primary" size={24} />
|
|
Diff file Directory
|
|
</h2>
|
|
|
|
<div className="space-y-1">
|
|
<div className='grid grid-cols-1 md:grid-cols-2 gap-2 items-center'>
|
|
<button
|
|
onClick={handlePickDiffFile}
|
|
disabled={isLoading.diff}
|
|
className="btn btn-primary"
|
|
>
|
|
<File size={20} />
|
|
{isLoading.diff ? 'Selecting...' : 'Select Diff file Folder'}
|
|
</button>
|
|
|
|
{diffDir && (
|
|
<div className="rounded-lg p-2">
|
|
<p className="font-mono text-sm px-3 py-2 rounded border truncate max-w-full overflow-hidden whitespace-nowrap">
|
|
{diffDir}
|
|
</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
{diffCheckResult && (
|
|
<div className={`flex items-center gap-2 p-3 mt-2 rounded-lg ${diffCheckResult === 'success'
|
|
? 'bg-success/5 text-success border border-success'
|
|
: 'bg-error/5 text-error border border-error'
|
|
}`}>
|
|
{diffCheckResult === 'success' ? (
|
|
<>
|
|
<Check size={20} />
|
|
<span>Valid diff file found!</span>
|
|
</>
|
|
) : (
|
|
<>
|
|
<X size={20} />
|
|
<span>Diff file not found. Please select the correct file.</span>
|
|
</>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Apply Button */}
|
|
<div className="mt-6 flex justify-center">
|
|
<button
|
|
onClick={handleUpdateGame}
|
|
disabled={!diffDir || !gameDir || isLoading.game || isLoading.diff}
|
|
className="bg-gradient-to-r from-indigo-500 to-purple-600 hover:from-indigo-600 hover:to-purple-700 disabled:from-gray-400 disabled:to-gray-500 text-white px-8 py-3 rounded-lg font-medium transition-all duration-200 flex items-center gap-2 shadow-lg hover:shadow-xl disabled:cursor-not-allowed cursor-pointer"
|
|
>
|
|
<Settings size={20} />
|
|
{isDiffLoading ? 'Updating...' : 'Update Game'}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
{isDiffLoading && (
|
|
<div className="fixed inset-0 z-50 h-full flex items-center justify-center bg-black/40 backdrop-blur-sm">
|
|
<div className="relative w-[90%] max-w-5xl bg-base-100 text-base-content rounded-xl border border-purple-500/50 shadow-lg shadow-purple-500/20">
|
|
<div className="border-b border-purple-500/30 px-6 py-4 mb-4 text-center">
|
|
<h3 className="font-bold text-2xl text-transparent bg-clip-text bg-gradient-to-r from-pink-400 to-cyan-400">
|
|
Update Game
|
|
</h3>
|
|
</div>
|
|
|
|
<div className="px-6 pb-6">
|
|
<div className="w-full p-4">
|
|
<div className="space-y-3">
|
|
<div className="flex justify-center items-center text-sm text-white/80">
|
|
<span className="font-bold text-lg text-accent">{stageType}:</span>
|
|
<div className="flex items-center gap-4 ml-2">
|
|
{stageType !== 'Cut Data' && <span className="text-white font-bold">{progressUpdate.toFixed(0)} / {maxProgressUpdate.toFixed(0)}</span>}
|
|
{stageType === 'Cut Data' && <span className="text-white font-bold truncate max-w-full overflow-hidden whitespace-nowrap">{messageUpdate}</span>}
|
|
</div>
|
|
</div>
|
|
<div className="w-full bg-white/20 rounded-full h-2 overflow-hidden">
|
|
<motion.div
|
|
className="h-full bg-gradient-to-r from-cyan-400 to-blue-500 rounded-full"
|
|
initial={{ width: 0 }}
|
|
animate={{ width: `${(progressUpdate/maxProgressUpdate)*100}%` }}
|
|
transition={{ duration: 0.3 }}
|
|
/>
|
|
</div>
|
|
<div className="text-center text-lg text-white/60">
|
|
Please wait...
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Instructions */}
|
|
<div className="bg-info/5 rounded-lg p-4 border border-info/30 mt-6">
|
|
<h3 className="font-medium text-error mb-2">📋 Instructions:</h3>
|
|
<ol className="text-sm text-error space-y-1">
|
|
<li>1. Click "Select Game Folder" and choose your game's root directory</li>
|
|
<li>2. Wait for the system to validate the game directory</li>
|
|
<li>3. Click "Select Diff file Folder" and choose your diff file's root directory</li>
|
|
<li>4. Wait for the system to validate the diff file directory</li>
|
|
<li>5. Click "Update Game" to save your changes</li>
|
|
</ol>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
} |