Merge branch 'marie/integrity' into 'main'

Add file integrity/verification

See merge request KRypt0n_/an-anime-game-launcher!45
This commit is contained in:
Observer KRypt0n_ 2022-02-20 12:50:00 +00:00
commit 0f3693d849
17 changed files with 214 additions and 2 deletions

View file

@ -25,6 +25,8 @@ launcher:
unpacking: Spiel wird entpackt...
applying_changes: Änderungen werden angewandt...
deleting_outdated: Lösche veraltete dateien...
integrity_check: Verifiziere Dateien...
download_mismatch_files: Lade nicht übereinstimmenden Dateien herunter...
# Voice packages installation
voice:
@ -70,6 +72,7 @@ launcher:
ready:
launch: Starten
predownload: Update vorinstallieren
integrity: Dateien verifizieren und reparieren
# Einstellungs Fenster
settings:

View file

@ -25,6 +25,8 @@ launcher:
unpacking: Unpacking game...
applying_changes: Applying changes...
deleting_outdated: Deleting outdated files...
integrity_check: Verifying Files...
download_mismatch_files: Downloading mismatched files...
# Voice packages installation
voice:
@ -70,6 +72,7 @@ launcher:
ready:
launch: Launch
predownload: Pre-download update
integrity: Verify Files & Repair
# Settings window
settings:

View file

@ -25,6 +25,8 @@ launcher:
unpacking: Descomprimiendo el juego...
applying_changes: Applying changes...
deleting_outdated: Borrando archivos viejos...
integrity_check: Verifying Files...
download_mismatch_files: Downloading mismatched files...
# Instalación de paquetes de voz
voice:
@ -70,6 +72,7 @@ launcher:
ready:
launch: Jugar
predownload: Pre-descargar actualización
integrity: Verify Files & Repair
# Ventana de opciones
settings:

View file

@ -25,6 +25,8 @@ launcher:
unpacking: Décompression du jeu...
applying_changes: Application des changements...
deleting_outdated: Suppression des fichiers non à jour...
integrity_check: Verifying Files...
download_mismatch_files: Downloading mismatched files...
# Voice packages installation
voice:
@ -73,6 +75,7 @@ launcher:
ready:
launch: Lancer
predownload: Pre-téléchargement de mise à jour
integrity: Verify Files & Repair
# Settings window
settings:

View file

@ -25,6 +25,8 @@ launcher:
unpacking: Játék kibontása folyamatban...
applying_changes: Változások alkalmazása...
deleting_outdated: Lejárt fájlok kitörlése...
integrity_check: Verifying Files...
download_mismatch_files: Downloading mismatched files...
# Voice packages installation
voice:
@ -70,6 +72,7 @@ launcher:
ready:
launch: Indítás
predownload: Frissítés előtöltése
integrity: Verify Files & Repair
# Settings window
settings:

View file

@ -25,6 +25,8 @@ launcher:
unpacking: Membongkar game...
applying_changes: Applying changes...
deleting_outdated: Menghapus file lama...
integrity_check: Verifying Files...
download_mismatch_files: Downloading mismatched files...
# Voice packages installation
voice:
@ -70,6 +72,7 @@ launcher:
ready:
launch: Luncurkan
predownload: Pembaruan pra-unduh
integrity: Verify Files & Repair
# Settings window
settings:

View file

@ -25,6 +25,8 @@ launcher:
unpacking: Decomprimendo il gioco...
applying_changes: Applicando i cambiamenti...
deleting_outdated: Cancellando file vecchi...
integrity_check: Verifying Files...
download_mismatch_files: Downloading mismatched files...
# Voice packages installation
voice:
@ -70,6 +72,7 @@ launcher:
ready:
launch: Avvia
predownload: Pre-scarica aggiornamento
integrity: Verify Files & Repair
# Settings window
settings:

View file

@ -25,6 +25,8 @@ launcher:
unpacking: Pakker ut spill...
applying_changes: Applying changes...
deleting_outdated: Fjerner utdaterte filer...
integrity_check: Verifying Files...
download_mismatch_files: Downloading mismatched files...
# Voice packages installation
voice:
@ -70,6 +72,7 @@ launcher:
ready:
launch: Start
predownload: Forhånds-nedlast oppdatering
integrity: Verify Files & Repair
# Settings window
settings:

View file

