From 75b3b5826c781e47b3db693fa6d3d17f67c79e56 Mon Sep 17 00:00:00 2001 From: Daniel Karbach Date: Tue, 10 May 2022 16:16:53 +0200 Subject: [PATCH] basic aosr patcher --- .env.example | 1 + .gitignore | 1 + app/Http/Controllers/AosSeedController.php | 24 ++++ app/Models/AosSeed.php | 18 +++ config/filesystems.php | 130 ++++++++++-------- ...22_05_10_115552_create_aos_seeds_table.php | 38 +++++ package-lock.json | 11 ++ package.json | 1 + resources/js/components/aos/App.js | 2 + resources/js/components/aos/Seed.js | 77 +++++++++++ resources/js/components/pages/AosSeed.js | 77 +++++++++++ resources/js/helpers/bps.js | 4 +- resources/js/i18n/de.js | 34 +++++ resources/js/i18n/en.js | 34 +++++ routes/api.php | 2 + webpack.mix.js | 2 + 16 files changed, 393 insertions(+), 63 deletions(-) create mode 100644 app/Http/Controllers/AosSeedController.php create mode 100644 app/Models/AosSeed.php create mode 100644 database/migrations/2022_05_10_115552_create_aos_seeds_table.php create mode 100644 resources/js/components/aos/Seed.js create mode 100644 resources/js/components/pages/AosSeed.js diff --git a/.env.example b/.env.example index 3774044..9fa47a0 100644 --- a/.env.example +++ b/.env.example @@ -62,3 +62,4 @@ DISCORD_BOT_CREATE_COMMANDS= DISCORD_BOT_ENABLE_COMMANDS= AOS_HOSTNAME=aos.localhorst.tv +AOS_URL=https://aos.localhorst.tv diff --git a/.gitignore b/.gitignore index e9429a0..42465fe 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /node_modules +/public/aos-seeds /public/css/app.css /public/css/app.css.map /public/doortracker diff --git a/app/Http/Controllers/AosSeedController.php b/app/Http/Controllers/AosSeedController.php new file mode 100644 index 0000000..9aea748 --- /dev/null +++ b/app/Http/Controllers/AosSeedController.php @@ -0,0 +1,24 @@ +firstOrFail(); + + if ($seed->race) { + $seed->hide('seed'); + } + if ($seed->mystery) { + $seed->hide('settings'); + } + + return $seed->toJson(); + } + +} diff --git a/app/Models/AosSeed.php b/app/Models/AosSeed.php new file mode 100644 index 0000000..d44cfcc --- /dev/null +++ b/app/Models/AosSeed.php @@ -0,0 +1,18 @@ + 'boolean', + 'race' => 'boolean', + 'settings' => 'array', + ]; + +} diff --git a/config/filesystems.php b/config/filesystems.php index e9d9dbd..68b2694 100644 --- a/config/filesystems.php +++ b/config/filesystems.php @@ -2,75 +2,83 @@ return [ - /* - |-------------------------------------------------------------------------- - | Default Filesystem Disk - |-------------------------------------------------------------------------- - | - | Here you may specify the default filesystem disk that should be used - | by the framework. The "local" disk, as well as a variety of cloud - | based disks are available to your application. Just store away! - | - */ + /* + |-------------------------------------------------------------------------- + | Default Filesystem Disk + |-------------------------------------------------------------------------- + | + | Here you may specify the default filesystem disk that should be used + | by the framework. The "local" disk, as well as a variety of cloud + | based disks are available to your application. Just store away! + | + */ - 'default' => env('FILESYSTEM_DISK', 'local'), + 'default' => env('FILESYSTEM_DISK', 'local'), - /* - |-------------------------------------------------------------------------- - | Filesystem Disks - |-------------------------------------------------------------------------- - | - | Here you may configure as many filesystem "disks" as you wish, and you - | may even configure multiple disks of the same driver. Defaults have - | been set up for each driver as an example of the required values. - | - | Supported Drivers: "local", "ftp", "sftp", "s3" - | - */ + /* + |-------------------------------------------------------------------------- + | Filesystem Disks + |-------------------------------------------------------------------------- + | + | Here you may configure as many filesystem "disks" as you wish, and you + | may even configure multiple disks of the same driver. Defaults have + | been set up for each driver as an example of the required values. + | + | Supported Drivers: "local", "ftp", "sftp", "s3" + | + */ - 'disks' => [ + 'disks' => [ - 'local' => [ - 'driver' => 'local', - 'root' => storage_path('app'), - 'throw' => false, - ], + 'local' => [ + 'driver' => 'local', + 'root' => storage_path('app'), + 'throw' => false, + ], - 'public' => [ - 'driver' => 'local', - 'root' => storage_path('app/public'), - 'url' => env('APP_URL').'/storage', - 'visibility' => 'public', - 'throw' => false, - ], + 'public' => [ + 'driver' => 'local', + 'root' => storage_path('app/public'), + 'url' => env('APP_URL').'/storage', + 'visibility' => 'public', + 'throw' => false, + ], - 's3' => [ - 'driver' => 's3', - 'key' => env('AWS_ACCESS_KEY_ID'), - 'secret' => env('AWS_SECRET_ACCESS_KEY'), - 'region' => env('AWS_DEFAULT_REGION'), - 'bucket' => env('AWS_BUCKET'), - 'url' => env('AWS_URL'), - 'endpoint' => env('AWS_ENDPOINT'), - 'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false), - 'throw' => false, - ], + 'aos-seeds' => [ + 'driver' => 'local', + 'root' => storage_path('app/aos-seeds'), + 'url' => env('AOS_URL').'/aos-seeds', + 'visibility' => 'public', + ], - ], + 's3' => [ + 'driver' => 's3', + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'region' => env('AWS_DEFAULT_REGION'), + 'bucket' => env('AWS_BUCKET'), + 'url' => env('AWS_URL'), + 'endpoint' => env('AWS_ENDPOINT'), + 'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false), + 'throw' => false, + ], - /* - |-------------------------------------------------------------------------- - | Symbolic Links - |-------------------------------------------------------------------------- - | - | Here you may configure the symbolic links that will be created when the - | `storage:link` Artisan command is executed. The array keys should be - | the locations of the links and the values should be their targets. - | - */ + ], - 'links' => [ - public_path('storage') => storage_path('app/public'), - ], + /* + |-------------------------------------------------------------------------- + | Symbolic Links + |-------------------------------------------------------------------------- + | + | Here you may configure the symbolic links that will be created when the + | `storage:link` Artisan command is executed. The array keys should be + | the locations of the links and the values should be their targets. + | + */ + + 'links' => [ + public_path('storage') => storage_path('app/public'), + public_path('aos-seeds') => storage_path('app/aos-seeds'), + ], ]; diff --git a/database/migrations/2022_05_10_115552_create_aos_seeds_table.php b/database/migrations/2022_05_10_115552_create_aos_seeds_table.php new file mode 100644 index 0000000..649d473 --- /dev/null +++ b/database/migrations/2022_05_10_115552_create_aos_seeds_table.php @@ -0,0 +1,38 @@ +id(); + $table->binary('hash'); + $table->string('generator'); + $table->string('preset'); + $table->boolean('race')->default(false); + $table->boolean('mystery')->default(false); + $table->string('seed'); + $table->text('settings'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('aos_seeds'); + } +}; diff --git a/package-lock.json b/package-lock.json index bd34127..f5f22c9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "@fortawesome/free-solid-svg-icons": "^6.0.0", "@fortawesome/react-fontawesome": "^0.1.17", "crc-32": "^1.2.2", + "file-saver": "^2.0.5", "formik": "^2.2.9", "i18next": "^21.6.13", "i18next-browser-languagedetector": "^6.1.3", @@ -5726,6 +5727,11 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/file-saver": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz", + "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==" + }, "node_modules/file-type": { "version": "12.4.2", "resolved": "https://registry.npmjs.org/file-type/-/file-type-12.4.2.tgz", @@ -16499,6 +16505,11 @@ } } }, + "file-saver": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz", + "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==" + }, "file-type": { "version": "12.4.2", "resolved": "https://registry.npmjs.org/file-type/-/file-type-12.4.2.tgz", diff --git a/package.json b/package.json index a71398d..144f3dd 100644 --- a/package.json +++ b/package.json @@ -77,6 +77,7 @@ "@fortawesome/free-solid-svg-icons": "^6.0.0", "@fortawesome/react-fontawesome": "^0.1.17", "crc-32": "^1.2.2", + "file-saver": "^2.0.5", "formik": "^2.2.9", "i18next": "^21.6.13", "i18next-browser-languagedetector": "^6.1.3", diff --git a/resources/js/components/aos/App.js b/resources/js/components/aos/App.js index 82f177c..9f00eff 100644 --- a/resources/js/components/aos/App.js +++ b/resources/js/components/aos/App.js @@ -3,6 +3,7 @@ import React, { useEffect, useState } from 'react'; import { BrowserRouter, Route, Routes } from 'react-router-dom'; import Header from './Header'; +import AosSeed from '../pages/AosSeed'; import Front from '../pages/Front'; import User from '../pages/User'; import AosBaseRomProvider from '../../helpers/AosBaseRomContext'; @@ -53,6 +54,7 @@ const App = () => {
+ } /> } /> } /> diff --git a/resources/js/components/aos/Seed.js b/resources/js/components/aos/Seed.js new file mode 100644 index 0000000..289e00e --- /dev/null +++ b/resources/js/components/aos/Seed.js @@ -0,0 +1,77 @@ +import FileSaver from 'file-saver'; +import PropTypes from 'prop-types'; +import React from 'react'; +import { Button, Col, Container, Row } from 'react-bootstrap'; +import { withTranslation } from 'react-i18next'; +import toastr from 'toastr'; + +import BaseRomButton from './BaseRomButton'; +import { useAosBaseRom } from '../../helpers/AosBaseRomContext'; +import BPS from '../../helpers/bps'; +import i18n from '../../i18n'; + +const applyPatch = (rom, patch, filename) => { + try { + const bps = new BPS(); + bps.setPatch(patch); + bps.setSource(rom); + const result = bps.applyPatch(); + FileSaver.saveAs(new Blob([result], { type: 'application/octet-stream' }), filename); + } catch (e) { + toastr.error(i18n.t('aosSeeds.patchError', { msg: e.message })); + } +}; + +const Seed = ({ patch, seed }) => { + const { rom } = useAosBaseRom(); + + return +

{i18n.t('aosSeeds.heading')}

+ + + {rom ? + + : + + } + + +

+ {i18n.t('aosSeeds.preset')}: + {' '} + {i18n.t(`aosSeeds.presets.${seed.preset}`)} +

+

{i18n.t(seed.race ? 'aosSeeds.race' : 'aosSeeds.noRace')}

+

{i18n.t(seed.mystery ? 'aosSeeds.mystery' : 'aosSeeds.noMystery')}

+ +
+

{i18n.t('aosSeeds.generator')}

+

{i18n.t(`aosSeeds.generators.${seed.generator}`)}

+
; +}; + +Seed.propTypes = { + patch: PropTypes.instanceOf(ArrayBuffer), + seed: PropTypes.shape({ + generator: PropTypes.string, + hash: PropTypes.string, + mystery: PropTypes.bool, + preset: PropTypes.string, + race: PropTypes.bool, + }), +}; + +export default withTranslation()(Seed); diff --git a/resources/js/components/pages/AosSeed.js b/resources/js/components/pages/AosSeed.js new file mode 100644 index 0000000..6cea156 --- /dev/null +++ b/resources/js/components/pages/AosSeed.js @@ -0,0 +1,77 @@ +import axios from 'axios'; +import React, { useEffect, useState } from 'react'; +import { useParams } from 'react-router-dom'; + +import NotFound from './NotFound'; +import Seed from '../aos/Seed'; +import ErrorBoundary from '../common/ErrorBoundary'; +import ErrorMessage from '../common/ErrorMessage'; +import Loading from '../common/Loading'; + +const AosSeed = () => { + const params = useParams(); + const { hash } = params; + + const [error, setError] = useState(null); + const [loading, setLoading] = useState(true); + const [patch, setPatch] = useState(null); + const [seed, setSeed] = useState(null); + + useEffect(() => { + setLoading(true); + const ctrl = new AbortController(); + axios + .get(`/api/aos-seed/${hash}`, { signal: ctrl.signal }) + .then(response => { + setError(null); + setLoading(false); + setSeed(response.data); + window.document.title = response.data.hash; + }) + .catch(error => { + setError(error); + setLoading(false); + setSeed(null); + }); + return () => { + ctrl.abort(); + }; + }, [hash]); + + useEffect(() => { + setPatch(null); + const ctrl = new AbortController(); + axios + .get(`/aos-seeds/${hash}.bps`, { + responseType: 'arraybuffer', + signal: ctrl.signal, + }) + .then(response => { + setPatch(response.data); + }) + .catch(error => { + setError(error); + }); + return () => { + ctrl.abort(); + }; + }, [hash]); + + if (loading) { + return ; + } + + if (error) { + return ; + } + + if (!seed) { + return ; + } + + return + + ; +}; + +export default AosSeed; diff --git a/resources/js/helpers/bps.js b/resources/js/helpers/bps.js index 9409309..1df44f1 100644 --- a/resources/js/helpers/bps.js +++ b/resources/js/helpers/bps.js @@ -85,14 +85,14 @@ export default class BPS { setSource(file) { this.sourceFile = new Uint8Array(file); - this.sourceChecksum = CRC32.buf(file); + this.sourceChecksum = CRC32.buf(this.sourceFile); return this; } setTarget(file) { this.targetFile = new Uint8Array(file); - this.targetChecksum = CRC32.buf(file); + this.targetChecksum = CRC32.buf(this.targetFile); return this; } diff --git a/resources/js/i18n/de.js b/resources/js/i18n/de.js index dcc3d68..391e176 100644 --- a/resources/js/i18n/de.js +++ b/resources/js/i18n/de.js @@ -7,6 +7,40 @@ export default { baseRomSet: 'Base ROM gespeichert.', setBaseRom: 'Base ROM auswählen', }, + aosSeeds: { + fetchingPatch: 'Lade Patch', + filename: 'aosr - {{preset}} - {{hash}}', + heading: 'Aria of Sorrow Randomizer Seed', + generator: 'Generator', + generators: { + surge: 'Dieser Seed wurde mit dem Randomizer von fusecv auf aosrando.surge.sh generiert', + }, + mystery: 'Mystery', + noMystery: 'Kein Mystery', + noRace: 'Kein Race', + patch: 'ROM patchen', + patchError: 'Fehler beim Patchen: {{msg}}', + preset: 'Preset', + presets: { + Area: 'Area', + AreaAllBosses: 'Area alle Bosse', + AreaBeginner: 'Area Beginner', + AreaExpert: 'Area Experte', + AreaExtraFast: 'Area extra schnell', + AreaSpicy: 'Area spicy', + Beginner: 'Beginner', + DoorAllBosses: 'Door alle Bosse', + DoorAllBossesEasy: 'Door alle Bosses einfach', + ExtraFastNormal: 'Extra schnell normal', + Normal: 'Normal', + SGLive2020: 'SGLive 2020', + SGLive2021: 'SGLive 2021', + SpicyNormal: 'Spicy normal', + Tournament2021: 'Turnier 2021', + Tournament2022: 'Turnier 2022', + }, + race: 'Race', + }, applications: { accept: 'Annehmen', acceptError: 'Fehler beim Annehmen', diff --git a/resources/js/i18n/en.js b/resources/js/i18n/en.js index 5cc5ed1..190acea 100644 --- a/resources/js/i18n/en.js +++ b/resources/js/i18n/en.js @@ -7,6 +7,40 @@ export default { baseRomSet: 'Base ROM set.', setBaseRom: 'Set base ROM', }, + aosSeeds: { + fetchingPatch: 'Fetching patch', + filename: 'aosr - {{preset}} - {{hash}}', + heading: 'Aria of Sorrow Randomizer Seed', + generator: 'Generator', + generators: { + surge: 'This seed has been generated with fusecv\'s randomizer on aosrando.surge.sh.', + }, + mystery: 'Mystery', + noMystery: 'No mystery', + noRace: 'No race', + patch: 'Patch ROM', + patchError: 'Error applying patch: {{msg}}', + preset: 'Preset', + presets: { + Area: 'Area', + AreaAllBosses: 'Area all bosses', + AreaBeginner: 'Area beginner', + AreaExpert: 'Area expert', + AreaExtraFast: 'Area extra fast', + AreaSpicy: 'Area spicy', + Beginner: 'Beginner', + DoorAllBosses: 'Door all bosses', + DoorAllBossesEasy: 'Door all bosses easy', + ExtraFastNormal: 'Extra fast normal', + Normal: 'Normal', + SGLive2020: 'SGLive 2020', + SGLive2021: 'SGLive 2021', + SpicyNormal: 'Spicy normal', + Tournament2021: 'Tournament 2021', + Tournament2022: 'Tournament 2022', + }, + race: 'Race', + }, applications: { accept: 'Accept', acceptError: 'Error accepting', diff --git a/routes/api.php b/routes/api.php index 39a7531..e3a5ad8 100644 --- a/routes/api.php +++ b/routes/api.php @@ -18,6 +18,8 @@ Route::middleware('auth:sanctum')->get('/user', function (Request $request) { return $request->user(); }); +Route::get('aos-seed/{hash}', 'App\Http\Controllers\AosSeedController@byHash'); + Route::post('application/{application}/accept', 'App\Http\Controllers\ApplicationController@accept'); Route::post('application/{application}/reject', 'App\Http\Controllers\ApplicationController@reject'); diff --git a/webpack.mix.js b/webpack.mix.js index 8dd2c99..b38b232 100644 --- a/webpack.mix.js +++ b/webpack.mix.js @@ -25,10 +25,12 @@ mix.js('resources/js/app.js', 'public/js') '@restart/hooks', '@restart/ui', 'axios', + 'file-saver', 'formik', 'i18next', 'i18next-browser-languagedetector', 'laravel-echo', + 'localforage', 'lodash', 'moment', 'numeral', -- 2.39.2