]> git.localhorst.tv Git - alttp.git/commitdiff
basic aosr patcher
authorDaniel Karbach <daniel.karbach@localhorst.tv>
Tue, 10 May 2022 14:16:53 +0000 (16:16 +0200)
committerDaniel Karbach <daniel.karbach@localhorst.tv>
Tue, 10 May 2022 14:16:53 +0000 (16:16 +0200)
16 files changed:
.env.example
.gitignore
app/Http/Controllers/AosSeedController.php [new file with mode: 0644]
app/Models/AosSeed.php [new file with mode: 0644]
config/filesystems.php
database/migrations/2022_05_10_115552_create_aos_seeds_table.php [new file with mode: 0644]
package-lock.json
package.json
resources/js/components/aos/App.js
resources/js/components/aos/Seed.js [new file with mode: 0644]
resources/js/components/pages/AosSeed.js [new file with mode: 0644]
resources/js/helpers/bps.js
resources/js/i18n/de.js
resources/js/i18n/en.js
routes/api.php
webpack.mix.js

index 3774044686de13597ff32fb080b4238292b5f498..9fa47a0ab8d9af784640edd7ebf710c84223e27f 100644 (file)
@@ -62,3 +62,4 @@ DISCORD_BOT_CREATE_COMMANDS=
 DISCORD_BOT_ENABLE_COMMANDS=
 
 AOS_HOSTNAME=aos.localhorst.tv
+AOS_URL=https://aos.localhorst.tv
index e9429a0a23da9dc9f72b29a7b3c52155cb5bd619..42465fed9adbd77ba696c6422df0e0f4456e3ef8 100644 (file)
@@ -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 (file)
index 0000000..9aea748
--- /dev/null
@@ -0,0 +1,24 @@
+<?php
+
+namespace App\Http\Controllers;
+
+use App\Models\AosSeed;
+use Illuminate\Http\Request;
+
+class AosSeedController extends Controller
+{
+
+       public function byHash($hash) {
+               $seed = AosSeed::where('hash', '=', $hash)->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 (file)
index 0000000..d44cfcc
--- /dev/null
@@ -0,0 +1,18 @@
+<?php
+
+namespace App\Models;
+
+use Illuminate\Database\Eloquent\Factories\HasFactory;
+use Illuminate\Database\Eloquent\Model;
+
+class AosSeed extends Model
+{
+       use HasFactory;
+
+       protected $casts = [
+               'mystery' => 'boolean',
+               'race' => 'boolean',
+               'settings' => 'array',
+       ];
+
+}
index e9d9dbdbe8ad384c1ea73b5f06bf9b9daa18007d..68b2694e004cdc85c3124420c351a734d07c94a1 100644 (file)
@@ -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 (file)
index 0000000..649d473
--- /dev/null
@@ -0,0 +1,38 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+       /**
+        * Run the migrations.
+        *
+        * @return void
+        */
+       public function up()
+       {
+               Schema::create('aos_seeds', function (Blueprint $table) {
+                       $table->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');
+       }
+};
index bd3412738b1df3f695d569c7da03ab9eb31c1aaa..f5f22c9936fc59834327d86440333150252279c6 100644 (file)
@@ -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",
                 "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",
                 }
             }
         },
+        "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",
index a71398d0b322802b156ddc232ec4b9a875040d6d..144f3dd1c025a89bf87206d9d8bf1da5dc287a60 100644 (file)
@@ -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",
index 82f177c01b6b7a35b3cc1ec1f5fa34e089a21db8..9f00effaf08c1aac7dd13a2ceaf2398e3b5df005 100644 (file)
@@ -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 = () => {
                        <UserContext.Provider value={user}>
                                <Header doLogout={doLogout} />
                                <Routes>
+                                       <Route path="h/:hash" element={<AosSeed />} />
                                        <Route path="users/:id" element={<User />} />
                                        <Route path="*" element={<Front />} />
                                </Routes>
diff --git a/resources/js/components/aos/Seed.js b/resources/js/components/aos/Seed.js
new file mode 100644 (file)
index 0000000..289e00e
--- /dev/null
@@ -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 <Container>
+               <h1>{i18n.t('aosSeeds.heading')}</h1>
+               <Row>
+                       <Col md={{ order: 2 }}>
+                               {rom ?
+                                       <Button
+                                               disabled={!patch}
+                                               onClick={() => applyPatch(
+                                                       rom,
+                                                       patch,
+                                                       `${i18n.t('aosSeeds.filename', {
+                                                               hash: seed.hash,
+                                                               preset: seed.preset,
+                                                       })}.gba`,
+                                               )}
+                                               variant="primary"
+                                       >
+                                               {i18n.t(patch ? 'aosSeeds.patch' : 'aosSeeds.fetchingPatch')}
+                                       </Button>
+                               :
+                                       <BaseRomButton />
+                               }
+                       </Col>
+                       <Col md={{ order: 1 }}>
+                               <p>
+                                       {i18n.t('aosSeeds.preset')}:
+                                       {' '}
+                                       <strong>{i18n.t(`aosSeeds.presets.${seed.preset}`)}</strong>
+                               </p>
+                               <p>{i18n.t(seed.race ? 'aosSeeds.race' : 'aosSeeds.noRace')}</p>
+                               <p>{i18n.t(seed.mystery ? 'aosSeeds.mystery' : 'aosSeeds.noMystery')}</p>
+                       </Col>
+               </Row>
+               <h2 className="mt-5">{i18n.t('aosSeeds.generator')}</h2>
+               <p>{i18n.t(`aosSeeds.generators.${seed.generator}`)}</p>
+       </Container>;
+};
+
+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 (file)
index 0000000..6cea156
--- /dev/null
@@ -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 <Loading />;
+       }
+
+       if (error) {
+               return <ErrorMessage error={error} />;
+       }
+
+       if (!seed) {
+               return <NotFound />;
+       }
+
+       return <ErrorBoundary>
+               <Seed patch={patch} seed={seed} />
+       </ErrorBoundary>;
+};
+
+export default AosSeed;
index 9409309a0c39d53d74ec9da1c71a9aa356550128..1df44f1876e0c7902195b2b60b2fcbd04ebfe06d 100644 (file)
@@ -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;
        }
index dcc3d68d96a50bffdf10c5a1b69c227be31cdfb4..391e176cf3130aedeec88c714fde86533641cc72 100644 (file)
@@ -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',
index 5cc5ed1f0b88252256fc8b9f96d49ed88cc0707d..190aceae3e4ea0644ab6864de352a053abedc959 100644 (file)
@@ -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',
index 39a75317017d7ff405d211364bd99a44790e5b6b..e3a5ad8b32bf1523ff98f9a66e93bac86726b6f2 100644 (file)
@@ -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');
 
index 8dd2c993e7005b084ed9be48f850c3da5f48ff05..b38b23209f69282ba51aa57a148456b04cf33814 100644 (file)
@@ -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',