@ -25,6 +25,8 @@ launcher:
unpacking: Распаковка игры...
applying_changes: Применение изменений...
deleting_outdated: Удаление устаревших файлов...
integrity_check: Verifying Files...
download_mismatch_files: Downloading mismatched files...
# Установка звуковых пакетов
voice:
@ -70,6 +72,7 @@ launcher:
ready:
launch: Запустить
predownload: Предзагрузить обновление
integrity: Verify Files & Repair
# Окно настроек
settings:

View file

@ -25,6 +25,8 @@ launcher:
unpacking: unpacking g-game (o^▽^o)
applying_changes: appwying changes...
deleting_outdated: deweting outdated fiwes...
integrity_check: vewifying fiwes...
download_mismatch_files: downwoading mismatched fiwes...
# Voice packages installation
voice:
@ -70,6 +72,7 @@ launcher:
ready:
launch: launch (◕▿◕✿)
predownload: pwe-downwoad update
integrity: vewify files & wepaiw
# Settings window
settings:

View file

@ -24,6 +24,8 @@ launcher:
unpacking: 解压游戏中...
applying_changes: 应用更改...
deleting_outdated: 删除过期文件...
integrity_check: Verifying Files...
download_mismatch_files: Downloading mismatched files...
# Voice packages installation
voice:
@ -69,6 +71,7 @@ launcher:
ready:
launch: 启动
predownload: 预下载更新
integrity: Verify Files & Repair
# Settings window
settings:

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View file

@ -16,6 +16,7 @@
import Gear from './assets/images/gear.png';
import GearActive from './assets/images/gear-active.png';
import Download from './assets/images/cloud-download.png';
import Integrity from './assets/images/integrity.png';
const launcher = new Launcher(onMount);
@ -145,6 +146,10 @@
<img src={GearActive} class="active" alt="Settings">
</div>
<button class="button hint--left hint--small" aria-label="{typeof $locale === 'string' ? $_('launcher.states.ready.integrity') : ''}" id="integrity">
<img src={Integrity} alt="Integrity" />
</button>
<button class="button hint--left hint--small" aria-label="{typeof $locale === 'string' ? $_('launcher.states.ready.predownload') : ''}" id="predownload">
<img src={Download} alt="Download" />
</button>

View file

@ -70,6 +70,22 @@ img.background
width: 60%
margin: auto
#integrity
position: absolute
display: none
width: 52px
height: 52px
right: 386px
bottom: 54px
border-radius: 8px
img
width: 80%
margin: auto
#settings
width: 76px
height: 76px
@ -152,4 +168,4 @@ img.background
#downloader
background-color: rgba(0,0,0,.3)
border: 1px solid rgba(0,0,0,.4)
border: 1px solid rgba(0,0,0,.4)

View file

@ -22,7 +22,7 @@ class Stream extends AbstractInstaller
export default class Voice
{
protected static readonly langs = {
public static readonly langs = {
'en-us': 'English(US)',
'ja-jp': 'Japanese',
'ko-kr': 'Korean',

View file

@ -27,6 +27,7 @@ export default class State
public launchButton: HTMLElement;
public pauseButton: HTMLElement;
public predownloadButton: HTMLElement;
public integrityButton: HTMLElement;
public settingsButton: HTMLElement;
protected _state: LauncherState = 'game-launch-available';
@ -56,6 +57,7 @@ export default class State
this.launchButton = <HTMLElement>document.getElementById('launch');
this.pauseButton = <HTMLElement>document.getElementById('pause');
this.predownloadButton = <HTMLElement>document.getElementById('predownload');
this.integrityButton = <HTMLElement>document.getElementById('integrity');
this.settingsButton = <HTMLElement>document.getElementById('settings');
this.launchButton.onclick = () => {
@ -96,6 +98,21 @@ export default class State
});
};
this.integrityButton.onclick = () => {
this.launchButton.style['display'] = 'none';
this.integrityButton.style['display'] = 'none';
this.settingsButton.style['display'] = 'none';
import('./states/CheckIntegrity').then((module) => {
module.default(this.launcher).then(() => {
this.update().then(() => {
this.launchButton.style['display'] = 'block';
this.settingsButton.style['display'] = 'block';
});
});
});
};
this.update().then(async () => {
// Close splash screen
IPC.write('launcher-loaded');
@ -239,6 +256,7 @@ export default class State
this.launcher.progressBar!.hide();
this.predownloadButton.style['display'] = 'none';
this.integrityButton.style['display'] = 'none';
this.launchButton.classList.remove('button-blue');
this.launchButton.setAttribute('aria-label', '');
@ -272,6 +290,8 @@ export default class State
break;
case 'game-launch-available':
this.integrityButton.style['display'] = 'block';
this.launchButton.textContent = dictionary['ready']['launch'];
break;

View file

@ -0,0 +1,138 @@
import type Launcher from '../../Launcher';
import { Debug, fs, path, Cache, Downloader } from '../../../empathize';
import constants from '../../Constants';
import Patch from "../../Patch";
import Locales from '../Locales';
import Voice from "../../Voice";
declare const Neutralino;
async function download(fileInfo): Promise<boolean>
{
return new Promise(async (resolve) => {
const gameDir = await constants.paths.gameDir;
const cache = await Cache.get('Game.getLatestData.ServerResponse');
let uri = `${cache!.value['game']['latest']['decompressed_path']}/${fileInfo.remoteName}`;
Downloader.download(uri, `${gameDir}/${fileInfo.remoteName}.new`).then((stream) => {
stream.finish(async () => {
const process = await Neutralino.os.execCommand(`md5sum "${path.addSlashes(`${gameDir}/${fileInfo.remoteName}.new`)}"`);
const fileHash = (process.stdOut || process.stdErr).split(' ')[0];
if (fileHash == fileInfo.md5)
{
await fs.remove(`${gameDir}/${fileInfo.remoteName}`);
await fs.move(`${gameDir}/${fileInfo.remoteName}.new`, `${gameDir}/${fileInfo.remoteName}`);
resolve(true);
}
else
{
await fs.remove(`${gameDir}/${fileInfo.remoteName}.new`);
resolve(false);
}
});
});
});
}
export default (launcher: Launcher): Promise<void> => {
return new Promise(async (resolve) => {
const gameDir = await constants.paths.gameDir;
Neutralino.filesystem.readFile(`${gameDir}/pkg_version`)
.then(async (files) => {
// Check Game and Voice Pack Integrity
let mismatchedFiles = new Array();
files = files.split(/\r\n|\r|\n/).filter((file) => file != '');
const InstalledVoices = await Voice.installed;
for (const voice of InstalledVoices)
{
Neutralino.filesystem.readFile(`${gameDir}/Audio_${Voice.langs[voice.lang]}_pkg_version`)
.then(async (vfiles) => {
vfiles = vfiles.split(/\r\n|\r|\n/).filter((file) => file != '');
files.push(...vfiles);
})
}
if (files.length > 0)
{
const patch = await Patch.latest;
launcher.progressBar?.init({
label: Locales.translate('launcher.progress.game.integrity_check') as string,
showSpeed: false,
showEta: true,
showPercents: true,
showTotals: false
});
launcher.progressBar?.show();
let current = 0, total = files.length;
for (const file of files)
{
// {"remoteName": "AnAnimeGame_Data/StreamingAssets/AssetBundles/blocks/00/16567284.blk", "md5": "79ab71cfff894edeaaef025ef1152b77", "fileSize": 3232361}
const fileCheckInfo = JSON.parse(file) as { remoteName: string, md5: string, fileSize: number };
// If the file exists and it's not UnityPlayer.dll
// or if it's UnityPlayer.dll but the patch wasn't applied
if (await fs.exists(`${gameDir}/${fileCheckInfo.remoteName}`) &&
(!fileCheckInfo.remoteName.includes('UnityPlayer.dll') || !patch.applied))
{
const process = await Neutralino.os.execCommand(`md5sum "${path.addSlashes(`${gameDir}/${fileCheckInfo.remoteName}`)}"`);
const fileHash = (process.stdOut || process.stdErr).split(' ')[0];
if (fileHash != fileCheckInfo.md5)
mismatchedFiles.push(fileCheckInfo);
}
launcher.progressBar?.update(++current, total, 1);
}
Debug.log({
function: 'Launcher/States/Integrity',
message: mismatchedFiles.length == 0 ?
`Checked ${total} files with ${mismatchedFiles.length} mismatches` :
[
`Checked ${total} files with ${mismatchedFiles.length} mismatch(es):`,
...mismatchedFiles.map(e => `[${e.md5}] ${e.remoteName}`)
]
});
}
launcher.progressBar?.hide();
// Replace mismatched files
mismatchedFiles.forEach(async (fileInfo: { remoteName: string, md5: string, fileSize: number }) =>
{
launcher.progressBar?.init({
label: Locales.translate('launcher.progress.game.download_mismatch_files') as string,
showSpeed: false,
showEta: false,
showPercents: false,
showTotals: false
});
download(fileInfo).then(async (success) => {
if (!success)
Debug.log({
function: 'Launcher/States/Integrity',
message: `Download of ${fileInfo.remoteName} failed`
});
});
})
resolve();
})
.catch(() => resolve());
})
}